tables: was on enum variants, union arms and the fields of a type a table reaches (#442, #478) - #580
Merged
Conversation
R1/R2 are the vocabulary rename pair: an enum variant, a union arm with a payload, a payload-free arm and a field of a type a table reaches by value, each renamed in R2 under was. The checker, baseline, projection, formatter and cross-target tests state what the tree does not do yet: main's parser has no variant qualification, its checker refuses was on an arm as a named follow-on and on a type's field as a packet-wire concept, and its baseline matches variants and arms by name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…able reaches (#442, #478) A variant, an arm and a field of a type a table closure reaches ride the table wire under the hash of their name, so a bare rename orphans every stored value, body and field. Each now takes was, and its wire id is the old name's hash: the enum identity tables, the arm switches, the keyed slots, the vocabulary, the tool's encode and decode, the baseline, the projection and the build version all read the wire name, so a rename under was moves neither id. A flags variant refuses it, because a mask is positional. A was outside a table closure is refused naming the declaration, on the rule a table field's was already follows. The baseline matches variants and arms by wire id, renders was= beside a renamed variant or arm, and refuses a second rename aimed at the intermediate spelling. Every other target refuses a unit that declares one of the three, naming the follow-on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
) SPEC §4.2 and §4.8 carry the attribute on a variant, an arm and a field of a type a table reaches, SPEC-TABLES §2.6, §4.1, §5, §15, §18 and §20 say what each keeps and what a bare rename costs, the versioning page's promise 3 covers every name the table wire carries, and the competition rows flip. R1/R2 join the conformance leg with three pinned instances and three silent report rows, and the C++ test writes the goldens and reads them across. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…#478) r1_cfg, r1_ping and r2_cfg from the reference, r2_cfg byte-identical to r1_cfg, the text of each from the tool with the new names, and the three silent report rows. The union gains a scalar arm so it stays a table-closure construct, which is where a payload-free arm is C++'s. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The enum variant loop binds the wire name to v so the variant-order sabotage still removes the list, and the union-arm-order sabotage names v.WireName() where it named v.Name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nts five A qualified enum variant ends its line, so the stripped R2 gives Argent the comma a bare variant needs. Without was the reader cannot name five things in the golden: the value, the array element holding the same value, the keyed slot, the arm and the type's field. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… state of was The wire-fuzz driver registers tblr1.Cfg and tblr2.Cfg, so the six R seeds are live (127 seeds over 38 roots, 10 absent, 0 divergences), and the cook generator derives its node type ids from the wire name. TUTORIAL.md quotes the diagnostic the checker emits for a type's field outside a closure and says variants and arms take was, COMPETITION.md s9b no longer lists was among what an arm may not take, and SPEC.md's front-end status line says what each line kind does with a tag today: a type's tag is emitted, a table's is gathered and emitted nowhere yet, and an enum variant, a flags variant and a payload-free arm accept a bare tag in silence. Also from the cold read: the unused `_ = inTable` is gone, the format test uses strings.Contains, and the added prose in SPEC.md, USAGE.md, SPEC-TABLES.md, wasrows.go, ir.go, R1.schema and R2.schema carries no em dash. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
rowan-claude
pushed a commit
that referenced
this pull request
Sep 6, 2026
#576 and #580 land `was` on a table declaration, on enum variants and union arms, and on the fields of a `type` a table reaches, and #572 and #573 move beside them. The message form follows: a vocabulary entry's id is the WIRE name's at every place a name becomes one, so an arm, a variant and a table name renamed under `was` keep the slot they had. Main's list work generalized hasMapExtent to hasExtent and TableMapCarve to TableExtentCarve; the message emitters take the general names. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #577, which GitHub closed when its base branch was-rows-1 was deleted by #576's merge; the branch is rebased onto main with #576's commits dropped, the diff unchanged.
The second
wasrow:wason an enum variant and a union arm (#442) and on a field of atypea table closure reaches (#478), in the C++ reference, the tool, the baseline and the pages. Stacked on #576 (basewas-rows-1); it retargets to main when that merges.Red first
Commit 9b21580 holds the tests alone, and the shipped compiler at
45888e55refuses the corpus unit they are written against:Behind the parser, main's checker refused
wason an arm as "a named follow-on" and on atype's field as "a table-wire concept ... a type's wire is positional, so a rename there moves no bit", which #478 found to be wrong for a closure-reached type: its fields have table-wire ids, and the bare rename orphaned stored data in silence.What rides
The grammar. An enum variant takes a qualification section (
Argent | was = "Silver"), and a qualified variant ends its line, so the newline is its separator. A payload-free arm takes one too (pong | was = "ping"), and an arm with a payload carrieswason its field line.schemafmtlines the pipes up and round-trips both.The checker.
wason a variant or an arm is held to the field rule: a quoted string, not empty, not its own name, and an alias colliding with a live id is refused naming both, with the alias shown where the id comes from. Aflagsvariant refuses it by name (a mask is positional, its identity is its bit).wason a variant, an arm or atype's field that NO table closure reaches is refused naming the declaration, the rule a table field'swasalready follows, and a field of a type a closure reaches takes it on the same terms as a table field,json =pairing included.The ids.
Enum.VariantWireName,UnionVariant.WireNameandir.TableFieldWireNameare the one place each name is read, and every derivation reads them: the enum identity tables (TableEnumRef,TableEnumId,TableEnumValue), the arm switches on load and the arm refs on measure and save, the keyed slots of an enum-keyed array, the wire extent walk, the descriptors, the announced vocabulary and the id capacity, the tool's encode and decode, the baseline, the wire projection and the cook projection. A rename underwasmoves neither id (TestVocabularyRenamesUnderWasMoveNeitherId, with the discrimination control: the same rename bare, with atypereaching every declaration, moves both).The baseline matches variants and arms by wire id, as fields are matched, renders
was=beside a renamed variant or arm (only when declared, so the rendering version is unmoved), and refuses a second rename aimed at the intermediate spelling naming the first (TestVocabularyWasChainsAreRefused, with the discrimination control carrying the first wire names forward in silence and the attribution control removing thewas-chainrule). A bare rename of a variant or an arm warns that the old name was removed, and a bare rename of a type's field warns as the rename pair it is.The cross-target refusal.
compiler/wasrows.go: every other backend refuses a unit whose closure carries a variant, arm or type-fieldwas, naming the rows and--lang cpp(TestWasRowsAreCppOnly).The corpus.
test/tables/R1.schema/R2.schemaare the vocabulary rename pair:SilvertoArgent,wardtoshield,pingtopong,Buff.multipliertomult, with an enum-keyed[Grade]int32whoseSilverslot rides under the same id. Three pinned instances (r1_cfg,r1_ping,r2_cfg, the last byte-identical to the first) ride every conformance surface, and three report rows pin the silent cross reads:The tool's text of
r2_cfgkeeps the new names ("grade": "Argent","effect": {"shield": ...},"buff": {"mult": 1.5},"tally": {"Argent": 7}) while its bytes arer1_cfg's, which is thejson =rule applied to a vocabulary:waskeeps the WIRE id and the text key is the declared name.Gates and their negative controls
make tables-wasrows-negative-controlcompilestest/tables/wasrows_control_main.cpptwice, against the shipped R2 and against an R2 with the four attributes stripped and regenerated by the shipped compiler, and reads the samer1_cfggolden through both:Five unknowns, because the golden carries
Silvertwice (thegradevalue andgrades[0]) beside the keyed slot, the arm and the type's field.The Go gates, each with its own controls:
internal/check/wasrows_test.go:TestWasRowRefusals, fourteen refusals by name (a variant'swasnaming itself, empty, unquoted, bare, a valued key a variant does not take, a collision with a live variant, a variant outside a closure, aflagsvariant, an arm'swasnaming itself with and without a payload, an arm collision, an arm outside a closure, a type field outside a closure, and a type-field collision), andTestWasRowsResolvefor what each accepted spelling lands in the IR.internal/baseline/wasrows_test.go:TestVocabularyRenamesUnderWasMoveNothing(silent, rendered, round-tripped, and the bare renames sayvariant Silver removed,arm ward removed,arm ping removed,multiplier removed and mult added) andTestVocabularyWasChainsAreRefused.ir/wasrows_test.go,internal/format/wasrows_test.go,compiler/wasrows_test.go.test/tables/main.cpptest_wasrows_vocabulary: R2 writes R1's bytes, the cross read in both directions lands every renamed name with a byte-identical re-save, and the payload-free arm renamed reads asPong.The zero-cost law (2026-09-05)
This PR adds no counter, reason or descriptor, and no read or write path gains an instruction: every emitter change substitutes the wire name for the declared name in a constant the generated code already carried (
0x...ullin acase, aref, a descriptor column). The committedgenerated/tree, the bench unit included, regenerates byte-identically under this compiler, and the bench leg's binary is the same machine code #576 measured (the sitting there: write and round_trip unchanged within noise, binariescmpclean).Pages
docs/SPEC.md§4.2 (the attribute vocabulary, thewasparagraph, theVariantandUnionVariantproductions), §4.8 (an arm's qualification),docs/SPEC-TABLES.md§2.6 (an arm takeswas, andjsonalone waits), §4.1 (five rows: a type's field, a variant or an arm, each underwasand bare, and the second rename), §5 (variants and arms carry the same attribute, and a closure-reached type's fields), §15 (the compile-time refusals), §18.1 and §18.2 (the rendering and the chain rule), §20.4 (a rename underwasmoves nothing),docs/VERSIONING.mdpromise 3 (every name the table wire carries), the edits table and the group-3 notes, the 3.0.0 list entry gone,docs/USAGE.md(the vocabulary rename, and an arm'swas),docs/COMPETITION.md(the two rename rows move to ✅).#366
The row variant and arm was (#442), type-field was (#478) moves to ✅ in the cpp column. Every other column stays ❌ behind the named refusal.
Silences the page left
json =on an arm stays a named follow-on (§15): an arm's key is its name.flagsvariant takes nowasby ruling, not by omission: the page says why.Gate state
make testwhole on this branch, green: the C++ table tests plain and under ASan/UBSan,make tables-wire-fuzz N=20000on both builds withtblr1.Cfgandtblr2.Cfgregistered in the driver, so the six R seeds are live (wire-fuzz: seed 24845619678, 127 seeds over 38 roots, 10 seeds absent (roots the leg has no codec for), 111515 enumerated + 20000 random = 131515 mutants, 0 divergences, and each R golden replayed through both units, 0 divergences), every wire-fuzz negative control red, the three projection controls (the variant-order and union-arm-order sabotages re-aimed at the wire-name loops, each still red on its own gate and green on the other's),tables-was-negative-controlandtables-wasrows-negative-controlas quoted, the conformance leg (wire 66/66, message 11/11, report 56/56, json-read 62/62, json-write 62/62, json-hostile 125/125, cook 6/6, cook-write 92/92, cook-foreign 6/6, block 2/2, block-foreign 2/2, block-dump 2/2, forgery 11/11, cook-forgery 111/111),go vetclean, gofmt clean, andgo test ./.... The committedgenerated/tree regenerates byte-identically. MacBook Air, sequential, one-minute load under 3 at the start.🤖 Generated with Claude Code