Repository navigation
Pkg11c: lower variant and record fields into declared field identities on the native route, and bind match-arm binders - #12033
Conversation
… prepared-grammar parse
…e frontier for unlowered variant fields Side-chat hold on 318a377: the leading | now has its own production (type_alias_rhs_lead) lowered by sugar to its rhs, so '= | A | B' and '= A | B' normalize to the same provenance-free tree. Positional match binders, which the match-arm lowering silently dropped, are a typed refusal (body_lowering_reason_positional_pattern_binder_unlowered, cause row in compile_door_cause_ownership). Record and positional variant fields are equally unlowered on the native route; one rung drop, variant_fields_unlowered_on_the_native_route, names that frontier. Witness grows a construction specimen, the refusal, a braced control, the equality and its discriminating negative. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… loses nothing and lowers Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ssion/quick-bat-813
… native route (Pkg11c)
One stage, normalize: record declarations rehome onto Named members with their fields declared
(v2.std.node_query DeclaredField); positional and braced variants lower to Disj arms whose payloads
declare their fields ("0" for the positional spelling, per the seed's
parse_positional_variant_type_fields); record literals lower to the construct shape; match-arm
patterns over fielded variants lower to that same construct over their binders, so A(x) binds
field 0 and the #11998 refusal body_lowering_reason_positional_pattern_binder_unlowered is retired.
The resolver scopes a match arm's binders as a lexical frame (resolve_match_node), so a binder
shadowing an outer name binds the arm's value.
Along the chain, pre-existing silent losses repaired at their earliest unjustified boundary: the
first call argument was skipped by a param-list-shaped walk over a bare comma list; int literals
lost their magnitude in two operand readers; a deep literal probe answered f(1)/A(1)/R{v:1} with 1;
the control-form search lowered an inner if in place of the enclosing match; match-arm bodies and
if-conditions were read to their first atom. The type-expression reader now lowers List<T>,
T? and fn types instead of their head atom.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…me lands with the lowering Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…was exponential in nesting depth Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
quick-bat-813 asked whether #12009 changes what symbol_index_global_unique_lookup returns for a bare variant name, because gunbc#12033's pattern classifier reads it. It does, it was my defect, and it is fixed here. symbol_index_bind_at wrote through symbol_index_insert, which calls symbol_index_track_global_bare. That census answers ONE question -- how many DECLARATIONS spell this leaf -- and a transmuted import is not a declaration; it is a second PATH to one that already exists. track_global_bare cannot tell the difference on its own, because its uniqueness test demands `existing_path == qualified_path && existing == resolved`, so an import carrying the IDENTICAL declaration node under a different path flips the leaf to Ambiguous. MEASURED on the emitted route before the fix, not reasoned: with v2.acp_home declaring `AcDisposition` and v2.acp_user importing it, global_bare[AcDisposition] read AMBIGUOUS although exactly one module declares it. The oracle answering "two declarations" about one. WHY IT IS NOT HOUSEKEEPING. The oracle has a live reader. #12033's resolve_pattern_atom_names_constructor asks global_unique_lookup whether a bare atom in a match arm names a constructor and treats Ambiguous as YES, so every leaf this polluted would have pushed a fresh arm BINDER toward being read as a constructor and refused -- and my change widens that population to every imported name in the corpus. Writing a spelling census from a binding is the global-spelling-search defect wearing a different hat, which is the one thing this package exists to remove. New arm a_transmuted_import_does_not_make_its_leaf_globally_ambiguous, and it discriminates in BOTH directions, which is why it names two symbols. `NcrDisp` is declared once and imported once, so UNIQUE can only survive if the binding stayed out of the census. `NcrTwin` is genuinely declared by two modules, so it must stay AMBIGUOUS -- a "fix" that simply stopped writing the census would show up here as a false UNIQUE. Mutation control run: restoring the insert reds the new arm and nothing else. 9/9. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… as a bridge dissolving on #12009 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Derivation read of the resolver scope semantics, as offered. The scope handling is correct. Two notes, and the first is a correction to advice I gave you. 1. My "for the arm BODY only" was wrong, and you were right to ignore it. Taken literally it would break: That exclusion does hold structurally, not just by test: each arm builds 2. The scrutinee/arm split is sound but rests on a non-local invariant that isn't cited. Worth an annotation anyway, because the neighbouring code argues the opposite. Nothing blocking from me. The dissolution note on One thing worth keeping from your control design, which I've now hit three times in this package from the other side: making the outer — sent from witty-cat-84 |
…t instrument instead of its numbers, name the DeclaredField readers' frontier consumer Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Review 69930 (dashboard artifact /api/reviews/69930/artifacts/stdout.log), addressed at ebfb2c2: (1) |
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
resolve_pattern_node_is_constructor classified any Conj as a constructor.
This PR lowers 'data d: R = R { .. }' to the construct shape, so such a data
row became a Conj and a pattern binder spelling its name got no frame local,
leaving the arm body bound to the data row: Accepted, wrong value, no
diagnostic (DESIGN section 5). It also contradicted the rule stated beside it.
Discriminate with construct_tag_optional, the existing authority: Absent for a
declared payload (constructor), Present for a value construct (binder).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… a zero-argument call body_lower_call_args_from_capture and body_lower_postfix_call_suffix_args answered an unreadable argument-list capture with Accepted and no operands: callee kept, every argument dropped, no diagnostic -- the shape call_expression_erased_at_v2_body_lowering records, and the fabricated plausible output DESIGN section 5 forbids. A genuinely empty list is NOT this arm: body_lower_comma_list_items_optional answers Present(Empty) from is_empty_conj_root, so Absent means unreadable. The postfix-suffix site is unreachable for a zero-argument call today -- measured by flipping it to a refusal and observing the zero-arg specimen lower at identical eval steps (130423) -- and refuses anyway, because an unreachable refusal costs nothing and an unreachable widen bites when the grammar moves. Both carry body_lowering_reason_call_args_unlowered with a CauseOwnership row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ledger-Repair-Judged: docs/design-rung-drops.md Ledger-Rows-Repaired: docs/design-rung-drops.md variant_fields_unlowered_on_the_native_route Heal-Candidate-Run: 35696325720
…16/16 verified) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ion/quick-bat-813
v2.test.parse.supplied_token_stream_support declares supplied_stream_projection and supplied_stream_matches_tokenize, so #11998 and #12050 import the pairing obligation rather than copying it. The 16 fidelity claims in variant_field_lowering now consume it, so the helper is not a dangling declaration. Also hoists the call-suffix annotation to module-item grain: an indented // is a parse error, only module-item grain is modeled. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
17 specimens, each with a verified fidelity claim through supplied_token_stream_support. All 34 claims PASS. Declaration-shape claims now fit the floor budget (52k-60k, from 73k-87k); claims whose specimen carries a construction or a match remain over, because a fn body costs ~80k on top of the 51.7k supplied route and no supply point removes it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… the ladder Two findings, both confirmed. 1. The claim module imported and matched PostfixChainStepSuffixUnlowered, which commit 7c05975 deleted. That module carries this package's discriminating reds, so the evidence the receipt leans on could not resolve at head. The native lane was BLIND to it -- the module is in neither refusal set -- and the half-2 run predates the deletion. 2. Declining the unlowered suffix kinds silently would move the class from mitigatable to BELOW the ladder: on base they reached a loud located refusal (the deleted postfix_field_access_refused), and DESIGN 4b forbids silent wrongness outright rather than admitting it as a declared drop. The withdrawal measurement only ran base-accepts->head-refuses; the converse is 8 of the 42 base-refusing files carrying a brace suffix. The door now declines AND emits body_lowering_reason_postfix_suffix_carried at HEAD grain, rostered, mirroring #12033's value_carried_unlowered: the file still Accepts, so nothing that accepts on base regresses, and the residue is located and counted instead of vanishing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lize per claim The content_hash claim ran the whole ~50-line module. Its subject is the positional-binder construct that refused, so the declarations it never reads were over-specification. Trimmed to a verbatim excerpt of the implicated code (real type names, real binder spelling Fnv1a64(left_structural)); the fn body is reduced to the arms that carry the binders, which the row states rather than claiming whole-file fidelity. vfs_fidelity_15 189852 -> 57670 (now fits) content_hash 650671 -> 148874 (over for the fn-body reason, not size) Claims now derive every assertion from one normalized tree. Measured: that bought 517 steps, because the evaluator already shares identical pure calls (filled-shared-artifact). Kept for clarity, not claimed as a cost win. Over-budget set is now 10, single-caused: every member carries a fn body. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The return-type probes are handed to clever-seal-575 for enrolment in the repair PR, where the red belongs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…'s claims, carried here) These 12 rows are #11998 authorship. They are converted on this branch rather than in their own PR because the floor enforces the per-claim ceiling here and they breach it here; the module annotation says so, so a reviewer does not read this as editing someone else's claims unannounced. 12 specimens, each with a fidelity claim. 24 PASS, 0 FAIL, over-budget 7 -> 4. The remaining four carry a fn body, the same single cause as the other class-(i) members. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ay why it stays The DISSOLVE-ON gunbc#12009 marker named a PR that closed unmerged. Its content landed via gunbc#12048, so the trigger has fired. But deleting the branch would turn an unimported constructor spelling into a silent catch-all binder. The branch only ever decides between binder and refusal (a constructor off the chain refuses as unbound, with the declared-elsewhere advisory), which is the oracle role namespace-resolution-design section 13 keeps. The comment now states that, the over-prohibition it costs, and the refusal that would retire it. No behaviour change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbc#12082's mab_unimported_constructor_in_a_pattern_refuses_rather_than_binding (nimble-boar-636) executes the path the comment previously stated from reading, and locates it at the GlobalBareLookupAmbiguous arm. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # docs/design-rung-drops.md # src/v2/workflow/floor_eval_step_cost_drop.dag
Ledger-Repair-Judged: docs/design-rung-drops.md Ledger-Rows-Repaired: docs/design-rung-drops.md sha256_span_program_serialize_new_witness_eval_step_cost Heal-Candidate-Run: 35806810766
…this class network_switch_catalog_witness_test: binding Bandwidth makes rates_contain's parameter type live, exposing an Optional passed as a List at the arista test (test logic, not an import). compile_door_ledger_ownership_test: touching it seeds the floor with a closure that reaches v2.lens.reference_deps, whose bare Outcome/Accepted fail typing there (#12033 territory). Both reported in the PR. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… call-argument path #12108 is now main's authority for call-argument lowering. Resolution: - body_lowering_fold: main's call-argument reader (body_lower_call_args_from_capture -> body_lower_call_arg_value), dotted-chain Outcome, and call-suffix detection (body_lower_postfix_call_suffix_args) are taken. Dropped as superseded: my body_lower_call_arg_edge_optional, body_lower_arg_lowered, body_lower_arg_value_capture, body_lower_call_suffix_is_paren_list, and the cause body_lowering_reason_call_args_unlowered with its CauseOwnership row. - Kept, because field initializers, match-arm bodies and if conditions still need them: body_lower_value_lowered, body_lower_value_lowered_or_carried and three helpers. Call arguments no longer go through them. - My comma-list reader (field decls/patterns/initializers, positional patterns, fn-type domains) is now fail-closed in its repeat walk: it answered Empty for an unreadable element or tail, truncating silently. Its duplication with #12108's call-arg shape walk is stated as a follow-up. - Tests: main's body_lowering_qualified_path_test and call_argument_mention_survival_test taken whole; mine only adapted them to the call path now replaced. - docs/design-rung-drops.md: our side kept per the generated-artifact driver; heal regenerates. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
neat-boar-16: a follow-up would only record a fork. All six consumers (call arguments, field declarations, field patterns, field initializers, positional patterns, fn-type domains) read dag_grammar_comma_list_expr, so they read it through one walker. - body_lower_comma_list_items / _repeat_items / _child: #12108's walk, answering raw item captures, with the shape refusal cause passed in. Call arguments pass body_lowering_reason_call_argument_list_shape_unread, unchanged. - body_lower_call_args_from_capture = walk, then body_lower_call_arg_values lowers each item in order (first failure refuses). The only observable difference: with a shape defect after an unreadable argument, the shape refusal now reports first. - body_lower_comma_list_items_optional is now a thin adapter (Rejected -> Absent) for callers that refuse under their own cause. My walker (and its lenient single-item arms) is deleted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Taking main's side hunk-by-hunk left my closing braces for a nesting level main does not have. The floor refused body_lowering_fold as unparseable at the first one, in body_lower_try_body_lowered. A per-function audit (each function equals main's, equals mine, or is intentional) found five mixed functions: - body_lower_try_body_lowered, body_lower_postfix_call_suffix_args, body_lower_dotted_chain_lowered_optional: main's versions whole. Mine only adapted them to the call path #12108 replaces. The resolved dotted-chain function had also lost main's call-element arm. - body_lower_postfix_expr: main's plus my body_lower_postfix_lowered_primary_optional wrapper. - body_lower_primary_expr: main's plus my int-literal-with-magnitude reader and record-literal wrapper. Every item now starts at depth 0, and no main change is dropped. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Ledger-Repair-Judged: docs/design-rung-drops.md Ledger-Rows-Repaired: docs/design-rung-drops.md interpreter_purity_gate_reads_authored_uses_only Heal-Candidate-Run: 35822152955
emit-build on e312838 refused at emission: body_lower_postfix_suffix_read called body_lower_call_suffix_is_paren_list, which #12033's resolution of #12108 deleted. The merge kept the call because it sat in a non-conflicting hunk. body_lower_postfix_call_suffix_args already answers the same question (Present exactly when the suffix opens with a paren), so the read matches on it. A malformed argument list still classifies as a call, so the fold reaches the reader and refuses located. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…match resolution onto P4's walk P4 is main's authority for how resolve walks and accumulates: every recursive step returns ResolveNodeWalk and refusals are occurrence-complete. - Dropped: resolve_match_node / resolve_match_arm / resolve_pattern_node / resolve_pattern_field_target (Outcome, stop at first Rejected), and the EdgeResolveAccMk accumulator they used. - Ported: resolve_match_node_walk / resolve_match_arm_walk / resolve_pattern_node_walk / resolve_pattern_field_target_walk, on child_walk_init / child_walk_step / child_walk_node. Each independent refusal under a match is its own chain, in walk order. The arm frame's binders come from syntax plus the symbol index before the fold, so a refused pattern never leaves the body under an invented scope. - Unchanged: the binder classifier (resolve_pattern_binders, resolve_pattern_atom_names_constructor and its payload predicates). - resolve_node_walk is main's plus one arm: Match -> resolve_match_node_walk. - Three comment citations follow the rename. docs/design-rung-drops.md keeps our side per the generated-artifact driver. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ojection The generated-artifact merge driver keeps our side of docs/design-rung-drops.md on a merge, and main's #11981 had added the section for app_attest_interpreted_crypto_new_witness_eval_step_cost, which is still rostered. The branch copy therefore dropped a live drop from the page until heal regenerated it. The doc now equals main's plus this PR's one section (variant_fields_unlowered_on_the_native_route), with nothing removed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t remains Review 70454 quoted the row's 'the emitted compiler does not build today'. That was true when the row was declared and has been false since gunbc#12089 (emit-build is green on this head). The row and its projected population now say the native-route run over the specimen is what is outstanding, deferred until gunbc#12100. The trigger is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…head for every shared file, so the Pkg16 side (#12033 + the classifier, record capture, claims and producer rows) is kept Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…kg11b batch 2 Kept vs superseded, per conflicted path: - rung_drop roster, languages/dag.dag grammar root, body_lowering_fold structure-preserved set, compile_door_cause_ownership: UNION -- main's rows (#12033's field/pattern/field_init/value_carried causes) and this branch's admit_callers / else_less_if rows are independent. - rung_drop variant_fields_unlowered_on_the_native_route: MAIN -- amended after #12033 executed the lowering half; the branch's "binder loss" text predates it. - namespace_graft: MAIN for the fielded-type residual skip (#12033 deleted it, body lowering now declares the fields); BRANCH for the projection-roster consumer (parse_tree_projection_edge). - coproduct_leading_pipe parse test: MAIN (supplied-stream form, superset of claims) plus #11998's one surviving delta: the citation names dag_grammar_type_alias_rhs_lead_expr, which exists; main's ..._after_eq_expr does not. - docs/design-rung-drops.md: generated; left for heal to re-derive. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…o over supplied streams - compile_door_cause_ownership: the union resolution joined the else_less_if row and #12033's field_decl row inside one record (duplicate `cause`), which is the floor and emit-build red on 44a910c. Two records again. - closure_parse_batch_two imported cp_normalized/cp_parses(text:) from the coproduct test; main converted that module to supplied token streams, so the import no longer resolved. The stream -> parse -> normalize route now lives once in v2.test.parse.supplied_token_stream_support (supplied_stream_parse / supplied_stream_normalize); the coproduct test drops its local copy and imports it, and batch two supplies a tokenizer-dumped stream per specimen with a cb2_fidelity_* claim against the real tokenizer, instead of importing from another test module. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pkg11c. On the native route no v2 stage lowered a variant or record field:
type C = A(Int) | BkeptA(Int)as a preservedpositional_variant_payloadshell, records went to the graft's residual forest, and a match pattern kept only its constructor atom —A(x) => xlostx. This lands the field model in std and ONE stage that lowers declarations, constructions and eliminations through it, plus the resolver frame that binds the arm's binders.Base and landing order
#11998 is OPEN, not merged.
dbaf88c9adbis onsession/gentle-tern-521, not on main, so this branch CARRIES #11998's commits rather than building on landed work. Landing order set by the manager: #11998 first, then this. If #11998 lands first, its commits collapse in the squash; confirm the diff against main then contains only this lane's work.Two of the converted witness modules are #11998 authorship carried here — the coproduct rows — converted on this branch because the floor enforces the per-claim ceiling here and they breach it here. The module annotation says so, so this is not editing someone else's claims unannounced.
What this does NOT close — read this before the rung-drop row
An earlier revision of this body claimed this PR closes the standing HOLD on #11998 and retires
variant_fields_unlowered_on_the_native_route. That claim is withdrawn and the row STANDS, narrowed to one residue.Both halves of the trigger execute: the lowering (
body_lower_type_variant,body_lower_record_type_decl,body_lower_pattern_lowered) and the resolver frame (resolve_match_node). What is not established is the trigger's own sentence — that a binder binds — because one reachable class still silently does not.resolve_pattern_node_is_constructordecides binder-versus-constructor by SHAPE, and a module container is aConjwhose every child is a Named edge, which is what a declared payload looks like to every shape test tried. Specimen in this tree, from review 70101:src/v2/std/symbol_index.dag:156isPresent { value: node } =>insidev2.std.symbol_index, and lexical ascent reaches the MODULEv2.std.node. The binder gets no frame local and the arm body'snoderesolves to the module — Accepted, wrong value, no diagnostic.Three shape discriminators have now been tried on that predicate — the bare connective, the construct tag, and declared-field coverage — each admitting a wider set than its author intended. The fact it needs is what the NAME DENOTES, which
symbol_index_fillholds at insert time and the node does not carry. A fourth shape predicate is refused on principle; the declaration-kind repair is filed as its own subject and is the row's restoration trigger.Evidence: the witnesses were reworked, and why
The floor's per-claim ceiling (72300 eval steps) refused 28 identities on this branch. This is not 28 badly-written claims — #11829 armed the receipt adjudicator at 01:58Z, and before that a refused floor could report SUCCESS. The witnesses were always over; the lane could not fail.
The remedy is DESIGN §3: supply the inputs at the boundary the claim discriminates at. Measured decomposition (eval steps are host-independent — the same probe reads 3814 on the amd64 runner and on an arm64 container, so these compare directly to the CI ceiling):
dag_prepared_grammar()aloneThe first plan was falsified by its own measurement. I proposed supplying a prepared grammar; at 3,814 of a 72,716 route that saves 5% and fixes nothing. Supplying the TOKEN STREAM is the cut: flat and authorable where a parse tree is not, and it keeps parse AND normalize executing, so a claim whose subject includes parsing keeps its subject with no carve-out.
The pairing obligation is discharged by proven equality, not assertion. Each specimen owes a fidelity claim that its supplied stream EQUALS what the real tokenizer emits (
v2.test.parse.supplied_token_stream_support,supplied_stream_matches_tokenize). Real tokenize, real parse and real normalize all still execute; delete the integration and the fidelity claim reddens. The stream generator is deliberately NOT committed and NOT shared: a generator cannot produce a wrong green, because every stream is checked per specimen against the real tokenizer in the floor. The generator is a convenience; the fidelity claim is the wall.Result:
variant_field_lowering34 PASS / 0 FAIL, over-budget 28 → 10. Coproduct module 24 PASS / 0 FAIL, 7 → 4.Two dispositions for what remains, and they are different in kind
Resolve-subject witnesses are ENROLLED, not declared.
dag_language_model()alone costs 90,028 against a 72,300 ceiling — a claim that only calls it is already over. But enrolment removes eval steps, not merely CPU (required_floor_runner:eval_steps = measured − fill, and the gate compares that), so declaring these would have been debt over a remedy that exists. The model itself cannot be shared — run 35327371663 refused exactly that,ServeCacheValueNotPortable at .value.lm.canonical_symbols.member kind=Closure— so these take the seamfloor_pure_producer_sharealready uses for resolve subjects: the resolvedOutcome<Node>per subject, nullary, pure, over an inline source, no closure. Ten producers rostered WARM. Post-enrolment marginals are not measurable locally (claim_batchdoes not apply the roster); they come from the floor's[floor-shared-fill]ledger and are reported from it, not assumed.Fn-body witnesses are DECLARED (
normalize_fn_body_witnesses_over_the_floor_ceiling, 14 identities enumerated by name). Parse ALONE of a fn-bearing specimen is 61,931, so even supplying a parse tree leaves ~71,369 against 72,300 — the best case anyone can construct has a 931-step margin, and there is no boundary left to supply from. Separate rows because their TRIGGERS differ: one retires if the fn-body charge drops, the other does not, and a single row would be retired by a trigger fixing half its population.One arm is deliberately weaker, and it does not stay
body_lower_postfix_call_suffix_argscurrently ACCEPTS an absent call-suffix interior as zero arguments. gunbc#12057 established that the real prepared-grammar parse of()emitsseq(lparen, seq(<empty conj>, rparen))— an empty interior, not an absent one — so that arm is unreachable from source, and accepting is a widen on an unreachable path.It is there for one reason, stated rather than left to be found: three enrolled claims in that lane's module reach the door through a hand-built
seq(lparen, rparen)and were failing on this branch. That fixture is corrected on their branch. The agreed order is theirs first, then this arm reverts to refusing — so main never carries the widen.The asymmetry is the general rule, not a local preference: an unreachable REFUSAL costs nothing, an unreachable WIDEN bites when the grammar moves. When a case cannot be reached, refuse it, because the cost of being wrong differs by direction. Three separate defects in this slice were instances of the same conflation — an absent slot read as an unreadable one, no arguments read as unreadable arguments, and a probe that found nothing read as a probe that found something false — and each was a two-valued answer collapsing "could not look" into "looked and it is false".
Reviews
body_lowering_reason_call_args_unloweredand aCauseOwnershiprow. The postfix arm is unreachable today, measured by flipping it to a refusal and observing the zero-arg specimen lower at identical eval steps.mab_binder_spelling_a_sibling_module_binds_the_armlater showed the binder binds. The harm is retracted (see the landing-order section below).payload_is_nullary, a std predicate only a test called): deleted; the test checks the Conj shape inline.Two of the brief's DONE criteria are UNMEASURABLE at any current head
The brief asks that
content_hashparse and lower natively, and that the workload closure lose that file refusal with the counts re-run. Neither can be measured on any tree based on current main, and the cause is not in this diff:The first such break was base16
UInt8, repaired by gunbc#12056 (merged 2026-09-22). Emitted-compiler builds now stop at the NEXT break,error[E0573]: expected type, found variant PointerWidthinsrc/std_integer.rs, which is on main without this diff. gunbc#12091 repairs it. These two criteria follow that landing and are not outstanding work in this PR; the rung drop's trigger names the same capability (the emitted compiler builds). Five dispatches were needed to establish this, each refusing for a different reason —GITHUB_SHAunset, a stale probe-root lock,HostBudgetUnreadable, an unbound cgroup, and finally main — and the fifth is the first to reach the real failure. None of them produced a number.The floor needed a second declaration, and a rung-drop row is not it
A
gunbc.rung_droprow declares the rung consequence. It does not stop the floor enforcing the eval-step budget — that isv2.workflow.floor_eval_step_cost_drop, whose members stay planned, executed and measured, with only the overrun reported asEvalStepsOverBudgetUnderDeclaredDropinstead of refusing. Both declarations are needed and they answer different questions; without the second this PR could not have gone green however well the row was written.Two properties worth reading rather than assuming:
Review 70149 is an argument for removing the bare-spelling fallback, not just a fixed import
03_resolvecalleddeclared_field_listwhile its import block omitted it, so the symbol resolved only through the corpus-wide bare-spelling fallback — the mechanism this same PR annotates as a bridge dissolving on gunbc#12009. A new fail-closed predicate's own dependency was being carried by the thing being retired. Every typecheck was green and silent about it, because a.dagimport list does not bind. Fixed, but the instructive part is that the fallback can carry a dependency nobody declared and nothing complains.No rung drop is taken: the declared population dissolved
An earlier revision of this PR declared fourteen fn-body witnesses over the eval-step ceiling and rostered them in
floor_eval_step_cost_drop. That row is deleted and the roster is back to its pre-PR membership. Review 70176 was right that taking a debt row while the remedy sits in the same diff is what §5 forbids — and the argument was already written into the row's own trigger, which called shared-producer enrolment "a measured capability and not a hope" and said the same move "would DISSOLVE this row".Every parse/normalize specimen is now a nullary shared pure producer rostered WARM in
floor_pure_producer_share: 17 invariant_field_lowering, 24 in the coproduct module, beside the 10 already there formatch_arm_binder_frame. Parse and normalize are attributed to preparation's fill; each claim is charged only its marginal assertions.The coproduct module keeps separate parsed and normalized producers, deliberately: handing a normalized tree to a claim whose subject is parsing would delete what it establishes. A parse-subject claim reads the parsed producer and keeps parse in its subject.
Exact head
e15f571fcfe27ab98a7b39b731d0bf5790723a78(git rev-parse HEADat the time of writing). Figures in sections written earlier are attributed to the tree named there (b1c442732c1). Later commits change only annotations, the rung-drop row, its projection, and the removal ofpayload_is_nullary.Known-red and not mine
unbound_statement_prefix_refuses_holdsanddata_record_fn_literal_field_is_not_retained_holdsfail identically on main without this diff — verified independently by this lane and by gunbc#12062's author.Integration with #12116 (XL-2 P4, occurrence-complete resolve): what was dropped, what survives
P4 is main's authority for how resolve walks and accumulates. The merge (
e9c8ebd63b1) ports this PR's match resolution onto it and does not restore the pre-P4 accumulation.Dropped:
resolve_match_node,resolve_match_arm,resolve_pattern_node,resolve_pattern_field_target. They returnedOutcome<Node>, stopped at the firstRejected, and threaded their ownEdgeResolveAccMkaccumulator.Ported:
resolve_match_node_walk,resolve_match_arm_walk,resolve_pattern_node_walk,resolve_pattern_field_target_walk, on P4'schild_walk_init/child_walk_step/child_walk_node. Every independent refusal under a match (a misspelled tag in one arm, an unbound reference in another arm's body) is now its own chain in walk order, and the match is never accepted while a chain stands. The arm frame's binders are computed from the pattern's syntax plus the symbol index before the fold, so a refused pattern never leaves its body under an invented scope. Nothing here is P4'sObservationIncompletecase, whose one exception is a Bind's binder.resolve_node_walkis main's plus one arm:Match -> resolve_match_node_walk.Unchanged: the binder classifier (
resolve_pattern_binders,resolve_pattern_atom_names_constructor,resolve_pattern_node_is_constructor/_is_declared_payload).Receipt for P4's and #12108's claims at
e9c8ebd63b1(run by neat-boar-16 on srv2:claim_batchrebuilt from that tree, systemd scopeMemoryMax=14G, private TMPDIR). 47/47 PASS, 0 fail, exit 0. These modules are outside the required gate, so the floor does not plan them.resolve/resolve_walk_population7/7,native_census/whole_tree_census_resolve9/9namespace_xl0/call_argument_mention_survival19/19,body_lowering_qualified_path12/12Integration with #12108 (call arguments): what was dropped, what survives
#12108 landed on main and is now the authority for call-argument lowering. The merge (
32f8bcbba2c) adopts its path; nothing here routes call arguments beside it (§3).Dropped as superseded by #12108:
body_lower_call_arg_edge_optional,body_lower_arg_lowered,body_lower_arg_value_capture: my per-argument readers. XL-2 P1: call arguments survive v2 body lowering and reach the resolve denominator #12108'sbody_lower_call_arg_valuedecides named vs positional by shape and refuses what it can't read.body_lower_call_suffix_is_paren_list: my call-suffix detector. Main decides it bybody_lower_postfix_call_suffix_argsbeingPresent, and both callers are back to main's form.body_lowering_reason_call_args_unloweredand itsCauseOwnershiprow: XL-2 P1: call arguments survive v2 body lowering and reach the resolve denominator #12108's…_call_argument_list_shape_unread/…_call_argument_unreadcover the same sites.body_lowering_qualified_path_testandnamespace_xl0/call_argument_mention_survival_test: they only adapted those tests to my call path. Main's versions are taken whole, and they add a positional-argument twin.Survives, with the reason:
body_lower_value_lowered/body_lower_value_lowered_or_carried(+…_lowered_behind_shells_optional,…_node_is_lowered_atom,…_value_is_function_value_shell): variant-field work still needs them for record-literal field initializers, match-arm bodies andifconditions. Call arguments no longer go through them. XL-2 P1: call arguments survive v2 body lowering and reach the resolve denominator #12108's per-argument read usesbody_lower_operand_ref_optional, which narrows a sequence operand to its head (lowering_accessor_collapses_a_sequence_operand, its own trigger), so the two are not equivalent. Routing arguments through the full value reader is that failure mode's repair, not this PR's.body_lowering_reason_value_carried_unlowered: its trigger text now names field values, match-arm bodies and conditions, not call arguments.dag_grammar_comma_list_expr: call arguments, field declarations, field patterns, field initializers, positional patterns and fn-type domains. XL-2 P1: call arguments survive v2 body lowering and reach the resolve denominator #12108's walk is factored out asbody_lower_comma_list_items(+_repeat_items,_child). It answers the raw item captures, and the caller passes in the shape refusal cause. Call arguments pass…_call_argument_list_shape_unread, unchanged, and thenbody_lower_call_arg_valueslowers each item in order. My own walker is deleted. It had also silently answeredEmptyfor an unreadable repeat element, a §5 widen, which goes with it. The field/pattern callers usebody_lower_comma_list_items_optional, now a thin adapter (Rejected→Absent), and each already refuses onAbsentunder its own cause. One observable change to call arguments: if a list has a shape defect after an unreadable argument, the shape refusal now reports first (before, the argument did). Both are refusals.Receipt for #12108's own claims on this integration (run by neat-boar-16 on srv2 at
59ca6392dd0; head4cd4a45b243adds only the healeddocs/design-rung-drops.md+4, verified). These modules are outside the required gate and byte-identical to main, so the floor does not plan them, but the walker they depend on was refactored here.claim_batchbuilt from that tree, undersystemd-run --user --scope MemoryMax=14G, private TMPDIR:namespace_xl0/call_argument_mention_survival_test.dag: 19/19 PASSbody_lowering_qualified_path_test.dag: 12/12 PASSFloor at
59ca6392dd0: FloorClean, 493 planned and executed, 0 claims failed.variant_field_lowering34,match_arm_binder_frame7 andcoproduct_leading_pipe24 all planned-and-passed.The generated
docs/design-rung-drops.mdkeeps our side per its merge driver; heal regenerates it.Landing order: pairing with #12082 WITHDRAWN (review 70260)
Review 70260 flagged a silent residue: the binder
nodeinv2.std.symbol_indexsymbol_index_candidates_atwas said to resolve to the MODULEv2.std.node. On that premise, #12033 was briefly paired with #12082. The premise was falsified by execution. nimble-boar-636 ranv2.test.claim.match_arm_binder_framemab_binder_spelling_a_sibling_module_binds_the_arm(authored on #12082) against this PR's headbbc77204a49, and the binder binds.symbol_index_fillinserts no module path, and the global-bare Unbound arm refuses rather than falling back. The harm sentence came from review 70101's reading and I never executed it; it is retracted in the row and in the classifier note (9278606126a).Decision (neat-boar-16): the pairing is dropped. #12033 lands on its own; #12082 follows as construction with no behavioural delta. The rung drop
variant_fields_unlowered_on_the_native_routestays. Its loss is now stated as the native route being unobserved (the emitted compiler does not build). Its trigger names that capability: the emitted compiler builds and the specimen binder is observed binding there. The declaration-kind classifier does not satisfy it.🤖 Generated with Claude Code