Repository navigation
v2 grammar: offer binary_expr before match/if/loop (revives #11911) + the tree-shape control it left open - #12817
Merged
Merged
Conversation
…hain can parse
dag_grammar_expr_expr is an ordered Choice that offered match_expr, if_expr and
loop_expr before binary_expr. But dag_grammar_primary_expr_core's final three
alternatives already ARE those productions, so a block-shaped expression is a
legal binary operand by construction. The ordering denied it: given
`match a { ... } && b` the choice committed to match_expr, which succeeded
having consumed the match alone, and `&& b` reached no production -- the parse
ended with tokens remaining. Nothing was unrepresentable; the alternative that
represents it was never offered.
fn_literal, block_expr and arrow_lambda stay above binary_expr, where they
already were: primary_expr_core admits a braced field-initializer list, so a
block offered below could be taken as a record literal instead. The first tokens
of match/if/loop are disjoint from those three, so moving them below changes
nothing between them.
Evidence, against the live grammar rather than a model of it: the new witness
v2.test.parse.block_expr_as_binary_operand_parse runs source text through
dag_prepared_grammar. Two arms are red before this change -- a match and an if
heading a binary chain -- and three controls are green on both sides: each block
form standing alone, and an operand that was never block-shaped. Whole-corpus
census moves 829 file refusals to 650; 193 leave the parse stage and 14 of those
land deeper, at body lowering or normalize, which is the signature of a wall
moving back rather than vanishing.
Not established, and stated rather than assumed: that a bare block form produces
the same NODE as before, since the controls assert parsing and not shape; and
whether the trailing match/if/loop alternatives are now unreachable, which holds
exactly if binary_expr never fails where they would succeed. They are retained
rather than deleted because that argument is not made here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ft open, warm-enrol its parses A bare match / if still lowers to the Match / Branch construct itself after the reorder (normalized arrow body kind), so routing it through binary_expr neither wraps nor retains it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nion) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
briansrls
approved these changes
Sep 30, 2026
briansrls
left a comment
Contributor
There was a problem hiding this comment.
APPROVE-MERGE at exact head 9e0536d, merge queue only.
No findings. The ordered-choice repair exposes binary_expr before the match/if/loop prefixes it already contains through primary_expr_core, rather than adding a second syntax form. The two formerly denied chain forms are discriminating reds, and the added normalized-tree controls close #11911's open shape question by requiring bare match/if bodies to remain Match/Branch themselves. Warm enrollment and the unchanged ordinary/bare controls bound the reordering.
This approval requires the actual merge_group candidate to pass against then-current main; no direct merge or check bypass.
This was referenced Oct 1, 2026
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Oct 1, 2026
…ing a chain is held by #12817's module) 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.
XL-2, per quiet-seal-543's ruling A on class E: revives #11911 with the control it left open.
Provenance
#11911 ("The expression choice offered binary_expr last, so a block heading a && chain could not parse") was closed unmerged by the operator account at exactly 2026-09-30 00:00. That timing looks like a bulk stale-PR sweep, not a rejection; no review or comment on the PR rejects the change. The grammar commit is cherry-picked unchanged. Its note on
dag_grammar_expr_exprexplains the ordering, what stays abovebinary_exprand why, and what was not established.The defect, found independently
A v2 parse probe on main, one module per case:
a && b(same line)athen&& bon the next linereturn athen&& bmatch a {..} && b(same line)match a {..}then&& breturn match a {..} && bThe newline is irrelevant.
dag_grammar_expr_expris an ordered choice that offeredmatch_expr/if_expr/loop_exprbeforebinary_expr, so the choice commits to the bare block and&& bis left over. v1 reads amatchas a primary and continues the operator loop after it (v102_parseparse_primary), which is the reference.binary_expralready reachesmatch_expr/if_expr/loop_exprthroughprimary_expr_core, so offering it first admits the form without a new production.The control #11911 left open
#11911 asserted that a bare block still parses, not that it yields the same node. After the reorder a bare
match/ifis reached asbinary_expr -> primary_expr_core -> match_expr, so its parse path changes by construction. What must not change is what it lowers to. Added:a_bare_match_still_lowers_to_the_match_itself_holds: the normalized arrow body isComputationNode { behavior: Match }.a_bare_if_still_lowers_to_the_branch_itself_holds: the normalized arrow body isComputationNode { behavior: Branch }.Both pass on main and on this branch. They go red if routing a bare block through
binary_exprever wraps or retains it.The file's parses are now nullary values enrolled warm in
v2.workflow.floor_pure_producer_share, and each claim reads one, so each claim stays within the new-witness eval-step budget.Evidence (claim_batch, 30 GB BuildBuddy runner)
v2.test.parse.block_expr_as_binary_operand_parse:match_heading_a_binary_chain_parses_holdsandif_heading_a_binary_chain_parses_holdsFAIL; all 5 controls pass.Other suites on this branch:
occurrence_roleandexpression_bodied_fn_decl_parsepass.reference_conservation's two refused-normalization reds are pre-existing on main and fixed by reference_conservation: refused-normalization controls use a service module (red on main since #12713) #12812.parse_test_fn_decl_return_clauseexits 1 with no verdict output identically on main, so it is independent of this change.Parse count (the two class-E files)
Both files also carry an
admit_callerstrailing comma, so #12796's grammar change was applied to both arms.test.claim.dispatch_preflight_witness_test&& match)test.claim.approval_device_redemption_witness_test&& code_of_..)#11911 measured the population wider: a whole-corpus census of 829 → 650 file refusals, 673 → 480 at parse, net −179. I have not re-derived that census at this head.
No emitted Rust changes: the v2 grammar has no stage0 mirror.
Land only via the merge queue.
🤖 Generated with Claude Code