Skip to content

D4: single-arm match, field named match, data-initializer lambdas - #10967

Merged
briansrls merged 13 commits into
mainfrom
d4-body-lowering-match-arm
Sep 11, 2026
Merged

briansrls merged 13 commits into
mainfrom
d4-body-lowering-match-arm

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Three independently evidenced roots in body_lowering_fold.

Freeze head: 49dff28 (rebased onto main 267d69b). The merge conflict was body_lowering_fold.dag (D1 where-refinements vs Cause R structure-preserve). Resolution kept both: where_refinement_clause / where_predicate and arrow_lambda / fn_literal. That is closure source, so the aaaad15 srv2 receipt does not carry to this head; retake after G at the landing head. Land last: #10988 -> D2 -> D3 -> D1 -> G -> D4. P0a.1 is not a prerequisite. The required-v2-native CI job is deleted (#11003). Required CI is build + floor. The srv2 167-member closure receipt bound to the landing head is the required native semantic evidence; retake it after G lands. That receipt establishes the compiler closure and this PR's cause-relative delta, not whole-v2.test.* native admission.

  • D body_lower_match_arm_repeat_elem_is_separator — empty comma-list Repeat tail is skipped before extract. Pipeline-path control: zero-arm match (t) { } still refuses body_lowering_reason_match_arm_navigation_refused; one-arm wildcard/constructor/shorthand accept (two-arm and three-arm stay green). Closure 7/7 RefusalCleared (prior receipt): src/v2/compiler/05_eval.dag, src/v2/extdeps/runtimes/v2_effect_io_pure.dag, src/v2/lens/machine_shape.dag, src/v2/lens/undecidable_verdict_collapse.dag, src/v2/std/cardinality.dag, src/v2/std/integer.dag, src/v2/std/model_core.dag.
  • F body_lower_match_spine_has_lbrace — identity-absent kw_match sniff requires a following lbrace. Pipeline-path: r.match and a.match == b.match accept; r.other, r.loop, r.type already accepted. Closure: src/v2/std/runtime.dag AdvancedToNewCause normalize_reason_post_normalize_not_well_formed (cause 19, not this PR).
  • R body_lower_is_structure_preserved_emitted — dag_surface_arrow_lambda and dag_surface_fn_literal preserved after the child fold. Pipeline-path: data-field (a, b) => a and fn(a, b) { a } accept; the same literal in an fn body already accepted. Closure: src/v2/std/logic.dag RefusalCleared.

Restored pipeline-path claims and floor_cost_debt

Skip/sniff/structure replacements are deleted. Witnesses are again tokenize/parse/normalize (plus the F sniff that already completed). After review 63375: proven_chunk_21 is deleted. Eleven identities interrupted_before_verdict on the floor of record (job 103096598642 / run 34534985281, 501-520ms) are on floor_cost_debt_censored_chunk_03. That asserts they did not finish inside the ceiling on the charged clock; own-work cost is unmeasured. Restoration is a measured fill-netted marginal under 500ms on that floor. A local claim_batch PASS is an observation, not the roster's fact. Left off because they produced verdicts: zero-arm refusal, F sniff, and both Cause R data-record discriminators.

Claim Path floor_cost_debt Why
single_arm_wildcard_match_normalizes_holds tokenize/parse/normalize censored_chunk_03 interrupted_before_verdict 501-520ms on floor of record
two_arm_match_still_accepts_holds tokenize/parse/normalize censored_chunk_03 same
three_arm_match_is_not_parity_holds tokenize/parse/normalize censored_chunk_03 same
one_arm_constructor_pattern_normalizes_holds tokenize/parse/normalize censored_chunk_03 same
one_arm_shorthand_field_pattern_normalizes_holds tokenize/parse/normalize censored_chunk_03 same
zero_arm_match_still_refuses_holds tokenize/parse/normalize, fixture match (t) { } no produced a verdict; enrolled RED on the ordinary floor
field_named_match_normalizes_holds tokenize/parse/normalize censored_chunk_03 interrupted_before_verdict 501-520ms on floor of record
infix_field_named_match_normalizes_holds tokenize/parse/normalize censored_chunk_03 same
field_renamed_other_control_holds tokenize/parse/normalize censored_chunk_03 same
field_named_loop_control_holds tokenize/parse/normalize censored_chunk_03 same
field_named_type_control_holds tokenize/parse/normalize censored_chunk_03 same
match_keyword_after_dot_is_not_a_match_expr_sniff_holds synthetic spine via body_lowering_match_field_name_spine_does_not_sniff_holds (was already on e268ca2, not a 092689f replacement) no produced a verdict; ordinary-floor F control
data_record_arrow_lambda_field_is_not_retained_holds tokenize/parse/normalize no produced a verdict; ordinary-floor R discriminator
data_record_fn_literal_field_is_not_retained_holds tokenize/parse/normalize no produced a verdict; ordinary-floor R discriminator
lambda_in_fn_body_control_holds tokenize/parse/normalize censored_chunk_03 interrupted_before_verdict 501-520ms on floor of record

