Skip to content

Commit 969f56b

Browse files
rescope temp lifetime in let-chain into IfElse
1 parent 3cb0030 commit 969f56b

File tree

9 files changed

+187
-31
lines changed

9 files changed

+187
-31
lines changed

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,8 @@ declare_features! (
491491
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
492492
/// Allows `if let` guard in match arms.
493493
(unstable, if_let_guard, "1.47.0", Some(51114)),
494+
/// Rescoping temporaries in `if let` to align with Rust 2024.
495+
(unstable, if_let_rescope, "1.78.0", Some(124085)),
494496
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
495497
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
496498
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.

compiler/rustc_hir_typeck/src/rvalue_scopes.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ pub fn resolve_rvalue_scopes<'a, 'tcx>(
7070
def_id: DefId,
7171
) -> RvalueScopes {
7272
let tcx = &fcx.tcx;
73-
let mut rvalue_scopes = RvalueScopes::new();
73+
let mut rvalue_scopes =
74+
RvalueScopes::new(tcx.features().if_let_rescope && tcx.sess.at_least_rust_2024());
7475
debug!("start resolving rvalue scopes, def_id={def_id:?}");
7576
debug!("rvalue_scope: rvalue_candidates={:?}", scope_tree.rvalue_candidates);
7677
for (&hir_id, candidate) in &scope_tree.rvalue_candidates {

compiler/rustc_middle/src/ty/rvalue_scopes.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
77
/// rules laid out in `rustc_hir_analysis::check::rvalue_scopes`.
88
#[derive(TyEncodable, TyDecodable, Clone, Debug, Default, Eq, PartialEq, HashStable)]
99
pub struct RvalueScopes {
10+
rescope_if_let: bool,
1011
map: ItemLocalMap<Option<Scope>>,
1112
}
1213

1314
impl RvalueScopes {
14-
pub fn new() -> Self {
15-
Self { map: <_>::default() }
15+
pub fn new(rescope_if_let: bool) -> Self {
16+
Self { rescope_if_let, map: <_>::default() }
1617
}
1718

1819
/// Returns the scope when the temp created by `expr_id` will be cleaned up.
@@ -39,7 +40,17 @@ impl RvalueScopes {
3940
debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
4041
return Some(id);
4142
}
42-
_ => id = p,
43+
ScopeData::IfThen => {
44+
if self.rescope_if_let {
45+
debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]");
46+
return Some(p);
47+
}
48+
id = p;
49+
}
50+
ScopeData::Node
51+
| ScopeData::CallSite
52+
| ScopeData::Arguments
53+
| ScopeData::Remainder(_) => id = p,
4354
}
4455
}
4556

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,7 @@ symbols! {
978978
ident,
979979
if_let,
980980
if_let_guard,
981+
if_let_rescope,
981982
if_while_or_patterns,
982983
ignore,
983984
impl_header_lifetime_elision,
+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//@ run-pass
2+
//@ edition:2024
3+
//@ compile-flags: -Z validate-mir -Zunstable-options
4+
5+
#![feature(let_chains)]
6+
#![feature(if_let_rescope)]
7+
8+
use std::cell::RefCell;
9+
use std::convert::TryInto;
10+
11+
#[derive(Default)]
12+
struct DropOrderCollector(RefCell<Vec<u32>>);
13+
14+
struct LoudDrop<'a>(&'a DropOrderCollector, u32);
15+
16+
impl Drop for LoudDrop<'_> {
17+
fn drop(&mut self) {
18+
println!("{}", self.1);
19+
self.0.0.borrow_mut().push(self.1);
20+
}
21+
}
22+
23+
impl DropOrderCollector {
24+
fn option_loud_drop(&self, n: u32) -> Option<LoudDrop> {
25+
Some(LoudDrop(self, n))
26+
}
27+
28+
fn print(&self, n: u32) {
29+
println!("{}", n);
30+
self.0.borrow_mut().push(n)
31+
}
32+
33+
fn assert_sorted(self) {
34+
assert!(
35+
self.0
36+
.into_inner()
37+
.into_iter()
38+
.enumerate()
39+
.all(|(idx, item)| idx + 1 == item.try_into().unwrap())
40+
);
41+
}
42+
43+
fn if_let(&self) {
44+
if let None = self.option_loud_drop(1) {
45+
unreachable!();
46+
} else {
47+
self.print(2);
48+
}
49+
50+
if let Some(_) = self.option_loud_drop(4) {
51+
self.print(3);
52+
}
53+
54+
if let Some(_d) = self.option_loud_drop(6) {
55+
self.print(5);
56+
}
57+
}
58+
59+
fn let_chain(&self) {
60+
// take the "then" branch
61+
if self.option_loud_drop(1).is_some() // 1
62+
&& self.option_loud_drop(2).is_some() // 2
63+
&& let Some(_d) = self.option_loud_drop(4)
64+
// 4
65+
{
66+
self.print(3); // 3
67+
}
68+
69+
// take the "else" branch
70+
if self.option_loud_drop(5).is_some() // 1
71+
&& self.option_loud_drop(6).is_some() // 2
72+
&& let None = self.option_loud_drop(7)
73+
// 3
74+
{
75+
unreachable!();
76+
} else {
77+
self.print(8); // 4
78+
}
79+
80+
// let exprs interspersed
81+
if self.option_loud_drop(9).is_some() // 1
82+
&& let Some(_d) = self.option_loud_drop(13) // 5
83+
&& self.option_loud_drop(10).is_some() // 2
84+
&& let Some(_e) = self.option_loud_drop(12)
85+
// 4
86+
{
87+
self.print(11); // 3
88+
}
89+
90+
// let exprs first
91+
if let Some(_d) = self.option_loud_drop(18) // 5
92+
&& let Some(_e) = self.option_loud_drop(17) // 4
93+
&& self.option_loud_drop(14).is_some() // 1
94+
&& self.option_loud_drop(15).is_some()
95+
// 2
96+
{
97+
self.print(16); // 3
98+
}
99+
100+
// let exprs last
101+
if self.option_loud_drop(19).is_some() // 1
102+
&& self.option_loud_drop(20).is_some() // 2
103+
&& let Some(_d) = self.option_loud_drop(23) // 5
104+
&& let Some(_e) = self.option_loud_drop(22)
105+
// 4
106+
{
107+
self.print(21); // 3
108+
}
109+
}
110+
}
111+
112+
fn main() {
113+
println!("-- if let --");
114+
let collector = DropOrderCollector::default();
115+
collector.if_let();
116+
collector.assert_sorted();
117+
118+
println!("-- let chain --");
119+
let collector = DropOrderCollector::default();
120+
collector.let_chain();
121+
collector.assert_sorted();
122+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
struct A;
2+
struct B<'a, T>(&'a mut T);
3+
4+
impl A {
5+
fn f(&mut self) -> Option<B<'_, Self>> {
6+
Some(B(self))
7+
}
8+
}
9+
10+
impl<'a, T> Drop for B<'a, T> {
11+
fn drop(&mut self) {
12+
// this is needed to keep NLL's hands off and to ensure
13+
// the inner mutable borrow stays alive
14+
}
15+
}
16+
17+
fn main() {
18+
let mut a = A;
19+
if let None = a.f().as_ref() {
20+
unreachable!()
21+
} else {
22+
a.f().unwrap();
23+
//~^ ERROR cannot borrow `a` as mutable more than once at a time
24+
};
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0499]: cannot borrow `a` as mutable more than once at a time
2+
--> $DIR/feature-gate-if-let-rescope.rs:22:9
3+
|
4+
LL | if let None = a.f().as_ref() {
5+
| -----
6+
| |
7+
| first mutable borrow occurs here
8+
| a temporary with access to the first borrow is created here ...
9+
...
10+
LL | a.f().unwrap();
11+
| ^ second mutable borrow occurs here
12+
LL |
13+
LL | };
14+
| - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option<B<'_, A>>`
15+
16+
error: aborting due to 1 previous error
17+
18+
For more information about this error, try `rustc --explain E0499`.

tests/ui/nll/issue-54556-niconii.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ check-pass
12
// This is a reduction of a concrete test illustrating a case that was
23
// annoying to Rust developer niconii (see comment thread on #21114).
34
//
@@ -19,7 +20,7 @@ impl Mutex {
1920
fn main() {
2021
let counter = Mutex;
2122

22-
if let Ok(_) = counter.lock() { } //~ ERROR does not live long enough
23+
if let Ok(_) = counter.lock() { }
2324

2425
// With this code as written, the dynamic semantics here implies
2526
// that `Mutex::drop` for `counter` runs *before*
@@ -28,4 +29,5 @@ fn main() {
2829
//
2930
// The goal of #54556 is to explain that within a compiler
3031
// diagnostic.
32+
()
3133
}

tests/ui/nll/issue-54556-niconii.stderr

-26
This file was deleted.

0 commit comments

Comments
 (0)