Let chains should still drop temporaries
by the end of the condition's execution
This commit is contained in:
parent
0938e1680d
commit
ad8b242724
@ -388,7 +388,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||||||
else_opt: Option<&Expr>,
|
else_opt: Option<&Expr>,
|
||||||
) -> hir::ExprKind<'hir> {
|
) -> hir::ExprKind<'hir> {
|
||||||
let lowered_cond = self.lower_expr(cond);
|
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);
|
let then_expr = self.lower_block_expr(then);
|
||||||
if let Some(rslt) = else_opt {
|
if let Some(rslt) = else_opt {
|
||||||
hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), Some(self.lower_expr(rslt)))
|
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`
|
// Wraps a condition (i.e. `cond` in `if cond` or `while cond`) in a terminating scope
|
||||||
// in a temporary block.
|
// so that temporaries created in the condition don't live beyond it.
|
||||||
fn manage_let_cond(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> {
|
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 {
|
fn has_let_expr<'hir>(expr: &'hir hir::Expr<'hir>) -> bool {
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
|
hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
|
||||||
@ -407,12 +407,35 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if has_let_expr(cond) {
|
|
||||||
cond
|
// We have to take special care for `let` exprs in the condition, e.g. in
|
||||||
} else {
|
// `if let pat = val` or `if foo && let pat = val`, as we _do_ want `val` to live beyond the
|
||||||
let reason = DesugaringKind::CondTemporary;
|
// condition in this case.
|
||||||
let span_block = self.mark_span_with_reason(reason, cond.span, None);
|
//
|
||||||
self.expr_drop_temps(span_block, cond, AttrVec::new())
|
// 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>,
|
opt_label: Option<Label>,
|
||||||
) -> hir::ExprKind<'hir> {
|
) -> hir::ExprKind<'hir> {
|
||||||
let lowered_cond = self.with_loop_condition_scope(|t| t.lower_expr(cond));
|
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 then = self.lower_block_expr(body);
|
||||||
let expr_break = self.expr_break(span, AttrVec::new());
|
let expr_break = self.expr_break(span, AttrVec::new());
|
||||||
let stmt_break = self.stmt_expr(span, expr_break);
|
let stmt_break = self.stmt_expr(span, expr_break);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// run-pass
|
// run-pass
|
||||||
|
#![feature(let_chains)]
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::convert::TryInto;
|
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) {
|
fn assert_sorted(self) {
|
||||||
assert!(
|
assert!(
|
||||||
self.0
|
self.0
|
||||||
@ -142,4 +195,14 @@ fn main() {
|
|||||||
let collector = DropOrderCollector::default();
|
let collector = DropOrderCollector::default();
|
||||||
collector.match_();
|
collector.match_();
|
||||||
collector.assert_sorted();
|
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();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user