Observation (not this PR)

match t { } is parsed as a record literal on the ident scrutinee t, so an empty match-arm list is not a parse of that spelling. Reproducer: module m.t, type T = A | B, fn f(t: T) -> Int { match t { } } — normalize_outcome is PARSE_REFUSED. Control: match t { _ => 1 } in the same module ACCEPTED. Discriminator: match (t) { } reaches body-lowering and refuses body_lowering_reason_match_arm_navigation_refused. Candidate row for a later parse/grammar lane (D1 owns dag.dag); not part of #10967.

Test plan

  • Pipeline-path witnesses restored; synthetics deleted; eleven interrupted siblings on censored_chunk_03; R discriminators, zero-arm, and F sniff off the roster.
  • Zero-arm fixture match (t) { } enrolled on the floor.
  • srv2 167-member closure receipt bound to the landing head (retake after G lands).
  • Exact-head review + build+floor green; land last after Cut the seed out of the emitted closure's dependency graph #10988, D2, D3, D1, G.

@gunbai-bot
gunbai-bot Bot marked this pull request as draft September 10, 2026 20:28
@gunbai-bot

gunbai-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

review 63228 is right: body_lower_match_arm_list_tail_exhausted was a second reader of the arm-list, and default-true plus discarding extract's Rejected could lower a match with a silently dropped arm.

The tail walk now skips only before extract, and only for empty Conj, a comma atom, Optional whose element skips, or a sequence whose both sides skip. A navigate-emitted production with no captured child (and any other unknown shape) is not a skip; extract's body_lowering_reason_match_arm_navigation_refused stands.

Head is now 3a52a25. PR stays draft for the srv2 closure receipt on this sha.

— sent from gentle-pike-522

@gunbai-bot

gunbai-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Closure before/after for #10967 at head e6171ced71 (main merged), overlaid on main 0006e0aabd (PR diff from merge-base applied, no unmerged paths; srv2, emitted driver over the 167-member closure root, controls-only universe, {"_terminal":"complete","file_refusals":60}, wall 259 s, rss 0.9 GB). Baseline: the #10943 carrier at 2fb67b411a, 68 refusals.

68 → 60. Zero regressions: no previously accepted module refuses; no file outside the attributed set changed cause; nothing moved to an earlier stage. Per mechanism, using the carrier's attribution:

mechanism carrier files outcome
D single-arm match (body_lowering_reason_match_arm_navigation_refused) src/v2/compiler/05_eval.dag, src/v2/extdeps/runtimes/v2_effect_io_pure.dag, src/v2/lens/machine_shape.dag, src/v2/lens/undecidable_verdict_collapse.dag, src/v2/std/cardinality.dag, src/v2/std/integer.dag, src/v2/std/model_core.dag RefusalCleared ×7
F field named match src/v2/std/runtime.dag AdvancedToNewCause → normalize_reason_post_normalize_not_well_formed (cause 19, first exposed by D1; not owned here)
R data-initializer lambda wrapper retention (normalized_tree_reason_wrapper_retention_not_normalized) src/v2/std/logic.dag RefusalCleared

