Repository navigation
v2 body lowering: a let-match with returning arms heading a tail spine lowers to the match (N7 root F) - #13118
Merged
Merged
Conversation
…e lowers to the match (N7 root F) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…roll the eval-equality control at its measured infer refusal Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Pushed 2 fixes.
Locally against a fresh build: |
…(enrolment margin) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Removed from the merge queue: head 2b5083c has a side-chat review OBJECTION, a silent semantic widening. body_lower_pattern_binders classifies a nullary-constructor pattern atom (e.g. 'A => A') as a binder and renames it to the let binder, so it also matches other variants; and pre-resolve renaming can erase a binder_hides_visible_value refusal. The repair (resolve-honoured MatchArmScopeExitEdge, no lowering-time classification) is in progress on this branch. Do not queue this head. |
…urs, replacing the lowering-time binder rename Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Oct 4, 2026
main (#13118) renamed PositionalPlusOneNamedEdges; the R4 import named the old arm, which emit-build refused. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Oct 4, 2026
Roster file keeps this change's side (the hand roster is deleted). Main added nine roster rows meanwhile (#13118: lme_outcomes, lme_shape, lmee_exit_path, lmee_binding_path; #13069: five ccr_* producers); they are dispositioned by the derivation and a follow-on probe of their three modules. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
N7 root F:
dag/extdeps/uri_path.dagrefused on the native route asbody_lowering_reason_return_not_in_tail_position.parse_segment_tokensbinds four lets from matches that have an early-return arm, e.g.let prefix = match … { Present { value: p } => p Absent => return … }.Derivation (DESIGN §6b)
test.claim.diverging_match_arm_join_witness). v2 lowering admitted only tail returns and guards, so the earliest unjustified boundary is v2 body lowering.Lowering
With exactly one binding arm,
let x = match s { P => v, Q => return e }followed byrestlowers to Match(s, P => Bind[x, v, rest, +MatchArmScopeExitEdge], Q => e).restis lowered once and appears once; a control counts it.Two or more binding arms refuse with
body_lowering_reason_multi_binding_arm_early_return_needs_join_point. Its trigger is v2 infer typing a local fn value.Scope: the declared marker (calm-boar-904's ruling, option A)
Under the arm, the arm's pattern binders would otherwise reach
rest. Lowering cannot tell a binder from a nullary constructor (A, orNoneinSome { value: None }), so it neither classifies nor renames: the authored pattern is kept.v2.std.nodegains oneCoreEdgeLabelarm,MatchArmScopeExitEdge, also added tocore_edge_labelsandcore_edge_label_canonical_symbol.body_lower_let_match_binding_arm_continued.03_resolveresolve_match_arm_admitted, throughresolve_arm_scope_exit_bind. The Bind's value resolves in the arm's scope. Its binder and body resolve in the scope enclosing the match. Constructor-versus-binder and binder-hides-a-visible-value are judged against the authored scopes.resolve_reason_malformed_tree(resolve_bind_node).PositionalPlusOneNamedEdges→PositionalPlusNamedMarkerEdges, admitting at most two named edges. The emitted discipline symbol is unchanged.v2.std.type_binderbind_named_labels_conformadmits the annotation and the marker, each at most once, and nothing else.This replaces this PR's earlier lowering-time rename, which calm-boar-904 objected to and which never merged.
A => Ainto a catch-all.SUBSTRATE CHANGE (for the reviewer)
This PR edits the substrate's node vocabulary and its well-formedness wall, not only a pipeline stage:
v2.std.nodeCoreEdgeLabelgains the armMatchArmScopeExitEdge. It is listed incore_edge_labelsand gets the canonical symbol^core_match_arm_scope_exit_edgeincore_edge_label_canonical_symbol.v2.std.nodeBind edge discipline.PositionalPlusOneNamedEdgesis renamed toPositionalPlusNamedMarkerEdges, and its bound goes from at most one named edge to at most two. Its only other reader,v2.std.compilers.target_model, is renamed with it and still emits the same discipline symbol.v2.std.type_binderbind_named_labels_conform. A Bind's named edges were "the<type-annotation>marker and nothing else". They are now that annotation andMatchArmScopeExitEdge, each at most once, and nothing else.Every existing Bind (annotated or not) conforms unchanged, and a Bind with any other named label still refuses. The only new admitted shape is a Bind carrying the scope-exit marker, and resolve accepts that only as a match arm's body.
Floor rows (amended freeze, sharp-raven-357)
These
floor_cross_claim_pure_producers_warmrows are restored so this PR passes its own floor. royal-deer-478 drops them when #13043 lands.v2.test.claim.namespace_xl0.let_match_early_return.lme_outcomes: will be derived as shared.v2.test.claim.namespace_xl0.let_match_early_return.lme_shape: will be derived as shared.v2.test.claim.callexec.let_match_early_return_eval.lmee_exit_path: becomes aSingleClaimFillDebtModulerow after Floor: derive cross-claim pure-share from planned claims' call-site demand; delete the hand roster #13043.v2.test.claim.callexec.let_match_early_return_eval.lmee_binding_path: becomes aSingleClaimFillDebtModulerow after Floor: derive cross-claim pure-share from planned claims' call-site demand; delete the hand roster #13043.Evidence
All results are local, from
gunbc run --claim-runon a fresh build of this branch merged with main 80fc619.v2.test.claim.namespace_xl0.let_match_early_return: 18/18.LmeB => LmeBbeside returning arms stays a constructor pattern;binder_hides_visible_value;LmeSome { value: LmeB }resolves with LmeB as a constructor;0 => 1is accepted.restoccurs once and every returning operand is kept.parse_segment_tokenspasses body lowering.v2.test.claim.binder_admission.arm_scope_exit_marker: 2/2. These use supplied nodes.malformed_tree;v2.test.claim.callexec.let_match_early_return_eval: 2/2. These are eval-equality rows enrolled at their measured refusal.infer_grounding_not_derived): a branch over a parameter is not yet grounded.early == shaped == 7 / 6, when it is.Regressions, all green:
return_tail_position17/17,body_cast_node26/26,callable_binder_slice9/9,match_arm_list_structure3/3,wildcard_pattern_form5/5,match_position_structure3/3.Red-first. The production files
body_lowering_foldand03_resolvewere swapped in from each earlier state, with the current tests kept and run in sequence. Every new control goes red against at least one earlier state:80fc619b900(main at this branch's merge)c1bacc80aad(rest placed under the arm, no rename)69971fed065/2b5083c967cSome { value: None }; (e) literal arm; a binding arm's value using its own bindersc1de47278c1On control (a). A parameter
ytogether with an armP { f: y } => yis refused at the arm asbinder_hides_visible_value, beforerestresolves. That is control (c), so "the rest binds the outery" is not an observable state. What (a) asserts is that no arm binder reaches the rest. It is red against the pre-hygiene lowering, where the rest silently bound the arm's binder.The RFM row
return_lowered_as_its_operand_outside_the_function_tailrecords the narrowed residue.🤖 Generated with Claude Code