Repository navigation
XL-2: if-arms lower through the statement authority; a statement followed by another refuses located - #12221
Conversation
…owed by another refuses located
The then/else arms of an if were read by the operand reader alone, so a call
argument in an arm (`if c { g(p: x) }`) reached no reference site. An arm is a
brace statement sequence and now lowers through body_lower_stmt_spine.
body_lower_try_statement_spine handed only let-headed spines to
body_lower_stmt_spine, so `{ c \n x }` dropped `x`. It now hands over every
spine with a following statement; a non-binding statement followed by another
refuses as statement_precedes_without_binding.
Adds the witness v2.test.claim.namespace_xl0.if_arm_and_statement_lowering_refusal
(13 claims) and the RFM row a_wildcard_match_arm_resolves_as_an_unbound_name for
the pre-existing wildcard resolve defect the if-arm repair exposes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…owered Addresses review 70856: the header cited body_lower_if_part_lowered, a symbol from an earlier revision that no longer exists, and described the arms as going through the condition reader. They go through body_lower_stmt_spine. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review 70856 (stale symbol): fixed in 8dfa610. The witness header cited — sent from zesty-dove-429 |
briansrls
left a comment
There was a problem hiding this comment.
APPROVE on exact head 8dfa610 for MERGE-QUEUE landing only.
This closes XL-2 gaps 1 and 2 at the claimed grain. The if-arm path now sends ordinary arm bodies through the complete statement-spine lowerer before extracting the lowered value, while the explicit nested else-if route retains the existing if reader. That addresses the silent narrowing demonstrated by the call-argument specimens without broadening the claim to Optional-member access.
The statement-spine change is likewise appropriately bounded: it dispatches any spine with a successor to the existing reducer, so a non-let predecessor reaches the already-located body_lowering_reason_statement_precedes_without_binding refusal instead of silently discarding the tail. Single-statement and nested-if neighbour controls preserve the old accepted cases.
Review 70856's requested discriminators are present on this exact head. The 13-claim witness is enrolled and passes on the floor; the six targeted claims discriminate against main and the two neighbour controls stay green. All five exact-head checks are green.
The main-to-head identity census is adequately dispositioned: no refusing identity is removed; the three added refusing identities are the enumerated list-literal class owned by #12208; two common identities change reason; 15 modules lose dropped atoms and none drop more. Keep those facts separate from the local repair claim.
The Optional accessor remains explicitly outstanding with its three specimens. The wildcard-match-arm RFM is correctly entered as a pre-existing resolve defect exposed here, not repaired by this PR.
Carrier impact after landing: mark XL-2 gaps 1 and 2 delivered only. Leave the Optional accessor gap open. Enqueue this exact head; do not direct-merge it. Require the merge_group candidate and its checks against then-current main.
# Conflicts: # src/v2/workflow/floor_pure_producer_share.dag
briansrls
left a comment
There was a problem hiding this comment.
Approved for merge-queue landing at exact head c351015.
This refreshes my approval at 8dfa610. The new head is a merge commit whose first parent is that exact approved head and whose other parent is the main revision merged after the queue conflict. The merge commit records one conflict resolution in src/v2/workflow/floor_pure_producer_share.dag, retaining both enrolment comments; src/v2/compiler/body_lowering_fold.dag and the XL-2 implementation merged cleanly.
Revalidated on c351015:
- all five exact-head checks pass;
- all 13 if_arm_and_statement_lowering_refusal claims were planned and passed;
- the required floor is clean;
- the PR is currently mergeable/clean.
Review 70899's operand-reader fallback remark is explicitly not a finding and remains scoped to the declared accessor follow-up. The XL-2 gaps 1 and 2 approval therefore stands unchanged.
Approval is for merge-queue landing only, with the merge_group candidate required to revalidate against then-current main.
…ntReferenceVisibility as delivered by #12221 (its own owner claim) and re-partitions the witness, so both program-status files take main's version; this PR keeps only the reference_conservation restatements Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d as refused-not-accepted, and is the shared refusing control (the caret fixture no longer refuses) Seed run at 1204ef3 (fierce-gull-556): the_block_second_statement_is_reported_dropped_holds went red once it asked reference_conservation_accepted -- since #12221 a statement followed by another refuses located, so the old 'one drop' verdict had been the refusal standing in for a drop (the masking this predicate exists to expose). a_refusing_module_is_neither_accepted_nor_admitted_holds was red because a caret operand no longer refuses. The block fixture is now a_block_with_a_second_statement_is_refused_not_accepted_holds (refused > 0, not accepted, not admitted), the shared mutation for every lowered-and-... control; the caret fixture is removed. block_second_statement_numbers prints the report's counts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
XL-2 continuation, gaps 1 and 2 of body_lowering_fold. Gap 3 (the Optional accessor) is not claimed fixed; see §4.
1. What was wrong, and the repair
If-arms were read by the operand reader alone.
body_lower_try_if_from_capturedlowered the condition withbody_lower_value_lowered, but read each arm withbody_lower_if_arm_operand_optional. That operand reader reads a callg(p: x)asg. So on main, an undeclaredxin either arm resolved, and a block arm kept only its first statement.An arm is
{ stmt_seq }, andbody_lower_stmt_spineis already the one authority for a statement sequence. The newbody_lower_if_arm_loweredlowers each arm through it:letbinds the rest, and any other statement followed by another refuses;else ifarm is anif_expr, not a brace body, so it keeps theifreader;Three readers were tried before this one, and the reasons they failed are recorded in the code comment:
value_lowered) deep-unwraps and folds amatchspine bottom-up, which reads the pattern_as a reference;A statement spine went to
body_lower_stmt_spineonly when let-headed. The guardbody_lower_stmt_spine_is_let_headedlet every other multi-statement body fall to the expression walkers, which lowered the first statement and dropped the rest, so{ c \n x }resolved withxundeclared. It is replaced bybody_lower_stmt_spine_has_successor: any spine with a following statement goes to the authority, which already refuses a non-binding statement that precedes another asstatement_precedes_without_binding. A one-statement spine keeps its path.This is the existing verdict applied, not a new rule:
{ 1 \n c }now refuses even with every name declared. No module in the stratified sample refuses this way (§3).No stage0 file embeds these functions, so there is no stage0 install. #12173 changed the same fold without one.
2. Witness: red on main 57f5b94, green on this head
The witness is
v2.test.claim.namespace_xl0.if_arm_and_statement_lowering_refusal, 13 claims. It runs on the same native front end + resolve route ascall_argument_value_resolve_refusal. Its produceriasl_outcomesis enrolled WARM inv2.workflow.floor_pure_producer_share.The witness deliberately avoids list literals and lambdas, so #12198 and #12208 do not flip it. CI still has to plan and pass these claims on this head; the table above is from direct remote runs.
3. Census: main 57f5b94 vs this head
Instrument:
v2.compiler.reference_conservation_censusreference_conservation_census_for_pathsover the 315 files ofreference_conservation_stratified_sample_pathsplusexpression_bodied_fn_decl_parse_test.dag. Onlybody_lowering_fold.dagdiffers between the two runs. Refusal reasons come fromnative_test_context_from_ingestfile_refusalsfor every module that refuses on either side (100 modules).Added or changed refusal identities (path, fatal reason), each with a disposition:
join([...], "")) was silently dropped on main. #12208 lowers it.Removed: none.
src/v2/std/cross_tree/resolution.dagrefused withmatch_arm_navigation_refusedunder an intermediate version of this change; on this head it normalizes.Atom conservation outside those modules: 15 modules drop fewer atoms, for example
host_effect_plan73→53,ci_compile_jobs34→13 andcross_tree/resolution170→128. No module drops more.compiler_closure_emit_driver,systemctl_status_readandruntime_configmove one or two atoms from conserved tolocus_erased: they reach the normalized tree without their occurrence, and none is dropped.4. Gap 3, the Optional accessor: not fixed, RFM row stays open
I could not build a probe that isolates
body_lower_operand_ref_optionalanswering Absent instead of a located refusal, meaning red on main and green on a head that fixes it.lowering_accessor_collapses_a_sequence_operandis therefore not retired.Three specimens were found in the same silent-narrowing family. They are recorded here for the owner of that row; I did not edit the row, because #12198 appends to it:
if c { c } else if c && (x => x) { c } else { c }: an unreadable lambda operand in anelse ifcondition resolves on main and on this head. The same operand at the top of a fn body refuses withparen_group_unread.matchdirectly in a match arm, whose call argument is[c](BlgB { r: y } =>\n match s { BlgA => cc(a: c, b: [c]) ... }): resolves on main and on this head, so the list is dropped silently.accelerator_demo_eval.dagabove: a list-literal refusal inside nested match arms surfaces asmatch_arm_navigation_refused, and the cause is lost.5. New RFM row: a pre-existing resolve defect, exposed and not fixed
gunbc.recurring_failure_mode.a_wildcard_match_arm_resolves_as_an_unbound_name:match s { BlgA => c \n _ => c }refuses on main 57f5b94 asresolve_reason_unbound_symbolat_;if c { s } else { match s { .. _ => .. } }now refuses the same way, where main had dropped that match whole;Proposed carrier wording
compiler_frontend_program_status.dagis not edited here. Proposed wording for its owner: "If-arms lower through the statement authority (body_lower_stmt_spine): a call argument in either arm reaches resolve, and a statement followed by another refuses as statement_precedes_without_binding at any depth. The Optional accessor's Absent (lowering_accessor_collapses_a_sequence_operand) remains open; its specimens are in #12221."🤖 Generated with Claude Code