Rollup merge of #103034 - nathanwhit:let-chains-rhs-temporaries, r=wesleywiser

Let expressions on RHS shouldn't be terminating scopes

Fixes #100276.

Before this PR, we were unconditionally marking the RHS of short-circuiting binary expressions as a terminating scope.

In the case of a let chain where the `let` expression was on the RHS, this meant that temporaries within the `let` expr would only live until the end of the expression. Since this only affected the RHS, this led to surprising behavior ([example](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=d1b0a5d1f01882f9c89c2194a75eb19f)).

After this PR, we only mark the RHS as a terminating scope if it is not a `let` expression.
This commit is contained in:
Dylan DPC 2022-10-19 14:05:52 +05:30 committed by GitHub
commit 48c5e0c262
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 24 additions and 8 deletions

View File

@ -252,9 +252,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
) => { ) => {
// For shortcircuiting operators, mark the RHS as a terminating // For shortcircuiting operators, mark the RHS as a terminating
// scope since it only executes conditionally. // scope since it only executes conditionally.
terminating(r.hir_id.local_id);
}
// `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries
// should live beyond the immediate expression
if !matches!(r.kind, hir::ExprKind::Let(_)) {
terminating(r.hir_id.local_id);
}
}
hir::ExprKind::If(_, ref then, Some(ref otherwise)) => { hir::ExprKind::If(_, ref then, Some(ref otherwise)) => {
terminating(then.hir_id.local_id); terminating(then.hir_id.local_id);
terminating(otherwise.hir_id.local_id); terminating(otherwise.hir_id.local_id);

View File

@ -129,10 +129,10 @@ fn let_chain(&self) {
// take the "else" branch // take the "else" branch
if self.option_loud_drop(6).is_some() // 2 if self.option_loud_drop(6).is_some() // 2
&& self.option_loud_drop(5).is_some() // 1 && self.option_loud_drop(5).is_some() // 1
&& let None = self.option_loud_drop(7) { // 3 && let None = self.option_loud_drop(8) { // 4
unreachable!(); unreachable!();
} else { } else {
self.print(8); // 4 self.print(7); // 3
} }
// let exprs interspersed // let exprs interspersed

View File

@ -0,0 +1,12 @@
// check-pass
// compile-flags: -Z validate-mir
#![feature(let_chains)]
fn let_chains(entry: std::io::Result<std::fs::DirEntry>) {
if let Ok(entry) = entry
&& let Some(s) = entry.file_name().to_str()
&& s.contains("")
{}
}
fn main() {}

View File

@ -12,7 +12,7 @@
pub struct DropLogger<'a, T> { pub struct DropLogger<'a, T> {
extra: T, extra: T,
id: usize, id: usize,
log: &'a panic::AssertUnwindSafe<RefCell<Vec<usize>>> log: &'a panic::AssertUnwindSafe<RefCell<Vec<usize>>>,
} }
impl<'a, T> Drop for DropLogger<'a, T> { impl<'a, T> Drop for DropLogger<'a, T> {
@ -55,9 +55,9 @@ fn main() {
else { else {
// 10 is not constructed // 10 is not constructed
d(10, None) d(10, None)
} },
); );
assert_eq!(get(), vec![3, 8, 7, 1, 2]); assert_eq!(get(), vec![8, 7, 1, 3, 2]);
} }
assert_eq!(get(), vec![0, 4, 6, 9, 5]); assert_eq!(get(), vec![0, 4, 6, 9, 5]);
@ -89,5 +89,5 @@ fn main() {
panic::panic_any(InjectedFailure) panic::panic_any(InjectedFailure)
); );
}); });
assert_eq!(get(), vec![14, 19, 20, 17, 15, 11, 18, 16, 12, 13]); assert_eq!(get(), vec![20, 17, 15, 11, 19, 18, 16, 12, 14, 13]);
} }