Problem
RFC-022 introduced mount_hole for mechanical locating holes, deliberately scoped to circular holes only (diameter D), naming non-circular shapes as an explicit, deferred gap: "if a real footprint needs a non-circular locating feature (a slot, a keyed/D-shaped hole) — extend mount_hole's shape vocabulary via a scoped follow-up RFC."
That real need now exists, grounded in a real datasheet: the Kailh Choc V2 (Low Profile) switch's official "Recommended PCB Mounting Pad Dimensions" drawing (visually inspected, dimension labels confirmed: 2.00mm × 1.50mm) specifies two rectangular through-hole pads as the switch's two mechanical mounting/support legs — distinct from its two circular through-hole electrical pins. This is not a hypothetical case: it is the standard footprint for a real, widely-used mechanical keyboard switch family.
Confirmed against real source (src/ast.rs): MountHole today has exactly one geometry field, diameter: UnitValue — there is no way to express a rectangular (or oval) locating hole. Modeling the Choc V2's mounting legs as a pad is wrong for the same reason RFC-022 itself established: these legs are non-electrical (mechanical-only, no net) and have no device pin number to bind to, so forcing them through pad's pin-bound numbering would either be a category error or require breaking RFC-018's unconditional pad-completeness guarantee (RFC-022's own central rationale, unchanged here).
Who this is for: library authors modeling real switch/connector/mounting-hardware footprints whose mechanical legs are non-circular — starting concretely with a std-library Kailh Choc V2 footprint.
Goals
- Extend
mount_holeto support the same closed shape vocabularypadalready has (rect,circle,oval— RFC-018's existingPadShapeenum, reused verbatim, not reinvented) — closing exactly the gap RFC-022 named and no more. - Keep
mount_hole's core guarantees from RFC-022 completely unchanged: numbering stays disjoint frompad's pin-bound numbers, never checked against the bound device's declared pins;platingstays the same closed two-value set (non_plated/plated); always spansthrough_all.
Non-goals
- Not a general 2D shape/geometry language. The same three shapes
padalready supports, nothing more — no arbitrary polygons, no rounded-rectangle corner-radius control, no keyed/D-shaped/slotted profiles beyond whatrect/ovalalready express reasonably well. If a future real footprint needs a shape beyond these three, that is separately scoped future work, not silently expanded here. - Not board-level mounting holes — unchanged non-goal from RFC-022; still out of scope, still a design/board-level concept closer to
board_outline(RFC-020). - Not changing
pad's own shape/size grammar — this RFC only extendsmount_holeto reuse the existingPadShapeenum andpad's existing shape-dependent-sizing convention;paditself is untouched.
Design
// std/switches/kailh_choc_v2.cohdl
pub footprint KailhChocV2 {
pad 1: Round_1_0mm_THT at (-2.75mm, 3.0mm) // electrical pin, top
pad 2: Round_1_0mm_THT at (2.75mm, -3.0mm) // electrical pin, bottom
mount_hole 1: non_plated shape: rect size: (2.0mm, 1.5mm) at (-6.75mm, 0mm)
mount_hole 2: non_plated shape: rect size: (2.0mm, 1.5mm) at (6.75mm, 0mm)
courtyard { shape: rect, at: (0mm, 0mm), size: (15.5mm, 15.5mm) }
silkscreen_ref { at: (0mm, -8.5mm) }
}
mount_hole N: PLATING [shape: SHAPE] at (x, y) [diameter D | size: (w, h)]— ashape:field is now accepted, one ofrect,circle,oval(RFC-018's existingPadShapeclosed set, reused, not redefined).shape:is optional; its absence meanscircle— this preserves every existingmount_holedeclaration's meaning unchanged (RFC-022's own examples, and any real footprint authored since, all continue to parse and mean exactly what they meant before this RFC).- The geometry field is shape-dependent, mirroring
pad's own established convention exactly:circle(explicit or defaulted) takesdiameter D(a singleLengthvalue, unchanged from RFC-022);rect/ovaltakesize: (w, h)(twoLengthvalues, the same tuple shapepad's ownsize:field already uses forrect/oval). - Writing
diameteralongsideshape: rect/shape: oval, or writingsize:alongsideshape: circle(explicit or defaulted), is a compile error naming the mismatch — the same shape-dependent-field-consistency disciplinepadalready enforces for its owndrill:/plating:pairing (RFC-018). PLATING,at (x, y), the disjoint-from-padnumbering namespace, and the always-through_alllayer are all unchanged from RFC-022.
Type-system-first test
Both checks this RFC adds are structural, local to one mount_hole declaration, and checkable the moment it is parsed — never DRC candidates:
shape:, when present, must be one of the closed three-value set — identical checking shape topad's existingshape:field (RFC-018), just reused on a second construct.- The geometry field present must match what the (explicit or defaulted) shape requires —
diameterforcircle,size:forrect/oval, never both, never neither. This mirrorspad's existingdrill:-required-iff-plating: plated_through_holeconsistency check exactly, applied to a different field pairing.
Conceptual impact
Low. No new core concept, no new top-level declaration kind, no new enum — mount_hole gains one optional field (shape:) whose values are drawn from an enum (PadShape) that already exists and is already fully specified (RFC-018). The shape-dependent-geometry-field convention this RFC applies to mount_hole is likewise not new — it is pad's own established pattern, reused verbatim on a second construct rather than inventing a parallel one.
Coherence matrix row
| Concepts | Grammar | Oracle | Diagnostics | Netlist | Compat | Trust |
|---|---|---|---|---|---|---|
| Low | Low | Low | Low | Low | Low | High |
Trust (High): this closes a real, concrete gap RFC-022 itself flagged and disclosed honestly rather than silently working around — a real datasheet (Kailh Choc V2) needed exactly this shape and had no correct way to express it before this RFC. Grammar/Diagnostics (Low): one optional field, reusing an existing enum and an existing shape-dependent-sizing pattern — the smallest possible grammar surface for the job. Netlist (Low): the KiCad .kicad_mod/IPC-2581 emitters already know how to project rect/oval geometry for pad (RFC-018) — projecting the same shapes for mount_hole (still net-less, still np_thru_hole/plated-with-no-net per RFC-022) is a direct reuse of existing emitter code paths, not new geometry-handling logic. Compat (Low): every existing mount_hole declaration (circular, using diameter) is unchanged in meaning — shape:'s absence defaults to circle, so this is purely additive.
Gradeability
shape:value outside{rect, circle, oval}— checked at declaration/parse time, the same E8xx sub-case class RFC-022 already established formount_hole.- Geometry-field/shape mismatch (
diameterwithrect/oval,size:withcircle) — checked at declaration time, naming the specific mismatch, mirroringpad's existingdrill:/plating:diagnostic precedent (RFC-018). - Neither check runs in residual DRC.
AI-generatability
High. An author or model that already knows pad's shape:/size: convention (RFC-018) has already learned everything this RFC adds — the same enum, the same shape-dependent-sizing rule, applied to a second, already-familiar construct (mount_hole, RFC-022). No new concept to memorize.
Alternatives
- A separate construct for rectangular/oval locating holes (e.g.
mount_slot) — rejected: this would duplicate nearly all ofmount_hole's existing behavior (disjoint numbering, plating, position,through_alllayer) for what is really just a shape variation — the same "two constructs for one underlying relationship" smell RFC-018's own Alternatives rejected when considering separate constructs for pads vs. footprints. - Overload
diameterto accept either a scalar or a(w, h)tuple, inferring shape implicitly — rejected: this would make a footprint's rendered shape depend on an author's field-shape choice rather than an explicit, readableshape:declaration — exactly the kind of "correct by convention, not by the compiler" ambiguity the Constitution forbids elsewhere, and inconsistent withpad's own explicitshape:field precedent. - A general 2D CAD/polygon authoring mechanism for locating features — rejected as premature, unchanged from RFC-022's own reasoning: no concrete need beyond
rect/circle/ovalhas been shown; reusingpad's existing closed set is the right-sized slice.
Compatibility
Purely additive. Every existing mount_hole N: PLATING at (x, y) diameter D declaration (RFC-022's only prior shape) is unchanged in meaning — shape:'s default is circle, so no migration is required for any existing footprint.
Depends on: RFC-018 (for the reused PadShape enum and shape-dependent-sizing convention) and RFC-022 (mount_hole itself) — both already Accepted.
Tooling & operations
cohdl lsphover on amount_holeline should show its resolved shape and geometry (diameter or size), the same "resolve and show" precedent already established forpad(RFC-018) andmount_hole(RFC-022).cohdl fmtneeds one small update: formatshape:/size:onmount_holelines using the exact same spacing/comma convention already used forpad's ownshape:/size:fields — no new formatting category, a direct extension of an existing rule.- Reserves new E8xx sub-cases (designators & parts, RFC-018/022's home for footprint-completeness checks): invalid
mount_holeshape value, geometry-field/shape mismatch. cohdl build's KiCad.kicad_modemitter projects arect/oval-shapedmount_holeusing the same non-roundnp_thru_holepad geometry KiCad itself supports (KiCad'snp_thru_holepads are not restricted to round shapes — this was already true of the real target format RFC-022 grounded itself in); the IPC-2581 emitter projects the corresponding rect/oval hole/pin geometry with no net reference, unchanged in spirit from RFC-022's circular case.
Teaching cost
Low. An author who already knows pad's shape:/size: convention has zero new concept to learn — this RFC's entire teaching cost is "the same field, on a second construct." Authors who only ever need circular locating holes experience no change at all (the default is unchanged).
Failure modes
- A rectangular/oval
mount_hole's dimensions are wrong but structurally valid (a typo'd size) — this RFC's checks cannot catch this, the same disclosed, unavoidable limitation RFC-018/022 already named for pad/hole dimensions generally. - An author picks
shape: rectwhen the real hardware's hole is actually a rounded-rectangle or a true slot (rounded corners, not a sharp rectangle) —rect's geometry is an approximation for such cases, the same honest limitationpad's ownrectshape already carries; not solved by this RFC, consistent with the Non-goals above.
Migration path
No existing mount_hole declaration requires migration — shape:'s default (circle) preserves every prior declaration's meaning exactly. Real, non-mechanical authoring work remains separately: the std library should gain a real Kailh Choc V2 footprint (using this RFC's new shape: rect size: (2.0mm, 1.5mm) mount_hole syntax for its two mounting legs) — genuine new content to author, not a retrofit this RFC's completion bar requires.
Decision
Accepted — 2026-07-19. Recorded as DR-029 (see note 7). Extends mount_hole (RFC-022) with an optional shape: field (reusing RFC-018's existing PadShape closed set: rect, circle, oval) and a shape-dependent geometry field (diameter for circle, unchanged and still the default; size: (w, h) for rect/oval, new) — closing the exact non-circular-locating-hole gap RFC-022 itself named and deferred. Grounded in a real datasheet (Kailh Choc V2 switch, 2.0mm × 1.5mm rectangular through-hole mounting legs). Purely additive; no existing mount_hole declaration changes meaning. Language Specification (note 10) updates the "Mechanical locating holes (mount_hole)" section in place.