Skip to content

if let and while let support#1282

Open
sbillig wants to merge 4 commits intoargotorg:masterfrom
sbillig:if-let
Open

if let and while let support#1282
sbillig wants to merge 4 commits intoargotorg:masterfrom
sbillig:if-let

Conversation

@sbillig
Copy link
Collaborator

@sbillig sbillig commented Feb 25, 2026

fixes #1249
fixes #1250

We don't support rust-2024-style if-let in compound conditions yet (eg if let .. = y && let ... && foo {

This also fixes a loop-lowering bug in yul codegen.

@sbillig
Copy link
Collaborator Author

sbillig commented Feb 25, 2026

Maybe we could remove tree sitter parser.c from the repo and generate it on the fly? Then we won't have 35k line diffs every time we make a syntax change.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11593f2b51

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@sbillig sbillig marked this pull request as draft February 25, 2026 06:16
@sbillig sbillig marked this pull request as ready for review February 27, 2026 19:47
@sbillig
Copy link
Collaborator Author

sbillig commented Feb 27, 2026

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Hooray!

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for Rust-2024-style if let / while let condition expressions (including && let-chains) across parsing → HIR lowering/typecheck/CTFE → MIR lowering → codegen, and fixes a Yul loop-lowering/control-flow emission bug.

Changes:

  • Extend the parser and tree-sitter grammar to recognize condition-only let expressions in if/while, including && chains and a grammar-level restriction against mixing unparenthesized || with let conditions.
  • Introduce a dedicated HIR Cond tree (separate from Expr) and update visitors, type checking, const checks, and CTFE to evaluate/validate condition chains and their bindings correctly.
  • Update MIR lowering and Yul emission to handle condition decision trees (not just a single branch terminator) and fix loop/control-flow lowering issues; refresh snapshots and formatting fixtures accordingly.

Reviewed changes

