Skip to content

The expression choice offered binary_expr last, so a block heading a && chain could not parse - #11911

Closed
briansrls wants to merge 2 commits into
mainfrom
fix/expr-choice-order-block-as-binary-operand
Closed

briansrls wants to merge 2 commits into
mainfrom
fix/expr-choice-order-block-as-binary-operand

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

An ordering fact, not a missing form

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 same productions — 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.

A discriminating pair, against the live parser

New witness v2.test.parse.block_expr_as_binary_operand_parse runs source text through dag_prepared_grammar — the grammar under judgment, not a model of it.

arm before after
match a {…} && true FAIL PASS
if a {…} else {…} && true FAIL PASS
bare match · bare if · a && true PASS PASS

The three controls are what make this a repair rather than a trade: a change that satisfied the reds by making block forms parse only as operands would take them red.

The population

Whole-corpus census, same native binary both sides:

before after
total file refusals 829 650
at parse 673 480
at later stages 156 170

193 files left the parse stage; 14 of those now fail deeper. Net −179. That signature — the wall moving back rather than vanishing — is what a correct parse repair looks like, and the net is the honest headline, not the parse figure.

What is NOT verified

Tree shape. The controls assert that a bare match/if still parses, not that it yields the same node. A bare match now routes binary_expr → primary_expr_core → match_expr rather than straight to match_expr; if binary_expr with zero operators wraps instead of collapsing to its operand, every bare block form in the corpus changes shape. The census is indirect evidence against it — normalize runs, no new refusals — but it is not a structural assertion. The idiom to close it exists: the sibling expression_bodied_fn_decl_parse_test asserts on the parsed node via find_arrow_body_child. I'd take that before calling this done, and will add it on request.

Deadness of the trailing alternatives. match_expr/if_expr/loop_expr now sit below binary_expr and are unreachable exactly if binary_expr never fails where they would succeed. That argument is not made here, so they are retained rather than deleted, and the annotation says so.

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 read as a record literal.

🤖 Generated with Claude Code

…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>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-20T22:49:27.160489Z ebef802 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Ledger-Repair-Judged: docs/design-rung-drops.md
Heal-Candidate-Run: 35542724612
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