Skip to content

Commit 75cea03

Browse files
committed
de-duplicate condition scoping logic
1 parent 5e749eb commit 75cea03

File tree

3 files changed

+33
-56
lines changed

3 files changed

+33
-56
lines changed

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
532532
then: &Block,
533533
else_opt: Option<&Expr>,
534534
) -> hir::ExprKind<'hir> {
535-
let lowered_cond = self.lower_cond(cond);
535+
let lowered_cond = self.lower_expr(cond);
536536
let then_expr = self.lower_block_expr(then);
537537
if let Some(rslt) = else_opt {
538538
hir::ExprKind::If(
@@ -545,44 +545,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
545545
}
546546
}
547547

548-
// Lowers a condition (i.e. `cond` in `if cond` or `while cond`), wrapping it in a terminating scope
549-
// so that temporaries created in the condition don't live beyond it.
550-
fn lower_cond(&mut self, cond: &Expr) -> &'hir hir::Expr<'hir> {
551-
fn has_let_expr(expr: &Expr) -> bool {
552-
match &expr.kind {
553-
ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
554-
ExprKind::Let(..) => true,
555-
_ => false,
556-
}
557-
}
558-
559-
// We have to take special care for `let` exprs in the condition, e.g. in
560-
// `if let pat = val` or `if foo && let pat = val`, as we _do_ want `val` to live beyond the
561-
// condition in this case.
562-
//
563-
// In order to maintain the drop behavior for the non `let` parts of the condition,
564-
// we still wrap them in terminating scopes, e.g. `if foo && let pat = val` essentially
565-
// gets transformed into `if { let _t = foo; _t } && let pat = val`
566-
match &cond.kind {
567-
ExprKind::Binary(op @ Spanned { node: ast::BinOpKind::And, .. }, lhs, rhs)
568-
if has_let_expr(cond) =>
569-
{
570-
let op = self.lower_binop(*op);
571-
let lhs = self.lower_cond(lhs);
572-
let rhs = self.lower_cond(rhs);
573-
574-
self.arena.alloc(self.expr(cond.span, hir::ExprKind::Binary(op, lhs, rhs)))
575-
}
576-
ExprKind::Let(..) => self.lower_expr(cond),
577-
_ => {
578-
let cond = self.lower_expr(cond);
579-
let reason = DesugaringKind::CondTemporary;
580-
let span_block = self.mark_span_with_reason(reason, cond.span, None);
581-
self.expr_drop_temps(span_block, cond)
582-
}
583-
}
584-
}
585-
586548
// We desugar: `'label: while $cond $body` into:
587549
//
588550
// ```
@@ -606,7 +568,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
606568
body: &Block,
607569
opt_label: Option<Label>,
608570
) -> hir::ExprKind<'hir> {
609-
let lowered_cond = self.with_loop_condition_scope(|t| t.lower_cond(cond));
571+
let lowered_cond = self.with_loop_condition_scope(|t| t.lower_expr(cond));
610572
let then = self.lower_block_expr(body);
611573
let expr_break = self.expr_break(span);
612574
let stmt_break = self.stmt_expr(span, expr_break);
@@ -2095,7 +2057,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
20952057
/// In terms of drop order, it has the same effect as wrapping `expr` in
20962058
/// `{ let _t = $expr; _t }` but should provide better compile-time performance.
20972059
///
2098-
/// The drop order can be important in e.g. `if expr { .. }`.
2060+
/// The drop order can be important, e.g. to drop temporaries from an `async fn`
2061+
/// body before its parameters.
20992062
pub(super) fn expr_drop_temps(
21002063
&mut self,
21012064
span: Span,

compiler/rustc_hir_analysis/src/check/region.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -176,23 +176,39 @@ fn resolve_block<'tcx>(
176176
visitor.cx = prev_cx;
177177
}
178178

179-
fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
180-
fn has_let_expr(expr: &Expr<'_>) -> bool {
181-
match &expr.kind {
182-
hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
183-
hir::ExprKind::Let(..) => true,
184-
_ => false,
185-
}
186-
}
179+
/// Resolve a condition from an `if` expression or match guard so that it is a terminating scope
180+
/// if it doesn't contain `let` expressions.
181+
fn resolve_cond<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, cond: &'tcx hir::Expr<'tcx>) {
182+
let terminate = match cond.kind {
183+
// Temporaries for `let` expressions must live into the success branch.
184+
hir::ExprKind::Let(_) => false,
185+
// Logical operator chains are handled in `resolve_expr`. Since logical operator chains in
186+
// conditions are lowered to control-flow rather than boolean temporaries, there's no
187+
// temporary to drop for logical operators themselves. `resolve_expr` will also recursively
188+
// wrap any operands in terminating scopes, other than `let` expressions (which we shouldn't
189+
// terminate) and other logical operators (which don't need a terminating scope, since their
190+
// operands will be terminated). Any temporaries that would need to be dropped will be
191+
// dropped before we leave this operator's scope; terminating them here would be redundant.
192+
hir::ExprKind::Binary(
193+
source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
194+
_,
195+
_,
196+
) => false,
197+
// Otherwise, conditions should always drop their temporaries.
198+
_ => true,
199+
};
200+
resolve_expr(visitor, cond, terminate);
201+
}
187202

203+
fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
188204
let prev_cx = visitor.cx;
189205

190206
visitor.enter_node_scope_with_dtor(arm.hir_id.local_id, true);
191207
visitor.cx.var_parent = visitor.cx.parent;
192208

193209
resolve_pat(visitor, arm.pat);
194210
if let Some(guard) = arm.guard {
195-
resolve_expr(visitor, guard, !has_let_expr(guard));
211+
resolve_cond(visitor, guard);
196212
}
197213
resolve_expr(visitor, arm.body, false);
198214

@@ -420,7 +436,7 @@ fn resolve_expr<'tcx>(
420436
};
421437
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
422438
visitor.cx.var_parent = visitor.cx.parent;
423-
visitor.visit_expr(cond);
439+
resolve_cond(visitor, cond);
424440
resolve_expr(visitor, then, true);
425441
visitor.cx = expr_cx;
426442
resolve_expr(visitor, otherwise, true);
@@ -435,7 +451,7 @@ fn resolve_expr<'tcx>(
435451
};
436452
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
437453
visitor.cx.var_parent = visitor.cx.parent;
438-
visitor.visit_expr(cond);
454+
resolve_cond(visitor, cond);
439455
resolve_expr(visitor, then, true);
440456
visitor.cx = expr_cx;
441457
}

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,9 @@ fn report_unexpected_variant_res(
428428
}
429429
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(..), hir_id, .. }) => {
430430
suggestion.push((expr.span.shrink_to_lo(), "(".to_string()));
431-
if let hir::Node::Expr(drop_temps) = tcx.parent_hir_node(*hir_id)
432-
&& let hir::ExprKind::DropTemps(_) = drop_temps.kind
433-
&& let hir::Node::Expr(parent) = tcx.parent_hir_node(drop_temps.hir_id)
431+
if let hir::Node::Expr(parent) = tcx.parent_hir_node(*hir_id)
434432
&& let hir::ExprKind::If(condition, block, None) = parent.kind
435-
&& condition.hir_id == drop_temps.hir_id
433+
&& condition.hir_id == *hir_id
436434
&& let hir::ExprKind::Block(block, _) = block.kind
437435
&& block.stmts.is_empty()
438436
&& let Some(expr) = block.expr

0 commit comments

Comments
 (0)