bootstrap: accept pub const Name(T) = struct as a parameterised type declaration - #2168
bootstrap: accept pub const Name(T) = struct as a parameterised type declaration#2168gHashTag wants to merge 1 commit into
pub const Name(T) = struct as a parameterised type declaration#2168Conversation
…e declaration (Closes #2162) The AST contract was written first, in architecture/ADR-008, and the patch was made to satisfy it. That ordering is the point: a test authored after a patch tends to describe whatever the patch happened to do, and would have passed for a parser that accepted the form for the wrong reason. Measured [measured]: * 33 declarations of the form across 28 corpus files, all with `struct` on the right-hand side -- so the form is used consistently and is not a one-off * old binary rejects 6 of 6 positive fixtures; candidate accepts 6 of 6 * candidate rejects 7 of 7 negative fixtures, so acceptance is not blanket * cargo test --test generic_const_decl: 16 / 16 * cargo test --bin t27c: 1537 passed, 0 failed, 2 ignored Differential over the 634-spec library, /tmp/t27c.m2162base -> /tmp/t27c.m2162fix: 633 unchanged, 0 field-loss, 1 strict-improvement (specs/tri/collections/array.t27), 0 unknown. Read that with the coverage caveat now enforced by diffbin: the `unchanged` category of the tool that produced it counted files neither binary parsed as agreement, so the figure bounds regressions only over the files that were actually measured. This is a parser defect and not a corpus error, so none of the 28 files are rewritten. Not settled here, deliberately: * #2164, `Name(T)` in type application position. Accepting the declaration form does not imply accepting the use form; that needs its own ADR. * #2165, whether `test` is a reserved word. bootstrap/stage0/FROZEN_HASH moves cd2822f2... -> 8ba6585a.... That is a GOLD-RING seal and requires explicit human approval; it is not a mechanical consequence of the patch and must not be waved through with it.
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Review package for #2168 — measured, not mergedPrepared under the owner's order of 2026-08-15: prepare a reproducible commit, 1. Reproducible pair, provenance captured at build time
Both were built by The build path is a constant ( 2. Full uniform run over
|
| fixture | base | candidate | |
|---|---|---|---|
pos_01…pos_06 |
error | ok | as declared — the fix is load-bearing |
neg_01…neg_05 |
error | error | guard only, states no fix |
neg_06_value_rhs |
ok | error | not as declared |
neg_07_no_rhs |
ok | error | not as declared |
The last two are a behaviour change beyond the ADR, so they are disclosed rather
than described as "hardening". Inspected: base does not accept them either. It
emits a ConstDecl named Answer / Opaque with empty children plus a
stray sibling StmtAssign / StmtExpr — a hollow declaration and a loose
statement, from one source line. That is the silent-loss class: exit code 0 over
a dropped declaration. The candidate replaces it with a specific diagnostic.
So base=ok in my own table was a metric of the exit code, not of
acceptance. The classifier was the first suspect and it was guilty. Recorded as
such, because it is the same failure mode as an aggregate that printed
"0 regressions" while thirteen files lost declared fields.
An owner decision is still required on whether narrowing these two forms is
wanted, since the ADR settled the struct RHS and is silent on a value RHS and on
no RHS at all.
5. Not claimed, stated so it travels with the numbers
- Not merge-ready. This PR moves
bootstrap/stage0/FROZEN_HASH, and that needs
the owner's explicit permission independent of any measurement here. - Not mixed with parser: nesting decides the separator, never the terminator (#2127) #2151. That one stays blocked on residual
field-lossand on
the language decision about the 18 lines whose element type is physically lost
(corpus damage: freeze, per-class candidate patch, and what one rule does not reach (#2154 follow-up) #2160 / Generic type APPLICATION (Name(T) in a type position) is rejected; 20 library specs, needs a language decision #2164). This PR is isolated support for approved syntax. - The differential compares parse-level field sets. It says nothing about
generated code, type inference, diagnostics quality, or timing. - Coverage is 55.0 % of
specs/,specs/scratchis unmeasured, and no sentence
of the form "no regressions" is admissible without both facts attached.
Closes #2162
What this changes
pub const Name(T) = struct { ... }now parses asConstDecl(name, generic_parameters, StructExpr).The AST contract was written before the implementation, in
architecture/ADR-008-parameterised-const-type-declaration.md. That ordering isload-bearing rather than procedural: a test written after a patch tends to
describe whatever the patch happened to do, and would pass just as happily for a
parser that accepted the form for the wrong reason. The ADR states the shape the
tree must have, and the tests check that shape.
Measured
[measured]structon the rightcargo test --test generic_const_declcargo test --bin t27cDifferential over the 634-spec library,
/tmp/t27c.m2162base->/tmp/t27c.m2162fix: 633 unchanged, 0 field-loss, 1 strict-improvement(
specs/tri/collections/array.t27), 0 unknown.Read that differential with its coverage caveat. The version of
tri diffbinthat produced it counted files neither binary could parse as
unchanged, i.e.as agreement. Measured on another pair, 286 of 616
unchangedfiles were of thatkind. The figure above therefore bounds regressions over the files that were
actually measured, not over the whole library. The fix for that conflation is
#2166 and ships separately, on purpose: a measurement tool and the thing it
measures should not change in the same commit.
The seven negative fixtures matter as much as the six positive ones. Without
them, "accepts 6 of 6" is equally consistent with a parser that accepts nearly
anything in that position.
Requires explicit human approval
bootstrap/stage0/FROZEN_HASHmovescd2822f2...->8ba6585a.... That is aGOLD-RING seal. It is not a mechanical consequence of the patch and must not
be waved through as part of it. If the seal move is not approved, this PR does
not merge.
Deliberately not settled here
Name(T)in type application position. Accepting a declarationform does not imply accepting the use form; needs its own short ADR.
testis a reserved word.Both are left as
needs-language-ADR. Guessing either from a patch would put alanguage decision into a parser diff where no one would think to look for it.
Owner decision now recorded in the issue
An hourly tick independently re-measured #2162 and closed with blocked: waiting
on the language owner while the decision had already been taken — because the
decision existed only in a working session. It is now written into the issue
itself. A decision that is not in the tracker does not exist as far as any
automated consumer is concerned.