diff options
| author | bors <bors@rust-lang.org> | 2025-07-13 04:20:07 +0000 | 
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-07-13 04:20:07 +0000 | 
| commit | d2baa49a106fad06fbf6202fb6ea8a0b3d2767cc (patch) | |
| tree | 2a6f502acd4c3d9b62f0f08059ca73f9183b4b89 /compiler/rustc_ast_lowering/src/expr.rs | |
| parent | b1d2f2c64c6254b2a935d6a2d29b68f4511cf500 (diff) | |
| parent | 68d860f8be6ce5de68cb0e9969e8426ceb3ad532 (diff) | |
| download | rust-d2baa49a106fad06fbf6202fb6ea8a0b3d2767cc.tar.gz rust-d2baa49a106fad06fbf6202fb6ea8a0b3d2767cc.zip | |
Auto merge of #143213 - dianne:lower-cond-tweaks, r=cjgillot
de-duplicate condition scoping logic between AST→HIR lowering and `ScopeTree` construction There was some overlap between `rustc_ast_lowering::LoweringContext::lower_cond` and `rustc_hir_analysis::check::region::resolve_expr`, so I've removed the former and migrated its logic to the latter, with some simplifications. Consequences: - For `while` and `if` expressions' `let`-chains, this changes the `HirId`s for the `&&`s to properly correspond to their AST nodes. This is how guards were handled already. - This makes match guards share previously-duplicated logic with `if`/`while` expressions. This will also be used by guard pattern[^1] guards. - Aside from legacy syntax extensions (e.g. some builtin macros) that directly feed AST to the compiler, it's currently impossible to put attributes directly on `&&` operators in `let` chains[^2]. Nonetheless, attributes on `&&` operators in `let` chains in `if`/`while` expression conditions are no longer silently ignored and will be lowered. - This no longer wraps conditions in `DropTemps`, so the HIR and THIR will be slightly smaller. - `DesugaringKind::CondTemporary` is now gone. It's no longer applied to any spans, and all uses of it were dead since they were made to account for `if` and `while` being desugared to `match` on a boolean scrutinee. - Should be a marginal perf improvement beyond that due to leveraging [`ScopeTree` construction](https://github.com/rust-lang/rust/blob/5e749eb66f93ee998145399fbdde337e57cd72ef/compiler/rustc_hir_analysis/src/check/region.rs#L312-L355)'s clever handling of `&&` and `||`: - This removes some unnecessary terminating scopes that were placed around top-level `&&` and `||` operators in conditions. When lowered to MIR, logical operator chains don't create intermediate boolean temporaries, so there's no temporary to drop. The linked snippet handles wrapping the operands in terminating scopes as necessary, in case they create temporaries. - The linked snippet takes care of letting `let` temporaries live and terminating other operands, so we don't need separate traversals of `&&` chains for that. [^1]: rust-lang/rust#129967 [^2]: Case-by-case, here's my justification: `#[attr] e1 && e2` applies the attribute to `e1`. In `#[attr] (e1 && e2)` , the attribute is on the parentheses in the AST, plus it'd fail to parse if `e1` or `e2` contains a `let`. In `#[attr] expands_to_let_chain!()`, the attribute would already be ignored (rust-lang/rust#63221) and it'd fail to parse anyway; even if the expansion site is a condition, the expansion wouldn't be parsed with `Restrictions::ALLOW_LET`. If it *was* allowed, the notion of a "reparse context" from https://github.com/rust-lang/rust/issues/61733#issuecomment-509626449 would be necessary in order to make `let`-chains left-associative; multiple places in the compiler assume they are.
Diffstat (limited to 'compiler/rustc_ast_lowering/src/expr.rs')
| -rw-r--r-- | compiler/rustc_ast_lowering/src/expr.rs | 45 | 
1 files changed, 4 insertions, 41 deletions
| diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 8747e624a4a..15e736261d5 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -528,7 +528,7 @@ impl<'hir> LoweringContext<'_, 'hir> { then: &Block, else_opt: Option<&Expr>, ) -> hir::ExprKind<'hir> { - let lowered_cond = self.lower_cond(cond); + let lowered_cond = self.lower_expr(cond); let then_expr = self.lower_block_expr(then); if let Some(rslt) = else_opt { hir::ExprKind::If( @@ -541,44 +541,6 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - // Lowers a condition (i.e. `cond` in `if cond` or `while cond`), wrapping it in a terminating scope - // so that temporaries created in the condition don't live beyond it. - fn lower_cond(&mut self, cond: &Expr) -> &'hir hir::Expr<'hir> { - fn has_let_expr(expr: &Expr) -> bool { - match &expr.kind { - ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), - ExprKind::Let(..) => true, - _ => false, - } - } - - // We have to take special care for `let` exprs in the condition, e.g. in - // `if let pat = val` or `if foo && let pat = val`, as we _do_ want `val` to live beyond the - // condition in this case. - // - // In order to maintain the drop behavior for the non `let` parts of the condition, - // we still wrap them in terminating scopes, e.g. `if foo && let pat = val` essentially - // gets transformed into `if { let _t = foo; _t } && let pat = val` - match &cond.kind { - ExprKind::Binary(op @ Spanned { node: ast::BinOpKind::And, .. }, lhs, rhs) - if has_let_expr(cond) => - { - let op = self.lower_binop(*op); - let lhs = self.lower_cond(lhs); - let rhs = self.lower_cond(rhs); - - self.arena.alloc(self.expr(cond.span, hir::ExprKind::Binary(op, lhs, rhs))) - } - ExprKind::Let(..) => self.lower_expr(cond), - _ => { - let cond = self.lower_expr(cond); - let reason = DesugaringKind::CondTemporary; - let span_block = self.mark_span_with_reason(reason, cond.span, None); - self.expr_drop_temps(span_block, cond) - } - } - } - // We desugar: `'label: while $cond $body` into: // // ``` @@ -602,7 +564,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Block, opt_label: Option<Label>, ) -> hir::ExprKind<'hir> { - let lowered_cond = self.with_loop_condition_scope(|t| t.lower_cond(cond)); + let lowered_cond = self.with_loop_condition_scope(|t| t.lower_expr(cond)); let then = self.lower_block_expr(body); let expr_break = self.expr_break(span); let stmt_break = self.stmt_expr(span, expr_break); @@ -2091,7 +2053,8 @@ impl<'hir> LoweringContext<'_, 'hir> { /// In terms of drop order, it has the same effect as wrapping `expr` in /// `{ let _t = $expr; _t }` but should provide better compile-time performance. /// - /// The drop order can be important in e.g. `if expr { .. }`. + /// The drop order can be important, e.g. to drop temporaries from an `async fn` + /// body before its parameters. pub(super) fn expr_drop_temps( &mut self, span: Span, | 
