diff options
| author | Nathan Whitaker <nathan.whitaker01@gmail.com> | 2022-10-12 15:29:08 -0700 |
|---|---|---|
| committer | Nathan Whitaker <nathan.whitaker01@gmail.com> | 2022-10-12 17:57:32 -0700 |
| commit | ad8b24272428b28770471f222c19fa3154a65819 (patch) | |
| tree | f38880c0d0afd13e96210f9ff3dfbdf027b68107 | |
| parent | 0938e1680daf66ca6aad428aedf9a920a0dab5ad (diff) | |
| download | rust-ad8b24272428b28770471f222c19fa3154a65819.tar.gz rust-ad8b24272428b28770471f222c19fa3154a65819.zip | |
Let chains should still drop temporaries
by the end of the condition's execution
| -rw-r--r-- | compiler/rustc_ast_lowering/src/expr.rs | 45 | ||||
| -rw-r--r-- | src/test/ui/drop/drop_order.rs | 63 |
2 files changed, 97 insertions, 11 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index c55b4906302..104010f8d43 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -388,7 +388,7 @@ impl<'hir> LoweringContext<'_, 'hir> { else_opt: Option<&Expr>, ) -> hir::ExprKind<'hir> { let lowered_cond = self.lower_expr(cond); - let new_cond = self.manage_let_cond(lowered_cond); + let new_cond = self.wrap_cond_in_drop_scope(lowered_cond); let then_expr = self.lower_block_expr(then); if let Some(rslt) = else_opt { hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), Some(self.lower_expr(rslt))) @@ -397,9 +397,9 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - // If `cond` kind is `let`, returns `let`. Otherwise, wraps and returns `cond` - // in a temporary block. - fn manage_let_cond(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> { + // Wraps a condition (i.e. `cond` in `if cond` or `while cond`) in a terminating scope + // so that temporaries created in the condition don't live beyond it. + fn wrap_cond_in_drop_scope(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> { fn has_let_expr<'hir>(expr: &'hir hir::Expr<'hir>) -> bool { match expr.kind { hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), @@ -407,12 +407,35 @@ impl<'hir> LoweringContext<'_, 'hir> { _ => false, } } - if has_let_expr(cond) { - cond - } else { - let reason = DesugaringKind::CondTemporary; - let span_block = self.mark_span_with_reason(reason, cond.span, None); - self.expr_drop_temps(span_block, cond, AttrVec::new()) + + // 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 mantain 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 { + hir::ExprKind::Binary( + op @ Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + lhs, + rhs, + ) if has_let_expr(cond) => { + let lhs = self.wrap_cond_in_drop_scope(lhs); + let rhs = self.wrap_cond_in_drop_scope(rhs); + + self.arena.alloc(self.expr( + cond.span, + hir::ExprKind::Binary(op, lhs, rhs), + AttrVec::new(), + )) + } + hir::ExprKind::Let(_) => cond, + _ => { + let reason = DesugaringKind::CondTemporary; + let span_block = self.mark_span_with_reason(reason, cond.span, None); + self.expr_drop_temps(span_block, cond, AttrVec::new()) + } } } @@ -440,7 +463,7 @@ impl<'hir> LoweringContext<'_, 'hir> { opt_label: Option<Label>, ) -> hir::ExprKind<'hir> { let lowered_cond = self.with_loop_condition_scope(|t| t.lower_expr(cond)); - let new_cond = self.manage_let_cond(lowered_cond); + let new_cond = self.wrap_cond_in_drop_scope(lowered_cond); let then = self.lower_block_expr(body); let expr_break = self.expr_break(span, AttrVec::new()); let stmt_break = self.stmt_expr(span, expr_break); diff --git a/src/test/ui/drop/drop_order.rs b/src/test/ui/drop/drop_order.rs index e42150dcc09..bf740b6a9ab 100644 --- a/src/test/ui/drop/drop_order.rs +++ b/src/test/ui/drop/drop_order.rs @@ -1,4 +1,5 @@ // run-pass +#![feature(let_chains)] use std::cell::RefCell; use std::convert::TryInto; @@ -116,6 +117,58 @@ impl DropOrderCollector { } } + fn let_chain(&self) { + // take the "then" branch + if self.option_loud_drop(2).is_some() // 2 + && self.option_loud_drop(1).is_some() // 1 + && let Some(_d) = self.option_loud_drop(4) { // 4 + self.print(3); // 3 + } + + // take the "else" branch + if self.option_loud_drop(6).is_some() // 2 + && self.option_loud_drop(5).is_some() // 1 + && let None = self.option_loud_drop(7) { // 3 + unreachable!(); + } else { + self.print(8); // 4 + } + + // let exprs interspersed + if self.option_loud_drop(9).is_some() // 1 + && let Some(_d) = self.option_loud_drop(13) // 5 + && self.option_loud_drop(10).is_some() // 2 + && let Some(_e) = self.option_loud_drop(12) { // 4 + self.print(11); // 3 + } + + // let exprs first + if let Some(_d) = self.option_loud_drop(18) // 5 + && let Some(_e) = self.option_loud_drop(17) // 4 + && self.option_loud_drop(14).is_some() // 1 + && self.option_loud_drop(15).is_some() { // 2 + self.print(16); // 3 + } + + // let exprs last + if self.option_loud_drop(20).is_some() // 2 + && self.option_loud_drop(19).is_some() // 1 + && let Some(_d) = self.option_loud_drop(23) // 5 + && let Some(_e) = self.option_loud_drop(22) { // 4 + self.print(21); // 3 + } + } + + fn while_(&self) { + let mut v = self.option_loud_drop(4); + while let Some(_d) = v + && self.option_loud_drop(1).is_some() + && self.option_loud_drop(2).is_some() { + self.print(3); + v = None; + } + } + fn assert_sorted(self) { assert!( self.0 @@ -142,4 +195,14 @@ fn main() { let collector = DropOrderCollector::default(); collector.match_(); collector.assert_sorted(); + + println!("-- let chain --"); + let collector = DropOrderCollector::default(); + collector.let_chain(); + collector.assert_sorted(); + + println!("-- while --"); + let collector = DropOrderCollector::default(); + collector.while_(); + collector.assert_sorted(); } |
