Skip to content

Commit 9f2e753

Browse files
committed
Auto merge of #86965 - sexxi-goose:rfc2229-improve-lint, r=nikomatsakis,lqd
Improves migrations lint for RFC2229 This PR improves the current disjoint capture migration lint by providing more information on why drop order or auto trait implementation for a closure is impacted by the use of the new feature. The drop order migration lint will now look something like this: ``` error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:163:21 | LL | let c = || { | ^^ ... LL | tuple.0; | ------- in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` ... LL | } | - in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure ``` The auto trait migration lint will now look something like this: ``` error: changes to closure capture in Rust 2021 will affect `Send` trait implementation for closure --> $DIR/auto_traits.rs:14:19 | LL | thread::spawn(move || unsafe { | ^^^^^^^^^^^^^^ in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr.0` does not implement `Send` ... LL | *fptr.0 = 20; | ------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0` ``` r? `@nikomatsakis` Closes rust-lang/project-rfc-2229#54
2 parents 99f8efe + 08c6167 commit 9f2e753

25 files changed

+1274
-444
lines changed

compiler/rustc_typeck/src/check/upvar.rs

+243-154
Large diffs are not rendered by default.

src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-rustfix
22
#![deny(rust_2021_incompatible_closure_captures)]
3+
//~^ NOTE: the lint level is defined here
34

45
use std::thread;
56

@@ -11,9 +12,12 @@ fn test_send_trait() {
1112
let mut f = 10;
1213
let fptr = SendPointer(&mut f as *mut i32);
1314
thread::spawn(move || { let _ = &fptr; unsafe {
14-
//~^ ERROR: `Send` trait implementation
15+
//~^ ERROR: `Send` trait implementation for closure
16+
//~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr.0` does not implement `Send`
17+
//~| NOTE: for more information, see
1518
//~| HELP: add a dummy let to cause `fptr` to be fully captured
1619
*fptr.0 = 20;
20+
//~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0`
1721
} });
1822
}
1923

@@ -28,9 +32,12 @@ fn test_sync_trait() {
2832
let f = CustomInt(&mut f as *mut i32);
2933
let fptr = SyncPointer(f);
3034
thread::spawn(move || { let _ = &fptr; unsafe {
31-
//~^ ERROR: `Sync`, `Send` trait implementation
35+
//~^ ERROR: `Sync`, `Send` trait implementation for closure
36+
//~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure would no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send`
37+
//~| NOTE: for more information, see
3238
//~| HELP: add a dummy let to cause `fptr` to be fully captured
3339
*fptr.0.0 = 20;
40+
//~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0`
3441
} });
3542
}
3643

