diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/test/ui/closures | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/ui/closures')
332 files changed, 12904 insertions, 0 deletions
diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs new file mode 100644 index 000000000..2bcbd792e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -0,0 +1,22 @@ +// edition:2021 +#![feature(rustc_attrs)] + +// Ensure that capture analysis results in arrays being completely captured. +fn main() { + let mut m = [1, 2, 3, 4, 5]; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + m[0] += 10; + //~^ NOTE: Capturing m[] -> MutBorrow + //~| NOTE: Min Capture m[] -> MutBorrow + m[1] += 40; + //~^ NOTE: Capturing m[] -> MutBorrow + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr new file mode 100644 index 000000000..129b26456 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -0,0 +1,53 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/arrays-completely-captured.rs:8:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/arrays-completely-captured.rs:11:5 + | +LL | / || { +LL | | +LL | | +LL | | m[0] += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:14:9 + | +LL | m[0] += 10; + | ^ +note: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:17:9 + | +LL | m[1] += 40; + | ^ + +error: Min Capture analysis includes: + --> $DIR/arrays-completely-captured.rs:11:5 + | +LL | / || { +LL | | +LL | | +LL | | m[0] += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:14:9 + | +LL | m[0] += 10; + | ^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.rs b/src/test/ui/closures/2229_closure_analysis/by_value.rs new file mode 100644 index 000000000..d8d3bbee2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/by_value.rs @@ -0,0 +1,37 @@ +// edition:2021 + +// Test that we handle derferences properly when only some of the captures are being moved with +// `capture_disjoint_fields` enabled. +#![feature(rustc_attrs)] + +#[derive(Debug, Default)] +struct SomeLargeType; +struct MuchLargerType([SomeLargeType; 32]); + +// Ensure that we don't capture any derefs when moving captures into the closures, +// i.e. only data from the enclosing stack is moved. +fn big_box() { + let s = MuchLargerType(Default::default()); + let b = Box::new(s); + let t = (b, 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + let p = t.0.0; + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> ByValue + println!("{} {:?}", t.1, p); + //~^ NOTE: Capturing t[(1, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow + }; + + c(); +} + +fn main() { + big_box(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.stderr b/src/test/ui/closures/2229_closure_analysis/by_value.stderr new file mode 100644 index 000000000..097462253 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/by_value.stderr @@ -0,0 +1,58 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/by_value.rs:18:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/by_value.rs:21:5 + | +LL | / || { +LL | | +LL | | +LL | | let p = t.0.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue + --> $DIR/by_value.rs:24:17 + | +LL | let p = t.0.0; + | ^^^^^ +note: Capturing t[(1, 0)] -> ImmBorrow + --> $DIR/by_value.rs:27:29 + | +LL | println!("{} {:?}", t.1, p); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/by_value.rs:21:5 + | +LL | / || { +LL | | +LL | | +LL | | let p = t.0.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/by_value.rs:24:17 + | +LL | let p = t.0.0; + | ^^^^^ +note: Min Capture t[(1, 0)] -> ImmBorrow + --> $DIR/by_value.rs:27:29 + | +LL | println!("{} {:?}", t.1, p); + | ^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs new file mode 100644 index 000000000..dc53b3176 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs @@ -0,0 +1,33 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let p = Point { x: 10, y: 10 }; + let q = Point { x: 10, y: 10 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + println!("{:?}", p); + //~^ NOTE: Capturing p[] -> ImmBorrow + //~| NOTE: Min Capture p[] -> ImmBorrow + println!("{:?}", p.x); + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + + println!("{:?}", q.x); + //~^ NOTE: Capturing q[(0, 0)] -> ImmBorrow + println!("{:?}", q); + //~^ NOTE: Capturing q[] -> ImmBorrow + //~| NOTE: Min Capture q[] -> ImmBorrow + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr new file mode 100644 index 000000000..fceafb9c8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr @@ -0,0 +1,68 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-analysis-1.rs:15:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/capture-analysis-1.rs:18:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{:?}", p); +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[] -> ImmBorrow + --> $DIR/capture-analysis-1.rs:21:26 + | +LL | println!("{:?}", p); + | ^ +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/capture-analysis-1.rs:24:26 + | +LL | println!("{:?}", p.x); + | ^^^ +note: Capturing q[(0, 0)] -> ImmBorrow + --> $DIR/capture-analysis-1.rs:27:26 + | +LL | println!("{:?}", q.x); + | ^^^ +note: Capturing q[] -> ImmBorrow + --> $DIR/capture-analysis-1.rs:29:26 + | +LL | println!("{:?}", q); + | ^ + +error: Min Capture analysis includes: + --> $DIR/capture-analysis-1.rs:18:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{:?}", p); +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[] -> ImmBorrow + --> $DIR/capture-analysis-1.rs:21:26 + | +LL | println!("{:?}", p); + | ^ +note: Min Capture q[] -> ImmBorrow + --> $DIR/capture-analysis-1.rs:29:26 + | +LL | println!("{:?}", q); + | ^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs new file mode 100644 index 000000000..99d12f8d8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs @@ -0,0 +1,28 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +#[derive(Debug)] +struct Point { + x: String, + y: i32, +} + +fn main() { + let mut p = Point { x: String::new(), y: 10 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + let _x = p.x; + //~^ NOTE: Capturing p[(0, 0)] -> ByValue + //~| NOTE: p[] captured as ByValue here + println!("{:?}", p); + //~^ NOTE: Capturing p[] -> ImmBorrow + //~| NOTE: Min Capture p[] -> ByValue + //~| NOTE: p[] used here + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr new file mode 100644 index 000000000..cb44ca266 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr @@ -0,0 +1,56 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-analysis-2.rs:14:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/capture-analysis-2.rs:17:5 + | +LL | / || { +LL | | +LL | | +LL | | let _x = p.x; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ByValue + --> $DIR/capture-analysis-2.rs:20:18 + | +LL | let _x = p.x; + | ^^^ +note: Capturing p[] -> ImmBorrow + --> $DIR/capture-analysis-2.rs:23:26 + | +LL | println!("{:?}", p); + | ^ + +error: Min Capture analysis includes: + --> $DIR/capture-analysis-2.rs:17:5 + | +LL | / || { +LL | | +LL | | +LL | | let _x = p.x; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[] -> ByValue + --> $DIR/capture-analysis-2.rs:20:18 + | +LL | let _x = p.x; + | ^^^ p[] captured as ByValue here +... +LL | println!("{:?}", p); + | ^ p[] used here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs new file mode 100644 index 000000000..3f337097d --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs @@ -0,0 +1,33 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +#[derive(Debug)] +struct Child { + c: String, + d: String, +} + +#[derive(Debug)] +struct Parent { + b: Child, +} + +fn main() { + let mut a = Parent { b: Child {c: String::new(), d: String::new()} }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + let _x = a.b.c; + //~^ NOTE: Capturing a[(0, 0),(0, 0)] -> ByValue + //~| NOTE: a[(0, 0)] captured as ByValue here + println!("{:?}", a.b); + //~^ NOTE: Capturing a[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture a[(0, 0)] -> ByValue + //~| NOTE: a[(0, 0)] used here + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr new file mode 100644 index 000000000..71e7bdc35 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr @@ -0,0 +1,56 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-analysis-3.rs:19:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/capture-analysis-3.rs:22:5 + | +LL | / || { +LL | | +LL | | +LL | | let _x = a.b.c; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing a[(0, 0),(0, 0)] -> ByValue + --> $DIR/capture-analysis-3.rs:25:18 + | +LL | let _x = a.b.c; + | ^^^^^ +note: Capturing a[(0, 0)] -> ImmBorrow + --> $DIR/capture-analysis-3.rs:28:26 + | +LL | println!("{:?}", a.b); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-analysis-3.rs:22:5 + | +LL | / || { +LL | | +LL | | +LL | | let _x = a.b.c; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture a[(0, 0)] -> ByValue + --> $DIR/capture-analysis-3.rs:25:18 + | +LL | let _x = a.b.c; + | ^^^^^ a[(0, 0)] captured as ByValue here +... +LL | println!("{:?}", a.b); + | ^^^ a[(0, 0)] used here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs new file mode 100644 index 000000000..bc46ec997 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs @@ -0,0 +1,31 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +#[derive(Debug)] +struct Child { + c: String, + d: String, +} + +#[derive(Debug)] +struct Parent { + b: Child, +} + +fn main() { + let mut a = Parent { b: Child {c: String::new(), d: String::new()} }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + let _x = a.b; + //~^ NOTE: Capturing a[(0, 0)] -> ByValue + //~| NOTE: Min Capture a[(0, 0)] -> ByValue + println!("{:?}", a.b.c); + //~^ NOTE: Capturing a[(0, 0),(0, 0)] -> ImmBorrow + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr new file mode 100644 index 000000000..7e6e625bc --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr @@ -0,0 +1,53 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-analysis-4.rs:19:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/capture-analysis-4.rs:22:5 + | +LL | / || { +LL | | +LL | | +LL | | let _x = a.b; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing a[(0, 0)] -> ByValue + --> $DIR/capture-analysis-4.rs:25:18 + | +LL | let _x = a.b; + | ^^^ +note: Capturing a[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/capture-analysis-4.rs:28:26 + | +LL | println!("{:?}", a.b.c); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-analysis-4.rs:22:5 + | +LL | / || { +LL | | +LL | | +LL | | let _x = a.b; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture a[(0, 0)] -> ByValue + --> $DIR/capture-analysis-4.rs:25:18 + | +LL | let _x = a.b; + | ^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs new file mode 100644 index 000000000..6fd151553 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -0,0 +1,29 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 10 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + println!("{}", p.x); + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow + }; + + // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. + let py = &mut p.y; + + c(); + *py = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr new file mode 100644 index 000000000..0f64ecf3a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr @@ -0,0 +1,48 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-disjoint-field-struct.rs:13:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/capture-disjoint-field-struct.rs:16:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:19:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-disjoint-field-struct.rs:16:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:19:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs new file mode 100644 index 000000000..8d3bb3262 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -0,0 +1,24 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +fn main() { + let mut t = (10, 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + println!("{}", t.0); + //~^ NOTE: Capturing t[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow + }; + + // `c` only captures t.0, therefore mutating t.1 is allowed. + let t1 = &mut t.1; + + c(); + *t1 = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr new file mode 100644 index 000000000..a8ca9622a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr @@ -0,0 +1,48 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-disjoint-field-tuple.rs:8:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/capture-disjoint-field-tuple.rs:11:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", t.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:14:24 + | +LL | println!("{}", t.0); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-disjoint-field-tuple.rs:11:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", t.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:14:24 + | +LL | println!("{}", t.0); + | ^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enum-field.rs b/src/test/ui/closures/2229_closure_analysis/capture-enum-field.rs new file mode 100644 index 000000000..bbe3aa31a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-enum-field.rs @@ -0,0 +1,27 @@ +// edition:2021 +// run-pass + +#[derive(Debug, PartialEq, Eq)] +pub enum Color { + RGB(u8, u8, u8), +} + +fn main() { + let mut color = Color::RGB(0, 0, 0); + let mut red = |v| { + let Color::RGB(ref mut r, _, _) = color; + *r = v; + }; + let mut green = |v| { + let Color::RGB(_, ref mut g, _) = color; + *g = v; + }; + let mut blue = |v| { + let Color::RGB(_, _, ref mut b) = color; + *b = v; + }; + red(1); + green(2); + blue(3); + assert_eq!(Color::RGB(1, 2, 3), color); +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.rs b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs new file mode 100644 index 000000000..322ae99b8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs @@ -0,0 +1,62 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +enum Info { + Point(i32, i32, String), + Meta(String, Vec<(i32, i32)>) +} + +fn multi_variant_enum() { + let point = Info::Point(10, -10, "1".into()); + + let vec = Vec::new(); + let meta = Info::Meta("meta".into(), vec); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + if let Info::Point(_, _, str) = point { + //~^ NOTE: Capturing point[] -> ImmBorrow + //~| NOTE: Capturing point[(2, 0)] -> ByValue + //~| NOTE: Min Capture point[] -> ByValue + println!("{}", str); + } + + if let Info::Meta(_, v) = meta { + //~^ NOTE: Capturing meta[] -> ImmBorrow + //~| NOTE: Capturing meta[(1, 1)] -> ByValue + //~| NOTE: Min Capture meta[] -> ByValue + println!("{:?}", v); + } + }; + + c(); +} + +enum SingleVariant { + Point(i32, i32, String), +} + +fn single_variant_enum() { + let point = SingleVariant::Point(10, -10, "1".into()); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + let SingleVariant::Point(_, _, str) = point; + //~^ NOTE: Capturing point[(2, 0)] -> ByValue + //~| NOTE: Min Capture point[(2, 0)] -> ByValue + println!("{}", str); + }; + + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr new file mode 100644 index 000000000..8a6ba8444 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr @@ -0,0 +1,113 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-enums.rs:16:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-enums.rs:47:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/capture-enums.rs:19:5 + | +LL | / || { +LL | | +LL | | +LL | | if let Info::Point(_, _, str) = point { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing point[] -> ImmBorrow + --> $DIR/capture-enums.rs:22:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ +note: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:22:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ +note: Capturing meta[] -> ImmBorrow + --> $DIR/capture-enums.rs:29:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ +note: Capturing meta[(1, 1)] -> ByValue + --> $DIR/capture-enums.rs:29:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-enums.rs:19:5 + | +LL | / || { +LL | | +LL | | +LL | | if let Info::Point(_, _, str) = point { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture point[] -> ByValue + --> $DIR/capture-enums.rs:22:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ +note: Min Capture meta[] -> ByValue + --> $DIR/capture-enums.rs:29:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: First Pass analysis includes: + --> $DIR/capture-enums.rs:50:5 + | +LL | / || { +LL | | +LL | | +LL | | let SingleVariant::Point(_, _, str) = point; +... | +LL | | println!("{}", str); +LL | | }; + | |_____^ + | +note: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:53:47 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/capture-enums.rs:50:5 + | +LL | / || { +LL | | +LL | | +LL | | let SingleVariant::Point(_, _, str) = point; +... | +LL | | println!("{}", str); +LL | | }; + | |_____^ + | +note: Min Capture point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:53:47 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs new file mode 100644 index 000000000..3341166e2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs @@ -0,0 +1,50 @@ +// edition:2021 + +#![feature(rustc_attrs)] +#![allow(unused)] + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} +#[derive(Debug)] +struct Line { + p: Point, + q: Point +} +#[derive(Debug)] +struct Plane { + a: Line, + b: Line, +} + +fn main() { + let mut p = Plane { + a: Line { + p: Point { x: 1,y: 2 }, + q: Point { x: 3,y: 4 }, + }, + b: Line { + p: Point { x: 1,y: 2 }, + q: Point { x: 3,y: 4 }, + } + }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let x = &p.a.p.x; + //~^ NOTE: Capturing p[(0, 0),(0, 0),(0, 0)] -> ImmBorrow + p.b.q.y = 9; + //~^ NOTE: Capturing p[(1, 0),(1, 0),(1, 0)] -> MutBorrow + //~| NOTE: p[] captured as MutBorrow here + println!("{:?}", p); + //~^ NOTE: Capturing p[] -> ImmBorrow + //~| NOTE: Min Capture p[] -> MutBorrow + //~| NOTE: p[] used here + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr new file mode 100644 index 000000000..29e1af043 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr @@ -0,0 +1,61 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/deep-multilevel-struct.rs:34:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/deep-multilevel-struct.rs:37:5 + | +LL | / || { +LL | | +LL | | +LL | | let x = &p.a.p.x; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0),(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/deep-multilevel-struct.rs:40:18 + | +LL | let x = &p.a.p.x; + | ^^^^^^^ +note: Capturing p[(1, 0),(1, 0),(1, 0)] -> MutBorrow + --> $DIR/deep-multilevel-struct.rs:42:9 + | +LL | p.b.q.y = 9; + | ^^^^^^^ +note: Capturing p[] -> ImmBorrow + --> $DIR/deep-multilevel-struct.rs:45:26 + | +LL | println!("{:?}", p); + | ^ + +error: Min Capture analysis includes: + --> $DIR/deep-multilevel-struct.rs:37:5 + | +LL | / || { +LL | | +LL | | +LL | | let x = &p.a.p.x; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[] -> MutBorrow + --> $DIR/deep-multilevel-struct.rs:42:9 + | +LL | p.b.q.y = 9; + | ^^^^^^^ p[] captured as MutBorrow here +... +LL | println!("{:?}", p); + | ^ p[] used here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs new file mode 100644 index 000000000..34b0132f3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs @@ -0,0 +1,24 @@ +// edition:2021 +#![feature(rustc_attrs)] +#![allow(unused)] + +fn main() { + let mut t = (((1,2),(3,4)),((5,6),(7,8))); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let x = &t.0.0.0; + //~^ NOTE: Capturing t[(0, 0),(0, 0),(0, 0)] -> ImmBorrow + t.1.1.1 = 9; + //~^ NOTE: Capturing t[(1, 0),(1, 0),(1, 0)] -> MutBorrow + //~| NOTE: t[] captured as MutBorrow here + println!("{:?}", t); + //~^ NOTE: Min Capture t[] -> MutBorrow + //~| NOTE: Capturing t[] -> ImmBorrow + //~| NOTE: t[] used here + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr new file mode 100644 index 000000000..e91751676 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr @@ -0,0 +1,61 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/deep-multilevel-tuple.rs:8:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/deep-multilevel-tuple.rs:11:5 + | +LL | / || { +LL | | +LL | | +LL | | let x = &t.0.0.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/deep-multilevel-tuple.rs:14:18 + | +LL | let x = &t.0.0.0; + | ^^^^^^^ +note: Capturing t[(1, 0),(1, 0),(1, 0)] -> MutBorrow + --> $DIR/deep-multilevel-tuple.rs:16:9 + | +LL | t.1.1.1 = 9; + | ^^^^^^^ +note: Capturing t[] -> ImmBorrow + --> $DIR/deep-multilevel-tuple.rs:19:26 + | +LL | println!("{:?}", t); + | ^ + +error: Min Capture analysis includes: + --> $DIR/deep-multilevel-tuple.rs:11:5 + | +LL | / || { +LL | | +LL | | +LL | | let x = &t.0.0.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[] -> MutBorrow + --> $DIR/deep-multilevel-tuple.rs:16:9 + | +LL | t.1.1.1 = 9; + | ^^^^^^^ t[] captured as MutBorrow here +... +LL | println!("{:?}", t); + | ^ t[] used here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs new file mode 100644 index 000000000..6c65a7bf8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -0,0 +1,77 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +// Test to ensure Index projections are handled properly during capture analysis +// The array should be moved in entirety, even though only some elements are used. +fn arrays() { + let arr: [String; 5] = [format!("A"), format!("B"), format!("C"), format!("D"), format!("E")]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let [a, b, .., e] = arr; + //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Min Capture arr[] -> ByValue + assert_eq!(a, "A"); + assert_eq!(b, "B"); + assert_eq!(e, "E"); + }; + + c(); +} + +struct Point { + x: i32, + y: i32, + id: String, +} + +fn structs() { + let mut p = Point { x: 10, y: 10, id: String::new() }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let Point { x: ref mut x, y: _, id: moved_id } = p; + //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow + //~| NOTE: Capturing p[(2, 0)] -> ByValue + //~| NOTE: Min Capture p[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture p[(2, 0)] -> ByValue + + println!("{}, {}", x, moved_id); + }; + c(); +} + +fn tuples() { + let mut t = (10, String::new(), (String::new(), 42)); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let (ref mut x, ref ref_str, (moved_s, _)) = t; + //~^ NOTE: Capturing t[(0, 0)] -> MutBorrow + //~| NOTE: Capturing t[(1, 0)] -> ImmBorrow + //~| NOTE: Capturing t[(2, 0),(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(2, 0),(0, 0)] -> ByValue + + println!("{}, {} {}", x, ref_str, moved_s); + }; + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr new file mode 100644 index 000000000..44fbe6d81 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr @@ -0,0 +1,178 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:10:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:38:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:58:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:13:5 + | +LL | / || { +LL | | +LL | | +LL | | let [a, b, .., e] = arr; +... | +LL | | assert_eq!(e, "E"); +LL | | }; + | |_____^ + | +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:16:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:16:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:16:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:13:5 + | +LL | / || { +LL | | +LL | | +LL | | let [a, b, .., e] = arr; +... | +LL | | assert_eq!(e, "E"); +LL | | }; + | |_____^ + | +note: Min Capture arr[] -> ByValue + --> $DIR/destructure_patterns.rs:16:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:41:5 + | +LL | / || { +LL | | +LL | | +LL | | let Point { x: ref mut x, y: _, id: moved_id } = p; +... | +LL | | println!("{}, {}", x, moved_id); +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:44:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ +note: Capturing p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:44:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:41:5 + | +LL | / || { +LL | | +LL | | +LL | | let Point { x: ref mut x, y: _, id: moved_id } = p; +... | +LL | | println!("{}, {}", x, moved_id); +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:44:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ +note: Min Capture p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:44:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t; +... | +LL | | println!("{}, {} {}", x, ref_str, moved_s); +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ +note: Capturing t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ +note: Capturing t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t; +... | +LL | | println!("{}, {} {}", x, ref_str, moved_s); +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ +note: Min Capture t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ +note: Min Capture t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:64:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs new file mode 100644 index 000000000..93131b2ac --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs @@ -0,0 +1,85 @@ +// edition:2021 + +// Test that arrays are completely captured by closures by relying on the borrow check diagnostics + +fn arrays_1() { + let mut arr = [1, 2, 3, 4, 5]; + + let mut c = || { + arr[0] += 10; + }; + + // c will capture `arr` completely, therefore another index into the + // array can't be modified here + arr[1] += 10; + //~^ ERROR: cannot use `arr` because it was mutably borrowed + //~| ERROR: cannot use `arr[_]` because it was mutably borrowed + c(); +} + +fn arrays_2() { + let mut arr = [1, 2, 3, 4, 5]; + + let c = || { + println!("{:#?}", &arr[3..4]); + }; + + // c will capture `arr` completely, therefore another index into the + // array can't be modified here + arr[1] += 10; + //~^ ERROR: cannot assign to `arr[_]` because it is borrowed + c(); +} + +fn arrays_3() { + let mut arr = [1, 2, 3, 4, 5]; + + let c = || { + println!("{}", arr[3]); + }; + + // c will capture `arr` completely, therefore another index into the + // array can't be modified here + arr[1] += 10; + //~^ ERROR: cannot assign to `arr[_]` because it is borrowed + c(); +} + +fn arrays_4() { + let mut arr = [1, 2, 3, 4, 5]; + + let mut c = || { + arr[1] += 10; + }; + + // c will capture `arr` completely, therefore we cannot borrow another index + // into the array. + println!("{}", arr[3]); + //~^ ERROR: cannot use `arr` because it was mutably borrowed + //~| ERROR: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable + + c(); +} + +fn arrays_5() { + let mut arr = [1, 2, 3, 4, 5]; + + let mut c = || { + arr[1] += 10; + }; + + // c will capture `arr` completely, therefore we cannot borrow other indecies + // into the array. + println!("{:#?}", &arr[3..2]); + //~^ ERROR: cannot borrow `arr` as immutable because it is also borrowed as mutable + + c(); +} + +fn main() { + arrays_1(); + arrays_2(); + arrays_3(); + arrays_4(); + arrays_5(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr new file mode 100644 index 000000000..4f41060dc --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr @@ -0,0 +1,104 @@ +error[E0503]: cannot use `arr` because it was mutably borrowed + --> $DIR/arrays.rs:14:5 + | +LL | let mut c = || { + | -- borrow of `arr` occurs here +LL | arr[0] += 10; + | --- borrow occurs due to use of `arr` in closure +... +LL | arr[1] += 10; + | ^^^^^^ use of borrowed `arr` +... +LL | c(); + | - borrow later used here + +error[E0503]: cannot use `arr[_]` because it was mutably borrowed + --> $DIR/arrays.rs:14:5 + | +LL | let mut c = || { + | -- borrow of `arr` occurs here +LL | arr[0] += 10; + | --- borrow occurs due to use of `arr` in closure +... +LL | arr[1] += 10; + | ^^^^^^^^^^^^ use of borrowed `arr` +... +LL | c(); + | - borrow later used here + +error[E0506]: cannot assign to `arr[_]` because it is borrowed + --> $DIR/arrays.rs:29:5 + | +LL | let c = || { + | -- borrow of `arr[_]` occurs here +LL | println!("{:#?}", &arr[3..4]); + | --- borrow occurs due to use in closure +... +LL | arr[1] += 10; + | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here +LL | +LL | c(); + | - borrow later used here + +error[E0506]: cannot assign to `arr[_]` because it is borrowed + --> $DIR/arrays.rs:43:5 + | +LL | let c = || { + | -- borrow of `arr[_]` occurs here +LL | println!("{}", arr[3]); + | --- borrow occurs due to use in closure +... +LL | arr[1] += 10; + | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here +LL | +LL | c(); + | - borrow later used here + +error[E0503]: cannot use `arr` because it was mutably borrowed + --> $DIR/arrays.rs:57:20 + | +LL | let mut c = || { + | -- borrow of `arr` occurs here +LL | arr[1] += 10; + | --- borrow occurs due to use of `arr` in closure +... +LL | println!("{}", arr[3]); + | ^^^^^^ use of borrowed `arr` +... +LL | c(); + | - borrow later used here + +error[E0502]: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable + --> $DIR/arrays.rs:57:20 + | +LL | let mut c = || { + | -- mutable borrow occurs here +LL | arr[1] += 10; + | --- first borrow occurs due to use of `arr` in closure +... +LL | println!("{}", arr[3]); + | ^^^^^^ immutable borrow occurs here +... +LL | c(); + | - mutable borrow later used here + | + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0502]: cannot borrow `arr` as immutable because it is also borrowed as mutable + --> $DIR/arrays.rs:73:24 + | +LL | let mut c = || { + | -- mutable borrow occurs here +LL | arr[1] += 10; + | --- first borrow occurs due to use of `arr` in closure +... +LL | println!("{:#?}", &arr[3..2]); + | ^^^ immutable borrow occurs here +... +LL | c(); + | - mutable borrow later used here + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0502, E0503, E0506. +For more information about an error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs new file mode 100644 index 000000000..3664d76c2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs @@ -0,0 +1,19 @@ +// edition:2021 + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} +fn main() { + let mut p = Point {x: 1, y: 2 }; + + let y = &mut p.y; + let mut c = || { + //~^ ERROR cannot borrow `p` as mutable more than once at a time + let x = &mut p.x; + println!("{:?}", p); + }; + c(); + *y+=1; +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr new file mode 100644 index 000000000..341d2bc65 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr @@ -0,0 +1,19 @@ +error[E0499]: cannot borrow `p` as mutable more than once at a time + --> $DIR/borrowck-1.rs:12:17 + | +LL | let y = &mut p.y; + | -------- first mutable borrow occurs here +LL | let mut c = || { + | ^^ second mutable borrow occurs here +LL | +LL | let x = &mut p.x; + | --- capture is mutable because of use here +LL | println!("{:?}", p); + | - second borrow occurs due to use of `p` in closure +... +LL | *y+=1; + | ----- first borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs new file mode 100644 index 000000000..ae416bab6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs @@ -0,0 +1,19 @@ +// edition:2021 + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} +fn main() { + let mut p = Point {x: 1, y: 2 }; + + let y = &p.y; + let mut c = || { + //~^ ERROR cannot borrow `p` as mutable because it is also borrowed as immutable + println!("{:?}", p); + let x = &mut p.x; + }; + c(); + println!("{}", y); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr new file mode 100644 index 000000000..584bb862b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr @@ -0,0 +1,19 @@ +error[E0502]: cannot borrow `p` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-2.rs:12:17 + | +LL | let y = &p.y; + | ---- immutable borrow occurs here +LL | let mut c = || { + | ^^ mutable borrow occurs here +LL | +LL | println!("{:?}", p); + | - second borrow occurs due to use of `p` in closure +LL | let x = &mut p.x; + | --- capture is mutable because of use here +... +LL | println!("{}", y); + | - immutable borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs new file mode 100644 index 000000000..bdd6cb79b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs @@ -0,0 +1,18 @@ +// edition:2021 + +#[derive(Debug)] +struct Point { + x: String, + y: String, +} +fn main() { + let mut c = { + let mut p = Point {x: "1".to_string(), y: "2".to_string() }; + || { + let x = &mut p.x; + println!("{:?}", p); + //~^ ERROR `p` does not live long enough + } + }; + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr new file mode 100644 index 000000000..dab1809a3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr @@ -0,0 +1,18 @@ +error[E0597]: `p` does not live long enough + --> $DIR/borrowck-3.rs:13:29 + | +LL | let mut c = { + | ----- borrow later stored here +LL | let mut p = Point {x: "1".to_string(), y: "2".to_string() }; +LL | || { + | -- value captured here +LL | let x = &mut p.x; +LL | println!("{:?}", p); + | ^ borrowed value does not live long enough +... +LL | }; + | - `p` dropped here while still borrowed + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs new file mode 100644 index 000000000..a2290d850 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs @@ -0,0 +1,20 @@ +// edition:2021 + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} +fn foo () -> impl FnMut()->() { + let mut p = Point {x: 1, y: 2 }; + let mut c = || { + //~^ ERROR closure may outlive the current function, but it borrows `p` + p.x+=5; + println!("{:?}", p); + }; + c +} +fn main() { + let c = foo(); + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr new file mode 100644 index 000000000..46379a381 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr @@ -0,0 +1,22 @@ +error[E0373]: closure may outlive the current function, but it borrows `p`, which is owned by the current function + --> $DIR/borrowck-4.rs:10:17 + | +LL | let mut c = || { + | ^^ may outlive borrowed value `p` +... +LL | println!("{:?}", p); + | - `p` is borrowed here + | +note: closure is returned here + --> $DIR/borrowck-4.rs:15:5 + | +LL | c + | ^ +help: to force the closure to take ownership of `p` (and any other referenced variables), use the `move` keyword + | +LL | let mut c = move || { + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0373`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs new file mode 100644 index 000000000..5ff7b1242 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs @@ -0,0 +1,26 @@ +// edition:2021 + + + +// Tests that two closures cannot simultaneously have mutable +// and immutable access to the variable. Issue #6801. + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn a() { + let mut p = Point {x: 3, y:4}; + let c2 = || p.y * 5; + let c1 = || { + //~^ ERROR cannot borrow `p` as mutable because it is also borrowed as immutable + dbg!(&p); + p.x = 4; + }; + drop(c2); +} + +fn main() { +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr new file mode 100644 index 000000000..5f1dae297 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr @@ -0,0 +1,21 @@ +error[E0502]: cannot borrow `p` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-closures-mut-and-imm.rs:17:14 + | +LL | let c2 = || p.y * 5; + | -- --- first borrow occurs due to use of `p.y` in closure + | | + | immutable borrow occurs here +LL | let c1 = || { + | ^^ mutable borrow occurs here +LL | +LL | dbg!(&p); + | - second borrow occurs due to use of `p` in closure +LL | p.x = 4; + | --- capture is mutable because of use here +LL | }; +LL | drop(c2); + | -- immutable borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs new file mode 100644 index 000000000..a110fa4e2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs @@ -0,0 +1,64 @@ +// edition:2021 + +// Test borrow checker when we precise capture when using boxes + +struct MetaData { x: String, name: String } +struct Data { m: MetaData } +struct BoxedData(Box<Data>); +struct EvenMoreBoxedData(Box<BoxedData>); + +// Check diagnostics when the same path is mutated both inside and outside the closure +fn box_1() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let mut c = || { + e.0.0.m.x = format!("not-x"); + }; + + e.0.0.m.x = format!("not-x"); + //~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed + c(); +} + +// Check diagnostics when a path is mutated inside a closure while attempting to read it outside +// the closure. +fn box_2() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let mut c = || { + e.0.0.m.x = format!("not-x"); + }; + + println!("{}", e.0.0.m.x); + //~^ ERROR: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable + c(); +} + +// Check diagnostics when a path is read inside a closure while attempting to mutate it outside +// the closure. +fn box_3() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let c = || { + println!("{}", e.0.0.m.x); + }; + + e.0.0.m.x = format!("not-x"); + //~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed + c(); +} + +fn main() { + box_1(); + box_2(); + box_3(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr new file mode 100644 index 000000000..f8b178752 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr @@ -0,0 +1,48 @@ +error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed + --> $DIR/box.rs:21:5 + | +LL | let mut c = || { + | -- borrow of `e.0.0.m.x` occurs here +LL | e.0.0.m.x = format!("not-x"); + | --------- borrow occurs due to use in closure +... +LL | e.0.0.m.x = format!("not-x"); + | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here +LL | +LL | c(); + | - borrow later used here + +error[E0502]: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable + --> $DIR/box.rs:38:20 + | +LL | let mut c = || { + | -- mutable borrow occurs here +LL | e.0.0.m.x = format!("not-x"); + | --------- first borrow occurs due to use of `e.0.0.m.x` in closure +... +LL | println!("{}", e.0.0.m.x); + | ^^^^^^^^^ immutable borrow occurs here +LL | +LL | c(); + | - mutable borrow later used here + | + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed + --> $DIR/box.rs:55:5 + | +LL | let c = || { + | -- borrow of `e.0.0.m.x` occurs here +LL | println!("{}", e.0.0.m.x); + | --------- borrow occurs due to use in closure +... +LL | e.0.0.m.x = format!("not-x"); + | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here +LL | +LL | c(); + | - borrow later used here + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0502, E0506. +For more information about an error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs new file mode 100644 index 000000000..77effcb00 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs @@ -0,0 +1,19 @@ +// edition:2021 + +// Test that if we deref an immutable borrow to access a Place, +// then we can't mutate the final place. + +fn main() { + let mut x = (format!(""), format!("X2")); + let mut y = (&x, "Y"); + let z = (&mut y, "Z"); + + // `x.0` is mutable but we access `x` via `*z.0.0`, which is an immutable reference and + // therefore can't be mutated. + let mut c = || { + //~^ ERROR: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference + z.0.0.0 = format!("X1"); + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr new file mode 100644 index 000000000..38c530b80 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr @@ -0,0 +1,12 @@ +error[E0596]: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference + --> $DIR/cant-mutate-imm-borrow.rs:13:17 + | +LL | let mut c = || { + | ^^ cannot borrow as mutable +LL | +LL | z.0.0.0 = format!("X1"); + | ------- mutable borrow occurs due to use of `*z.0.0` in closure + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs new file mode 100644 index 000000000..25ee9a149 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs @@ -0,0 +1,34 @@ +// edition:2021 + +// Ensure that diagnostics for mutability error (because the root variable +// isn't mutable) work with `capture_disjoint_fields` enabled. + +fn mut_error_struct() { + let x = (10, 10); + let y = (x, 10); + let z = (y, 10); + + let mut c = || { + z.0.0.0 = 20; + //~^ ERROR: cannot assign to `z.0.0.0`, as it is not declared as mutable + }; + + c(); +} + +fn mut_error_box() { + let x = (10, 10); + let bx = Box::new(x); + + let mut c = || { + bx.0 = 20; + //~^ ERROR: cannot assign to `*bx.0`, as it is not declared as mutable + }; + + c(); +} + +fn main() { + mut_error_struct(); + mut_error_box(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr new file mode 100644 index 000000000..98414fa8a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr @@ -0,0 +1,21 @@ +error[E0594]: cannot assign to `z.0.0.0`, as it is not declared as mutable + --> $DIR/cant-mutate-imm.rs:12:9 + | +LL | let z = (y, 10); + | - help: consider changing this to be mutable: `mut z` +... +LL | z.0.0.0 = 20; + | ^^^^^^^^^^^^ cannot assign + +error[E0594]: cannot assign to `*bx.0`, as it is not declared as mutable + --> $DIR/cant-mutate-imm.rs:24:9 + | +LL | let bx = Box::new(x); + | -- help: consider changing this to be mutable: `mut bx` +... +LL | bx.0 = 20; + | ^^^^^^^^^ cannot assign + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs new file mode 100644 index 000000000..f3be542e4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs @@ -0,0 +1,13 @@ +// edition:2021 + +// Test that array access is not stored as part of closure kind origin + +fn expect_fn<F: Fn()>(_f: F) {} + +fn main() { + let s = [format!("s"), format!("s")]; + let c = || { //~ ERROR expected a closure that implements the `Fn` + let [_, _s] = s; + }; + expect_fn(c); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr new file mode 100644 index 000000000..bcde35983 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr @@ -0,0 +1,14 @@ +error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` + --> $DIR/closure-origin-array-diagnostics.rs:9:13 + | +LL | let c = || { + | ^^ this closure implements `FnOnce`, not `Fn` +LL | let [_, _s] = s; + | - closure is `FnOnce` because it moves the variable `s` out of its environment +LL | }; +LL | expect_fn(c); + | --------- the requirement to implement `Fn` derives from here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0525`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs new file mode 100644 index 000000000..aa85b55b1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs @@ -0,0 +1,27 @@ +// edition:2021 + +// Check that precise paths are being reported back in the error message. + +enum MultiVariant { + Point(i32, i32), + Meta(i32) +} + +fn main() { + let mut point = MultiVariant::Point(10, -10,); + + let mut meta = MultiVariant::Meta(1); + + let c = || { + if let MultiVariant::Point(ref mut x, _) = point { + *x += 1; + } + + if let MultiVariant::Meta(ref mut v) = meta { + *v += 1; + } + }; + + let a = c; + let b = c; //~ ERROR use of moved value: `c` [E0382] +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr new file mode 100644 index 000000000..83d282aad --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr @@ -0,0 +1,21 @@ +error[E0382]: use of moved value: `c` + --> $DIR/closure-origin-multi-variant-diagnostics.rs:26:13 + | +LL | let a = c; + | - value moved here +LL | let b = c; + | ^ value used here after move + | +note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment + --> $DIR/closure-origin-multi-variant-diagnostics.rs:16:52 + | +LL | if let MultiVariant::Point(ref mut x, _) = point { + | ^^^^^ +help: consider mutably borrowing `c` + | +LL | let a = &mut c; + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs new file mode 100644 index 000000000..bedb103cc --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs @@ -0,0 +1,18 @@ +// edition:2021 + + +enum SingleVariant { + Point(i32, i32), +} + +fn main() { + let mut point = SingleVariant::Point(10, -10); + + let c = || { + let SingleVariant::Point(ref mut x, _) = point; + *x += 1; + }; + + let b = c; + let a = c; //~ ERROR use of moved value: `c` [E0382] +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr new file mode 100644 index 000000000..46323b752 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr @@ -0,0 +1,21 @@ +error[E0382]: use of moved value: `c` + --> $DIR/closure-origin-single-variant-diagnostics.rs:17:13 + | +LL | let b = c; + | - value moved here +LL | let a = c; + | ^ value used here after move + | +note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment + --> $DIR/closure-origin-single-variant-diagnostics.rs:12:50 + | +LL | let SingleVariant::Point(ref mut x, _) = point; + | ^^^^^ +help: consider mutably borrowing `c` + | +LL | let b = &mut c; + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs new file mode 100644 index 000000000..3277a83c4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs @@ -0,0 +1,22 @@ +// edition:2021 + +// Check that precise paths are being reported back in the error message. + +struct Y { + y: X +} + +struct X { + a: u32, + b: u32, +} + +fn main() { + let mut x = Y { y: X { a: 5, b: 0 } }; + let hello = || { + x.y.a += 1; + }; + + let b = hello; + let c = hello; //~ ERROR use of moved value: `hello` [E0382] +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr new file mode 100644 index 000000000..25029cc7b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr @@ -0,0 +1,21 @@ +error[E0382]: use of moved value: `hello` + --> $DIR/closure-origin-struct-diagnostics.rs:21:13 + | +LL | let b = hello; + | ----- value moved here +LL | let c = hello; + | ^^^^^ value used here after move + | +note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.y.a` out of its environment + --> $DIR/closure-origin-struct-diagnostics.rs:17:9 + | +LL | x.y.a += 1; + | ^^^^^ +help: consider mutably borrowing `hello` + | +LL | let b = &mut hello; + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs new file mode 100644 index 000000000..dc3a57ae7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs @@ -0,0 +1,13 @@ +// edition:2021 + +// Check that precise paths are being reported back in the error message. + +fn main() { + let mut x = (5, 0); + let hello = || { + x.0 += 1; + }; + + let b = hello; + let c = hello; //~ ERROR use of moved value: `hello` [E0382] +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr new file mode 100644 index 000000000..06ef7baf9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr @@ -0,0 +1,21 @@ +error[E0382]: use of moved value: `hello` + --> $DIR/closure-origin-tuple-diagnostics-1.rs:12:13 + | +LL | let b = hello; + | ----- value moved here +LL | let c = hello; + | ^^^^^ value used here after move + | +note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.0` out of its environment + --> $DIR/closure-origin-tuple-diagnostics-1.rs:8:9 + | +LL | x.0 += 1; + | ^^^ +help: consider mutably borrowing `hello` + | +LL | let b = &mut hello; + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs new file mode 100644 index 000000000..fa1328013 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs @@ -0,0 +1,13 @@ +// edition:2021 + +struct S(String, String); + +fn expect_fn<F: Fn()>(_f: F) {} + +fn main() { + let s = S(format!("s"), format!("s")); + let c = || { //~ ERROR expected a closure that implements the `Fn` + let s = s.1; + }; + expect_fn(c); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr new file mode 100644 index 000000000..df33c4f1f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr @@ -0,0 +1,14 @@ +error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` + --> $DIR/closure-origin-tuple-diagnostics.rs:9:13 + | +LL | let c = || { + | ^^ this closure implements `FnOnce`, not `Fn` +LL | let s = s.1; + | --- closure is `FnOnce` because it moves the variable `s.1` out of its environment +LL | }; +LL | expect_fn(c); + | --------- the requirement to implement `Fn` derives from here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0525`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs new file mode 100644 index 000000000..3399bc001 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs @@ -0,0 +1,92 @@ +// edition:2021 + +// check-pass +#![allow(unreachable_code)] +#![warn(unused)] +#![allow(dead_code)] + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +pub fn f() { + let mut a = 1; + let mut c = Point{ x:1, y:0 }; + + // Captured by value, but variable is dead on entry. + (move || { + // This will not trigger a warning for unused variable as + // c.x will be treated as a Non-tracked place + c.x = 1; + println!("{}", c.x); + a = 1; //~ WARN value captured by `a` is never read + println!("{}", a); + })(); + + // Read and written to, but never actually used. + (move || { + // This will not trigger a warning for unused variable as + // c.x will be treated as a Non-tracked place + c.x += 1; + a += 1; //~ WARN unused variable: `a` + })(); + + (move || { + println!("{}", c.x); + // Value is read by closure itself on later invocations. + // This will not trigger a warning for unused variable as + // c.x will be treated as a Non-tracked place + c.x += 1; + println!("{}", a); + a += 1; + })(); + let b = Box::new(42); + (move || { + println!("{}", c.x); + // Never read because this is FnOnce closure. + // This will not trigger a warning for unused variable as + // c.x will be treated as a Non-tracked place + c.x += 1; + println!("{}", a); + a += 1; //~ WARN value assigned to `a` is never read + drop(b); + })(); +} + +#[derive(Debug)] +struct MyStruct<'a> { + x: Option<& 'a str>, + y: i32, +} + +pub fn nested() { + let mut a : Option<& str>; + a = None; + let mut b : Option<& str>; + b = None; + let mut d = MyStruct{ x: None, y: 1}; + let mut e = MyStruct{ x: None, y: 1}; + (|| { + (|| { + // This will not trigger a warning for unused variable as + // d.x will be treated as a Non-tracked place + d.x = Some("d1"); + d.x = Some("d2"); + a = Some("d1"); //~ WARN value assigned to `a` is never read + a = Some("d2"); + })(); + (move || { + // This will not trigger a warning for unused variable as + //e.x will be treated as a Non-tracked place + e.x = Some("e1"); + e.x = Some("e2"); + b = Some("e1"); //~ WARN value assigned to `b` is never read + //~| WARN unused variable: `b` + b = Some("e2"); //~ WARN value assigned to `b` is never read + })(); + })(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr new file mode 100644 index 000000000..7e767cba3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr @@ -0,0 +1,65 @@ +warning: value captured by `a` is never read + --> $DIR/liveness.rs:24:9 + | +LL | a = 1; + | ^ + | +note: the lint level is defined here + --> $DIR/liveness.rs:5:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` + = help: did you mean to capture by reference instead? + +warning: unused variable: `a` + --> $DIR/liveness.rs:33:9 + | +LL | a += 1; + | ^ + | + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + = help: did you mean to capture by reference instead? + +warning: value assigned to `a` is never read + --> $DIR/liveness.rs:53:9 + | +LL | a += 1; + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `a` is never read + --> $DIR/liveness.rs:77:13 + | +LL | a = Some("d1"); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `b` is never read + --> $DIR/liveness.rs:85:13 + | +LL | b = Some("e1"); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `b` is never read + --> $DIR/liveness.rs:87:13 + | +LL | b = Some("e2"); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: unused variable: `b` + --> $DIR/liveness.rs:85:13 + | +LL | b = Some("e1"); + | ^ + | + = help: did you mean to capture by reference instead? + +warning: 7 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs new file mode 100644 index 000000000..465c9476b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs @@ -0,0 +1,43 @@ +// edition:2021 + +// check-pass +#![warn(unused)] +#![allow(dead_code)] + +#[derive(Debug)] +struct MyStruct { + a: i32, + b: i32, +} + +pub fn unintentional_copy_one() { + let mut a = 1; + let mut last = MyStruct{ a: 1, b: 1}; + let mut f = move |s| { + // This will not trigger a warning for unused variable + // as last.a will be treated as a Non-tracked place + last.a = s; + a = s; + //~^ WARN value assigned to `a` is never read + //~| WARN unused variable: `a` + }; + f(2); + f(3); + f(4); +} + +pub fn unintentional_copy_two() { + let mut a = 1; + let mut sum = MyStruct{ a: 1, b: 0}; + (1..10).for_each(move |x| { + // This will not trigger a warning for unused variable + // as sum.b will be treated as a Non-tracked place + sum.b += x; + a += x; //~ WARN unused variable: `a` + }); +} + +fn main() { + unintentional_copy_one(); + unintentional_copy_two(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr new file mode 100644 index 000000000..2ac801b49 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr @@ -0,0 +1,33 @@ +warning: value assigned to `a` is never read + --> $DIR/liveness_unintentional_copy.rs:20:9 + | +LL | a = s; + | ^ + | +note: the lint level is defined here + --> $DIR/liveness_unintentional_copy.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` + = help: maybe it is overwritten before being read? + +warning: unused variable: `a` + --> $DIR/liveness_unintentional_copy.rs:20:9 + | +LL | a = s; + | ^ + | + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + = help: did you mean to capture by reference instead? + +warning: unused variable: `a` + --> $DIR/liveness_unintentional_copy.rs:36:9 + | +LL | a += x; + | ^ + | + = help: did you mean to capture by reference instead? + +warning: 3 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs new file mode 100644 index 000000000..fa73ff23f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs @@ -0,0 +1,28 @@ +// edition:2021 + +// Test that when a borrow checker diagnostics are emitted, it's as precise +// as the capture by the closure. + +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let mut c = || { + w.p.x += 20; + }; + + let py = &mut w.p.x; + //~^ ERROR: cannot borrow `w.p.x` as mutable more than once at a time + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr new file mode 100644 index 000000000..ac4c9c937 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr @@ -0,0 +1,17 @@ +error[E0499]: cannot borrow `w.p.x` as mutable more than once at a time + --> $DIR/multilevel-path.rs:23:14 + | +LL | let mut c = || { + | -- first mutable borrow occurs here +LL | w.p.x += 20; + | ----- first borrow occurs due to use of `w.p.x` in closure +... +LL | let py = &mut w.p.x; + | ^^^^^^^^^^ second mutable borrow occurs here +LL | +LL | c(); + | - first borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs new file mode 100644 index 000000000..3d5a31e8b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs @@ -0,0 +1,37 @@ +// edition:2021 + +// Test that we can't mutate a place if we need to deref an imm-borrow +// to reach it. + +fn imm_mut_ref() { + let mut x = String::new(); + let y = String::new(); + let mref_x = &mut x; + let ref_mref_x = &mref_x; + + let c = || { + //~^ ERROR: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference + **ref_mref_x = y; + }; + + c(); +} + +fn mut_imm_ref() { + let x = String::new(); + let y = String::new(); + let mut ref_x = &x; + let mref_ref_x = &mut ref_x; + + let c = || { + //~^ ERROR: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference + **mref_ref_x = y; + }; + + c(); +} + +fn main() { + imm_mut_ref(); + mut_imm_ref(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr new file mode 100644 index 000000000..481d7e585 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr @@ -0,0 +1,24 @@ +error[E0596]: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference + --> $DIR/mut_ref.rs:12:13 + | +LL | let ref_mref_x = &mref_x; + | ------- help: consider changing this to be a mutable reference: `&mut mref_x` +LL | +LL | let c = || { + | ^^ `ref_mref_x` is a `&` reference, so the data it refers to cannot be borrowed as mutable +LL | +LL | **ref_mref_x = y; + | ------------ mutable borrow occurs due to use of `**ref_mref_x` in closure + +error[E0596]: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference + --> $DIR/mut_ref.rs:26:13 + | +LL | let c = || { + | ^^ cannot borrow as mutable +LL | +LL | **mref_ref_x = y; + | ------------ mutable borrow occurs due to use of `**mref_ref_x` in closure + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs new file mode 100644 index 000000000..1488f3296 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs @@ -0,0 +1,32 @@ +// edition:2021 + +// Given how the closure desugaring is implemented (at least at the time of writing this test), +// we don't need to truncate the captured path to a reference into a packed-struct if the field +// being referenced will be moved into the closure, since it's safe to move out a field from a +// packed-struct. +// +// However to avoid surprises for the user, or issues when the closure is +// inlined we will truncate the capture to access just the struct regardless of if the field +// might get moved into the closure. +// +// It is possible for someone to try writing the code that relies on the desugaring to create a ref +// into a packed-struct. Here we test that the compiler still detects that case. +fn test_missing_unsafe_warning_on_repr_packed() { + #[repr(packed)] + struct Foo { x: String } + + let foo = Foo { x: String::new() }; + + let c = || { + println!("{}", foo.x); + //~^ ERROR: reference to packed field is unaligned + //~| WARNING: this was previously accepted by the compiler but is being phased out + let _z = foo.x; + }; + + c(); +} + +fn main() { + test_missing_unsafe_warning_on_repr_packed(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr new file mode 100644 index 000000000..93abbecf4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr @@ -0,0 +1,29 @@ +error: reference to packed field is unaligned + --> $DIR/repr_packed.rs:21:24 + | +LL | println!("{}", foo.x); + | ^^^^^ + | + = note: `#[deny(unaligned_references)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +Future incompatibility report: Future breakage diagnostic: +error: reference to packed field is unaligned + --> $DIR/repr_packed.rs:21:24 + | +LL | println!("{}", foo.x); + | ^^^^^ + | + = note: `#[deny(unaligned_references)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs new file mode 100644 index 000000000..ed2d9a3de --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs @@ -0,0 +1,25 @@ +// edition:2021 + +// Test that borrow checker error is accurate and that min capture pass of the +// closure analysis is working as expected. + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 20 }; + + // `p` is captured via mutable borrow. + let mut c = || { + p.x += 10; + println!("{:?}", p); + }; + + + println!("{:?}", p); + //~^ ERROR: cannot borrow `p` as immutable because it is also borrowed as mutable + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr new file mode 100644 index 000000000..06157b2af --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr @@ -0,0 +1,21 @@ +error[E0502]: cannot borrow `p` as immutable because it is also borrowed as mutable + --> $DIR/simple-struct-min-capture.rs:22:22 + | +LL | let mut c = || { + | -- mutable borrow occurs here +LL | p.x += 10; + | --- capture is mutable because of use here +LL | println!("{:?}", p); + | - first borrow occurs due to use of `p` in closure +... +LL | println!("{:?}", p); + | ^ immutable borrow occurs here +LL | +LL | c(); + | - mutable borrow later used here + | + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs new file mode 100644 index 000000000..46b54846e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs @@ -0,0 +1,25 @@ +// edition:2021 + +// Test that we point to the correct location that results a union being captured. +// Union is special because it can't be disjointly captured. + +union A { + y: u32, + x: (), +} + +fn main() { + let mut a = A { y: 1 }; + let mut c = || { + //~^ borrow of `a.y` occurs here + let _ = unsafe { &a.y }; + let _ = &mut a; + //~^ borrow occurs due to use in closure + let _ = unsafe { &mut a.y }; + }; + a.y = 1; + //~^ cannot assign to `a.y` because it is borrowed [E0506] + //~| assignment to borrowed `a.y` occurs here + c(); + //~^ borrow later used here +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr new file mode 100644 index 000000000..7c34e2336 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr @@ -0,0 +1,18 @@ +error[E0506]: cannot assign to `a.y` because it is borrowed + --> $DIR/union.rs:20:5 + | +LL | let mut c = || { + | -- borrow of `a.y` occurs here +... +LL | let _ = &mut a; + | - borrow occurs due to use in closure +... +LL | a.y = 1; + | ^^^^^^^ assignment to borrowed `a.y` occurs here +... +LL | c(); + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs new file mode 100644 index 000000000..269cf76e6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -0,0 +1,18 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +fn main() { + let s = format!("s"); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("This uses new capture analyysis to capture s={}", s); + //~^ NOTE: Capturing s[] -> ImmBorrow + //~| NOTE: Min Capture s[] -> ImmBorrow + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr new file mode 100644 index 000000000..b936c5ee3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -0,0 +1,48 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/feature-gate-capture_disjoint_fields.rs:8:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/feature-gate-capture_disjoint_fields.rs:11:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("This uses new capture analyysis to capture s={}", s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:14:69 + | +LL | println!("This uses new capture analyysis to capture s={}", s); + | ^ + +error: Min Capture analysis includes: + --> $DIR/feature-gate-capture_disjoint_fields.rs:11:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("This uses new capture analyysis to capture s={}", s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:14:69 + | +LL | println!("This uses new capture analyysis to capture s={}", s); + | ^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs new file mode 100644 index 000000000..bfa3ebcd6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -0,0 +1,41 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +struct Filter { + div: i32, +} +impl Filter { + fn allowed(&self, x: i32) -> bool { + x % self.div == 1 + } +} + +struct Data { + filter: Filter, + list: Vec<i32>, +} +impl Data { + fn update(&mut self) { + // The closure passed to filter only captures self.filter, + // therefore mutating self.list is allowed. + self.list.retain( + #[rustc_capture_analysis] + |v| self.filter.allowed(*v), + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing self[Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture self[Deref,(0, 0)] -> ImmBorrow + ); + } +} + +fn main() { + let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() }; + + for i in 1..10 { + d.list.push(i); + } + + d.update(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr new file mode 100644 index 000000000..10e0d076b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -0,0 +1,26 @@ +error: First Pass analysis includes: + --> $DIR/filter-on-struct-member.rs:24:13 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Capturing self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:24:17 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/filter-on-struct-member.rs:24:13 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Min Capture self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:24:17 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87378.rs b/src/test/ui/closures/2229_closure_analysis/issue-87378.rs new file mode 100644 index 000000000..75901a571 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-87378.rs @@ -0,0 +1,26 @@ +#![feature(rustc_attrs)] + +// edition:2021 + +// Test that any precise capture on a union is truncated because it's unsafe to do so. + +union Union { + value: u64, +} + +fn main() { + let u = Union { value: 42 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + unsafe { u.value } + //~^ NOTE: Capturing u[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture u[] -> ImmBorrow + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87378.stderr b/src/test/ui/closures/2229_closure_analysis/issue-87378.stderr new file mode 100644 index 000000000..16c3f7c97 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-87378.stderr @@ -0,0 +1,48 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/issue-87378.rs:14:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/issue-87378.rs:17:5 + | +LL | / || { +LL | | +LL | | +LL | | unsafe { u.value } +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing u[(0, 0)] -> ImmBorrow + --> $DIR/issue-87378.rs:20:17 + | +LL | unsafe { u.value } + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/issue-87378.rs:17:5 + | +LL | / || { +LL | | +LL | | +LL | | unsafe { u.value } +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture u[] -> ImmBorrow + --> $DIR/issue-87378.rs:20:17 + | +LL | unsafe { u.value } + | ^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87987.rs b/src/test/ui/closures/2229_closure_analysis/issue-87987.rs new file mode 100644 index 000000000..d26343c33 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-87987.rs @@ -0,0 +1,27 @@ +// run-pass +// edition:2021 + +struct Props { + field_1: u32, //~ WARNING: fields `field_1` and `field_2` are never read + field_2: u32, +} + +fn main() { + // Test 1 + let props_2 = Props { field_1: 1, field_2: 1 }; + + let _ = || { + let _: Props = props_2; + }; + + // Test 2 + let mut arr = [1, 3, 4, 5]; + + let mref = &mut arr; + + let _c = || match arr { + [_, _, _, _] => println!("A"), + }; + + println!("{:#?}", mref); +} diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87987.stderr b/src/test/ui/closures/2229_closure_analysis/issue-87987.stderr new file mode 100644 index 000000000..5696a010c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-87987.stderr @@ -0,0 +1,14 @@ +warning: fields `field_1` and `field_2` are never read + --> $DIR/issue-87987.rs:5:5 + | +LL | struct Props { + | ----- fields in this struct +LL | field_1: u32, + | ^^^^^^^ +LL | field_2: u32, + | ^^^^^^^ + | + = note: `#[warn(dead_code)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs new file mode 100644 index 000000000..0cfb1a55b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs @@ -0,0 +1,24 @@ +// edition:2021 +// run-pass +#![feature(if_let_guard)] +#[allow(unused_must_use)] +#[allow(dead_code)] + +fn print_error_count(registry: &Registry) { + |x: &Registry| { + match &x { + Registry if let _ = registry.try_find_description() => { } + //~^ WARNING: irrefutable `if let` guard pattern + _ => {} + } + }; +} + +struct Registry; +impl Registry { + pub fn try_find_description(&self) { + unimplemented!() + } +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr new file mode 100644 index 000000000..15689023d --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr @@ -0,0 +1,12 @@ +warning: irrefutable `if let` guard pattern + --> $DIR/issue-88118-2.rs:10:29 + | +LL | Registry if let _ = registry.try_find_description() => { } + | ^ + | + = note: `#[warn(irrefutable_let_patterns)]` on by default + = note: this pattern will always match, so the guard is useless + = help: consider removing the guard and adding a `let` inside the match arm + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88476.rs b/src/test/ui/closures/2229_closure_analysis/issue-88476.rs new file mode 100644 index 000000000..f5906d306 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-88476.rs @@ -0,0 +1,62 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +// Test that we can't move out of struct that impls `Drop`. + + +use std::rc::Rc; + +// Test that we restrict precision when moving not-`Copy` types, if any of the parent paths +// implement `Drop`. This is to ensure that we don't move out of a type that implements Drop. +pub fn test1() { + struct Foo(Rc<i32>); + + impl Drop for Foo { + fn drop(self: &mut Foo) {} + } + + let f = Foo(Rc::new(1)); + let x = #[rustc_capture_analysis] move || { + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + //~| ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{:?}", f.0); + //~^ NOTE: Capturing f[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture f[] -> ByValue + }; + + x(); +} + +// Test that we don't restrict precision when moving `Copy` types(i.e. when copying), +// even if any of the parent paths implement `Drop`. +fn test2() { + struct Character { + hp: u32, + name: String, + } + + impl Drop for Character { + fn drop(&mut self) {} + } + + let character = Character { hp: 100, name: format!("A") }; + + let c = #[rustc_capture_analysis] move || { + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + //~| ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", character.hp) + //~^ NOTE: Capturing character[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture character[(0, 0)] -> ByValue + }; + + c(); + + println!("{}", character.name); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88476.stderr b/src/test/ui/closures/2229_closure_analysis/issue-88476.stderr new file mode 100644 index 000000000..c7c9ecbbb --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-88476.stderr @@ -0,0 +1,97 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/issue-88476.rs:20:13 + | +LL | let x = #[rustc_capture_analysis] move || { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/issue-88476.rs:47:13 + | +LL | let c = #[rustc_capture_analysis] move || { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/issue-88476.rs:20:39 + | +LL | let x = #[rustc_capture_analysis] move || { + | _______________________________________^ +LL | | +LL | | +LL | | +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing f[(0, 0)] -> ImmBorrow + --> $DIR/issue-88476.rs:25:26 + | +LL | println!("{:?}", f.0); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/issue-88476.rs:20:39 + | +LL | let x = #[rustc_capture_analysis] move || { + | _______________________________________^ +LL | | +LL | | +LL | | +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture f[] -> ByValue + --> $DIR/issue-88476.rs:25:26 + | +LL | println!("{:?}", f.0); + | ^^^ + +error: First Pass analysis includes: + --> $DIR/issue-88476.rs:47:39 + | +LL | let c = #[rustc_capture_analysis] move || { + | _______________________________________^ +LL | | +LL | | +LL | | +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing character[(0, 0)] -> ImmBorrow + --> $DIR/issue-88476.rs:52:24 + | +LL | println!("{}", character.hp) + | ^^^^^^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/issue-88476.rs:47:39 + | +LL | let c = #[rustc_capture_analysis] move || { + | _______________________________________^ +LL | | +LL | | +LL | | +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture character[(0, 0)] -> ByValue + --> $DIR/issue-88476.rs:52:24 + | +LL | println!("{}", character.hp) + | ^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/issue-89606.rs b/src/test/ui/closures/2229_closure_analysis/issue-89606.rs new file mode 100644 index 000000000..1bb6aa40f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-89606.rs @@ -0,0 +1,40 @@ +// Regression test for #89606. Used to ICE. +// +// check-pass +// revisions: twenty_eighteen twenty_twentyone +// [twenty_eighteen]compile-flags: --edition 2018 +// [twenty_twentyone]compile-flags: --edition 2021 + +struct S<'a>(Option<&'a mut i32>); + +fn by_ref(s: &mut S<'_>) { + (|| { + let S(_o) = s; + s.0 = None; + })(); +} + +fn by_value(s: S<'_>) { + (|| { + let S(ref _o) = s; + let _g = s.0; + })(); +} + +struct V<'a>((Option<&'a mut i32>,)); + +fn nested(v: &mut V<'_>) { + (|| { + let V((_o,)) = v; + v.0 = (None, ); + })(); +} + +fn main() { + let mut s = S(None); + by_ref(&mut s); + by_value(s); + + let mut v = V((None, )); + nested(&mut v); +} diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed b/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed new file mode 100644 index 000000000..4e0b18e72 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE lint level is defined here + +fn main() { + struct Foo(u32); + impl Drop for Foo { + fn drop(&mut self) { + println!("dropped {}", self.0); + } + } + + let f0 = Foo(0); + let f1 = Foo(1); + + let c0 = move || { + let _ = &f0; + //~^ ERROR changes to closure capture in Rust 2021 will affect drop order + //~| NOTE for more information + let _ = f0; + //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect + }; + + let c1 = move || { + let _ = &f1; + }; + + println!("dropping 0"); + drop(c0); + println!("dropping 1"); + drop(c1); + println!("dropped all"); +} +//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.rs b/src/test/ui/closures/2229_closure_analysis/issue-90465.rs new file mode 100644 index 000000000..466e6dbab --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.rs @@ -0,0 +1,34 @@ +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE lint level is defined here + +fn main() { + struct Foo(u32); + impl Drop for Foo { + fn drop(&mut self) { + println!("dropped {}", self.0); + } + } + + let f0 = Foo(0); + let f1 = Foo(1); + + let c0 = move || { + //~^ ERROR changes to closure capture in Rust 2021 will affect drop order + //~| NOTE for more information + let _ = f0; + //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect + }; + + let c1 = move || { + let _ = &f1; + }; + + println!("dropping 0"); + drop(c0); + println!("dropping 1"); + drop(c1); + println!("dropped all"); +} +//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr b/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr new file mode 100644 index 000000000..3e921dc0f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr @@ -0,0 +1,26 @@ +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/issue-90465.rs:17:14 + | +LL | let c0 = move || { + | ^^^^^^^ +... +LL | let _ = f0; + | -- in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect +... +LL | } + | - in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure + | +note: the lint level is defined here + --> $DIR/issue-90465.rs:3:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `f0` to be fully captured + | +LL ~ let c0 = move || { +LL + let _ = &f0; + | + +error: aborting due to previous error + diff --git a/src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs b/src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs new file mode 100644 index 000000000..a3b17755f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs @@ -0,0 +1,14 @@ +// ICEs if checking if there is a significant destructor causes a query cycle +// check-pass + +#![warn(rust_2021_incompatible_closure_captures)] +pub struct Foo(Bar); +pub struct Bar(Baz); +pub struct Baz(Vec<Foo>); + +impl Foo { + pub fn baz(self, v: Baz) -> Baz { + (|| v)() + } +} +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/issue_88118.rs b/src/test/ui/closures/2229_closure_analysis/issue_88118.rs new file mode 100644 index 000000000..453b7e04a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue_88118.rs @@ -0,0 +1,15 @@ +// Regression test for #88118. Used to ICE. +// +// check-pass + +#![allow(incomplete_features)] +#![feature(capture_disjoint_fields)] + +fn foo<MsU>(handler: impl FnOnce() -> MsU + Clone + 'static) { + Box::new(move |value| { + (|_| handler.clone()())(value); + None + }) as Box<dyn Fn(i32) -> Option<i32>>; +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs b/src/test/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs new file mode 100644 index 000000000..4060c4093 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs @@ -0,0 +1,10 @@ +#[non_exhaustive] +pub enum E1 {} + +#[non_exhaustive] +pub enum E2 { A, B } + +#[non_exhaustive] +pub enum E3 { C } + +pub enum E4 { D } diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-87097.rs b/src/test/ui/closures/2229_closure_analysis/match/issue-87097.rs new file mode 100644 index 000000000..815fc0a71 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/issue-87097.rs @@ -0,0 +1,35 @@ +// run-pass +// edition:2021 + +enum Variant { + A, + B, //~ WARNING: variant `B` is never constructed +} + +struct A { + field: Variant, +} + +fn discriminant_is_a_ref() { + let here = A { field: Variant::A }; + let out_ref = &here.field; + + || match out_ref { //~ WARNING: unused closure that must be used + Variant::A => (), + Variant::B => (), + }; +} + +fn discriminant_is_a_field() { + let here = A { field: Variant::A }; + + || match here.field { //~ WARNING: unused closure that must be used + Variant::A => (), + Variant::B => (), + }; +} + +fn main() { + discriminant_is_a_ref(); + discriminant_is_a_field(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-87097.stderr b/src/test/ui/closures/2229_closure_analysis/match/issue-87097.stderr new file mode 100644 index 000000000..2a49ed4b5 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/issue-87097.stderr @@ -0,0 +1,36 @@ +warning: variant `B` is never constructed + --> $DIR/issue-87097.rs:6:5 + | +LL | enum Variant { + | ------- variant in this enum +LL | A, +LL | B, + | ^ + | + = note: `#[warn(dead_code)]` on by default + +warning: unused closure that must be used + --> $DIR/issue-87097.rs:17:5 + | +LL | / || match out_ref { +LL | | Variant::A => (), +LL | | Variant::B => (), +LL | | }; + | |______^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: closures are lazy and do nothing unless called + +warning: unused closure that must be used + --> $DIR/issue-87097.rs:26:5 + | +LL | / || match here.field { +LL | | Variant::A => (), +LL | | Variant::B => (), +LL | | }; + | |______^ + | + = note: closures are lazy and do nothing unless called + +warning: 3 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-87426.rs b/src/test/ui/closures/2229_closure_analysis/match/issue-87426.rs new file mode 100644 index 000000000..74506979a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/issue-87426.rs @@ -0,0 +1,14 @@ +// run-pass +// edition:2021 + +pub fn foo() { + let ref_x_ck = 123; + let _y = || match ref_x_ck { + 2_000_000..=3_999_999 => { println!("A")} + _ => { println!("B")} + }; +} + +fn main() { + foo(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-87988.rs b/src/test/ui/closures/2229_closure_analysis/match/issue-87988.rs new file mode 100644 index 000000000..27e7fabf1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/issue-87988.rs @@ -0,0 +1,19 @@ +// run-pass +// edition:2021 + +const LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: i32 = 0x01; +const LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: i32 = 0x02; + +pub fn hotplug_callback(event: i32) { + let _ = || { + match event { + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => (), + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => (), + _ => (), + }; + }; +} + +fn main() { + hotplug_callback(1); +} diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-88331.rs b/src/test/ui/closures/2229_closure_analysis/match/issue-88331.rs new file mode 100644 index 000000000..0a6d71c68 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/issue-88331.rs @@ -0,0 +1,33 @@ +// edition:2021 + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Opcode(pub u8); + +impl Opcode { + pub const OP1: Opcode = Opcode(0x1); +} + +pub fn example1(msg_type: Opcode) -> impl FnMut(&[u8]) { + move |i| match msg_type { + //~^ ERROR: non-exhaustive patterns: `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered + Opcode::OP1 => unimplemented!(), + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Opcode2(Opcode); + +impl Opcode2 { + pub const OP2: Opcode2 = Opcode2(Opcode(0x1)); +} + + +pub fn example2(msg_type: Opcode2) -> impl FnMut(&[u8]) { + + move |i| match msg_type { + //~^ ERROR: non-exhaustive patterns: `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered + Opcode2::OP2=> unimplemented!(), + } +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-88331.stderr b/src/test/ui/closures/2229_closure_analysis/match/issue-88331.stderr new file mode 100644 index 000000000..7e22defa9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/issue-88331.stderr @@ -0,0 +1,39 @@ +error[E0004]: non-exhaustive patterns: `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered + --> $DIR/issue-88331.rs:11:20 + | +LL | move |i| match msg_type { + | ^^^^^^^^ patterns `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered + | +note: `Opcode` defined here + --> $DIR/issue-88331.rs:4:12 + | +LL | pub struct Opcode(pub u8); + | ^^^^^^ + = note: the matched value is of type `Opcode` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms + | +LL ~ Opcode::OP1 => unimplemented!(), +LL ~ Opcode(0_u8) | Opcode(2_u8..=u8::MAX) => todo!(), + | + +error[E0004]: non-exhaustive patterns: `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered + --> $DIR/issue-88331.rs:27:20 + | +LL | move |i| match msg_type { + | ^^^^^^^^ patterns `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered + | +note: `Opcode2` defined here + --> $DIR/issue-88331.rs:18:12 + | +LL | pub struct Opcode2(Opcode); + | ^^^^^^^ + = note: the matched value is of type `Opcode2` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms + | +LL ~ Opcode2::OP2=> unimplemented!(), +LL ~ Opcode2(Opcode(0_u8)) | Opcode2(Opcode(2_u8..=u8::MAX)) => todo!(), + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs new file mode 100644 index 000000000..914ebbe26 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs @@ -0,0 +1,44 @@ +// run-pass +// edition:2021 + +const PATTERN_REF: &str = "Hello World"; +const NUMBER: i32 = 30; +const NUMBER_POINTER: *const i32 = &NUMBER; + +pub fn edge_case_ref(event: &str) { + let _ = || { + match event { + PATTERN_REF => (), + _ => (), + }; + }; +} + +pub fn edge_case_str(event: String) { + let _ = || { + match event.as_str() { + "hello" => (), + _ => (), + }; + }; +} + +pub fn edge_case_raw_ptr(event: *const i32) { + let _ = || { + match event { + NUMBER_POINTER => (), + _ => (), + }; + }; +} + +pub fn edge_case_char(event: char) { + let _ = || { + match event { + 'a' => (), + _ => (), + }; + }; +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs new file mode 100644 index 000000000..ae724f9c3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs @@ -0,0 +1,37 @@ +// edition:2021 + +enum SingleVariant { + A +} + +struct TestStruct { + x: i32, + y: i32, + z: i32, +} + +fn edge_case_if() { + let sv = SingleVariant::A; + let condition = true; + // sv should not be captured as it is a SingleVariant + let _a = || { + match sv { + SingleVariant::A if condition => (), + _ => () + } + }; + let mut mut_sv = sv; + _a(); + + // ts should be captured + let ts = TestStruct { x: 1, y: 1, z: 1 }; + let _b = || { match ts { + TestStruct{ x: 1, .. } => (), + _ => () + }}; + let mut mut_ts = ts; + //~^ ERROR: cannot move out of `ts` because it is borrowed + _b(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr new file mode 100644 index 000000000..1e42d73c6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr @@ -0,0 +1,17 @@ +error[E0505]: cannot move out of `ts` because it is borrowed + --> $DIR/match-edge-cases_2.rs:32:22 + | +LL | let _b = || { match ts { + | -- -- borrow occurs due to use in closure + | | + | borrow of `ts` occurs here +... +LL | let mut mut_ts = ts; + | ^^ move out of `ts` occurs here +LL | +LL | _b(); + | -- borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs b/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs new file mode 100644 index 000000000..318673ef8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs @@ -0,0 +1,54 @@ +// edition:2021 + +// aux-build:match_non_exhaustive_lib.rs + +/* The error message for non-exhaustive matches on non-local enums + * marked as non-exhaustive should mention the fact that the enum + * is marked as non-exhaustive (issue #85227). + */ + +// Ignore non_exhaustive in the same crate +#[non_exhaustive] +enum L1 { A, B } +enum L2 { C } + +extern crate match_non_exhaustive_lib; +use match_non_exhaustive_lib::{E1, E2, E3, E4}; + +fn foo() -> (L1, L2) {todo!()} +fn bar() -> (E1, E2, E3, E4) {todo!()} + +fn main() { + let (l1, l2) = foo(); + // No error for enums defined in this crate + let _a = || { match l1 { L1::A => (), L1::B => () } }; + // (except if the match is already non-exhaustive) + let _b = || { match l1 { L1::A => () } }; + //~^ ERROR: non-exhaustive patterns: `B` not covered [E0004] + + // l2 should not be captured as it is a non-exhaustive SingleVariant + // defined in this crate + let _c = || { match l2 { L2::C => (), _ => () } }; + let mut mut_l2 = l2; + _c(); + + // E1 is not visibly uninhabited from here + let (e1, e2, e3, e4) = bar(); + let _d = || { match e1 {} }; + //~^ ERROR: non-exhaustive patterns: type `E1` is non-empty [E0004] + let _e = || { match e2 { E2::A => (), E2::B => () } }; + //~^ ERROR: non-exhaustive patterns: `_` not covered [E0004] + let _f = || { match e2 { E2::A => (), E2::B => (), _ => () } }; + + // e3 should be captured as it is a non-exhaustive SingleVariant + // defined in another crate + let _g = || { match e3 { E3::C => (), _ => () } }; + let mut mut_e3 = e3; + //~^ ERROR: cannot move out of `e3` because it is borrowed + _g(); + + // e4 should not be captured as it is a SingleVariant + let _h = || { match e4 { E4::D => (), _ => () } }; + let mut mut_e4 = e4; + _h(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr new file mode 100644 index 000000000..e0678bc71 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr @@ -0,0 +1,70 @@ +error[E0004]: non-exhaustive patterns: `B` not covered + --> $DIR/non-exhaustive-match.rs:26:25 + | +LL | let _b = || { match l1 { L1::A => () } }; + | ^^ pattern `B` not covered + | +note: `L1` defined here + --> $DIR/non-exhaustive-match.rs:12:14 + | +LL | enum L1 { A, B } + | -- ^ not covered + = note: the matched value is of type `L1` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL | let _b = || { match l1 { L1::A => (), B => todo!() } }; + | ++++++++++++++ + +error[E0004]: non-exhaustive patterns: type `E1` is non-empty + --> $DIR/non-exhaustive-match.rs:37:25 + | +LL | let _d = || { match e1 {} }; + | ^^ + | +note: `E1` defined here + --> $DIR/auxiliary/match_non_exhaustive_lib.rs:2:1 + | +LL | pub enum E1 {} + | ^^^^^^^^^^^ + = note: the matched value is of type `E1`, which is marked as non-exhaustive +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ let _d = || { match e1 { +LL + _ => todo!(), +LL ~ } }; + | + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/non-exhaustive-match.rs:39:25 + | +LL | let _e = || { match e2 { E2::A => (), E2::B => () } }; + | ^^ pattern `_` not covered + | +note: `E2` defined here + --> $DIR/auxiliary/match_non_exhaustive_lib.rs:5:1 + | +LL | pub enum E2 { A, B } + | ^^^^^^^^^^^ + = note: the matched value is of type `E2`, which is marked as non-exhaustive +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL | let _e = || { match e2 { E2::A => (), E2::B => (), _ => todo!() } }; + | ++++++++++++++ + +error[E0505]: cannot move out of `e3` because it is borrowed + --> $DIR/non-exhaustive-match.rs:46:22 + | +LL | let _g = || { match e3 { E3::C => (), _ => () } }; + | -- -- borrow occurs due to use in closure + | | + | borrow of `e3` occurs here +LL | let mut mut_e3 = e3; + | ^^ move out of `e3` occurs here +LL | +LL | _g(); + | -- borrow later used here + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0004, E0505. +For more information about an error, try `rustc --explain E0004`. diff --git a/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs b/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs new file mode 100644 index 000000000..69cf920de --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs @@ -0,0 +1,79 @@ +// edition:2021 + +#![feature(never_type)] + +// Should fake read the discriminant and throw an error +fn test1() { + let x: !; + let c1 = || match x { }; + //~^ ERROR E0381 +} + +// Should fake read the discriminant and throw an error +fn test2() { + let x: !; + let c2 = || match x { _ => () }; + //~^ ERROR E0381 +} + +// Testing single variant patterns +enum SingleVariant { + Points(u32) +} + +// Should fake read the discriminant and throw an error +fn test3() { + let variant: !; + let c = || { + //~^ ERROR E0381 + match variant { + SingleVariant::Points(_) => {} + } + }; + c(); +} + +// Should fake read the discriminant and throw an error +fn test4() { + let variant: !; + let c = || { //~ ERROR E0381 + match variant { + SingleVariant::Points(a) => { + println!("{:?}", a); + } + } + }; + c(); +} + +fn test5() { + let t: !; + let g: !; + + let a = || { + match g { }; //~ ERROR E0381 + let c = || { + match t { }; //~ ERROR E0381 + }; + + c(); + }; + +} + +// Should fake read the discriminant and throw an error +fn test6() { + let x: u8; + let c1 = || match x { }; + //~^ ERROR E0381 + //~| ERROR: non-exhaustive patterns: type `u8` is non-empty +} + +fn main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr b/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr new file mode 100644 index 000000000..fea5441ec --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr @@ -0,0 +1,83 @@ +error[E0004]: non-exhaustive patterns: type `u8` is non-empty + --> $DIR/pattern-matching-should-fail.rs:67:23 + | +LL | let c1 = || match x { }; + | ^ + | + = note: the matched value is of type `u8` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + | +LL ~ let c1 = || match x { +LL + _ => todo!(), +LL ~ }; + | + +error[E0381]: used binding `x` isn't initialized + --> $DIR/pattern-matching-should-fail.rs:8:23 + | +LL | let x: !; + | - binding declared here but left uninitialized +LL | let c1 = || match x { }; + | ^ `x` used here but it isn't initialized + +error[E0381]: used binding `x` isn't initialized + --> $DIR/pattern-matching-should-fail.rs:15:14 + | +LL | let x: !; + | - binding declared here but left uninitialized +LL | let c2 = || match x { _ => () }; + | ^^ - borrow occurs due to use in closure + | | + | `x` used here but it isn't initialized + +error[E0381]: used binding `variant` isn't initialized + --> $DIR/pattern-matching-should-fail.rs:27:13 + | +LL | let variant: !; + | ------- binding declared here but left uninitialized +LL | let c = || { + | ^^ `variant` used here but it isn't initialized +LL | +LL | match variant { + | ------- borrow occurs due to use in closure + +error[E0381]: used binding `variant` isn't initialized + --> $DIR/pattern-matching-should-fail.rs:39:13 + | +LL | let variant: !; + | ------- binding declared here but left uninitialized +LL | let c = || { + | ^^ `variant` used here but it isn't initialized +LL | match variant { + | ------- borrow occurs due to use in closure + +error[E0381]: used binding `g` isn't initialized + --> $DIR/pattern-matching-should-fail.rs:54:15 + | +LL | let g: !; + | - binding declared here but left uninitialized +... +LL | match g { }; + | ^ `g` used here but it isn't initialized + +error[E0381]: used binding `t` isn't initialized + --> $DIR/pattern-matching-should-fail.rs:56:19 + | +LL | let t: !; + | - binding declared here but left uninitialized +... +LL | match t { }; + | ^ `t` used here but it isn't initialized + +error[E0381]: used binding `x` isn't initialized + --> $DIR/pattern-matching-should-fail.rs:67:23 + | +LL | let x: u8; + | - binding declared here but left uninitialized +LL | let c1 = || match x { }; + | ^ `x` used here but it isn't initialized + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0004, E0381. +For more information about an error, try `rustc --explain E0004`. diff --git a/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs b/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs new file mode 100644 index 000000000..56f5ac44d --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs @@ -0,0 +1,137 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +// Should capture the discriminant since a variant of a multivariant enum is +// mentioned in the match arm; the discriminant is captured by the closure regardless +// of if it creates a binding +fn test_1_should_capture() { + let variant = Some(2229); + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + match variant { + //~^ NOTE: Capturing variant[] -> ImmBorrow + //~| NOTE: Min Capture variant[] -> ImmBorrow + Some(_) => {} + _ => {} + } + }; + c(); +} + +// Should not capture the discriminant since only a wildcard is mentioned in the +// match arm +fn test_2_should_not_capture() { + let variant = Some(2229); + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + match variant { + _ => {} + } + }; + c(); +} + +// Testing single variant patterns +enum SingleVariant { + Points(u32) +} + +// Should not capture the discriminant since the single variant mentioned +// in the match arm does not trigger a binding +fn test_3_should_not_capture_single_variant() { + let variant = SingleVariant::Points(1); + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + match variant { + SingleVariant::Points(_) => {} + } + }; + c(); +} + +// Should not capture the discriminant since the single variant mentioned +// in the match arm does not trigger a binding +fn test_6_should_capture_single_variant() { + let variant = SingleVariant::Points(1); + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + match variant { + //~^ NOTE: Capturing variant[] -> ImmBorrow + //~| NOTE: Capturing variant[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture variant[] -> ImmBorrow + SingleVariant::Points(a) => { + println!("{:?}", a); + } + } + }; + c(); +} + +// Should not capture the discriminant since only wildcards are mentioned in the +// match arm +fn test_4_should_not_capture_array() { + let array: [i32; 3] = [0; 3]; + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + match array { + [_,_,_] => {} + } + }; + c(); +} + +// Testing MultiVariant patterns +enum MVariant { + A, + B, + C, +} + +// Should capture the discriminant since a variant of the multi variant enum is +// mentioned in the match arm; the discriminant is captured by the closure +// regardless of if it creates a binding +fn test_5_should_capture_multi_variant() { + let variant = MVariant::A; + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + match variant { + //~^ NOTE: Capturing variant[] -> ImmBorrow + //~| NOTE: Min Capture variant[] -> ImmBorrow + MVariant::A => {} + _ => {} + } + }; + c(); +} + +fn main() { + test_1_should_capture(); + test_2_should_not_capture(); + test_3_should_not_capture_single_variant(); + test_6_should_capture_single_variant(); + test_4_should_not_capture_array(); + test_5_should_capture_multi_variant(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr b/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr new file mode 100644 index 000000000..460813333 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr @@ -0,0 +1,203 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:10:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:31:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:52:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:68:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:90:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:114:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:14:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:17:15 + | +LL | match variant { + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/patterns-capture-analysis.rs:14:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:17:15 + | +LL | match variant { + | ^^^^^^^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:34:5 + | +LL | / || { +LL | | +LL | | match variant { +LL | | _ => {} +LL | | } +LL | | }; + | |_____^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:55:5 + | +LL | / || { +LL | | +LL | | match variant { +LL | | SingleVariant::Points(_) => {} +LL | | } +LL | | }; + | |_____^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:71:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:74:15 + | +LL | match variant { + | ^^^^^^^ +note: Capturing variant[(0, 0)] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:74:15 + | +LL | match variant { + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/patterns-capture-analysis.rs:71:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:74:15 + | +LL | match variant { + | ^^^^^^^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:93:5 + | +LL | / || { +LL | | +LL | | match array { +LL | | [_,_,_] => {} +LL | | } +LL | | }; + | |_____^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:117:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:120:15 + | +LL | match variant { + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/patterns-capture-analysis.rs:117:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:120:15 + | +LL | match variant { + | ^^^^^^^ + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed new file mode 100644 index 000000000..26703fbf8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed @@ -0,0 +1,88 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +use std::thread; + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +/* Test Send Trait Migration */ +struct SendPointer(*mut i32); +unsafe impl Send for SendPointer {} + +fn test_send_trait() { + let mut f = 10; + let fptr = SendPointer(&mut f as *mut i32); + thread::spawn(move || { let _ = &fptr; unsafe { + //~^ ERROR: changes to closure capture + //~| NOTE: in Rust 2018, this closure implements `Send` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `fptr` to be fully captured + *fptr.0 = 20; + //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0` + } }); +} + +/* Test Sync Trait Migration */ +struct CustomInt(*mut i32); +struct SyncPointer(CustomInt); +unsafe impl Sync for SyncPointer {} +unsafe impl Send for CustomInt {} + +fn test_sync_trait() { + let mut f = 10; + let f = CustomInt(&mut f as *mut i32); + let fptr = SyncPointer(f); + thread::spawn(move || { let _ = &fptr; unsafe { + //~^ ERROR: changes to closure capture + //~| NOTE: in Rust 2018, this closure implements `Sync` + //~| NOTE: in Rust 2018, this closure implements `Send` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `fptr` to be fully captured + *fptr.0.0 = 20; + //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0` + } }); +} + +/* Test Clone Trait Migration */ +struct S(Foo); +struct T(i32); + +struct U(S, T); + +impl Clone for U { + fn clone(&self) -> Self { + U(S(Foo(0)), T(0)) + } +} + +fn test_clone_trait() { + let f = U(S(Foo(0)), T(0)); + let c = || { + let _ = &f; + //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements + //~| NOTE: in Rust 2018, this closure implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f` to be fully captured + let f_1 = f.1; + //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1` + println!("{:?}", f_1.0); + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f` is dropped here, but in Rust 2021, only `f.1` will be dropped here as part of the closure + +fn main() { + test_send_trait(); + test_sync_trait(); + test_clone_trait(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs new file mode 100644 index 000000000..932db51d4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs @@ -0,0 +1,87 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +use std::thread; + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +/* Test Send Trait Migration */ +struct SendPointer(*mut i32); +unsafe impl Send for SendPointer {} + +fn test_send_trait() { + let mut f = 10; + let fptr = SendPointer(&mut f as *mut i32); + thread::spawn(move || unsafe { + //~^ ERROR: changes to closure capture + //~| NOTE: in Rust 2018, this closure implements `Send` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `fptr` to be fully captured + *fptr.0 = 20; + //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0` + }); +} + +/* Test Sync Trait Migration */ +struct CustomInt(*mut i32); +struct SyncPointer(CustomInt); +unsafe impl Sync for SyncPointer {} +unsafe impl Send for CustomInt {} + +fn test_sync_trait() { + let mut f = 10; + let f = CustomInt(&mut f as *mut i32); + let fptr = SyncPointer(f); + thread::spawn(move || unsafe { + //~^ ERROR: changes to closure capture + //~| NOTE: in Rust 2018, this closure implements `Sync` + //~| NOTE: in Rust 2018, this closure implements `Send` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `fptr` to be fully captured + *fptr.0.0 = 20; + //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0` + }); +} + +/* Test Clone Trait Migration */ +struct S(Foo); +struct T(i32); + +struct U(S, T); + +impl Clone for U { + fn clone(&self) -> Self { + U(S(Foo(0)), T(0)) + } +} + +fn test_clone_trait() { + let f = U(S(Foo(0)), T(0)); + let c = || { + //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements + //~| NOTE: in Rust 2018, this closure implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f` to be fully captured + let f_1 = f.1; + //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1` + println!("{:?}", f_1.0); + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f` is dropped here, but in Rust 2021, only `f.1` will be dropped here as part of the closure + +fn main() { + test_send_trait(); + test_sync_trait(); + test_clone_trait(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr new file mode 100644 index 000000000..d7104bafe --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr @@ -0,0 +1,67 @@ +error: changes to closure capture in Rust 2021 will affect which traits the closure implements + --> $DIR/auto_traits.rs:22:19 + | +LL | thread::spawn(move || unsafe { + | ^^^^^^^ in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0` does not implement `Send` +... +LL | *fptr.0 = 20; + | ------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0` + | +note: the lint level is defined here + --> $DIR/auto_traits.rs:2:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `fptr` to be fully captured + | +LL ~ thread::spawn(move || { let _ = &fptr; unsafe { +LL | + ... +LL | +LL ~ } }); + | + +error: changes to closure capture in Rust 2021 will affect which traits the closure implements + --> $DIR/auto_traits.rs:42:19 + | +LL | thread::spawn(move || unsafe { + | ^^^^^^^ + | | + | in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0.0` does not implement `Send` + | in Rust 2018, this closure implements `Sync` as `fptr` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr` is not fully captured and `fptr.0.0` does not implement `Sync` +... +LL | *fptr.0.0 = 20; + | --------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0` + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `fptr` to be fully captured + | +LL ~ thread::spawn(move || { let _ = &fptr; unsafe { +LL | + ... +LL | +LL ~ } }); + | + +error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements + --> $DIR/auto_traits.rs:67:13 + | +LL | let c = || { + | ^^ in Rust 2018, this closure implements `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f` is not fully captured and `f.1` does not implement `Clone` +... +LL | let f_1 = f.1; + | --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1` +... +LL | } + | - in Rust 2018, `f` is dropped here, but in Rust 2021, only `f.1` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `f` to be fully captured + | +LL ~ let c = || { +LL + let _ = &f; + | + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed new file mode 100644 index 000000000..9a6db588c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed @@ -0,0 +1,33 @@ +// run-rustfix +// edition:2018 +// check-pass +#![warn(rust_2021_compatibility)] + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +macro_rules! m { + (@ $body:expr) => {{ + let f = || $body; + //~^ WARNING: drop order + f(); + }}; + ($body:block) => {{ + m!(@ $body); + }}; +} + +fn main() { + let a = (Foo(0), Foo(1)); + m!({ + let _ = &a; + //~^ HELP: add a dummy + let x = a.0; + println!("{:?}", x); + }); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs new file mode 100644 index 000000000..08cc24b4b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs @@ -0,0 +1,32 @@ +// run-rustfix +// edition:2018 +// check-pass +#![warn(rust_2021_compatibility)] + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +macro_rules! m { + (@ $body:expr) => {{ + let f = || $body; + //~^ WARNING: drop order + f(); + }}; + ($body:block) => {{ + m!(@ $body); + }}; +} + +fn main() { + let a = (Foo(0), Foo(1)); + m!({ + //~^ HELP: add a dummy + let x = a.0; + println!("{:?}", x); + }); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr new file mode 100644 index 000000000..c611daf13 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr @@ -0,0 +1,33 @@ +warning: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/closure-body-macro-fragment.rs:16:17 + | +LL | let f = || $body; + | ^^ +... +LL | }}; + | - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure +... +LL | / m!({ +LL | | +LL | | let x = a.0; + | | --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0` +LL | | println!("{:?}", x); +LL | | }); + | |______- in this macro invocation + | +note: the lint level is defined here + --> $DIR/closure-body-macro-fragment.rs:4:9 + | +LL | #![warn(rust_2021_compatibility)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(rust_2021_incompatible_closure_captures)]` implied by `#[warn(rust_2021_compatibility)]` + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> + = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add a dummy let to cause `a` to be fully captured + | +LL ~ m!({ +LL + let _ = &a; + | + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed new file mode 100644 index 000000000..2652bf598 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed @@ -0,0 +1,38 @@ +// run-pass +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +#![allow(unused)] + +// Test cases for types that implement an insignificant drop (stlib defined) + +macro_rules! test_insig_dtor_for_type { + ($t: ty, $disambiguator: ident) => { + mod $disambiguator { + use std::collections::*; + use std::rc::Rc; + use std::sync::Mutex; + + fn test_for_type(t: $t) { + let tup = (Mutex::new(0), t); + + let _c = || tup.0; + } + } + }; +} + +test_insig_dtor_for_type!(i32, prim_i32); +test_insig_dtor_for_type!(Vec<i32>, vec_i32); +test_insig_dtor_for_type!(String, string); +test_insig_dtor_for_type!(Vec<String>, vec_string); +test_insig_dtor_for_type!(HashMap<String, String>, hash_map); +test_insig_dtor_for_type!(BTreeMap<String, i32>, btree_map); +test_insig_dtor_for_type!(LinkedList<String>, linked_list); +test_insig_dtor_for_type!(Rc<i32>, rc_i32); +test_insig_dtor_for_type!(Rc<String>, rc_string); +test_insig_dtor_for_type!(std::vec::IntoIter<String>, vec_into_iter); +test_insig_dtor_for_type!(btree_map::IntoIter<String, String>, btree_map_into_iter); +test_insig_dtor_for_type!(std::array::IntoIter<String, 5>, array_into_iter); + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs new file mode 100644 index 000000000..2652bf598 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs @@ -0,0 +1,38 @@ +// run-pass +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +#![allow(unused)] + +// Test cases for types that implement an insignificant drop (stlib defined) + +macro_rules! test_insig_dtor_for_type { + ($t: ty, $disambiguator: ident) => { + mod $disambiguator { + use std::collections::*; + use std::rc::Rc; + use std::sync::Mutex; + + fn test_for_type(t: $t) { + let tup = (Mutex::new(0), t); + + let _c = || tup.0; + } + } + }; +} + +test_insig_dtor_for_type!(i32, prim_i32); +test_insig_dtor_for_type!(Vec<i32>, vec_i32); +test_insig_dtor_for_type!(String, string); +test_insig_dtor_for_type!(Vec<String>, vec_string); +test_insig_dtor_for_type!(HashMap<String, String>, hash_map); +test_insig_dtor_for_type!(BTreeMap<String, i32>, btree_map); +test_insig_dtor_for_type!(LinkedList<String>, linked_list); +test_insig_dtor_for_type!(Rc<i32>, rc_i32); +test_insig_dtor_for_type!(Rc<String>, rc_string); +test_insig_dtor_for_type!(std::vec::IntoIter<String>, vec_into_iter); +test_insig_dtor_for_type!(btree_map::IntoIter<String, String>, btree_map_into_iter); +test_insig_dtor_for_type!(std::array::IntoIter<String, 5>, array_into_iter); + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed new file mode 100644 index 000000000..d985e3bb9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed @@ -0,0 +1,76 @@ +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here +#![feature(rustc_attrs)] +#![allow(unused)] + +use std::sync::Mutex; + + #[rustc_insignificant_dtor] +struct InsignificantDropPoint { + x: i32, + y: Mutex<i32>, +} + +impl Drop for InsignificantDropPoint { + fn drop(&mut self) {} +} + +struct SigDrop; + +impl Drop for SigDrop { + fn drop(&mut self) {} +} + +#[rustc_insignificant_dtor] +struct GenericStruct<T>(T, T); + +impl<T> Drop for GenericStruct<T> { + fn drop(&mut self) {} +} + +struct Wrapper<T>(GenericStruct<T>, i32); + +// `SigDrop` implements drop and therefore needs to be migrated. +fn significant_drop_needs_migration() { + let t = (SigDrop {}, SigDrop {}); + + let c = || { + let _ = &t; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +// Even if a type implements an insignificant drop, if it's +// elements have a significant drop then the overall type is +// consdered to have an significant drop. Since the elements +// of `GenericStruct` implement drop, migration is required. +fn generic_struct_with_significant_drop_needs_migration() { + let t = Wrapper(GenericStruct(SigDrop {}, SigDrop {}), 5); + + // move is used to force i32 to be copied instead of being a ref + let c = move || { + let _ = &t; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.1; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure + +fn main() { + significant_drop_needs_migration(); + generic_struct_with_significant_drop_needs_migration(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs new file mode 100644 index 000000000..f95d34eeb --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs @@ -0,0 +1,74 @@ +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here +#![feature(rustc_attrs)] +#![allow(unused)] + +use std::sync::Mutex; + + #[rustc_insignificant_dtor] +struct InsignificantDropPoint { + x: i32, + y: Mutex<i32>, +} + +impl Drop for InsignificantDropPoint { + fn drop(&mut self) {} +} + +struct SigDrop; + +impl Drop for SigDrop { + fn drop(&mut self) {} +} + +#[rustc_insignificant_dtor] +struct GenericStruct<T>(T, T); + +impl<T> Drop for GenericStruct<T> { + fn drop(&mut self) {} +} + +struct Wrapper<T>(GenericStruct<T>, i32); + +// `SigDrop` implements drop and therefore needs to be migrated. +fn significant_drop_needs_migration() { + let t = (SigDrop {}, SigDrop {}); + + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +// Even if a type implements an insignificant drop, if it's +// elements have a significant drop then the overall type is +// consdered to have an significant drop. Since the elements +// of `GenericStruct` implement drop, migration is required. +fn generic_struct_with_significant_drop_needs_migration() { + let t = Wrapper(GenericStruct(SigDrop {}, SigDrop {}), 5); + + // move is used to force i32 to be copied instead of being a ref + let c = move || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.1; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure + +fn main() { + significant_drop_needs_migration(); + generic_struct_with_significant_drop_needs_migration(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr new file mode 100644 index 000000000..832a81711 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr @@ -0,0 +1,45 @@ +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/insignificant_drop_attr_migrations.rs:39:13 + | +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` +... +LL | } + | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + | +note: the lint level is defined here + --> $DIR/insignificant_drop_attr_migrations.rs:3:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t` to be fully captured + | +LL ~ let c = || { +LL + let _ = &t; + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/insignificant_drop_attr_migrations.rs:59:13 + | +LL | let c = move || { + | ^^^^^^^ +... +LL | let _t = t.1; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1` +... +LL | } + | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t` to be fully captured + | +LL ~ let c = move || { +LL + let _ = &t; + | + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs new file mode 100644 index 000000000..3f184a67f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs @@ -0,0 +1,45 @@ +// run-pass + +#![deny(rust_2021_incompatible_closure_captures)] +#![feature(rustc_attrs)] +#![allow(unused)] +#[rustc_insignificant_dtor] + +struct InsignificantDropPoint { + x: i32, + y: i32, +} + +impl Drop for InsignificantDropPoint { + fn drop(&mut self) {} +} + +struct GenericStruct<T>(T, T); + +// No drop reordering is required as the elements of `t` implement insignificant drop +fn insignificant_drop_does_not_need_migration() { + let t = (InsignificantDropPoint { x: 4, y: 9 }, InsignificantDropPoint { x: 4, y: 9 }); + + let c = || { + let _t = t.0; + }; + + c(); +} + +// Generic struct whose elements don't have significant drops don't need drop reordering +fn generic_struct_with_insignificant_drop_does_not_need_migration() { + let t = + GenericStruct(InsignificantDropPoint { x: 4, y: 9 }, InsignificantDropPoint { x: 4, y: 9 }); + + let c = || { + let _t = t.0; + }; + + c(); +} + +fn main() { + insignificant_drop_does_not_need_migration(); + generic_struct_with_insignificant_drop_does_not_need_migration(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs new file mode 100644 index 000000000..ff5d28461 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs @@ -0,0 +1,10 @@ +// run-pass + +#![warn(rust_2021_incompatible_closure_captures)] + +fn main() { + if let a = "" { + //~^ WARNING: irrefutable `if let` pattern + drop(|_: ()| drop(a)); + } +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr new file mode 100644 index 000000000..41b675f79 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr @@ -0,0 +1,12 @@ +warning: irrefutable `if let` pattern + --> $DIR/issue-78720.rs:6:8 + | +LL | if let a = "" { + | ^^^^^^^^^^ + | + = note: `#[warn(irrefutable_let_patterns)]` on by default + = note: this pattern will always match, so the `if let` is useless + = help: consider replacing the `if let` with a `let` + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs new file mode 100644 index 000000000..fce9cac62 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs @@ -0,0 +1,34 @@ +// edition:2018 +// check-pass + +#![warn(rust_2021_compatibility)] + +use std::future::Future; + +struct Runtime; + +impl Runtime { + pub fn block_on<F: Future>(&self, _future: F) -> F::Output { + unimplemented!() + } +} + +pub fn http<F, Fut>(_func: F) +where + F: Fn() -> Fut, + Fut: Future<Output = ()>, +{ + let rt = Runtime {}; + let srv = rt.block_on(async move { serve(move || async move { unimplemented!() }) }); + let _ = || rt.block_on(async { srv }); +} + +pub struct Server<S> { + _marker: std::marker::PhantomData<S>, +} + +pub fn serve<S>(_new_service: S) -> Server<S> { + unimplemented!() +} + +fn main() { } diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs new file mode 100644 index 000000000..ed8cb042b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs @@ -0,0 +1,37 @@ +// Test that rustc doesn't ICE as in #90024. +// check-pass +// edition=2018 + +#![warn(rust_2021_incompatible_closure_captures)] + +// Checks there's no double-subst into the generic args, otherwise we get OOB +// MCVE by @lqd +pub struct Graph<N, E, Ix> { + _edges: E, + _nodes: N, + _ix: Vec<Ix>, +} +fn graph<N, E>() -> Graph<N, E, i32> { + todo!() +} +fn first_ice() { + let g = graph::<i32, i32>(); + let _ = || g; +} + +// Checks that there is a subst into the fields, otherwise we get normalization error +// MCVE by @cuviper +use std::iter::Empty; +struct Foo<I: Iterator> { + data: Vec<I::Item>, +} +pub fn second_ice() { + let v = Foo::<Empty<()>> { data: vec![] }; + + (|| v.data[0])(); +} + +pub fn main() { + first_ice(); + second_ice(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed new file mode 100644 index 000000000..31fe494dc --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed @@ -0,0 +1,25 @@ +// run-rustfix + +// See https://github.com/rust-lang/rust/issues/87955 + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +fn main() { + let a = (Foo(0), Foo(1)); + let _ = || { let _ = &a; dbg!(a.0) }; + //~^ ERROR: drop order + //~| NOTE: will only capture `a.0` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `a` to be fully captured +} +//~^ NOTE: dropped here diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/macro.rs b/src/test/ui/closures/2229_closure_analysis/migrations/macro.rs new file mode 100644 index 000000000..0f0c49749 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/macro.rs @@ -0,0 +1,25 @@ +// run-rustfix + +// See https://github.com/rust-lang/rust/issues/87955 + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +fn main() { + let a = (Foo(0), Foo(1)); + let _ = || dbg!(a.0); + //~^ ERROR: drop order + //~| NOTE: will only capture `a.0` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `a` to be fully captured +} +//~^ NOTE: dropped here diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr new file mode 100644 index 000000000..2d0c56aad --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr @@ -0,0 +1,22 @@ +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/macro.rs:19:13 + | +LL | let _ = || dbg!(a.0); + | ^^ --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0` +... +LL | } + | - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure + | +note: the lint level is defined here + --> $DIR/macro.rs:5:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `a` to be fully captured + | +LL | let _ = || { let _ = &a; dbg!(a.0) }; + | +++++++++++++ + + +error: aborting due to previous error + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed new file mode 100644 index 000000000..ce8b60725 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed @@ -0,0 +1,47 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +// Test the two possible cases for automated migartion using rustfix +// - Closure contains a block i.e. `|| { .. };` +// - Closure contains just an expr `|| ..;` + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +fn closure_contains_block() { + let t = (Foo(0), Foo(0)); + let c = || { + let _ = &t; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +fn closure_doesnt_contain_block() { + let t = (Foo(0), Foo(0)); + let c = || { let _ = &t; t.0 }; + //~^ ERROR: drop order + //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +fn main() { + closure_contains_block(); + closure_doesnt_contain_block(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs new file mode 100644 index 000000000..2237bebd7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs @@ -0,0 +1,46 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +// Test the two possible cases for automated migartion using rustfix +// - Closure contains a block i.e. `|| { .. };` +// - Closure contains just an expr `|| ..;` + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +fn closure_contains_block() { + let t = (Foo(0), Foo(0)); + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +fn closure_doesnt_contain_block() { + let t = (Foo(0), Foo(0)); + let c = || t.0; + //~^ ERROR: drop order + //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +fn main() { + closure_contains_block(); + closure_doesnt_contain_block(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr new file mode 100644 index 000000000..12760cc72 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr @@ -0,0 +1,41 @@ +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/migrations_rustfix.rs:19:13 + | +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` +... +LL | } + | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + | +note: the lint level is defined here + --> $DIR/migrations_rustfix.rs:2:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t` to be fully captured + | +LL ~ let c = || { +LL + let _ = &t; + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/migrations_rustfix.rs:33:13 + | +LL | let c = || t.0; + | ^^ --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` +... +LL | } + | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; t.0 }; + | +++++++++++++ + + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed new file mode 100644 index 000000000..89f393141 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed @@ -0,0 +1,51 @@ +// run-rustfix +// needs-unwind + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here +// ignore-wasm32-bare compiled with panic=abort by default +#![feature(fn_traits)] +#![feature(never_type)] + +use std::panic; + +fn foo_diverges() -> ! { + panic!() +} + +fn assert_panics<F>(f: F) +where + F: FnOnce(), +{ + let f = panic::AssertUnwindSafe(f); + let result = panic::catch_unwind(move || { + let _ = &f; + //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures] + //~| NOTE: in Rust 2018, this closure implements `UnwindSafe` + //~| NOTE: in Rust 2018, this closure implements `RefUnwindSafe` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f` to be fully captured + f.0() + //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0` + }); + if let Ok(..) = result { + panic!("diverging function returned"); + } +} + +fn test_fn_ptr_panic<T>(mut t: T) +where + T: Fn() -> !, +{ + let as_fn = <T as Fn<()>>::call; + assert_panics(|| as_fn(&t, ())); + let as_fn_mut = <T as FnMut<()>>::call_mut; + assert_panics(|| as_fn_mut(&mut t, ())); + let as_fn_once = <T as FnOnce<()>>::call_once; + assert_panics(|| as_fn_once(t, ())); +} + +fn main() { + test_fn_ptr_panic(foo_diverges); + test_fn_ptr_panic(foo_diverges as fn() -> !); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs new file mode 100644 index 000000000..6b0b10521 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs @@ -0,0 +1,50 @@ +// run-rustfix +// needs-unwind + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here +// ignore-wasm32-bare compiled with panic=abort by default +#![feature(fn_traits)] +#![feature(never_type)] + +use std::panic; + +fn foo_diverges() -> ! { + panic!() +} + +fn assert_panics<F>(f: F) +where + F: FnOnce(), +{ + let f = panic::AssertUnwindSafe(f); + let result = panic::catch_unwind(move || { + //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures] + //~| NOTE: in Rust 2018, this closure implements `UnwindSafe` + //~| NOTE: in Rust 2018, this closure implements `RefUnwindSafe` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f` to be fully captured + f.0() + //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0` + }); + if let Ok(..) = result { + panic!("diverging function returned"); + } +} + +fn test_fn_ptr_panic<T>(mut t: T) +where + T: Fn() -> !, +{ + let as_fn = <T as Fn<()>>::call; + assert_panics(|| as_fn(&t, ())); + let as_fn_mut = <T as FnMut<()>>::call_mut; + assert_panics(|| as_fn_mut(&mut t, ())); + let as_fn_once = <T as FnOnce<()>>::call_once; + assert_panics(|| as_fn_once(t, ())); +} + +fn main() { + test_fn_ptr_panic(foo_diverges); + test_fn_ptr_panic(foo_diverges as fn() -> !); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr new file mode 100644 index 000000000..2648b0043 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr @@ -0,0 +1,26 @@ +error: changes to closure capture in Rust 2021 will affect which traits the closure implements + --> $DIR/mir_calls_to_shims.rs:21:38 + | +LL | let result = panic::catch_unwind(move || { + | ^^^^^^^ + | | + | in Rust 2018, this closure implements `RefUnwindSafe` as `f` implements `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `RefUnwindSafe` because `f` is not fully captured and `f.0` does not implement `RefUnwindSafe` + | in Rust 2018, this closure implements `UnwindSafe` as `f` implements `UnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe` because `f` is not fully captured and `f.0` does not implement `UnwindSafe` +... +LL | f.0() + | --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0` + | +note: the lint level is defined here + --> $DIR/mir_calls_to_shims.rs:4:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `f` to be fully captured + | +LL ~ let result = panic::catch_unwind(move || { +LL + let _ = &f; + | + +error: aborting due to previous error + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed new file mode 100644 index 000000000..173dd2e2c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed @@ -0,0 +1,157 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +use std::thread; + +#[derive(Debug)] +struct Foo(String); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +impl Foo { + fn from(s: &str) -> Self { + Self(String::from(s)) + } +} + +struct S(#[allow(unused_tuple_struct_fields)] Foo); + +#[derive(Clone)] +struct T(#[allow(unused_tuple_struct_fields)] i32); + +struct U(S, T); + +impl Clone for U { + fn clone(&self) -> Self { + U(S(Foo::from("Hello World")), T(0)) + } +} + +fn test_multi_issues() { + let f1 = U(S(Foo::from("foo")), T(0)); + let f2 = U(S(Foo::from("bar")), T(0)); + let c = || { + let _ = (&f1, &f2); + //~^ ERROR: changes to closure capture in Rust 2021 + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured + let _f_1 = f1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` + let _f_2 = f2.1; + //~^ NOTE: in Rust 2018, this closure captures all of `f2`, but in Rust 2021, it will only capture `f2.1` + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f2` is dropped here, but in Rust 2021, only `f2.1` will be dropped here as part of the closure + +fn test_capturing_all_disjoint_fields_individually() { + let f1 = U(S(Foo::from("foo")), T(0)); + let c = || { + let _ = &f1; + //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures] + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_1 = f1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` + let _f_2 = f1.1; + }; + + let c_clone = c.clone(); + + c_clone(); +} + +struct U1(S, T, S); + +impl Clone for U1 { + fn clone(&self) -> Self { + U1(S(Foo::from("foo")), T(0), S(Foo::from("bar"))) + } +} + +fn test_capturing_several_disjoint_fields_individually_1() { + let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar"))); + let c = || { + let _ = &f1; + //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures] + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_0 = f1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` + let _f_2 = f1.2; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.2` + }; + + let c_clone = c.clone(); + + c_clone(); +} + +fn test_capturing_several_disjoint_fields_individually_2() { + let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar"))); + let c = || { + let _ = &f1; + //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_0 = f1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` + let _f_1 = f1.1; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.1` + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.1` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.0` will be dropped here as part of the closure + +struct SendPointer(*mut i32); +unsafe impl Send for SendPointer {} + +struct CustomInt(*mut i32); +struct SyncPointer(CustomInt); +unsafe impl Sync for SyncPointer {} +unsafe impl Send for CustomInt {} + +fn test_multi_traits_issues() { + let mut f1 = 10; + let f1 = CustomInt(&mut f1 as *mut i32); + let fptr1 = SyncPointer(f1); + + let mut f2 = 10; + let fptr2 = SendPointer(&mut f2 as *mut i32); + thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe { + //~^ ERROR: changes to closure capture in Rust 2021 + //~| NOTE: in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync` + //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr1` implements `Send` + //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured + *fptr1.0.0 = 20; + //~^ NOTE: in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0` + *fptr2.0 = 20; + //~^ NOTE: in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0` + } }); +} + +fn main() { + test_multi_issues(); + test_capturing_all_disjoint_fields_individually(); + test_capturing_several_disjoint_fields_individually_1(); + test_capturing_several_disjoint_fields_individually_2(); + test_multi_traits_issues(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs new file mode 100644 index 000000000..cfc4555ca --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs @@ -0,0 +1,153 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +use std::thread; + +#[derive(Debug)] +struct Foo(String); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +impl Foo { + fn from(s: &str) -> Self { + Self(String::from(s)) + } +} + +struct S(#[allow(unused_tuple_struct_fields)] Foo); + +#[derive(Clone)] +struct T(#[allow(unused_tuple_struct_fields)] i32); + +struct U(S, T); + +impl Clone for U { + fn clone(&self) -> Self { + U(S(Foo::from("Hello World")), T(0)) + } +} + +fn test_multi_issues() { + let f1 = U(S(Foo::from("foo")), T(0)); + let f2 = U(S(Foo::from("bar")), T(0)); + let c = || { + //~^ ERROR: changes to closure capture in Rust 2021 + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured + let _f_1 = f1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` + let _f_2 = f2.1; + //~^ NOTE: in Rust 2018, this closure captures all of `f2`, but in Rust 2021, it will only capture `f2.1` + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f2` is dropped here, but in Rust 2021, only `f2.1` will be dropped here as part of the closure + +fn test_capturing_all_disjoint_fields_individually() { + let f1 = U(S(Foo::from("foo")), T(0)); + let c = || { + //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures] + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_1 = f1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` + let _f_2 = f1.1; + }; + + let c_clone = c.clone(); + + c_clone(); +} + +struct U1(S, T, S); + +impl Clone for U1 { + fn clone(&self) -> Self { + U1(S(Foo::from("foo")), T(0), S(Foo::from("bar"))) + } +} + +fn test_capturing_several_disjoint_fields_individually_1() { + let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar"))); + let c = || { + //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures] + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_0 = f1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` + let _f_2 = f1.2; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.2` + }; + + let c_clone = c.clone(); + + c_clone(); +} + +fn test_capturing_several_disjoint_fields_individually_2() { + let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar"))); + let c = || { + //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements + //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_0 = f1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` + let _f_1 = f1.1; + //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.1` + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.1` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.0` will be dropped here as part of the closure + +struct SendPointer(*mut i32); +unsafe impl Send for SendPointer {} + +struct CustomInt(*mut i32); +struct SyncPointer(CustomInt); +unsafe impl Sync for SyncPointer {} +unsafe impl Send for CustomInt {} + +fn test_multi_traits_issues() { + let mut f1 = 10; + let f1 = CustomInt(&mut f1 as *mut i32); + let fptr1 = SyncPointer(f1); + + let mut f2 = 10; + let fptr2 = SendPointer(&mut f2 as *mut i32); + thread::spawn(move || unsafe { + //~^ ERROR: changes to closure capture in Rust 2021 + //~| NOTE: in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync` + //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr1` implements `Send` + //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured + *fptr1.0.0 = 20; + //~^ NOTE: in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0` + *fptr2.0 = 20; + //~^ NOTE: in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0` + }); +} + +fn main() { + test_multi_issues(); + test_capturing_all_disjoint_fields_individually(); + test_capturing_several_disjoint_fields_individually_1(); + test_capturing_several_disjoint_fields_individually_2(); + test_multi_traits_issues(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr new file mode 100644 index 000000000..96d5c936f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr @@ -0,0 +1,118 @@ +error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements + --> $DIR/multi_diagnostics.rs:37:13 + | +LL | let c = || { + | ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone` +... +LL | let _f_1 = f1.0; + | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` +LL | +LL | let _f_2 = f2.1; + | ---- in Rust 2018, this closure captures all of `f2`, but in Rust 2021, it will only capture `f2.1` +... +LL | } + | - in Rust 2018, `f2` is dropped here, but in Rust 2021, only `f2.1` will be dropped here as part of the closure + | +note: the lint level is defined here + --> $DIR/multi_diagnostics.rs:2:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `f1`, `f2` to be fully captured + | +LL ~ let c = || { +LL + let _ = (&f1, &f2); + | + +error: changes to closure capture in Rust 2021 will affect which traits the closure implements + --> $DIR/multi_diagnostics.rs:56:13 + | +LL | let c = || { + | ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone` +... +LL | let _f_1 = f1.0; + | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `f1` to be fully captured + | +LL ~ let c = || { +LL + let _ = &f1; + | + +error: changes to closure capture in Rust 2021 will affect which traits the closure implements + --> $DIR/multi_diagnostics.rs:81:13 + | +LL | let c = || { + | ^^ + | | + | in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone` + | in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.2` does not implement `Clone` +... +LL | let _f_0 = f1.0; + | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` +LL | +LL | let _f_2 = f1.2; + | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.2` + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `f1` to be fully captured + | +LL ~ let c = || { +LL + let _ = &f1; + | + +error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements + --> $DIR/multi_diagnostics.rs:100:13 + | +LL | let c = || { + | ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone` +... +LL | let _f_0 = f1.0; + | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0` +LL | +LL | let _f_1 = f1.1; + | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.1` +... +LL | } + | - + | | + | in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.0` will be dropped here as part of the closure + | in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.1` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `f1` to be fully captured + | +LL ~ let c = || { +LL + let _ = &f1; + | + +error: changes to closure capture in Rust 2021 will affect which traits the closure implements + --> $DIR/multi_diagnostics.rs:133:19 + | +LL | thread::spawn(move || unsafe { + | ^^^^^^^ + | | + | in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Send` + | in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Sync` + | in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr2` is not fully captured and `fptr2.0` does not implement `Send` +... +LL | *fptr1.0.0 = 20; + | ---------- in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0` +LL | +LL | *fptr2.0 = 20; + | -------- in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0` + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `fptr1`, `fptr2` to be fully captured + | +LL ~ thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe { +LL | + ... +LL | +LL ~ } }); + | + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs new file mode 100644 index 000000000..8b75e226a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs @@ -0,0 +1,81 @@ +// run-pass + +// Set of test cases that don't need migrations + +#![deny(rust_2021_incompatible_closure_captures)] + +// Copy types as copied by the closure instead of being moved into the closure +// Therefore their drop order isn't tied to the closure and won't be requiring any +// migrations. +fn test1_only_copy_types() { + let t = (0i32, 0i32); + + let c = || { + let _t = t.0; + }; + + c(); +} + +// Same as test1 but using a move closure +fn test2_only_copy_types_move_closure() { + let t = (0i32, 0i32); + + let c = move || { + println!("{}", t.0); + }; + + c(); +} + +// Don't need to migrate if captured by ref +fn test3_only_copy_types_move_closure() { + let t = (String::new(), String::new()); + + let c = || { + println!("{}", t.0); + }; + + c(); +} + +// Test migration analysis in case of Insignificant Drop + Non Drop aggregates. +// Note in this test the closure captures a non Drop type and therefore the variable +// is only captured by ref. +fn test4_insignificant_drop_non_drop_aggregate() { + let t = (String::new(), 0i32); + + let c = || { + let _t = t.1; + }; + + c(); +} + +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +// Test migration analysis in case of Significant Drop + Non Drop aggregates. +// Note in this test the closure captures a non Drop type and therefore the variable +// is only captured by ref. +fn test5_significant_drop_non_drop_aggregate() { + let t = (Foo(0), 0i32); + + let c = || { + let _t = t.1; + }; + + c(); +} + +fn main() { + test1_only_copy_types(); + test2_only_copy_types_move_closure(); + test3_only_copy_types_move_closure(); + test4_insignificant_drop_non_drop_aggregate(); + test5_significant_drop_non_drop_aggregate(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/old_name.rs b/src/test/ui/closures/2229_closure_analysis/migrations/old_name.rs new file mode 100644 index 000000000..16e3cca7b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/old_name.rs @@ -0,0 +1,9 @@ +// check-pass + +// Ensure that the old name for `rust_2021_incompatible_closure_captures` is still +// accepted by the compiler + +#![allow(disjoint_capture_migration)] +//~^ WARN lint `disjoint_capture_migration` has been renamed + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/old_name.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/old_name.stderr new file mode 100644 index 000000000..47cb689fa --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/old_name.stderr @@ -0,0 +1,10 @@ +warning: lint `disjoint_capture_migration` has been renamed to `rust_2021_incompatible_closure_captures` + --> $DIR/old_name.rs:6:10 + | +LL | #![allow(disjoint_capture_migration)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rust_2021_incompatible_closure_captures` + | + = note: `#[warn(renamed_and_removed_lints)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed new file mode 100644 index 000000000..7892a72c7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed @@ -0,0 +1,69 @@ +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +struct ConstainsDropField(Foo, Foo); + +// Test that lint is triggered if a path that implements Drop is not captured by move +fn test_precise_analysis_drop_paths_not_captured_by_move() { + let t = ConstainsDropField(Foo(10), Foo(20)); + + let c = || { + let _ = &t; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + let _t = &t.1; + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +struct S; +impl Drop for S { + fn drop(&mut self) {} +} + +struct T(S, S); +struct U(T, T); + +// Test precise analysis for the lint works with paths longer than one. +fn test_precise_analysis_long_path_missing() { + let u = U(T(S, S), T(S, S)); + + let c = || { + let _ = &u; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `u` to be fully captured + let _x = u.0.0; + //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.0` + let _x = u.0.1; + //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.1` + let _x = u.1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.1.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.0` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.1` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.1.0` will be dropped here as part of the closure + + +fn main() { + test_precise_analysis_drop_paths_not_captured_by_move(); + test_precise_analysis_long_path_missing(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs new file mode 100644 index 000000000..f5e99002b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs @@ -0,0 +1,67 @@ +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +struct ConstainsDropField(Foo, Foo); + +// Test that lint is triggered if a path that implements Drop is not captured by move +fn test_precise_analysis_drop_paths_not_captured_by_move() { + let t = ConstainsDropField(Foo(10), Foo(20)); + + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + let _t = &t.1; + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +struct S; +impl Drop for S { + fn drop(&mut self) {} +} + +struct T(S, S); +struct U(T, T); + +// Test precise analysis for the lint works with paths longer than one. +fn test_precise_analysis_long_path_missing() { + let u = U(T(S, S), T(S, S)); + + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `u` to be fully captured + let _x = u.0.0; + //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.0` + let _x = u.0.1; + //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.1` + let _x = u.1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.1.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.0` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.1` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.1.0` will be dropped here as part of the closure + + +fn main() { + test_precise_analysis_drop_paths_not_captured_by_move(); + test_precise_analysis_long_path_missing(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr new file mode 100644 index 000000000..aa9b8672a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr @@ -0,0 +1,55 @@ +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/precise.rs:20:13 + | +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` +... +LL | } + | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + | +note: the lint level is defined here + --> $DIR/precise.rs:3:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t` to be fully captured + | +LL ~ let c = || { +LL + let _ = &t; + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/precise.rs:45:13 + | +LL | let c = || { + | ^^ +... +LL | let _x = u.0.0; + | ----- in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.0` +LL | +LL | let _x = u.0.1; + | ----- in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.1` +LL | +LL | let _x = u.1.0; + | ----- in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.1.0` +... +LL | } + | - + | | + | in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.0` will be dropped here as part of the closure + | in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.1` will be dropped here as part of the closure + | in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.1.0` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `u` to be fully captured + | +LL ~ let c = || { +LL + let _ = &u; + | + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs new file mode 100644 index 000000000..587d71c40 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs @@ -0,0 +1,104 @@ +// run-pass + +#![deny(rust_2021_incompatible_closure_captures)] + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +struct ConstainsDropField(Foo, Foo); + +// Test that if all paths starting at root variable that implement Drop are captured +// then it doesn't trigger the lint. +fn test_precise_analysis_simple_1() { + let t = (Foo(10), Foo(20), Foo(30)); + + let c = || { + let _t = t.0; + let _t = t.1; + let _t = t.2; + }; + + c(); +} + +// Test that if all paths starting at root variable that implement Drop are captured +// then it doesn't trigger the lint. +fn test_precise_analysis_simple_2() { + let t = ConstainsDropField(Foo(10), Foo(20)); + + let c = || { + let _t = t.0; + let _t = t.1; + }; + + c(); +} + +#[derive(Debug)] +struct ContainsAndImplsDrop(Foo); +impl Drop for ContainsAndImplsDrop { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +// If a path isn't directly captured but requires Drop, then this tests that migrations aren't +// needed if the a parent to that path is captured. +fn test_precise_analysis_parent_captured_1() { + let t = ConstainsDropField(Foo(10), Foo(20)); + + let c = || { + let _t = t; + }; + + c(); +} + +// If a path isn't directly captured but requires Drop, then this tests that migrations aren't +// needed if the a parent to that path is captured. +fn test_precise_analysis_parent_captured_2() { + let t = ContainsAndImplsDrop(Foo(10)); + + let c = || { + let _t = t; + }; + + c(); +} + +struct S; +impl Drop for S { + fn drop(&mut self) {} +} + +struct T(S, S); +struct U(T, T); + +// Test that if the path is longer than just one element, precise analysis works correctly. +fn test_precise_analysis_long_path() { + let u = U(T(S, S), T(S, S)); + + let c = || { + let _x = u.0.0; + let _x = u.0.1; + let _x = u.1.0; + let _x = u.1.1; + }; + + c(); +} + +fn main() { + test_precise_analysis_simple_1(); + test_precise_analysis_simple_2(); + + test_precise_analysis_parent_captured_1(); + test_precise_analysis_parent_captured_2(); + + test_precise_analysis_long_path(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed new file mode 100644 index 000000000..e99dbb5ab --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed @@ -0,0 +1,229 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +// Test cases for types that implement a significant drop (user defined) + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +#[derive(Debug)] +struct ConstainsDropField(Foo, #[allow(unused_tuple_struct_fields)] Foo); + +// `t` needs Drop because one of its elements needs drop, +// therefore precise capture might affect drop ordering +fn test1_all_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0)); + let t2 = (Foo(0), Foo(0)); + + let c = || { + let _ = (&t, &t1, &t2); + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + let _t1 = t1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0` + let _t2 = t2.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t2`, but in Rust 2021, it will only capture `t2.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `t2` is dropped here, but in Rust 2021, only `t2.0` will be dropped here as part of the closure + +// String implements drop and therefore should be migrated. +// But in this test cases, `t2` is completely captured and when it is dropped won't be affected +fn test2_only_precise_paths_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0)); + let t2 = (Foo(0), Foo(0)); + + let c = || { + let _ = (&t, &t1); + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + let _t1 = t1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0` + let _t2 = t2; + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure + +// If a variable would've not been captured by value then it would've not been +// dropped with the closure and therefore doesn't need migration. +fn test3_only_by_value_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0)); + let c = || { + let _ = &t; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + println!("{:?}", t1.1); + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +// The root variable might not implement drop themselves but some path starting +// at the root variable might implement Drop. +// +// If this path isn't captured we need to migrate for the root variable. +fn test4_type_contains_drop_need_migration() { + let t = ConstainsDropField(Foo(0), Foo(0)); + + let c = || { + let _ = &t; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +// Test migration analysis in case of Drop + Non Drop aggregates. +// Note we need migration here only because the non-copy (because Drop type) is captured, +// otherwise we won't need to, since we can get away with just by ref capture in that case. +fn test5_drop_non_drop_aggregate_need_migration() { + let t = (Foo(0), Foo(0), 0i32); + + let c = || { + let _ = &t; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +// Test migration analysis in case of Significant and Insignificant Drop aggregates. +fn test6_significant_insignificant_drop_aggregate_need_migration() { + let t = (Foo(0), String::new()); + + let c = || { + let _ = &t; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.1; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure + +// Since we are using a move closure here, both `t` and `t1` get moved +// even though they are being used by ref inside the closure. +fn test7_move_closures_non_copy_types_might_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0), Foo(0)); + + let c = move || { + let _ = (&t1, &t); + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured + println!("{:?} {:?}", t1.1, t.1); + //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.1` + //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.1` will be dropped here as part of the closure + + +fn test8_drop_order_and_blocks() { + { + let tuple = + (Foo(0), Foo(1)); + { + let c = || { + let _ = &tuple; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tuple` to be fully captured + tuple.0; + //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0` + }; + + c(); + } + //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure + } +} + +fn test9_drop_order_and_nested_closures() { + let tuple = + (Foo(0), Foo(1)); + let b = || { + let c = || { + let _ = &tuple; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tuple` to be fully captured + tuple.0; + //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0` + }; + + c(); + }; + //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure + + b(); +} + +// Test that we migrate if drop order of Vec<T> would be affected if T is a significant drop type +fn test10_vec_of_significant_drop_type() { + + let tup = (Foo(0), vec![Foo(3)]); + + let _c = || { let _ = &tup; tup.0 }; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tup` to be fully captured + //~| NOTE: in Rust 2018, this closure captures all of `tup`, but in Rust 2021, it will only capture `tup.0` +} +//~^ NOTE: in Rust 2018, `tup` is dropped here, but in Rust 2021, only `tup.0` will be dropped here as part of the closure + +fn main() { + test1_all_need_migration(); + test2_only_precise_paths_need_migration(); + test3_only_by_value_need_migration(); + test4_type_contains_drop_need_migration(); + test5_drop_non_drop_aggregate_need_migration(); + test6_significant_insignificant_drop_aggregate_need_migration(); + test7_move_closures_non_copy_types_might_need_migration(); + test8_drop_order_and_blocks(); + test9_drop_order_and_nested_closures(); + test10_vec_of_significant_drop_type(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs new file mode 100644 index 000000000..62a984c9e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs @@ -0,0 +1,220 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +// Test cases for types that implement a significant drop (user defined) + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +#[derive(Debug)] +struct ConstainsDropField(Foo, #[allow(unused_tuple_struct_fields)] Foo); + +// `t` needs Drop because one of its elements needs drop, +// therefore precise capture might affect drop ordering +fn test1_all_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0)); + let t2 = (Foo(0), Foo(0)); + + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + let _t1 = t1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0` + let _t2 = t2.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t2`, but in Rust 2021, it will only capture `t2.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `t2` is dropped here, but in Rust 2021, only `t2.0` will be dropped here as part of the closure + +// String implements drop and therefore should be migrated. +// But in this test cases, `t2` is completely captured and when it is dropped won't be affected +fn test2_only_precise_paths_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0)); + let t2 = (Foo(0), Foo(0)); + + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + let _t1 = t1.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0` + let _t2 = t2; + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure + +// If a variable would've not been captured by value then it would've not been +// dropped with the closure and therefore doesn't need migration. +fn test3_only_by_value_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0)); + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + println!("{:?}", t1.1); + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +// The root variable might not implement drop themselves but some path starting +// at the root variable might implement Drop. +// +// If this path isn't captured we need to migrate for the root variable. +fn test4_type_contains_drop_need_migration() { + let t = ConstainsDropField(Foo(0), Foo(0)); + + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +// Test migration analysis in case of Drop + Non Drop aggregates. +// Note we need migration here only because the non-copy (because Drop type) is captured, +// otherwise we won't need to, since we can get away with just by ref capture in that case. +fn test5_drop_non_drop_aggregate_need_migration() { + let t = (Foo(0), Foo(0), 0i32); + + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + +// Test migration analysis in case of Significant and Insignificant Drop aggregates. +fn test6_significant_insignificant_drop_aggregate_need_migration() { + let t = (Foo(0), String::new()); + + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.1; + //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure + +// Since we are using a move closure here, both `t` and `t1` get moved +// even though they are being used by ref inside the closure. +fn test7_move_closures_non_copy_types_might_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0), Foo(0)); + + let c = move || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured + println!("{:?} {:?}", t1.1, t.1); + //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.1` + //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1` + }; + + c(); +} +//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure +//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.1` will be dropped here as part of the closure + + +fn test8_drop_order_and_blocks() { + { + let tuple = + (Foo(0), Foo(1)); + { + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tuple` to be fully captured + tuple.0; + //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0` + }; + + c(); + } + //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure + } +} + +fn test9_drop_order_and_nested_closures() { + let tuple = + (Foo(0), Foo(1)); + let b = || { + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tuple` to be fully captured + tuple.0; + //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0` + }; + + c(); + }; + //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure + + b(); +} + +// Test that we migrate if drop order of Vec<T> would be affected if T is a significant drop type +fn test10_vec_of_significant_drop_type() { + + let tup = (Foo(0), vec![Foo(3)]); + + let _c = || tup.0; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tup` to be fully captured + //~| NOTE: in Rust 2018, this closure captures all of `tup`, but in Rust 2021, it will only capture `tup.0` +} +//~^ NOTE: in Rust 2018, `tup` is dropped here, but in Rust 2021, only `tup.0` will be dropped here as part of the closure + +fn main() { + test1_all_need_migration(); + test2_only_precise_paths_need_migration(); + test3_only_by_value_need_migration(); + test4_type_contains_drop_need_migration(); + test5_drop_non_drop_aggregate_need_migration(); + test6_significant_insignificant_drop_aggregate_need_migration(); + test7_move_closures_non_copy_types_might_need_migration(); + test8_drop_order_and_blocks(); + test9_drop_order_and_nested_closures(); + test10_vec_of_significant_drop_type(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr new file mode 100644 index 000000000..0d9f09ee3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr @@ -0,0 +1,214 @@ +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:25:13 + | +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` +LL | +LL | let _t1 = t1.0; + | ---- in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0` +LL | +LL | let _t2 = t2.0; + | ---- in Rust 2018, this closure captures all of `t2`, but in Rust 2021, it will only capture `t2.0` +... +LL | } + | - + | | + | in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + | in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure + | in Rust 2018, `t2` is dropped here, but in Rust 2021, only `t2.0` will be dropped here as part of the closure + | +note: the lint level is defined here + --> $DIR/significant_drop.rs:2:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t`, `t1`, `t2` to be fully captured + | +LL ~ let c = || { +LL + let _ = (&t, &t1, &t2); + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:50:13 + | +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` +LL | +LL | let _t1 = t1.0; + | ---- in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0` +... +LL | } + | - + | | + | in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + | in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t`, `t1` to be fully captured + | +LL ~ let c = || { +LL + let _ = (&t, &t1); + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:71:13 + | +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` +... +LL | } + | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t` to be fully captured + | +LL ~ let c = || { +LL + let _ = &t; + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:91:13 + | +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` +... +LL | } + | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t` to be fully captured + | +LL ~ let c = || { +LL + let _ = &t; + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:109:13 + | +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0` +... +LL | } + | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t` to be fully captured + | +LL ~ let c = || { +LL + let _ = &t; + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:125:13 + | +LL | let c = || { + | ^^ +... +LL | let _t = t.1; + | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1` +... +LL | } + | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t` to be fully captured + | +LL ~ let c = || { +LL + let _ = &t; + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:143:13 + | +LL | let c = move || { + | ^^^^^^^ +... +LL | println!("{:?} {:?}", t1.1, t.1); + | ---- --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1` + | | + | in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.1` +... +LL | } + | - + | | + | in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.1` will be dropped here as part of the closure + | in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `t1`, `t` to be fully captured + | +LL ~ let c = move || { +LL + let _ = (&t1, &t); + | + +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, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0` +... +LL | } + | - in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `tuple` to be fully captured + | +LL ~ let c = || { +LL + let _ = &tuple; + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:181:17 + | +LL | let c = || { + | ^^ +... +LL | tuple.0; + | ------- in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0` +... +LL | }; + | - in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `tuple` to be fully captured + | +LL ~ let c = || { +LL + let _ = &tuple; + | + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:201:18 + | +LL | let _c = || tup.0; + | ^^ ----- in Rust 2018, this closure captures all of `tup`, but in Rust 2021, it will only capture `tup.0` +... +LL | } + | - in Rust 2018, `tup` is dropped here, but in Rust 2021, only `tup.0` will be dropped here as part of the closure + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> +help: add a dummy let to cause `tup` to be fully captured + | +LL | let _c = || { let _ = &tup; tup.0 }; + | +++++++++++++++ + + +error: aborting due to 10 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs b/src/test/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs new file mode 100644 index 000000000..39cf82053 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs @@ -0,0 +1,13 @@ +//run-pass +#![deny(rust_2021_incompatible_closure_captures)] +#![allow(unused_must_use)] + +fn filter_try_fold( + predicate: &mut impl FnMut() -> bool, +) -> impl FnMut() -> bool + '_ { + move || predicate() +} + +fn main() { + filter_try_fold(&mut || true); +} diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/move_closure.rs new file mode 100644 index 000000000..b542fa243 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.rs @@ -0,0 +1,222 @@ +// edition:2021 + +// Test that move closures drop derefs with `capture_disjoint_fields` enabled. + +#![feature(rustc_attrs)] + +fn simple_move_closure() { + struct S(String); + struct T(S); + + let t = T(S("s".into())); + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + t.0.0 = "new S".into(); + //~^ NOTE: Capturing t[(0, 0),(0, 0)] -> MutBorrow + //~| NOTE: Min Capture t[(0, 0),(0, 0)] -> ByValue + }; + c(); +} + +// Test move closure use reborrows when using references +fn simple_ref() { + let mut s = 10; + let ref_s = &mut s; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + *ref_s += 10; + //~^ NOTE: Capturing ref_s[Deref] -> MutBorrow + //~| NOTE: Min Capture ref_s[] -> ByValue + }; + c(); +} + +// Test move closure use reborrows when using references +fn struct_contains_ref_to_another_struct_1() { + struct S(String); + struct T<'a>(&'a mut S); + + let mut s = S("s".into()); + let t = T(&mut s); + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + t.0.0 = "new s".into(); + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> MutBorrow + //~| NOTE: Min Capture t[(0, 0)] -> ByValue + }; + + c(); +} + +// Test that we can use reborrows to read data of Copy types +// i.e. without truncating derefs +fn struct_contains_ref_to_another_struct_2() { + struct S(i32); + struct T<'a>(&'a S); + + let s = S(0); + let t = T(&s); + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let _t = t.0.0; + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(0, 0)] -> ByValue + }; + + c(); +} + +// Test that we can use truncate to move out of !Copy types +fn struct_contains_ref_to_another_struct_3() { + struct S(String); + struct T<'a>(&'a S); + + let s = S("s".into()); + let t = T(&s); + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let _t = t.0.0; + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> ByValue + }; + + c(); +} + +// Test that derefs of box are truncated in move closures +fn truncate_box_derefs() { + struct S(i32); + + + // Content within the box is moved within the closure + let b = Box::new(S(10)); + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let _t = b.0; + //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture b[] -> ByValue + }; + + c(); + + // Content within the box is used by a shared ref and the box is the root variable + let b = Box::new(S(10)); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", b.0); + //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture b[] -> ByValue + }; + + c(); + + // Content within the box is used by a shared ref and the box is not the root variable + let b = Box::new(S(10)); + let t = (0, b); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", t.1.0); + //~^ NOTE: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(1, 0)] -> ByValue + }; +} + +struct Foo { x: i32 } + +// Ensure that even in move closures, if the data is not owned by the root variable +// then we don't truncate the derefs or a ByValue capture, rather do a reborrow +fn box_mut_1() { + let mut foo = Foo { x: 0 } ; + + let p_foo = &mut foo; + let box_p_foo = Box::new(p_foo); + + let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + //~| First Pass analysis includes: + //~| NOTE: Capturing box_p_foo[Deref,Deref,(0, 0)] -> MutBorrow + //~| Min Capture analysis includes: + //~| NOTE: Min Capture box_p_foo[] -> ByValue +} + +// Ensure that even in move closures, if the data is not owned by the root variable +// then we don't truncate the derefs or a ByValue capture, rather do a reborrow +fn box_mut_2() { + let foo = Foo { x: 0 } ; + + let mut box_foo = Box::new(foo); + let p_foo = &mut box_foo; + + let c = #[rustc_capture_analysis] move || p_foo.x += 10; + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + //~| First Pass analysis includes: + //~| NOTE: Capturing p_foo[Deref,Deref,(0, 0)] -> MutBorrow + //~| Min Capture analysis includes: + //~| NOTE: Min Capture p_foo[] -> ByValue +} + +// Test that move closures can take ownership of Copy type +fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 { + let x = 10; + + let c = #[rustc_capture_analysis] move || x; + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + //~| First Pass analysis includes: + //~| NOTE: Capturing x[] -> ImmBorrow + //~| Min Capture analysis includes: + //~| NOTE: Min Capture x[] -> ByValue + + c +} + +fn main() { + simple_move_closure(); + simple_ref(); + struct_contains_ref_to_another_struct_1(); + struct_contains_ref_to_another_struct_2(); + struct_contains_ref_to_another_struct_3(); + truncate_box_derefs(); + box_mut_2(); + box_mut_1(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.stderr b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr new file mode 100644 index 000000000..fd80e05c6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr @@ -0,0 +1,462 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:12:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:30:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:51:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:74:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:96:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:117:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:133:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:150:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:172:13 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:189:13 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:202:13 + | +LL | let c = #[rustc_capture_analysis] move || x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:202:39 + | +LL | let c = #[rustc_capture_analysis] move || x; + | ^^^^^^^^^ + | +note: Capturing x[] -> ImmBorrow + --> $DIR/move_closure.rs:202:47 + | +LL | let c = #[rustc_capture_analysis] move || x; + | ^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:202:39 + | +LL | let c = #[rustc_capture_analysis] move || x; + | ^^^^^^^^^ + | +note: Min Capture x[] -> ByValue + --> $DIR/move_closure.rs:202:47 + | +LL | let c = #[rustc_capture_analysis] move || x; + | ^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:15:5 + | +LL | / move || { +LL | | +LL | | +LL | | t.0.0 = "new S".into(); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),(0, 0)] -> MutBorrow + --> $DIR/move_closure.rs:18:9 + | +LL | t.0.0 = "new S".into(); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:15:5 + | +LL | / move || { +LL | | +LL | | +LL | | t.0.0 = "new S".into(); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0),(0, 0)] -> ByValue + --> $DIR/move_closure.rs:18:9 + | +LL | t.0.0 = "new S".into(); + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:33:5 + | +LL | / move || { +LL | | +LL | | +LL | | *ref_s += 10; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing ref_s[Deref] -> MutBorrow + --> $DIR/move_closure.rs:36:9 + | +LL | *ref_s += 10; + | ^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:33:5 + | +LL | / move || { +LL | | +LL | | +LL | | *ref_s += 10; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture ref_s[] -> ByValue + --> $DIR/move_closure.rs:36:9 + | +LL | *ref_s += 10; + | ^^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:54:5 + | +LL | / move || { +LL | | +LL | | +LL | | t.0.0 = "new s".into(); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),Deref,(0, 0)] -> MutBorrow + --> $DIR/move_closure.rs:57:9 + | +LL | t.0.0 = "new s".into(); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:54:5 + | +LL | / move || { +LL | | +LL | | +LL | | t.0.0 = "new s".into(); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/move_closure.rs:57:9 + | +LL | t.0.0 = "new s".into(); + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:77:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = t.0.0; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:80:18 + | +LL | let _t = t.0.0; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:77:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = t.0.0; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/move_closure.rs:80:18 + | +LL | let _t = t.0.0; + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:99:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = t.0.0; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue + --> $DIR/move_closure.rs:102:18 + | +LL | let _t = t.0.0; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:99:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = t.0.0; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/move_closure.rs:102:18 + | +LL | let _t = t.0.0; + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:120:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = b.0; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing b[Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:123:18 + | +LL | let _t = b.0; + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:120:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = b.0; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture b[] -> ByValue + --> $DIR/move_closure.rs:123:18 + | +LL | let _t = b.0; + | ^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:136:5 + | +LL | / move || { +LL | | +LL | | +LL | | println!("{}", b.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing b[Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:139:24 + | +LL | println!("{}", b.0); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:136:5 + | +LL | / move || { +LL | | +LL | | +LL | | println!("{}", b.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture b[] -> ByValue + --> $DIR/move_closure.rs:139:24 + | +LL | println!("{}", b.0); + | ^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:153:5 + | +LL | / move || { +LL | | +LL | | +LL | | println!("{}", t.1.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:156:24 + | +LL | println!("{}", t.1.0); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:153:5 + | +LL | / move || { +LL | | +LL | | +LL | | println!("{}", t.1.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(1, 0)] -> ByValue + --> $DIR/move_closure.rs:156:24 + | +LL | println!("{}", t.1.0); + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:172:39 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Capturing box_p_foo[Deref,Deref,(0, 0)] -> MutBorrow + --> $DIR/move_closure.rs:172:47 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:172:39 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Min Capture box_p_foo[] -> ByValue + --> $DIR/move_closure.rs:172:47 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:189:39 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: Capturing p_foo[Deref,Deref,(0, 0)] -> MutBorrow + --> $DIR/move_closure.rs:189:47 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:189:39 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: Min Capture p_foo[] -> ByValue + --> $DIR/move_closure.rs:189:47 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^ + +error: aborting due to 33 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs new file mode 100644 index 000000000..a8a2acfa7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -0,0 +1,39 @@ +// edition:2021 + +#![feature(rustc_attrs)] +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + // Only paths that appears within the closure that directly start off + // a variable defined outside the closure are captured. + // + // Therefore `w.p` is captured + // Note that `wp.x` doesn't start off a variable defined outside the closure. + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let wp = &w.p; + //~^ NOTE: Capturing w[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture w[(0, 0)] -> ImmBorrow + println!("{}", wp.x); + }; + + // Since `c` captures `w.p` by an ImmBorrow, `w.p.y` can't be mutated. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr new file mode 100644 index 000000000..29ad1c591 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr @@ -0,0 +1,48 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/multilevel-path-1.rs:22:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/multilevel-path-1.rs:25:5 + | +LL | / || { +LL | | +LL | | +LL | | let wp = &w.p; +... | +LL | | println!("{}", wp.x); +LL | | }; + | |_____^ + | +note: Capturing w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:28:19 + | +LL | let wp = &w.p; + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/multilevel-path-1.rs:25:5 + | +LL | / || { +LL | | +LL | | +LL | | let wp = &w.p; +... | +LL | | println!("{}", wp.x); +LL | | }; + | |_____^ + | +note: Min Capture w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:28:19 + | +LL | let wp = &w.p; + | ^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs new file mode 100644 index 000000000..e21fe318c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -0,0 +1,33 @@ +// edition:2021 + +#![feature(rustc_attrs)] +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", w.p.x); + //~^ NOTE: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + }; + + // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr new file mode 100644 index 000000000..929cba113 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr @@ -0,0 +1,48 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/multilevel-path-2.rs:17:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/multilevel-path-2.rs:20:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", w.p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:23:24 + | +LL | println!("{}", w.p.x); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/multilevel-path-2.rs:20:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", w.p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:23:24 + | +LL | println!("{}", w.p.x); + | ^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs new file mode 100644 index 000000000..22eae744b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -0,0 +1,53 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +struct Point { + x: i32, + y: i32, +} + +// This testcase ensures that nested closures are handles properly +// - The nested closure is analyzed first. +// - The capture kind of the nested closure is accounted for by the enclosing closure +// - Any captured path by the nested closure that starts off a local variable in the enclosing +// closure is not listed as a capture of the enclosing closure. + +fn main() { + let mut p = Point { x: 5, y: 20 }; + + let mut c1 = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", p.x); + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow + let incr = 10; + let mut c2 = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || p.y += incr; + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing p[(1, 0)] -> MutBorrow + //~| NOTE: Capturing incr[] -> ImmBorrow + //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture incr[] -> ImmBorrow + //~| NOTE: Capturing p[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow + c2(); + println!("{}", p.y); + //~^ NOTE: Capturing p[(1, 0)] -> ImmBorrow + }; + + c1(); + + let px = &p.x; + + println!("{}", px); + + c1(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr new file mode 100644 index 000000000..a50d0c6a1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -0,0 +1,106 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/nested-closure.rs:19:18 + | +LL | let mut c1 = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/nested-closure.rs:29:22 + | +LL | let mut c2 = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/nested-closure.rs:32:9 + | +LL | || p.y += incr; + | ^^^^^^^^^^^^^^ + | +note: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:32:12 + | +LL | || p.y += incr; + | ^^^ +note: Capturing incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:32:19 + | +LL | || p.y += incr; + | ^^^^ + +error: Min Capture analysis includes: + --> $DIR/nested-closure.rs:32:9 + | +LL | || p.y += incr; + | ^^^^^^^^^^^^^^ + | +note: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:32:12 + | +LL | || p.y += incr; + | ^^^ +note: Min Capture incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:32:19 + | +LL | || p.y += incr; + | ^^^^ + +error: First Pass analysis includes: + --> $DIR/nested-closure.rs:22:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:25:24 + | +LL | println!("{}", p.x); + | ^^^ +note: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:32:12 + | +LL | || p.y += incr; + | ^^^ +note: Capturing p[(1, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:42:24 + | +LL | println!("{}", p.y); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/nested-closure.rs:22:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:25:24 + | +LL | println!("{}", p.x); + | ^^^ +note: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:32:12 + | +LL | || p.y += incr; + | ^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs new file mode 100644 index 000000000..e7edc0bbc --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs @@ -0,0 +1,34 @@ +// edition:2021 + +#![feature(rustc_attrs)] +#![allow(unused)] +#![allow(dead_code)] + +struct Int(i32); +struct B<'a>(&'a i32); + +const I : Int = Int(0); +const REF_I : &'static Int = &I; + + +struct MyStruct<'a> { + a: &'static Int, + b: B<'a>, +} + +fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { + let c = #[rustc_capture_analysis] || drop(&m.a.0); + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + //~| ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow + c +} + +fn main() { + let t = 0; + let s = MyStruct { a: REF_I, b: B(&t) }; + let _ = foo(&s); +} diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr new file mode 100644 index 000000000..87d5d5bee --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr @@ -0,0 +1,36 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/edge_case.rs:20:13 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/edge_case.rs:20:39 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^^^^^^^^^^^ + | +note: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/edge_case.rs:20:48 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/edge_case.rs:20:39 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^^^^^^^^^^^ + | +note: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow + --> $DIR/edge_case.rs:20:48 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs new file mode 100644 index 000000000..033fd6f17 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs @@ -0,0 +1,27 @@ +// edition:2021 +// run-pass + +#![allow(unused)] +#![allow(dead_code)] + +struct Int(i32); +struct B<'a>(&'a i32); + +const I : Int = Int(0); +const REF_I : &'static Int = &I; + +struct MyStruct<'a> { + a: &'static Int, + b: B<'a>, +} + +fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { + let c = || drop(&m.a.0); + c +} + +fn main() { + let t = 0; + let s = MyStruct { a: REF_I, b: B(&t) }; + let _ = foo(&s); +} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs new file mode 100644 index 000000000..0c1031931 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -0,0 +1,33 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +struct Point { + x: f32, + y: f32, +} + +struct Pentagon { + points: [Point; 5], +} + +fn main() { + let p1 = Point { x: 10.0, y: 10.0 }; + let p2 = Point { x: 7.5, y: 12.5 }; + let p3 = Point { x: 15.0, y: 15.0 }; + let p4 = Point { x: 12.5, y: 12.5 }; + let p5 = Point { x: 20.0, y: 10.0 }; + + let pent = Pentagon { points: [p1, p2, p3, p4, p5] }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", pent.points[5].x); + //~^ NOTE: Capturing pent[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture pent[(0, 0)] -> ImmBorrow + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr new file mode 100644 index 000000000..124b7bf6f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr @@ -0,0 +1,48 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/path-with-array-access.rs:23:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/path-with-array-access.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", pent.points[5].x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:29:24 + | +LL | println!("{}", pent.points[5].x); + | ^^^^^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/path-with-array-access.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", pent.points[5].x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:29:24 + | +LL | println!("{}", pent.points[5].x); + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs new file mode 100644 index 000000000..2f8cddc06 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs @@ -0,0 +1,101 @@ +// edition:2021 + +// Tests that in cases where we individually capture all the fields of a type, +// we still drop them in the order they would have been dropped in the 2018 edition. + +// NOTE: It is *critical* that the order of the min capture NOTES in the stderr output +// does *not* change! + +#![feature(rustc_attrs)] + +#[derive(Debug)] +struct HasDrop; +impl Drop for HasDrop { + fn drop(&mut self) { + println!("dropped"); + } +} + +fn test_one() { + let a = (HasDrop, HasDrop); + let b = (HasDrop, HasDrop); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: Min Capture analysis includes: + //~| ERROR + println!("{:?}", a.0); + //~^ NOTE: Min Capture a[(0, 0)] -> ImmBorrow + //~| NOTE + println!("{:?}", a.1); + //~^ NOTE: Min Capture a[(1, 0)] -> ImmBorrow + //~| NOTE + + println!("{:?}", b.0); + //~^ NOTE: Min Capture b[(0, 0)] -> ImmBorrow + //~| NOTE + println!("{:?}", b.1); + //~^ NOTE: Min Capture b[(1, 0)] -> ImmBorrow + //~| NOTE + }; +} + +fn test_two() { + let a = (HasDrop, HasDrop); + let b = (HasDrop, HasDrop); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: Min Capture analysis includes: + //~| ERROR + println!("{:?}", a.1); + //~^ NOTE: Min Capture a[(1, 0)] -> ImmBorrow + //~| NOTE + println!("{:?}", a.0); + //~^ NOTE: Min Capture a[(0, 0)] -> ImmBorrow + //~| NOTE + + println!("{:?}", b.1); + //~^ NOTE: Min Capture b[(1, 0)] -> ImmBorrow + //~| NOTE + println!("{:?}", b.0); + //~^ NOTE: Min Capture b[(0, 0)] -> ImmBorrow + //~| NOTE + }; +} + +fn test_three() { + let a = (HasDrop, HasDrop); + let b = (HasDrop, HasDrop); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: Min Capture analysis includes: + //~| ERROR + println!("{:?}", b.1); + //~^ NOTE: Min Capture b[(1, 0)] -> ImmBorrow + //~| NOTE + println!("{:?}", a.1); + //~^ NOTE: Min Capture a[(1, 0)] -> ImmBorrow + //~| NOTE + println!("{:?}", a.0); + //~^ NOTE: Min Capture a[(0, 0)] -> ImmBorrow + //~| NOTE + + println!("{:?}", b.0); + //~^ NOTE: Min Capture b[(0, 0)] -> ImmBorrow + //~| NOTE + }; +} + +fn main() { + test_one(); + test_two(); + test_three(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr new file mode 100644 index 000000000..2d1dc8727 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr @@ -0,0 +1,228 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/preserve_field_drop_order.rs:23:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/preserve_field_drop_order.rs:49:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/preserve_field_drop_order.rs:75:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/preserve_field_drop_order.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{:?}", a.0); +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing a[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:29:26 + | +LL | println!("{:?}", a.0); + | ^^^ +note: Capturing a[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:32:26 + | +LL | println!("{:?}", a.1); + | ^^^ +note: Capturing b[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:36:26 + | +LL | println!("{:?}", b.0); + | ^^^ +note: Capturing b[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:39:26 + | +LL | println!("{:?}", b.1); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/preserve_field_drop_order.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{:?}", a.0); +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture a[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:29:26 + | +LL | println!("{:?}", a.0); + | ^^^ +note: Min Capture a[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:32:26 + | +LL | println!("{:?}", a.1); + | ^^^ +note: Min Capture b[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:36:26 + | +LL | println!("{:?}", b.0); + | ^^^ +note: Min Capture b[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:39:26 + | +LL | println!("{:?}", b.1); + | ^^^ + +error: First Pass analysis includes: + --> $DIR/preserve_field_drop_order.rs:52:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{:?}", a.1); +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing a[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:55:26 + | +LL | println!("{:?}", a.1); + | ^^^ +note: Capturing a[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:58:26 + | +LL | println!("{:?}", a.0); + | ^^^ +note: Capturing b[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:62:26 + | +LL | println!("{:?}", b.1); + | ^^^ +note: Capturing b[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:65:26 + | +LL | println!("{:?}", b.0); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/preserve_field_drop_order.rs:52:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{:?}", a.1); +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture a[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:58:26 + | +LL | println!("{:?}", a.0); + | ^^^ +note: Min Capture a[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:55:26 + | +LL | println!("{:?}", a.1); + | ^^^ +note: Min Capture b[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:65:26 + | +LL | println!("{:?}", b.0); + | ^^^ +note: Min Capture b[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:62:26 + | +LL | println!("{:?}", b.1); + | ^^^ + +error: First Pass analysis includes: + --> $DIR/preserve_field_drop_order.rs:78:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{:?}", b.1); +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing b[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:81:26 + | +LL | println!("{:?}", b.1); + | ^^^ +note: Capturing a[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:84:26 + | +LL | println!("{:?}", a.1); + | ^^^ +note: Capturing a[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:87:26 + | +LL | println!("{:?}", a.0); + | ^^^ +note: Capturing b[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:91:26 + | +LL | println!("{:?}", b.0); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/preserve_field_drop_order.rs:78:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{:?}", b.1); +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture b[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:91:26 + | +LL | println!("{:?}", b.0); + | ^^^ +note: Min Capture b[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:81:26 + | +LL | println!("{:?}", b.1); + | ^^^ +note: Min Capture a[(0, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:87:26 + | +LL | println!("{:?}", a.0); + | ^^^ +note: Min Capture a[(1, 0)] -> ImmBorrow + --> $DIR/preserve_field_drop_order.rs:84:26 + | +LL | println!("{:?}", a.1); + | ^^^ + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs new file mode 100644 index 000000000..1cae776dd --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs @@ -0,0 +1,58 @@ +// run-pass +// check-run-results +// revisions: twenty_eighteen twenty_twentyone +// [twenty_eighteen]compile-flags: --edition 2018 +// [twenty_twentyone]compile-flags: --edition 2021 + +#[derive(Debug)] +struct Dropable(&'static str); + +impl Drop for Dropable { + fn drop(&mut self) { + println!("Dropping {}", self.0) + } +} + +#[derive(Debug)] +struct A { + x: Dropable, + y: Dropable, +} + +#[derive(Debug)] +struct B { + c: A, + d: A, +} + +#[derive(Debug)] +struct R<'a> { + c: &'a A, + d: &'a A, +} + +fn main() { + let a = A { x: Dropable("x"), y: Dropable("y") }; + + let c = move || println!("{:?} {:?}", a.y, a.x); + + c(); + + let b = B { + c: A { x: Dropable("b.c.x"), y: Dropable("b.c.y") }, + d: A { x: Dropable("b.d.x"), y: Dropable("b.d.y") }, + }; + + let d = move || println!("{:?} {:?} {:?} {:?}", b.d.y, b.d.x, b.c.y, b.c.x); + + d(); + + let r = R { + c: &A { x: Dropable("r.c.x"), y: Dropable("r.c.y") }, + d: &A { x: Dropable("r.d.x"), y: Dropable("r.d.y") }, + }; + + let e = move || println!("{:?} {:?} {:?} {:?}", r.d.y, r.d.x, r.c.y, r.c.x); + + e(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout new file mode 100644 index 000000000..557d047c1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout @@ -0,0 +1,13 @@ +Dropable("y") Dropable("x") +Dropable("b.d.y") Dropable("b.d.x") Dropable("b.c.y") Dropable("b.c.x") +Dropable("r.d.y") Dropable("r.d.x") Dropable("r.c.y") Dropable("r.c.x") +Dropping r.d.x +Dropping r.d.y +Dropping r.c.x +Dropping r.c.y +Dropping b.c.x +Dropping b.c.y +Dropping b.d.x +Dropping b.d.y +Dropping x +Dropping y diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout new file mode 100644 index 000000000..557d047c1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout @@ -0,0 +1,13 @@ +Dropable("y") Dropable("x") +Dropable("b.d.y") Dropable("b.d.x") Dropable("b.c.y") Dropable("b.c.x") +Dropable("r.d.y") Dropable("r.d.x") Dropable("r.c.y") Dropable("r.c.x") +Dropping r.d.x +Dropping r.d.y +Dropping r.c.x +Dropping r.c.y +Dropping b.c.x +Dropping b.c.y +Dropping b.d.x +Dropping b.d.y +Dropping x +Dropping y diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.rs b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs new file mode 100644 index 000000000..3ed780f51 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs @@ -0,0 +1,101 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +// `u8` aligned at a byte and are unaffected by repr(packed). +// Therefore we can precisely (and safely) capture references to both the fields. +fn test_alignment_not_affected() { + #[repr(packed)] + struct Foo { x: u8, y: u8 } + + let mut foo = Foo { x: 0, y: 0 }; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let z1: &u8 = &foo.x; + //~^ NOTE: Capturing foo[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture foo[(0, 0)] -> ImmBorrow + let z2: &mut u8 = &mut foo.y; + //~^ NOTE: Capturing foo[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture foo[(1, 0)] -> MutBorrow + + *z2 = 42; + + println!("({}, {})", z1, z2); + }; + + c(); +} + +// `String`, `u16` are not aligned at a one byte boundry and are thus affected by repr(packed). +// +// Here we test that the closure doesn't capture a reference point to `foo.x` but +// rather capture `foo` entirely. +fn test_alignment_affected() { + #[repr(packed)] + struct Foo { x: String, y: u16 } + + let mut foo = Foo { x: String::new(), y: 0 }; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let z1: &String = &foo.x; + //~^ NOTE: Capturing foo[] -> ImmBorrow + let z2: &mut u16 = &mut foo.y; + //~^ NOTE: Capturing foo[] -> MutBorrow + //~| NOTE: Min Capture foo[] -> MutBorrow + + + *z2 = 42; + + println!("({}, {})", z1, z2); + }; + + c(); +} + +// Given how the closure desugaring is implemented (at least at the time of writing this test), +// we don't need to truncate the captured path to a reference into a packed-struct if the field +// being referenced will be moved into the closure, since it's safe to move out a field from a +// packed-struct. +// +// However to avoid surprises for the user, or issues when the closure is +// inlined we will truncate the capture to access just the struct regardless of if the field +// might get moved into the closure. +fn test_truncation_when_ref_and_move() { + #[repr(packed)] + struct Foo { x: String } + + let mut foo = Foo { x: String::new() }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", foo.x); + //~^ NOTE: Capturing foo[] -> ImmBorrow + //~| NOTE: Min Capture foo[] -> ByValue + //~| NOTE: foo[] used here + let _z = foo.x; + //~^ NOTE: Capturing foo[(0, 0)] -> ByValue + //~| NOTE: foo[] captured as ByValue here + }; + + c(); +} + +fn main() { + test_truncation_when_ref_and_move(); + test_alignment_affected(); + test_alignment_not_affected(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr new file mode 100644 index 000000000..580061ebc --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr @@ -0,0 +1,161 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/repr_packed.rs:13:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/repr_packed.rs:44:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/repr_packed.rs:79:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/repr_packed.rs:16:5 + | +LL | / || { +LL | | +LL | | +LL | | let z1: &u8 = &foo.x; +... | +LL | | println!("({}, {})", z1, z2); +LL | | }; + | |_____^ + | +note: Capturing foo[(0, 0)] -> ImmBorrow + --> $DIR/repr_packed.rs:19:24 + | +LL | let z1: &u8 = &foo.x; + | ^^^^^ +note: Capturing foo[(1, 0)] -> MutBorrow + --> $DIR/repr_packed.rs:22:32 + | +LL | let z2: &mut u8 = &mut foo.y; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/repr_packed.rs:16:5 + | +LL | / || { +LL | | +LL | | +LL | | let z1: &u8 = &foo.x; +... | +LL | | println!("({}, {})", z1, z2); +LL | | }; + | |_____^ + | +note: Min Capture foo[(0, 0)] -> ImmBorrow + --> $DIR/repr_packed.rs:19:24 + | +LL | let z1: &u8 = &foo.x; + | ^^^^^ +note: Min Capture foo[(1, 0)] -> MutBorrow + --> $DIR/repr_packed.rs:22:32 + | +LL | let z2: &mut u8 = &mut foo.y; + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/repr_packed.rs:47:5 + | +LL | / || { +LL | | +LL | | +LL | | let z1: &String = &foo.x; +... | +LL | | println!("({}, {})", z1, z2); +LL | | }; + | |_____^ + | +note: Capturing foo[] -> ImmBorrow + --> $DIR/repr_packed.rs:50:28 + | +LL | let z1: &String = &foo.x; + | ^^^^^ +note: Capturing foo[] -> MutBorrow + --> $DIR/repr_packed.rs:52:33 + | +LL | let z2: &mut u16 = &mut foo.y; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/repr_packed.rs:47:5 + | +LL | / || { +LL | | +LL | | +LL | | let z1: &String = &foo.x; +... | +LL | | println!("({}, {})", z1, z2); +LL | | }; + | |_____^ + | +note: Min Capture foo[] -> MutBorrow + --> $DIR/repr_packed.rs:52:33 + | +LL | let z2: &mut u16 = &mut foo.y; + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/repr_packed.rs:82:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", foo.x); +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing foo[] -> ImmBorrow + --> $DIR/repr_packed.rs:85:24 + | +LL | println!("{}", foo.x); + | ^^^^^ +note: Capturing foo[(0, 0)] -> ByValue + --> $DIR/repr_packed.rs:89:18 + | +LL | let _z = foo.x; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/repr_packed.rs:82:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", foo.x); +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture foo[] -> ByValue + --> $DIR/repr_packed.rs:85:24 + | +LL | println!("{}", foo.x); + | ^^^^^ foo[] used here +... +LL | let _z = foo.x; + | ^^^^^ foo[] captured as ByValue here + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/box.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/box.rs new file mode 100644 index 000000000..73aca288f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/box.rs @@ -0,0 +1,92 @@ +// edition:2021 +// run-pass + +// Test precise capture when using boxes + +struct MetaData { x: String, name: String } +struct Data { m: MetaData } +struct BoxedData(Box<Data>); +struct EvenMoreBoxedData(Box<BoxedData>); + +// Mutate disjoint paths, one inside one outside the closure +fn box_1() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let mut c = || { + e.0.0.m.x = format!("not-x"); + }; + + e.0.0.m.name = format!("not-name"); + c(); +} + +// Mutate a path inside the closure and read a disjoint path outside the closure +fn box_2() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let mut c = || { + e.0.0.m.x = format!("not-x"); + }; + + println!("{}", e.0.0.m.name); + c(); +} + +// Read a path inside the closure and mutate a disjoint path outside the closure +fn box_3() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let mut e = EvenMoreBoxedData(Box::new(b)); + + let c = || { + println!("{}", e.0.0.m.name); + }; + + e.0.0.m.x = format!("not-x"); + c(); +} + +// Read disjoint paths, one inside the closure and one outside the closure. +fn box_4() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let e = EvenMoreBoxedData(Box::new(b)); + + let c = || { + println!("{}", e.0.0.m.name); + }; + + println!("{}", e.0.0.m.x); + c(); +} + +// Read the same path, once inside the closure and once outside the closure. +fn box_5() { + let m = MetaData { x: format!("x"), name: format!("name") }; + let d = Data { m }; + let b = BoxedData(Box::new(d)); + let e = EvenMoreBoxedData(Box::new(b)); + + let c = || { + println!("{}", e.0.0.m.name); + }; + + println!("{}", e.0.0.m.name); + c(); +} + +fn main() { + box_1(); + box_2(); + box_3(); + box_4(); + box_5(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs new file mode 100644 index 000000000..2c828aed5 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs @@ -0,0 +1,26 @@ +// edition:2021 +// run-pass + +// Test that ByValue captures compile sucessefully especially when the captures are +// derefenced within the closure. + +#[derive(Debug, Default)] +struct SomeLargeType; +struct MuchLargerType([SomeLargeType; 32]); + +fn big_box() { + let s = MuchLargerType(Default::default()); + let b = Box::new(s); + let t = (b, 10); + + let c = || { + let p = t.0.0; + println!("{} {:?}", t.1, p); + }; + + c(); +} + +fn main() { + big_box(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs new file mode 100644 index 000000000..3cb1eb329 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs @@ -0,0 +1,24 @@ +// edition:2021 +// run-pass + +// Test that we can immutably borrow field of an instance of a structure from within a closure, +// while having a mutable borrow to another field of the same instance outside the closure. + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 10 }; + + let c = || { + println!("{}", p.x); + }; + + // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. + let py = &mut p.y; + + c(); + *py = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs new file mode 100644 index 000000000..0f79b7ae7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs @@ -0,0 +1,20 @@ +// edition:2021 +// run-pass + +// Test that we can mutate an element of a tuple from within a closure +// while immutably borrowing another element of the same tuple outside the closure. + +#![feature(rustc_attrs)] + +fn main() { + let mut t = (10, 10); + + let mut c = || { + let t1 = &mut t.1; + *t1 = 20; + }; + + // Test that `c` only captures t.1, therefore reading t.0 is allowed. + println!("{}", t.0); + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs new file mode 100644 index 000000000..81f0328b9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs @@ -0,0 +1,21 @@ +// edition:2021 +// run-pass + +// Test that we can immutably borrow an element of a tuple from within a closure, +// while having a mutable borrow to another element of the same tuple outside the closure. + +#![feature(rustc_attrs)] + +fn main() { + let mut t = (10, 10); + + let c = || { + println!("{}", t.0); + }; + + // `c` only captures t.0, therefore mutating t.1 is allowed. + let t1 = &mut t.1; + + c(); + *t1 = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs new file mode 100644 index 000000000..cea02fbe1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs @@ -0,0 +1,27 @@ +// edition:2021 +//check-pass + +fn test1() { + let foo : [Vec<u8>; 3] = ["String".into(), "String".into(), "String".into()]; + let c = || { + match foo { _ => () }; + }; + drop(foo); + c(); +} + +fn test2() { + let foo : Option<[Vec<u8>; 3]> = Some(["String".into(), "String".into(), "String".into()]); + let c = || { + match foo { + Some(_) => 1, + _ => 2 + }; + }; + c(); +} + +fn main() { + test1(); + test2(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs new file mode 100644 index 000000000..5c278bff9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs @@ -0,0 +1,21 @@ +// edition:2021 +// check-pass +#![warn(unused)] + +fn main() { + let t = (String::from("Hello"), String::from("World")); + let g = (String::from("Mr"), String::from("Goose")); + + let a = || { + let (_, g2) = g; + //~^ WARN unused variable: `g2` + let c = || { + let (_, t2) = t; + //~^ WARN unused variable: `t2` + }; + + c(); + }; + + a(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr new file mode 100644 index 000000000..40274c883 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr @@ -0,0 +1,21 @@ +warning: unused variable: `t2` + --> $DIR/destructure-pattern-closure-within-closure.rs:13:21 + | +LL | let (_, t2) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t2` + | +note: the lint level is defined here + --> $DIR/destructure-pattern-closure-within-closure.rs:3:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: unused variable: `g2` + --> $DIR/destructure-pattern-closure-within-closure.rs:10:17 + | +LL | let (_, g2) = g; + | ^^ help: if this is intentional, prefix it with an underscore: `_g2` + +warning: 2 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs new file mode 100644 index 000000000..dacc2c616 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs @@ -0,0 +1,119 @@ +// edition:2021 +// check-pass +#![warn(unused)] + +struct Point { + x: u32, + y: u32, +} + +fn test1() { + let t = (String::from("Hello"), String::from("World")); + + let c = || { + let (t1, t2) = t; + //~^ WARN unused variable: `t1` + //~| WARN unused variable: `t2` + }; + + c(); +} + +fn test2() { + let t = (String::from("Hello"), String::from("World")); + + let c = || { + let (t1, _) = t; + //~^ WARN unused variable: `t1` + }; + + c(); +} + +fn test3() { + let t = (String::from("Hello"), String::from("World")); + + let c = || { + let (_, t2) = t; + //~^ WARN unused variable: `t2` + }; + + c(); +} + +fn test4() { + let t = (String::from("Hello"), String::from("World")); + + let c = || { + let (_, _) = t; + }; + + c(); +} + +fn test5() { + let t = (String::new(), String::new()); + let _c = || { + let _a = match t { + (t1, _) => t1, + }; + }; +} + +fn test6() { + let t = (String::new(), String::new()); + let _c = || { + let _a = match t { + (_, t2) => t2, + }; + }; +} + +fn test7() { + let t = (String::new(), String::new()); + let _c = || { + let _a = match t { + (t1, t2) => (t1, t2), + }; + }; +} + +fn test8() { + let x = 0; + let tup = (1, 2); + let p = Point { x: 10, y: 20 }; + + let c = || { + let _ = x; + let Point { x, y } = p; + //~^ WARN unused variable: `x` + println!("{}", y); + let (_, _) = tup; + }; + + c(); +} + +fn test9() { + let _z = 9; + let t = (String::from("Hello"), String::from("World")); + + let c = || { + let (_, t) = t; + println!("{}", t); + }; + + c(); +} + +fn main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr new file mode 100644 index 000000000..7706f68ba --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr @@ -0,0 +1,39 @@ +warning: unused variable: `t1` + --> $DIR/destructure_patterns.rs:14:14 + | +LL | let (t1, t2) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t1` + | +note: the lint level is defined here + --> $DIR/destructure_patterns.rs:3:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: unused variable: `t2` + --> $DIR/destructure_patterns.rs:14:18 + | +LL | let (t1, t2) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t2` + +warning: unused variable: `t1` + --> $DIR/destructure_patterns.rs:26:14 + | +LL | let (t1, _) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t1` + +warning: unused variable: `t2` + --> $DIR/destructure_patterns.rs:37:17 + | +LL | let (_, t2) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t2` + +warning: unused variable: `x` + --> $DIR/destructure_patterns.rs:88:21 + | +LL | let Point { x, y } = p; + | ^ help: try ignoring the field: `x: _` + +warning: 5 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs new file mode 100644 index 000000000..88a9816a0 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs @@ -0,0 +1,23 @@ +// edition:2021 +// run-pass + +// Tests that if a closure uses indivual fields of the same object +// then that case is handled properly. + +#![allow(unused)] + +struct Struct { + x: i32, + y: i32, + s: String, +} + +fn main() { + let mut s = Struct { x: 10, y: 10, s: String::new() }; + + let mut c = { + s.x += 10; + s.y += 42; + s.s = String::from("new"); + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs new file mode 100644 index 000000000..477fdd613 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs @@ -0,0 +1,11 @@ +// edition:2021 +// check-pass +#![feature(rustc_attrs)] + +fn main() { + let mut x = 1; + let c = || { + drop(&mut x); + match x { _ => () } + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs new file mode 100644 index 000000000..20bbe1d89 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs @@ -0,0 +1,23 @@ +// edition:2021 +// run-pass + +// Test that edition 2021 enables disjoint capture by default. + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 10 }; + + let c = || { + println!("{}", p.x); + }; + + // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. + let py = &mut p.y; + + c(); + *py = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs new file mode 100644 index 000000000..e19f5ff1b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs @@ -0,0 +1,37 @@ +// edition:2021 +// run-pass + +// Test disjoint capture within an impl block + +struct Filter { + div: i32, +} +impl Filter { + fn allowed(&self, x: i32) -> bool { + x % self.div == 1 + } +} + +struct Data { + filter: Filter, + list: Vec<i32>, +} +impl Data { + fn update(&mut self) { + // The closure passed to filter only captures self.filter, + // therefore mutating self.list is allowed. + self.list.retain( + |v| self.filter.allowed(*v), + ); + } +} + +fn main() { + let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() }; + + for i in 1..10 { + d.list.push(i); + } + + d.update(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs new file mode 100644 index 000000000..1286613cb --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs @@ -0,0 +1,42 @@ +// edition:2021 +// run-pass + +// Test that functional record update/struct update syntax works inside +// a closure when the feature `capture_disjoint_fields` is enabled. + +#[derive(Clone)] +struct S { + a: String, + b: String, +} + +struct T { + a: String, + s: S, +} + +fn main() { + let a = String::new(); + let b = String::new(); + let c = String::new(); + let s = S {a, b}; + let t = T { + a: c, + s: s.clone() + }; + + let c = || { + let s2 = S { + a: format!("New s2"), + ..s + }; + let s3 = S { + a: format!("New s3"), + ..t.s + }; + println!("{} {}", s2.a, s2.b); + println!("{} {} {}", s3.a, s3.b, t.a); + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs new file mode 100644 index 000000000..c64475fda --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs @@ -0,0 +1,16 @@ +// edition:2021 +// check-pass + +union Union { + value: u64, +} + +fn main() { + let u = Union { value: 42 }; + + let c = || { + unsafe { u.value } + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs new file mode 100644 index 000000000..25fbb6cb9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs @@ -0,0 +1,19 @@ +// edition:2021 +// run-pass + + +fn solve<F>(validate: F) -> Option<u64> +where + F: Fn(&mut [i8; 1]), +{ + let mut position: [i8; 1] = [1]; + Some(0).map(|_| { + validate(&mut position); + let [_x] = position; + 0 + }) +} + +fn main() { + solve(|_| ()); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs new file mode 100644 index 000000000..999620530 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs @@ -0,0 +1,59 @@ +// edition:2021 +// check-pass + +use std::collections::HashMap; +use std::future::Future; +use std::pin::Pin; + +pub struct GameMode {} + +struct GameStateManager<'a> { + gamestate_stack: Vec<Box<dyn GameState<'a> + 'a>>, +} + +pub trait GameState<'a> {} + +async fn construct_gamestate_replay<'a>( + _gamemode: &GameMode, + _factory: &mut GameStateManager<'a>, +) -> Box<dyn GameState<'a> + 'a> { + unimplemented!() +} + +type FutureGameState<'a, 'b> = Pin<Box<dyn Future<Output = Box<dyn GameState<'a> + 'a>> + 'b>>; + +struct MenuOption<'a> { + command: Box<dyn for<'b> Fn(&'b mut GameStateManager<'a>) -> FutureGameState<'a, 'b> + 'a>, +} + +impl<'a> MenuOption<'a> { + fn new( + _command: impl for<'b> Fn(&'b mut GameStateManager<'a>) -> FutureGameState<'a, 'b> + 'a, + ) -> Self { + unimplemented!() + } +} + +struct MenuState<'a> { + options: Vec<MenuOption<'a>>, +} + +impl<'a> GameState<'a> for MenuState<'a> {} + +pub async fn get_replay_menu<'a>( + gamemodes: &'a HashMap<&str, GameMode>, +) -> Box<dyn GameState<'a> + 'a> { + let recordings: Vec<String> = vec![]; + let _ = recordings + .into_iter() + .map(|entry| { + MenuOption::new(move |f| { + Box::pin(construct_gamestate_replay(&gamemodes[entry.as_str()], f)) + }) + }) + .collect::<Vec<_>>(); + + todo!() +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs new file mode 100644 index 000000000..f44c2af80 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs @@ -0,0 +1,47 @@ +// check-pass +// edition:2021 + +use std::rc::Rc; + +// Test that we restrict precision when moving not-`Copy` types, if any of the parent paths +// implement `Drop`. This is to ensure that we don't move out of a type that implements Drop. +pub fn test1() { + struct Foo(Rc<i32>); + + impl Drop for Foo { + fn drop(self: &mut Foo) {} + } + + let f = Foo(Rc::new(1)); + let x = move || { + println!("{:?}", f.0); + }; + + x(); +} + + +// Test that we don't restrict precision when moving `Copy` types(i.e. when copying), +// even if any of the parent paths implement `Drop`. +pub fn test2() { + struct Character { + hp: u32, + name: String, + } + + impl Drop for Character { + fn drop(&mut self) {} + } + + let character = Character { hp: 100, name: format!("A") }; + + let c = move || { + println!("{}", character.hp) + }; + + c(); + + println!("{}", character.name); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs new file mode 100644 index 000000000..d2375aa69 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs @@ -0,0 +1,30 @@ +// edition:2021 +//check-pass +#![warn(unused)] +#![feature(rustc_attrs)] +#![feature(btree_drain_filter)] + +use std::collections::BTreeMap; +use std::panic::{catch_unwind, AssertUnwindSafe}; + +fn main() { + let mut map = BTreeMap::new(); + map.insert("a", ()); + map.insert("b", ()); + map.insert("c", ()); + + { + let mut it = map.drain_filter(|_, _| true); + catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); + let result = catch_unwind(AssertUnwindSafe(|| it.next())); + assert!(matches!(result, Ok(None))); + } + + { + let mut it = map.drain_filter(|_, _| true); + catch_unwind(AssertUnwindSafe(|| while let Some(_) = it.next() {})).unwrap_err(); + let result = catch_unwind(AssertUnwindSafe(|| it.next())); + assert!(matches!(result, Ok(None))); + } + +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs new file mode 100644 index 000000000..f76965bdd --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs @@ -0,0 +1,93 @@ +// edition:2021 +// run-pass + +// Test that move closures compile properly with `capture_disjoint_fields` enabled. + +#![allow(unused)] + +fn simple_ref() { + let mut s = 10; + let ref_s = &mut s; + + let mut c = move || { + *ref_s += 10; + }; + c(); +} + +fn struct_contains_ref_to_another_struct() { + struct S(String); + struct T<'a>(&'a mut S); + + let mut s = S("s".into()); + let t = T(&mut s); + + let mut c = move || { + t.0.0 = "new s".into(); + }; + + c(); +} + +#[derive(Debug)] +struct S(String); + +#[derive(Debug)] +struct T(S); + +fn no_ref() { + let mut t = T(S("s".into())); + let mut c = move || { + t.0.0 = "new S".into(); + }; + c(); +} + +fn no_ref_nested() { + let mut t = T(S("s".into())); + let c = || { + println!("{:?}", t.0); + let mut c = move || { + t.0.0 = "new S".into(); + println!("{:?}", t.0.0); + }; + c(); + }; + c(); +} + +// Test that even if a path is moved into the closure, the closure is not FnOnce +// if the path is not moved by the closure call. +fn data_moved_but_not_fn_once() { + let x = Box::new(10i32); + + let c = move || { + // *x has type i32 which is Copy. So even though the box `x` will be moved + // into the closure, `x` is never moved when the closure is called, i.e. the + // ownership stays with the closure and therefore we can call the function multiple times. + let _x = *x; + }; + + c(); + c(); +} + +// Test that move closures can take ownership of Copy type +fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 { + let x = 10; + + let c = move || x; + + c +} + +fn main() { + simple_ref(); + struct_contains_ref_to_another_struct(); + no_ref(); + no_ref_nested(); + + data_moved_but_not_fn_once(); + + returned_closure_owns_copy_type_data(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs new file mode 100644 index 000000000..b8e464031 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs @@ -0,0 +1,33 @@ +// edition:2021 +// run-pass + +// Test that closures can catpure paths that are more precise than just one level +// from the root variable. +// +// If the closures can handle such precison we should be able to mutate one path in the closure +// while being able to mutate another path outside the closure, where the two paths are disjoint +// after applying two projections on the root variable. + +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let mut c = || { + w.p.x += 20; + }; + + // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs new file mode 100644 index 000000000..11a324d8a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs @@ -0,0 +1,31 @@ +// edition:2021 +// run-pass + +#![allow(unused)] + +// If the closures can handle such precison we should be able to read one path in the closure +// while being able mutate another path outside the closure, where the two paths are disjoint +// after applying two projections on the root variable. + + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let c = || { + println!("{}", w.p.x); + }; + + // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs new file mode 100644 index 000000000..8fc0efb60 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs @@ -0,0 +1,28 @@ +// edition:2021 +// run-pass + +#![allow(unused)] + +// Test that when `capture_disjoint_fields` is enabled we can read a path +// both inside and outside the closure at the same time. + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let c = || { + println!("{}", w.p.x); + }; + + let px = &w.p.x; + c(); + + println!("{}", px); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs new file mode 100644 index 000000000..9f0c4d96a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs @@ -0,0 +1,54 @@ +// edition:2021 +// run-pass + +// Test that we can mutate a place through a mut-borrow +// that is captured by the closure + +// Check that we can mutate when one deref is required +fn mut_ref_1() { + let mut x = String::new(); + let rx = &mut x; + + let mut c = || { + *rx = String::new(); + }; + + c(); +} + +// Similar example as mut_ref_1, we don't deref the imm-borrow here, +// and so we are allowed to mutate. +fn mut_ref_2() { + let x = String::new(); + let y = String::new(); + let mut ref_x = &x; + let m_ref_x = &mut ref_x; + + let mut c = || { + *m_ref_x = &y; + }; + + c(); +} + +// Check that we can mutate when multiple derefs of mut-borrows are required to reach +// the target place. +// It works because all derefs are mutable, if either of them was an immutable +// borrow, then we would not be able to deref. +fn mut_mut_ref() { + let mut x = String::new(); + let mut mref_x = &mut x; + let m_mref_x = &mut mref_x; + + let mut c = || { + **m_mref_x = String::new(); + }; + + c(); +} + +fn main() { + mut_ref_1(); + mut_ref_2(); + mut_mut_ref(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs new file mode 100644 index 000000000..bb784774b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs @@ -0,0 +1,43 @@ +// edition:2021 +// run-pass + +// Test that we can mutate a place through a mut-borrow +// that is captured by the closure + +// More specifically we test that the if the mutable reference isn't root variable of a capture +// but rather accessed while acessing the precise capture. + +fn mut_tuple() { + let mut t = (10, 10); + + let t1 = (&mut t, 10); + + let mut c = || { + // Mutable because (*t.0) is mutable + t1.0.0 += 10; + }; + + c(); +} + +fn mut_tuple_nested() { + let mut t = (10, 10); + + let t1 = (&mut t, 10); + + let mut c = || { + let mut c = || { + // Mutable because (*t.0) is mutable + t1.0.0 += 10; + }; + + c(); + }; + + c(); +} + +fn main() { + mut_tuple(); + mut_tuple_nested(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs new file mode 100644 index 000000000..a80b40bb4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs @@ -0,0 +1,36 @@ +// edition:2021 +// run-pass + +// Test whether if we can do precise capture when using nested clsoure. + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 5, y: 20 }; + + // c1 should capture `p.x` via immutable borrow and + // `p.y` via mutable borrow. + let mut c1 = || { + println!("{}", p.x); + + let incr = 10; + + let mut c2 = || p.y += incr; + c2(); + + println!("{}", p.y); + }; + + c1(); + + // This should not throw an error because `p.x` is borrowed via Immutable borrow, + // and multiple immutable borrow of the same place are allowed. + let px = &p.x; + + println!("{}", px); + + c1(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs new file mode 100644 index 000000000..ed222b314 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs @@ -0,0 +1,49 @@ +// edition:2021 +//check-pass +#![warn(unused)] +#![allow(dead_code)] +#![feature(rustc_attrs)] + +#[derive(Debug, Clone, Copy)] +enum PointType { + TwoD { x: u32, y: u32 }, + + ThreeD{ x: u32, y: u32, z: u32 } +} + +// Testing struct patterns +struct Points { + points: Vec<PointType>, +} + +impl Points { + pub fn test1(&mut self) -> Vec<usize> { + (0..self.points.len()) + .filter_map(|i| { + let idx = i as usize; + match self.test2(idx) { + PointType::TwoD { .. } => Some(i), + PointType::ThreeD { .. } => None, + } + }) + .collect() + } + + pub fn test2(&mut self, i: usize) -> PointType { + self.points[i] + } +} + +fn main() { + let mut points = Points { + points: Vec::<PointType>::new() + }; + + points.points.push(PointType::ThreeD { x:0, y:0, z:0 }); + points.points.push(PointType::TwoD{ x:0, y:0 }); + points.points.push(PointType::ThreeD{ x:0, y:0, z:0 }); + points.points.push(PointType::TwoD{ x:0, y:0 }); + + println!("{:?}", points.test1()); + println!("{:?}", points.points); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs new file mode 100644 index 000000000..f3f44433c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs @@ -0,0 +1,43 @@ +// edition:2021 +//check-pass + +#[derive(Copy, Clone)] +enum PointType { + TwoD(u32, u32), + ThreeD(u32, u32, u32) +} + +// Testing tuple struct patterns +struct Points { + points: Vec<PointType>, +} + +impl Points { + pub fn test1(&mut self) -> Vec<usize> { + (0..self.points.len()) + .filter_map(|i| { + match self.test2(i) { + PointType::TwoD (..) => Some(i), + PointType::ThreeD (..) => None, + } + }) + .collect() + } + + pub fn test2(&mut self, i: usize) -> PointType { + self.points[i] + } +} + +fn main() { + let mut points = Points { + points: Vec::<PointType>::new() + }; + + points.points.push(PointType::ThreeD(0,0,0)); + points.points.push(PointType::TwoD(0,0)); + points.points.push(PointType::ThreeD(0,0,1)); + points.points.push(PointType::TwoD(0,1)); + + println!("{:?}", points.test1()); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs new file mode 100644 index 000000000..3f7ddf93f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs @@ -0,0 +1,47 @@ +// edition:2021 +// run-pass + +// Test that we can use raw ptrs when using `capture_disjoint_fields`. + +#![allow(dead_code)] + +#[derive(Debug)] +struct S { + s: String, + t: String, +} + +struct T(*const S); + +fn unsafe_imm() { + let s = "".into(); + let t = "".into(); + let my_speed: Box<S> = Box::new(S { s, t }); + + let p : *const S = Box::into_raw(my_speed); + let t = T(p); + + let c = || unsafe { + println!("{:?}", (*t.0).s); + }; + + c(); +} + +fn unsafe_mut() { + let s = "".into(); + let t = "".into(); + let mut my_speed: Box<S> = Box::new(S { s, t }); + let p : *mut S = &mut *my_speed; + + let c = || { + let x = unsafe { &mut (*p).s }; + *x = "s".into(); + }; + c(); +} + +fn main() { + unsafe_mut(); + unsafe_imm(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs new file mode 100644 index 000000000..0206927cc --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs @@ -0,0 +1,11 @@ +// edition:2021 +//check-pass +#![feature(rustc_attrs)] + +fn main() { + let mut x = 0; + let c = || { + &mut x; // mutable borrow of `x` + match x { _ => () } // fake read of `x` + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs new file mode 100644 index 000000000..563095d44 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -0,0 +1,39 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +// Test to ensure that min analysis meets capture kind for all paths captured. + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 20 }; + + // + // Requirements: + // p.x -> MutBoorrow + // p -> ImmBorrow + // + // Requirements met when p is captured via MutBorrow + // + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + p.x += 10; + //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow + //~| NOTE: p[] captured as MutBorrow here + println!("{:?}", p); + //~^ NOTE: Capturing p[] -> ImmBorrow + //~| NOTE: Min Capture p[] -> MutBorrow + //~| NOTE: p[] used here + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr new file mode 100644 index 000000000..05d79797a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr @@ -0,0 +1,56 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/simple-struct-min-capture.rs:23:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/simple-struct-min-capture.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | p.x += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:29:9 + | +LL | p.x += 10; + | ^^^ +note: Capturing p[] -> ImmBorrow + --> $DIR/simple-struct-min-capture.rs:32:26 + | +LL | println!("{:?}", p); + | ^ + +error: Min Capture analysis includes: + --> $DIR/simple-struct-min-capture.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | p.x += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:29:9 + | +LL | p.x += 10; + | ^^^ p[] captured as MutBorrow here +... +LL | println!("{:?}", p); + | ^ p[] used here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs new file mode 100644 index 000000000..eab9f9d08 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs @@ -0,0 +1,62 @@ +// edition:2021 + +// Test that we restrict precision of a capture when we access a raw ptr, +// i.e. the capture doesn't deref the raw ptr. + + +#![feature(rustc_attrs)] + +#[derive(Debug)] +struct S { + s: String, + t: String, +} + +struct T(*const S); + +fn unsafe_imm() { + let s = "".into(); + let t = "".into(); + let my_speed: Box<S> = Box::new(S { s, t }); + + let p : *const S = Box::into_raw(my_speed); + let t = T(p); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || unsafe { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{:?}", (*t.0).s); + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow + }; + + c(); +} + +fn unsafe_mut() { + let s = "".into(); + let t = "".into(); + let mut my_speed: Box<S> = Box::new(S { s, t }); + let p : *mut S = &mut *my_speed; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let x = unsafe { &mut (*p).s }; + //~^ NOTE: Capturing p[Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[] -> ImmBorrow + *x = "s".into(); + }; + c(); +} + +fn main() { + unsafe_mut(); + unsafe_imm(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr new file mode 100644 index 000000000..e740a4d2d --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr @@ -0,0 +1,93 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/unsafe_ptr.rs:25:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/unsafe_ptr.rs:45:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/unsafe_ptr.rs:28:6 + | +LL | / || unsafe { +LL | | +LL | | +LL | | println!("{:?}", (*t.0).s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/unsafe_ptr.rs:31:26 + | +LL | println!("{:?}", (*t.0).s); + | ^^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/unsafe_ptr.rs:28:6 + | +LL | / || unsafe { +LL | | +LL | | +LL | | println!("{:?}", (*t.0).s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ImmBorrow + --> $DIR/unsafe_ptr.rs:31:26 + | +LL | println!("{:?}", (*t.0).s); + | ^^^^^^^^ + +error: First Pass analysis includes: + --> $DIR/unsafe_ptr.rs:48:5 + | +LL | / || { +LL | | +LL | | +LL | | let x = unsafe { &mut (*p).s }; +... | +LL | | *x = "s".into(); +LL | | }; + | |_____^ + | +note: Capturing p[Deref,(0, 0)] -> ImmBorrow + --> $DIR/unsafe_ptr.rs:51:31 + | +LL | let x = unsafe { &mut (*p).s }; + | ^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/unsafe_ptr.rs:48:5 + | +LL | / || { +LL | | +LL | | +LL | | let x = unsafe { &mut (*p).s }; +... | +LL | | *x = "s".into(); +LL | | }; + | |_____^ + | +note: Min Capture p[] -> ImmBorrow + --> $DIR/unsafe_ptr.rs:51:31 + | +LL | let x = unsafe { &mut (*p).s }; + | ^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs new file mode 100644 index 000000000..a795088a1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs @@ -0,0 +1,73 @@ +// edition:2021 + +#![feature(rustc_attrs)] + +// Test to ensure that we can handle cases where +// let statements create no bindings are initialized +// using a Place expression +// +// Note: Currently when feature `capture_disjoint_fields` is enabled +// we can't handle such cases. So the test current use `_x` instead of +// `_` until the issue is resolved. +// Check rust-lang/project-rfc-2229#24 for status. + +struct Point { + x: i32, + y: i32, +} + +fn wild_struct() { + let p = Point { x: 10, y: 20 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + // FIXME(arora-aman): Change `_x` to `_` + let Point { x: _x, y: _ } = p; + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow + }; + + c(); +} + +fn wild_tuple() { + let t = (String::new(), 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + // FIXME(arora-aman): Change `_x` to `_` + let (_x, _) = t; + //~^ NOTE: Capturing t[(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> ByValue + }; + + c(); +} + +fn wild_arr() { + let arr = [String::new(), String::new()]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + // FIXME(arora-aman): Change `_x` to `_` + let [_x, _] = arr; + //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Min Capture arr[] -> ByValue + }; + + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr new file mode 100644 index 000000000..c64378091 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr @@ -0,0 +1,138 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:22:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:40:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:58:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:25:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:29:37 + | +LL | let Point { x: _x, y: _ } = p; + | ^ + +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:25:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:29:37 + | +LL | let Point { x: _x, y: _ } = p; + | ^ + +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:43:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:47:23 + | +LL | let (_x, _) = t; + | ^ + +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:43:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:47:23 + | +LL | let (_x, _) = t; + | ^ + +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing arr[Index] -> ByValue + --> $DIR/wild_patterns.rs:65:23 + | +LL | let [_x, _] = arr; + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture arr[] -> ByValue + --> $DIR/wild_patterns.rs:65:23 + | +LL | let [_x, _] = arr; + | ^^^ + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/add_semicolon_non_block_closure.rs b/src/test/ui/closures/add_semicolon_non_block_closure.rs new file mode 100644 index 000000000..3ae91be60 --- /dev/null +++ b/src/test/ui/closures/add_semicolon_non_block_closure.rs @@ -0,0 +1,11 @@ +fn foo(_f: impl Fn()) {} + +fn bar() -> i32 { + 1 +} + +fn main() { + foo(|| bar()) + //~^ ERROR mismatched types [E0308] + //~| HELP consider using a semicolon here +} diff --git a/src/test/ui/closures/add_semicolon_non_block_closure.stderr b/src/test/ui/closures/add_semicolon_non_block_closure.stderr new file mode 100644 index 000000000..ed829fc98 --- /dev/null +++ b/src/test/ui/closures/add_semicolon_non_block_closure.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/add_semicolon_non_block_closure.rs:8:12 + | +LL | fn main() { + | - expected `()` because of default return type +LL | foo(|| bar()) + | ^^^^^ expected `()`, found `i32` + | +help: consider using a semicolon here + | +LL | foo(|| { bar(); }) + | + +++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/binder/async-closure-with-binder.rs b/src/test/ui/closures/binder/async-closure-with-binder.rs new file mode 100644 index 000000000..4fa599d37 --- /dev/null +++ b/src/test/ui/closures/binder/async-closure-with-binder.rs @@ -0,0 +1,8 @@ +// edition:2021 +#![feature(closure_lifetime_binder)] +#![feature(async_closure)] +fn main() { + for<'a> async || (); + //~^ ERROR `for<...>` binders on `async` closures are not currently supported + //~^^ ERROR implicit types in closure signatures are forbidden when `for<...>` is present +} diff --git a/src/test/ui/closures/binder/async-closure-with-binder.stderr b/src/test/ui/closures/binder/async-closure-with-binder.stderr new file mode 100644 index 000000000..1d4628b1a --- /dev/null +++ b/src/test/ui/closures/binder/async-closure-with-binder.stderr @@ -0,0 +1,16 @@ +error: `for<...>` binders on `async` closures are not currently supported + --> $DIR/async-closure-with-binder.rs:5:5 + | +LL | for<'a> async || (); + | ^^^^^^^ + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/async-closure-with-binder.rs:5:5 + | +LL | for<'a> async || (); + | -------^^^^^^^^^ + | | + | `for<...>` is here + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/closures/binder/implicit-return.rs b/src/test/ui/closures/binder/implicit-return.rs new file mode 100644 index 000000000..d34e5721d --- /dev/null +++ b/src/test/ui/closures/binder/implicit-return.rs @@ -0,0 +1,6 @@ +#![feature(closure_lifetime_binder)] + +fn main() { + let _f = for<'a> |_: &'a ()| {}; + //~^ implicit types in closure signatures are forbidden when `for<...>` is present +} diff --git a/src/test/ui/closures/binder/implicit-return.stderr b/src/test/ui/closures/binder/implicit-return.stderr new file mode 100644 index 000000000..5bfb97113 --- /dev/null +++ b/src/test/ui/closures/binder/implicit-return.stderr @@ -0,0 +1,10 @@ +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-return.rs:4:34 + | +LL | let _f = for<'a> |_: &'a ()| {}; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to previous error + diff --git a/src/test/ui/closures/binder/implicit-stuff.rs b/src/test/ui/closures/binder/implicit-stuff.rs new file mode 100644 index 000000000..09e4c747a --- /dev/null +++ b/src/test/ui/closures/binder/implicit-stuff.rs @@ -0,0 +1,27 @@ +#![feature(closure_lifetime_binder)] + +fn main() { + // Implicit types + let _ = for<> || {}; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> || -> &'a _ { &() }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x| -> &'a () { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a _| -> &'a () { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a Vec::<_>| -> &'a Vec::<()> { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a Vec<()>| -> &'a Vec<_> { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a _| -> &'a &'a () { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _ = for<'a> |x: &'a _, y, z: _| -> &'a _ { //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present + let _: &u8 = x; + let _: u32 = y; + let _: i32 = z; + x + }; + + // Lifetime elision + let _ = for<> |_: &()| -> () {}; //~ ERROR `&` without an explicit lifetime name cannot be used here + let _ = for<> |x: &()| -> &() { x }; //~ ERROR `&` without an explicit lifetime name cannot be used here + //~| ERROR `&` without an explicit lifetime name cannot be used here + let _ = for<> |x: &'_ ()| -> &'_ () { x }; //~ ERROR `'_` cannot be used here + //~| ERROR `'_` cannot be used here + let _ = for<'a> |x: &()| -> &'a () { x }; //~ ERROR `&` without an explicit lifetime name cannot be used here + let _ = for<'a> |x: &'a ()| -> &() { x }; //~ ERROR `&` without an explicit lifetime name cannot be used here +} diff --git a/src/test/ui/closures/binder/implicit-stuff.stderr b/src/test/ui/closures/binder/implicit-stuff.stderr new file mode 100644 index 000000000..779a08a44 --- /dev/null +++ b/src/test/ui/closures/binder/implicit-stuff.stderr @@ -0,0 +1,107 @@ +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:20:23 + | +LL | let _ = for<> |_: &()| -> () {}; + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:21:23 + | +LL | let _ = for<> |x: &()| -> &() { x }; + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:21:31 + | +LL | let _ = for<> |x: &()| -> &() { x }; + | ^ explicit lifetime name needed here + +error[E0637]: `'_` cannot be used here + --> $DIR/implicit-stuff.rs:23:24 + | +LL | let _ = for<> |x: &'_ ()| -> &'_ () { x }; + | ^^ `'_` is a reserved lifetime name + +error[E0637]: `'_` cannot be used here + --> $DIR/implicit-stuff.rs:23:35 + | +LL | let _ = for<> |x: &'_ ()| -> &'_ () { x }; + | ^^ `'_` is a reserved lifetime name + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:25:25 + | +LL | let _ = for<'a> |x: &()| -> &'a () { x }; + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/implicit-stuff.rs:26:36 + | +LL | let _ = for<'a> |x: &'a ()| -> &() { x }; + | ^ explicit lifetime name needed here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:5:22 + | +LL | let _ = for<> || {}; + | ----- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:6:31 + | +LL | let _ = for<'a> || -> &'a _ { &() }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:7:22 + | +LL | let _ = for<'a> |x| -> &'a () { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:8:29 + | +LL | let _ = for<'a> |x: &'a _| -> &'a () { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:9:35 + | +LL | let _ = for<'a> |x: &'a Vec::<_>| -> &'a Vec::<()> { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:10:49 + | +LL | let _ = for<'a> |x: &'a Vec<()>| -> &'a Vec<_> { x }; + | ------- `for<...>` is here ^ + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:11:29 + | +LL | let _ = for<'a> |x: &'a _| -> &'a &'a () { x }; + | ------- ^ + | | + | `for<...>` is here + +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/implicit-stuff.rs:12:29 + | +LL | let _ = for<'a> |x: &'a _, y, z: _| -> &'a _ { + | ------- ^ ^ ^ ^ + | | + | `for<...>` is here + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0637`. diff --git a/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs new file mode 100644 index 000000000..b476dd50c --- /dev/null +++ b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs @@ -0,0 +1,7 @@ +#![feature(closure_lifetime_binder)] +fn main() { + for<> |_: &'a ()| -> () {}; + //~^ ERROR use of undeclared lifetime name `'a` + for<'a> |_: &'b ()| -> () {}; + //~^ ERROR use of undeclared lifetime name `'b` +} diff --git a/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr new file mode 100644 index 000000000..1381acc15 --- /dev/null +++ b/src/test/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr @@ -0,0 +1,33 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/suggestion-for-introducing-lifetime-into-binder.rs:3:16 + | +LL | for<> |_: &'a ()| -> () {}; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | for<'a, > |_: &'a ()| -> () {}; + | +++ +help: consider introducing lifetime `'a` here + | +LL | fn main<'a>() { + | ++++ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/suggestion-for-introducing-lifetime-into-binder.rs:5:18 + | +LL | for<'a> |_: &'b ()| -> () {}; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'b` here + | +LL | for<'b, 'a> |_: &'b ()| -> () {}; + | +++ +help: consider introducing lifetime `'b` here + | +LL | fn main<'b>() { + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/closures/closure-array-break-length.rs b/src/test/ui/closures/closure-array-break-length.rs new file mode 100644 index 000000000..fda590fda --- /dev/null +++ b/src/test/ui/closures/closure-array-break-length.rs @@ -0,0 +1,7 @@ +fn main() { + |_: [_; continue]| {}; //~ ERROR: `continue` outside of a loop + + while |_: [_; continue]| {} {} //~ ERROR: `continue` outside of a loop + + while |_: [_; break]| {} {} //~ ERROR: `break` outside of a loop +} diff --git a/src/test/ui/closures/closure-array-break-length.stderr b/src/test/ui/closures/closure-array-break-length.stderr new file mode 100644 index 000000000..2b8ab9bfc --- /dev/null +++ b/src/test/ui/closures/closure-array-break-length.stderr @@ -0,0 +1,21 @@ +error[E0268]: `continue` outside of a loop + --> $DIR/closure-array-break-length.rs:2:13 + | +LL | |_: [_; continue]| {}; + | ^^^^^^^^ cannot `continue` outside of a loop + +error[E0268]: `continue` outside of a loop + --> $DIR/closure-array-break-length.rs:4:19 + | +LL | while |_: [_; continue]| {} {} + | ^^^^^^^^ cannot `continue` outside of a loop + +error[E0268]: `break` outside of a loop + --> $DIR/closure-array-break-length.rs:6:19 + | +LL | while |_: [_; break]| {} {} + | ^^^^^ cannot `break` outside of a loop + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0268`. diff --git a/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.rs b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.rs new file mode 100644 index 000000000..039cf3e04 --- /dev/null +++ b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.rs @@ -0,0 +1,11 @@ +struct X<F> where F: FnOnce() + 'static + Send { + field: F, +} + +fn foo<F>(blk: F) -> X<F> where F: FnOnce() + 'static { + //~^ ERROR `F` cannot be sent between threads safely + return X { field: blk }; +} + +fn main() { +} diff --git a/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr new file mode 100644 index 000000000..bf6ec5c36 --- /dev/null +++ b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr @@ -0,0 +1,19 @@ +error[E0277]: `F` cannot be sent between threads safely + --> $DIR/closure-bounds-cant-promote-superkind-in-struct.rs:5:22 + | +LL | fn foo<F>(blk: F) -> X<F> where F: FnOnce() + 'static { + | ^^^^ `F` cannot be sent between threads safely + | +note: required by a bound in `X` + --> $DIR/closure-bounds-cant-promote-superkind-in-struct.rs:1:43 + | +LL | struct X<F> where F: FnOnce() + 'static + Send { + | ^^^^ required by this bound in `X` +help: consider further restricting this bound + | +LL | fn foo<F>(blk: F) -> X<F> where F: FnOnce() + 'static + std::marker::Send { + | +++++++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.rs b/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.rs new file mode 100644 index 000000000..7327d8256 --- /dev/null +++ b/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.rs @@ -0,0 +1,13 @@ +fn bar<F>(blk: F) where F: FnOnce() + 'static { +} + +fn foo(x: &()) { + bar(|| { + //~^ ERROR borrowed data escapes + //~| ERROR closure may outlive + let _ = x; + }) +} + +fn main() { +} diff --git a/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.stderr b/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.stderr new file mode 100644 index 000000000..85df5c1e5 --- /dev/null +++ b/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.stderr @@ -0,0 +1,44 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/closure-bounds-static-cant-capture-borrowed.rs:5:5 + | +LL | fn foo(x: &()) { + | - - let's call the lifetime of this reference `'1` + | | + | `x` is a reference that is only valid in the function body +LL | / bar(|| { +LL | | +LL | | +LL | | let _ = x; +LL | | }) + | | ^ + | | | + | |______`x` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/closure-bounds-static-cant-capture-borrowed.rs:5:9 + | +LL | bar(|| { + | ^^ may outlive borrowed value `x` +... +LL | let _ = x; + | - `x` is borrowed here + | +note: function requires argument type to outlive `'static` + --> $DIR/closure-bounds-static-cant-capture-borrowed.rs:5:5 + | +LL | / bar(|| { +LL | | +LL | | +LL | | let _ = x; +LL | | }) + | |______^ +help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | bar(move || { + | ++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0373, E0521. +For more information about an error, try `rustc --explain E0373`. diff --git a/src/test/ui/closures/closure-bounds-subtype.rs b/src/test/ui/closures/closure-bounds-subtype.rs new file mode 100644 index 000000000..4888cbfcc --- /dev/null +++ b/src/test/ui/closures/closure-bounds-subtype.rs @@ -0,0 +1,16 @@ +fn take_any<F>(_: F) where F: FnOnce() { +} + +fn take_const_owned<F>(_: F) where F: FnOnce() + Sync + Send { +} + +fn give_any<F>(f: F) where F: FnOnce() { + take_any(f); +} + +fn give_owned<F>(f: F) where F: FnOnce() + Send { + take_any(f); + take_const_owned(f); //~ ERROR `F` cannot be shared between threads safely [E0277] +} + +fn main() {} diff --git a/src/test/ui/closures/closure-bounds-subtype.stderr b/src/test/ui/closures/closure-bounds-subtype.stderr new file mode 100644 index 000000000..1a40326d9 --- /dev/null +++ b/src/test/ui/closures/closure-bounds-subtype.stderr @@ -0,0 +1,21 @@ +error[E0277]: `F` cannot be shared between threads safely + --> $DIR/closure-bounds-subtype.rs:13:22 + | +LL | take_const_owned(f); + | ---------------- ^ `F` cannot be shared between threads safely + | | + | required by a bound introduced by this call + | +note: required by a bound in `take_const_owned` + --> $DIR/closure-bounds-subtype.rs:4:50 + | +LL | fn take_const_owned<F>(_: F) where F: FnOnce() + Sync + Send { + | ^^^^ required by this bound in `take_const_owned` +help: consider further restricting this bound + | +LL | fn give_owned<F>(f: F) where F: FnOnce() + Send + std::marker::Sync { + | +++++++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr new file mode 100644 index 000000000..8846ccef3 --- /dev/null +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr @@ -0,0 +1,37 @@ +error: lifetime may not live long enough + --> $DIR/expect-region-supply-region-2.rs:14:30 + | +LL | fn expect_bound_supply_named<'x>() { + | -- lifetime `'x` defined here +... +LL | closure_expecting_bound(|x: &'x u32| { + | ^ - let's call the lifetime of this reference `'1` + | | + | requires that `'1` must outlive `'x` + +error[E0521]: borrowed data escapes outside of closure + --> $DIR/expect-region-supply-region-2.rs:20:9 + | +LL | let mut f: Option<&u32> = None; + | ----- `f` declared here, outside of the closure body +... +LL | closure_expecting_bound(|x: &'x u32| { + | - `x` is a reference that is only valid in the closure body +... +LL | f = Some(x); + | ^^^^^^^^^^^ `x` escapes the closure body here + +error: lifetime may not live long enough + --> $DIR/expect-region-supply-region-2.rs:14:30 + | +LL | fn expect_bound_supply_named<'x>() { + | -- lifetime `'x` defined here +... +LL | closure_expecting_bound(|x: &'x u32| { + | ^ requires that `'x` must outlive `'static` + | + = help: consider replacing `'x` with `'static` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0521`. diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.rs b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.rs new file mode 100644 index 000000000..9b51bbd58 --- /dev/null +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.rs @@ -0,0 +1,24 @@ +#![allow(warnings)] + +fn closure_expecting_bound<F>(_: F) +where + F: FnOnce(&u32), +{ +} + +fn expect_bound_supply_named<'x>() { + let mut f: Option<&u32> = None; + + // Here we give a type annotation that `x` should be free. We get + // an error because of that. + closure_expecting_bound(|x: &'x u32| { + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough + + // Borrowck doesn't get a chance to run, but if it did it should error + // here. + f = Some(x); + }); +} + +fn main() {} diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr new file mode 100644 index 000000000..9aab51c98 --- /dev/null +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr @@ -0,0 +1,22 @@ +error: lifetime may not live long enough + --> $DIR/expect-region-supply-region-2.rs:14:30 + | +LL | fn expect_bound_supply_named<'x>() { + | -- lifetime `'x` defined here +... +LL | closure_expecting_bound(|x: &'x u32| { + | ^ - let's call the lifetime of this reference `'1` + | | + | requires that `'1` must outlive `'x` + +error: lifetime may not live long enough + --> $DIR/expect-region-supply-region-2.rs:14:30 + | +LL | fn expect_bound_supply_named<'x>() { + | -- lifetime `'x` defined here +... +LL | closure_expecting_bound(|x: &'x u32| { + | ^ requires that `'x` must outlive `'static` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region.rs b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.rs new file mode 100644 index 000000000..55c6aa795 --- /dev/null +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.rs @@ -0,0 +1,57 @@ +#![allow(warnings)] + +fn closure_expecting_bound<F>(_: F) +where + F: FnOnce(&u32), +{ +} + +fn closure_expecting_free<'a, F>(_: F) +where + F: FnOnce(&'a u32), +{ +} + +fn expect_bound_supply_nothing() { + // Because `x` is inferred to have a bound region, we cannot allow + // it to escape into `f`: + let mut f: Option<&u32> = None; + closure_expecting_bound(|x| { + f = Some(x); //~ ERROR borrowed data escapes outside of closure + }); +} + +fn expect_bound_supply_bound() { + // Because `x` is inferred to have a bound region, we cannot allow + // it to escape into `f`, even with an explicit type annotation on + // closure: + let mut f: Option<&u32> = None; + closure_expecting_bound(|x: &u32| { + f = Some(x); //~ ERROR borrowed data escapes outside of closure + }); +} + +fn expect_free_supply_nothing() { + let mut f: Option<&u32> = None; + closure_expecting_free(|x| f = Some(x)); // OK +} + +fn expect_free_supply_bound() { + let mut f: Option<&u32> = None; + + // Here, even though the annotation `&u32` could be seen as being + // bound in the closure, we permit it to be defined as a free + // region (which is inferred to something in the fn body). + closure_expecting_free(|x: &u32| f = Some(x)); // OK +} + +fn expect_free_supply_named<'x>() { + let mut f: Option<&u32> = None; + + // Here, even though the annotation `&u32` could be seen as being + // bound in the closure, we permit it to be defined as a free + // region (which is inferred to something in the fn body). + closure_expecting_free(|x: &'x u32| f = Some(x)); // OK +} + +fn main() {} diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region.stderr b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.stderr new file mode 100644 index 000000000..0d97fa7e2 --- /dev/null +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.stderr @@ -0,0 +1,23 @@ +error[E0521]: borrowed data escapes outside of closure + --> $DIR/expect-region-supply-region.rs:20:9 + | +LL | let mut f: Option<&u32> = None; + | ----- `f` declared here, outside of the closure body +LL | closure_expecting_bound(|x| { + | - `x` is a reference that is only valid in the closure body +LL | f = Some(x); + | ^^^^^^^^^^^ `x` escapes the closure body here + +error[E0521]: borrowed data escapes outside of closure + --> $DIR/expect-region-supply-region.rs:30:9 + | +LL | let mut f: Option<&u32> = None; + | ----- `f` declared here, outside of the closure body +LL | closure_expecting_bound(|x: &u32| { + | - `x` is a reference that is only valid in the closure body +LL | f = Some(x); + | ^^^^^^^^^^^ `x` escapes the closure body here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0521`. diff --git a/src/test/ui/closures/closure-expected.rs b/src/test/ui/closures/closure-expected.rs new file mode 100644 index 000000000..68cac3dd8 --- /dev/null +++ b/src/test/ui/closures/closure-expected.rs @@ -0,0 +1,5 @@ +fn main() { + let x = Some(1); + let y = x.or_else(4); + //~^ ERROR expected a `FnOnce<()>` closure, found `{integer}` +} diff --git a/src/test/ui/closures/closure-expected.stderr b/src/test/ui/closures/closure-expected.stderr new file mode 100644 index 000000000..7ffe3c1ef --- /dev/null +++ b/src/test/ui/closures/closure-expected.stderr @@ -0,0 +1,19 @@ +error[E0277]: expected a `FnOnce<()>` closure, found `{integer}` + --> $DIR/closure-expected.rs:3:23 + | +LL | let y = x.or_else(4); + | ------- ^ expected an `FnOnce<()>` closure, found `{integer}` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce<()>` is not implemented for `{integer}` + = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }` +note: required by a bound in `Option::<T>::or_else` + --> $SRC_DIR/core/src/option.rs:LL:COL + | +LL | F: ~const FnOnce() -> Option<T>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::or_else` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/closures/closure-immutable-outer-variable.fixed b/src/test/ui/closures/closure-immutable-outer-variable.fixed new file mode 100644 index 000000000..1b0feede3 --- /dev/null +++ b/src/test/ui/closures/closure-immutable-outer-variable.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +// Point at the captured immutable outer variable + +fn foo(mut f: Box<dyn FnMut()>) { + f(); +} + +fn main() { + let mut y = true; + foo(Box::new(move || y = !y) as Box<_>); + //~^ ERROR cannot assign to `y`, as it is not declared as mutable +} diff --git a/src/test/ui/closures/closure-immutable-outer-variable.rs b/src/test/ui/closures/closure-immutable-outer-variable.rs new file mode 100644 index 000000000..50ec1c614 --- /dev/null +++ b/src/test/ui/closures/closure-immutable-outer-variable.rs @@ -0,0 +1,13 @@ +// run-rustfix + +// Point at the captured immutable outer variable + +fn foo(mut f: Box<dyn FnMut()>) { + f(); +} + +fn main() { + let y = true; + foo(Box::new(move || y = !y) as Box<_>); + //~^ ERROR cannot assign to `y`, as it is not declared as mutable +} diff --git a/src/test/ui/closures/closure-immutable-outer-variable.rs.fixed b/src/test/ui/closures/closure-immutable-outer-variable.rs.fixed new file mode 100644 index 000000000..5c6358beb --- /dev/null +++ b/src/test/ui/closures/closure-immutable-outer-variable.rs.fixed @@ -0,0 +1,10 @@ +// Point at the captured immutable outer variable + +fn foo(mut f: Box<FnMut()>) { + f(); +} + +fn main() { + let mut y = true; + foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable +} diff --git a/src/test/ui/closures/closure-immutable-outer-variable.stderr b/src/test/ui/closures/closure-immutable-outer-variable.stderr new file mode 100644 index 000000000..799097889 --- /dev/null +++ b/src/test/ui/closures/closure-immutable-outer-variable.stderr @@ -0,0 +1,11 @@ +error[E0594]: cannot assign to `y`, as it is not declared as mutable + --> $DIR/closure-immutable-outer-variable.rs:11:26 + | +LL | let y = true; + | - help: consider changing this to be mutable: `mut y` +LL | foo(Box::new(move || y = !y) as Box<_>); + | ^^^^^^ cannot assign + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/closures/closure-move-sync.rs b/src/test/ui/closures/closure-move-sync.rs new file mode 100644 index 000000000..ea2d1434c --- /dev/null +++ b/src/test/ui/closures/closure-move-sync.rs @@ -0,0 +1,22 @@ +use std::thread; +use std::sync::mpsc::channel; + +fn bar() { + let (send, recv) = channel(); + let t = thread::spawn(|| { + recv.recv().unwrap(); + //~^^ ERROR `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely + }); + + send.send(()); + + t.join().unwrap(); +} + +fn foo() { + let (tx, _rx) = channel(); + thread::spawn(|| tx.send(()).unwrap()); + //~^ ERROR `Sender<()>` cannot be shared between threads safely +} + +fn main() {} diff --git a/src/test/ui/closures/closure-move-sync.stderr b/src/test/ui/closures/closure-move-sync.stderr new file mode 100644 index 000000000..1086cfa29 --- /dev/null +++ b/src/test/ui/closures/closure-move-sync.stderr @@ -0,0 +1,41 @@ +error[E0277]: `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely + --> $DIR/closure-move-sync.rs:6:13 + | +LL | let t = thread::spawn(|| { + | ^^^^^^^^^^^^^ `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `std::sync::mpsc::Receiver<()>` + = note: required because of the requirements on the impl of `Send` for `&std::sync::mpsc::Receiver<()>` +note: required because it's used within this closure + --> $DIR/closure-move-sync.rs:6:27 + | +LL | let t = thread::spawn(|| { + | ^^ +note: required by a bound in `spawn` + --> $SRC_DIR/std/src/thread/mod.rs:LL:COL + | +LL | F: Send + 'static, + | ^^^^ required by this bound in `spawn` + +error[E0277]: `Sender<()>` cannot be shared between threads safely + --> $DIR/closure-move-sync.rs:18:5 + | +LL | thread::spawn(|| tx.send(()).unwrap()); + | ^^^^^^^^^^^^^ `Sender<()>` cannot be shared between threads safely + | + = help: the trait `Sync` is not implemented for `Sender<()>` + = note: required because of the requirements on the impl of `Send` for `&Sender<()>` +note: required because it's used within this closure + --> $DIR/closure-move-sync.rs:18:19 + | +LL | thread::spawn(|| tx.send(()).unwrap()); + | ^^ +note: required by a bound in `spawn` + --> $SRC_DIR/std/src/thread/mod.rs:LL:COL + | +LL | F: Send + 'static, + | ^^^^ required by this bound in `spawn` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/closures/closure-no-fn-1.rs b/src/test/ui/closures/closure-no-fn-1.rs new file mode 100644 index 000000000..48c3e4725 --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-1.rs @@ -0,0 +1,8 @@ +// Ensure that capturing closures are never coerced to fns +// Especially interesting as non-capturing closures can be. + +fn main() { + let mut a = 0u8; + let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; + //~^ ERROR mismatched types +} diff --git a/src/test/ui/closures/closure-no-fn-1.stderr b/src/test/ui/closures/closure-no-fn-1.stderr new file mode 100644 index 000000000..eab7482e6 --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-1.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/closure-no-fn-1.rs:6:29 + | +LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; + | ------------ ^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure + | | + | expected due to this + | + = note: expected fn pointer `fn(u8) -> u8` + found closure `[closure@$DIR/closure-no-fn-1.rs:6:29: 6:36]` +note: closures can only be coerced to `fn` types if they do not capture any variables + --> $DIR/closure-no-fn-1.rs:6:39 + | +LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; + | ^ `a` captured here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure-no-fn-2.rs b/src/test/ui/closures/closure-no-fn-2.rs new file mode 100644 index 000000000..f3066f7a3 --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-2.rs @@ -0,0 +1,8 @@ +// Ensure that capturing closures are never coerced to fns +// Especially interesting as non-capturing closures can be. + +fn main() { + let b = 0u8; + let bar: fn() -> u8 = || { b }; + //~^ ERROR mismatched types +} diff --git a/src/test/ui/closures/closure-no-fn-2.stderr b/src/test/ui/closures/closure-no-fn-2.stderr new file mode 100644 index 000000000..e1f0143ab --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-2.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/closure-no-fn-2.rs:6:27 + | +LL | let bar: fn() -> u8 = || { b }; + | ---------- ^^^^^^^^ expected fn pointer, found closure + | | + | expected due to this + | + = note: expected fn pointer `fn() -> u8` + found closure `[closure@$DIR/closure-no-fn-2.rs:6:27: 6:29]` +note: closures can only be coerced to `fn` types if they do not capture any variables + --> $DIR/closure-no-fn-2.rs:6:32 + | +LL | let bar: fn() -> u8 = || { b }; + | ^ `b` captured here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure-no-fn-3.rs b/src/test/ui/closures/closure-no-fn-3.rs new file mode 100644 index 000000000..53217c2f1 --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-3.rs @@ -0,0 +1,8 @@ +// Ensure that capturing closures are never coerced to fns +// Especially interesting as non-capturing closures can be. + +fn main() { + let b = 0u8; + let baz: fn() -> u8 = (|| { b }) as fn() -> u8; + //~^ ERROR non-primitive cast +} diff --git a/src/test/ui/closures/closure-no-fn-3.stderr b/src/test/ui/closures/closure-no-fn-3.stderr new file mode 100644 index 000000000..6009389b1 --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-3.stderr @@ -0,0 +1,9 @@ +error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:28: 6:30]` as `fn() -> u8` + --> $DIR/closure-no-fn-3.rs:6:27 + | +LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0605`. diff --git a/src/test/ui/closures/closure-no-fn-4.rs b/src/test/ui/closures/closure-no-fn-4.rs new file mode 100644 index 000000000..275bff645 --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-4.rs @@ -0,0 +1,8 @@ +fn main() { + let b = 2; + let _: fn(usize) -> usize = match true { + true => |a| a + 1, + false => |a| a - b, + //~^ ERROR `match` arms have incompatible types + }; +} diff --git a/src/test/ui/closures/closure-no-fn-4.stderr b/src/test/ui/closures/closure-no-fn-4.stderr new file mode 100644 index 000000000..d1b704884 --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-4.stderr @@ -0,0 +1,24 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/closure-no-fn-4.rs:5:18 + | +LL | let _: fn(usize) -> usize = match true { + | _________________________________- +LL | | true => |a| a + 1, + | | --------- this is found to be of type `fn(usize) -> usize` +LL | | false => |a| a - b, + | | ^^^^^^^^^ expected fn pointer, found closure +LL | | +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected fn pointer `fn(usize) -> usize` + found closure `[closure@$DIR/closure-no-fn-4.rs:5:18: 5:21]` +note: closures can only be coerced to `fn` types if they do not capture any variables + --> $DIR/closure-no-fn-4.rs:5:26 + | +LL | false => |a| a - b, + | ^ `b` captured here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure-no-fn-5.rs b/src/test/ui/closures/closure-no-fn-5.rs new file mode 100644 index 000000000..43e3e977e --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-5.rs @@ -0,0 +1,12 @@ +// When providing diagnostics about not being able to coerce a capturing-closure +// to fn type, we want to report only upto 4 captures. + +fn main() { + let a = 0u8; + let b = 0u8; + let c = 0u8; + let d = 0u8; + let e = 0u8; + let bar: fn() -> u8 = || { a; b; c; d; e }; + //~^ ERROR mismatched types +} diff --git a/src/test/ui/closures/closure-no-fn-5.stderr b/src/test/ui/closures/closure-no-fn-5.stderr new file mode 100644 index 000000000..a33b847ea --- /dev/null +++ b/src/test/ui/closures/closure-no-fn-5.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/closure-no-fn-5.rs:10:27 + | +LL | let bar: fn() -> u8 = || { a; b; c; d; e }; + | ---------- ^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure + | | + | expected due to this + | + = note: expected fn pointer `fn() -> u8` + found closure `[closure@$DIR/closure-no-fn-5.rs:10:27: 10:29]` +note: closures can only be coerced to `fn` types if they do not capture any variables + --> $DIR/closure-no-fn-5.rs:10:32 + | +LL | let bar: fn() -> u8 = || { a; b; c; d; e }; + | ^ ^ ^ ^ `d` captured here + | | | | + | | | `c` captured here + | | `b` captured here + | `a` captured here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure-referencing-itself-issue-25954.rs b/src/test/ui/closures/closure-referencing-itself-issue-25954.rs new file mode 100644 index 000000000..7dd0e5179 --- /dev/null +++ b/src/test/ui/closures/closure-referencing-itself-issue-25954.rs @@ -0,0 +1,18 @@ +// Regression test for #25954: detect and reject a closure type that +// references itself. + +use std::cell::{Cell, RefCell}; + +struct A<T: Fn()> { + x: RefCell<Option<T>>, + b: Cell<i32>, +} + +fn main() { + let mut p = A{x: RefCell::new(None), b: Cell::new(4i32)}; + + // This is an error about types of infinite size: + let q = || p.b.set(5i32); //~ ERROR mismatched types + + *(p.x.borrow_mut()) = Some(q); +} diff --git a/src/test/ui/closures/closure-referencing-itself-issue-25954.stderr b/src/test/ui/closures/closure-referencing-itself-issue-25954.stderr new file mode 100644 index 000000000..8ca43cd1c --- /dev/null +++ b/src/test/ui/closures/closure-referencing-itself-issue-25954.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/closure-referencing-itself-issue-25954.rs:15:13 + | +LL | let q = || p.b.set(5i32); + | ^^^^^^^^^^^^^^^^ cyclic type of infinite size + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure-reform-bad.rs b/src/test/ui/closures/closure-reform-bad.rs new file mode 100644 index 000000000..0ba48ab51 --- /dev/null +++ b/src/test/ui/closures/closure-reform-bad.rs @@ -0,0 +1,12 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +fn call_bare(f: fn(&str)) { + f("Hello "); +} + +fn main() { + let string = "world!"; + let f = |s: &str| println!("{}{}", s, string); + call_bare(f) //~ ERROR mismatched types +} diff --git a/src/test/ui/closures/closure-reform-bad.stderr b/src/test/ui/closures/closure-reform-bad.stderr new file mode 100644 index 000000000..9dfff8499 --- /dev/null +++ b/src/test/ui/closures/closure-reform-bad.stderr @@ -0,0 +1,26 @@ +error[E0308]: mismatched types + --> $DIR/closure-reform-bad.rs:11:15 + | +LL | let f = |s: &str| println!("{}{}", s, string); + | --------- the found closure +LL | call_bare(f) + | --------- ^ expected fn pointer, found closure + | | + | arguments to this function are incorrect + | + = note: expected fn pointer `for<'r> fn(&'r str)` + found closure `[closure@$DIR/closure-reform-bad.rs:10:13: 10:22]` +note: closures can only be coerced to `fn` types if they do not capture any variables + --> $DIR/closure-reform-bad.rs:10:43 + | +LL | let f = |s: &str| println!("{}{}", s, string); + | ^^^^^^ `string` captured here +note: function defined here + --> $DIR/closure-reform-bad.rs:4:4 + | +LL | fn call_bare(f: fn(&str)) { + | ^^^^^^^^^ ----------- + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure-return-type-mismatch.rs b/src/test/ui/closures/closure-return-type-mismatch.rs new file mode 100644 index 000000000..1631bb303 --- /dev/null +++ b/src/test/ui/closures/closure-return-type-mismatch.rs @@ -0,0 +1,17 @@ +fn main() { + || { + if false { + return "test"; + } + let a = true; + a //~ ERROR mismatched types + }; + + || -> bool { + if false { + return "hello" //~ ERROR mismatched types + }; + let b = true; + b + }; +} diff --git a/src/test/ui/closures/closure-return-type-mismatch.stderr b/src/test/ui/closures/closure-return-type-mismatch.stderr new file mode 100644 index 000000000..3a89d30a0 --- /dev/null +++ b/src/test/ui/closures/closure-return-type-mismatch.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/closure-return-type-mismatch.rs:7:9 + | +LL | a + | ^ expected `&str`, found `bool` + | +note: return type inferred to be `&str` here + --> $DIR/closure-return-type-mismatch.rs:4:20 + | +LL | return "test"; + | ^^^^^^ + +error[E0308]: mismatched types + --> $DIR/closure-return-type-mismatch.rs:12:20 + | +LL | return "hello" + | ^^^^^^^ expected `bool`, found `&str` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure-wrong-kind.rs b/src/test/ui/closures/closure-wrong-kind.rs new file mode 100644 index 000000000..9bf38bfb6 --- /dev/null +++ b/src/test/ui/closures/closure-wrong-kind.rs @@ -0,0 +1,12 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +struct X; +fn foo<T>(_: T) {} +fn bar<T: Fn(u32)>(_: T) {} + +fn main() { + let x = X; + let closure = |_| foo(x); //~ ERROR E0525 + bar(closure); +} diff --git a/src/test/ui/closures/closure-wrong-kind.stderr b/src/test/ui/closures/closure-wrong-kind.stderr new file mode 100644 index 000000000..35caf71a5 --- /dev/null +++ b/src/test/ui/closures/closure-wrong-kind.stderr @@ -0,0 +1,13 @@ +error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` + --> $DIR/closure-wrong-kind.rs:10:19 + | +LL | let closure = |_| foo(x); + | ^^^ - closure is `FnOnce` because it moves the variable `x` out of its environment + | | + | this closure implements `FnOnce`, not `Fn` +LL | bar(closure); + | --- the requirement to implement `Fn` derives from here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0525`. diff --git a/src/test/ui/closures/closure_cap_coerce_many_fail.rs b/src/test/ui/closures/closure_cap_coerce_many_fail.rs new file mode 100644 index 000000000..9133a2921 --- /dev/null +++ b/src/test/ui/closures/closure_cap_coerce_many_fail.rs @@ -0,0 +1,39 @@ +fn add(a: i32, b: i32) -> i32 { + a + b +} +fn main() { + // We shouldn't coerce capturing closure to a function + let cap = 0; + let _ = match "+" { + "+" => add, + "-" => |a, b| (a - b + cap) as i32, + _ => unimplemented!(), + }; + //~^^^ ERROR `match` arms have incompatible types + + + // We shouldn't coerce capturing closure to a non-capturing closure + let _ = match "+" { + "+" => |a, b| (a + b) as i32, + "-" => |a, b| (a - b + cap) as i32, + _ => unimplemented!(), + }; + //~^^^ ERROR `match` arms have incompatible types + + + // We shouldn't coerce non-capturing closure to a capturing closure + let _ = match "+" { + "+" => |a, b| (a + b + cap) as i32, + "-" => |a, b| (a - b) as i32, + _ => unimplemented!(), + }; + //~^^^ ERROR `match` arms have incompatible types + + // We shouldn't coerce capturing closure to a capturing closure + let _ = match "+" { + "+" => |a, b| (a + b + cap) as i32, + "-" => |a, b| (a - b + cap) as i32, + _ => unimplemented!(), + }; + //~^^^ ERROR `match` arms have incompatible types +} diff --git a/src/test/ui/closures/closure_cap_coerce_many_fail.stderr b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr new file mode 100644 index 000000000..ca8a43328 --- /dev/null +++ b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr @@ -0,0 +1,82 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/closure_cap_coerce_many_fail.rs:9:16 + | +LL | let _ = match "+" { + | _____________- +LL | | "+" => add, + | | --- this is found to be of type `fn(i32, i32) -> i32 {add}` +LL | | "-" => |a, b| (a - b + cap) as i32, + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found closure +LL | | _ => unimplemented!(), +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected fn item `fn(i32, i32) -> i32 {add}` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:9:16: 9:22]` + +error[E0308]: `match` arms have incompatible types + --> $DIR/closure_cap_coerce_many_fail.rs:18:16 + | +LL | let _ = match "+" { + | _____________- +LL | | "+" => |a, b| (a + b) as i32, + | | --------------------- + | | | + | | the expected closure + | | this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:22]` +LL | | "-" => |a, b| (a - b + cap) as i32, + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure +LL | | _ => unimplemented!(), +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:22]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:18:16: 18:22]` + = note: no two closures, even if identical, have the same type + = help: consider boxing your closure and/or using it as a trait object + +error[E0308]: `match` arms have incompatible types + --> $DIR/closure_cap_coerce_many_fail.rs:27:16 + | +LL | let _ = match "+" { + | _____________- +LL | | "+" => |a, b| (a + b + cap) as i32, + | | --------------------------- + | | | + | | the expected closure + | | this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:22]` +LL | | "-" => |a, b| (a - b) as i32, + | | ^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure +LL | | _ => unimplemented!(), +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:22]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:27:16: 27:22]` + = note: no two closures, even if identical, have the same type + = help: consider boxing your closure and/or using it as a trait object + +error[E0308]: `match` arms have incompatible types + --> $DIR/closure_cap_coerce_many_fail.rs:35:16 + | +LL | let _ = match "+" { + | _____________- +LL | | "+" => |a, b| (a + b + cap) as i32, + | | --------------------------- + | | | + | | the expected closure + | | this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:22]` +LL | | "-" => |a, b| (a - b + cap) as i32, + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure +LL | | _ => unimplemented!(), +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:22]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:35:16: 35:22]` + = note: no two closures, even if identical, have the same type + = help: consider boxing your closure and/or using it as a trait object + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs b/src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs new file mode 100644 index 000000000..ce461810e --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs @@ -0,0 +1,166 @@ +// check-pass +// Ensure non-capturing Closure passes CoerceMany. +fn foo(x: usize) -> usize { + 0 +} + +fn bar(x: usize) -> usize { + 1 +} + +fn main() { + // One FnDef and one non-capturing Closure + let _ = match 0 { + 0 => foo, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = match 0 { + 2 => |a| 2, + 0 => foo, + _ => unimplemented!(), + }; + + let _ = [foo, |a| 2]; + let _ = [|a| 2, foo]; + + + + // Two FnDefs and one non-capturing Closure + let _ = match 0 { + 0 => foo, + 1 => bar, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = match 0 { + 0 => foo, + 2 => |a| 2, + 1 => bar, + _ => unimplemented!(), + }; + + let _ = match 0 { + 2 => |a| 2, + 0 => foo, + 1 => bar, + _ => unimplemented!(), + }; + + let _ = [foo, bar, |a| 2]; + let _ = [foo, |a| 2, bar]; + let _ = [|a| 2, foo, bar]; + + + + // One FnDef and two non-capturing Closures + let _ = match 0 { + 0 => foo, + 1 => |a| 1, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = match 0 { + 1 => |a| 1, + 0 => foo, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = match 0 { + 1 => |a| 1, + 2 => |a| 2, + 0 => foo, + _ => unimplemented!(), + }; + + let _ = [foo, |a| 1, |a| 2]; + let _ = [|a| 1, foo, |a| 2]; + let _ = [|a| 1, |a| 2, foo]; + + + + // Three non-capturing Closures + let _ = match 0 { + 0 => |a: usize| 0, + 1 => |a| 1, + 2 => |a| 2, + _ => unimplemented!(), + }; + + let _ = [|a: usize| 0, |a| 1, |a| 2]; + + + + // Three non-capturing Closures variable + let clo0 = |a: usize| 0; + let clo1 = |a| 1; + let clo2 = |a| 2; + let _ = match 0 { + 0 => clo0, + 1 => clo1, + 2 => clo2, + _ => unimplemented!(), + }; + + let clo0 = |a: usize| 0; + let clo1 = |a| 1; + let clo2 = |a| 2; + let _ = [clo0, clo1, clo2]; + + + + // --- Function pointer related part + + // Closure is not in a variable + type FnPointer = fn(usize) -> usize; + + let _ = match 0 { + 0 => foo as FnPointer, + 2 => |a| 2, + _ => unimplemented!(), + }; + let _ = match 0 { + 2 => |a| 2, + 0 => foo as FnPointer, + _ => unimplemented!(), + }; + let _ = [foo as FnPointer, |a| 2]; + let _ = [|a| 2, foo as FnPointer]; + let _ = [foo, bar, |x| x]; + let _ = [foo as FnPointer, bar, |x| x]; + let _ = [foo, bar as FnPointer, |x| x]; + let _ = [foo, bar, (|x| x) as FnPointer]; + let _ = [foo as FnPointer, bar as FnPointer, |x| x]; + + // Closure is in a variable + let x = |a| 2; + let _ = match 0 { + 0 => foo as FnPointer, + 2 => x, + _ => unimplemented!(), + }; + let x = |a| 2; + let _ = match 0 { + 2 => x, + 0 => foo as FnPointer, + _ => unimplemented!(), + }; + let x = |a| 2; + let _ = [foo as FnPointer, x]; + let _ = [x, foo as FnPointer]; + + let x = |a| 2; + let _ = [foo, bar, x]; + let x: FnPointer = |a| 2; + let _ = [foo, bar, x]; + let x = |a| 2; + let _ = [foo, bar as FnPointer, x]; + let x = |a| 2; + let _ = [foo as FnPointer, bar, x]; + let x = |a| 2; + let _ = [foo as FnPointer, bar as FnPointer, x]; +} diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs b/src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs new file mode 100644 index 000000000..3c5fe8a55 --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs @@ -0,0 +1,59 @@ +// run-pass +// Ensure non-capturing Closure passing CoerceMany work correctly. +fn foo(_: usize) -> usize { + 0 +} + +fn bar(_: usize) -> usize { + 1 +} + +fn add(a: i32, b: i32) -> i32 { + a + b +} + +fn main() { + // Coerce result check + + type FnPointer = fn(usize) -> usize; + + let c = |x| x; + let c_pointer: FnPointer = c; + assert_eq!(c_pointer(42), 42); + + let f = match 0 { + 0 => foo, + 1 => |_| 1, + _ => unimplemented!(), + }; + assert_eq!(f(42), 0); + + let f = match 2 { + 2 => |_| 2, + 0 => foo, + _ => unimplemented!(), + }; + assert_eq!(f(42), 2); + + let f = match 1 { + 0 => foo, + 1 => bar, + 2 => |_| 2, + _ => unimplemented!(), + }; + assert_eq!(f(42), 1); + + let clo0 = |_: usize| 0; + let clo1 = |_| 1; + let clo2 = |_| 2; + let f = match 0 { + 0 => clo0, + 1 => clo1, + 2 => clo2, + _ => unimplemented!(), + }; + assert_eq!(f(42), 0); + + let funcs = [add, |a, b| (a - b) as i32]; + assert_eq!([funcs[0](5, 5), funcs[1](5, 5)], [10, 0]); +} diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr new file mode 100644 index 000000000..2f9c7973b --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:15:23 + | +LL | let result: i32 = foo(5, 5); + | ^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:24:23 + | +LL | let result: i32 = foo(5, 5); + | ^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs new file mode 100644 index 000000000..bdb3eb23c --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs @@ -0,0 +1,25 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + +// Ensure we get unsafe function after coercion +unsafe fn add(a: i32, b: i32) -> i32 { + a + b +} +fn main() { + // We can coerce non-capturing closure to unsafe function + let foo = match "+" { + "+" => add, + "-" => |a, b| (a - b) as i32, + _ => unimplemented!(), + }; + let result: i32 = foo(5, 5); //~ ERROR call to unsafe function + + + // We can coerce unsafe function to non-capturing closure + let foo = match "+" { + "-" => |a, b| (a - b) as i32, + "+" => add, + _ => unimplemented!(), + }; + let result: i32 = foo(5, 5); //~ ERROR call to unsafe function +} diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr new file mode 100644 index 000000000..2f9c7973b --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:15:23 + | +LL | let result: i32 = foo(5, 5); + | ^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:24:23 + | +LL | let result: i32 = foo(5, 5); + | ^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs new file mode 100644 index 000000000..a6d6125a1 --- /dev/null +++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs @@ -0,0 +1,23 @@ +// run-pass +// Ensure we get correct unsafe function after coercion +unsafe fn add(a: i32, b: i32) -> i32 { + a + b +} +fn main() { + // We can coerce non-capturing closure to unsafe function + let foo = match "+" { + "+" => add, + "-" => |a, b| (a - b) as i32, + _ => unimplemented!(), + }; + assert_eq!(unsafe { foo(5, 5) }, 10); + + + // We can coerce unsafe function to non-capturing closure + let foo = match "-" { + "-" => |a, b| (a - b) as i32, + "+" => add, + _ => unimplemented!(), + }; + assert_eq!(unsafe { foo(5, 5) }, 0); +} diff --git a/src/test/ui/closures/closure_promotion.rs b/src/test/ui/closures/closure_promotion.rs new file mode 100644 index 000000000..db36985af --- /dev/null +++ b/src/test/ui/closures/closure_promotion.rs @@ -0,0 +1,7 @@ +// build-pass (FIXME(62277): could be check-pass?) + +#![allow(const_err)] + +fn main() { + let x: &'static _ = &|| { let z = 3; z }; +} diff --git a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr new file mode 100644 index 000000000..a60100dda --- /dev/null +++ b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr @@ -0,0 +1,11 @@ +error[E0133]: call to unsafe function is unsafe and requires unsafe function or block + --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:5:31 + | +LL | let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs new file mode 100644 index 000000000..57358fbdd --- /dev/null +++ b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs @@ -0,0 +1,8 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + +fn main() { + let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; + //~^ ERROR E0133 + let _: unsafe fn() = || unsafe { ::std::pin::Pin::new_unchecked(&0_u8); }; // OK +} diff --git a/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr new file mode 100644 index 000000000..8c516e890 --- /dev/null +++ b/src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr @@ -0,0 +1,11 @@ +error[E0133]: call to unsafe function `Pin::<P>::new_unchecked` is unsafe and requires unsafe function or block + --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:5:31 + | +LL | let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/closures/coerce-unsafe-to-closure.rs b/src/test/ui/closures/coerce-unsafe-to-closure.rs new file mode 100644 index 000000000..78bdd36f9 --- /dev/null +++ b/src/test/ui/closures/coerce-unsafe-to-closure.rs @@ -0,0 +1,4 @@ +fn main() { + let x: Option<&[u8]> = Some("foo").map(std::mem::transmute); + //~^ ERROR E0277 +} diff --git a/src/test/ui/closures/coerce-unsafe-to-closure.stderr b/src/test/ui/closures/coerce-unsafe-to-closure.stderr new file mode 100644 index 000000000..bd095c2d8 --- /dev/null +++ b/src/test/ui/closures/coerce-unsafe-to-closure.stderr @@ -0,0 +1,19 @@ +error[E0277]: expected a `FnOnce<(&str,)>` closure, found `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` + --> $DIR/coerce-unsafe-to-closure.rs:2:44 + | +LL | let x: Option<&[u8]> = Some("foo").map(std::mem::transmute); + | --- ^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce<(&str,)>` is not implemented for `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` + = note: unsafe function cannot be called generically without an unsafe block +note: required by a bound in `Option::<T>::map` + --> $SRC_DIR/core/src/option.rs:LL:COL + | +LL | F: ~const FnOnce(T) -> U, + | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::map` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/closures/deeply-nested_closures.rs b/src/test/ui/closures/deeply-nested_closures.rs new file mode 100644 index 000000000..a02684ee1 --- /dev/null +++ b/src/test/ui/closures/deeply-nested_closures.rs @@ -0,0 +1,23 @@ +// Check that this can be compiled in a reasonable time. + +// build-pass + +fn main() { + // 96 nested closures + let x = (); + || || || || || || || || + || || || || || || || || + || || || || || || || || + || || || || || || || || + + || || || || || || || || + || || || || || || || || + || || || || || || || || + || || || || || || || || + + || || || || || || || || + || || || || || || || || + || || || || || || || || + || || || || || || || || + [&(), &x]; +} diff --git a/src/test/ui/closures/diverging-closure.rs b/src/test/ui/closures/diverging-closure.rs new file mode 100644 index 000000000..1213a883e --- /dev/null +++ b/src/test/ui/closures/diverging-closure.rs @@ -0,0 +1,10 @@ +// run-fail +// error-pattern:oops +// ignore-emscripten no processes + +fn main() { + let func = || -> ! { + panic!("oops"); + }; + func(); +} diff --git a/src/test/ui/closures/issue-10398.rs b/src/test/ui/closures/issue-10398.rs new file mode 100644 index 000000000..0405b2d01 --- /dev/null +++ b/src/test/ui/closures/issue-10398.rs @@ -0,0 +1,11 @@ +#![feature(box_syntax)] + +fn main() { + let x: Box<_> = box 1; + let f = move|| { + let _a = x; + drop(x); + //~^ ERROR: use of moved value: `x` + }; + f(); +} diff --git a/src/test/ui/closures/issue-10398.stderr b/src/test/ui/closures/issue-10398.stderr new file mode 100644 index 000000000..8d9faf324 --- /dev/null +++ b/src/test/ui/closures/issue-10398.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of moved value: `x` + --> $DIR/issue-10398.rs:7:14 + | +LL | let _a = x; + | - value moved here +LL | drop(x); + | ^ value used here after move + | + = note: move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/issue-41366.rs b/src/test/ui/closures/issue-41366.rs new file mode 100644 index 000000000..acc1c6ae1 --- /dev/null +++ b/src/test/ui/closures/issue-41366.rs @@ -0,0 +1,13 @@ +// check-pass + +trait T<'x> { + type V; +} + +impl<'g> T<'g> for u32 { + type V = u16; +} + +fn main() { + (&|_| ()) as &dyn for<'x> Fn(<u32 as T<'x>>::V); +} diff --git a/src/test/ui/closures/issue-42463.rs b/src/test/ui/closures/issue-42463.rs new file mode 100644 index 000000000..51d6ea3f7 --- /dev/null +++ b/src/test/ui/closures/issue-42463.rs @@ -0,0 +1,32 @@ +// run-pass +use std::ops::{Deref, DerefMut}; + +struct CheckedDeref<T, F> { + value: T, + check: F +} + +impl<F: Fn(&T) -> bool, T> Deref for CheckedDeref<T, F> { + type Target = T; + fn deref(&self) -> &T { + assert!((self.check)(&self.value)); + &self.value + } +} + +impl<F: Fn(&T) -> bool, T> DerefMut for CheckedDeref<T, F> { + fn deref_mut(&mut self) -> &mut T { + assert!((self.check)(&self.value)); + &mut self.value + } +} + + +fn main() { + let mut v = CheckedDeref { + value: vec![0], + check: |v: &Vec<_>| !v.is_empty() + }; + v.push(1); + assert_eq!(*v, vec![0, 1]); +} diff --git a/src/test/ui/closures/issue-46742.rs b/src/test/ui/closures/issue-46742.rs new file mode 100644 index 000000000..cd8dc4869 --- /dev/null +++ b/src/test/ui/closures/issue-46742.rs @@ -0,0 +1,9 @@ +// check-pass +fn main() { + let _: i32 = (match "" { + "+" => ::std::ops::Add::add, + "-" => ::std::ops::Sub::sub, + "<" => |a,b| (a < b) as i32, + _ => unimplemented!(), + })(5, 5); +} diff --git a/src/test/ui/closures/issue-48109.rs b/src/test/ui/closures/issue-48109.rs new file mode 100644 index 000000000..ce1f2a036 --- /dev/null +++ b/src/test/ui/closures/issue-48109.rs @@ -0,0 +1,14 @@ +// check-pass +fn useful(i: usize) -> usize { + i +} + +fn useful2(i: usize) -> usize { + i +} + +fn main() { + for f in &[useful, useful2, |x| x] { + println!("{}", f(6)); + } +} diff --git a/src/test/ui/closures/issue-52437.rs b/src/test/ui/closures/issue-52437.rs new file mode 100644 index 000000000..f79a0bd35 --- /dev/null +++ b/src/test/ui/closures/issue-52437.rs @@ -0,0 +1,6 @@ +fn main() { + [(); &(&'static: loop { |x| {}; }) as *const _ as usize] + //~^ ERROR: invalid label name `'static` + //~| ERROR: type annotations needed + //~| ERROR mismatched types +} diff --git a/src/test/ui/closures/issue-52437.stderr b/src/test/ui/closures/issue-52437.stderr new file mode 100644 index 000000000..38d9d08ce --- /dev/null +++ b/src/test/ui/closures/issue-52437.stderr @@ -0,0 +1,29 @@ +error: invalid label name `'static` + --> $DIR/issue-52437.rs:2:13 + | +LL | [(); &(&'static: loop { |x| {}; }) as *const _ as usize] + | ^^^^^^^ + +error[E0282]: type annotations needed + --> $DIR/issue-52437.rs:2:30 + | +LL | [(); &(&'static: loop { |x| {}; }) as *const _ as usize] + | ^ + | +help: consider giving this closure parameter an explicit type + | +LL | [(); &(&'static: loop { |x: _| {}; }) as *const _ as usize] + | +++ + +error[E0308]: mismatched types + --> $DIR/issue-52437.rs:2:5 + | +LL | fn main() { + | - expected `()` because of default return type +LL | [(); &(&'static: loop { |x| {}; }) as *const _ as usize] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found array `[(); _]` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0282, E0308. +For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/closures/issue-67123.rs b/src/test/ui/closures/issue-67123.rs new file mode 100644 index 000000000..014c530e6 --- /dev/null +++ b/src/test/ui/closures/issue-67123.rs @@ -0,0 +1,5 @@ +fn foo<T>(t: T) { + || { t; t; }; //~ ERROR: use of moved value +} + +fn main() {} diff --git a/src/test/ui/closures/issue-67123.stderr b/src/test/ui/closures/issue-67123.stderr new file mode 100644 index 000000000..7877c7334 --- /dev/null +++ b/src/test/ui/closures/issue-67123.stderr @@ -0,0 +1,17 @@ +error[E0382]: use of moved value: `t` + --> $DIR/issue-67123.rs:2:13 + | +LL | || { t; t; }; + | - ^ value used here after move + | | + | value moved here + | + = note: move occurs because `t` has type `T`, which does not implement the `Copy` trait +help: consider restricting type parameter `T` + | +LL | fn foo<T: Copy>(t: T) { + | ++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/issue-6801.rs b/src/test/ui/closures/issue-6801.rs new file mode 100644 index 000000000..694d86feb --- /dev/null +++ b/src/test/ui/closures/issue-6801.rs @@ -0,0 +1,21 @@ +// Creating a stack closure which references a box and then +// transferring ownership of the box before invoking the stack +// closure results in a crash. + +#![feature(box_syntax)] + +fn twice(x: Box<usize>) -> usize { + *x * 2 +} + +fn invoke<F>(f: F) where F: FnOnce() -> usize { + f(); +} + +fn main() { + let x : Box<usize> = box 9; + let sq = || { *x * *x }; + + twice(x); //~ ERROR: cannot move out of + invoke(sq); +} diff --git a/src/test/ui/closures/issue-6801.stderr b/src/test/ui/closures/issue-6801.stderr new file mode 100644 index 000000000..6a40db0d5 --- /dev/null +++ b/src/test/ui/closures/issue-6801.stderr @@ -0,0 +1,16 @@ +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/issue-6801.rs:19:13 + | +LL | let sq = || { *x * *x }; + | -- -- borrow occurs due to use in closure + | | + | borrow of `x` occurs here +LL | +LL | twice(x); + | ^ move out of `x` occurs here +LL | invoke(sq); + | -- borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0505`. diff --git a/src/test/ui/closures/issue-68025.rs b/src/test/ui/closures/issue-68025.rs new file mode 100644 index 000000000..261bfd60a --- /dev/null +++ b/src/test/ui/closures/issue-68025.rs @@ -0,0 +1,12 @@ +// check-pass + +fn foo<F, G>(_: G, _: Box<F>) +where + F: Fn(), + G: Fn(Box<F>), +{ +} + +fn main() { + foo(|f| (*f)(), Box::new(|| {})); +} diff --git a/src/test/ui/closures/issue-72408-nested-closures-exponential.rs b/src/test/ui/closures/issue-72408-nested-closures-exponential.rs new file mode 100644 index 000000000..2d6ba9365 --- /dev/null +++ b/src/test/ui/closures/issue-72408-nested-closures-exponential.rs @@ -0,0 +1,59 @@ +// build-pass + +// Closures include captured types twice in a type tree. +// +// Wrapping one closure with another leads to doubling +// the amount of types in the type tree. +// +// This test ensures that rust can handle +// deeply nested type trees with a lot +// of duplicated subtrees. + +fn dup(f: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 { + move |a| f(a * 2) +} + +fn main() { + let f = |a| a; + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + // Compiler dies around here if it tries + // to walk the tree exhaustively. + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + println!("Type size was at least {}", f(1)); +} diff --git a/src/test/ui/closures/issue-78720.rs b/src/test/ui/closures/issue-78720.rs new file mode 100644 index 000000000..4cdb9f491 --- /dev/null +++ b/src/test/ui/closures/issue-78720.rs @@ -0,0 +1,19 @@ +fn server() -> impl { +//~^ ERROR at least one trait must be specified + ().map2(|| "") +} + +trait FilterBase2 { + fn map2<F>(self, f: F) -> Map2<F> {} + //~^ ERROR mismatched types + //~^^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +struct Map2<Segment2> { + _func: F, + //~^ ERROR cannot find type `F` in this scope +} + +impl<F> FilterBase2 for F {} + +fn main() {} diff --git a/src/test/ui/closures/issue-78720.stderr b/src/test/ui/closures/issue-78720.stderr new file mode 100644 index 000000000..3dd138772 --- /dev/null +++ b/src/test/ui/closures/issue-78720.stderr @@ -0,0 +1,55 @@ +error: at least one trait must be specified + --> $DIR/issue-78720.rs:1:16 + | +LL | fn server() -> impl { + | ^^^^ + +error[E0412]: cannot find type `F` in this scope + --> $DIR/issue-78720.rs:13:12 + | +LL | _func: F, + | ^ + | + ::: $SRC_DIR/core/src/ops/function.rs:LL:COL + | +LL | pub trait Fn<Args>: FnMut<Args> { + | ------------------------------- similarly named trait `Fn` defined here + | +help: a trait with a similar name exists + | +LL | _func: Fn, + | ~~ +help: you might be missing a type parameter + | +LL | struct Map2<Segment2, F> { + | +++ + +error[E0308]: mismatched types + --> $DIR/issue-78720.rs:7:39 + | +LL | fn map2<F>(self, f: F) -> Map2<F> {} + | ^^ expected struct `Map2`, found `()` + | + = note: expected struct `Map2<F>` + found unit type `()` + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/issue-78720.rs:7:16 + | +LL | fn map2<F>(self, f: F) -> Map2<F> {} + | ^^^^ doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider further restricting `Self` + | +LL | fn map2<F>(self, f: F) -> Map2<F> where Self: Sized {} + | +++++++++++++++++ +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn map2<F>(&self, f: F) -> Map2<F> {} + | + + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308, E0412. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs new file mode 100644 index 000000000..ff210ae06 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs @@ -0,0 +1,7 @@ +fn main() { + let mut my_var = false; + let callback = || { + &mut my_var; + }; + callback(); //~ ERROR E0596 +} diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr new file mode 100644 index 000000000..bf9e1febd --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr @@ -0,0 +1,14 @@ +error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable + --> $DIR/issue-80313-mutable-borrow-in-closure.rs:6:5 + | +LL | let callback = || { + | -------- help: consider changing this to be mutable: `mut callback` +LL | &mut my_var; + | ------ calling `callback` requires mutable binding due to mutable borrow of `my_var` +LL | }; +LL | callback(); + | ^^^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs new file mode 100644 index 000000000..8f2d8a676 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs @@ -0,0 +1,7 @@ +fn main() { + let mut my_var = false; + let callback = move || { + &mut my_var; + }; + callback(); //~ ERROR E0596 +} diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr new file mode 100644 index 000000000..b67cec6a6 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr @@ -0,0 +1,14 @@ +error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable + --> $DIR/issue-80313-mutable-borrow-in-move-closure.rs:6:5 + | +LL | let callback = move || { + | -------- help: consider changing this to be mutable: `mut callback` +LL | &mut my_var; + | ------ calling `callback` requires mutable binding due to possible mutation of `my_var` +LL | }; +LL | callback(); + | ^^^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-80313-mutation-in-closure.rs b/src/test/ui/closures/issue-80313-mutation-in-closure.rs new file mode 100644 index 000000000..e082ea562 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutation-in-closure.rs @@ -0,0 +1,7 @@ +fn main() { + let mut my_var = false; + let callback = || { + my_var = true; + }; + callback(); //~ ERROR E0596 +} diff --git a/src/test/ui/closures/issue-80313-mutation-in-closure.stderr b/src/test/ui/closures/issue-80313-mutation-in-closure.stderr new file mode 100644 index 000000000..6e98549f6 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutation-in-closure.stderr @@ -0,0 +1,14 @@ +error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable + --> $DIR/issue-80313-mutation-in-closure.rs:6:5 + | +LL | let callback = || { + | -------- help: consider changing this to be mutable: `mut callback` +LL | my_var = true; + | ------ calling `callback` requires mutable binding due to mutable borrow of `my_var` +LL | }; +LL | callback(); + | ^^^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-80313-mutation-in-move-closure.rs b/src/test/ui/closures/issue-80313-mutation-in-move-closure.rs new file mode 100644 index 000000000..f66bf4e06 --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutation-in-move-closure.rs @@ -0,0 +1,7 @@ +fn main() { + let mut my_var = false; + let callback = move || { + my_var = true; + }; + callback(); //~ ERROR E0596 +} diff --git a/src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr b/src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr new file mode 100644 index 000000000..edd55422a --- /dev/null +++ b/src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr @@ -0,0 +1,14 @@ +error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable + --> $DIR/issue-80313-mutation-in-move-closure.rs:6:5 + | +LL | let callback = move || { + | -------- help: consider changing this to be mutable: `mut callback` +LL | my_var = true; + | ------ calling `callback` requires mutable binding due to possible mutation of `my_var` +LL | }; +LL | callback(); + | ^^^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-81700-mut-borrow.rs b/src/test/ui/closures/issue-81700-mut-borrow.rs new file mode 100644 index 000000000..a27a61601 --- /dev/null +++ b/src/test/ui/closures/issue-81700-mut-borrow.rs @@ -0,0 +1,5 @@ +fn foo(x: &mut u32) { + let bar = || { foo(x); }; + bar(); //~ ERROR cannot borrow +} +fn main() {} diff --git a/src/test/ui/closures/issue-81700-mut-borrow.stderr b/src/test/ui/closures/issue-81700-mut-borrow.stderr new file mode 100644 index 000000000..3f564afff --- /dev/null +++ b/src/test/ui/closures/issue-81700-mut-borrow.stderr @@ -0,0 +1,13 @@ +error[E0596]: cannot borrow `bar` as mutable, as it is not declared as mutable + --> $DIR/issue-81700-mut-borrow.rs:3:5 + | +LL | let bar = || { foo(x); }; + | --- - calling `bar` requires mutable binding due to mutable borrow of `x` + | | + | help: consider changing this to be mutable: `mut bar` +LL | bar(); + | ^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-82438-mut-without-upvar.rs b/src/test/ui/closures/issue-82438-mut-without-upvar.rs new file mode 100644 index 000000000..5d88e1e77 --- /dev/null +++ b/src/test/ui/closures/issue-82438-mut-without-upvar.rs @@ -0,0 +1,28 @@ +use std::error::Error; +struct A { +} + +impl A { + pub fn new() -> A { + A { + } + } + + pub fn f<'a>( + &'a self, + team_name: &'a str, + c: &'a mut dyn FnMut(String, String, u64, u64) + ) -> Result<(), Box<dyn Error>> { + Ok(()) + } +} + + +fn main() { + let A = A::new(); + let participant_name = "A"; + + let c = |a, b, c, d| {}; + + A.f(participant_name, &mut c); //~ ERROR cannot borrow +} diff --git a/src/test/ui/closures/issue-82438-mut-without-upvar.stderr b/src/test/ui/closures/issue-82438-mut-without-upvar.stderr new file mode 100644 index 000000000..802284b26 --- /dev/null +++ b/src/test/ui/closures/issue-82438-mut-without-upvar.stderr @@ -0,0 +1,12 @@ +error[E0596]: cannot borrow `c` as mutable, as it is not declared as mutable + --> $DIR/issue-82438-mut-without-upvar.rs:27:27 + | +LL | let c = |a, b, c, d| {}; + | - help: consider changing this to be mutable: `mut c` +LL | +LL | A.f(participant_name, &mut c); + | ^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-84044-drop-non-mut.rs b/src/test/ui/closures/issue-84044-drop-non-mut.rs new file mode 100644 index 000000000..aed7750f1 --- /dev/null +++ b/src/test/ui/closures/issue-84044-drop-non-mut.rs @@ -0,0 +1,6 @@ +// #84044: This used to ICE. + +fn main() { + let f = || {}; + drop(&mut f); //~ ERROR cannot borrow `f` as mutable, as it is not declared as mutable +} diff --git a/src/test/ui/closures/issue-84044-drop-non-mut.stderr b/src/test/ui/closures/issue-84044-drop-non-mut.stderr new file mode 100644 index 000000000..c0bfad263 --- /dev/null +++ b/src/test/ui/closures/issue-84044-drop-non-mut.stderr @@ -0,0 +1,11 @@ +error[E0596]: cannot borrow `f` as mutable, as it is not declared as mutable + --> $DIR/issue-84044-drop-non-mut.rs:5:10 + | +LL | let f = || {}; + | - help: consider changing this to be mutable: `mut f` +LL | drop(&mut f); + | ^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/issue-84128.rs b/src/test/ui/closures/issue-84128.rs new file mode 100644 index 000000000..f81d7cfaa --- /dev/null +++ b/src/test/ui/closures/issue-84128.rs @@ -0,0 +1,16 @@ +// test for issue 84128 +// missing suggestion for similar ADT type with diffetent generic paramenter +// on closure ReturnNoExpression + +struct Foo<T>(T); + +fn main() { + || { + if false { + return Foo(0); + } + + Foo(()) + //~^ ERROR mismatched types [E0308] + }; +} diff --git a/src/test/ui/closures/issue-84128.stderr b/src/test/ui/closures/issue-84128.stderr new file mode 100644 index 000000000..09c44d261 --- /dev/null +++ b/src/test/ui/closures/issue-84128.stderr @@ -0,0 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/issue-84128.rs:13:13 + | +LL | Foo(()) + | --- ^^ expected integer, found `()` + | | + | arguments to this struct are incorrect + | +note: return type inferred to be `{integer}` here + --> $DIR/issue-84128.rs:10:20 + | +LL | return Foo(0); + | ^^^^^^ +note: tuple struct defined here + --> $DIR/issue-84128.rs:5:8 + | +LL | struct Foo<T>(T); + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/issue-87461.rs b/src/test/ui/closures/issue-87461.rs new file mode 100644 index 000000000..0151080ee --- /dev/null +++ b/src/test/ui/closures/issue-87461.rs @@ -0,0 +1,29 @@ +// Regression test for #87461. + +// edition:2021 + +async fn func() -> Result<u16, u64> { + let _ = async { + Err(42u64) + }.await?; + + Ok(()) + //~^ ERROR: mismatched types [E0308] +} + +async fn func2() -> Result<u16, u64> { + Err(42u64)?; + + Ok(()) + //~^ ERROR: mismatched types [E0308] +} + +fn main() { + || -> Result<u16, u64> { + if true { + return Err(42u64); + } + Ok(()) + //~^ ERROR: mismatched types [E0308] + }; +} diff --git a/src/test/ui/closures/issue-87461.stderr b/src/test/ui/closures/issue-87461.stderr new file mode 100644 index 000000000..0e788a16e --- /dev/null +++ b/src/test/ui/closures/issue-87461.stderr @@ -0,0 +1,45 @@ +error[E0308]: mismatched types + --> $DIR/issue-87461.rs:10:8 + | +LL | Ok(()) + | -- ^^ expected `u16`, found `()` + | | + | arguments to this enum variant are incorrect + | +note: tuple variant defined here + --> $SRC_DIR/core/src/result.rs:LL:COL + | +LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T), + | ^^ + +error[E0308]: mismatched types + --> $DIR/issue-87461.rs:17:8 + | +LL | Ok(()) + | -- ^^ expected `u16`, found `()` + | | + | arguments to this enum variant are incorrect + | +note: tuple variant defined here + --> $SRC_DIR/core/src/result.rs:LL:COL + | +LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T), + | ^^ + +error[E0308]: mismatched types + --> $DIR/issue-87461.rs:26:12 + | +LL | Ok(()) + | -- ^^ expected `u16`, found `()` + | | + | arguments to this enum variant are incorrect + | +note: tuple variant defined here + --> $SRC_DIR/core/src/result.rs:LL:COL + | +LL | Ok(#[stable(feature = "rust1", since = "1.0.0")] T), + | ^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/issue-87814-1.rs b/src/test/ui/closures/issue-87814-1.rs new file mode 100644 index 000000000..5cf01ddf5 --- /dev/null +++ b/src/test/ui/closures/issue-87814-1.rs @@ -0,0 +1,8 @@ +// check-pass +fn main() { + let mut schema_all = vec![]; + (0..42).for_each(|_x| match Err(()) as Result<(), _> { + Ok(()) => schema_all.push(()), + Err(_) => (), + }); +} diff --git a/src/test/ui/closures/issue-87814-2.rs b/src/test/ui/closures/issue-87814-2.rs new file mode 100644 index 000000000..efe77f90f --- /dev/null +++ b/src/test/ui/closures/issue-87814-2.rs @@ -0,0 +1,10 @@ +// check-pass + +fn main() { + let mut schema_all: (Vec<String>, Vec<String>) = (vec![], vec![]); + + let _c = || match schema_all.0.try_reserve(1) as Result<(), _> { + Ok(()) => (), + Err(_) => (), + }; +} diff --git a/src/test/ui/closures/issue-90871.rs b/src/test/ui/closures/issue-90871.rs new file mode 100644 index 000000000..9c70bbc85 --- /dev/null +++ b/src/test/ui/closures/issue-90871.rs @@ -0,0 +1,5 @@ +fn main() { + 2: n([u8; || 1]) + //~^ ERROR cannot find type `n` in this scope + //~| ERROR mismatched types +} diff --git a/src/test/ui/closures/issue-90871.stderr b/src/test/ui/closures/issue-90871.stderr new file mode 100644 index 000000000..1e102cc98 --- /dev/null +++ b/src/test/ui/closures/issue-90871.stderr @@ -0,0 +1,23 @@ +error[E0412]: cannot find type `n` in this scope + --> $DIR/issue-90871.rs:2:8 + | +LL | 2: n([u8; || 1]) + | ^ expecting a type here because of type ascription + +error[E0308]: mismatched types + --> $DIR/issue-90871.rs:2:15 + | +LL | 2: n([u8; || 1]) + | ^^^^ expected `usize`, found closure + | + = note: expected type `usize` + found closure `[closure@$DIR/issue-90871.rs:2:15: 2:17]` +help: use parentheses to call this closure + | +LL | 2: n([u8; (|| 1)()]) + | + +++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0412. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/issue-99565.rs b/src/test/ui/closures/issue-99565.rs new file mode 100644 index 000000000..3a30d2ee0 --- /dev/null +++ b/src/test/ui/closures/issue-99565.rs @@ -0,0 +1,7 @@ +#![crate_type = "lib"] + +fn foo<T, U>(_: U) {} + +fn bar() { + foo(|| {}); //~ ERROR type annotations needed +} diff --git a/src/test/ui/closures/issue-99565.stderr b/src/test/ui/closures/issue-99565.stderr new file mode 100644 index 000000000..0d940aa9a --- /dev/null +++ b/src/test/ui/closures/issue-99565.stderr @@ -0,0 +1,14 @@ +error[E0282]: type annotations needed + --> $DIR/issue-99565.rs:6:5 + | +LL | foo(|| {}); + | ^^^ cannot infer type of the type parameter `T` declared on the function `foo` + | +help: consider specifying the generic arguments + | +LL | foo::<T, _>(|| {}); + | ++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/closures/local-type-mix.rs b/src/test/ui/closures/local-type-mix.rs new file mode 100644 index 000000000..006e6f490 --- /dev/null +++ b/src/test/ui/closures/local-type-mix.rs @@ -0,0 +1,17 @@ +// Check that using the parameter name in its type does not ICE. +// edition:2018 + +#![feature(async_closure)] + +fn main() { + let _ = |x: x| x; //~ ERROR expected type + let _ = |x: bool| -> x { x }; //~ ERROR expected type + let _ = async move |x: x| x; //~ ERROR expected type + let _ = async move |x: bool| -> x { x }; //~ ERROR expected type +} + +fn foo(x: x) {} //~ ERROR expected type +fn foo_ret(x: bool) -> x {} //~ ERROR expected type + +async fn async_foo(x: x) {} //~ ERROR expected type +async fn async_foo_ret(x: bool) -> x {} //~ ERROR expected type diff --git a/src/test/ui/closures/local-type-mix.stderr b/src/test/ui/closures/local-type-mix.stderr new file mode 100644 index 000000000..68c320a06 --- /dev/null +++ b/src/test/ui/closures/local-type-mix.stderr @@ -0,0 +1,51 @@ +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type-mix.rs:7:17 + | +LL | let _ = |x: x| x; + | ^ not a type + +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type-mix.rs:8:26 + | +LL | let _ = |x: bool| -> x { x }; + | ^ not a type + +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type-mix.rs:9:28 + | +LL | let _ = async move |x: x| x; + | ^ not a type + +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type-mix.rs:10:37 + | +LL | let _ = async move |x: bool| -> x { x }; + | ^ not a type + +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type-mix.rs:13:11 + | +LL | fn foo(x: x) {} + | ^ not a type + +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type-mix.rs:14:24 + | +LL | fn foo_ret(x: bool) -> x {} + | ^ not a type + +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type-mix.rs:16:23 + | +LL | async fn async_foo(x: x) {} + | ^ not a type + +error[E0573]: expected type, found local variable `x` + --> $DIR/local-type-mix.rs:17:36 + | +LL | async fn async_foo_ret(x: bool) -> x {} + | ^ not a type + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0573`. diff --git a/src/test/ui/closures/old-closure-arg-call-as.rs b/src/test/ui/closures/old-closure-arg-call-as.rs new file mode 100644 index 000000000..87cf3a487 --- /dev/null +++ b/src/test/ui/closures/old-closure-arg-call-as.rs @@ -0,0 +1,12 @@ +// run-pass + +#![allow(non_snake_case)] + +fn asBlock<F>(f: F) -> usize where F: FnOnce() -> usize { + return f(); +} + +pub fn main() { + let x = asBlock(|| 22); + assert_eq!(x, 22); +} diff --git a/src/test/ui/closures/old-closure-arg.rs b/src/test/ui/closures/old-closure-arg.rs new file mode 100644 index 000000000..bd1385e5c --- /dev/null +++ b/src/test/ui/closures/old-closure-arg.rs @@ -0,0 +1,11 @@ +// run-pass +// Check usage and precedence of block arguments in expressions: +pub fn main() { + let v = vec![-1.0f64, 0.0, 1.0, 2.0, 3.0]; + + // Statement form does not require parentheses: + for i in &v { + println!("{}", *i); + } + +} diff --git a/src/test/ui/closures/old-closure-explicit-types.rs b/src/test/ui/closures/old-closure-explicit-types.rs new file mode 100644 index 000000000..860fcc8df --- /dev/null +++ b/src/test/ui/closures/old-closure-explicit-types.rs @@ -0,0 +1,6 @@ +// run-pass + +pub fn main() { + fn as_buf<T, F>(s: String, f: F) -> T where F: FnOnce(String) -> T { f(s) } + as_buf("foo".to_string(), |foo: String| -> () { println!("{}", foo) }); +} diff --git a/src/test/ui/closures/old-closure-expr-precedence.rs b/src/test/ui/closures/old-closure-expr-precedence.rs new file mode 100644 index 000000000..13b2fe9c3 --- /dev/null +++ b/src/test/ui/closures/old-closure-expr-precedence.rs @@ -0,0 +1,62 @@ +// run-pass + +#![allow(unused_must_use)] +#![allow(unused_parens)] +// This test has some extra semis in it that the pretty-printer won't +// reproduce so we don't want to automatically reformat it + +// no-reformat + + +/* + * + * When you write a block-expression thing followed by + * a lone unary operator, you can get a surprising parse: + * + * if (...) { ... } + * -num; + * + * for example, or: + * + * if (...) { ... } + * *box; + * + * These will parse as subtraction and multiplication binops. + * To get them to parse "the way you want" you need to brace + * the leading unops: + + * if (...) { ... } + * {-num}; + * + * or alternatively, semi-separate them: + * + * if (...) { ... }; + * -num; + * + * This seems a little wonky, but the alternative is to lower + * precedence of such block-like exprs to the point where + * you have to parenthesize them to get them to occur in the + * RHS of a binop. For example, you'd have to write: + * + * 12 + (if (foo) { 13 } else { 14 }); + * + * rather than: + * + * 12 + if (foo) { 13 } else { 14 }; + * + * Since we want to maintain the ability to write the latter, + * we leave the parens-burden on the trailing unop case. + * + */ + +pub fn main() { + + let num = 12; + + assert_eq!(if (true) { 12 } else { 12 } - num, 0); + assert_eq!(12 - if (true) { 12 } else { 12 }, 0); + if (true) { 12; } {-num}; + if (true) { 12; }; {-num}; + if (true) { 12; };;; -num; + //~^ WARNING unnecessary trailing semicolons +} diff --git a/src/test/ui/closures/old-closure-expr-precedence.stderr b/src/test/ui/closures/old-closure-expr-precedence.stderr new file mode 100644 index 000000000..fabece1ad --- /dev/null +++ b/src/test/ui/closures/old-closure-expr-precedence.stderr @@ -0,0 +1,10 @@ +warning: unnecessary trailing semicolons + --> $DIR/old-closure-expr-precedence.rs:60:21 + | +LL | if (true) { 12; };;; -num; + | ^^ help: remove these semicolons + | + = note: `#[warn(redundant_semicolons)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/old-closure-expression-remove-semicolon.fixed b/src/test/ui/closures/old-closure-expression-remove-semicolon.fixed new file mode 100644 index 000000000..623e917da --- /dev/null +++ b/src/test/ui/closures/old-closure-expression-remove-semicolon.fixed @@ -0,0 +1,12 @@ +// run-rustfix + +fn foo() -> i32 { + 0 +} + +fn main() { + let _x: i32 = { + //~^ ERROR mismatched types + foo() //~ HELP remove this semicolon + }; +} diff --git a/src/test/ui/closures/old-closure-expression-remove-semicolon.rs b/src/test/ui/closures/old-closure-expression-remove-semicolon.rs new file mode 100644 index 000000000..4974b0866 --- /dev/null +++ b/src/test/ui/closures/old-closure-expression-remove-semicolon.rs @@ -0,0 +1,12 @@ +// run-rustfix + +fn foo() -> i32 { + 0 +} + +fn main() { + let _x: i32 = { + //~^ ERROR mismatched types + foo(); //~ HELP remove this semicolon + }; +} diff --git a/src/test/ui/closures/old-closure-expression-remove-semicolon.stderr b/src/test/ui/closures/old-closure-expression-remove-semicolon.stderr new file mode 100644 index 000000000..9b73ce4fe --- /dev/null +++ b/src/test/ui/closures/old-closure-expression-remove-semicolon.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/old-closure-expression-remove-semicolon.rs:8:19 + | +LL | let _x: i32 = { + | ___________________^ +LL | | +LL | | foo(); + | | - help: remove this semicolon +LL | | }; + | |_____^ expected `i32`, found `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/old-closure-fn-coerce.rs b/src/test/ui/closures/old-closure-fn-coerce.rs new file mode 100644 index 000000000..d993ad994 --- /dev/null +++ b/src/test/ui/closures/old-closure-fn-coerce.rs @@ -0,0 +1,11 @@ +// run-pass +#![allow(unused_braces)] + +fn force<F>(f: F) -> isize where F: FnOnce() -> isize { return f(); } + +pub fn main() { + fn f() -> isize { return 7; } + assert_eq!(force(f), 7); + let g = {||force(f)}; + assert_eq!(g(), 7); +} diff --git a/src/test/ui/closures/old-closure-iter-1.rs b/src/test/ui/closures/old-closure-iter-1.rs new file mode 100644 index 000000000..caf0266cf --- /dev/null +++ b/src/test/ui/closures/old-closure-iter-1.rs @@ -0,0 +1,15 @@ +// run-pass + +fn iter_vec<T, F>(v: Vec<T> , mut f: F) where F: FnMut(&T) { for x in &v { f(x); } } + +pub fn main() { + let v = vec![1, 2, 3, 4, 5, 6, 7]; + let mut odds = 0; + iter_vec(v, |i| { + if *i % 2 == 1 { + odds += 1; + } + }); + println!("{}", odds); + assert_eq!(odds, 4); +} diff --git a/src/test/ui/closures/old-closure-iter-2.rs b/src/test/ui/closures/old-closure-iter-2.rs new file mode 100644 index 000000000..e90c1ee81 --- /dev/null +++ b/src/test/ui/closures/old-closure-iter-2.rs @@ -0,0 +1,15 @@ +// run-pass + +fn iter_vec<T, F>(v: Vec<T>, mut f: F) where F: FnMut(&T) { for x in &v { f(x); } } + +pub fn main() { + let v = vec![1, 2, 3, 4, 5]; + let mut sum = 0; + iter_vec(v.clone(), |i| { + iter_vec(v.clone(), |j| { + sum += *i * *j; + }); + }); + println!("{}", sum); + assert_eq!(sum, 225); +} diff --git a/src/test/ui/closures/once-move-out-on-heap.rs b/src/test/ui/closures/once-move-out-on-heap.rs new file mode 100644 index 000000000..4e2e400ce --- /dev/null +++ b/src/test/ui/closures/once-move-out-on-heap.rs @@ -0,0 +1,18 @@ +// run-pass +// Testing guarantees provided by once functions. + + + +use std::sync::Arc; + +fn foo<F:FnOnce()>(blk: F) { + blk(); +} + +pub fn main() { + let x = Arc::new(true); + foo(move|| { + assert!(*x); + drop(x); + }); +} diff --git a/src/test/ui/closures/print/closure-print-generic-1.rs b/src/test/ui/closures/print/closure-print-generic-1.rs new file mode 100644 index 000000000..504b4adbe --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-1.rs @@ -0,0 +1,23 @@ +fn to_fn_once<F: FnOnce()>(f: F) -> F { + f +} + +fn f<T: std::fmt::Display>(y: T) { + struct Foo<U: std::fmt::Display> { + x: U, + }; + + let foo = Foo { x: "x" }; + + let c = to_fn_once(move || { + println!("{} {}", foo.x, y); + }); + + c(); + c(); + //~^ ERROR use of moved value +} + +fn main() { + f("S"); +} diff --git a/src/test/ui/closures/print/closure-print-generic-1.stderr b/src/test/ui/closures/print/closure-print-generic-1.stderr new file mode 100644 index 000000000..b21734f02 --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-1.stderr @@ -0,0 +1,20 @@ +error[E0382]: use of moved value: `c` + --> $DIR/closure-print-generic-1.rs:17:5 + | +LL | let c = to_fn_once(move || { + | - move occurs because `c` has type `[closure@$DIR/closure-print-generic-1.rs:12:24: 12:31]`, which does not implement the `Copy` trait +... +LL | c(); + | --- `c` moved due to this call +LL | c(); + | ^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/closure-print-generic-1.rs:16:5 + | +LL | c(); + | ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/print/closure-print-generic-2.rs b/src/test/ui/closures/print/closure-print-generic-2.rs new file mode 100644 index 000000000..3f77fd26b --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-2.rs @@ -0,0 +1,13 @@ +mod mod1 { + pub fn f<T: std::fmt::Display>(t: T) { + let x = 20; + + let c = || println!("{} {}", t, x); + let c1: () = c; + //~^ ERROR mismatched types + } +} + +fn main() { + mod1::f(5i32); +} diff --git a/src/test/ui/closures/print/closure-print-generic-2.stderr b/src/test/ui/closures/print/closure-print-generic-2.stderr new file mode 100644 index 000000000..e53277a93 --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-2.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/closure-print-generic-2.rs:6:22 + | +LL | let c = || println!("{} {}", t, x); + | -- the found closure +LL | let c1: () = c; + | -- ^ expected `()`, found closure + | | + | expected due to this + | + = note: expected unit type `()` + found closure `[closure@$DIR/closure-print-generic-2.rs:5:17: 5:19]` +help: use parentheses to call this closure + | +LL | let c1: () = c(); + | ++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.rs b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.rs new file mode 100644 index 000000000..07bf8fe4c --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.rs @@ -0,0 +1,16 @@ +// compile-flags: -Ztrim-diagnostic-paths=off -Zverbose + +mod mod1 { + pub fn f<T: std::fmt::Display>(t: T) + { + let x = 20; + + let c = || println!("{} {}", t, x); + let c1 : () = c; + //~^ ERROR mismatched types + } +} + +fn main() { + mod1::f(5i32); +} diff --git a/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr new file mode 100644 index 000000000..ff89dd340 --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/closure-print-generic-trim-off-verbose-2.rs:9:23 + | +LL | let c = || println!("{} {}", t, x); + | -- the found closure +LL | let c1 : () = c; + | -- ^ expected `()`, found closure + | | + | expected due to this + | + = note: expected unit type `()` + found closure `[mod1::f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#16t, extern "rust-call" fn(()), _#15t]]` +help: use parentheses to call this closure + | +LL | let c1 : () = c(); + | ++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/print/closure-print-generic-verbose-1.rs b/src/test/ui/closures/print/closure-print-generic-verbose-1.rs new file mode 100644 index 000000000..67d37f1c5 --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-verbose-1.rs @@ -0,0 +1,24 @@ +// compile-flags: -Zverbose + +fn to_fn_once<F:FnOnce()>(f: F) -> F { f } + +fn f<T: std::fmt::Display>(y: T) { + struct Foo<U: std::fmt::Display> { + x: U + }; + + let foo = Foo{ x: "x" }; + + let c = to_fn_once(move|| { + println!("{} {}", foo.x, y); + }); + + c(); + c(); + //~^ ERROR use of moved value +} + + +fn main() { + f("S"); +} diff --git a/src/test/ui/closures/print/closure-print-generic-verbose-1.stderr b/src/test/ui/closures/print/closure-print-generic-verbose-1.stderr new file mode 100644 index 000000000..fdaf353fe --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-verbose-1.stderr @@ -0,0 +1,20 @@ +error[E0382]: use of moved value: `c` + --> $DIR/closure-print-generic-verbose-1.rs:17:5 + | +LL | let c = to_fn_once(move|| { + | - move occurs because `c` has type `[f<T>::{closure#0} closure_kind_ty=i32 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=(Foo<&'_#10r str>, T)]`, which does not implement the `Copy` trait +... +LL | c(); + | --- `c` moved due to this call +LL | c(); + | ^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/closure-print-generic-verbose-1.rs:16:5 + | +LL | c(); + | ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/print/closure-print-generic-verbose-2.rs b/src/test/ui/closures/print/closure-print-generic-verbose-2.rs new file mode 100644 index 000000000..f460fedff --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-verbose-2.rs @@ -0,0 +1,16 @@ +// compile-flags: -Zverbose + +mod mod1 { + pub fn f<T: std::fmt::Display>(t: T) + { + let x = 20; + + let c = || println!("{} {}", t, x); + let c1 : () = c; + //~^ ERROR mismatched types + } +} + +fn main() { + mod1::f(5i32); +} diff --git a/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr b/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr new file mode 100644 index 000000000..5bbf84f96 --- /dev/null +++ b/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/closure-print-generic-verbose-2.rs:9:23 + | +LL | let c = || println!("{} {}", t, x); + | -- the found closure +LL | let c1 : () = c; + | -- ^ expected `()`, found closure + | | + | expected due to this + | + = note: expected unit type `()` + found closure `[f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#16t, extern "rust-call" fn(()), _#15t]]` +help: use parentheses to call this closure + | +LL | let c1 : () = c(); + | ++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/print/closure-print-verbose.rs b/src/test/ui/closures/print/closure-print-verbose.rs new file mode 100644 index 000000000..4b0438a91 --- /dev/null +++ b/src/test/ui/closures/print/closure-print-verbose.rs @@ -0,0 +1,12 @@ +// compile-flags: -Zverbose + +// Same as closure-coerce-fn-1.rs + +// Ensure that capturing closures are never coerced to fns +// Especially interesting as non-capturing closures can be. + +fn main() { + let mut a = 0u8; + let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; + //~^ ERROR mismatched types +} diff --git a/src/test/ui/closures/print/closure-print-verbose.stderr b/src/test/ui/closures/print/closure-print-verbose.stderr new file mode 100644 index 000000000..083717b33 --- /dev/null +++ b/src/test/ui/closures/print/closure-print-verbose.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/closure-print-verbose.rs:10:29 + | +LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; + | ------------ ^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure + | | + | expected due to this + | + = note: expected fn pointer `fn(u8) -> u8` + found closure `[main::{closure#0} closure_substs=(unavailable) substs=[i8, extern "rust-call" fn((u8,)) -> u8, _#6t]]` +note: closures can only be coerced to `fn` types if they do not capture any variables + --> $DIR/closure-print-verbose.rs:10:39 + | +LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; + | ^ `a` captured here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/semistatement-in-lambda.rs b/src/test/ui/closures/semistatement-in-lambda.rs new file mode 100644 index 000000000..ebd55e0ba --- /dev/null +++ b/src/test/ui/closures/semistatement-in-lambda.rs @@ -0,0 +1,12 @@ +// run-pass + +#![allow(unused_must_use)] + +pub fn main() { + // Test that lambdas behave as unary expressions with block-like expressions + -if true { 1 } else { 2 } * 3; + || if true { 1 } else { 2 } * 3; + + // The following is invalid and parses as `if true { 1 } else { 2 }; *3` + // if true { 1 } else { 2 } * 3 +} diff --git a/src/test/ui/closures/thir-unsafeck-issue-85871.rs b/src/test/ui/closures/thir-unsafeck-issue-85871.rs new file mode 100644 index 000000000..aea539b74 --- /dev/null +++ b/src/test/ui/closures/thir-unsafeck-issue-85871.rs @@ -0,0 +1,20 @@ +// Tests that no ICE occurs when a closure appears inside a node +// that does not have a body when compiling with +// compile-flags: -Zthir-unsafeck=yes +// check-pass + +#![allow(dead_code)] + +struct Bug { + inner: [(); match || 1 { + _n => 42, // we may not call the closure here (E0015) + }], +} + +enum E { + V([(); { let _ = || 1; 42 }]), +} + +type Ty = [(); { let _ = || 1; 42 }]; + +fn main() {} |