Copilot reviewed 74 out of 75 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
crates/uitest/fixtures/ty_check/if_let_chain_scope.snap New snapshot asserting let-chain bindings don’t leak outside condition/body scopes.
crates/uitest/fixtures/ty_check/if_let_chain_scope.fe New typecheck fixture for if let / while let chains and scoping.
crates/uitest/fixtures/ty_check/const_eval/if_let_condition.snap New snapshot for const-eval + if let condition behavior.
crates/uitest/fixtures/ty_check/const_eval/if_let_condition.fe New CTFE fixture using if let in a const fn context.
crates/uitest/fixtures/parser/if_let_or_chain.snap New parser diagnostic snapshot for `
crates/uitest/fixtures/parser/if_let_or_chain.fe New parser fixture exercising `if let ...
crates/tree-sitter-fe/src/node-types.json Adds node types for condition-specific binary expressions and let_condition.
crates/tree-sitter-fe/src/grammar.json Adds condition sub-grammars to model let conditions and restrict `
crates/tree-sitter-fe/grammar.js Implements condition-only let_condition + condition grammar variants and conflicts.
crates/parser/test_files/syntax_node/stmts/while.snap Updates syntax-node snapshot to include while let parsing.
crates/parser/test_files/syntax_node/stmts/while.fe Adds while let examples to parser test input.
crates/parser/test_files/syntax_node/exprs/if.snap Updates syntax-node snapshot to include if let parsing.
crates/parser/test_files/syntax_node/exprs/if.fe Adds if let examples (incl. else if let) to parser test input.
crates/parser/src/syntax_kind.rs Introduces SyntaxKind::LetExpr for condition-only let expressions.
crates/parser/src/parser/stmt.rs Switches while condition parsing to parse_condition_expr.
crates/parser/src/parser/expr_atom.rs Switches if condition parsing to parse_condition_expr.
crates/parser/src/parser/expr.rs Implements condition parsing with let expressions and `
crates/parser/src/ast/stmt.rs Adds AST tests for while let condition parsing and `
crates/parser/src/ast/expr.rs Adds AST support/tests for LetExpr and let-chains in if conditions.
crates/mir/tests/fixtures/trait_assoc_fn_call.mir.snap Snapshot update due to control-flow lowering changes.
crates/mir/tests/fixtures/if_let_while_let.mir.snap New MIR snapshot validating if let / while let lowering.
crates/mir/tests/fixtures/if_let_while_let.fe New MIR fixture covering if let and while let.
crates/mir/tests/fixtures/for_continue.mir.snap Snapshot update due to branch lowering changes.
crates/mir/tests/fixtures/echo.mir.snap Snapshot update due to branch lowering changes.
crates/mir/tests/fixtures/contract_field_layout_slots_const_hole.mir.snap Snapshot update due to branch lowering changes.
crates/mir/tests/fixtures/const_hole_storage_map_contract_defaults.mir.snap Snapshot update due to branch lowering changes.
crates/mir/tests/fixtures/abi_decode.mir.snap Snapshot update due to branch lowering changes.
crates/mir/src/lower/mod.rs Updates MIR builder to traverse the new Cond structure for return-param collection.
crates/mir/src/lower/match_lowering.rs Adds lowering for let conditions into match/decision-tree branching.
crates/mir/src/lower/expr.rs Lowers if/while using CondId via lower_condition_branch (supports decision trees).
crates/language-server/test_files/completion.snap Snapshot update (completion list shifted by unrelated symbol set changes).
crates/hir/tests/ty_check.rs Skips unresolved spans when collecting typed-property snapshots (stability fix).
crates/hir/test_files/ty_check/if_.snap Snapshot update reflecting if let typing and new test cases.
crates/hir/test_files/ty_check/if_.fe Adds if let typing test (no-else behavior) and updates file layout.
crates/hir/test_files/ty_check/contract_effects.snap Snapshot update due to condition typing/printing changes.
crates/hir/src/core/visitor.rs Updates visitor traversal to walk Cond trees (and bind patterns/exprs inside).
crates/hir/src/core/print.rs Adds pretty-printing for Cond nodes.
crates/hir/src/core/lower/stmt.rs Lowers while condition into Cond instead of Expr.
crates/hir/src/core/lower/expr.rs Lowers if condition into Cond and rejects direct lowering of let as normal Expr.
crates/hir/src/core/lower/event.rs Threads conds store into body creation for event lowering.
crates/hir/src/core/lower/body.rs Adds conds NodeStore to Body lowering context and final body assembly.
crates/hir/src/core/hir_def/stmt.rs Changes While to store CondId instead of ExprId.
crates/hir/src/core/hir_def/expr.rs Introduces Cond / CondId and changes Expr::If to store CondId.
crates/hir/src/core/hir_def/body.rs Adds conds NodeStore to Body.
crates/hir/src/analysis/ty/ty_check/stmt.rs Typechecks while via check_cond with correct scoping/pending-binding handling.
crates/hir/src/analysis/ty/ty_check/expr.rs Implements check_cond + check_let_condition; updates if typing/scoping for let-chains.
crates/hir/src/analysis/ty/ty_check/env.rs Adds enter_lexical_scope and clear_pending_bindings utilities for let-chain handling.
crates/hir/src/analysis/ty/ctfe.rs Adds eval_cond so CTFE can evaluate let-conditions and logical condition chains.
crates/hir/src/analysis/ty/const_check.rs Adds check_cond so const checking visits let-conditions and condition trees.
crates/fmt/tests/fixtures/loops.snap Snapshot update for formatted while let.
crates/fmt/tests/fixtures/loops.fe Adds unformatted while let case to formatter fixtures.
crates/fmt/tests/fixtures/if_else_width.snap Snapshot update to include if let width behavior.
crates/fmt/tests/fixtures/if_else_width.fe Adds unformatted if let single-line case to formatter fixtures.
crates/fmt/src/ast/types.rs Adds formatting dispatch for ExprKind::Let.
crates/fmt/src/ast/stmt.rs Adjusts while formatting node handling (robustness with new AST shape).
crates/fmt/src/ast/expr.rs Adds LetExpr formatter and adjusts if formatting node handling (robustness).
crates/fe/tests/fixtures/fe_test/if_let_while_let.fe End-to-end test fixture for if let / while let (incl. let-chains).
crates/codegen/tests/fixtures/sonatina_ir/range_bounds.snap Snapshot update from changed branching lowering patterns.
crates/codegen/tests/fixtures/sonatina_ir/if_else.snap Snapshot update from changed branching lowering patterns.
crates/codegen/tests/fixtures/sonatina_ir/high_level_contract.snap Snapshot update from changed branching lowering patterns.
crates/codegen/tests/fixtures/sonatina_ir/for_range.snap Snapshot update from changed branching lowering patterns.
crates/codegen/tests/fixtures/sonatina_ir/erc20.snap Snapshot update from changed branching lowering patterns.
crates/codegen/tests/fixtures/sonatina_ir/create_contract.snap Snapshot update from changed branching lowering patterns.
crates/codegen/tests/fixtures/sonatina_ir/const_fn_packed_config.snap Snapshot update from changed branching lowering patterns.
crates/codegen/tests/fixtures/sonatina_ir/const_array_branch.snap Snapshot update from changed branching lowering patterns.
crates/codegen/tests/fixtures/range_bounds.snap Snapshot update of Yul output due to branch emission changes.
crates/codegen/tests/fixtures/if_else.snap Snapshot update of Yul output due to branch emission changes.
crates/codegen/tests/fixtures/high_level_contract.snap Snapshot update of Yul output due to branch emission changes.
crates/codegen/tests/fixtures/for_range.snap Snapshot update of Yul output due to branch emission changes.
crates/codegen/tests/fixtures/erc20.snap Snapshot update of Yul output due to branch emission changes.
crates/codegen/tests/fixtures/create_contract.snap Snapshot update of Yul output due to branch emission changes.
crates/codegen/tests/fixtures/const_fn_packed_config.snap Snapshot update of Yul output due to branch emission changes.
crates/codegen/tests/fixtures/const_array_branch.snap Snapshot update of Yul output due to branch emission changes.
crates/codegen/src/yul/emitter/control_flow.rs Fixes loop/control-flow emission to handle complex loop headers and avoid incorrect join detection.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 332 to +363
parser.set_newline_as_trivia(false);
let is_or = parser.current_kind() == Some(SyntaxKind::Pipe2);
let msg = "`||` cannot be mixed with `let` conditions in this position";
let (_, rbp) = infix_binding_power(parser).unwrap();
let saw_let_before = self
.condition_state
.as_ref()
.is_some_and(|state| state.borrow().saw_let);
let mut reported = false;
if self.allow_let_expr && is_or && saw_let_before {
parser.error_msg_on_current_token(msg);
reported = true;
}
bump_bin_op(parser);
parse_expr_with_min_bp(parser, rbp, false)
parse_expr_with_min_bp(
parser,
rbp,
false,
self.allow_let_expr,
self.condition_state.clone(),
)?;

if self.allow_let_expr
&& is_or
&& !reported
&& self
.condition_state
.as_ref()
.is_some_and(|state| state.borrow().saw_let)
{
parser.error(msg);
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In BinExprScope::parse, when || is followed by a let condition on the RHS (e.g. ready || let Some(x) = ...), the diagnostic is currently emitted via parser.error(msg) after parsing the RHS. That attaches a zero-length range at the next token (often {), rather than highlighting the || operator. Consider detecting let on the RHS upfront (e.g. via lookahead to the next non-trivia token) and using error_msg_on_current_token on the || token so the error location is consistent and points at the operator.

Copilot uses AI. Check for mistakes.
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.

support while let support if let

2 participants