@@ -49,16 +56,20 @@ impl Clone for U {
4956
fn test_clone_trait() {
5057
let f = U(S(String::from("Hello World")), T(0));
5158
let c = || { let _ = &f;
52-
//~^ ERROR: `Clone` trait implementation, and drop order
59+
//~^ ERROR: `Clone` trait implementation for closure and drop order
60+
//~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f.1` does not implement `Clone`
61+
//~| NOTE: for more information, see
5362
//~| HELP: add a dummy let to cause `f` to be fully captured
5463
let f_1 = f.1;
64+
//~^ NOTE: in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1`
5565
println!("{:?}", f_1.0);
5666
};
5767

5868
let c_clone = c.clone();
5969

6070
c_clone();
6171
}
72+
//~^ NOTE: in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure
6273

6374
fn main() {
6475
test_send_trait();

src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-rustfix
22
#![deny(rust_2021_incompatible_closure_captures)]
3+
//~^ NOTE: the lint level is defined here
34

45
use std::thread;
56

@@ -11,9 +12,12 @@ fn test_send_trait() {
1112
let mut f = 10;
1213
let fptr = SendPointer(&mut f as *mut i32);
1314
thread::spawn(move || unsafe {
14-
//~^ ERROR: `Send` trait implementation
15+
//~^ ERROR: `Send` trait implementation for closure
16+
//~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr.0` does not implement `Send`
17+
//~| NOTE: for more information, see
1518
//~| HELP: add a dummy let to cause `fptr` to be fully captured
1619
*fptr.0 = 20;
20+
//~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0`
1721
});
1822
}
1923

@@ -28,9 +32,12 @@ fn test_sync_trait() {
2832
let f = CustomInt(&mut f as *mut i32);
2933
let fptr = SyncPointer(f);
3034
thread::spawn(move || unsafe {
31-
//~^ ERROR: `Sync`, `Send` trait implementation
35+
//~^ ERROR: `Sync`, `Send` trait implementation for closure
36+
//~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure would no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send`
37+
//~| NOTE: for more information, see
3238
//~| HELP: add a dummy let to cause `fptr` to be fully captured
3339
*fptr.0.0 = 20;
40+
//~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0`
3441
});
3542
}
3643

@@ -49,16 +56,20 @@ impl Clone for U {
4956
fn test_clone_trait() {
5057
let f = U(S(String::from("Hello World")), T(0));
5158
let c = || {
52-
//~^ ERROR: `Clone` trait implementation, and drop order
59+
//~^ ERROR: `Clone` trait implementation for closure and drop order
60+
//~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f.1` does not implement `Clone`
61+
//~| NOTE: for more information, see
5362
//~| HELP: add a dummy let to cause `f` to be fully captured
5463
let f_1 = f.1;
64+
//~^ NOTE: in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1`
5565
println!("{:?}", f_1.0);
5666
};
5767

5868
let c_clone = c.clone();
5969

6070
c_clone();
6171
}
72+
//~^ NOTE: in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure
6273

6374
fn main() {
6475
test_send_trait();
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
error: `Send` trait implementation will change in Rust 2021
2-
--> $DIR/auto_traits.rs:13:19
1+
error: changes to closure capture in Rust 2021 will affect `Send` trait implementation for closure
2+
--> $DIR/auto_traits.rs:14:19
33
|
4-
LL | thread::spawn(move || unsafe {
5-
| ___________________^
6-
LL | |
7-
LL | |
8-
LL | | *fptr.0 = 20;
9-
LL | | });
10-
| |_____^
4+
LL | thread::spawn(move || unsafe {
5+
| ^^^^^^^^^^^^^^ in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr.0` does not implement `Send`
6+
...
7+
LL | *fptr.0 = 20;
8+
| ------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0`
119
|
1210
note: the lint level is defined here
1311
--> $DIR/auto_traits.rs:2:9
@@ -20,53 +18,53 @@ help: add a dummy let to cause `fptr` to be fully captured
2018
LL | thread::spawn(move || { let _ = &fptr; unsafe {
2119
LL |
2220
LL |
21+
LL |
22+
LL |
2323
LL | *fptr.0 = 20;
24-
LL | } });
25-
|
24+
...
2625

27-
error: `Sync`, `Send` trait implementation will change in Rust 2021
28-
--> $DIR/auto_traits.rs:30:19
26+
error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` trait implementation for closure
27+
--> $DIR/auto_traits.rs:34:19
2928
|
30-
LL | thread::spawn(move || unsafe {
31-
| ___________________^
32-
LL | |
33-
LL | |
34-
LL | | *fptr.0.0 = 20;
35-
LL | | });
36-
| |_____^
29+
LL | thread::spawn(move || unsafe {
30+
| ^^^^^^^^^^^^^^ in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure would no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send`
31+
...
32+
LL | *fptr.0.0 = 20;
33+
| --------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0`
3734
|
3835
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
3936
help: add a dummy let to cause `fptr` to be fully captured
4037
|
4138
LL | thread::spawn(move || { let _ = &fptr; unsafe {
4239
LL |
4340
LL |
41+
LL |
42+
LL |
4443
LL | *fptr.0.0 = 20;
45-
LL | } });
46-
|
44+
...
4745

48-
error: `Clone` trait implementation, and drop order will change in Rust 2021
49-
--> $DIR/auto_traits.rs:51:13
46+
error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure and drop order
47+
--> $DIR/auto_traits.rs:58:13
5048
|
51-
LL | let c = || {
52-
| _____________^
53-
LL | |
54-
LL | |
55-
LL | | let f_1 = f.1;
56-
LL | | println!("{:?}", f_1.0);
57-
LL | | };
58-
| |_____^
49+
LL | let c = || {
50+
| ^^ in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f.1` does not implement `Clone`
51+
...
52+
LL | let f_1 = f.1;
53+
| --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1`
54+
...
55+
LL | }
56+
| - in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure
5957
|
6058
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
6159
help: add a dummy let to cause `f` to be fully captured
6260
|
6361
LL | let c = || { let _ = &f;
6462
LL |
6563
LL |
64+
LL |
65+
LL |
6666
LL | let f_1 = f.1;
67-
LL | println!("{:?}", f_1.0);
68-
LL | };
69-
|
67+
...
7068

7169
error: aborting due to 3 previous errors
7270

src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed

+22
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@ fn test1_all_need_migration() {
1818
//~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured
1919

2020
let _t = t.0;
21+
//~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0`
2122
let _t1 = t1.0;
23+
//~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0`
2224
let _t2 = t2.0;
25+
//~^ NOTE: in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0`
2326
};
2427

2528
c();
2629
}
30+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
31+
//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure
32+
//~| in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure
2733

2834
// String implements drop and therefore should be migrated.
2935
// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
@@ -37,12 +43,16 @@ fn test2_only_precise_paths_need_migration() {
3743
//~| NOTE: for more information, see
3844
//~| HELP: add a dummy let to cause `t`, `t1` to be fully captured
3945
let _t = t.0;
46+
//~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0`
4047
let _t1 = t1.0;
48+
//~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0`
4149
let _t2 = t2;
4250
};
4351

4452
c();
4553
}
54+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
55+
//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure
4656

4757
// If a variable would've not been captured by value then it would've not been
4858
// dropped with the closure and therefore doesn't need migration.
@@ -54,11 +64,13 @@ fn test3_only_by_value_need_migration() {
5464
//~| NOTE: for more information, see
5565
//~| HELP: add a dummy let to cause `t` to be fully captured
5666
let _t = t.0;
67+
//~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0`
5768
println!("{}", t1.1);
5869
};
5970

6071
c();
6172
}
73+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
6274

6375
// Copy types get copied into the closure instead of move. Therefore we don't need to
6476
// migrate then as their drop order isn't tied to the closure.
@@ -73,11 +85,13 @@ fn test4_only_non_copy_types_need_migration() {
7385
//~| NOTE: for more information, see
7486
//~| HELP: add a dummy let to cause `t` to be fully captured
7587
let _t = t.0;
88+
//~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0`
7689
let _t1 = t1.0;
7790
};
7891

7992
c();
8093
}
94+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
8195

8296
fn test5_only_drop_types_need_migration() {
8397
struct S(i32, i32);
@@ -92,11 +106,13 @@ fn test5_only_drop_types_need_migration() {
92106
//~| NOTE: for more information, see
93107
//~| HELP: add a dummy let to cause `t` to be fully captured
94108
let _t = t.0;
109+
//~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0`
95110
let _s = s.0;
96111
};
97112

98113
c();
99114
}
115+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
100116

101117
// Since we are using a move closure here, both `t` and `t1` get moved
102118
// even though they are being used by ref inside the closure.
@@ -108,10 +124,14 @@ fn test6_move_closures_non_copy_types_might_need_migration() {
108124
//~| NOTE: for more information, see
109125
//~| HELP: add a dummy let to cause `t1`, `t` to be fully captured
110126
println!("{} {}", t1.1, t.1);
127+
//~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1`
128+
//~| NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1`
111129
};
112130

113131
c();
114132
}
133+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure
134+
//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure
115135

116136
// Test migration analysis in case of Drop + Non Drop aggregates.
117137
// Note we need migration here only because the non-copy (because Drop type) is captured,
@@ -124,10 +144,12 @@ fn test7_drop_non_drop_aggregate_need_migration() {
124144
//~| NOTE: for more information, see
125145
//~| HELP: add a dummy let to cause `t` to be fully captured
126146
let _t = t.0;
147+
//~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0`
127148
};
128149

129150
c();
130151
}
152+
//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure
131153

132154
fn main() {
133155
test1_all_need_migration();

0 commit comments

Comments
 (0)