Terminal-cause histogram after: parse 26, graft 25, lexer 8, post-normalize-not-well-formed 1. The match-arm and wrapper-retention causes no longer occur anywhere in the closure.

This satisfies the cause-relative bar for D, F and R at population grain. Still owed before un-draft (per the review requirement relayed separately): three cause-level commits with three red/green control pairs so each mechanism's evidence stands on its own; this receipt already attributes the closure delta per mechanism, so the split needs only the commit and fixture structure.

@gunbai-bot
gunbai-bot Bot force-pushed the d4-body-lowering-match-arm branch from e6171ce to ab1267b Compare September 10, 2026 21:31
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review September 10, 2026 21:31
@gunbai-bot
gunbai-bot Bot force-pushed the d4-body-lowering-match-arm branch from ab1267b to 0443d4e Compare September 10, 2026 21:33
@gunbai-bot

gunbai-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed closure receipt for #10967 at head e268ca203f (PR diff from merge-base a4b21cb8e4 overlaid on main 90c6041a4b; srv2 private base clone; emitted driver over the 167-member closure root, controls-only universe; {"_terminal":"complete","file_refusals":60}, wall 270 s, rss 0.87 GB; instrument digest 815a9684d69a0651). Baseline: #10943 carrier at 2fb67b411a, 68 refusals.

Identical to the e6171ced result: 68 → 60, zero regressions. D: 7/7 RefusalCleared; R (src/v2/std/logic.dag): RefusalCleared; F (src/v2/std/runtime.dag): AdvancedToNewCause → normalize_reason_post_normalize_not_well_formed (cause 19, not owned here). No accepted module regressed, no other cause moved, nothing earlier-stage. Histogram after: parse 26, graft 25, lexer 8, post-normalize-not-well-formed 1.

This receipt binds to e268ca203f. Any later push touching a closure source voids it; a test-only/annotation-only push carries it forward but needs its own exact-head CI verdict.

@gunbai-bot

gunbai-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

review 63356 — addressed on 3ce7f15.

  1. Censored enrolment of the thirteen interrupted identities. Agreed: the 501–520ms charged interrupt is not a cost (this file: a censored figure may not be used as a cost). Those rows are no longer on the censored roster. Eleven siblings that local claim_batch already reached PASS for (same tokenize/parse/normalize fold as statement_let_bind, already proven) are on proven_chunk_21; the interrupt figure is discarded. The zero-arm refusal and the F sniff stay off the roster as before.

  2. Cause R classifier widening with no floor control. Agreed. Both discriminating data-record claims (data_record_arrow_lambda_field_is_not_retained_holds, data_record_fn_literal_field_is_not_retained_holds) are off floor_cost_debt so they remain ordinary-floor executing. The in-body lambda stays on proven_chunk_21 as the already-accepted control. Fixtures were also shrunk so those two are cheaper to finish under the 500ms changed-witness line.

  3. Three copies of normalize_outcome. Extracted to v2.test.claim.body_lowering.normalize_outcome_helpers body_lowering_normalize_outcome; the three modules this PR owns now call it. The two pre-existing copies in other body_lowering claim files are outside this PR’s subject and were left in place.

@gunbai-bot

gunbai-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

review 63375 — agreed; not pushed (frozen head 3ce7f15).

floor_cost_debt_proven_chunk_21 does not meet this carrier's PROVEN bar: there is no measured fill-netted marginal CPU over the 500ms ceiling. Local PASS plus shape analogy to statement_let_bind is not that measurement. The 501–520ms interrupt remains discarded as a cost (review 63356).

Proposed fix on parent go: delete proven_chunk_21 and unwire it. The eleven siblings return to the ordinary floor until a real marginal exists. Not re-censored: local claim_batch already reached PASS, so the claim is not only "did not finish on the charged clock." Cause R data-record discriminators, zero-arm, and the F sniff stay off the roster.

— sent from gentle-pike-522

@gunbai-bot

gunbai-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

review 63375 — roster class corrected on 2d2fe70.

