diff options
| author | dianne <diannes.gm@gmail.com> | 2025-06-28 01:37:33 -0700 |
|---|---|---|
| committer | dianne <diannes.gm@gmail.com> | 2025-07-05 17:14:06 -0700 |
| commit | 75cea03e0385b793a640f3f74237e3caf5060641 (patch) | |
| tree | 23b50c3d9720a5ac2a0b98cdf8329d2b437acabc | |
| parent | 5e749eb66f93ee998145399fbdde337e57cd72ef (diff) | |
| download | rust-75cea03e0385b793a640f3f74237e3caf5060641.tar.gz rust-75cea03e0385b793a640f3f74237e3caf5060641.zip | |
de-duplicate condition scoping logic
| -rw-r--r-- | compiler/rustc_ast_lowering/src/expr.rs | 45 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/region.rs | 38 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/lib.rs | 6 |
3 files changed, 33 insertions, 56 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index c2140514e31..9942862a25d 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -532,7 +532,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( @@ -545,44 +545,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: // // ``` @@ -606,7 +568,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); @@ -2095,7 +2057,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, diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index c458878da15..0c575430f0e 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -176,15 +176,31 @@ fn resolve_block<'tcx>( visitor.cx = prev_cx; } -fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) { - fn has_let_expr(expr: &Expr<'_>) -> bool { - match &expr.kind { - hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), - hir::ExprKind::Let(..) => true, - _ => false, - } - } +/// Resolve a condition from an `if` expression or match guard so that it is a terminating scope +/// if it doesn't contain `let` expressions. +fn resolve_cond<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, cond: &'tcx hir::Expr<'tcx>) { + let terminate = match cond.kind { + // Temporaries for `let` expressions must live into the success branch. + hir::ExprKind::Let(_) => false, + // Logical operator chains are handled in `resolve_expr`. Since logical operator chains in + // conditions are lowered to control-flow rather than boolean temporaries, there's no + // temporary to drop for logical operators themselves. `resolve_expr` will also recursively + // wrap any operands in terminating scopes, other than `let` expressions (which we shouldn't + // terminate) and other logical operators (which don't need a terminating scope, since their + // operands will be terminated). Any temporaries that would need to be dropped will be + // dropped before we leave this operator's scope; terminating them here would be redundant. + hir::ExprKind::Binary( + source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + _, + _, + ) => false, + // Otherwise, conditions should always drop their temporaries. + _ => true, + }; + resolve_expr(visitor, cond, terminate); +} +fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) { let prev_cx = visitor.cx; visitor.enter_node_scope_with_dtor(arm.hir_id.local_id, true); @@ -192,7 +208,7 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: resolve_pat(visitor, arm.pat); if let Some(guard) = arm.guard { - resolve_expr(visitor, guard, !has_let_expr(guard)); + resolve_cond(visitor, guard); } resolve_expr(visitor, arm.body, false); @@ -420,7 +436,7 @@ fn resolve_expr<'tcx>( }; visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data }); visitor.cx.var_parent = visitor.cx.parent; - visitor.visit_expr(cond); + resolve_cond(visitor, cond); resolve_expr(visitor, then, true); visitor.cx = expr_cx; resolve_expr(visitor, otherwise, true); @@ -435,7 +451,7 @@ fn resolve_expr<'tcx>( }; visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data }); visitor.cx.var_parent = visitor.cx.parent; - visitor.visit_expr(cond); + resolve_cond(visitor, cond); resolve_expr(visitor, then, true); visitor.cx = expr_cx; } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 1cc618e2aee..b3de473acdb 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -428,11 +428,9 @@ fn report_unexpected_variant_res( } hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(..), hir_id, .. }) => { suggestion.push((expr.span.shrink_to_lo(), "(".to_string())); - if let hir::Node::Expr(drop_temps) = tcx.parent_hir_node(*hir_id) - && let hir::ExprKind::DropTemps(_) = drop_temps.kind - && let hir::Node::Expr(parent) = tcx.parent_hir_node(drop_temps.hir_id) + if let hir::Node::Expr(parent) = tcx.parent_hir_node(*hir_id) && let hir::ExprKind::If(condition, block, None) = parent.kind - && condition.hir_id == drop_temps.hir_id + && condition.hir_id == *hir_id && let hir::ExprKind::Block(block, _) = block.kind && block.stmts.is_empty() && let Some(expr) = block.expr |
