@@ -388,7 +388,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
388
388
else_opt : Option < & Expr > ,
389
389
) -> hir:: ExprKind < ' hir > {
390
390
let lowered_cond = self . lower_expr ( cond) ;
391
- let new_cond = self . manage_let_cond ( lowered_cond) ;
391
+ let new_cond = self . wrap_cond_in_drop_scope ( lowered_cond) ;
392
392
let then_expr = self . lower_block_expr ( then) ;
393
393
if let Some ( rslt) = else_opt {
394
394
hir:: ExprKind :: If ( new_cond, self . arena . alloc ( then_expr) , Some ( self . lower_expr ( rslt) ) )
@@ -397,22 +397,45 @@ impl<'hir> LoweringContext<'_, 'hir> {
397
397
}
398
398
}
399
399
400
- // If `cond` kind is `let`, returns `let`. Otherwise, wraps and returns `cond`
401
- // in a temporary block .
402
- fn manage_let_cond ( & mut self , cond : & ' hir hir:: Expr < ' hir > ) -> & ' hir hir:: Expr < ' hir > {
400
+ // Wraps a condition (i.e. `cond` in `if cond` or `while cond`) in a terminating scope
401
+ // so that temporaries created in the condition don't live beyond it .
402
+ fn wrap_cond_in_drop_scope ( & mut self , cond : & ' hir hir:: Expr < ' hir > ) -> & ' hir hir:: Expr < ' hir > {
403
403
fn has_let_expr < ' hir > ( expr : & ' hir hir:: Expr < ' hir > ) -> bool {
404
404
match expr. kind {
405
405
hir:: ExprKind :: Binary ( _, lhs, rhs) => has_let_expr ( lhs) || has_let_expr ( rhs) ,
406
406
hir:: ExprKind :: Let ( ..) => true ,
407
407
_ => false ,
408
408
}
409
409
}
410
- if has_let_expr ( cond) {
411
- cond
412
- } else {
413
- let reason = DesugaringKind :: CondTemporary ;
414
- let span_block = self . mark_span_with_reason ( reason, cond. span , None ) ;
415
- self . expr_drop_temps ( span_block, cond, AttrVec :: new ( ) )
410
+
411
+ // We have to take special care for `let` exprs in the condition, e.g. in
412
+ // `if let pat = val` or `if foo && let pat = val`, as we _do_ want `val` to live beyond the
413
+ // condition in this case.
414
+ //
415
+ // In order to mantain the drop behavior for the non `let` parts of the condition,
416
+ // we still wrap them in terminating scopes, e.g. `if foo && let pat = val` essentially
417
+ // gets transformed into `if { let _t = foo; _t } && let pat = val`
418
+ match cond. kind {
419
+ hir:: ExprKind :: Binary (
420
+ op @ Spanned { node : hir:: BinOpKind :: And | hir:: BinOpKind :: Or , .. } ,
421
+ lhs,
422
+ rhs,
423
+ ) if has_let_expr ( cond) => {
424
+ let lhs = self . wrap_cond_in_drop_scope ( lhs) ;
425
+ let rhs = self . wrap_cond_in_drop_scope ( rhs) ;
426
+
427
+ self . arena . alloc ( self . expr (
428
+ cond. span ,
429
+ hir:: ExprKind :: Binary ( op, lhs, rhs) ,
430
+ AttrVec :: new ( ) ,
431
+ ) )
432
+ }
433
+ hir:: ExprKind :: Let ( _) => cond,
434
+ _ => {
435
+ let reason = DesugaringKind :: CondTemporary ;
436
+ let span_block = self . mark_span_with_reason ( reason, cond. span , None ) ;
437
+ self . expr_drop_temps ( span_block, cond, AttrVec :: new ( ) )
438
+ }
416
439
}
417
440
}
418
441
@@ -440,7 +463,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
440
463
opt_label : Option < Label > ,
441
464
) -> hir:: ExprKind < ' hir > {
442
465
let lowered_cond = self . with_loop_condition_scope ( |t| t. lower_expr ( cond) ) ;
443
- let new_cond = self . manage_let_cond ( lowered_cond) ;
466
+ let new_cond = self . wrap_cond_in_drop_scope ( lowered_cond) ;
444
467
let then = self . lower_block_expr ( body) ;
445
468
let expr_break = self . expr_break ( span, AttrVec :: new ( ) ) ;
446
469
let stmt_break = self . stmt_expr ( span, expr_break) ;
0 commit comments