PROVEN was the wrong class: there is no measured fill-netted marginal over 500ms. proven_chunk_21 is deleted. The eleven siblings that the floor of record interrupted before a verdict (job 103096598642 / run 34534985281, 501–520ms) are on floor_cost_debt_censored_chunk_03. That asserts only that they did not finish inside the ceiling on the charged clock; own-work cost is unmeasured. Restoration is a measured fill-netted marginal under 500ms on that floor (shrunk fixture or a real fill-netted measurement).

A local claim_batch PASS is recorded as an observation, not as the roster's fact.

Cause R data-record discriminators, zero-arm, and the F sniff stay off the roster: they produced verdicts.

@gunbai-bot

gunbai-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Closure before/after for #10967 at head aaaad15319, overlaid on main b0a6d53c35 (PR diff from merge-base applied, no unmerged paths; srv2, pinned instrument sha256 815a9684d69a0651, emitted driver over the 167-member closure root, controls-only universe, {"_terminal":"complete","file_refusals":60}). Baseline: the #10943 carrier at 2fb67b411a, 68 refusals. Target cause (D): body_lowering_reason_match_arm_navigation_refused (8 baseline rows); (F) the r.match field-name file.

68 → 60. Zero regressions. Cause-relative outcome for the 8 D rows: 7 RefusalCleared (src/v2/compiler/05_eval.dag, src/v2/extdeps/runtimes/v2_effect_io_pure.dag, src/v2/lens/machine_shape.dag, src/v2/lens/undecidable_verdict_collapse.dag, src/v2/std/cardinality.dag, src/v2/std/integer.dag, src/v2/std/model_core.dag), 1 AdvancedToNewCause (src/v2/std/runtime.dag → normalize_reason_post_normalize_not_well_formed, cause 19), 0 StillBlocked, 0 Regressed — all 8 discharge. The F file src/v2/std/logic.dag left the refusal set (RefusalCleared under its own prior cause). Non-target files whose cause changed: 0. Previously accepted members now refusing: 0. After-histogram: parse_g0_tokens_remain 26, namespace_graft_body_dissolved_refused 25, tokenize_lex_e1_unrecognized_char 8, normalize_reason_post_normalize_not_well_formed 1.

Identical outcome to the receipt at e268ca20; this receipt supersedes it, is bound to aaaad15319, and carries forward to the test/roster-only successors fedc23c9, 3ce7f150, 2d2fe708 (no closure source moved). It is interim: the binding receipt is retaken at D4's landing head after G lands. It establishes the compiler closure and this PR's cause-relative delta, not whole-v2.test.* native admission, and does not retire v2_native_route_off_the_merge_path.

Brian Searls and others added 12 commits September 11, 2026 02:59
…er as a malformed match.

A one-arm match was refused because the empty comma-list Repeat tail was navigated as a missing arm; a field named match was sniffed as a match expression because kw_match headed an identity-absent sequence after a dot. Empty-arm match still refuses. Arrow-lambda and fn-literal shells in data initializers are preserved after the child fold so they stop emitting wrapper-retention.

Co-authored-by: Cursor <cursoragent@cursor.com>
Skip only empty Conj, comma, Optional-of-skip, or a sequence of those, and do it before extract. An unknown or navigate-emitted node keeps its match-arm navigation refusal instead of lowering a silently shorter match.

Co-authored-by: Cursor <cursoragent@cursor.com>
Two-arm and three-arm remain green so the discriminator is arm count, not parity or pattern form.

Co-authored-by: Cursor <cursoragent@cursor.com>
… keyword controls.

Infix a.match == b.match is the same field-access sniff, not a bare-access special case.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ral in an fn body already did.

Arrow-lambda and fn-literal spellings both sit in the data initializer; the fn-body record is the control that never wrapper-retained.

Co-authored-by: Cursor <cursoragent@cursor.com>
…eir own pair.

Co-authored-by: Cursor <cursoragent@cursor.com>
The annotation still said five identities after the list grew; the count is not the authority.

Co-authored-by: Cursor <cursoragent@cursor.com>
The tokenize/parse/normalize control strings were BUDGET-REFUSED or, for the
zero-arm source, completed false against a reason string; the floor's
discriminators are now the skip/sniff/structure classifiers themselves.

Co-authored-by: Cursor <cursoragent@cursor.com>
The negative control was PARSE_REFUSED because the empty brace was taken as a
record on t. match (t) { } is the empty arm list, and still refuses
body_lowering_reason_match_arm_navigation_refused.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ows.

The skip/sniff/structure stand-ins are not evidence about tokenize/parse/normalize.
Thirteen claims that BUDGET-REFUSED on job 103096598642 stay enrolled via
floor_cost_debt censored (D3's mechanism). Zero-arm and the F sniff stay on the
floor: they completed. Zero-arm fixture remains match (t) { }.

Co-authored-by: Cursor <cursoragent@cursor.com>
review 63356: drop the censored enrolment that treated a 501ms interrupt as a cost, extract the shared normalize helper, and leave both data-record classifier witnesses off the debt roster.

Co-authored-by: Cursor <cursoragent@cursor.com>
review 63375: PROVEN needs a fill-netted marginal over the ceiling, which these rows do not have. The floor of record interrupted them before a verdict; that is the censored class, keyed to the CI observation, with restoration a measured marginal under 500ms.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gunbai-bot
gunbai-bot Bot force-pushed the d4-body-lowering-match-arm branch from 2d2fe70 to 49dff28 Compare September 11, 2026 03:00
@gunbai-bot

gunbai-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

review 63396 — already resolved on 49dff28; no new commit.

That review was filed against the pre-rebase head (DIRTY / CONFLICTING). This head is rebased onto main 267d69b (mergeable: MERGEABLE). body_lower_is_structure_preserved_emitted now carries both main's where rows and this PR's lambda/fn-literal rows:

  • dag_surface_where_refinement_clause
  • dag_surface_where_predicate
  • dag_surface_arrow_lambda
  • dag_surface_fn_literal

The where-rehome helpers (body_lower_type_variant, body_lower_kept_where_clause, …) and the #10961 = expr helpers (body_lower_eq_body_fold_init / body_lower_eq_body_fold_step) remain from main. Taking the PR side of the classifier is not what landed.

Because the rebase touched body_lowering_fold.dag, the aaaad15 srv2 receipt does not carry to this head.

— sent from gentle-pike-522

required-floor job 103155806208 refused: 13 interrupted before verdict (D4 R/zero-arm plus the D1 where family) and braced_fn_decl_survives_normalize completed at 503ms. Censored vs proven split matches what that job observed.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gunbai-bot

gunbai-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Verified against 5000c1d for review 63588. Both findings are real. No commit on this head: D4 is frozen until the post-#11029 (then D2/D3) remerge.

Finding 1 (zero_arm + two R data-record claims on chunk_04 vs stale “stay off floor_cost_debt” comments). The enrolment is true: those three identities are in floor_cost_debt_censored_chunk_04, so ordinary-floor disposition is DeclinedCostDebt. The comments in data_initializer_fn_value_test.dag (review 63356) and the chunk_03 header are stale against that enrolment. Cause F’s match_keyword_after_dot_is_not_a_match_expr_sniff_holds is still off the roster.

Parent ruling for the remerge (not this SHA): those three stay on D4’s censored list because they interrupted on the floor of record (job 103155806208 / run 34556768895). They are D4’s own changed-witness interrupts, not hitchhikers. The 503 ms proven_chunk_21 row is dropped (main’s D1/G censored family owns braced_fn_decl_survives_normalize_holds). The §4b(4) ordinary-path control for D/R after that land is the F sniff plus ChangedCostDebtVerdictOnly when these files are touched — weaker than a cheap always-on pair. A new cheap D/R pair is not being authored on the freeze; if that is required before land, it has to be an explicit go.

Finding 2 (ten D1/G identities + proven_21 on a D4 chunk). Agreed, and already the remerge recipe: keep main’s chunk_03 and gunbc.rung_drop.floor_cost_debt_censored_d1_g_parse_family; cut D4 chunk_04 to the three D4-own interrupts only; drop proven_chunk_21; no identity twice. That is one motion after #11029 lands, not a push now.

Stale comments will be corrected in that same motion so they no longer claim the three stay on the ordinary floor.

— sent from gentle-pike-522

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.

1 participant