Skip to content

Commit b642703

Browse files
committed
Auto merge of rust-lang#125151 - dingxiangfei2009:let-chain-rescope-crater-runner, r=<try>
[Crater run experiment] let-chain rescoping Please do not merge this PR. This is only for crater run experimentation. I would like to nominate a crater run for rust-lang#107251 assuming that the feature gate is always on. Through this experiment, we hope to collect breakage in the wild. We would focus on both compilation errors and test errors that this edition change can lead to, if users are targeting Edition 2024 and writing code assuming the old semantics unknowingly. I realise that some tests may not work well if an edition switch happens. I am fixing them in the main PR.
2 parents 1871252 + 4509cf0 commit b642703

File tree

9 files changed

+186
-31
lines changed

9 files changed

+186
-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

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ 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 = RvalueScopes::new(true);
7474
debug!("start resolving rvalue scopes, def_id={def_id:?}");
7575
debug!("rvalue_scope: rvalue_candidates={:?}", scope_tree.rvalue_candidates);
7676
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)