Skip to content

docs: settle retain-unknown's four rulings and the enum-bound refusal (#604, #605) - #608

Merged
gafferongames merged 3 commits into
mainfrom
spec-retain-and-bound
Sep 6, 2026
Merged

docs: settle retain-unknown's four rulings and the enum-bound refusal (#604, #605)#608
gafferongames merged 3 commits into
mainfrom
spec-retain-and-bound

Conversation

@gafferongames

@gafferongames gafferongames commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Spec only, no code. The two issues carry the rulings: #604 (retain-unknown) and #605 (the enum-bound array refusal). One paragraph a section below, in the diff's order, so the summary and the diff read side by side. No status line is deleted except the one #603 reported stale, named at the end.

The #606 fence. #605's proposed scope and #606's proposed answer exclude each other on one case, [E.Max]T inside a type a table closure reaches, and that case is the owner's. So this PR states the refusal for the scope both answers share, a table body and a union arm, and states neither answer for the type. The bound's PROVENANCE, [E.Count]T and the folded constant are uncontested and land in full.

SPEC-TABLES.md §2.4 — enum-keyed arrays (#605, #606)

The refusal is restated once on the bound's PROVENANCE rather than on its spelling: a positional array whose bound folds from an enum's Max or Count, directly or through any chain of constants, is refused in a table body and a union arm. Three bullets carry it, the bound, the scope and the diagnostic. A paragraph states the type case rather than deciding it: a type a table reaches rides this wire as a kind 13 body with the positional array under kind 14 inside it today, so the same middle insert shifts every later element and a stored file carries neither the connect gate nor the baseline that would report it, and the two answers that close it, refusing the shape across the closure or keying the wire for an enum-extent array wherever it is declared, are schema#606's. The type-wire paragraph says the spelling stays legal in a type NO table reaches and that a type a table reaches keeps it until #606 rules. A HELD BY TEST block lands beside it, one diagnostics row a shape, red first: the bound's own shapes in a table body ([E.Max]T, [E.Count]T, [N]T under const N = E.Max, and one folded through a second constant), the union arm's shape, the controls that hold the other edge, and a negative control that removes the constant fold from the bound check. The CHECKER STATUS line stays and widens to "in any shape" over the narrowed scope, still owed as schema#540, and one RULING STATUS line lands beside it: the type-held case is ruled on schema#606, and until then a type no table reaches keeps the spelling and a type a table reaches is not refused.

SPEC-TABLES.md §2.9 — unbounded arrays (#604)

The retention paragraph is re-pointed at §6.6's step pair: an element of a []T is the ELEMENT INDEX half of the step, exactly as a fixed or bounded array's element is and as a map entry's slot is under its key order, and the data-driven count is named as the reason the step is a pair at all. The caller-hazard paragraph below it is untouched.

SPEC-TABLES.md §3.2 — enum-keyed arrays on the wire (#605, #606)

One sentence added to the contrast paragraph, referencing §2.4 rather than restating it: the positional array is refused in a table body and a union arm, on the bound's provenance and not its spelling. Whether a type the closure holds can still carry the class in on kind 14 under a kind 13 body, or rides keyed there as every enum-extent array would, is named as schema#606's, because the sentence inverts between the two answers rather than narrowing.

SPEC-TABLES.md §3.3 — the message form (#604)

One clause added to the form-2 SaveRetain refusal, so the new retained-id list does not read as an answer to it: that list names ids into a FILE's trailer, and a form-2 receiver reads slots and shapes from an announcement it never got. The refusal, its two reasons and the two named answers are unchanged.

SPEC-TABLES.md §4.1 — the silent class (#605, #606)

The first REPORTABLE-by-construction bullet is widened from "[E.Max]T is now REFUSED in a table body by name" to the provenance rule over a table body and a union arm, with the two ways the class could otherwise be reopened named: spelling the bound another way, or folding it through a constant. The type-held array is named as #606's, and the bullet says the class is closed under either of its answers, which is the claim §4.1 actually rests on. The count of the silent class does not move.

SPEC-TABLES.md §6.6 — retain-unknown (#604, all four rulings)

Ruling 1: the prose now says SIX EXCLUDED CLASSES and states that the table is the law and its rows are the count. The table itself is unchanged, and the HELD BY TEST list collapses to one row a class, six rows, each class named once with retain_lost pinned at one and retained unmoved; the NODE-INDEX class's row IS the recursive shape (an unknown outer table holding a nested pointer three bodies down, beside an unknown scalar sibling, the whole record dropped and one retain_lost counted), so #575's ruling stands and no longer sits in a second bullet. Ruling 2: a new block, THE RETAINED IDS' STORAGE IS THE CALLER'S, DECLARED BY CAPACITY, mirroring §3.3's resolved-vocabulary shape. The GENERATED ID TABLE is untouched, its capacity and overflow rule standing exactly as they are (TableIds is named once, as its C++ spelling, with PORTING.md as its home), and the retention tail carries its own id list in caller-owned storage declared beside the retained records; the FILE still carries one id table, and the split is the writer's storage rather than the wire's. An ENTRY is the id and the id's own SLOT in the trailer being written, because both stores are numbered into one trailer in merged first-use order and an index into the caller's list is not the number a repeat reference wants; the entry's layout is the port's own, exactly as the retained record's is, and it rides inside TableRetain and spells no name of its own. The list fills as the save walk interns and LoadRetain only clears it, so the overflow is a measure-time and save-time event under one walk. A retained id past the capacity counts one retain_lost and drops its record, and the save is never refused. C / 8 is the COUNT bound and an upper bound no caller meets, since the smallest record is over ten bytes, so a caller that declares C / 8 entries pays the port's entry size for entries the buffer can never fill and one that declares fewer reads the shortfall in retain_lost. The surface sample, the TableRetain bullet, the retain_lost bullet, the security bound's first and third bullets and two new test rows carry it through. Ruling 3: the path step is defined once as a PAIR, the field ordinal in the body the step descends from and the element index inside that field (zero for a scalar body, the element's index for any array, the arm ordinal for a union, the key's slot for a map in ascending key order), with the reason stated (a data-driven count and an arm-indexed ordinal cannot both live in one number), the retention form encoding the pair, the record-layout sentence and the security bound's path term updated, and a test row that puts a map entry, an unbounded element and a union arm in one body. Ruling 4: a new paragraph states that retention is the variable class's and a fixed-class root gets none, LoadRetain on one refused by name in the source the unit does emit rather than as a missing symbol, on §11's own precedent for a surface a class does not carry, the three suffixes still claimed on every closure member, and the conformance rows on POINTERED units, with the fixed class's own row being the refusal. The "Backend status: NOT BUILT" line stays.

SPEC-TABLES.md §11 — refused by name (#605, #606)

The enum-keyed arrays entry is restated on the provenance rule over a table body and a union arm, naming the three spellings and pointing at §2.4 for the scope rather than repeating its wrapper list. Its CHECKER STATUS line stays and widens to "in any shape", still owed as schema#540, with a RULING STATUS clause naming #606 for the type-held case. No name is claimed or unclaimed by this PR: retention's id list rides inside TableRetain and spells nothing of its own, and §6.6 says so.

SPEC-TABLES.md §13.3 — the fixed-class constructs, ruled (#604, #605)

Two references, no new ruling. The optional-fields bullet gains the sentence that a fixed-down-to-root unit gets no retention and that LoadRetain on such a root is refused by name (§6.6). The enum-keyed arrays bullet's narrowing sentence is aligned with §2.4: legal in a type no table reaches, refused in a table body and a union arm, the type a table closure reaches ruled on #606.

SPEC-TABLES.md §20.4 — what moves the build version (#604)

One row in the "does NOT move on" list: retention, in either direction and at either store. It is a runtime feature of the reader and the writer, no byte of §3 moves, no offset and no sizeof moves, and both stores are the caller's own memory, so a build that retains and one that does not hold one build version.

SPEC.md §3.1 — the protocol id's projection (#605, #606, and #603's stale line)

Two references to the refusal are brought to the narrowed scope. The flags-is-the-one-exception paragraph names the table body and the union arm and names #606 for the type. The "what scoping OPENED" bullet is phrased to survive both answers, since its claim is that an enum a table reaches is read by variant name at EVERY site: no positional enum-bound array rides anywhere a table closure reaches, which the refusal answer and the keyed-wire answer each satisfy. Separately, and from #603's report: the stale status clause "Compiler status for that sentence: NO COMMENT KIND IS READ YET" is DELETED, since 02b3fc1 landed the /// doc block, and the sentence it qualified drops its "now" with it.

USAGE.md — enum-keyed arrays (#605, #606)

The teaching page is brought to the same rule, since it stated the narrow one: the refusal follows where the bound comes from rather than how it is spelled, [E.Max]T, [E.Count]T and [N]T under a const N = E.Max all take it, in a table body and a union arm. Its own italic status note stays and widens to all of those shapes, and a second italic note says the type-held case is open on #606 and not refused today.

VERSIONING.md — promise 10, the never-clobber rule, the sharp edges, and #540 (#604, #605, #606)

Four places, each following §6.6 and §2.4 rather than restating them: promise 10 says bounded side STORAGE rather than a buffer and adds that a fixed-class root has no retention; the never-clobber paragraph names the two stores the caller hands LoadRetain; the sharp edge is corrected from "Seven things are still dropped" to SIX CLASSES, with the node-index class stated as the one recursive class it is (kind 17, an array of 17, and any unknown table, union, array or map the walk meets a 17 inside at any depth, the whole record going with it) and the fixed-class root's refusal named; and the reachability sharp edge plus the #540 ledger row are widened from the [E.Max]T table-body refusal to the provenance rule over a table body and a union arm, with #606 named for the type-held case.

Silences met and NOT filled

Each of these is a question the diff runs into that neither issue decides, left open rather than answered:

  1. The BOUNDED spellings. The [E.Max]T refusal follows the bound's provenance and reaches every type a table closure holds (#540, one body away) #605 rules on a POSITIONAL array, so the page states the rule for the positional spelling. Whether [..E.Count]T, or any bounded array whose bound folds from an enum, is in the class is not decided here.
  2. A null id list, or an id capacity of zero. SaveRetain refuses a null REPORT by name (§6.6). Whether a null id list or a zero id capacity is that same misuse refusal, or simply the drop rule taken to its limit, is not decided.
  3. internal/tablewire's side of the id list. The fuzzer's retention leg needs the oracle to retain (§6.6), and whether the oracle carries the caller-owned id list shape or a growable one of its own is left to the implementation PR.

🤖 Generated with Claude Code

rowan-claude and others added 3 commits September 6, 2026 01:09
Retain-unknown (#604): the excluded classes are six with the table as law,
the retention tail carries its own id list in caller-owned storage, a path
step after the first is the pair of a field ordinal and an element index,
and a fixed-class root gets no retention.

The enum-bound array refusal (#605): a positional array whose bound folds
from an enum's Max or Count, directly or through a constant, is refused
anywhere a table closure reaches.

Refs #604, #605.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The enum-bound refusal is stated for a table body and a union arm, the
scope that stands under either answer on schema#606, with a ruling status
line beside §2.4's checker status. The bound's provenance, E.Count and the
folded constant are unchanged.

The retained-id entry is the id and the slot a repeat reference wants, its
layout the port's own, and C/8 is the count bound rather than an exact
sizing. The list fills as the save walk interns and load only clears it.
The generated id table is named once, with TableIds as its C++ spelling.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The projection landed (#603), which carries #524 whole and #540 in part, so
the four conflicts are all one question: what the pages say the checker does
today.

`schema check` now refuses `[E.Max]T` in a table body and in a union arm,
which is what main deleted the "NOT REFUSED YET" lines for. It still accepts
`[E.Count]T` and `[N]T` under a `const N` that folds from either, because
`checkPositionalKeyedSpelling` matches the SPELLING where this branch's rule
follows the bound's PROVENANCE. The merged status lines say exactly that, in
SPEC-TABLES.md §2.4 and §11, in USAGE.md's keyed-array section and in
VERSIONING.md's owed list, and #540 stays owed for the gap alone. The #524
entry goes, landed whole.

The #606 fence stands untouched: the type-held case is stated as ruled and
not refused, and no sentence in §2.4 or §6.6 commits to the closure-wide
refusal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants