From 7fba2a24a9bded0376233cf22d9458845053d863 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 27 Sep 2026 16:58:22 +0000 Subject: [PATCH 1/8] WIP: a control form is taken only when it is the expression Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v2/compiler/body_lowering_fold.dag | 65 ++++++++++++++++---------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/src/v2/compiler/body_lowering_fold.dag b/src/v2/compiler/body_lowering_fold.dag index 3c1c510fe49..5ca8b4aa339 100644 --- a/src/v2/compiler/body_lowering_fold.dag +++ b/src/v2/compiler/body_lowering_fold.dag @@ -2758,11 +2758,16 @@ fn body_lower_body_subtree_lower(captured: Node) -> Outcome> { } } -// THE OUTERMOST CONTROL FORM, NOT THE FIRST OF A FIXED KIND. The four control productions were -// searched one kind at a time, each search deep, so a body `match c { A(x) => if p { .. } .. }` -// was answered by the `if` inside its arm -- the whole match, both arms and the binder, gone -- -// because `if` was searched before `match`. One pre-order walk over the four kinds finds the -// form nearest the root, which on an expression spine is the one that owns the body. +// A CONTROL FORM IS TAKEN ONLY WHEN IT IS THE EXPRESSION. The walk descends through what does not +// change the expression -- a production to its captured child, a sequence whose right side is an +// empty tail, and a lone opening or closing delimiter around it (`( .. )`, `{ .. }`) -- and stops +// anywhere else. It used to walk the whole capture pre-order and answer the FIRST if / match / loop / +// let anywhere beneath it, so `t && match w {..}`, `f(a: match ..)`, `x + if c {..} else {..}` and +// `R { v: if .. }` each lowered to the nested block alone and the enclosing operator, call or record +// was dropped with no diagnostic (gunbc.recurring_failure_mode +// expression_enclosing_a_block_headed_operand_lowers_to_that_block). An expression that is not a +// control form goes whole to body_lower_body_subtree_lower_from_binary, and a block nested inside it +// is reached as that expression's operand, argument or field value. type ControlFormFound { emitted: Symbol captured: Node @@ -2775,35 +2780,45 @@ fn body_lower_is_control_form_emitted(id: Symbol) -> Bool { || (id == ^dag_surface_let_expr) } +fn body_lower_node_is_delimiter_atom(n: Node, opening: Bool) -> Bool { + match node_atom_identity_optional(node: body_lower_deep_unwrap_optional(node: n)) { + Present { value: id } => + if opening { (id == ^dag_token_lparen) || (id == ^dag_token_lbrace) } else { (id == ^dag_token_rparen) || (id == ^dag_token_rbrace) } + Absent => false + } +} + fn body_lower_find_control_form_optional(root: Node) -> Optional { match parse_production_emitted_identity_optional(node: root) { Present { value: id } => - match body_lower_is_control_form_emitted(id: id) { - true => - match parse_production_captured_child_optional(node: root) { - Present { value: captured } => - optional_present(value: ControlFormFound { emitted: id, captured: captured }) - Absent => body_lower_find_control_form_under_children(root: root) + match parse_production_captured_child_optional(node: root) { + Absent => optional_absent() + Present { value: captured } => + if body_lower_is_control_form_emitted(id: id) { + optional_present(value: ControlFormFound { emitted: id, captured: captured }) + } else if body_lower_find_captured_prunable(id: id) { + optional_absent() + } else { + body_lower_find_control_form_optional(root: captured) } - false => - match body_lower_find_captured_prunable(id: id) { - true => optional_absent() - false => body_lower_find_control_form_under_children(root: root) + } + Absent => + match sugar_sequence_pair_optional(node: root) { + Absent => optional_absent() + Present { value: pair } => + if is_empty_conj_root(n: body_lower_deep_unwrap_optional(node: pair.right)) { + body_lower_find_control_form_optional(root: pair.left) + } else if body_lower_node_is_delimiter_atom(n: pair.right, opening: false) { + body_lower_find_control_form_optional(root: pair.left) + } else if body_lower_node_is_delimiter_atom(n: pair.left, opening: true) { + body_lower_find_control_form_optional(root: pair.right) + } else { + optional_absent() } } - Absent => body_lower_find_control_form_under_children(root: root) } } -fn body_lower_find_control_form_under_children(root: Node) -> Optional { - fold(root.children, init: Absent, f: fn(acc, edge) { - match acc { - Present { value: _ } => acc - Absent => body_lower_find_control_form_optional(root: edge.target) - } - }) -} - fn body_lower_body_subtree_lower_control_forms(captured: Node) -> Outcome> { match body_lower_find_control_form_optional(root: captured) { Absent => body_lower_body_subtree_lower_from_binary(captured: captured) From 7bcafca53de5f01f79967b23eab1a1d39a0e1718 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 27 Sep 2026 17:03:59 +0000 Subject: [PATCH 2/8] WIP: an operator operand the fold leaves unlowered is read by the one value reader Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v2/compiler/body_lowering_fold.dag | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/v2/compiler/body_lowering_fold.dag b/src/v2/compiler/body_lowering_fold.dag index 5ca8b4aa339..77ba1efb2f4 100644 --- a/src/v2/compiler/body_lowering_fold.dag +++ b/src/v2/compiler/body_lowering_fold.dag @@ -1436,7 +1436,11 @@ fn body_lower_operator_refusal(reason: Symbol, at: Node) -> OperatorExpressionRe OperatorExpressionRefused { diagnostics: diagnostics_singleton(d: body_lower_diagnostic(reason: reason, n: at)) } } -// One operand of an operator expression, lowered whole or refused at the operand. +// One operand of an operator expression, lowered whole or refused at the operand. An operand the +// bottom-up fold does not bring to a lowered node is a VALUE, read by the one value reader +// (body_lower_value_read), so a block operand (`t && match w {..}`) lowers as that block and its +// refusal is the value's, located at the value. It used to fall to body_lower_operand_ref_optional, +// an operand reader that cannot read a block, and refuse as operator_operand_unread. fn body_lower_operator_operand(node: Node) -> Outcome { let operand = body_lower_strip_operator_free_levels(node: node) match body_lower_read_operator_expression(node: operand) { @@ -1450,14 +1454,7 @@ fn body_lower_operator_operand(node: Node) -> Outcome { Rejected { diagnostics: r } => Rejected { diagnostics: r } Accepted { value: Present { value: lowered }, diagnostics: d } => outcome_with_diagnostics(value: lowered, diagnostics: d) - Accepted { value: Absent, diagnostics: _ } => - match body_lower_operand_ref_optional(node: operand) { - Present { value: lowered } => outcome_accepted(value: lowered) - Absent => - outcome_rejected( - d: body_lower_diagnostic(reason: ^body_lowering_reason_operator_operand_unread, n: operand) - ) - } + Accepted { value: Absent, diagnostics: _ } => body_lower_value_read(value: operand) } } } From a088c935d2fa76c7e73b8356a18a62319ae34ca4 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 27 Sep 2026 18:12:38 +0000 Subject: [PATCH 3/8] A primary whose capture the fold already lowered is that node; the operand-reader detour is reverted Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v2/compiler/body_lowering_fold.dag | 27 ++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/v2/compiler/body_lowering_fold.dag b/src/v2/compiler/body_lowering_fold.dag index 77ba1efb2f4..f2352df16fd 100644 --- a/src/v2/compiler/body_lowering_fold.dag +++ b/src/v2/compiler/body_lowering_fold.dag @@ -1436,11 +1436,7 @@ fn body_lower_operator_refusal(reason: Symbol, at: Node) -> OperatorExpressionRe OperatorExpressionRefused { diagnostics: diagnostics_singleton(d: body_lower_diagnostic(reason: reason, n: at)) } } -// One operand of an operator expression, lowered whole or refused at the operand. An operand the -// bottom-up fold does not bring to a lowered node is a VALUE, read by the one value reader -// (body_lower_value_read), so a block operand (`t && match w {..}`) lowers as that block and its -// refusal is the value's, located at the value. It used to fall to body_lower_operand_ref_optional, -// an operand reader that cannot read a block, and refuse as operator_operand_unread. +// One operand of an operator expression, lowered whole or refused at the operand. fn body_lower_operator_operand(node: Node) -> Outcome { let operand = body_lower_strip_operator_free_levels(node: node) match body_lower_read_operator_expression(node: operand) { @@ -1454,7 +1450,14 @@ fn body_lower_operator_operand(node: Node) -> Outcome { Rejected { diagnostics: r } => Rejected { diagnostics: r } Accepted { value: Present { value: lowered }, diagnostics: d } => outcome_with_diagnostics(value: lowered, diagnostics: d) - Accepted { value: Absent, diagnostics: _ } => body_lower_value_read(value: operand) + Accepted { value: Absent, diagnostics: _ } => + match body_lower_operand_ref_optional(node: operand) { + Present { value: lowered } => outcome_accepted(value: lowered) + Absent => + outcome_rejected( + d: body_lower_diagnostic(reason: ^body_lowering_reason_operator_operand_unread, n: operand) + ) + } } } } @@ -7394,11 +7397,22 @@ fn body_lower_list_literal(literal: Node, items_capture: Node) -> Outcome ) } +// A CAPTURE THE FOLD ALREADY LOWERED IS THE PRIMARY'S VALUE. body_lower_fold_reduce folds a node's +// children before reducing it, so a primary whose capture is a control form -- `match w {..}`, +// `if c {..} else {..}` in operand, argument or field position -- reaches this reducer holding the +// lowered Match or Branch, not syntax. The strategy readers below read raw syntax: handed a lowered +// node, one of them answered it with its first child, so `t && match w {..}` lowered to `t && w` +// with every arm dropped (the value reader on the match_expr node itself was correct; on its primary +// parent it was not). The postfix reducer already passes a lowered primary through +// (body_lower_postfix_lowered_primary_optional); this is the same rule one level down. fn body_lower_primary_expr(shell: Node) -> Outcome { let captured = match body_lower_unwrap_captured(shell: shell) { Present { value: inner } => inner Absent => shell } + if body_lower_is_core_substrate(node: captured) { + outcome_accepted(value: captured) + } else { match body_lower_int_literal_with_magnitude_optional(node: captured) { Present { value: lit } => outcome_accepted(value: lit) Absent => @@ -7478,6 +7492,7 @@ fn body_lower_primary_expr(shell: Node) -> Outcome { } } } + } } fn body_lower_fn_body_shell(shell: Node) -> Outcome { From 784e3ab1427553c7c51371c1c7f59b7346607daf Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 27 Sep 2026 18:13:24 +0000 Subject: [PATCH 4/8] Enclosing-expression pinned drops flip to conservation controls; RFM row climbs Co-Authored-By: Claude Opus 5.5 (1M context) --- ...wers_to_that_block_at_v2_body_lowering.dag | 9 ++++---- ...rence_conservation_accepted_drops_test.dag | 22 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/dag/gunbc/recurring_failure_mode/expression_enclosing_a_block_headed_operand_lowers_to_that_block_at_v2_body_lowering.dag b/dag/gunbc/recurring_failure_mode/expression_enclosing_a_block_headed_operand_lowers_to_that_block_at_v2_body_lowering.dag index 9000ff9402f..b90e358013b 100644 --- a/dag/gunbc/recurring_failure_mode/expression_enclosing_a_block_headed_operand_lowers_to_that_block_at_v2_body_lowering.dag +++ b/dag/gunbc/recurring_failure_mode/expression_enclosing_a_block_headed_operand_lowers_to_that_block_at_v2_body_lowering.dag @@ -13,12 +13,13 @@ data expression_enclosing_a_block_headed_operand_lowers_to_that_block_at_v2_body "RECEIPTS. Fixtures, traced by a scratch normalized-tree dump over `v2.compiler.program_assembly` `program_assembly_phase_normalize` at main 8bf4b66774c, and by the census line of each: all six forms above lower to the block alone. The block as LEFT operand (`match w {..} && t`) and the parenthesised form (`t && (match w {..})`) are REFUSED at normalize, not dropped. Census: `v2.compiler.reference_conservation_census` `reference_conservation_census_for_paths` over `reference_conservation_stratified_sample_paths` at 69e0bb7566e. Around 113 atoms absent in accepted modules sit in a && or || chain beside a block, plus the largest residue declarations: `v2.std.float` `float_body_compare_ieee_ordered_outcome` (`Accepted { value: match a {..}, diagnostics: None }`) and `test.claim.codex_package_delivery_wet_witness_test`, whose witnesses are `&& match ..` chains, so a claim there can hold on its last conjunct alone.", "DISTINGUISHING FACTS. Not `lowering_accessor_collapses_a_sequence_operand`, which keeps the LEFT element of an operand; here the block is kept and the enclosing expression is lost. Not gunbc#11911, a parse-order defect; here the parse accepts. Not `match_arms_after_the_second_are_dropped_at_v2_body_lowering`, which loses arms INSIDE a match; this loses what is AROUND it.", "RUNG FOUND AT: below the floor (silent wrongness). CEILING: structurally guaranteed. An expression lowers to its own operator, construct or application over every constituent, or refuses at the constituent it could not read. A control form is lowered as the whole value only where it IS the value. NEXT-RUNG TRIGGER, NAMING THE CAPABILITY: v2 body lowering has no reader that answers a value by searching its subtree for a control form, so an enclosing expression the fold left unlowered refuses instead of being replaced. Owner: bright-boar-848's fold queue (quiet-seal-543, 2026-09-25). The fall-back reader is that lane's subject. Pinned drops, each holding while the drop happens and redding when the repair lands, in `v2.test.claim.namespace_xl0.reference_conservation_accepted_drops`: `a_binary_operand_beside_a_match_is_reported_dropped_holds`, `a_binary_operand_beside_an_if_is_reported_dropped_holds`, `a_record_field_beside_a_match_valued_field_is_reported_dropped_holds`, `a_call_argument_beside_a_match_valued_argument_is_reported_dropped_holds`. The census's clean control, `v2.test.claim.namespace_xl0.reference_conservation` `the_clean_control_conserves_every_authored_atom_holds`, is the neighbour that conserves.", + "CLIMBED, THE TRIGGER CAPABILITY MET (bright-boar-848, XL-2). Two links, both in `v2.compiler.body_lowering_fold`. (1) `body_lower_find_control_form_optional` no longer searches a value's subtree: it descends only through what does not change the expression -- a production to its captured child, a sequence whose right side is an empty tail, a lone opening or closing delimiter -- so a control form is lowered as the whole value only where it IS the value, and an enclosing operator, call or record goes whole to the binary/postfix route. (2) That route then exposed the second link: `body_lower_primary_expr` ran its raw-syntax strategy readers over a capture the bottom-up fold had ALREADY lowered (a Match or Branch) and one answered it with its first child, so `t && match w {..}` became `t && w`. Measured by calling `body_lower_value_read` at each production level of the operand: correct at the match_expr node, collapsed from its primary parent up. A primary whose capture is core substrate is now that node, the rule `body_lower_postfix_lowered_primary_optional` already states one level up. The four pinned drops flip to conservation controls (accepted AND the enclosing constituent not dropped), red against the base fold and green after. The label and let-binder atoms that stay absent in the same fixtures are the separately declared NamedArgumentLabel and StatementLetBinder drops. RUNG NOW: structurally guaranteed for the enclosing expression.", ], evidence: [ - DeclarationRef { module_path: "v2.test.claim.namespace_xl0.reference_conservation_accepted_drops", decl_name: "a_binary_operand_beside_a_match_is_reported_dropped_holds", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.test.claim.namespace_xl0.reference_conservation_accepted_drops", decl_name: "a_binary_operand_beside_an_if_is_reported_dropped_holds", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.test.claim.namespace_xl0.reference_conservation_accepted_drops", decl_name: "a_record_field_beside_a_match_valued_field_is_reported_dropped_holds", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.test.claim.namespace_xl0.reference_conservation_accepted_drops", decl_name: "a_call_argument_beside_a_match_valued_argument_is_reported_dropped_holds", field: WholeDeclaration }, + DeclarationRef { module_path: "v2.test.claim.namespace_xl0.reference_conservation_accepted_drops", decl_name: "a_binary_operand_beside_a_match_is_conserved_holds", field: WholeDeclaration }, + DeclarationRef { module_path: "v2.test.claim.namespace_xl0.reference_conservation_accepted_drops", decl_name: "a_binary_operand_beside_an_if_is_conserved_holds", field: WholeDeclaration }, + DeclarationRef { module_path: "v2.test.claim.namespace_xl0.reference_conservation_accepted_drops", decl_name: "a_record_field_beside_a_match_valued_field_is_conserved_holds", field: WholeDeclaration }, + DeclarationRef { module_path: "v2.test.claim.namespace_xl0.reference_conservation_accepted_drops", decl_name: "a_call_argument_beside_a_match_valued_argument_is_conserved_holds", field: WholeDeclaration }, ], } diff --git a/src/v2/test/claim/namespace_xl0/reference_conservation_accepted_drops_test.dag b/src/v2/test/claim/namespace_xl0/reference_conservation_accepted_drops_test.dag index 0cfe1dd1358..72af9776ccd 100644 --- a/src/v2/test/claim/namespace_xl0/reference_conservation_accepted_drops_test.dag +++ b/src/v2/test/claim/namespace_xl0/reference_conservation_accepted_drops_test.dag @@ -1,7 +1,7 @@ module v2.test.claim.namespace_xl0.reference_conservation_accepted_drops import v2.test.claim.namespace_xl0.reference_conservation { conservation_report_of, conservation_subject, report_drop_count } -import v2.compiler.reference_conservation { ReferenceConservationSubject } +import v2.compiler.reference_conservation { ReferenceConservationSubject, reference_conservation_accepted } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } import v2.std.logic { Bool } import v2.std.text { String } @@ -31,8 +31,9 @@ fn binary_operand_beside_match_subject() -> ReferenceConservationSubject { conservation_subject(id_tail: "binary_operand_beside_match", source: binary_operand_beside_match_source) } -test fn a_binary_operand_beside_a_match_is_reported_dropped_holds() -> Bool { - report_drop_count(report: conservation_report_of(subject: binary_operand_beside_match_subject()), wanted: ^rc_left) == 1 +test fn a_binary_operand_beside_a_match_is_conserved_holds() -> Bool { + let r = conservation_report_of(subject: binary_operand_beside_match_subject()) + reference_conservation_accepted(report: r) && (report_drop_count(report: r, wanted: ^rc_left) == 0) } data binary_operand_beside_if_source: String = "module v2.test.reference_conservation_binop_if\n\ndata rc_left: Bool = true\n\nfn rc_and(c: Bool) -> Bool {\n rc_left && if c { true } else { false }\n}\n" @@ -41,8 +42,9 @@ fn binary_operand_beside_if_subject() -> ReferenceConservationSubject { conservation_subject(id_tail: "binary_operand_beside_if", source: binary_operand_beside_if_source) } -test fn a_binary_operand_beside_an_if_is_reported_dropped_holds() -> Bool { - report_drop_count(report: conservation_report_of(subject: binary_operand_beside_if_subject()), wanted: ^rc_left) == 1 +test fn a_binary_operand_beside_an_if_is_conserved_holds() -> Bool { + let r = conservation_report_of(subject: binary_operand_beside_if_subject()) + reference_conservation_accepted(report: r) && (report_drop_count(report: r, wanted: ^rc_left) == 0) } data record_field_beside_match_source: String = "module v2.test.reference_conservation_record_match\n\ntype RcT = RcA | RcB\n\ntype RcR { k: Bool, j: Bool }\n\ndata rc_sibling: Bool = true\n\nfn rc_record(t: RcT) -> RcR {\n RcR { k: match t {\n RcA => true\n RcB => false\n }, j: rc_sibling }\n}\n" @@ -51,8 +53,9 @@ fn record_field_beside_match_subject() -> ReferenceConservationSubject { conservation_subject(id_tail: "record_field_beside_match", source: record_field_beside_match_source) } -test fn a_record_field_beside_a_match_valued_field_is_reported_dropped_holds() -> Bool { - report_drop_count(report: conservation_report_of(subject: record_field_beside_match_subject()), wanted: ^rc_sibling) == 1 +test fn a_record_field_beside_a_match_valued_field_is_conserved_holds() -> Bool { + let r = conservation_report_of(subject: record_field_beside_match_subject()) + reference_conservation_accepted(report: r) && (report_drop_count(report: r, wanted: ^rc_sibling) == 0) } data call_argument_beside_match_source: String = "module v2.test.reference_conservation_call_match\n\ntype RcT = RcA | RcB\n\ndata rc_sibling: Bool = true\n\nfn rc_h(a: Bool, b: Bool) -> Bool { a }\n\nfn rc_call(t: RcT) -> Bool {\n rc_h(a: match t {\n RcA => true\n RcB => false\n }, b: rc_sibling)\n}\n" @@ -61,8 +64,9 @@ fn call_argument_beside_match_subject() -> ReferenceConservationSubject { conservation_subject(id_tail: "call_argument_beside_match", source: call_argument_beside_match_source) } -test fn a_call_argument_beside_a_match_valued_argument_is_reported_dropped_holds() -> Bool { - report_drop_count(report: conservation_report_of(subject: call_argument_beside_match_subject()), wanted: ^rc_sibling) == 1 +test fn a_call_argument_beside_a_match_valued_argument_is_conserved_holds() -> Bool { + let r = conservation_report_of(subject: call_argument_beside_match_subject()) + reference_conservation_accepted(report: r) && (report_drop_count(report: r, wanted: ^rc_sibling) == 0) } // A primary expression the primary readers do not recognize lowers to its first atom: a map literal From 69e407f2f73cbe7240e8db9585b6730b34959fb6 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 27 Sep 2026 18:28:53 +0000 Subject: [PATCH 5/8] Witness: an expression containing a block lowers to itself over every part (producer boundary) Co-Authored-By: Claude Opus 5.5 (1M context) --- .../enclosing_expression_structure_test.dag | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag diff --git a/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag b/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag new file mode 100644 index 00000000000..0aaa2a46833 --- /dev/null +++ b/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag @@ -0,0 +1,159 @@ +module v2.test.claim.body_lowering.enclosing_expression_structure + +import v2.compiler.body_lowering_fold { body_lower_value_read } +import v2.compiler.parse { parse_module } +import v2.compiler.tokenize { tokenize } +import v2.extdeps.languages.dag { dag_language_model, parse_production_emitted_identity_optional } +import v2.std.diagnostic { Accepted, Rejected } +import v2.std.grammar { node_atom_identity_optional } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } +import v2.std.logic { Bool } +import v2.std.node { ComputationNode, Match, Node, Symbol, Transform } +import v2.std.optional { Absent, Optional, Present, optional_absent, optional_present } +import v2.std.text { String } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +// AN EXPRESSION THAT CONTAINS A BLOCK LOWERS TO ITSELF, WITH THE BLOCK AS ONE OF ITS PARTS. +// +// THE SUBJECT IS ONE PRODUCER BOUNDARY: v2.compiler.body_lowering_fold body_lower_value_read, the one +// value reader, called on the body expression of a parsed module (tokenize and parse only; no +// normalize runs). The conservation controls +// (v2.test.claim.namespace_xl0.reference_conservation_accepted_drops *_is_conserved_holds) establish +// that every constituent atom reaches the tree; they stay green under a producer that keeps every +// atom but builds the wrong shape. These claims read the shape: an `&&` whose right operand is a +// match lowers to a Transform headed by the `&&` token over the left operand and a Match, not to the +// Match alone (the dropped-enclosing form) and not to `t && w` (the collapsed-operand form, where the +// match was answered with its scrutinee atom). +// +// Parameters, not data declarations, so the first expression in the module is the body's. + +data ees_and_match_source: String = "module m\n\nfn ees(ees_t: Bool, ees_w: Bool) -> Bool {\n ees_t && match ees_w {\n true => ees_t\n false => ees_w\n }\n}\n" + +data ees_call_match_source: String = "module m\n\nfn ees_h(a: Bool, b: Bool) -> Bool { a }\n\nfn ees(ees_s: Bool, ees_w: Bool) -> Bool {\n ees_h(a: match ees_w {\n true => ees_s\n false => ees_w\n }, b: ees_s)\n}\n" + +fn ees_parsed(source: String) -> Optional { + let lm = dag_language_model() + match tokenize(text: source, file: ^ees_file, rules: lm.lex) { + Rejected { diagnostics: _ } => optional_absent() + Accepted { value: ts, diagnostics: _ } => + match parse_module(tokens: ts, grammar: lm.grammar) { + Rejected { diagnostics: _ } => optional_absent() + Accepted { value: artifact, diagnostics: _ } => optional_present(value: artifact.tree) + } + } +} + +// The last dag_surface_expr in pre-order whose subtree is the whole body of the LAST fn: found as the +// first dag_surface_expr under the last dag_surface_fn_body. +fn ees_emitted_first(root: Node, production: Symbol) -> Optional { + match parse_production_emitted_identity_optional(node: root) { + Present { value: e } => if e == production { optional_present(value: root) } else { ees_emitted_first_kids(root: root, production: production) } + Absent => ees_emitted_first_kids(root: root, production: production) + } +} + +fn ees_emitted_first_kids(root: Node, production: Symbol) -> Optional { + fold(root.children, init: optional_absent(), f: fn(acc, e) { + match acc { Present { value: _ } => acc Absent => ees_emitted_first(root: e.target, production: production) } + }) +} + +fn ees_emitted_last(root: Node, production: Symbol) -> Optional { + let own = match parse_production_emitted_identity_optional(node: root) { + Present { value: e } => if e == production { optional_present(value: root) } else { optional_absent() } + Absent => optional_absent() + } + fold(root.children, init: own, f: fn(acc, e) { + match ees_emitted_last(root: e.target, production: production) { Present { value: n } => optional_present(value: n) Absent => acc } + }) +} + +type EesValue + = EesNotRead + | EesRefused { reason: Symbol } + | EesLowered { value: Node } + +fn ees_body_value(source: String) -> EesValue { + match ees_parsed(source: source) { + Absent => EesNotRead + Present { value: tree } => + match ees_emitted_last(root: tree, production: ^dag_surface_fn_body) { + Absent => EesNotRead + Present { value: body } => + match ees_emitted_first(root: body, production: ^dag_surface_expr) { + Absent => EesNotRead + Present { value: expr } => + match body_lower_value_read(value: expr) { + Rejected { diagnostics: ds } => EesRefused { reason: ds.head.reason } + Accepted { value: v, diagnostics: _ } => EesLowered { value: v } + } + } + } + } +} + +fn ees_child(n: Node, i: Int) -> Optional { + match n.children.skip(n: i).first() { + Present { value: e } => optional_present(value: e.target) + Absent => optional_absent() + } +} + +fn ees_is_atom(n: Optional, spelling: Symbol) -> Bool { + match n { + Present { value: x } => + match node_atom_identity_optional(node: x) { Present { value: s } => s == spelling Absent => false } + Absent => false + } +} + +fn ees_is_match(n: Optional, scrutinee: Symbol, arms: Int) -> Bool { + match n { + Present { value: x } => + match x.kind { + ComputationNode { behavior: Match } => ees_is_atom(n: ees_child(n: x, i: 0), spelling: scrutinee) && (count(x.children) == arms + 1) + _ => false + } + Absent => false + } +} + +fn ees_is_transform(n: Node, arity: Int) -> Bool { + match n.kind { + ComputationNode { behavior: Transform } => count(n.children) == arity + _ => false + } +} + +fn ees_and_match_value() -> EesValue { + ees_body_value(source: ees_and_match_source) +} + +fn ees_call_match_value() -> EesValue { + ees_body_value(source: ees_call_match_source) +} + +// `ees_t && match ees_w {..}` is Transform(&&, ees_t, Match(ees_w, arm, arm)). +test fn an_operand_beside_a_match_lowers_as_the_operator_over_both_operands() -> Bool { + match ees_and_match_value() { + EesLowered { value: v } => + ees_is_transform(n: v, arity: 3) + && ees_is_atom(n: ees_child(n: v, i: 0), spelling: ^dag_token_and_and) + && ees_is_atom(n: ees_child(n: v, i: 1), spelling: ^ees_t) + && ees_is_match(n: ees_child(n: v, i: 2), scrutinee: ^ees_w, arms: 2) + _ => false + } +} + +// `ees_h(a: match ees_w {..}, b: ees_s)` is Transform(ees_h, Match(ees_w, arm, arm), ees_s). +test fn a_call_with_a_match_argument_lowers_as_the_call_over_every_argument() -> Bool { + match ees_call_match_value() { + EesLowered { value: v } => + ees_is_transform(n: v, arity: 3) + && ees_is_atom(n: ees_child(n: v, i: 0), spelling: ^ees_h) + && ees_is_match(n: ees_child(n: v, i: 1), scrutinee: ^ees_w, arms: 2) + && ees_is_atom(n: ees_child(n: v, i: 2), spelling: ^ees_s) + _ => false + } +} From 7e68ca63d3ad6298971bd6b58ab420a7a0a1c5c2 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 27 Sep 2026 18:48:05 +0000 Subject: [PATCH 6/8] Structural witness covers both routes: the one value reader and the body walker Co-Authored-By: Claude Opus 5.5 (1M context) --- .../enclosing_expression_structure_test.dag | 61 +++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag b/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag index 0aaa2a46833..f92ac9e57d3 100644 --- a/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag +++ b/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag @@ -1,6 +1,6 @@ module v2.test.claim.body_lowering.enclosing_expression_structure -import v2.compiler.body_lowering_fold { body_lower_value_read } +import v2.compiler.body_lowering_fold { body_lower_body_subtree_lower, body_lower_value_read } import v2.compiler.parse { parse_module } import v2.compiler.tokenize { tokenize } import v2.extdeps.languages.dag { dag_language_model, parse_production_emitted_identity_optional } @@ -74,7 +74,20 @@ type EesValue | EesRefused { reason: Symbol } | EesLowered { value: Node } +// THE TWO ROUTES. A value reaches lowering by two producers: the one value reader (field, argument, +// let and arm positions, and any value the bottom-up fold is handed) and the top-down body walker +// (body_lower_body_subtree_lower), which the fn body producer and the value reader's own fall-back +// enter. The defect had one link on each: the walker searched the whole subtree for a control form +// and lowered that form alone, and the primary reducer re-read a capture the fold had already +// lowered. Each claim below names its route; each route's claims are red with that route's fix +// reverted and the other kept. +type EesRoute = EesValueReader | EesBodyWalker + fn ees_body_value(source: String) -> EesValue { + ees_route_value(source: source, route: EesValueReader) +} + +fn ees_route_value(source: String, route: EesRoute) -> EesValue { match ees_parsed(source: source) { Absent => EesNotRead Present { value: tree } => @@ -84,9 +97,18 @@ fn ees_body_value(source: String) -> EesValue { match ees_emitted_first(root: body, production: ^dag_surface_expr) { Absent => EesNotRead Present { value: expr } => - match body_lower_value_read(value: expr) { - Rejected { diagnostics: ds } => EesRefused { reason: ds.head.reason } - Accepted { value: v, diagnostics: _ } => EesLowered { value: v } + match route { + EesValueReader => + match body_lower_value_read(value: expr) { + Rejected { diagnostics: ds } => EesRefused { reason: ds.head.reason } + Accepted { value: v, diagnostics: _ } => EesLowered { value: v } + } + EesBodyWalker => + match body_lower_body_subtree_lower(captured: expr) { + Rejected { diagnostics: ds } => EesRefused { reason: ds.head.reason } + Accepted { value: Absent, diagnostics: _ } => EesNotRead + Accepted { value: Present { value: v }, diagnostics: _ } => EesLowered { value: v } + } } } } @@ -157,3 +179,34 @@ test fn a_call_with_a_match_argument_lowers_as_the_call_over_every_argument() -> _ => false } } + +fn ees_and_match_walked() -> EesValue { + ees_route_value(source: ees_and_match_source, route: EesBodyWalker) +} + +fn ees_call_match_walked() -> EesValue { + ees_route_value(source: ees_call_match_source, route: EesBodyWalker) +} + +// The same two expressions through the body walker, the fn-body route. +test fn the_body_walker_lowers_an_operand_beside_a_match_as_the_operator_over_both_operands() -> Bool { + match ees_and_match_walked() { + EesLowered { value: v } => + ees_is_transform(n: v, arity: 3) + && ees_is_atom(n: ees_child(n: v, i: 0), spelling: ^dag_token_and_and) + && ees_is_atom(n: ees_child(n: v, i: 1), spelling: ^ees_t) + && ees_is_match(n: ees_child(n: v, i: 2), scrutinee: ^ees_w, arms: 2) + _ => false + } +} + +test fn the_body_walker_lowers_a_call_with_a_match_argument_as_the_call_over_every_argument() -> Bool { + match ees_call_match_walked() { + EesLowered { value: v } => + ees_is_transform(n: v, arity: 3) + && ees_is_atom(n: ees_child(n: v, i: 0), spelling: ^ees_h) + && ees_is_match(n: ees_child(n: v, i: 1), scrutinee: ^ees_w, arms: 2) + && ees_is_atom(n: ees_child(n: v, i: 2), spelling: ^ees_s) + _ => false + } +} From 364e33cb5c653632b5c5ccbd8ce2e0f982fb1284 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 27 Sep 2026 19:04:56 +0000 Subject: [PATCH 7/8] Label the value-reader call claim as a control; share the four producers Co-Authored-By: Claude Opus 5.5 (1M context) --- .../body_lowering/enclosing_expression_structure_test.dag | 8 ++++++-- src/v2/workflow/floor_pure_producer_share.dag | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag b/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag index f92ac9e57d3..971d08c027e 100644 --- a/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag +++ b/src/v2/test/claim/body_lowering/enclosing_expression_structure_test.dag @@ -79,8 +79,9 @@ type EesValue // (body_lower_body_subtree_lower), which the fn body producer and the value reader's own fall-back // enter. The defect had one link on each: the walker searched the whole subtree for a control form // and lowered that form alone, and the primary reducer re-read a capture the fold had already -// lowered. Each claim below names its route; each route's claims are red with that route's fix -// reverted and the other kept. +// lowered. Measured (gunbc PR body): with the search fix reverted and the primary rule kept, the +// body walker's call claim is red; with the primary rule reverted and the search fix kept, both +// binary claims are red; the base fold reds all three discriminating claims. type EesRoute = EesValueReader | EesBodyWalker fn ees_body_value(source: String) -> EesValue { @@ -169,6 +170,9 @@ test fn an_operand_beside_a_match_lowers_as_the_operator_over_both_operands() -> } // `ees_h(a: match ees_w {..}, b: ees_s)` is Transform(ees_h, Match(ees_w, arm, arm), ees_s). +// A CONTROL, NOT A DISCRIMINATOR, on this route: the value reader already lowered a match-valued +// argument whole before this change (green on the base fold and under either revert). It pins that +// the value-reader route stays correct; the discriminating call claim is the body-walker one below. test fn a_call_with_a_match_argument_lowers_as_the_call_over_every_argument() -> Bool { match ees_call_match_value() { EesLowered { value: v } => diff --git a/src/v2/workflow/floor_pure_producer_share.dag b/src/v2/workflow/floor_pure_producer_share.dag index d1cf6883485..1b7b4e61bcc 100644 --- a/src/v2/workflow/floor_pure_producer_share.dag +++ b/src/v2/workflow/floor_pure_producer_share.dag @@ -608,6 +608,10 @@ import v2.std.collection { List } // THE XL-2 IF-ARM AND STATEMENT-SPINE ROW IS THE SAME GROUND AT THIRTEEN SPECIMENS: // v2.test.claim.namespace_xl0.if_arm_and_statement_lowering_refusal iasl_outcomes drives one front // end over thirteen inline modules and stores one verdict arm per module, portable for the same reason. +// THE XL-2 ENCLOSING-EXPRESSION ROW: v2.test.claim.body_lowering.enclosing_expression_structure +// ees_and_match_value, ees_call_match_value, ees_and_match_walked and ees_call_match_walked each +// tokenize and parse one inline module and call one producer on its body expression; the stored value +// is an EesValue (a lowered Node or a reason Symbol, no resolution context and no closure). // THE XL-2 BOUND-VALUE RESOLVE ROW IS THE SAME GROUND AT ELEVEN SPECIMENS: // v2.test.claim.namespace_xl0.bound_value_resolve_refusal bvr_outcomes drives one front end over // eleven inline modules and stores one verdict arm per module, portable for the same reason. @@ -689,6 +693,10 @@ data floor_cross_claim_pure_producers_warm: List = [ "v2.test.claim.namespace_xl0.value_read_refusal.vrr_outcomes", "v2.test.claim.namespace_xl0.if_arm_and_statement_lowering_refusal.iasl_outcomes", "v2.test.claim.namespace_xl0.bound_value_resolve_refusal.bvr_outcomes", + "v2.test.claim.body_lowering.enclosing_expression_structure.ees_and_match_value", + "v2.test.claim.body_lowering.enclosing_expression_structure.ees_call_match_value", + "v2.test.claim.body_lowering.enclosing_expression_structure.ees_and_match_walked", + "v2.test.claim.body_lowering.enclosing_expression_structure.ees_call_match_walked", "v2.test.claim.namespace_xl0.wildcard_arm_resolve.wc_outcomes", "v2.test.cli.v2_native_cli.cli_probe_trailing_outcome", "v2.test.emit.closure_emit_arrow_body_refusal.arrow_body_probe_verdict", From 5114e2cea7fd4484324f9715132df835d030ad9a Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 27 Sep 2026 19:05:23 +0000 Subject: [PATCH 8/8] Anchor the share rows away from #12383's append point Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v2/workflow/floor_pure_producer_share.dag | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/v2/workflow/floor_pure_producer_share.dag b/src/v2/workflow/floor_pure_producer_share.dag index 1b7b4e61bcc..3983ab7898b 100644 --- a/src/v2/workflow/floor_pure_producer_share.dag +++ b/src/v2/workflow/floor_pure_producer_share.dag @@ -602,16 +602,16 @@ import v2.std.collection { List } // THE XL-2 SEQUENCE-OPERAND RESOLVE ROW IS THE SAME GROUND AT FIVE SPECIMENS: // v2.test.claim.namespace_xl0.sequence_operand_resolve_refusal sqo_outcomes drives one front end // over five inline modules and stores one verdict arm per module, portable for the same reason. +// THE XL-2 ENCLOSING-EXPRESSION ROW: v2.test.claim.body_lowering.enclosing_expression_structure +// ees_and_match_value, ees_call_match_value, ees_and_match_walked and ees_call_match_walked each +// tokenize and parse one inline module and call one producer on its body expression; the stored value +// is an EesValue (a lowered Node or a reason Symbol, no resolution context and no closure). // THE XL-2 CALL-ARGUMENT VALUE RESOLVE ROW IS THE SAME GROUND AT TWENTY-ONE SPECIMENS: // v2.test.claim.namespace_xl0.call_argument_value_resolve_refusal cav_outcomes drives one front end // over twenty-one inline modules and stores one verdict arm per module, portable for the same reason. // THE XL-2 IF-ARM AND STATEMENT-SPINE ROW IS THE SAME GROUND AT THIRTEEN SPECIMENS: // v2.test.claim.namespace_xl0.if_arm_and_statement_lowering_refusal iasl_outcomes drives one front // end over thirteen inline modules and stores one verdict arm per module, portable for the same reason. -// THE XL-2 ENCLOSING-EXPRESSION ROW: v2.test.claim.body_lowering.enclosing_expression_structure -// ees_and_match_value, ees_call_match_value, ees_and_match_walked and ees_call_match_walked each -// tokenize and parse one inline module and call one producer on its body expression; the stored value -// is an EesValue (a lowered Node or a reason Symbol, no resolution context and no closure). // THE XL-2 BOUND-VALUE RESOLVE ROW IS THE SAME GROUND AT ELEVEN SPECIMENS: // v2.test.claim.namespace_xl0.bound_value_resolve_refusal bvr_outcomes drives one front end over // eleven inline modules and stores one verdict arm per module, portable for the same reason. @@ -681,6 +681,10 @@ data floor_cross_claim_pure_producers_warm: List = [ "v2.test.manual.body_lowering_normalize_add.body_lowering_normalized_module", "v2.test.claim.resolve.namespace_candidate_rule.ncr_outcomes", "v2.test.claim.namespace_xl0.call_argument_resolve_refusal.xl2_outcomes", + "v2.test.claim.body_lowering.enclosing_expression_structure.ees_and_match_value", + "v2.test.claim.body_lowering.enclosing_expression_structure.ees_call_match_value", + "v2.test.claim.body_lowering.enclosing_expression_structure.ees_and_match_walked", + "v2.test.claim.body_lowering.enclosing_expression_structure.ees_call_match_walked", "v2.test.claim.namespace_xl0.sequence_operand_resolve_refusal.sqo_outcomes", "v2.test.claim.namespace_xl0.value_position_whole_read.vpw_outcomes", "v2.test.claim.namespace_xl0.value_position_whole_read.vpw_normalized", @@ -693,10 +697,6 @@ data floor_cross_claim_pure_producers_warm: List = [ "v2.test.claim.namespace_xl0.value_read_refusal.vrr_outcomes", "v2.test.claim.namespace_xl0.if_arm_and_statement_lowering_refusal.iasl_outcomes", "v2.test.claim.namespace_xl0.bound_value_resolve_refusal.bvr_outcomes", - "v2.test.claim.body_lowering.enclosing_expression_structure.ees_and_match_value", - "v2.test.claim.body_lowering.enclosing_expression_structure.ees_call_match_value", - "v2.test.claim.body_lowering.enclosing_expression_structure.ees_and_match_walked", - "v2.test.claim.body_lowering.enclosing_expression_structure.ees_call_match_walked", "v2.test.claim.namespace_xl0.wildcard_arm_resolve.wc_outcomes", "v2.test.cli.v2_native_cli.cli_probe_trailing_outcome", "v2.test.emit.closure_emit_arrow_body_refusal.arrow_body_probe_verdict",