diff options
Diffstat (limited to 'src/test/ui/nll')
535 files changed, 17529 insertions, 0 deletions
diff --git a/src/test/ui/nll/assign-while-to-immutable.rs b/src/test/ui/nll/assign-while-to-immutable.rs new file mode 100644 index 000000000..c803321b5 --- /dev/null +++ b/src/test/ui/nll/assign-while-to-immutable.rs @@ -0,0 +1,11 @@ +// We used to incorrectly assign to `x` twice when generating MIR for this +// function, preventing this from compiling. + +// check-pass + +fn main() { + let x = while false { + break; + }; + let y = 'l: while break 'l {}; +} diff --git a/src/test/ui/nll/borrow-use-issue-46875.rs b/src/test/ui/nll/borrow-use-issue-46875.rs new file mode 100644 index 000000000..42e28b967 --- /dev/null +++ b/src/test/ui/nll/borrow-use-issue-46875.rs @@ -0,0 +1,18 @@ +// run-pass + +fn vec() { + let mut _x = vec!['c']; + let _y = &_x; + _x = Vec::new(); +} + +fn int() { + let mut _x = 5; + let _y = &_x; + _x = 7; +} + +fn main() { + vec(); + int(); +} diff --git a/src/test/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs b/src/test/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs new file mode 100644 index 000000000..7d3b00dfc --- /dev/null +++ b/src/test/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs @@ -0,0 +1,25 @@ +// +// run-pass +// +// FIXME(#54366) - We probably shouldn't allow #[thread_local] static mut to get a 'static lifetime. + +#![feature(thread_local)] + +#[thread_local] +static mut X1: u64 = 0; + +struct S1 { + a: &'static mut u64, +} + +impl S1 { + fn new(_x: u64) -> S1 { + S1 { + a: unsafe { &mut X1 }, + } + } +} + +fn main() { + S1::new(0).a; +} diff --git a/src/test/ui/nll/borrowed-local-error.rs b/src/test/ui/nll/borrowed-local-error.rs new file mode 100644 index 000000000..d333356d9 --- /dev/null +++ b/src/test/ui/nll/borrowed-local-error.rs @@ -0,0 +1,12 @@ +fn gimme(x: &(u32,)) -> &u32 { + &x.0 +} + +fn main() { + let x = gimme({ + let v = (22,); + &v + //~^ ERROR `v` does not live long enough [E0597] + }); + println!("{:?}", x); +} diff --git a/src/test/ui/nll/borrowed-local-error.stderr b/src/test/ui/nll/borrowed-local-error.stderr new file mode 100644 index 000000000..d629caa43 --- /dev/null +++ b/src/test/ui/nll/borrowed-local-error.stderr @@ -0,0 +1,15 @@ +error[E0597]: `v` does not live long enough + --> $DIR/borrowed-local-error.rs:8:9 + | +LL | let x = gimme({ + | ----- borrow later used by call +LL | let v = (22,); +LL | &v + | ^^ borrowed value does not live long enough +LL | +LL | }); + | - `v` 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/nll/borrowed-match-issue-45045.rs b/src/test/ui/nll/borrowed-match-issue-45045.rs new file mode 100644 index 000000000..978eeb868 --- /dev/null +++ b/src/test/ui/nll/borrowed-match-issue-45045.rs @@ -0,0 +1,18 @@ +// Regression test for issue #45045 + +enum Xyz { + A, + B, +} + +fn main() { + let mut e = Xyz::A; + let f = &mut e; + let g = f; + match e { + //~^ cannot use `e` because it was mutably borrowed [E0503] + Xyz::A => println!("a"), + Xyz::B => println!("b"), + }; + *g = Xyz::B; +} diff --git a/src/test/ui/nll/borrowed-match-issue-45045.stderr b/src/test/ui/nll/borrowed-match-issue-45045.stderr new file mode 100644 index 000000000..9d4682667 --- /dev/null +++ b/src/test/ui/nll/borrowed-match-issue-45045.stderr @@ -0,0 +1,15 @@ +error[E0503]: cannot use `e` because it was mutably borrowed + --> $DIR/borrowed-match-issue-45045.rs:12:11 + | +LL | let f = &mut e; + | ------ borrow of `e` occurs here +LL | let g = f; +LL | match e { + | ^ use of borrowed `e` +... +LL | *g = Xyz::B; + | ----------- borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0503`. diff --git a/src/test/ui/nll/borrowed-referent-issue-38899.rs b/src/test/ui/nll/borrowed-referent-issue-38899.rs new file mode 100644 index 000000000..1fe133283 --- /dev/null +++ b/src/test/ui/nll/borrowed-referent-issue-38899.rs @@ -0,0 +1,17 @@ +// Regression test for issue #38899 + +pub struct Block<'a> { + current: &'a u8, + unrelated: &'a u8, +} + +fn bump<'a>(mut block: &mut Block<'a>) { + let x = &mut block; + println!("{}", x.current); + let p: &'a u8 = &*block.current; + //~^ ERROR cannot borrow `*block.current` as immutable because it is also borrowed as mutable + drop(x); + drop(p); +} + +fn main() {} diff --git a/src/test/ui/nll/borrowed-referent-issue-38899.stderr b/src/test/ui/nll/borrowed-referent-issue-38899.stderr new file mode 100644 index 000000000..16588cbcf --- /dev/null +++ b/src/test/ui/nll/borrowed-referent-issue-38899.stderr @@ -0,0 +1,15 @@ +error[E0502]: cannot borrow `*block.current` as immutable because it is also borrowed as mutable + --> $DIR/borrowed-referent-issue-38899.rs:11:21 + | +LL | let x = &mut block; + | ---------- mutable borrow occurs here +LL | println!("{}", x.current); +LL | let p: &'a u8 = &*block.current; + | ^^^^^^^^^^^^^^^ immutable borrow occurs here +LL | +LL | drop(x); + | - mutable 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/nll/borrowed-temporary-error.rs b/src/test/ui/nll/borrowed-temporary-error.rs new file mode 100644 index 000000000..37d0e670d --- /dev/null +++ b/src/test/ui/nll/borrowed-temporary-error.rs @@ -0,0 +1,12 @@ +fn gimme(x: &(u32,)) -> &u32 { + &x.0 +} + +fn main() { + let x = gimme({ + let v = 22; + &(v,) + //~^ ERROR temporary value dropped while borrowed [E0716] + }); + println!("{:?}", x); +} diff --git a/src/test/ui/nll/borrowed-temporary-error.stderr b/src/test/ui/nll/borrowed-temporary-error.stderr new file mode 100644 index 000000000..2c6bd9264 --- /dev/null +++ b/src/test/ui/nll/borrowed-temporary-error.stderr @@ -0,0 +1,16 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/borrowed-temporary-error.rs:8:10 + | +LL | &(v,) + | ^^^^ creates a temporary which is freed while still in use +LL | +LL | }); + | - temporary value is freed at the end of this statement +LL | println!("{:?}", x); + | - borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0716`. diff --git a/src/test/ui/nll/borrowed-universal-error-2.rs b/src/test/ui/nll/borrowed-universal-error-2.rs new file mode 100644 index 000000000..3f9b2f292 --- /dev/null +++ b/src/test/ui/nll/borrowed-universal-error-2.rs @@ -0,0 +1,7 @@ +fn foo<'a>(x: &'a (u32,)) -> &'a u32 { + let v = 22; + &v + //~^ ERROR cannot return reference to local variable `v` [E0515] +} + +fn main() {} diff --git a/src/test/ui/nll/borrowed-universal-error-2.stderr b/src/test/ui/nll/borrowed-universal-error-2.stderr new file mode 100644 index 000000000..7213ed3ba --- /dev/null +++ b/src/test/ui/nll/borrowed-universal-error-2.stderr @@ -0,0 +1,9 @@ +error[E0515]: cannot return reference to local variable `v` + --> $DIR/borrowed-universal-error-2.rs:3:5 + | +LL | &v + | ^^ returns a reference to data owned by the current function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/src/test/ui/nll/borrowed-universal-error.rs b/src/test/ui/nll/borrowed-universal-error.rs new file mode 100644 index 000000000..fc9ffd470 --- /dev/null +++ b/src/test/ui/nll/borrowed-universal-error.rs @@ -0,0 +1,11 @@ +fn gimme(x: &(u32,)) -> &u32 { + &x.0 +} + +fn foo<'a>(x: &'a (u32,)) -> &'a u32 { + let v = 22; + gimme(&(v,)) + //~^ ERROR cannot return value referencing temporary value [E0515] +} + +fn main() {} diff --git a/src/test/ui/nll/borrowed-universal-error.stderr b/src/test/ui/nll/borrowed-universal-error.stderr new file mode 100644 index 000000000..88a2d8fcf --- /dev/null +++ b/src/test/ui/nll/borrowed-universal-error.stderr @@ -0,0 +1,12 @@ +error[E0515]: cannot return value referencing temporary value + --> $DIR/borrowed-universal-error.rs:7:5 + | +LL | gimme(&(v,)) + | ^^^^^^^----^ + | | | + | | temporary value created here + | returns a value referencing data owned by the current function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/src/test/ui/nll/cannot-move-block-spans.rs b/src/test/ui/nll/cannot-move-block-spans.rs new file mode 100644 index 000000000..e994fd3d5 --- /dev/null +++ b/src/test/ui/nll/cannot-move-block-spans.rs @@ -0,0 +1,22 @@ +// Test that the we point to the inner expression when moving out to initialize +// a variable, and that we don't give a useless suggestion such as &{ *r }. + +pub fn deref(r: &String) { + let x = { *r }; //~ ERROR + let y = unsafe { *r }; //~ ERROR + let z = loop { break *r; }; //~ ERROR +} + +pub fn index(arr: [String; 2]) { + let x = { arr[0] }; //~ ERROR + let y = unsafe { arr[0] }; //~ ERROR + let z = loop { break arr[0]; }; //~ ERROR +} + +pub fn additional_statement_cases(r: &String) { + let x = { let mut u = 0; u += 1; *r }; //~ ERROR + let y = unsafe { let mut u = 0; u += 1; *r }; //~ ERROR + let z = loop { let mut u = 0; u += 1; break *r; u += 2; }; //~ ERROR +} + +fn main() {} diff --git a/src/test/ui/nll/cannot-move-block-spans.stderr b/src/test/ui/nll/cannot-move-block-spans.stderr new file mode 100644 index 000000000..56a5cdff0 --- /dev/null +++ b/src/test/ui/nll/cannot-move-block-spans.stderr @@ -0,0 +1,88 @@ +error[E0507]: cannot move out of `*r` which is behind a shared reference + --> $DIR/cannot-move-block-spans.rs:5:15 + | +LL | let x = { *r }; + | ^^ + | | + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&*r` + +error[E0507]: cannot move out of `*r` which is behind a shared reference + --> $DIR/cannot-move-block-spans.rs:6:22 + | +LL | let y = unsafe { *r }; + | ^^ + | | + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&*r` + +error[E0507]: cannot move out of `*r` which is behind a shared reference + --> $DIR/cannot-move-block-spans.rs:7:26 + | +LL | let z = loop { break *r; }; + | ^^ + | | + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&*r` + +error[E0508]: cannot move out of type `[String; 2]`, a non-copy array + --> $DIR/cannot-move-block-spans.rs:11:15 + | +LL | let x = { arr[0] }; + | ^^^^^^ + | | + | cannot move out of here + | move occurs because `arr[_]` has type `String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&arr[0]` + +error[E0508]: cannot move out of type `[String; 2]`, a non-copy array + --> $DIR/cannot-move-block-spans.rs:12:22 + | +LL | let y = unsafe { arr[0] }; + | ^^^^^^ + | | + | cannot move out of here + | move occurs because `arr[_]` has type `String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&arr[0]` + +error[E0508]: cannot move out of type `[String; 2]`, a non-copy array + --> $DIR/cannot-move-block-spans.rs:13:26 + | +LL | let z = loop { break arr[0]; }; + | ^^^^^^ + | | + | cannot move out of here + | move occurs because `arr[_]` has type `String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&arr[0]` + +error[E0507]: cannot move out of `*r` which is behind a shared reference + --> $DIR/cannot-move-block-spans.rs:17:38 + | +LL | let x = { let mut u = 0; u += 1; *r }; + | ^^ + | | + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&*r` + +error[E0507]: cannot move out of `*r` which is behind a shared reference + --> $DIR/cannot-move-block-spans.rs:18:45 + | +LL | let y = unsafe { let mut u = 0; u += 1; *r }; + | ^^ + | | + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&*r` + +error[E0507]: cannot move out of `*r` which is behind a shared reference + --> $DIR/cannot-move-block-spans.rs:19:49 + | +LL | let z = loop { let mut u = 0; u += 1; break *r; u += 2; }; + | ^^ + | | + | move occurs because `*r` has type `String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&*r` + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0507, E0508. +For more information about an error, try `rustc --explain E0507`. diff --git a/src/test/ui/nll/capture-mut-ref.fixed b/src/test/ui/nll/capture-mut-ref.fixed new file mode 100644 index 000000000..2dacb26b6 --- /dev/null +++ b/src/test/ui/nll/capture-mut-ref.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +// Check that capturing a mutable reference by move and assigning to its +// referent doesn't make the unused mut lint think that it is mutable. + +#![deny(unused_mut)] + +pub fn mutable_upvar() { + let x = &mut 0; + //~^ ERROR + let _ = move || { + *x = 1; + }; +} + +fn main() {} diff --git a/src/test/ui/nll/capture-mut-ref.rs b/src/test/ui/nll/capture-mut-ref.rs new file mode 100644 index 000000000..56e01f7b7 --- /dev/null +++ b/src/test/ui/nll/capture-mut-ref.rs @@ -0,0 +1,16 @@ +// run-rustfix + +// Check that capturing a mutable reference by move and assigning to its +// referent doesn't make the unused mut lint think that it is mutable. + +#![deny(unused_mut)] + +pub fn mutable_upvar() { + let mut x = &mut 0; + //~^ ERROR + let _ = move || { + *x = 1; + }; +} + +fn main() {} diff --git a/src/test/ui/nll/capture-mut-ref.stderr b/src/test/ui/nll/capture-mut-ref.stderr new file mode 100644 index 000000000..4898d5692 --- /dev/null +++ b/src/test/ui/nll/capture-mut-ref.stderr @@ -0,0 +1,16 @@ +error: variable does not need to be mutable + --> $DIR/capture-mut-ref.rs:9:9 + | +LL | let mut x = &mut 0; + | ----^ + | | + | help: remove this `mut` + | +note: the lint level is defined here + --> $DIR/capture-mut-ref.rs:6:9 + | +LL | #![deny(unused_mut)] + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/capture-ref-in-struct.rs b/src/test/ui/nll/capture-ref-in-struct.rs new file mode 100644 index 000000000..db6ac7d66 --- /dev/null +++ b/src/test/ui/nll/capture-ref-in-struct.rs @@ -0,0 +1,36 @@ +// Test that a structure which tries to store a pointer to `y` into +// `p` (indirectly) fails to compile. + +struct SomeStruct<'a, 'b: 'a> { + p: &'a mut &'b i32, + y: &'b i32, +} + +fn test() { + let x = 44; + let mut p = &x; + + { + let y = 22; + + let closure = SomeStruct { + p: &mut p, + y: &y, + //~^ ERROR `y` does not live long enough [E0597] + }; + + closure.invoke(); + } + + deref(p); +} + +impl<'a, 'b> SomeStruct<'a, 'b> { + fn invoke(self) { + *self.p = self.y; + } +} + +fn deref(_: &i32) { } + +fn main() { } diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr new file mode 100644 index 000000000..cdfe7f6db --- /dev/null +++ b/src/test/ui/nll/capture-ref-in-struct.stderr @@ -0,0 +1,15 @@ +error[E0597]: `y` does not live long enough + --> $DIR/capture-ref-in-struct.rs:18:16 + | +LL | y: &y, + | ^^ borrowed value does not live long enough +... +LL | } + | - `y` dropped here while still borrowed +LL | +LL | deref(p); + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/closure-access-spans.rs b/src/test/ui/nll/closure-access-spans.rs new file mode 100644 index 000000000..2a59e80b2 --- /dev/null +++ b/src/test/ui/nll/closure-access-spans.rs @@ -0,0 +1,56 @@ +// check that accesses due to a closure capture give a special note + +fn closure_imm_capture_conflict(mut x: i32) { + let r = &mut x; + || x; //~ ERROR + r.use_mut(); +} + +fn closure_mut_capture_conflict(mut x: i32) { + let r = &mut x; + || x = 2; //~ ERROR + r.use_mut(); +} + +fn closure_unique_capture_conflict(mut x: &mut i32) { + let r = &mut x; + || *x = 2; //~ ERROR + r.use_mut(); +} + +fn closure_copy_capture_conflict(mut x: i32) { + let r = &mut x; + move || x; //~ ERROR + r.use_ref(); +} + +fn closure_move_capture_conflict(mut x: String) { + let r = &x; + || x; //~ ERROR + r.use_ref(); +} + +fn closure_imm_capture_moved(mut x: String) { + let r = x; + || x.len(); //~ ERROR +} + +fn closure_mut_capture_moved(mut x: String) { + let r = x; + || x = String::new(); //~ ERROR +} + +fn closure_unique_capture_moved(x: &mut String) { + let r = x; + || *x = String::new(); //~ ERROR +} + +fn closure_move_capture_moved(x: &mut String) { + let r = x; + || x; //~ ERROR +} + +fn main() {} + +trait Fake { fn use_mut(&mut self) { } fn use_ref(&self) { } } +impl<T> Fake for T { } diff --git a/src/test/ui/nll/closure-access-spans.stderr b/src/test/ui/nll/closure-access-spans.stderr new file mode 100644 index 000000000..e9d7ca953 --- /dev/null +++ b/src/test/ui/nll/closure-access-spans.stderr @@ -0,0 +1,110 @@ +error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable + --> $DIR/closure-access-spans.rs:5:5 + | +LL | let r = &mut x; + | ------ mutable borrow occurs here +LL | || x; + | ^^ - second borrow occurs due to use of `x` in closure + | | + | immutable borrow occurs here +LL | r.use_mut(); + | ----------- mutable borrow later used here + +error[E0499]: cannot borrow `x` as mutable more than once at a time + --> $DIR/closure-access-spans.rs:11:5 + | +LL | let r = &mut x; + | ------ first mutable borrow occurs here +LL | || x = 2; + | ^^ - second borrow occurs due to use of `x` in closure + | | + | second mutable borrow occurs here +LL | r.use_mut(); + | ----------- first borrow later used here + +error[E0500]: closure requires unique access to `x` but it is already borrowed + --> $DIR/closure-access-spans.rs:17:5 + | +LL | let r = &mut x; + | ------ borrow occurs here +LL | || *x = 2; + | ^^ -- second borrow occurs due to use of `x` in closure + | | + | closure construction occurs here +LL | r.use_mut(); + | ----------- first borrow later used here + +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/closure-access-spans.rs:23:13 + | +LL | let r = &mut x; + | ------ borrow of `x` occurs here +LL | move || x; + | ^ use of borrowed `x` +LL | r.use_ref(); + | ----------- borrow later used here + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/closure-access-spans.rs:29:5 + | +LL | let r = &x; + | -- borrow of `x` occurs here +LL | || x; + | ^^ - move occurs due to use in closure + | | + | move out of `x` occurs here +LL | r.use_ref(); + | ----------- borrow later used here + +error[E0382]: borrow of moved value: `x` + --> $DIR/closure-access-spans.rs:35:5 + | +LL | fn closure_imm_capture_moved(mut x: String) { + | ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait +LL | let r = x; + | - value moved here +LL | || x.len(); + | ^^ - borrow occurs due to use in closure + | | + | value borrowed here after move + +error[E0382]: borrow of moved value: `x` + --> $DIR/closure-access-spans.rs:40:5 + | +LL | fn closure_mut_capture_moved(mut x: String) { + | ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait +LL | let r = x; + | - value moved here +LL | || x = String::new(); + | ^^ - borrow occurs due to use in closure + | | + | value borrowed here after move + +error[E0382]: borrow of moved value: `x` + --> $DIR/closure-access-spans.rs:45:5 + | +LL | fn closure_unique_capture_moved(x: &mut String) { + | - move occurs because `x` has type `&mut String`, which does not implement the `Copy` trait +LL | let r = x; + | - value moved here +LL | || *x = String::new(); + | ^^ -- borrow occurs due to use in closure + | | + | value borrowed here after move + +error[E0382]: use of moved value: `x` + --> $DIR/closure-access-spans.rs:50:5 + | +LL | fn closure_move_capture_moved(x: &mut String) { + | - move occurs because `x` has type `&mut String`, which does not implement the `Copy` trait +LL | let r = x; + | - value moved here +LL | || x; + | ^^ - use occurs due to use in closure + | | + | value used here after move + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0382, E0499, E0500, E0502, E0503, E0505. +For more information about an error, try `rustc --explain E0382`. diff --git a/src/test/ui/nll/closure-borrow-spans.rs b/src/test/ui/nll/closure-borrow-spans.rs new file mode 100644 index 000000000..b38f7900e --- /dev/null +++ b/src/test/ui/nll/closure-borrow-spans.rs @@ -0,0 +1,100 @@ +// check that existing borrows due to a closure capture give a special note + +fn move_while_borrowed(x: String) { + let f = || x.len(); + let y = x; //~ ERROR + f.use_ref(); +} + +fn borrow_mut_while_borrowed(mut x: i32) { + let f = || x; + let y = &mut x; //~ ERROR + f.use_ref(); +} + +fn drop_while_borrowed() { + let f; + { + let x = 1; + f = || x; //~ ERROR + } + f.use_ref(); +} + +fn assign_while_borrowed(mut x: i32) { + let f = || x; + x = 1; //~ ERROR + f.use_ref(); +} + +fn copy_while_borrowed_mut(mut x: i32) { + let f = || x = 0; + let y = x; //~ ERROR + f.use_ref(); +} + +fn borrow_while_borrowed_mut(mut x: i32) { + let f = || x = 0; + let y = &x; //~ ERROR + f.use_ref(); +} + +fn borrow_mut_while_borrowed_mut(mut x: i32) { + let f = || x = 0; + let y = &mut x; //~ ERROR + f.use_ref(); +} + +fn drop_while_borrowed_mut() { + let f; + { + let mut x = 1; + f = || x = 0; //~ ERROR + } + f.use_ref(); +} + +fn assign_while_borrowed_mut(mut x: i32) { + let f = || x = 0; + x = 1; //~ ERROR + f.use_ref(); +} + +fn copy_while_borrowed_unique(x: &mut i32) { + let f = || *x = 0; + let y = x; //~ ERROR + f.use_ref(); +} + +fn borrow_while_borrowed_unique(x: &mut i32) { + let f = || *x = 0; + let y = &x; //~ ERROR + f.use_ref(); +} + +fn borrow_mut_while_borrowed_unique(mut x: &mut i32) { + let f = || *x = 0; + let y = &mut x; //~ ERROR + f.use_ref(); +} + +fn drop_while_borrowed_unique() { + let mut z = 1; + let f; + { + let x = &mut z; + f = || *x = 0; //~ ERROR + } + f.use_ref(); +} + +fn assign_while_borrowed_unique(x: &mut i32) { + let f = || *x = 0; + *x = 1; //~ ERROR + f.use_ref(); +} + +fn main() {} + +trait Fake { fn use_mut(&mut self) { } fn use_ref(&self) { } } +impl<T> Fake for T { } diff --git a/src/test/ui/nll/closure-borrow-spans.stderr b/src/test/ui/nll/closure-borrow-spans.stderr new file mode 100644 index 000000000..bada4e1b8 --- /dev/null +++ b/src/test/ui/nll/closure-borrow-spans.stderr @@ -0,0 +1,172 @@ +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/closure-borrow-spans.rs:5:13 + | +LL | let f = || x.len(); + | -- - borrow occurs due to use in closure + | | + | borrow of `x` occurs here +LL | let y = x; + | ^ move out of `x` occurs here +LL | f.use_ref(); + | ----------- borrow later used here + +error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable + --> $DIR/closure-borrow-spans.rs:11:13 + | +LL | let f = || x; + | -- - first borrow occurs due to use of `x` in closure + | | + | immutable borrow occurs here +LL | let y = &mut x; + | ^^^^^^ mutable borrow occurs here +LL | f.use_ref(); + | ----------- immutable borrow later used here + +error[E0597]: `x` does not live long enough + --> $DIR/closure-borrow-spans.rs:19:16 + | +LL | f = || x; + | -- ^ borrowed value does not live long enough + | | + | value captured here +LL | } + | - `x` dropped here while still borrowed +LL | f.use_ref(); + | ----------- borrow later used here + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/closure-borrow-spans.rs:26:5 + | +LL | let f = || x; + | -- - borrow occurs due to use in closure + | | + | borrow of `x` occurs here +LL | x = 1; + | ^^^^^ assignment to borrowed `x` occurs here +LL | f.use_ref(); + | ----------- borrow later used here + +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/closure-borrow-spans.rs:32:13 + | +LL | let f = || x = 0; + | -- - borrow occurs due to use of `x` in closure + | | + | borrow of `x` occurs here +LL | let y = x; + | ^ use of borrowed `x` +LL | f.use_ref(); + | ----------- borrow later used here + +error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable + --> $DIR/closure-borrow-spans.rs:38:13 + | +LL | let f = || x = 0; + | -- - first borrow occurs due to use of `x` in closure + | | + | mutable borrow occurs here +LL | let y = &x; + | ^^ immutable borrow occurs here +LL | f.use_ref(); + | ----------- mutable borrow later used here + +error[E0499]: cannot borrow `x` as mutable more than once at a time + --> $DIR/closure-borrow-spans.rs:44:13 + | +LL | let f = || x = 0; + | -- - first borrow occurs due to use of `x` in closure + | | + | first mutable borrow occurs here +LL | let y = &mut x; + | ^^^^^^ second mutable borrow occurs here +LL | f.use_ref(); + | ----------- first borrow later used here + +error[E0597]: `x` does not live long enough + --> $DIR/closure-borrow-spans.rs:52:16 + | +LL | f = || x = 0; + | -- ^ borrowed value does not live long enough + | | + | value captured here +LL | } + | - `x` dropped here while still borrowed +LL | f.use_ref(); + | ----------- borrow later used here + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/closure-borrow-spans.rs:59:5 + | +LL | let f = || x = 0; + | -- - borrow occurs due to use in closure + | | + | borrow of `x` occurs here +LL | x = 1; + | ^^^^^ assignment to borrowed `x` occurs here +LL | f.use_ref(); + | ----------- borrow later used here + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/closure-borrow-spans.rs:65:13 + | +LL | let f = || *x = 0; + | -- -- borrow occurs due to use in closure + | | + | borrow of `x` occurs here +LL | let y = x; + | ^ move out of `x` occurs here +LL | f.use_ref(); + | ----------- borrow later used here + +error[E0501]: cannot borrow `x` as immutable because previous closure requires unique access + --> $DIR/closure-borrow-spans.rs:71:13 + | +LL | let f = || *x = 0; + | -- -- first borrow occurs due to use of `x` in closure + | | + | closure construction occurs here +LL | let y = &x; + | ^^ second borrow occurs here +LL | f.use_ref(); + | ----------- first borrow later used here + +error[E0501]: cannot borrow `x` as mutable because previous closure requires unique access + --> $DIR/closure-borrow-spans.rs:77:13 + | +LL | let f = || *x = 0; + | -- -- first borrow occurs due to use of `x` in closure + | | + | closure construction occurs here +LL | let y = &mut x; + | ^^^^^^ second borrow occurs here +LL | f.use_ref(); + | ----------- first borrow later used here + +error[E0597]: `x` does not live long enough + --> $DIR/closure-borrow-spans.rs:86:16 + | +LL | f = || *x = 0; + | -- ^^ borrowed value does not live long enough + | | + | value captured here +LL | } + | - `x` dropped here while still borrowed +LL | f.use_ref(); + | ----------- borrow later used here + +error[E0506]: cannot assign to `*x` because it is borrowed + --> $DIR/closure-borrow-spans.rs:93:5 + | +LL | let f = || *x = 0; + | -- -- borrow occurs due to use in closure + | | + | borrow of `*x` occurs here +LL | *x = 1; + | ^^^^^^ assignment to borrowed `*x` occurs here +LL | f.use_ref(); + | ----------- borrow later used here + +error: aborting due to 14 previous errors + +Some errors have detailed explanations: E0499, E0501, E0502, E0503, E0505, E0506, E0597. +For more information about an error, try `rustc --explain E0499`. diff --git a/src/test/ui/nll/closure-captures.rs b/src/test/ui/nll/closure-captures.rs new file mode 100644 index 000000000..16d90b971 --- /dev/null +++ b/src/test/ui/nll/closure-captures.rs @@ -0,0 +1,55 @@ +// Some cases with closures that might be problems + +// Should have one error per assignment + +fn one_closure(x: i32) { + || + x = 1; //~ ERROR + move || + x = 1; //~ ERROR +} + +fn two_closures(x: i32) { + || { + || + x = 1; //~ ERROR + }; + move || { + || + x = 1; //~ ERROR + }; +} + +fn fn_ref<F: Fn()>(f: F) -> F { f } + +fn two_closures_ref_mut(mut x: i32) { + fn_ref(|| { + || //~ ERROR + x = 1;} + ); + fn_ref(move || { + || //~ ERROR + x = 1;}); +} + +// This still gives two messages, but it requires two things to be fixed. +fn two_closures_ref(x: i32) { + fn_ref(|| { + || //~ ERROR + x = 1;} //~ ERROR + ); + fn_ref(move || { + || //~ ERROR + x = 1;}); //~ ERROR +} + +fn two_closures_two_refs(x: &mut i32) { + fn_ref(|| { + || //~ ERROR + *x = 1;}); + fn_ref(move || { + || //~ ERROR + *x = 1;}); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-captures.stderr b/src/test/ui/nll/closure-captures.stderr new file mode 100644 index 000000000..5233f0b24 --- /dev/null +++ b/src/test/ui/nll/closure-captures.stderr @@ -0,0 +1,148 @@ +error[E0594]: cannot assign to `x`, as it is not declared as mutable + --> $DIR/closure-captures.rs:7:5 + | +LL | fn one_closure(x: i32) { + | - help: consider changing this to be mutable: `mut x` +LL | || +LL | x = 1; + | ^^^^^ cannot assign + +error[E0594]: cannot assign to `x`, as it is not declared as mutable + --> $DIR/closure-captures.rs:9:5 + | +LL | fn one_closure(x: i32) { + | - help: consider changing this to be mutable: `mut x` +... +LL | x = 1; + | ^^^^^ cannot assign + +error[E0594]: cannot assign to `x`, as it is not declared as mutable + --> $DIR/closure-captures.rs:15:9 + | +LL | fn two_closures(x: i32) { + | - help: consider changing this to be mutable: `mut x` +... +LL | x = 1; + | ^^^^^ cannot assign + +error[E0594]: cannot assign to `x`, as it is not declared as mutable + --> $DIR/closure-captures.rs:19:9 + | +LL | fn two_closures(x: i32) { + | - help: consider changing this to be mutable: `mut x` +... +LL | x = 1; + | ^^^^^ cannot assign + +error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure + --> $DIR/closure-captures.rs:27:9 + | +LL | fn fn_ref<F: Fn()>(f: F) -> F { f } + | - change this to accept `FnMut` instead of `Fn` +... +LL | fn_ref(|| { + | ------ -- in this closure + | | + | expects `Fn` instead of `FnMut` +LL | || + | ^^ cannot borrow as mutable +LL | x = 1;} + | - mutable borrow occurs due to use of `x` in closure + +error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure + --> $DIR/closure-captures.rs:31:9 + | +LL | fn fn_ref<F: Fn()>(f: F) -> F { f } + | - change this to accept `FnMut` instead of `Fn` +... +LL | fn_ref(move || { + | ------ ------- in this closure + | | + | expects `Fn` instead of `FnMut` +LL | || + | ^^ cannot borrow as mutable +LL | x = 1;}); + | - mutable borrow occurs due to use of `x` in closure + +error[E0594]: cannot assign to `x`, as it is not declared as mutable + --> $DIR/closure-captures.rs:39:10 + | +LL | fn two_closures_ref(x: i32) { + | - help: consider changing this to be mutable: `mut x` +... +LL | x = 1;} + | ^^^^^ cannot assign + +error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure + --> $DIR/closure-captures.rs:38:9 + | +LL | fn fn_ref<F: Fn()>(f: F) -> F { f } + | - change this to accept `FnMut` instead of `Fn` +... +LL | fn_ref(|| { + | ------ -- in this closure + | | + | expects `Fn` instead of `FnMut` +LL | || + | ^^ cannot borrow as mutable +LL | x = 1;} + | - mutable borrow occurs due to use of `x` in closure + +error[E0594]: cannot assign to `x`, as it is not declared as mutable + --> $DIR/closure-captures.rs:43:5 + | +LL | fn two_closures_ref(x: i32) { + | - help: consider changing this to be mutable: `mut x` +... +LL | x = 1;}); + | ^^^^^ cannot assign + +error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure + --> $DIR/closure-captures.rs:42:9 + | +LL | fn fn_ref<F: Fn()>(f: F) -> F { f } + | - change this to accept `FnMut` instead of `Fn` +... +LL | fn_ref(move || { + | ------ ------- in this closure + | | + | expects `Fn` instead of `FnMut` +LL | || + | ^^ cannot borrow as mutable +LL | x = 1;}); + | - mutable borrow occurs due to use of `x` in closure + +error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure + --> $DIR/closure-captures.rs:48:9 + | +LL | fn fn_ref<F: Fn()>(f: F) -> F { f } + | - change this to accept `FnMut` instead of `Fn` +... +LL | fn_ref(|| { + | ------ -- in this closure + | | + | expects `Fn` instead of `FnMut` +LL | || + | ^^ cannot borrow as mutable +LL | *x = 1;}); + | -- mutable borrow occurs due to use of `x` in closure + +error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure + --> $DIR/closure-captures.rs:51:9 + | +LL | fn fn_ref<F: Fn()>(f: F) -> F { f } + | - change this to accept `FnMut` instead of `Fn` +... +LL | fn_ref(move || { + | ------ ------- in this closure + | | + | expects `Fn` instead of `FnMut` +LL | || + | ^^ cannot borrow as mutable +LL | *x = 1;}); + | -- mutable borrow occurs due to use of `x` in closure + +error: aborting due to 12 previous errors + +Some errors have detailed explanations: E0594, E0596. +For more information about an error, try `rustc --explain E0594`. diff --git a/src/test/ui/nll/closure-move-spans.rs b/src/test/ui/nll/closure-move-spans.rs new file mode 100644 index 000000000..bf2431870 --- /dev/null +++ b/src/test/ui/nll/closure-move-spans.rs @@ -0,0 +1,21 @@ +// check that moves due to a closure capture give a special note + +fn move_after_move(x: String) { + || x; + let y = x; //~ ERROR +} + +fn borrow_after_move(x: String) { + || x; + let y = &x; //~ ERROR +} + +fn borrow_mut_after_move(mut x: String) { + || x; + let y = &mut x; //~ ERROR +} + +fn fn_ref<F: Fn()>(f: F) -> F { f } +fn fn_mut<F: FnMut()>(f: F) -> F { f } + +fn main() {} diff --git a/src/test/ui/nll/closure-move-spans.stderr b/src/test/ui/nll/closure-move-spans.stderr new file mode 100644 index 000000000..0446ef7b0 --- /dev/null +++ b/src/test/ui/nll/closure-move-spans.stderr @@ -0,0 +1,39 @@ +error[E0382]: use of moved value: `x` + --> $DIR/closure-move-spans.rs:5:13 + | +LL | fn move_after_move(x: String) { + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait +LL | || x; + | -- - variable moved due to use in closure + | | + | value moved into closure here +LL | let y = x; + | ^ value used here after move + +error[E0382]: borrow of moved value: `x` + --> $DIR/closure-move-spans.rs:10:13 + | +LL | fn borrow_after_move(x: String) { + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait +LL | || x; + | -- - variable moved due to use in closure + | | + | value moved into closure here +LL | let y = &x; + | ^^ value borrowed here after move + +error[E0382]: borrow of moved value: `x` + --> $DIR/closure-move-spans.rs:15:13 + | +LL | fn borrow_mut_after_move(mut x: String) { + | ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait +LL | || x; + | -- - variable moved due to use in closure + | | + | value moved into closure here +LL | let y = &mut x; + | ^^^^^^ value borrowed here after move + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.rs b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs new file mode 100644 index 000000000..3aea511b0 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs @@ -0,0 +1,42 @@ +// Test closure that: +// +// - takes an argument `y` with lifetime `'a` (in the code, it's anonymous) +// - stores `y` into another, longer-lived spot with lifetime `'b` +// +// Because `'a` and `'b` are two different, unrelated higher-ranked +// regions with no relationship to one another, this is an error. This +// error is reported by the closure itself and is not propagated to +// its creator: this is because `'a` and `'b` are higher-ranked +// (late-bound) regions and the closure is not allowed to propagate +// additional where clauses between higher-ranked regions, only those +// that appear free in its type (hence, we see it before the closure's +// "external requirements" report). + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +#[rustc_regions] +fn test() { + let x = 44; + let mut p = &x; + + { + let y = 22; + let mut closure = expect_sig(|p, y| *p = y); + //~^ ERROR + closure(&mut p, &y); + } + + deref(p); +} + +fn expect_sig<F>(f: F) -> F + where F: FnMut(&mut &i32, &i32) +{ + f +} + +fn deref(_p: &i32) { } + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr new file mode 100644 index 000000000..f86a19fff --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -0,0 +1,37 @@ +note: no external requirements + --> $DIR/escape-argument-callee.rs:26:38 + | +LL | let mut closure = expect_sig(|p, y| *p = y); + | ^^^^^^ + | + = note: defining type: test::{closure#0} with closure substs [ + i16, + for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) i32)), + (), + ] + +error: lifetime may not live long enough + --> $DIR/escape-argument-callee.rs:26:45 + | +LL | let mut closure = expect_sig(|p, y| *p = y); + | - - ^^^^^^ assignment requires that `'1` must outlive `'2` + | | | + | | has type `&'1 i32` + | has type `&'_#2r mut &'2 i32` + +note: no external requirements + --> $DIR/escape-argument-callee.rs:20:1 + | +LL | / fn test() { +LL | | let x = 44; +LL | | let mut p = &x; +LL | | +... | +LL | | deref(p); +LL | | } + | |_^ + | + = note: defining type: test + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/escape-argument.rs b/src/test/ui/nll/closure-requirements/escape-argument.rs new file mode 100644 index 000000000..066cd4360 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument.rs @@ -0,0 +1,42 @@ +// Test closure that: +// +// - takes an argument `y` +// - stores `y` into another, longer-lived spot +// +// but is invoked with a spot that doesn't live long +// enough to store `y`. +// +// The error is reported in the caller: invoking the closure links the +// lifetime of the variable that is given as `y` (via subtyping) and +// thus forces the corresponding borrow to live too long. This is +// basically checking that the MIR type checker correctly enforces the +// closure signature. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +#[rustc_regions] +fn test() { + let x = 44; + let mut p = &x; + + { + let y = 22; + let mut closure = expect_sig(|p, y| *p = y); + closure(&mut p, &y); + //~^ ERROR `y` does not live long enough [E0597] + } + + deref(p); +} + +fn expect_sig<F>(f: F) -> F + where F: for<'a, 'b> FnMut(&'a mut &'b i32, &'b i32) +{ + f +} + +fn deref(_p: &i32) { } + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr new file mode 100644 index 000000000..8cd8b43ca --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -0,0 +1,41 @@ +note: no external requirements + --> $DIR/escape-argument.rs:26:38 + | +LL | let mut closure = expect_sig(|p, y| *p = y); + | ^^^^^^ + | + = note: defining type: test::{closure#0} with closure substs [ + i16, + for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) i32)), + (), + ] + +note: no external requirements + --> $DIR/escape-argument.rs:20:1 + | +LL | / fn test() { +LL | | let x = 44; +LL | | let mut p = &x; +LL | | +... | +LL | | deref(p); +LL | | } + | |_^ + | + = note: defining type: test + +error[E0597]: `y` does not live long enough + --> $DIR/escape-argument.rs:27:25 + | +LL | closure(&mut p, &y); + | ^^ borrowed value does not live long enough +LL | +LL | } + | - `y` dropped here while still borrowed +LL | +LL | deref(p); + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs new file mode 100644 index 000000000..765a3cf96 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs @@ -0,0 +1,33 @@ +// As in `escape-upvar-ref.rs`, test closure that: +// +// - captures a variable `y` +// - stores reference to `y` into another, longer-lived spot +// +// except that the closure does so via a second closure. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +#[rustc_regions] +fn test() { + let x = 44; + let mut p = &x; + + { + let y = 22; + + let mut closure = || { + let mut closure1 = || p = &y; //~ ERROR `y` does not live long enough [E0597] + closure1(); + }; + + closure(); + } + + deref(p); +} + +fn deref(_p: &i32) { } + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr new file mode 100644 index 000000000..abf80e039 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -0,0 +1,59 @@ +note: external requirements + --> $DIR/escape-upvar-nested.rs:21:32 + | +LL | let mut closure1 = || p = &y; + | ^^ + | + = note: defining type: test::{closure#0}::{closure#0} with closure substs [ + i16, + extern "rust-call" fn(()), + (&'_#1r mut &'_#2r i32, &'_#3r i32), + ] + = note: number of external vids: 4 + = note: where '_#3r: '_#2r + +note: external requirements + --> $DIR/escape-upvar-nested.rs:20:27 + | +LL | let mut closure = || { + | ^^ + | + = note: defining type: test::{closure#0} with closure substs [ + i16, + extern "rust-call" fn(()), + (&'_#1r mut &'_#2r i32, &'_#3r i32), + ] + = note: number of external vids: 4 + = note: where '_#3r: '_#2r + +note: no external requirements + --> $DIR/escape-upvar-nested.rs:13:1 + | +LL | / fn test() { +LL | | let x = 44; +LL | | let mut p = &x; +LL | | +... | +LL | | deref(p); +LL | | } + | |_^ + | + = note: defining type: test + +error[E0597]: `y` does not live long enough + --> $DIR/escape-upvar-nested.rs:21:40 + | +LL | let mut closure = || { + | -- value captured here +LL | let mut closure1 = || p = &y; + | ^ borrowed value does not live long enough +... +LL | } + | - `y` dropped here while still borrowed +LL | +LL | deref(p); + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs new file mode 100644 index 000000000..0a562a0a1 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs @@ -0,0 +1,33 @@ +// Test closure that: +// +// - captures a variable `y` by reference +// - stores that reference to `y` into another, longer-lived place (`p`) +// +// Both of these are upvars of reference type (the capture of `y` is +// of type `&'a i32`, the capture of `p` is of type `&mut &'b +// i32`). The closure thus computes a relationship between `'a` and +// `'b`. This relationship is propagated to the closure creator, +// which reports an error. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +#[rustc_regions] +fn test() { + let x = 44; + let mut p = &x; + + { + let y = 22; + let mut closure = || p = &y; + //~^ ERROR `y` does not live long enough [E0597] + closure(); + } + + deref(p); +} + +fn deref(_p: &i32) { } + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr new file mode 100644 index 000000000..bc7546421 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -0,0 +1,45 @@ +note: external requirements + --> $DIR/escape-upvar-ref.rs:23:27 + | +LL | let mut closure = || p = &y; + | ^^ + | + = note: defining type: test::{closure#0} with closure substs [ + i16, + extern "rust-call" fn(()), + (&'_#1r mut &'_#2r i32, &'_#3r i32), + ] + = note: number of external vids: 4 + = note: where '_#3r: '_#2r + +note: no external requirements + --> $DIR/escape-upvar-ref.rs:17:1 + | +LL | / fn test() { +LL | | let x = 44; +LL | | let mut p = &x; +LL | | +... | +LL | | deref(p); +LL | | } + | |_^ + | + = note: defining type: test + +error[E0597]: `y` does not live long enough + --> $DIR/escape-upvar-ref.rs:23:35 + | +LL | let mut closure = || p = &y; + | -- ^ borrowed value does not live long enough + | | + | value captured here +... +LL | } + | - `y` dropped here while still borrowed +LL | +LL | deref(p); + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs b/src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs new file mode 100644 index 000000000..a83ebc21f --- /dev/null +++ b/src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs @@ -0,0 +1,36 @@ +// check-pass + +// Test that we propagate region relations from closures precisely when there is +// more than one non-local lower bound. + +// In this case the closure has signature +// |x: &'4 mut (&'5 (&'1 str, &'2 str), &'3 str)| -> .. +// We end up with a `'3: '5` constraint that we can propagate as +// `'3: '1`, `'3: '2`, but previously we approximated it as `'3: 'static`. + +// As an optimization, we primarily propagate bounds for the "representative" +// of each SCC. As such we have these two similar cases where hopefully one +// of them will test the case we want (case2, when this test was added). +mod case1 { + fn f(s: &str) { + g(s, |x| h(x)); + } + + fn g<T, F>(_: T, _: F) + where F: Fn(&mut (&(T, T), T)) {} + + fn h<T>(_: &mut (&(T, T), T)) {} +} + +mod case2 { + fn f(s: &str) { + g(s, |x| h(x)); + } + + fn g<T, F>(_: T, _: F) + where F: Fn(&mut (T, &(T, T))) {} + + fn h<T>(_: &mut (T, &(T, T))) {} +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs new file mode 100644 index 000000000..35a864b88 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs @@ -0,0 +1,51 @@ +// Test where we fail to approximate due to demanding a postdom +// relationship between our upper bounds. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Callee knows that: +// +// 'x: 'a +// 'x: 'b +// 'c: 'y +// +// we have to prove that `'x: 'y`. We currently can only approximate +// via a postdominator -- hence we fail to choose between `'a` and +// `'b` here and report the error in the closure. +fn establish_relationships<'a, 'b, 'c, F>( + _cell_a: Cell<&'a u32>, + _cell_b: Cell<&'b u32>, + _cell_c: Cell<&'c u32>, + _closure: F, +) where + F: for<'x, 'y> FnMut( + Cell<&'a &'x u32>, // shows that 'x: 'a + Cell<&'b &'x u32>, // shows that 'x: 'b + Cell<&'y &'c u32>, // shows that 'c: 'y + Cell<&'x u32>, + Cell<&'y u32>, + ), +{ +} + +fn demand_y<'x, 'y>(_cell_x: Cell<&'x u32>, _cell_y: Cell<&'y u32>, _y: &'y u32) {} + +#[rustc_regions] +fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) { + establish_relationships( + cell_a, + cell_b, + cell_c, + |_outlives1, _outlives2, _outlives3, x, y| { + // Only works if 'x: 'y: + let p = x.get(); + demand_y(x, y, p) //~ ERROR + }, + ); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr new file mode 100644 index 000000000..b9b0f3ad2 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -0,0 +1,42 @@ +note: no external requirements + --> $DIR/propagate-approximated-fail-no-postdom.rs:43:9 + | +LL | |_outlives1, _outlives2, _outlives3, x, y| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: supply::{closure#0} with closure substs [ + i16, + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>)), + (), + ] + = note: late-bound region is '_#4r + = note: late-bound region is '_#5r + = note: late-bound region is '_#6r + +error: lifetime may not live long enough + --> $DIR/propagate-approximated-fail-no-postdom.rs:46:13 + | +LL | |_outlives1, _outlives2, _outlives3, x, y| { + | ---------- ---------- has type `Cell<&'2 &'_#3r u32>` + | | + | has type `Cell<&'_#1r &'1 u32>` +... +LL | demand_y(x, y, p) + | ^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + +note: no external requirements + --> $DIR/propagate-approximated-fail-no-postdom.rs:38:1 + | +LL | / fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) { +LL | | establish_relationships( +LL | | cell_a, +LL | | cell_b, +... | +LL | | ); +LL | | } + | |_^ + | + = note: defining type: supply + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs new file mode 100644 index 000000000..7291c6e97 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs @@ -0,0 +1,50 @@ +// Rather convoluted setup where we infer a relationship between two +// free regions in the closure signature (`'a` and `'b`) on the basis +// of a relationship between two bound regions (`'x` and `'y`). +// +// The idea is that, thanks to invoking `demand_y`, `'x: 'y` must +// hold, where `'x` and `'y` are bound regions. The closure can't +// prove that directly, and because `'x` and `'y` are bound it cannot +// ask the caller to prove it either. But it has bounds on `'x` and +// `'y` in terms of `'a` and `'b`, and it can propagate a relationship +// between `'a` and `'b` to the caller. +// +// Note: the use of `Cell` here is to introduce invariance. One less +// variable. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Callee knows that: +// +// 'x: 'a +// 'b: 'y +// +// so if we are going to ensure that `'x: 'y`, then `'a: 'b` must +// hold. +fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F) +where + F: for<'x, 'y> FnMut( + &Cell<&'a &'x u32>, // shows that 'x: 'a + &Cell<&'y &'b u32>, // shows that 'b: 'y + &Cell<&'x u32>, + &Cell<&'y u32>, + ), +{ +} + +fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {} + +#[rustc_regions] +fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + // Only works if 'x: 'y: + demand_y(x, y, x.get()) + //~^ ERROR lifetime may not live long enough + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr new file mode 100644 index 000000000..a2371ee31 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -0,0 +1,45 @@ +note: external requirements + --> $DIR/propagate-approximated-ref.rs:43:47 + | +LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: supply::{closure#0} with closure substs [ + i16, + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>)), + (), + ] + = note: late-bound region is '_#3r + = note: late-bound region is '_#4r + = note: number of external vids: 5 + = note: where '_#1r: '_#2r + +note: no external requirements + --> $DIR/propagate-approximated-ref.rs:42:1 + | +LL | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +LL | | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +LL | | // Only works if 'x: 'y: +LL | | demand_y(x, y, x.get()) +LL | | +LL | | }); +LL | | } + | |_^ + | + = note: defining type: supply + +error: lifetime may not live long enough + --> $DIR/propagate-approximated-ref.rs:45:9 + | +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs new file mode 100644 index 000000000..afe6f10a5 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs @@ -0,0 +1,40 @@ +// Test a case where we setup relationships like `'x: 'a` or `'a: 'x`, +// where `'x` is bound in closure type but `'a` is free. This forces +// us to approximate `'x` one way or the other. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +use std::cell::Cell; + +fn foo<'a, F>(_cell: Cell<&'a u32>, _f: F) +where + F: for<'x> FnOnce(Cell<&'a u32>, Cell<&'x u32>), +{ +} + +#[rustc_regions] +fn case1() { + let a = 0; + let cell = Cell::new(&a); + foo(cell, |cell_a, cell_x| { + cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure + //~^ ERROR + }) +} + +#[rustc_regions] +fn case2() { + let a = 0; + let cell = Cell::new(&a); + //~^ ERROR `a` does not live long enough + + // As you can see in the stderr output, this closure propoagates a + // requirement that `'a: 'static'. + foo(cell, |cell_a, cell_x| { + cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error + }) +} + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr new file mode 100644 index 000000000..e53ae167f --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -0,0 +1,81 @@ +note: no external requirements + --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:21:15 + | +LL | foo(cell, |cell_a, cell_x| { + | ^^^^^^^^^^^^^^^^ + | + = note: defining type: case1::{closure#0} with closure substs [ + i32, + for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>)), + (), + ] + +error[E0521]: borrowed data escapes outside of closure + --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:22:9 + | +LL | foo(cell, |cell_a, cell_x| { + | ------ ------ `cell_x` is a reference that is only valid in the closure body + | | + | `cell_a` declared here, outside of the closure body +LL | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure + | ^^^^^^^^^^^^^^^^^^^^^^^^ `cell_x` escapes the closure body here + +note: no external requirements + --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:18:1 + | +LL | / fn case1() { +LL | | let a = 0; +LL | | let cell = Cell::new(&a); +LL | | foo(cell, |cell_a, cell_x| { +... | +LL | | }) +LL | | } + | |_^ + | + = note: defining type: case1 + +note: external requirements + --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:35:15 + | +LL | foo(cell, |cell_a, cell_x| { + | ^^^^^^^^^^^^^^^^ + | + = note: defining type: case2::{closure#0} with closure substs [ + i32, + for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>)), + (), + ] + = note: number of external vids: 2 + = note: where '_#1r: '_#0r + +note: no external requirements + --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:28:1 + | +LL | / fn case2() { +LL | | let a = 0; +LL | | let cell = Cell::new(&a); +LL | | +... | +LL | | }) +LL | | } + | |_^ + | + = note: defining type: case2 + +error[E0597]: `a` does not live long enough + --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:30:26 + | +LL | let cell = Cell::new(&a); + | ^^ borrowed value does not live long enough +... +LL | / foo(cell, |cell_a, cell_x| { +LL | | cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error +LL | | }) + | |______- argument requires that `a` is borrowed for `'static` +LL | } + | - `a` dropped here while still borrowed + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0521, E0597. +For more information about an error, try `rustc --explain E0521`. diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs new file mode 100644 index 000000000..372209075 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs @@ -0,0 +1,40 @@ +// Test a case where we are trying to prove `'x: 'y` and are forced to +// approximate the shorter end-point (`'y`) to with `'static`. This is +// because `'y` is higher-ranked but we know of no relations to other +// regions. Note that `'static` shows up in the stderr output as `'0`. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Callee knows that: +// +// 'x: 'a +// +// so the only way we can ensure that `'x: 'y` is to show that +// `'a: 'static`. +fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F) +where + F: for<'x, 'y> FnMut( + &Cell<&'a &'x u32>, // shows that 'x: 'a + &Cell<&'x u32>, + &Cell<&'y u32>, + ), +{ +} + +fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {} + +#[rustc_regions] +fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + //~^ ERROR borrowed data escapes outside of function + + // Only works if 'x: 'y: + demand_y(x, y, x.get()) + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr new file mode 100644 index 000000000..c3c7eb1bd --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -0,0 +1,55 @@ +note: external requirements + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:32:47 + | +LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + | ^^^^^^^^^^^^^^^^^ + | + = note: defining type: supply::{closure#0} with closure substs [ + i16, + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) u32>)), + (), + ] + = note: late-bound region is '_#2r + = note: late-bound region is '_#3r + = note: number of external vids: 4 + = note: where '_#1r: '_#0r + +note: no external requirements + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:31:1 + | +LL | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +LL | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { +LL | | +LL | | +... | +LL | | }); +LL | | } + | |_^ + | + = note: defining type: supply + +error[E0521]: borrowed data escapes outside of function + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:32:5 + | +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- ------ `cell_a` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +LL | / establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { +LL | | +LL | | +LL | | // Only works if 'x: 'y: +LL | | demand_y(x, y, x.get()) +LL | | }); + | | ^ + | | | + | |______`cell_a` escapes the function body here + | argument requires that `'a` must outlive `'static` + | + = note: requirement occurs because of the type `Cell<&'_#10r u32>`, which makes the generic argument `&'_#10r u32` invariant + = note: the struct `Cell<T>` is invariant over the parameter `T` + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs new file mode 100644 index 000000000..9898777c7 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs @@ -0,0 +1,43 @@ +// Test a case where we are trying to prove `'x: 'y` and are forced to +// approximate the shorter end-point (`'y`) to with `'static`. This is +// because `'y` is higher-ranked but we know of only irrelevant +// relations to other regions. Note that `'static` shows up in the +// stderr output as `'0`. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Callee knows that: +// +// 'x: 'a +// 'y: 'b +// +// so the only way we can ensure that `'x: 'y` is to show that +// `'a: 'static`. +fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F) +where + F: for<'x, 'y> FnMut( + &Cell<&'a &'x u32>, // shows that 'x: 'a + &Cell<&'b &'y u32>, // shows that 'y: 'b + &Cell<&'x u32>, + &Cell<&'y u32>, + ), +{ +} + +fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {} + +#[rustc_regions] +fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + //~^ ERROR borrowed data escapes outside of function + + // Only works if 'x: 'y: + demand_y(x, y, x.get()) + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr new file mode 100644 index 000000000..846e5aedb --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -0,0 +1,55 @@ +note: external requirements + --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:35:47 + | +LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: supply::{closure#0} with closure substs [ + i16, + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>)), + (), + ] + = note: late-bound region is '_#3r + = note: late-bound region is '_#4r + = note: number of external vids: 5 + = note: where '_#1r: '_#0r + +note: no external requirements + --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:34:1 + | +LL | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +LL | | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +LL | | +LL | | +... | +LL | | }); +LL | | } + | |_^ + | + = note: defining type: supply + +error[E0521]: borrowed data escapes outside of function + --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:35:5 + | +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- ------ `cell_a` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +LL | | +LL | | +LL | | // Only works if 'x: 'y: +LL | | demand_y(x, y, x.get()) +LL | | }); + | | ^ + | | | + | |______`cell_a` escapes the function body here + | argument requires that `'a` must outlive `'static` + | + = note: requirement occurs because of the type `Cell<&'_#11r u32>`, which makes the generic argument `&'_#11r u32` invariant + = note: the struct `Cell<T>` is invariant over the parameter `T` + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0521`. diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs new file mode 100644 index 000000000..5bb5eea99 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs @@ -0,0 +1,43 @@ +// A simpler variant of `outlives-from-argument` where cells are +// passed by value. +// +// This is simpler because there are no "extraneous" region +// relationships. In the 'main' variant, there are a number of +// anonymous regions as well. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Callee knows that: +// +// 'x: 'a +// 'b: 'y +// +// so if we are going to ensure that `'x: 'y`, then `'a: 'b` must +// hold. +fn establish_relationships<'a, 'b, F>(_cell_a: Cell<&'a u32>, _cell_b: Cell<&'b u32>, _closure: F) +where + F: for<'x, 'y> FnMut( + Cell<&'a &'x u32>, // shows that 'x: 'a + Cell<&'y &'b u32>, // shows that 'b: 'y + Cell<&'x u32>, + Cell<&'y u32>, + ), +{ +} + +fn demand_y<'x, 'y>(_outlives1: Cell<&&'x u32>, _outlives2: Cell<&'y &u32>, _y: &'y u32) {} + +#[rustc_regions] +fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { + // Only works if 'x: 'y: + demand_y(outlives1, outlives2, x.get()) + //~^ ERROR lifetime may not live long enough + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr new file mode 100644 index 000000000..a570932ed --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -0,0 +1,45 @@ +note: external requirements + --> $DIR/propagate-approximated-val.rs:36:45 + | +LL | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: test::{closure#0} with closure substs [ + i16, + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>)), + (), + ] + = note: late-bound region is '_#3r + = note: late-bound region is '_#4r + = note: number of external vids: 5 + = note: where '_#1r: '_#2r + +note: no external requirements + --> $DIR/propagate-approximated-val.rs:35:1 + | +LL | / fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +LL | | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { +LL | | // Only works if 'x: 'y: +LL | | demand_y(outlives1, outlives2, x.get()) +LL | | +LL | | }); +LL | | } + | |_^ + | + = note: defining type: test + +error: lifetime may not live long enough + --> $DIR/propagate-approximated-val.rs:38:9 + | +LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | demand_y(outlives1, outlives2, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs new file mode 100644 index 000000000..704a026d2 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs @@ -0,0 +1,50 @@ +// Test where we might in theory be able to see that the relationship +// between two bound regions is true within closure and hence have no +// need to propagate; but in fact we do because identity of free +// regions is erased. + +// compile-flags:-Zverbose +// check-pass + +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// In theory, callee knows that: +// +// 'x: 'a +// 'a: 'y +// +// and hence could satisfy that `'x: 'y` locally. However, in our +// checking, we ignore the precise free regions that come into the +// region and just assign each position a distinct universally bound +// region. Hence, we propagate a constraint to our caller that will +// wind up being solvable. +fn establish_relationships<'a, F>( + _cell_a: Cell<&'a u32>, + _closure: F, +) where + F: for<'x, 'y> FnMut( + Cell<&'a &'x u32>, // shows that 'x: 'a + Cell<&'y &'a u32>, // shows that 'a: 'y + Cell<&'x u32>, + Cell<&'y u32>, + ), +{ +} + +fn demand_y<'x, 'y>(_cell_x: Cell<&'x u32>, _cell_y: Cell<&'y u32>, _y: &'y u32) {} + +#[rustc_regions] +fn supply<'a>(cell_a: Cell<&'a u32>) { + establish_relationships( + cell_a, + |_outlives1, _outlives2, x, y| { + // Only works if 'x: 'y: + let p = x.get(); + demand_y(x, y, p) + }, + ); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr new file mode 100644 index 000000000..407bc6764 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr @@ -0,0 +1,29 @@ +note: external requirements + --> $DIR/propagate-despite-same-free-region.rs:42:9 + | +LL | |_outlives1, _outlives2, x, y| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: supply::{closure#0} with closure substs [ + i16, + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>)), + (), + ] + = note: late-bound region is '_#3r + = note: number of external vids: 4 + = note: where '_#1r: '_#2r + +note: no external requirements + --> $DIR/propagate-despite-same-free-region.rs:39:1 + | +LL | / fn supply<'a>(cell_a: Cell<&'a u32>) { +LL | | establish_relationships( +LL | | cell_a, +LL | | |_outlives1, _outlives2, x, y| { +... | +LL | | ); +LL | | } + | |_^ + | + = note: defining type: supply + diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs new file mode 100644 index 000000000..dcd05d7fa --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs @@ -0,0 +1,42 @@ +// Similarly to escape-argument-callee, a test case where the closure +// requires a relationship between 2 unrelated higher-ranked regions, +// with no helpful relations between the HRRs and free regions. +// +// In this case, the error is reported by the closure itself. This is +// because it is unable to approximate the higher-ranked region `'x`, +// as it knows of no relationships between `'x` and any +// non-higher-ranked regions. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Callee knows that: +// +// 'b: 'y +// +// but this doesn't really help us in proving that `'x: 'y`, so closure gets an error. +fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F) +where + F: for<'x, 'y> FnMut( + &Cell<&'y &'b u32>, // shows that 'b: 'y + &Cell<&'x u32>, + &Cell<&'y u32>, + ), +{ +} + +fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {} + +#[rustc_regions] +fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + // Only works if 'x: 'y: + demand_y(x, y, x.get()) + //~^ ERROR + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr new file mode 100644 index 000000000..fcb55d37f --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -0,0 +1,41 @@ +note: no external requirements + --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:35:47 + | +LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + | ^^^^^^^^^^^^^^^^^ + | + = note: defining type: supply::{closure#0} with closure substs [ + i16, + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>)), + (), + ] + = note: late-bound region is '_#2r + = note: late-bound region is '_#3r + +error: lifetime may not live long enough + --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:37:9 + | +LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + | --------- - has type `&'_#7r Cell<&'1 u32>` + | | + | has type `&'_#5r Cell<&'2 &'_#1r u32>` +LL | // Only works if 'x: 'y: +LL | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + +note: no external requirements + --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:34:1 + | +LL | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +LL | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { +LL | | // Only works if 'x: 'y: +LL | | demand_y(x, y, x.get()) +LL | | +LL | | }); +LL | | } + | |_^ + | + = note: defining type: supply + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs new file mode 100644 index 000000000..98be92d1c --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs @@ -0,0 +1,46 @@ +// Similarly to escape-argument-callee, a test case where the closure +// requires a relationship between 2 unrelated higher-ranked regions, +// with no helpful relations between the HRRs and free regions. +// +// In this case, the error is reported by the closure itself. This is +// because it is unable to approximate the higher-ranked region `'x`, +// as it only knows of regions that `'x` is outlived by, and none that +// `'x` outlives. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Callee knows that: +// +// 'a: 'x +// 'b: 'y +// +// but this doesn't really help us in proving that `'x: 'y`, so +// closure gets an error. In particular, we would need to know that +// `'x: 'a`, so that we could approximate `'x` "downwards" to `'a`. +fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F) +where + F: for<'x, 'y> FnMut( + &Cell<&'x &'a u32>, // shows that 'a: 'x + &Cell<&'y &'b u32>, // shows that 'b: 'y + &Cell<&'x u32>, + &Cell<&'y u32>, + ), +{ +} + +fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {} + +#[rustc_regions] +fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + // Only works if 'x: 'y: + demand_y(x, y, x.get()) + //~^ ERROR + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr new file mode 100644 index 000000000..75beae39e --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -0,0 +1,41 @@ +note: no external requirements + --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:39:47 + | +LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: supply::{closure#0} with closure substs [ + i16, + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>)), + (), + ] + = note: late-bound region is '_#3r + = note: late-bound region is '_#4r + +error: lifetime may not live long enough + --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:41:9 + | +LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + | ---------- ---------- has type `&'_#8r Cell<&'2 &'_#2r u32>` + | | + | has type `&'_#6r Cell<&'1 &'_#1r u32>` +LL | // Only works if 'x: 'y: +LL | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + +note: no external requirements + --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:38:1 + | +LL | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +LL | | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +LL | | // Only works if 'x: 'y: +LL | | demand_y(x, y, x.get()) +LL | | +LL | | }); +LL | | } + | |_^ + | + = note: defining type: supply + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs new file mode 100644 index 000000000..3bdb54339 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs @@ -0,0 +1,49 @@ +// Test that regions which appear only in the closure's generics (in +// this case, `'a`) are properly mapped to the creator's generics. In +// this case, the closure constrains its type parameter `T` to outlive +// the same `'a` for which it implements `Trait`, which can only be the `'a` +// from the function definition. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +trait Trait<'a> {} + +fn establish_relationships<T, F>(value: T, closure: F) +where + F: FnOnce(T), +{ + closure(value) +} + +fn require<'a, T>(t: T) +where + T: Trait<'a> + 'a, +{ +} + +#[rustc_regions] +fn supply<'a, T>(value: T) +where + T: Trait<'a>, +{ + establish_relationships(value, |value| { + //~^ ERROR the parameter type `T` may not live long enough + + // This function call requires that + // + // (a) T: Trait<'a> + // + // and + // + // (b) T: 'a + // + // The latter does not hold. + + require(value); + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr new file mode 100644 index 000000000..58aced2bf --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -0,0 +1,49 @@ +note: external requirements + --> $DIR/propagate-from-trait-match.rs:32:36 + | +LL | establish_relationships(value, |value| { + | ^^^^^^^ + | + = note: defining type: supply::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((T,)), + (), + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +note: no external requirements + --> $DIR/propagate-from-trait-match.rs:28:1 + | +LL | / fn supply<'a, T>(value: T) +LL | | where +LL | | T: Trait<'a>, +LL | | { +... | +LL | | }); +LL | | } + | |_^ + | + = note: defining type: supply::<'_#1r, T> + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/propagate-from-trait-match.rs:32:36 + | +LL | establish_relationships(value, |value| { + | ____________________________________^ +LL | | +LL | | +LL | | // This function call requires that +... | +LL | | require(value); +LL | | }); + | |_____^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: Trait<'a> + 'a, + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/closure-requirements/propagate-multiple-requirements.rs b/src/test/ui/nll/closure-requirements/propagate-multiple-requirements.rs new file mode 100644 index 000000000..a9d2a0771 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-multiple-requirements.rs @@ -0,0 +1,23 @@ +// Test that we propagate *all* requirements to the caller, not just the first +// one. + +fn once<S, T, U, F: FnOnce(S, T) -> U>(f: F, s: S, t: T) -> U { + f(s, t) +} + +pub fn dangle() -> &'static [i32] { + let other_local_arr = [0, 2, 4]; + let local_arr = other_local_arr; + let mut out: &mut &'static [i32] = &mut (&[1] as _); + once(|mut z: &[i32], mut out_val: &mut &[i32]| { + // We unfortunately point to the first use in the closure in the error + // message + z = &local_arr; //~ ERROR + *out_val = &local_arr; + }, &[] as &[_], &mut *out); + *out +} + +fn main() { + println!("{:?}", dangle()); +} diff --git a/src/test/ui/nll/closure-requirements/propagate-multiple-requirements.stderr b/src/test/ui/nll/closure-requirements/propagate-multiple-requirements.stderr new file mode 100644 index 000000000..2fec9bc62 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-multiple-requirements.stderr @@ -0,0 +1,17 @@ +error[E0597]: `local_arr` does not live long enough + --> $DIR/propagate-multiple-requirements.rs:15:14 + | +LL | let mut out: &mut &'static [i32] = &mut (&[1] as _); + | ------------------- type annotation requires that `local_arr` is borrowed for `'static` +LL | once(|mut z: &[i32], mut out_val: &mut &[i32]| { + | ----------------------------------------- value captured here +... +LL | z = &local_arr; + | ^^^^^^^^^ borrowed value does not live long enough +... +LL | } + | - `local_arr` 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/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs new file mode 100644 index 000000000..8147da09d --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs @@ -0,0 +1,13 @@ +// Basic test for free regions in the NLL code. This test ought to +// report an error due to a reborrowing constraint. Right now, we get +// a variety of errors from the older, AST-based machinery (notably +// borrowck), and then we get the NLL error at the end. + +// compile-flags:-Zverbose + +fn foo(x: &u32) -> &'static u32 { + &*x + //~^ ERROR lifetime may not live long enough +} + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr new file mode 100644 index 000000000..7034492ce --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/region-lbr-anon-does-not-outlive-static.rs:9:5 + | +LL | fn foo(x: &u32) -> &'static u32 { + | - let's call the lifetime of this reference `'1` +LL | &*x + | ^^^ returning this value requires that `'1` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs new file mode 100644 index 000000000..4acd2fc92 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs @@ -0,0 +1,13 @@ +// Basic test for free regions in the NLL code. This test ought to +// report an error due to a reborrowing constraint. Right now, we get +// a variety of errors from the older, AST-based machinery (notably +// borrowck), and then we get the NLL error at the end. + +// compile-flags:-Zverbose + +fn foo<'a>(x: &'a u32) -> &'static u32 { + &*x + //~^ ERROR +} + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr new file mode 100644 index 000000000..d0a24a267 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/region-lbr-named-does-not-outlive-static.rs:9:5 + | +LL | fn foo<'a>(x: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | &*x + | ^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs new file mode 100644 index 000000000..06e96be80 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs @@ -0,0 +1,13 @@ +// Basic test for free regions in the NLL code. This test ought to +// report an error due to a reborrowing constraint. Right now, we get +// a variety of errors from the older, AST-based machinery (notably +// borrowck), and then we get the NLL error at the end. + +// compile-flags:-Zverbose + +fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 { + &*x + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr new file mode 100644 index 000000000..d0ba53925 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/region-lbr1-does-not-outlive-ebr2.rs:9:5 + | +LL | fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | &*x + | ^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs b/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs new file mode 100644 index 000000000..014959fdb --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs @@ -0,0 +1,11 @@ +// Basic test for free regions in the NLL code. This test does not +// report an error because of the (implied) bound that `'b: 'a`. + +// check-pass +// compile-flags:-Zverbose + +fn foo<'a, 'b>(x: &'a &'b u32) -> &'a u32 { + &**x +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs new file mode 100644 index 000000000..e34a3f6f2 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs @@ -0,0 +1,23 @@ +// Test closure that takes two references and is supposed to return +// the first, but actually returns the second. This should fail within +// the closure. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +#[rustc_regions] +fn test() { + expect_sig(|a, b| b); // ought to return `a` + //~^ ERROR +} + +fn expect_sig<F>(f: F) -> F + where F: for<'a> FnMut(&'a i32, &i32) -> &'a i32 +{ + f +} + +fn deref(_p: &i32) { } + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr new file mode 100644 index 000000000..1c9d0c835 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -0,0 +1,34 @@ +note: no external requirements + --> $DIR/return-wrong-bound-region.rs:11:16 + | +LL | expect_sig(|a, b| b); // ought to return `a` + | ^^^^^^ + | + = note: defining type: test::{closure#0} with closure substs [ + i16, + for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) i32)) -> &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) i32, + (), + ] + +error: lifetime may not live long enough + --> $DIR/return-wrong-bound-region.rs:11:23 + | +LL | expect_sig(|a, b| b); // ought to return `a` + | - - ^ closure was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | | | + | | has type `&'1 i32` + | has type `&'2 i32` + +note: no external requirements + --> $DIR/return-wrong-bound-region.rs:10:1 + | +LL | / fn test() { +LL | | expect_sig(|a, b| b); // ought to return `a` +LL | | +LL | | } + | |_^ + | + = note: defining type: test + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-use-spans.rs b/src/test/ui/nll/closure-use-spans.rs new file mode 100644 index 000000000..6768250dc --- /dev/null +++ b/src/test/ui/nll/closure-use-spans.rs @@ -0,0 +1,21 @@ +// check that liveness due to a closure capture gives a special note + +fn use_as_borrow_capture(mut x: i32) { + let y = &x; + x = 0; //~ ERROR + || *y; +} + +fn use_as_borrow_mut_capture(mut x: i32) { + let y = &mut x; + x = 0; //~ ERROR + || *y = 1; +} + +fn use_as_move_capture(mut x: i32) { + let y = &x; + x = 0; //~ ERROR + move || *y; +} + +fn main() {} diff --git a/src/test/ui/nll/closure-use-spans.stderr b/src/test/ui/nll/closure-use-spans.stderr new file mode 100644 index 000000000..ad928f1bb --- /dev/null +++ b/src/test/ui/nll/closure-use-spans.stderr @@ -0,0 +1,33 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/closure-use-spans.rs:5:5 + | +LL | let y = &x; + | -- borrow of `x` occurs here +LL | x = 0; + | ^^^^^ assignment to borrowed `x` occurs here +LL | || *y; + | -- borrow later captured here by closure + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/closure-use-spans.rs:11:5 + | +LL | let y = &mut x; + | ------ borrow of `x` occurs here +LL | x = 0; + | ^^^^^ assignment to borrowed `x` occurs here +LL | || *y = 1; + | -- borrow later captured here by closure + +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/closure-use-spans.rs:17:5 + | +LL | let y = &x; + | -- borrow of `x` occurs here +LL | x = 0; + | ^^^^^ assignment to borrowed `x` occurs here +LL | move || *y; + | -- borrow later captured here by closure + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/closures-in-loops.rs b/src/test/ui/nll/closures-in-loops.rs new file mode 100644 index 000000000..491c186ec --- /dev/null +++ b/src/test/ui/nll/closures-in-loops.rs @@ -0,0 +1,24 @@ +// Test messages where a closure capture conflicts with itself because it's in +// a loop. + +fn repreated_move(x: String) { + for i in 0..10 { + || x; //~ ERROR + } +} + +fn repreated_mut_borrow(mut x: String) { + let mut v = Vec::new(); + for i in 0..10 { + v.push(|| x = String::new()); //~ ERROR + } +} + +fn repreated_unique_borrow(x: &mut String) { + let mut v = Vec::new(); + for i in 0..10 { + v.push(|| *x = String::new()); //~ ERROR + } +} + +fn main() {} diff --git a/src/test/ui/nll/closures-in-loops.stderr b/src/test/ui/nll/closures-in-loops.stderr new file mode 100644 index 000000000..2be0460df --- /dev/null +++ b/src/test/ui/nll/closures-in-loops.stderr @@ -0,0 +1,31 @@ +error[E0382]: use of moved value: `x` + --> $DIR/closures-in-loops.rs:6:9 + | +LL | fn repreated_move(x: String) { + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait +LL | for i in 0..10 { +LL | || x; + | ^^ - use occurs due to use in closure + | | + | value moved into closure here, in previous iteration of loop + +error[E0499]: cannot borrow `x` as mutable more than once at a time + --> $DIR/closures-in-loops.rs:13:16 + | +LL | v.push(|| x = String::new()); + | ^^ - borrows occur due to use of `x` in closure + | | + | `x` was mutably borrowed here in the previous iteration of the loop + +error[E0524]: two closures require unique access to `x` at the same time + --> $DIR/closures-in-loops.rs:20:16 + | +LL | v.push(|| *x = String::new()); + | ^^ -- borrows occur due to use of `x` in closure + | | + | closures are constructed here in different iterations of loop + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0382, E0499, E0524. +For more information about an error, try `rustc --explain E0382`. diff --git a/src/test/ui/nll/constant-thread-locals-issue-47053.rs b/src/test/ui/nll/constant-thread-locals-issue-47053.rs new file mode 100644 index 000000000..dde0ef7a5 --- /dev/null +++ b/src/test/ui/nll/constant-thread-locals-issue-47053.rs @@ -0,0 +1,10 @@ +// Regression test for issue #47053 + +#![feature(thread_local)] + +#[thread_local] +static FOO: isize = 5; + +fn main() { + FOO = 6; //~ ERROR cannot assign to immutable static item `FOO` [E0594] +} diff --git a/src/test/ui/nll/constant-thread-locals-issue-47053.stderr b/src/test/ui/nll/constant-thread-locals-issue-47053.stderr new file mode 100644 index 000000000..a44acfb5f --- /dev/null +++ b/src/test/ui/nll/constant-thread-locals-issue-47053.stderr @@ -0,0 +1,9 @@ +error[E0594]: cannot assign to immutable static item `FOO` + --> $DIR/constant-thread-locals-issue-47053.rs:9:5 + | +LL | FOO = 6; + | ^^^^^^^ cannot assign + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/nll/constant.rs b/src/test/ui/nll/constant.rs new file mode 100644 index 000000000..47f0eadf9 --- /dev/null +++ b/src/test/ui/nll/constant.rs @@ -0,0 +1,10 @@ +// Test that MIR borrowck and NLL analysis can handle constants of +// arbitrary types without ICEs. + +// check-pass + +const HI: &str = "hi"; + +fn main() { + assert_eq!(HI, "hi"); +} diff --git a/src/test/ui/nll/continue-after-missing-main.rs b/src/test/ui/nll/continue-after-missing-main.rs new file mode 100644 index 000000000..778639158 --- /dev/null +++ b/src/test/ui/nll/continue-after-missing-main.rs @@ -0,0 +1,29 @@ +#![allow(dead_code)] + +struct Tableau<'a, MP> { + provider: &'a MP, +} + +impl<'adapted_matrix_provider, 'original_data, MP> + Tableau<'adapted_matrix_provider, AdaptedMatrixProvider<'original_data, MP>> +{ + fn provider(&self) -> &'adapted_matrix_provider AdaptedMatrixProvider</*'original_data,*/ MP> { + self.provider + } +} + +struct AdaptedMatrixProvider<'a, T> { + original_problem: &'a T, +} + +impl<'a, T> AdaptedMatrixProvider<'a, T> { + fn clone_with_extra_bound(&self) -> Self { + AdaptedMatrixProvider { original_problem: self.original_problem } + } +} + +fn create_and_solve_subproblems<'data_provider, 'original_data, MP>( + tableau: Tableau<'data_provider, AdaptedMatrixProvider<'original_data, MP>>, +) { + let _: AdaptedMatrixProvider<'original_data, MP> = tableau.provider().clone_with_extra_bound(); +} //~ ERROR `main` function not found in crate diff --git a/src/test/ui/nll/continue-after-missing-main.stderr b/src/test/ui/nll/continue-after-missing-main.stderr new file mode 100644 index 000000000..0df8d8d70 --- /dev/null +++ b/src/test/ui/nll/continue-after-missing-main.stderr @@ -0,0 +1,9 @@ +error[E0601]: `main` function not found in crate `continue_after_missing_main` + --> $DIR/continue-after-missing-main.rs:29:2 + | +LL | } + | ^ consider adding a `main` function to `$DIR/continue-after-missing-main.rs` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0601`. diff --git a/src/test/ui/nll/decl-macro-illegal-copy.rs b/src/test/ui/nll/decl-macro-illegal-copy.rs new file mode 100644 index 000000000..f72432946 --- /dev/null +++ b/src/test/ui/nll/decl-macro-illegal-copy.rs @@ -0,0 +1,28 @@ +// Regression test for #46314 + +#![feature(decl_macro)] + +struct NonCopy(String); + +struct Wrapper { + inner: NonCopy, +} + +macro inner_copy($wrapper:ident) { + $wrapper.inner +} + +fn main() { + let wrapper = Wrapper { + inner: NonCopy("foo".into()), + }; + assert_two_non_copy( + inner_copy!(wrapper), + wrapper.inner, + //~^ ERROR use of moved value: `wrapper.inner` [E0382] + ); +} + +fn assert_two_non_copy(a: NonCopy, b: NonCopy) { + assert_eq!(a.0, b.0); +} diff --git a/src/test/ui/nll/decl-macro-illegal-copy.stderr b/src/test/ui/nll/decl-macro-illegal-copy.stderr new file mode 100644 index 000000000..7948485bd --- /dev/null +++ b/src/test/ui/nll/decl-macro-illegal-copy.stderr @@ -0,0 +1,14 @@ +error[E0382]: use of moved value: `wrapper.inner` + --> $DIR/decl-macro-illegal-copy.rs:21:9 + | +LL | $wrapper.inner + | -------------- value moved here +... +LL | wrapper.inner, + | ^^^^^^^^^^^^^ value used here after move + | + = note: move occurs because `wrapper.inner` has type `NonCopy`, 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/nll/do-not-ignore-lifetime-bounds-in-copy-proj.rs b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.rs new file mode 100644 index 000000000..96c871946 --- /dev/null +++ b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.rs @@ -0,0 +1,12 @@ +// Test that the 'static bound from the Copy impl is respected. Regression test for #29149. + +#[derive(Clone)] +struct Foo<'a>(&'a u32); +impl Copy for Foo<'static> {} + +fn main() { + let s = 2; + let a = (Foo(&s),); //~ ERROR `s` does not live long enough [E0597] + drop(a.0); + drop(a.0); +} diff --git a/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.stderr b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.stderr new file mode 100644 index 000000000..65be3b37e --- /dev/null +++ b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.stderr @@ -0,0 +1,14 @@ +error[E0597]: `s` does not live long enough + --> $DIR/do-not-ignore-lifetime-bounds-in-copy-proj.rs:9:18 + | +LL | let a = (Foo(&s),); + | ^^ borrowed value does not live long enough +LL | drop(a.0); + | --- copying this value requires that `s` is borrowed for `'static` +LL | drop(a.0); +LL | } + | - `s` 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/nll/do-not-ignore-lifetime-bounds-in-copy.rs b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy.rs new file mode 100644 index 000000000..99922cc51 --- /dev/null +++ b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy.rs @@ -0,0 +1,11 @@ +// Test that the 'static bound from the Copy impl is respected. Regression test for #29149. + +#[derive(Clone)] struct Foo<'a>(&'a u32); +impl Copy for Foo<'static> {} + +fn main() { + let s = 2; + let a = Foo(&s); //~ ERROR `s` does not live long enough [E0597] + drop(a); + drop(a); +} diff --git a/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy.stderr b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy.stderr new file mode 100644 index 000000000..b811ba4fd --- /dev/null +++ b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy.stderr @@ -0,0 +1,14 @@ +error[E0597]: `s` does not live long enough + --> $DIR/do-not-ignore-lifetime-bounds-in-copy.rs:8:17 + | +LL | let a = Foo(&s); + | ^^ borrowed value does not live long enough +LL | drop(a); + | - copying this value requires that `s` is borrowed for `'static` +LL | drop(a); +LL | } + | - `s` 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/nll/dont-print-desugared.rs b/src/test/ui/nll/dont-print-desugared.rs new file mode 100644 index 000000000..829d78ed4 --- /dev/null +++ b/src/test/ui/nll/dont-print-desugared.rs @@ -0,0 +1,21 @@ +// Test that we don't show variables with from for loop desugaring + +fn for_loop(s: &[i32]) { + for &ref mut x in s {} + //~^ ERROR cannot borrow data in a `&` reference as mutable [E0596] +} + +struct D<'a>(&'a ()); + +impl Drop for D<'_> { + fn drop(&mut self) {} +} + +fn for_loop_dropck(v: Vec<D<'static>>) { + for ref mut d in v { + let y = (); + *d = D(&y); //~ ERROR `y` does not live long enough + } +} + +fn main() {} diff --git a/src/test/ui/nll/dont-print-desugared.stderr b/src/test/ui/nll/dont-print-desugared.stderr new file mode 100644 index 000000000..fad6121cb --- /dev/null +++ b/src/test/ui/nll/dont-print-desugared.stderr @@ -0,0 +1,24 @@ +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/dont-print-desugared.rs:4:10 + | +LL | for &ref mut x in s {} + | ^^^^^^^^^ cannot borrow as mutable + +error[E0597]: `y` does not live long enough + --> $DIR/dont-print-desugared.rs:17:16 + | +LL | for ref mut d in v { + | - a temporary with access to the borrow is created here ... +LL | let y = (); +LL | *d = D(&y); + | ^^ borrowed value does not live long enough +LL | } + | - + | | + | `y` dropped here while still borrowed + | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0596, E0597. +For more information about an error, try `rustc --explain E0596`. diff --git a/src/test/ui/nll/drop-may-dangle.rs b/src/test/ui/nll/drop-may-dangle.rs new file mode 100644 index 000000000..b5531c29b --- /dev/null +++ b/src/test/ui/nll/drop-may-dangle.rs @@ -0,0 +1,34 @@ +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` includes the points after `&v[0]` up to (but not +// including) the call to `use_x`. The `else` branch is not included. + +// check-pass + +#![allow(warnings)] +#![feature(dropck_eyepatch)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p: WrapMayDangle<& /* R4 */ usize> = WrapMayDangle { value: &v[0] }; + if true { + // `p` will get dropped at end of this block. However, because of + // the `#[may_dangle]` attribute, we do not need to consider R4 + // live after this point. + use_x(*p.value); + } else { + v[0] += 1; + use_x(22); + } + + v[0] += 1; +} + +struct WrapMayDangle<T> { + value: T +} + +unsafe impl<#[may_dangle] T> Drop for WrapMayDangle<T> { + fn drop(&mut self) { } +} diff --git a/src/test/ui/nll/drop-no-may-dangle.rs b/src/test/ui/nll/drop-no-may-dangle.rs new file mode 100644 index 000000000..a0ff0c398 --- /dev/null +++ b/src/test/ui/nll/drop-no-may-dangle.rs @@ -0,0 +1,30 @@ +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` must include everything until `p` is dropped +// because of destructor. (Note that the stderr also identifies this +// destructor in the error message.) + +#![allow(warnings)] +#![feature(dropck_eyepatch)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] }; + if true { + use_x(*p.value); + } else { + use_x(22); + v[0] += 1; //~ ERROR cannot assign to `v[_]` because it is borrowed + } + + v[0] += 1; //~ ERROR cannot assign to `v[_]` because it is borrowed +} + +struct WrapMayNotDangle<T> { + value: T +} + +impl<T> Drop for WrapMayNotDangle<T> { + fn drop(&mut self) { } +} diff --git a/src/test/ui/nll/drop-no-may-dangle.stderr b/src/test/ui/nll/drop-no-may-dangle.stderr new file mode 100644 index 000000000..cb2808809 --- /dev/null +++ b/src/test/ui/nll/drop-no-may-dangle.stderr @@ -0,0 +1,26 @@ +error[E0506]: cannot assign to `v[_]` because it is borrowed + --> $DIR/drop-no-may-dangle.rs:18:9 + | +LL | let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] }; + | ----- borrow of `v[_]` occurs here +... +LL | v[0] += 1; + | ^^^^^^^^^ assignment to borrowed `v[_]` occurs here +... +LL | } + | - borrow might be used here, when `p` is dropped and runs the `Drop` code for type `WrapMayNotDangle` + +error[E0506]: cannot assign to `v[_]` because it is borrowed + --> $DIR/drop-no-may-dangle.rs:21:5 + | +LL | let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] }; + | ----- borrow of `v[_]` occurs here +... +LL | v[0] += 1; + | ^^^^^^^^^ assignment to borrowed `v[_]` occurs here +LL | } + | - borrow might be used here, when `p` is dropped and runs the `Drop` code for type `WrapMayNotDangle` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/empty-type-predicate-2.rs b/src/test/ui/nll/empty-type-predicate-2.rs new file mode 100644 index 000000000..20d6e47f7 --- /dev/null +++ b/src/test/ui/nll/empty-type-predicate-2.rs @@ -0,0 +1,18 @@ +// Regression test for #65553 +// +// `D::Error:` is lowered to `D::Error: ReEmpty` - check that we don't ICE in +// NLL for the unexpected region. + +// check-pass + +trait Deserializer { + type Error; +} + +fn d1<D: Deserializer>() where D::Error: {} + +fn d2<D: Deserializer>() { + d1::<D>(); +} + +fn main() {} diff --git a/src/test/ui/nll/empty-type-predicate.rs b/src/test/ui/nll/empty-type-predicate.rs new file mode 100644 index 000000000..d126a455d --- /dev/null +++ b/src/test/ui/nll/empty-type-predicate.rs @@ -0,0 +1,11 @@ +// Regression test for #61315 +// +// `dyn T:` is lowered to `dyn T: ReEmpty` - check that we don't ICE in NLL for +// the unexpected region. + +// check-pass + +trait T {} +fn f() where dyn T: {} + +fn main() { f(); } diff --git a/src/test/ui/nll/enum-drop-access.rs b/src/test/ui/nll/enum-drop-access.rs new file mode 100644 index 000000000..5ef0c3fe7 --- /dev/null +++ b/src/test/ui/nll/enum-drop-access.rs @@ -0,0 +1,49 @@ +enum DropOption<T> { + Some(T), + None, +} + +impl<T> Drop for DropOption<T> { + fn drop(&mut self) {} +} + +// Dropping opt could access the value behind the reference, +fn drop_enum(opt: DropOption<&mut i32>) -> Option<&mut i32> { + match opt { + DropOption::Some(&mut ref mut r) => { //~ ERROR + Some(r) + }, + DropOption::None => None, + } +} + +fn optional_drop_enum(opt: Option<DropOption<&mut i32>>) -> Option<&mut i32> { + match opt { + Some(DropOption::Some(&mut ref mut r)) => { //~ ERROR + Some(r) + }, + Some(DropOption::None) | None => None, + } +} + +// Ok, dropping opt doesn't access the reference +fn optional_tuple(opt: Option<(&mut i32, String)>) -> Option<&mut i32> { + match opt { + Some((&mut ref mut r, _)) => { + Some(r) + }, + None => None, + } +} + +// Ok, dropping res doesn't access the Ok case. +fn different_variants(res: Result<&mut i32, String>) -> Option<&mut i32> { + match res { + Ok(&mut ref mut r) => { + Some(r) + }, + Err(_) => None, + } +} + +fn main() {} diff --git a/src/test/ui/nll/enum-drop-access.stderr b/src/test/ui/nll/enum-drop-access.stderr new file mode 100644 index 000000000..a532ae121 --- /dev/null +++ b/src/test/ui/nll/enum-drop-access.stderr @@ -0,0 +1,31 @@ +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/enum-drop-access.rs:13:31 + | +LL | fn drop_enum(opt: DropOption<&mut i32>) -> Option<&mut i32> { + | - let's call the lifetime of this reference `'1` +LL | match opt { +LL | DropOption::Some(&mut ref mut r) => { + | ^^^^^^^^^ +LL | Some(r) + | ------- returning this value requires that `*opt.0` is borrowed for `'1` +... +LL | } + | - here, drop of `opt` needs exclusive access to `*opt.0`, because the type `DropOption<&mut i32>` implements the `Drop` trait + +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/enum-drop-access.rs:22:36 + | +LL | fn optional_drop_enum(opt: Option<DropOption<&mut i32>>) -> Option<&mut i32> { + | - let's call the lifetime of this reference `'1` +LL | match opt { +LL | Some(DropOption::Some(&mut ref mut r)) => { + | ^^^^^^^^^ +LL | Some(r) + | ------- returning this value requires that `*opt.0.0` is borrowed for `'1` +... +LL | } + | - here, drop of `opt` needs exclusive access to `*opt.0.0`, because the type `DropOption<&mut i32>` implements the `Drop` trait + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0713`. diff --git a/src/test/ui/nll/extra-unused-mut.rs b/src/test/ui/nll/extra-unused-mut.rs new file mode 100644 index 000000000..340f2952a --- /dev/null +++ b/src/test/ui/nll/extra-unused-mut.rs @@ -0,0 +1,66 @@ +// extra unused mut lint tests for #51918 + +// check-pass + +#![feature(generators)] +#![deny(unused_mut)] + +fn ref_argument(ref _y: i32) {} + +// #51801 +fn mutable_upvar() { + let mut x = 0; + move || { + x = 1; + }; +} + +// #50897 +fn generator_mutable_upvar() { + let mut x = 0; + move || { + x = 1; + yield; + }; +} + +// #51830 +fn ref_closure_argument() { + let _ = Some(0).as_ref().map(|ref _a| true); +} + +struct Expr { + attrs: Vec<u32>, +} + +// #51904 +fn parse_dot_or_call_expr_with(mut attrs: Vec<u32>) { + let x = Expr { attrs: vec![] }; + Some(Some(x)).map(|expr| + expr.map(|mut expr| { + attrs.push(666); + expr.attrs = attrs; + expr + }) + ); +} + +// Found when trying to bootstrap rustc +fn if_guard(x: Result<i32, i32>) { + match x { + Ok(mut r) | Err(mut r) if true => r = 1, + _ => (), + } +} + +// #59620 +fn nested_closures() { + let mut i = 0; + [].iter().for_each(|_: &i32| { + [].iter().for_each(move |_: &i32| { + i += 1; + }); + }); +} + +fn main() {} diff --git a/src/test/ui/nll/generator-distinct-lifetime.rs b/src/test/ui/nll/generator-distinct-lifetime.rs new file mode 100644 index 000000000..90fe6b569 --- /dev/null +++ b/src/test/ui/nll/generator-distinct-lifetime.rs @@ -0,0 +1,25 @@ +#![feature(generators)] + +// Test for issue #47189. Here, both `s` and `t` are live for the +// generator's lifetime, but within the generator they have distinct +// lifetimes. We accept this code -- even though the borrow extends +// over a yield -- because the data that is borrowed (`*x`) is not +// stored on the stack. + +// check-pass + +fn foo(x: &mut u32) { + move || { + let s = &mut *x; + yield; + *s += 1; + + let t = &mut *x; + yield; + *t += 1; + }; +} + +fn main() { + foo(&mut 0); +} diff --git a/src/test/ui/nll/generator-upvar-mutability.rs b/src/test/ui/nll/generator-upvar-mutability.rs new file mode 100644 index 000000000..c49ea15b8 --- /dev/null +++ b/src/test/ui/nll/generator-upvar-mutability.rs @@ -0,0 +1,14 @@ +// Check that generators respect the muatability of their upvars. + +#![feature(generators)] + +fn mutate_upvar() { + let x = 0; + move || { + x = 1; + //~^ ERROR + yield; + }; +} + +fn main() {} diff --git a/src/test/ui/nll/generator-upvar-mutability.stderr b/src/test/ui/nll/generator-upvar-mutability.stderr new file mode 100644 index 000000000..31b061b61 --- /dev/null +++ b/src/test/ui/nll/generator-upvar-mutability.stderr @@ -0,0 +1,12 @@ +error[E0594]: cannot assign to `x`, as it is not declared as mutable + --> $DIR/generator-upvar-mutability.rs:8:9 + | +LL | let x = 0; + | - help: consider changing this to be mutable: `mut x` +LL | move || { +LL | x = 1; + | ^^^^^ cannot assign + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/nll/get_default.polonius.stderr b/src/test/ui/nll/get_default.polonius.stderr new file mode 100644 index 000000000..476d86cfb --- /dev/null +++ b/src/test/ui/nll/get_default.polonius.stderr @@ -0,0 +1,18 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:32:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --- immutable borrow occurs here +LL | Some(v) => { +LL | map.set(String::new()); // Both AST and MIR error here + | ^^^ mutable borrow occurs here +LL | +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/nll/get_default.rs b/src/test/ui/nll/get_default.rs new file mode 100644 index 000000000..ffac8a33d --- /dev/null +++ b/src/test/ui/nll/get_default.rs @@ -0,0 +1,44 @@ +// Basic test for free regions in the NLL code. This test ought to +// report an error due to a reborrowing constraint. Right now, we get +// a variety of errors from the older, AST-based machinery (notably +// borrowck), and then we get the NLL error at the end. + +struct Map { +} + +impl Map { + fn get(&self) -> Option<&String> { None } + fn set(&mut self, v: String) { } +} + +fn ok(map: &mut Map) -> &String { + loop { + match map.get() { + Some(v) => { + return v; + } + None => { + map.set(String::new()); // Ideally, this would not error. + //~^ ERROR borrowed as immutable + } + } + } +} + +fn err(map: &mut Map) -> &String { + loop { + match map.get() { + Some(v) => { + map.set(String::new()); // Both AST and MIR error here + //~^ ERROR borrowed as immutable + return v; + } + None => { + map.set(String::new()); // Ideally, just AST would error here + //~^ ERROR borrowed as immutable + } + } + } +} + +fn main() { } diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr new file mode 100644 index 000000000..6998c0433 --- /dev/null +++ b/src/test/ui/nll/get_default.stderr @@ -0,0 +1,48 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:21:17 + | +LL | fn ok(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --------- immutable borrow occurs here +LL | Some(v) => { +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` +... +LL | map.set(String::new()); // Ideally, this would not error. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:32:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --------- immutable borrow occurs here +LL | Some(v) => { +LL | map.set(String::new()); // Both AST and MIR error here + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:37:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --------- immutable borrow occurs here +... +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` +... +LL | map.set(String::new()); // Ideally, just AST would error here + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/nll/guarantor-issue-46974.rs b/src/test/ui/nll/guarantor-issue-46974.rs new file mode 100644 index 000000000..96af4bf5c --- /dev/null +++ b/src/test/ui/nll/guarantor-issue-46974.rs @@ -0,0 +1,19 @@ +// Test that NLL analysis propagates lifetimes correctly through +// field accesses, Box accesses, etc. + +fn foo(s: &mut (i32,)) -> i32 { + let t = &mut *s; // this borrow should last for the entire function + let x = &t.0; + *s = (2,); //~ ERROR cannot assign to `*s` + *x +} + +fn bar(s: &Box<(i32,)>) -> &'static i32 { + // FIXME(#46983): error message should be better + &s.0 //~ ERROR lifetime may not live long enough +} + +fn main() { + foo(&mut (0,)); + bar(&Box::new((1,))); +} diff --git a/src/test/ui/nll/guarantor-issue-46974.stderr b/src/test/ui/nll/guarantor-issue-46974.stderr new file mode 100644 index 000000000..8854dd8d6 --- /dev/null +++ b/src/test/ui/nll/guarantor-issue-46974.stderr @@ -0,0 +1,23 @@ +error[E0506]: cannot assign to `*s` because it is borrowed + --> $DIR/guarantor-issue-46974.rs:7:5 + | +LL | let t = &mut *s; // this borrow should last for the entire function + | ------- borrow of `*s` occurs here +LL | let x = &t.0; +LL | *s = (2,); + | ^^^^^^^^^ assignment to borrowed `*s` occurs here +LL | *x + | -- borrow later used here + +error: lifetime may not live long enough + --> $DIR/guarantor-issue-46974.rs:13:5 + | +LL | fn bar(s: &Box<(i32,)>) -> &'static i32 { + | - let's call the lifetime of this reference `'1` +LL | // FIXME(#46983): error message should be better +LL | &s.0 + | ^^^^ returning this value requires that `'1` must outlive `'static` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/issue-16223.rs b/src/test/ui/nll/issue-16223.rs new file mode 100644 index 000000000..0ae0ed3d8 --- /dev/null +++ b/src/test/ui/nll/issue-16223.rs @@ -0,0 +1,52 @@ +// Regression test for #16223: without NLL the `if let` construct together with +// the nested box-structure of `Root` causes an unwanted collateral move. + +// The exact error prevented here is: +// +// error[E0382]: use of collaterally moved value: `(root.boxed.rhs as SomeVariant::B).0` +// --> src/main.rs:55:29 +// | +// 56 | lhs: SomeVariant::A(a), +// | - value moved here +// 57 | rhs: SomeVariant::B(b), +// | ^ value used here after move +// | +// = note: move occurs because the value has type `A`, which does not implement the `Copy` trait + +// check-pass + +#![feature(box_patterns)] + +struct Root { + boxed: Box<SetOfVariants>, +} + +struct SetOfVariants { + lhs: SomeVariant, + rhs: SomeVariant, +} + +enum SomeVariant { + A(A), + B(B), +} + +struct A(String); +struct B(String); + +fn main() { + let root = Root { + boxed: Box::new(SetOfVariants { + lhs: SomeVariant::A(A(String::from("This is A"))), + rhs: SomeVariant::B(B(String::from("This is B"))), + }), + }; + if let box SetOfVariants { + lhs: SomeVariant::A(a), + rhs: SomeVariant::B(b), + } = root.boxed + { + println!("a = {}", a.0); + println!("b = {}", b.0); + } +} diff --git a/src/test/ui/nll/issue-21114-ebfull.rs b/src/test/ui/nll/issue-21114-ebfull.rs new file mode 100644 index 000000000..fc4a6845a --- /dev/null +++ b/src/test/ui/nll/issue-21114-ebfull.rs @@ -0,0 +1,18 @@ +// check-pass + +use std::collections::HashMap; +use std::sync::Mutex; + +fn i_used_to_be_able_to(foo: &Mutex<HashMap<usize, usize>>) -> Vec<(usize, usize)> { + let mut foo = foo.lock().unwrap(); + + foo.drain().collect() +} + +fn but_after_nightly_update_now_i_gotta(foo: &Mutex<HashMap<usize, usize>>) -> Vec<(usize, usize)> { + let mut foo = foo.lock().unwrap(); + + return foo.drain().collect(); +} + +fn main() {} diff --git a/src/test/ui/nll/issue-21114-kixunil.rs b/src/test/ui/nll/issue-21114-kixunil.rs new file mode 100644 index 000000000..666f89f35 --- /dev/null +++ b/src/test/ui/nll/issue-21114-kixunil.rs @@ -0,0 +1,17 @@ +// check-pass + +fn from_stdin(min: u64) -> Vec<u64> { + use std::io::BufRead; + + let stdin = std::io::stdin(); + let stdin = stdin.lock(); + + stdin.lines() + .map(Result::unwrap) + .map(|val| val.parse()) + .map(Result::unwrap) + .filter(|val| *val >= min) + .collect() +} + +fn main() {} diff --git a/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.rs b/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.rs new file mode 100644 index 000000000..46a156d2a --- /dev/null +++ b/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.rs @@ -0,0 +1,60 @@ +// This test enumerates various cases of interest where an ADT or tuple is +// partially initialized and then used in some way that is wrong *even* +// after rust-lang/rust#54987 is implemented. +// +// See rust-lang/rust#21232, rust-lang/rust#54986, and rust-lang/rust#54987. +// +// See issue-21232-partial-init-and-use.rs for cases of tests that are +// meant to compile and run successfully once rust-lang/rust#54987 is +// implemented. + +struct D { + x: u32, + s: S, +} + +struct S { + y: u32, + z: u32, +} + + +impl Drop for D { + fn drop(&mut self) { } +} + +fn cannot_partially_init_adt_with_drop() { + let d: D; + d.x = 10; //~ ERROR E0381 +} + +fn cannot_partially_init_mutable_adt_with_drop() { + let mut d: D; + d.x = 10; //~ ERROR E0381 +} + +fn cannot_partially_reinit_adt_with_drop() { + let mut d = D { x: 0, s: S{ y: 0, z: 0 } }; + drop(d); + d.x = 10; + //~^ ERROR assign of moved value: `d` [E0382] +} + +fn cannot_partially_init_inner_adt_via_outer_with_drop() { + let d: D; + d.s.y = 20; //~ ERROR E0381 +} + +fn cannot_partially_init_inner_adt_via_mutable_outer_with_drop() { + let mut d: D; + d.s.y = 20; //~ ERROR E0381 +} + +fn cannot_partially_reinit_inner_adt_via_outer_with_drop() { + let mut d = D { x: 0, s: S{ y: 0, z: 0} }; + drop(d); + d.s.y = 20; + //~^ ERROR assign to part of moved value: `d` [E0382] +} + +fn main() { } diff --git a/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr b/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr new file mode 100644 index 000000000..63f230be7 --- /dev/null +++ b/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr @@ -0,0 +1,64 @@ +error[E0381]: assigned binding `d` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:28:5 + | +LL | let d: D; + | - binding declared here but left uninitialized +LL | d.x = 10; + | ^^^^^^^^ `d` assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: assigned binding `d` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:33:5 + | +LL | let mut d: D; + | ----- binding declared here but left uninitialized +LL | d.x = 10; + | ^^^^^^^^ `d` assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0382]: assign of moved value: `d` + --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:39:5 + | +LL | let mut d = D { x: 0, s: S{ y: 0, z: 0 } }; + | ----- move occurs because `d` has type `D`, which does not implement the `Copy` trait +LL | drop(d); + | - value moved here +LL | d.x = 10; + | ^^^^^^^^ value assigned here after move + +error[E0381]: partially assigned binding `d` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:45:5 + | +LL | let d: D; + | - binding declared here but left uninitialized +LL | d.s.y = 20; + | ^^^^^^^^^^ `d.s` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `d` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:50:5 + | +LL | let mut d: D; + | ----- binding declared here but left uninitialized +LL | d.s.y = 20; + | ^^^^^^^^^^ `d.s` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0382]: assign to part of moved value: `d` + --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:56:5 + | +LL | let mut d = D { x: 0, s: S{ y: 0, z: 0} }; + | ----- move occurs because `d` has type `D`, which does not implement the `Copy` trait +LL | drop(d); + | - value moved here +LL | d.s.y = 20; + | ^^^^^^^^^^ value partially assigned here after move + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0381, E0382. +For more information about an error, try `rustc --explain E0381`. diff --git a/src/test/ui/nll/issue-21232-partial-init-and-use.rs b/src/test/ui/nll/issue-21232-partial-init-and-use.rs new file mode 100644 index 000000000..4cd1e406f --- /dev/null +++ b/src/test/ui/nll/issue-21232-partial-init-and-use.rs @@ -0,0 +1,297 @@ +// This test enumerates various cases of interest for partial +// [re]initialization of ADTs and tuples. +// +// See rust-lang/rust#21232, rust-lang/rust#54986, and rust-lang/rust#54987. +// +// All of tests in this file are expected to change from being +// rejected, at least under NLL (by rust-lang/rust#54986) to being +// **accepted** when rust-lang/rust#54987 is implemented. +// (That's why there are assertions in the code.) +// +// See issue-21232-partial-init-and-erroneous-use.rs for cases of +// tests that are meant to continue failing to compile once +// rust-lang/rust#54987 is implemented. + +struct S<Y> { + x: u32, + + // Note that even though `y` may implement `Drop`, under #54987 we + // will still allow partial initialization of `S` itself. + y: Y, +} + +enum Void { } + +type B = Box<u32>; + +impl S<B> { fn new() -> Self { S { x: 0, y: Box::new(0) } } } + +fn borrow_s(s: &S<B>) { assert_eq!(s.x, 10); assert_eq!(*s.y, 20); } +fn move_s(s: S<B>) { assert_eq!(s.x, 10); assert_eq!(*s.y, 20); } +fn borrow_field(x: &u32) { assert_eq!(*x, 10); } + +type T = (u32, B); +type Tvoid = (u32, Void); + +fn borrow_t(t: &T) { assert_eq!(t.0, 10); assert_eq!(*t.1, 20); } +fn move_t(t: T) { assert_eq!(t.0, 10); assert_eq!(*t.1, 20); } + +struct Q<F> { + v: u32, + r: R<F>, +} + +struct R<F> { + w: u32, + f: F, +} + +impl<F> Q<F> { fn new(f: F) -> Self { Q { v: 0, r: R::new(f) } } } +impl<F> R<F> { fn new(f: F) -> Self { R { w: 0, f } } } + +// Axes to cover: +// * local/field: Is the structure in a local or a field +// * fully/partial/void: Are we fully initializing it before using any part? +// Is whole type empty due to a void component? +// * init/reinit: First initialization, or did we previously inititalize and then move out? +// * struct/tuple: Is this a struct or a (X, Y). +// +// As a shorthand for the cases above, adding a numeric summary to +// each test's fn name to denote each point on each axis. +// +// e.g., 1000 = field fully init struct; 0211 = local void reinit tuple + +// It got pretty monotonous writing the same code over and over, and I +// feared I would forget details. So I abstracted some desiderata into +// macros. But I left the initialization code inline, because that's +// where the errors for #54986 will be emitted. + +macro_rules! use_fully { + (struct $s:expr) => { { + borrow_field(& $s.x ); + borrow_s(& $s ); + move_s( $s ); + } }; + + (tuple $t:expr) => { { + borrow_field(& $t.0 ); + borrow_t(& $t ); + move_t( $t ); + } } +} + +macro_rules! use_part { + (struct $s:expr) => { { + borrow_field(& $s.x ); + match $s { S { ref x, y: _ } => { borrow_field(x); } } + } }; + + (tuple $t:expr) => { { + borrow_field(& $t.0 ); + match $t { (ref x, _) => { borrow_field(x); } } + } } +} + +fn test_0000_local_fully_init_and_use_struct() { + let s: S<B>; + s.x = 10; s.y = Box::new(20); //~ ERROR E0381 + use_fully!(struct s); +} + +fn test_0001_local_fully_init_and_use_tuple() { + let t: T; + t.0 = 10; t.1 = Box::new(20); //~ ERROR E0381 + use_fully!(tuple t); +} + +fn test_0010_local_fully_reinit_and_use_struct() { + let mut s: S<B> = S::new(); drop(s); + s.x = 10; s.y = Box::new(20); + //~^ ERROR assign to part of moved value: `s` [E0382] + use_fully!(struct s); +} + +fn test_0011_local_fully_reinit_and_use_tuple() { + let mut t: T = (0, Box::new(0)); drop(t); + t.0 = 10; t.1 = Box::new(20); + //~^ ERROR assign to part of moved value: `t` [E0382] + use_fully!(tuple t); +} + +fn test_0100_local_partial_init_and_use_struct() { + let s: S<B>; + s.x = 10; //~ ERROR E0381 + use_part!(struct s); +} + +fn test_0101_local_partial_init_and_use_tuple() { + let t: T; + t.0 = 10; //~ ERROR E0381 + use_part!(tuple t); +} + +fn test_0110_local_partial_reinit_and_use_struct() { + let mut s: S<B> = S::new(); drop(s); + s.x = 10; + //~^ ERROR assign to part of moved value: `s` [E0382] + use_part!(struct s); +} + +fn test_0111_local_partial_reinit_and_use_tuple() { + let mut t: T = (0, Box::new(0)); drop(t); + t.0 = 10; + //~^ ERROR assign to part of moved value: `t` [E0382] + use_part!(tuple t); +} + +fn test_0200_local_void_init_and_use_struct() { + let s: S<Void>; + s.x = 10; //~ ERROR E0381 + use_part!(struct s); +} + +fn test_0201_local_void_init_and_use_tuple() { + let t: Tvoid; + t.0 = 10; //~ ERROR E0381 + use_part!(tuple t); +} + +// NOTE: uniform structure of tests here makes n21n (aka combining +// Void with Reinit) an (even more) senseless case, as we cannot +// safely create initial instance containing Void to move out of and +// then reinitialize. While I was tempted to sidestep this via some +// unsafe code (eek), lets just instead not encode such tests. + +// fn test_0210_local_void_reinit_and_use_struct() { unimplemented!() } +// fn test_0211_local_void_reinit_and_use_tuple() { unimplemented!() } + +fn test_1000_field_fully_init_and_use_struct() { + let q: Q<S<B>>; + q.r.f.x = 10; q.r.f.y = Box::new(20); //~ ERROR E0381 + use_fully!(struct q.r.f); +} + +fn test_1001_field_fully_init_and_use_tuple() { + let q: Q<T>; + q.r.f.0 = 10; q.r.f.1 = Box::new(20); //~ ERROR E0381 + use_fully!(tuple q.r.f); +} + +fn test_1010_field_fully_reinit_and_use_struct() { + let mut q: Q<S<B>> = Q::new(S::new()); drop(q.r); + q.r.f.x = 10; q.r.f.y = Box::new(20); + //~^ ERROR assign to part of moved value: `q.r` [E0382] + use_fully!(struct q.r.f); +} + +fn test_1011_field_fully_reinit_and_use_tuple() { + let mut q: Q<T> = Q::new((0, Box::new(0))); drop(q.r); + q.r.f.0 = 10; q.r.f.1 = Box::new(20); + //~^ ERROR assign to part of moved value: `q.r` [E0382] + use_fully!(tuple q.r.f); +} + +fn test_1100_field_partial_init_and_use_struct() { + let q: Q<S<B>>; + q.r.f.x = 10; //~ ERROR E0381 + use_part!(struct q.r.f); +} + +fn test_1101_field_partial_init_and_use_tuple() { + let q: Q<T>; + q.r.f.0 = 10; //~ ERROR E0381 + use_part!(tuple q.r.f); +} + +fn test_1110_field_partial_reinit_and_use_struct() { + let mut q: Q<S<B>> = Q::new(S::new()); drop(q.r); + q.r.f.x = 10; + //~^ ERROR assign to part of moved value: `q.r` [E0382] + use_part!(struct q.r.f); +} + +fn test_1111_field_partial_reinit_and_use_tuple() { + let mut q: Q<T> = Q::new((0, Box::new(0))); drop(q.r); + q.r.f.0 = 10; + //~^ ERROR assign to part of moved value: `q.r` [E0382] + use_part!(tuple q.r.f); +} + +fn test_1200_field_void_init_and_use_struct() { + let mut q: Q<S<Void>>; + q.r.f.x = 10; //~ ERROR E0381 + use_part!(struct q.r.f); +} + +fn test_1201_field_void_init_and_use_tuple() { + let mut q: Q<Tvoid>; + q.r.f.0 = 10; //~ ERROR E0381 + use_part!(tuple q.r.f); +} + +// See NOTE abve. + +// fn test_1210_field_void_reinit_and_use_struct() { unimplemented!() } +// fn test_1211_field_void_reinit_and_use_tuple() { unimplemented!() } + +// The below are some additional cases of interest that have been +// transcribed from other bugs based on old erroneous codegen when we +// encountered partial writes. + +fn issue_26996() { + let mut c = (1, "".to_owned()); + match c { + c2 => { + c.0 = 2; //~ ERROR assign to part of moved value + assert_eq!(c2.0, 1); + } + } +} + +fn issue_27021() { + let mut c = (1, (1, "".to_owned())); + match c { + c2 => { + (c.1).0 = 2; //~ ERROR assign to part of moved value + assert_eq!((c2.1).0, 1); + } + } + + let mut c = (1, (1, (1, "".to_owned()))); + match c.1 { + c2 => { + ((c.1).1).0 = 3; //~ ERROR assign to part of moved value + assert_eq!((c2.1).0, 1); + } + } +} + +fn main() { + test_0000_local_fully_init_and_use_struct(); + test_0001_local_fully_init_and_use_tuple(); + test_0010_local_fully_reinit_and_use_struct(); + test_0011_local_fully_reinit_and_use_tuple(); + test_0100_local_partial_init_and_use_struct(); + test_0101_local_partial_init_and_use_tuple(); + test_0110_local_partial_reinit_and_use_struct(); + test_0111_local_partial_reinit_and_use_tuple(); + test_0200_local_void_init_and_use_struct(); + test_0201_local_void_init_and_use_tuple(); + // test_0210_local_void_reinit_and_use_struct(); + // test_0211_local_void_reinit_and_use_tuple(); + test_1000_field_fully_init_and_use_struct(); + test_1001_field_fully_init_and_use_tuple(); + test_1010_field_fully_reinit_and_use_struct(); + test_1011_field_fully_reinit_and_use_tuple(); + test_1100_field_partial_init_and_use_struct(); + test_1101_field_partial_init_and_use_tuple(); + test_1110_field_partial_reinit_and_use_struct(); + test_1111_field_partial_reinit_and_use_tuple(); + test_1200_field_void_init_and_use_struct(); + test_1201_field_void_init_and_use_tuple(); + // test_1210_field_void_reinit_and_use_struct(); + // test_1211_field_void_reinit_and_use_tuple(); + + issue_26996(); + issue_27021(); +} diff --git a/src/test/ui/nll/issue-21232-partial-init-and-use.stderr b/src/test/ui/nll/issue-21232-partial-init-and-use.stderr new file mode 100644 index 000000000..947c9e29b --- /dev/null +++ b/src/test/ui/nll/issue-21232-partial-init-and-use.stderr @@ -0,0 +1,236 @@ +error[E0381]: partially assigned binding `s` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:97:5 + | +LL | let s: S<B>; + | - binding declared here but left uninitialized +LL | s.x = 10; s.y = Box::new(20); + | ^^^^^^^^ `s` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `t` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:103:5 + | +LL | let t: T; + | - binding declared here but left uninitialized +LL | t.0 = 10; t.1 = Box::new(20); + | ^^^^^^^^ `t` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0382]: assign to part of moved value: `s` + --> $DIR/issue-21232-partial-init-and-use.rs:109:5 + | +LL | let mut s: S<B> = S::new(); drop(s); + | ----- - value moved here + | | + | move occurs because `s` has type `S<Box<u32>>`, which does not implement the `Copy` trait +LL | s.x = 10; s.y = Box::new(20); + | ^^^^^^^^ value partially assigned here after move + +error[E0382]: assign to part of moved value: `t` + --> $DIR/issue-21232-partial-init-and-use.rs:116:5 + | +LL | let mut t: T = (0, Box::new(0)); drop(t); + | ----- - value moved here + | | + | move occurs because `t` has type `(u32, Box<u32>)`, which does not implement the `Copy` trait +LL | t.0 = 10; t.1 = Box::new(20); + | ^^^^^^^^ value partially assigned here after move + +error[E0381]: partially assigned binding `s` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:123:5 + | +LL | let s: S<B>; + | - binding declared here but left uninitialized +LL | s.x = 10; + | ^^^^^^^^ `s` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `t` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:129:5 + | +LL | let t: T; + | - binding declared here but left uninitialized +LL | t.0 = 10; + | ^^^^^^^^ `t` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0382]: assign to part of moved value: `s` + --> $DIR/issue-21232-partial-init-and-use.rs:135:5 + | +LL | let mut s: S<B> = S::new(); drop(s); + | ----- - value moved here + | | + | move occurs because `s` has type `S<Box<u32>>`, which does not implement the `Copy` trait +LL | s.x = 10; + | ^^^^^^^^ value partially assigned here after move + +error[E0382]: assign to part of moved value: `t` + --> $DIR/issue-21232-partial-init-and-use.rs:142:5 + | +LL | let mut t: T = (0, Box::new(0)); drop(t); + | ----- - value moved here + | | + | move occurs because `t` has type `(u32, Box<u32>)`, which does not implement the `Copy` trait +LL | t.0 = 10; + | ^^^^^^^^ value partially assigned here after move + +error[E0381]: partially assigned binding `s` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:149:5 + | +LL | let s: S<Void>; + | - binding declared here but left uninitialized +LL | s.x = 10; + | ^^^^^^^^ `s` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `t` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:155:5 + | +LL | let t: Tvoid; + | - binding declared here but left uninitialized +LL | t.0 = 10; + | ^^^^^^^^ `t` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `q` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:170:5 + | +LL | let q: Q<S<B>>; + | - binding declared here but left uninitialized +LL | q.r.f.x = 10; q.r.f.y = Box::new(20); + | ^^^^^^^^^^^^ `q.r.f` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `q` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:176:5 + | +LL | let q: Q<T>; + | - binding declared here but left uninitialized +LL | q.r.f.0 = 10; q.r.f.1 = Box::new(20); + | ^^^^^^^^^^^^ `q.r.f` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0382]: assign to part of moved value: `q.r` + --> $DIR/issue-21232-partial-init-and-use.rs:182:5 + | +LL | let mut q: Q<S<B>> = Q::new(S::new()); drop(q.r); + | --- value moved here +LL | q.r.f.x = 10; q.r.f.y = Box::new(20); + | ^^^^^^^^^^^^ value partially assigned here after move + | + = note: move occurs because `q.r` has type `R<S<Box<u32>>>`, which does not implement the `Copy` trait + +error[E0382]: assign to part of moved value: `q.r` + --> $DIR/issue-21232-partial-init-and-use.rs:189:5 + | +LL | let mut q: Q<T> = Q::new((0, Box::new(0))); drop(q.r); + | --- value moved here +LL | q.r.f.0 = 10; q.r.f.1 = Box::new(20); + | ^^^^^^^^^^^^ value partially assigned here after move + | + = note: move occurs because `q.r` has type `R<(u32, Box<u32>)>`, which does not implement the `Copy` trait + +error[E0381]: partially assigned binding `q` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:196:5 + | +LL | let q: Q<S<B>>; + | - binding declared here but left uninitialized +LL | q.r.f.x = 10; + | ^^^^^^^^^^^^ `q.r.f` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `q` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:202:5 + | +LL | let q: Q<T>; + | - binding declared here but left uninitialized +LL | q.r.f.0 = 10; + | ^^^^^^^^^^^^ `q.r.f` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0382]: assign to part of moved value: `q.r` + --> $DIR/issue-21232-partial-init-and-use.rs:208:5 + | +LL | let mut q: Q<S<B>> = Q::new(S::new()); drop(q.r); + | --- value moved here +LL | q.r.f.x = 10; + | ^^^^^^^^^^^^ value partially assigned here after move + | + = note: move occurs because `q.r` has type `R<S<Box<u32>>>`, which does not implement the `Copy` trait + +error[E0382]: assign to part of moved value: `q.r` + --> $DIR/issue-21232-partial-init-and-use.rs:215:5 + | +LL | let mut q: Q<T> = Q::new((0, Box::new(0))); drop(q.r); + | --- value moved here +LL | q.r.f.0 = 10; + | ^^^^^^^^^^^^ value partially assigned here after move + | + = note: move occurs because `q.r` has type `R<(u32, Box<u32>)>`, which does not implement the `Copy` trait + +error[E0381]: partially assigned binding `q` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:222:5 + | +LL | let mut q: Q<S<Void>>; + | ----- binding declared here but left uninitialized +LL | q.r.f.x = 10; + | ^^^^^^^^^^^^ `q.r.f` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: partially assigned binding `q` isn't fully initialized + --> $DIR/issue-21232-partial-init-and-use.rs:228:5 + | +LL | let mut q: Q<Tvoid>; + | ----- binding declared here but left uninitialized +LL | q.r.f.0 = 10; + | ^^^^^^^^^^^^ `q.r.f` partially assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0382]: assign to part of moved value: `c` + --> $DIR/issue-21232-partial-init-and-use.rs:245:13 + | +LL | let mut c = (1, "".to_owned()); + | ----- move occurs because `c` has type `(i32, String)`, which does not implement the `Copy` trait +LL | match c { +LL | c2 => { + | -- value moved here +LL | c.0 = 2; + | ^^^^^^^ value partially assigned here after move + +error[E0382]: assign to part of moved value: `c` + --> $DIR/issue-21232-partial-init-and-use.rs:255:13 + | +LL | let mut c = (1, (1, "".to_owned())); + | ----- move occurs because `c` has type `(i32, (i32, String))`, which does not implement the `Copy` trait +LL | match c { +LL | c2 => { + | -- value moved here +LL | (c.1).0 = 2; + | ^^^^^^^^^^^ value partially assigned here after move + +error[E0382]: assign to part of moved value: `c.1` + --> $DIR/issue-21232-partial-init-and-use.rs:263:13 + | +LL | c2 => { + | -- value moved here +LL | ((c.1).1).0 = 3; + | ^^^^^^^^^^^^^^^ value partially assigned here after move + | + = note: move occurs because `c.1` has type `(i32, (i32, String))`, which does not implement the `Copy` trait + +error: aborting due to 23 previous errors + +Some errors have detailed explanations: E0381, E0382. +For more information about an error, try `rustc --explain E0381`. diff --git a/src/test/ui/nll/issue-22323-temp-destruction.rs b/src/test/ui/nll/issue-22323-temp-destruction.rs new file mode 100644 index 000000000..3f2ece1cf --- /dev/null +++ b/src/test/ui/nll/issue-22323-temp-destruction.rs @@ -0,0 +1,30 @@ +// rust-lang/rust#22323: regression test demonstrating that NLL +// precisely tracks temporary destruction order. + +// check-pass + +fn main() { + let _s = construct().borrow().consume_borrowed(); +} + +fn construct() -> Value { Value } + +pub struct Value; + +impl Value { + fn borrow<'a>(&'a self) -> Borrowed<'a> { unimplemented!() } +} + +pub struct Borrowed<'a> { + _inner: Guard<'a, Value>, +} + +impl<'a> Borrowed<'a> { + fn consume_borrowed(self) -> String { unimplemented!() } +} + +pub struct Guard<'a, T: ?Sized + 'a> { + _lock: &'a T, +} + +impl<'a, T: ?Sized> Drop for Guard<'a, T> { fn drop(&mut self) {} } diff --git a/src/test/ui/nll/issue-24535-allow-mutable-borrow-in-match-guard.rs b/src/test/ui/nll/issue-24535-allow-mutable-borrow-in-match-guard.rs new file mode 100644 index 000000000..7253d35ed --- /dev/null +++ b/src/test/ui/nll/issue-24535-allow-mutable-borrow-in-match-guard.rs @@ -0,0 +1,55 @@ +// run-pass +// This test illustrates that under NLL, we can remove our overly +// conservative approach for disallowing mutations of match inputs. + +// See further discussion on rust-lang/rust#24535, +// rust-lang/rfcs#1006, and rust-lang/rfcs#107 + +fn main() { + rust_issue_24535(); + rfcs_issue_1006_1(); + rfcs_issue_1006_2(); +} + +fn rust_issue_24535() { + fn compare(a: &u8, b: &mut u8) -> bool { + a == b + } + + let a = 3u8; + + match a { + 0 => panic!("nope"), + 3 if compare(&a, &mut 3) => (), + _ => panic!("nope"), + } +} + +fn rfcs_issue_1006_1() { + let v = vec!["1".to_string(), "2".to_string(), "3".to_string()]; + match Some(&v) { + Some(iv) if iv.iter().any(|x| &x[..]=="2") => true, + _ => panic!("nope"), + }; +} + +fn rfcs_issue_1006_2() { + #[inline(always)] + fn check<'a, I: Iterator<Item=&'a i32>>(mut i: I) -> bool { + i.any(|&x| x == 2) + } + + let slice = [1, 2, 3]; + + match 42 { + _ if slice.iter().any(|&x| x == 2) => { true }, + _ => { panic!("nope"); } + }; + + // (This match is just illustrating how easy it was to circumvent + // the checking performed for the previous `match`.) + match 42 { + _ if check(slice.iter()) => { true }, + _ => { panic!("nope"); } + }; +} diff --git a/src/test/ui/nll/issue-27282-move-match-input-into-guard.rs b/src/test/ui/nll/issue-27282-move-match-input-into-guard.rs new file mode 100644 index 000000000..4109c10e2 --- /dev/null +++ b/src/test/ui/nll/issue-27282-move-match-input-into-guard.rs @@ -0,0 +1,20 @@ +// Issue 27282: Example 2: This sidesteps the AST checks disallowing +// mutable borrows in match guards by hiding the mutable borrow in a +// guard behind a move (of the mutably borrowed match input) within a +// closure. +// +// This example is not rejected by AST borrowck (and then reliably +// reaches the panic code when executed, despite the compiler warning +// about that match arm being unreachable. + +fn main() { + let b = &mut true; + match b { + //~^ ERROR use of moved value: `b` [E0382] + &mut false => {}, + _ if { (|| { let bar = b; *bar = false; })(); + false } => { }, + &mut true => { println!("You might think we should get here"); }, + _ => panic!("surely we could never get here, since rustc warns it is unreachable."), + } +} diff --git a/src/test/ui/nll/issue-27282-move-match-input-into-guard.stderr b/src/test/ui/nll/issue-27282-move-match-input-into-guard.stderr new file mode 100644 index 000000000..9be1a9279 --- /dev/null +++ b/src/test/ui/nll/issue-27282-move-match-input-into-guard.stderr @@ -0,0 +1,16 @@ +error[E0382]: use of moved value: `b` + --> $DIR/issue-27282-move-match-input-into-guard.rs:12:5 + | +LL | let b = &mut true; + | - move occurs because `b` has type `&mut bool`, which does not implement the `Copy` trait +LL | match b { + | ^^^^^^^ value used here after move +... +LL | _ if { (|| { let bar = b; *bar = false; })(); + | -- - variable moved due to use in closure + | | + | value moved into closure here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/nll/issue-27282-move-ref-mut-into-guard.rs b/src/test/ui/nll/issue-27282-move-ref-mut-into-guard.rs new file mode 100644 index 000000000..afa0ba780 --- /dev/null +++ b/src/test/ui/nll/issue-27282-move-ref-mut-into-guard.rs @@ -0,0 +1,13 @@ +// Issue 27282: Example 1: This sidesteps the AST checks disallowing +// mutable borrows in match guards by hiding the mutable borrow in a +// guard behind a move (of the ref mut pattern id) within a closure. + +fn main() { + match Some(&4) { + None => {}, + ref mut foo + if { (|| { let bar = foo; bar.take() })(); false } => {}, + //~^ ERROR cannot move out of `foo` in pattern guard [E0507] + Some(s) => std::process::exit(*s), + } +} diff --git a/src/test/ui/nll/issue-27282-move-ref-mut-into-guard.stderr b/src/test/ui/nll/issue-27282-move-ref-mut-into-guard.stderr new file mode 100644 index 000000000..a0d32616f --- /dev/null +++ b/src/test/ui/nll/issue-27282-move-ref-mut-into-guard.stderr @@ -0,0 +1,13 @@ +error[E0507]: cannot move out of `foo` in pattern guard + --> $DIR/issue-27282-move-ref-mut-into-guard.rs:9:19 + | +LL | if { (|| { let bar = foo; bar.take() })(); false } => {}, + | ^^ --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait + | | + | move out of `foo` occurs here + | + = note: variables bound in patterns cannot be moved from until after the end of the pattern guard + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-1.rs b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-1.rs new file mode 100644 index 000000000..d17d6f07f --- /dev/null +++ b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-1.rs @@ -0,0 +1,31 @@ +// This is testing an attempt to corrupt the discriminant of the match +// arm in a guard, followed by an attempt to continue matching on that +// corrupted discriminant in the remaining match arms. +// +// Basically this is testing that our new NLL feature of emitting a +// fake read on each match arm is catching cases like this. +// +// This case is interesting because it includes a guard that +// diverges, and therefore a single final fake-read at the very end +// after the final match arm would not suffice. + +struct ForceFnOnce; + +fn main() { + let mut x = &mut Some(&2); + let force_fn_once = ForceFnOnce; + match x { + &mut None => panic!("unreachable"), + &mut Some(&_) if { + // ForceFnOnce needed to exploit #27282 + (|| { *x = None; drop(force_fn_once); })(); + //~^ ERROR cannot mutably borrow `x` in match guard [E0510] + false + } => {} + &mut Some(&a) if { // this binds to garbage if we've corrupted discriminant + println!("{}", a); + panic!() + } => {} + _ => panic!("unreachable"), + } +} diff --git a/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-1.stderr b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-1.stderr new file mode 100644 index 000000000..a1f973e0f --- /dev/null +++ b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-1.stderr @@ -0,0 +1,14 @@ +error[E0510]: cannot mutably borrow `x` in match guard + --> $DIR/issue-27282-mutate-before-diverging-arm-1.rs:21:14 + | +LL | match x { + | - value is immutable in match guard +... +LL | (|| { *x = None; drop(force_fn_once); })(); + | ^^ -- borrow occurs due to use of `x` in closure + | | + | cannot mutably borrow + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0510`. diff --git a/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-2.rs b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-2.rs new file mode 100644 index 000000000..9c3e7e997 --- /dev/null +++ b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-2.rs @@ -0,0 +1,40 @@ +// This is testing an attempt to corrupt the discriminant of the match +// arm in a guard, followed by an attempt to continue matching on that +// corrupted discriminant in the remaining match arms. +// +// Basically this is testing that our new NLL feature of emitting a +// fake read on each match arm is catching cases like this. +// +// This case is interesting because it includes a guard that +// diverges, and therefore a single final fake-read at the very end +// after the final match arm would not suffice. +// +// It is also interesting because the access to the corrupted data +// occurs in the pattern-match itself, and not in the guard +// expression. + +struct ForceFnOnce; + +fn main() { + let mut x = &mut Some(&2); + let force_fn_once = ForceFnOnce; + match x { + &mut None => panic!("unreachable"), + &mut Some(&_) + if { + // ForceFnOnce needed to exploit #27282 + (|| { *x = None; drop(force_fn_once); })(); + //~^ ERROR cannot mutably borrow `x` in match guard [E0510] + false + } => {} + + // this segfaults if we corrupted the discriminant, because + // the compiler gets to *assume* that it cannot be the `None` + // case, even though that was the effect of the guard. + &mut Some(&2) + if { + panic!() + } => {} + _ => panic!("unreachable"), + } +} diff --git a/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-2.stderr b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-2.stderr new file mode 100644 index 000000000..dd46308d1 --- /dev/null +++ b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-2.stderr @@ -0,0 +1,14 @@ +error[E0510]: cannot mutably borrow `x` in match guard + --> $DIR/issue-27282-mutate-before-diverging-arm-2.rs:26:18 + | +LL | match x { + | - value is immutable in match guard +... +LL | (|| { *x = None; drop(force_fn_once); })(); + | ^^ -- borrow occurs due to use of `x` in closure + | | + | cannot mutably borrow + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0510`. diff --git a/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-3.rs b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-3.rs new file mode 100644 index 000000000..cff9e963e --- /dev/null +++ b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-3.rs @@ -0,0 +1,30 @@ +// This is testing an attempt to corrupt the discriminant of the match +// arm in a guard, followed by an attempt to continue matching on that +// corrupted discriminant in the remaining match arms. +// +// Basically this is testing that our new NLL feature of emitting a +// fake read on each match arm is catching cases like this. +// +// This case is interesting because a borrow of **x is untracked, because **x is +// immutable. However, for matches we care that **x refers to the same value +// until we have chosen a match arm. + +struct ForceFnOnce; +fn main() { + let mut x = &mut &Some(&2); + let force_fn_once = ForceFnOnce; + match **x { + None => panic!("unreachable"), + Some(&_) if { + // ForceFnOnce needed to exploit #27282 + (|| { *x = &None; drop(force_fn_once); })(); + //~^ ERROR cannot mutably borrow `x` in match guard [E0510] + false + } => {} + Some(&a) if { // this binds to garbage if we've corrupted discriminant + println!("{}", a); + panic!() + } => {} + _ => panic!("unreachable"), + } +} diff --git a/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-3.stderr b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-3.stderr new file mode 100644 index 000000000..4a4a25790 --- /dev/null +++ b/src/test/ui/nll/issue-27282-mutate-before-diverging-arm-3.stderr @@ -0,0 +1,14 @@ +error[E0510]: cannot mutably borrow `x` in match guard + --> $DIR/issue-27282-mutate-before-diverging-arm-3.rs:20:14 + | +LL | match **x { + | --- value is immutable in match guard +... +LL | (|| { *x = &None; drop(force_fn_once); })(); + | ^^ -- borrow occurs due to use of `x` in closure + | | + | cannot mutably borrow + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0510`. diff --git a/src/test/ui/nll/issue-27282-mutation-in-guard.rs b/src/test/ui/nll/issue-27282-mutation-in-guard.rs new file mode 100644 index 000000000..395c7d214 --- /dev/null +++ b/src/test/ui/nll/issue-27282-mutation-in-guard.rs @@ -0,0 +1,13 @@ +fn main() { + match Some(&4) { + None => {}, + ref mut foo + if { + (|| { let bar = foo; bar.take() })(); + //~^ ERROR cannot move out of `foo` in pattern guard + false + } => {}, + Some(ref _s) => println!("Note this arm is bogus; the `Some` became `None` in the guard."), + _ => println!("Here is some supposedly unreachable code."), + } +} diff --git a/src/test/ui/nll/issue-27282-mutation-in-guard.stderr b/src/test/ui/nll/issue-27282-mutation-in-guard.stderr new file mode 100644 index 000000000..c4ce7e62f --- /dev/null +++ b/src/test/ui/nll/issue-27282-mutation-in-guard.stderr @@ -0,0 +1,13 @@ +error[E0507]: cannot move out of `foo` in pattern guard + --> $DIR/issue-27282-mutation-in-guard.rs:6:18 + | +LL | (|| { let bar = foo; bar.take() })(); + | ^^ --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait + | | + | move out of `foo` occurs here + | + = note: variables bound in patterns cannot be moved from until after the end of the pattern guard + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/src/test/ui/nll/issue-27282-reborrow-ref-mut-in-guard.rs b/src/test/ui/nll/issue-27282-reborrow-ref-mut-in-guard.rs new file mode 100644 index 000000000..82d8b9e9e --- /dev/null +++ b/src/test/ui/nll/issue-27282-reborrow-ref-mut-in-guard.rs @@ -0,0 +1,18 @@ +// Issue 27282: This is a variation on issue-27282-move-ref-mut-into-guard.rs +// +// It reborrows instead of moving the `ref mut` pattern borrow. This +// means that our conservative check for mutation in guards will +// reject it. But I want to make sure that we continue to reject it +// (under NLL) even when that conservaive check goes away. + +fn main() { + let mut b = &mut true; + match b { + &mut false => {}, + ref mut r if { (|| { let bar = &mut *r; **bar = false; })(); + //~^ ERROR cannot borrow `r` as mutable, as it is immutable for the pattern guard + false } => { &mut *r; }, + &mut true => { println!("You might think we should get here"); }, + _ => panic!("surely we could never get here, since rustc warns it is unreachable."), + } +} diff --git a/src/test/ui/nll/issue-27282-reborrow-ref-mut-in-guard.stderr b/src/test/ui/nll/issue-27282-reborrow-ref-mut-in-guard.stderr new file mode 100644 index 000000000..48433432d --- /dev/null +++ b/src/test/ui/nll/issue-27282-reborrow-ref-mut-in-guard.stderr @@ -0,0 +1,13 @@ +error[E0596]: cannot borrow `r` as mutable, as it is immutable for the pattern guard + --> $DIR/issue-27282-reborrow-ref-mut-in-guard.rs:12:25 + | +LL | ref mut r if { (|| { let bar = &mut *r; **bar = false; })(); + | ^^ -- mutable borrow occurs due to use of `r` in closure + | | + | cannot borrow as mutable + | + = note: variables bound in patterns are immutable until the end of the pattern guard + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/nll/issue-27868.rs b/src/test/ui/nll/issue-27868.rs new file mode 100644 index 000000000..e436b22db --- /dev/null +++ b/src/test/ui/nll/issue-27868.rs @@ -0,0 +1,28 @@ +// Regression test for issue #27868 + +use std::ops::AddAssign; + +struct MyVec<T>(Vec<T>); + +impl <T> Drop for MyVec<T> { + fn drop(&mut self) { + println!("Being dropped."); + } +} + +impl<T> AddAssign<T> for MyVec<T> { + fn add_assign(&mut self, _elem: T) { + println!("In add_assign."); + } +} + +fn main() { + let mut vec = MyVec(vec![0]); + let mut vecvec = vec![vec]; + + vecvec[0] += { + vecvec = vec![]; + //~^ ERROR cannot assign to `vecvec` because it is borrowed [E0506] + 0 + }; +} diff --git a/src/test/ui/nll/issue-27868.stderr b/src/test/ui/nll/issue-27868.stderr new file mode 100644 index 000000000..e0b3b5494 --- /dev/null +++ b/src/test/ui/nll/issue-27868.stderr @@ -0,0 +1,18 @@ +error[E0506]: cannot assign to `vecvec` because it is borrowed + --> $DIR/issue-27868.rs:24:9 + | +LL | vecvec[0] += { + | ------ + | | + | _____borrow of `vecvec` occurs here + | | +LL | | vecvec = vec![]; + | | ^^^^^^ assignment to borrowed `vecvec` occurs here +LL | | +LL | | 0 +LL | | }; + | |_____- 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/nll/issue-30104.rs b/src/test/ui/nll/issue-30104.rs new file mode 100644 index 000000000..38850cd3f --- /dev/null +++ b/src/test/ui/nll/issue-30104.rs @@ -0,0 +1,40 @@ +// Regression test for #30104 + +// check-pass + +use std::ops::{Deref, DerefMut}; + +fn box_two_field(v: &mut Box<(i32, i32)>) { + let _a = &mut v.0; + let _b = &mut v.1; +} + +fn box_destructure(v: &mut Box<(i32, i32)>) { + let (ref mut _head, ref mut _tail) = **v; +} + +struct Wrap<T>(T); + +impl<T> Deref for Wrap<T> { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } +} + +impl<T> DerefMut for Wrap<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +fn smart_two_field(v: &mut Wrap<(i32, i32)>) { + let _a = &mut v.0; + let _b = &mut v.1; +} + +fn smart_destructure(v: &mut Wrap<(i32, i32)>) { + let (ref mut _head, ref mut _tail) = **v; +} + +fn main() {} diff --git a/src/test/ui/nll/issue-31567.rs b/src/test/ui/nll/issue-31567.rs new file mode 100644 index 000000000..623954e6d --- /dev/null +++ b/src/test/ui/nll/issue-31567.rs @@ -0,0 +1,25 @@ +// Regression test for #31567: cached results of projections were +// causing region relations not to be enforced at all the places where +// they have to be enforced. + +struct VecWrapper<'a>(&'a mut S); + +struct S(Box<u32>); + +fn get_dangling<'a>(v: VecWrapper<'a>) -> &'a u32 { + let s_inner: &'a S = &*v.0; //~ ERROR borrow may still be in use when destructor runs [E0713] + &s_inner.0 +} + +impl<'a> Drop for VecWrapper<'a> { + fn drop(&mut self) { + *self.0 = S(Box::new(0)); + } +} + +fn main() { + let mut s = S(Box::new(11)); + let vw = VecWrapper(&mut s); + let dangling = get_dangling(vw); + println!("{}", dangling); +} diff --git a/src/test/ui/nll/issue-31567.stderr b/src/test/ui/nll/issue-31567.stderr new file mode 100644 index 000000000..7d43383e8 --- /dev/null +++ b/src/test/ui/nll/issue-31567.stderr @@ -0,0 +1,16 @@ +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/issue-31567.rs:10:26 + | +LL | fn get_dangling<'a>(v: VecWrapper<'a>) -> &'a u32 { + | -- lifetime `'a` defined here +LL | let s_inner: &'a S = &*v.0; + | ----- ^^^^^ + | | + | type annotation requires that `*v.0` is borrowed for `'a` +LL | &s_inner.0 +LL | } + | - here, drop of `v` needs exclusive access to `*v.0`, because the type `VecWrapper<'_>` implements the `Drop` trait + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0713`. diff --git a/src/test/ui/nll/issue-32382-index-assoc-type-with-lifetime.rs b/src/test/ui/nll/issue-32382-index-assoc-type-with-lifetime.rs new file mode 100644 index 000000000..a8a8e6930 --- /dev/null +++ b/src/test/ui/nll/issue-32382-index-assoc-type-with-lifetime.rs @@ -0,0 +1,41 @@ +// check-pass + +// rust-lang/rust#32382: Borrow checker used to complain about +// `foobar_3` in the `impl` below, presumably due to some interaction +// between the use of a lifetime in the associated type and the use of +// the overloaded operator[]. This regression test ensures that we do +// not resume complaining about it in the future. + + +use std::marker::PhantomData; +use std::ops::Index; + +pub trait Context: Clone { + type Container: ?Sized; + fn foobar_1( container: &Self::Container ) -> &str; + fn foobar_2( container: &Self::Container ) -> &str; + fn foobar_3( container: &Self::Container ) -> &str; +} + +#[derive(Clone)] +struct Foobar<'a> { + phantom: PhantomData<&'a ()> +} + +impl<'a> Context for Foobar<'a> { + type Container = [&'a str]; + + fn foobar_1<'r>( container: &'r [&'a str] ) -> &'r str { + container[0] + } + + fn foobar_2<'r>( container: &'r Self::Container ) -> &'r str { + container.index( 0 ) + } + + fn foobar_3<'r>( container: &'r Self::Container ) -> &'r str { + container[0] + } +} + +fn main() { } diff --git a/src/test/ui/nll/issue-42574-diagnostic-in-nested-closure.rs b/src/test/ui/nll/issue-42574-diagnostic-in-nested-closure.rs new file mode 100644 index 000000000..0ec0179e8 --- /dev/null +++ b/src/test/ui/nll/issue-42574-diagnostic-in-nested-closure.rs @@ -0,0 +1,11 @@ +// This test illustrates a case where full NLL (enabled by the feature +// switch below) produces superior diagnostics to the NLL-migrate +// mode. + +fn doit(data: &'static mut ()) { + || doit(data); + //~^ ERROR lifetime may not live long enough + //~| ERROR `data` does not live long enough +} + +fn main() { } diff --git a/src/test/ui/nll/issue-42574-diagnostic-in-nested-closure.stderr b/src/test/ui/nll/issue-42574-diagnostic-in-nested-closure.stderr new file mode 100644 index 000000000..f7a525ee9 --- /dev/null +++ b/src/test/ui/nll/issue-42574-diagnostic-in-nested-closure.stderr @@ -0,0 +1,26 @@ +error: lifetime may not live long enough + --> $DIR/issue-42574-diagnostic-in-nested-closure.rs:6:8 + | +LL | || doit(data); + | -- ^^^^^^^^^^ argument requires that `'1` must outlive `'static` + | | + | lifetime `'1` represents this closure's body + | + = note: closure implements `FnMut`, so references to captured variables can't escape the closure + +error[E0597]: `data` does not live long enough + --> $DIR/issue-42574-diagnostic-in-nested-closure.rs:6:13 + | +LL | || doit(data); + | -- -----^^^^- + | | | | + | | | borrowed value does not live long enough + | | argument requires that `data` is borrowed for `'static` + | value captured here +... +LL | } + | - `data` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-43058.rs b/src/test/ui/nll/issue-43058.rs new file mode 100644 index 000000000..227888d17 --- /dev/null +++ b/src/test/ui/nll/issue-43058.rs @@ -0,0 +1,26 @@ +// check-pass + +use std::borrow::Cow; + +#[derive(Clone, Debug)] +struct S<'a> { + name: Cow<'a, str> +} + +#[derive(Clone, Debug)] +struct T<'a> { + s: Cow<'a, [S<'a>]> +} + +fn main() { + let s1 = [S { name: Cow::Borrowed("Test1") }, S { name: Cow::Borrowed("Test2") }]; + let b1 = T { s: Cow::Borrowed(&s1) }; + let s2 = [S { name: Cow::Borrowed("Test3") }, S { name: Cow::Borrowed("Test4") }]; + let b2 = T { s: Cow::Borrowed(&s2) }; + + let mut v = Vec::new(); + v.push(b1); + v.push(b2); + + println!("{:?}", v); +} diff --git a/src/test/ui/nll/issue-45157.rs b/src/test/ui/nll/issue-45157.rs new file mode 100644 index 000000000..8d2bf22a0 --- /dev/null +++ b/src/test/ui/nll/issue-45157.rs @@ -0,0 +1,31 @@ +#![allow(unused)] + + +#[derive(Clone, Copy, Default)] +struct S { + a: u8, + b: u8, +} +#[derive(Clone, Copy, Default)] +struct Z { + c: u8, + d: u8, +} + +union U { + s: S, + z: Z, +} + +fn main() { + unsafe { + let mut u = U { s: Default::default() }; + + let mref = &mut u.s.a; + *mref = 22; + + let nref = &u.z.c; + //~^ ERROR cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`) [E0502] + println!("{} {}", mref, nref) + } +} diff --git a/src/test/ui/nll/issue-45157.stderr b/src/test/ui/nll/issue-45157.stderr new file mode 100644 index 000000000..57fd8d49c --- /dev/null +++ b/src/test/ui/nll/issue-45157.stderr @@ -0,0 +1,17 @@ +error[E0502]: cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`) + --> $DIR/issue-45157.rs:27:20 + | +LL | let mref = &mut u.s.a; + | ---------- mutable borrow occurs here (via `u.s.a`) +... +LL | let nref = &u.z.c; + | ^^^^^^ immutable borrow of `u.z.c` -- which overlaps with `u.s.a` -- occurs here +LL | +LL | println!("{} {}", mref, nref) + | ---- mutable borrow later used here + | + = note: `u.z.c` is a field of the union `U`, so it overlaps the field `u.s.a` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/nll/issue-45696-long-live-borrows-in-boxes.rs b/src/test/ui/nll/issue-45696-long-live-borrows-in-boxes.rs new file mode 100644 index 000000000..b3f655628 --- /dev/null +++ b/src/test/ui/nll/issue-45696-long-live-borrows-in-boxes.rs @@ -0,0 +1,114 @@ +// rust-lang/rust#45696: This test is checking that we can return +// mutable borrows owned by boxes even when the boxes are dropped. + +// run-pass + +// This function shows quite directly what is going on: We have a +// reborrow of contents within the box. +fn return_borrow_from_dropped_box_1(x: Box<&mut u32>) -> &mut u32 { &mut **x } + +// This function is the way you'll probably see this in practice (the +// reborrow is now implicit). +fn return_borrow_from_dropped_box_2(x: Box<&mut u32>) -> &mut u32 { *x } + +// For the remaining tests we just add some fields or other +// indirection to ensure that the compiler isn't just special-casing +// the above `Box<&mut T>` as the only type that would work. + +// Here we add a tuple of indirection between the box and the +// reference. +type BoxedTup<'a, 'b> = Box<(&'a mut u32, &'b mut u32)>; + +fn return_borrow_of_field_from_dropped_box_1<'a>(x: BoxedTup<'a, '_>) -> &'a mut u32 { + &mut *x.0 +} + +fn return_borrow_of_field_from_dropped_box_2<'a>(x: BoxedTup<'a, '_>) -> &'a mut u32 { + x.0 +} + +fn return_borrow_from_dropped_tupled_box_1<'a>(x: (BoxedTup<'a, '_>, &mut u32)) -> &'a mut u32 { + &mut *(x.0).0 +} + +fn return_borrow_from_dropped_tupled_box_2<'a>(x: (BoxedTup<'a, '_>, &mut u32)) -> &'a mut u32 { + (x.0).0 +} + +fn basic_tests() { + let mut x = 2; + let mut y = 3; + let mut z = 4; + *return_borrow_from_dropped_box_1(Box::new(&mut x)) += 10; + assert_eq!((x, y, z), (12, 3, 4)); + *return_borrow_from_dropped_box_2(Box::new(&mut x)) += 10; + assert_eq!((x, y, z), (22, 3, 4)); + *return_borrow_of_field_from_dropped_box_1(Box::new((&mut x, &mut y))) += 10; + assert_eq!((x, y, z), (32, 3, 4)); + *return_borrow_of_field_from_dropped_box_2(Box::new((&mut x, &mut y))) += 10; + assert_eq!((x, y, z), (42, 3, 4)); + *return_borrow_from_dropped_tupled_box_1((Box::new((&mut x, &mut y)), &mut z)) += 10; + assert_eq!((x, y, z), (52, 3, 4)); + *return_borrow_from_dropped_tupled_box_2((Box::new((&mut x, &mut y)), &mut z)) += 10; + assert_eq!((x, y, z), (62, 3, 4)); +} + +// These scribbling tests have been transcribed from +// issue-45696-scribble-on-boxed-borrow.rs +// +// In the context of that file, these tests are meant to show cases +// that should be *accepted* by the compiler, so here we are actually +// checking that the code we get when they are compiled matches our +// expectations. + +struct Scribble<'a>(&'a mut u32); + +impl<'a> Drop for Scribble<'a> { fn drop(&mut self) { *self.0 = 42; } } + +// this is okay, in both AST-borrowck and NLL: The `Scribble` here *has* +// to strictly outlive `'a` +fn borrowed_scribble<'a>(s: &'a mut Scribble) -> &'a mut u32 { + &mut *s.0 +} + +// this, by analogy to previous case, is also okay. +fn boxed_borrowed_scribble<'a>(s: Box<&'a mut Scribble>) -> &'a mut u32 { + &mut *(*s).0 +} + +// this, by analogy to previous case, is also okay. +fn boxed_boxed_borrowed_scribble<'a>(s: Box<Box<&'a mut Scribble>>) -> &'a mut u32 { + &mut *(**s).0 +} + +fn scribbling_tests() { + let mut x = 1; + { + let mut long_lived = Scribble(&mut x); + *borrowed_scribble(&mut long_lived) += 10; + assert_eq!(*long_lived.0, 11); + // (Scribble dtor runs here, after `&mut`-borrow above ends) + } + assert_eq!(x, 42); + x = 1; + { + let mut long_lived = Scribble(&mut x); + *boxed_borrowed_scribble(Box::new(&mut long_lived)) += 10; + assert_eq!(*long_lived.0, 11); + // (Scribble dtor runs here, after `&mut`-borrow above ends) + } + assert_eq!(x, 42); + x = 1; + { + let mut long_lived = Scribble(&mut x); + *boxed_boxed_borrowed_scribble(Box::new(Box::new(&mut long_lived))) += 10; + assert_eq!(*long_lived.0, 11); + // (Scribble dtor runs here, after `&mut`-borrow above ends) + } + assert_eq!(x, 42); +} + +fn main() { + basic_tests(); + scribbling_tests(); +} diff --git a/src/test/ui/nll/issue-45696-no-variant-box-recur.rs b/src/test/ui/nll/issue-45696-no-variant-box-recur.rs new file mode 100644 index 000000000..39f1607a3 --- /dev/null +++ b/src/test/ui/nll/issue-45696-no-variant-box-recur.rs @@ -0,0 +1,47 @@ +// rust-lang/rust#45696: This test checks the compiler won't infinite loop when +// you declare a variable of type `struct A(Box<A>, ...);` (which is impossible +// to construct but *is* possible to declare; see also issues #4287, #44933, +// and #52852). + +// run-pass + +// This test has structs and functions that are by definition unusable +// all over the place, so just go ahead and allow dead_code +#![allow(dead_code)] + +// direct regular recursion with indirect ownership via box +struct C { field: Box<C> } + +// direct non-regular recursion with indirect ownership via box +struct D { field: Box<(D, D)> } + +// indirect regular recursion with indirect ownership via box. +struct E { field: F } +struct F { field: Box<E> } + +// indirect non-regular recursion with indirect ownership via box. +struct G { field: (H, H) } +struct H { field: Box<G> } + +// These enums are cases that are not currently hit by the +// `visit_terminator_drop` recursion down a type's structural +// definition. +// +// But it seems prudent to include them in this test as variants on +// the above, in that they are similarly non-constructable data types +// with destructors that would diverge. +enum I { One(Box<I>) } +enum J { One(Box<J>), Two(Box<J>) } + +fn impossible_to_call_c(_c: C) { } +fn impossible_to_call_d(_d: D) { } +fn impossible_to_call_e(_e: E) { } +fn impossible_to_call_f(_f: F) { } +fn impossible_to_call_g(_g: G) { } +fn impossible_to_call_h(_h: H) { } +fn impossible_to_call_i(_i: I) { } +fn impossible_to_call_j(_j: J) { } + +fn main() { + +} diff --git a/src/test/ui/nll/issue-45696-scribble-on-boxed-borrow.rs b/src/test/ui/nll/issue-45696-scribble-on-boxed-borrow.rs new file mode 100644 index 000000000..637cf278f --- /dev/null +++ b/src/test/ui/nll/issue-45696-scribble-on-boxed-borrow.rs @@ -0,0 +1,67 @@ +// rust-lang/rust#45696: This test is checking that we *cannot* return +// mutable borrows that would be scribbled over by destructors before +// the return occurs. + +// ignore-compare-mode-polonius + +struct Scribble<'a>(&'a mut u32); + +impl<'a> Drop for Scribble<'a> { fn drop(&mut self) { *self.0 = 42; } } + +// this is okay: The `Scribble` here *has* to strictly outlive `'a` +fn borrowed_scribble<'a>(s: &'a mut Scribble) -> &'a mut u32 { + &mut *s.0 +} + +// this, by analogy to previous case, is also okay. +fn boxed_borrowed_scribble<'a>(s: Box<&'a mut Scribble>) -> &'a mut u32 { + &mut *(*s).0 +} + +// this, by analogy to previous case, is also okay. +fn boxed_boxed_borrowed_scribble<'a>(s: Box<Box<&'a mut Scribble>>) -> &'a mut u32 { + &mut *(**s).0 +} + +// this is not okay: in between the time that we take the mutable +// borrow and the caller receives it as a return value, the drop of +// `s` will scribble on it, violating our aliasing guarantees. +// +// * (Maybe in the future the two-phase borrows system will be +// extended to support this case. But for now, it is an error in +// NLL, even with two-phase borrows.) +fn scribbled<'a>(s: Scribble<'a>) -> &'a mut u32 { + &mut *s.0 //~ ERROR borrow may still be in use when destructor runs [E0713] +} + +// This, by analogy to previous case, is *also* not okay. +fn boxed_scribbled<'a>(s: Box<Scribble<'a>>) -> &'a mut u32 { + &mut *(*s).0 //~ ERROR borrow may still be in use when destructor runs [E0713] +} + +// This, by analogy to previous case, is *also* not okay. +fn boxed_boxed_scribbled<'a>(s: Box<Box<Scribble<'a>>>) -> &'a mut u32 { + &mut *(**s).0 //~ ERROR borrow may still be in use when destructor runs [E0713] +} + +fn main() { + let mut x = 1; + { + let mut long_lived = Scribble(&mut x); + *borrowed_scribble(&mut long_lived) += 10; + // (Scribble dtor runs here, after `&mut`-borrow above ends) + } + { + let mut long_lived = Scribble(&mut x); + *boxed_borrowed_scribble(Box::new(&mut long_lived)) += 10; + // (Scribble dtor runs here, after `&mut`-borrow above ends) + } + { + let mut long_lived = Scribble(&mut x); + *boxed_boxed_borrowed_scribble(Box::new(Box::new(&mut long_lived))) += 10; + // (Scribble dtor runs here, after `&mut`-borrow above ends) + } + *scribbled(Scribble(&mut x)) += 10; + *boxed_scribbled(Box::new(Scribble(&mut x))) += 10; + *boxed_boxed_scribbled(Box::new(Box::new(Scribble(&mut x)))) += 10; +} diff --git a/src/test/ui/nll/issue-45696-scribble-on-boxed-borrow.stderr b/src/test/ui/nll/issue-45696-scribble-on-boxed-borrow.stderr new file mode 100644 index 000000000..db0a17199 --- /dev/null +++ b/src/test/ui/nll/issue-45696-scribble-on-boxed-borrow.stderr @@ -0,0 +1,33 @@ +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/issue-45696-scribble-on-boxed-borrow.rs:34:5 + | +LL | fn scribbled<'a>(s: Scribble<'a>) -> &'a mut u32 { + | -- lifetime `'a` defined here +LL | &mut *s.0 + | ^^^^^^^^^ returning this value requires that `*s.0` is borrowed for `'a` +LL | } + | - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait + +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/issue-45696-scribble-on-boxed-borrow.rs:39:5 + | +LL | fn boxed_scribbled<'a>(s: Box<Scribble<'a>>) -> &'a mut u32 { + | -- lifetime `'a` defined here +LL | &mut *(*s).0 + | ^^^^^^^^^^^^ returning this value requires that `*s.0` is borrowed for `'a` +LL | } + | - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait + +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/issue-45696-scribble-on-boxed-borrow.rs:44:5 + | +LL | fn boxed_boxed_scribbled<'a>(s: Box<Box<Scribble<'a>>>) -> &'a mut u32 { + | -- lifetime `'a` defined here +LL | &mut *(**s).0 + | ^^^^^^^^^^^^^ returning this value requires that `*s.0` is borrowed for `'a` +LL | } + | - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0713`. diff --git a/src/test/ui/nll/issue-46023.rs b/src/test/ui/nll/issue-46023.rs new file mode 100644 index 000000000..a923eb244 --- /dev/null +++ b/src/test/ui/nll/issue-46023.rs @@ -0,0 +1,8 @@ +fn main() { + let x = 0; + + (move || { + x = 1; + //~^ ERROR cannot assign to `x`, as it is not declared as mutable [E0594] + })() +} diff --git a/src/test/ui/nll/issue-46023.stderr b/src/test/ui/nll/issue-46023.stderr new file mode 100644 index 000000000..ca19c2501 --- /dev/null +++ b/src/test/ui/nll/issue-46023.stderr @@ -0,0 +1,12 @@ +error[E0594]: cannot assign to `x`, as it is not declared as mutable + --> $DIR/issue-46023.rs:5:9 + | +LL | let x = 0; + | - help: consider changing this to be mutable: `mut x` +... +LL | x = 1; + | ^^^^^ cannot assign + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/nll/issue-46036.rs b/src/test/ui/nll/issue-46036.rs new file mode 100644 index 000000000..18af33c18 --- /dev/null +++ b/src/test/ui/nll/issue-46036.rs @@ -0,0 +1,12 @@ +// Issue 46036: [NLL] false edges on infinite loops +// Infinite loops should create false edges to the cleanup block. + +struct Foo { x: &'static u32 } + +fn foo() { + let a = 3; + let foo = Foo { x: &a }; //~ ERROR E0597 + loop { } +} + +fn main() { } diff --git a/src/test/ui/nll/issue-46036.stderr b/src/test/ui/nll/issue-46036.stderr new file mode 100644 index 000000000..e6e95ee61 --- /dev/null +++ b/src/test/ui/nll/issue-46036.stderr @@ -0,0 +1,15 @@ +error[E0597]: `a` does not live long enough + --> $DIR/issue-46036.rs:8:24 + | +LL | let foo = Foo { x: &a }; + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `a` is borrowed for `'static` +LL | loop { } +LL | } + | - `a` 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/nll/issue-46589.rs b/src/test/ui/nll/issue-46589.rs new file mode 100644 index 000000000..0a4c20d15 --- /dev/null +++ b/src/test/ui/nll/issue-46589.rs @@ -0,0 +1,31 @@ +// This tests passes in Polonius mode, so is skipped in the automated compare-mode. +// We will manually check it passes in Polonius tests, as we can't have a test here +// which conditionally passes depending on a test revision/compile-flags. + +// ignore-compare-mode-polonius + +struct Foo; + +impl Foo { + fn get_self(&mut self) -> Option<&mut Self> { + Some(self) + } + + fn new_self(&mut self) -> &mut Self { + self + } + + fn trigger_bug(&mut self) { + let other = &mut (&mut *self); + + *other = match (*other).get_self() { + Some(s) => s, + None => (*other).new_self() + //~^ ERROR cannot borrow `**other` as mutable more than once at a time [E0499] + }; + + let c = other; + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-46589.stderr b/src/test/ui/nll/issue-46589.stderr new file mode 100644 index 000000000..60ef3f7b8 --- /dev/null +++ b/src/test/ui/nll/issue-46589.stderr @@ -0,0 +1,15 @@ +error[E0499]: cannot borrow `**other` as mutable more than once at a time + --> $DIR/issue-46589.rs:23:21 + | +LL | *other = match (*other).get_self() { + | ------------------- first mutable borrow occurs here +LL | Some(s) => s, +LL | None => (*other).new_self() + | ^^^^^^^^^^^^^^^^^^^ + | | + | second mutable borrow occurs here + | 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/nll/issue-47022.rs b/src/test/ui/nll/issue-47022.rs new file mode 100644 index 000000000..521643c66 --- /dev/null +++ b/src/test/ui/nll/issue-47022.rs @@ -0,0 +1,33 @@ +// check-pass + +struct LoadedObject { + bodies: Vec<Body>, + color: Color, +} + +struct Body; + +#[derive(Clone)] +struct Color; + +struct Graphic { + color: Color, +} + +fn convert(objects: Vec<LoadedObject>) -> (Vec<Body>, Vec<Graphic>) { + objects + .into_iter() + .flat_map(|LoadedObject { bodies, color, .. }| { + bodies.into_iter().map(move |body| { + ( + body, + Graphic { + color: color.clone(), + }, + ) + }) + }) + .unzip() +} + +fn main() {} diff --git a/src/test/ui/nll/issue-47153-generic-const.rs b/src/test/ui/nll/issue-47153-generic-const.rs new file mode 100644 index 000000000..9f4d57111 --- /dev/null +++ b/src/test/ui/nll/issue-47153-generic-const.rs @@ -0,0 +1,18 @@ +// run-pass + +// Regression test for #47153: constants in a generic context (such as +// a trait) used to ICE. + +#![allow(warnings)] + +trait Foo { + const B: bool = true; +} + +struct Bar<T> { x: T } + +impl<T> Bar<T> { + const B: bool = true; +} + +fn main() { } diff --git a/src/test/ui/nll/issue-47388.rs b/src/test/ui/nll/issue-47388.rs new file mode 100644 index 000000000..207af380e --- /dev/null +++ b/src/test/ui/nll/issue-47388.rs @@ -0,0 +1,10 @@ +struct FancyNum { + num: u8, +} + +fn main() { + let mut fancy = FancyNum{ num: 5 }; + let fancy_ref = &(&mut fancy); + fancy_ref.num = 6; //~ ERROR E0594 + println!("{}", fancy_ref.num); +} diff --git a/src/test/ui/nll/issue-47388.stderr b/src/test/ui/nll/issue-47388.stderr new file mode 100644 index 000000000..a4ee77817 --- /dev/null +++ b/src/test/ui/nll/issue-47388.stderr @@ -0,0 +1,11 @@ +error[E0594]: cannot assign to `fancy_ref.num`, which is behind a `&` reference + --> $DIR/issue-47388.rs:8:5 + | +LL | let fancy_ref = &(&mut fancy); + | ------------- help: consider changing this to be a mutable reference: `&mut (&mut fancy)` +LL | fancy_ref.num = 6; + | ^^^^^^^^^^^^^^^^^ `fancy_ref` is a `&` reference, so the data it refers to cannot be written + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/nll/issue-47470.rs b/src/test/ui/nll/issue-47470.rs new file mode 100644 index 000000000..72ee7f886 --- /dev/null +++ b/src/test/ui/nll/issue-47470.rs @@ -0,0 +1,22 @@ +// Regression test for #47470: cached results of projections were +// causing region relations not to be enforced at all the places where +// they have to be enforced. + +struct Foo<'a>(&'a ()); +trait Bar { + type Assoc; + fn get(self) -> Self::Assoc; +} + +impl<'a> Bar for Foo<'a> { + type Assoc = &'a u32; + fn get(self) -> Self::Assoc { + let local = 42; + &local //~ ERROR cannot return reference to local variable `local` + } +} + +fn main() { + let f = Foo(&()).get(); + println!("{}", f); +} diff --git a/src/test/ui/nll/issue-47470.stderr b/src/test/ui/nll/issue-47470.stderr new file mode 100644 index 000000000..0b1247d60 --- /dev/null +++ b/src/test/ui/nll/issue-47470.stderr @@ -0,0 +1,9 @@ +error[E0515]: cannot return reference to local variable `local` + --> $DIR/issue-47470.rs:15:9 + | +LL | &local + | ^^^^^^ returns a reference to data owned by the current function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/src/test/ui/nll/issue-47589.rs b/src/test/ui/nll/issue-47589.rs new file mode 100644 index 000000000..280bf0811 --- /dev/null +++ b/src/test/ui/nll/issue-47589.rs @@ -0,0 +1,23 @@ +// run-pass + +pub struct DescriptorSet<'a> { + pub slots: Vec<AttachInfo<'a, Resources>> +} + +pub trait ResourcesTrait<'r>: Sized { + type DescriptorSet: 'r; +} + +pub struct Resources; + +impl<'a> ResourcesTrait<'a> for Resources { + type DescriptorSet = DescriptorSet<'a>; +} + +pub enum AttachInfo<'a, R: ResourcesTrait<'a>> { + NextDescriptorSet(Box<R::DescriptorSet>) +} + +fn main() { + let _x = DescriptorSet {slots: Vec::new()}; +} diff --git a/src/test/ui/nll/issue-48070.rs b/src/test/ui/nll/issue-48070.rs new file mode 100644 index 000000000..a9fe3521d --- /dev/null +++ b/src/test/ui/nll/issue-48070.rs @@ -0,0 +1,22 @@ +// run-pass + +struct Foo { + x: u32 +} + +impl Foo { + fn twiddle(&mut self) -> &mut Self { self } + fn twaddle(&mut self) -> &mut Self { self } + fn emit(&mut self) { + self.x += 1; + } +} + +fn main() { + let mut foo = Foo { x: 0 }; + match 22 { + 22 => &mut foo, + 44 => foo.twiddle(), + _ => foo.twaddle(), + }.emit(); +} diff --git a/src/test/ui/nll/issue-48238.rs b/src/test/ui/nll/issue-48238.rs new file mode 100644 index 000000000..d2e9285fa --- /dev/null +++ b/src/test/ui/nll/issue-48238.rs @@ -0,0 +1,10 @@ +// Regression test for issue #48238 + +fn use_val<'a>(val: &'a u8) -> &'a u8 { + val +} + +fn main() { + let orig: u8 = 5; + move || use_val(&orig); //~ ERROR +} diff --git a/src/test/ui/nll/issue-48238.stderr b/src/test/ui/nll/issue-48238.stderr new file mode 100644 index 000000000..0aa1eedad --- /dev/null +++ b/src/test/ui/nll/issue-48238.stderr @@ -0,0 +1,13 @@ +error: lifetime may not live long enough + --> $DIR/issue-48238.rs:9:13 + | +LL | move || use_val(&orig); + | ------- ^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` + | | | + | | return type of closure is &'2 u8 + | lifetime `'1` represents this closure's body + | + = note: closure implements `Fn`, so references to captured variables can't escape the closure + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-48623-closure.rs b/src/test/ui/nll/issue-48623-closure.rs new file mode 100644 index 000000000..3f8587eed --- /dev/null +++ b/src/test/ui/nll/issue-48623-closure.rs @@ -0,0 +1,16 @@ +// run-pass +#![allow(path_statements)] +#![allow(dead_code)] + +struct WithDrop; + +impl Drop for WithDrop { + fn drop(&mut self) {} +} + +fn reborrow_from_closure(r: &mut ()) -> &mut () { + let d = WithDrop; + (move || { d; &mut *r })() +} + +fn main() {} diff --git a/src/test/ui/nll/issue-48623-generator.rs b/src/test/ui/nll/issue-48623-generator.rs new file mode 100644 index 000000000..08d2584ee --- /dev/null +++ b/src/test/ui/nll/issue-48623-generator.rs @@ -0,0 +1,18 @@ +// run-pass +#![allow(path_statements)] +#![allow(dead_code)] + +#![feature(generators, generator_trait)] + +struct WithDrop; + +impl Drop for WithDrop { + fn drop(&mut self) {} +} + +fn reborrow_from_generator(r: &mut ()) { + let d = WithDrop; + move || { d; yield; &mut *r }; //~ WARN unused generator that must be used +} + +fn main() {} diff --git a/src/test/ui/nll/issue-48623-generator.stderr b/src/test/ui/nll/issue-48623-generator.stderr new file mode 100644 index 000000000..70a83e46f --- /dev/null +++ b/src/test/ui/nll/issue-48623-generator.stderr @@ -0,0 +1,11 @@ +warning: unused generator that must be used + --> $DIR/issue-48623-generator.rs:15:5 + | +LL | move || { d; yield; &mut *r }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_must_use)]` on by default + = note: generators are lazy and do nothing unless resumed + +warning: 1 warning emitted + diff --git a/src/test/ui/nll/issue-48697.rs b/src/test/ui/nll/issue-48697.rs new file mode 100644 index 000000000..16e29ab2a --- /dev/null +++ b/src/test/ui/nll/issue-48697.rs @@ -0,0 +1,10 @@ +// Regression test for #48697 + +fn foo(x: &i32) -> &i32 { + let z = 4; + let f = &|y| y; + let k = f(&z); + f(x) //~ cannot return value referencing local variable +} + +fn main() {} diff --git a/src/test/ui/nll/issue-48697.stderr b/src/test/ui/nll/issue-48697.stderr new file mode 100644 index 000000000..f0c29b72b --- /dev/null +++ b/src/test/ui/nll/issue-48697.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing local variable `z` + --> $DIR/issue-48697.rs:7:5 + | +LL | let k = f(&z); + | -- `z` is borrowed here +LL | f(x) + | ^^^^ returns a value referencing data owned by the current function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/src/test/ui/nll/issue-48803.rs b/src/test/ui/nll/issue-48803.rs new file mode 100644 index 000000000..f7fd04179 --- /dev/null +++ b/src/test/ui/nll/issue-48803.rs @@ -0,0 +1,13 @@ +fn flatten<'a, 'b, T>(x: &'a &'b T) -> &'a T { + x +} + +fn main() { + let mut x = "original"; + let y = &x; + let z = &y; + let w = flatten(z); + x = "modified"; + //~^ ERROR cannot assign to `x` because it is borrowed [E0506] + println!("{}", w); // prints "modified" +} diff --git a/src/test/ui/nll/issue-48803.stderr b/src/test/ui/nll/issue-48803.stderr new file mode 100644 index 000000000..2f94039c0 --- /dev/null +++ b/src/test/ui/nll/issue-48803.stderr @@ -0,0 +1,15 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/issue-48803.rs:10:5 + | +LL | let y = &x; + | -- borrow of `x` occurs here +... +LL | x = "modified"; + | ^^^^^^^^^^^^^^ assignment to borrowed `x` occurs here +LL | +LL | println!("{}", w); // prints "modified" + | - 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/nll/issue-50343.rs b/src/test/ui/nll/issue-50343.rs new file mode 100644 index 000000000..dd0afbbdf --- /dev/null +++ b/src/test/ui/nll/issue-50343.rs @@ -0,0 +1,8 @@ +// run-pass + +#![deny(unused_mut)] + +fn main() { + vec![42].iter().map(drop).count(); + vec![(42, 22)].iter().map(|(_x, _y)| ()).count(); +} diff --git a/src/test/ui/nll/issue-50461-used-mut-from-moves.rs b/src/test/ui/nll/issue-50461-used-mut-from-moves.rs new file mode 100644 index 000000000..2458b171e --- /dev/null +++ b/src/test/ui/nll/issue-50461-used-mut-from-moves.rs @@ -0,0 +1,17 @@ +// run-pass + +#![deny(unused_mut)] +#![allow(dead_code)] + +struct Foo { + pub value: i32 +} + +fn use_foo_mut(mut foo: Foo) { + foo = foo; + println!("{}", foo.value); +} + +fn main() { + use_foo_mut(Foo { value: 413 }); +} diff --git a/src/test/ui/nll/issue-50716-1.rs b/src/test/ui/nll/issue-50716-1.rs new file mode 100644 index 000000000..9c3e24de4 --- /dev/null +++ b/src/test/ui/nll/issue-50716-1.rs @@ -0,0 +1,10 @@ +// +// An additional regression test for the issue #50716 “NLL ignores lifetimes +// bounds derived from `Sized` requirements” that checks that the fixed compiler +// accepts this code fragment with both AST and MIR borrow checkers. +// +// check-pass + +struct Qey<Q: ?Sized>(Q); + +fn main() {} diff --git a/src/test/ui/nll/issue-50716.rs b/src/test/ui/nll/issue-50716.rs new file mode 100644 index 000000000..c2fc345fa --- /dev/null +++ b/src/test/ui/nll/issue-50716.rs @@ -0,0 +1,17 @@ +// +// Regression test for the issue #50716: NLL ignores lifetimes bounds +// derived from `Sized` requirements + +trait A { + type X: ?Sized; +} + +fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) +where + for<'b> &'b T: A, + <&'static T as A>::X: Sized +{ + let _x = *s; //~ ERROR +} + +fn main() {} diff --git a/src/test/ui/nll/issue-50716.stderr b/src/test/ui/nll/issue-50716.stderr new file mode 100644 index 000000000..38dd1b5f6 --- /dev/null +++ b/src/test/ui/nll/issue-50716.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/issue-50716.rs:14:14 + | +LL | fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) + | -- lifetime `'a` defined here +... +LL | let _x = *s; + | ^^ proving this value is `Sized` requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-51191.rs b/src/test/ui/nll/issue-51191.rs new file mode 100644 index 000000000..836587d93 --- /dev/null +++ b/src/test/ui/nll/issue-51191.rs @@ -0,0 +1,34 @@ +struct Struct; + +impl Struct { + fn bar(self: &mut Self) { + //~^ WARN function cannot return without recursing + //~^^ HELP a `loop` may express intention better if this is on purpose + (&mut self).bar(); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596] + //~^^ HELP try removing `&mut` here + } + + fn imm(self) { //~ HELP consider changing this to be mutable + (&mut self).bar(); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596] + } + + fn mtbl(mut self) { + (&mut self).bar(); + } + + fn immref(&self) { + (&mut self).bar(); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596] + //~^^ ERROR cannot borrow data in a `&` reference as mutable [E0596] + } + + fn mtblref(&mut self) { + (&mut self).bar(); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596] + //~^^ HELP try removing `&mut` here + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-51191.stderr b/src/test/ui/nll/issue-51191.stderr new file mode 100644 index 000000000..9f4e971f9 --- /dev/null +++ b/src/test/ui/nll/issue-51191.stderr @@ -0,0 +1,69 @@ +warning: function cannot return without recursing + --> $DIR/issue-51191.rs:4:5 + | +LL | fn bar(self: &mut Self) { + | ^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +... +LL | (&mut self).bar(); + | ----------------- recursive call site + | + = note: `#[warn(unconditional_recursion)]` on by default + = help: a `loop` may express intention better if this is on purpose + +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-51191.rs:7:9 + | +LL | (&mut self).bar(); + | ^^^^^^^^^^^ cannot borrow as mutable + | +note: the binding is already a mutable borrow + --> $DIR/issue-51191.rs:4:18 + | +LL | fn bar(self: &mut Self) { + | ^^^^^^^^^ +help: try removing `&mut` here + --> $DIR/issue-51191.rs:7:9 + | +LL | (&mut self).bar(); + | ^^^^^^^^^^^ + +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-51191.rs:13:9 + | +LL | fn imm(self) { + | ---- help: consider changing this to be mutable: `mut self` +LL | (&mut self).bar(); + | ^^^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-51191.rs:22:9 + | +LL | (&mut self).bar(); + | ^^^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/issue-51191.rs:22:9 + | +LL | (&mut self).bar(); + | ^^^^^^^^^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-51191.rs:28:9 + | +LL | (&mut self).bar(); + | ^^^^^^^^^^^ cannot borrow as mutable + | +note: the binding is already a mutable borrow + --> $DIR/issue-51191.rs:27:16 + | +LL | fn mtblref(&mut self) { + | ^^^^^^^^^ +help: try removing `&mut` here + --> $DIR/issue-51191.rs:28:9 + | +LL | (&mut self).bar(); + | ^^^^^^^^^^^ + +error: aborting due to 5 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/nll/issue-51244.rs b/src/test/ui/nll/issue-51244.rs new file mode 100644 index 000000000..c4cbee675 --- /dev/null +++ b/src/test/ui/nll/issue-51244.rs @@ -0,0 +1,5 @@ +fn main() { + let ref my_ref @ _ = 0; + *my_ref = 0; + //~^ ERROR cannot assign to `*my_ref`, which is behind a `&` reference [E0594] +} diff --git a/src/test/ui/nll/issue-51244.stderr b/src/test/ui/nll/issue-51244.stderr new file mode 100644 index 000000000..19f0223a3 --- /dev/null +++ b/src/test/ui/nll/issue-51244.stderr @@ -0,0 +1,11 @@ +error[E0594]: cannot assign to `*my_ref`, which is behind a `&` reference + --> $DIR/issue-51244.rs:3:5 + | +LL | let ref my_ref @ _ = 0; + | -------------- help: consider changing this to be a mutable reference: `ref mut my_ref @ _` +LL | *my_ref = 0; + | ^^^^^^^^^^^ `my_ref` is a `&` reference, so the data it refers to cannot be written + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/nll/issue-51268.rs b/src/test/ui/nll/issue-51268.rs new file mode 100644 index 000000000..dcdedf7d4 --- /dev/null +++ b/src/test/ui/nll/issue-51268.rs @@ -0,0 +1,21 @@ +struct Bar; + +impl Bar { + fn bar(&mut self, _: impl Fn()) {} +} + +struct Foo { + thing: Bar, + number: usize, +} + +impl Foo { + fn foo(&mut self) { + self.thing.bar(|| { + //~^ ERROR cannot borrow `self.thing` as mutable because it is also borrowed as immutable [E0502] + &self.number; + }); + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-51268.stderr b/src/test/ui/nll/issue-51268.stderr new file mode 100644 index 000000000..0483bda63 --- /dev/null +++ b/src/test/ui/nll/issue-51268.stderr @@ -0,0 +1,17 @@ +error[E0502]: cannot borrow `self.thing` as mutable because it is also borrowed as immutable + --> $DIR/issue-51268.rs:14:9 + | +LL | self.thing.bar(|| { + | ^ --- -- immutable borrow occurs here + | | | + | _________| immutable borrow later used by call + | | +LL | | +LL | | &self.number; + | | ----------- first borrow occurs due to use of `self` in closure +LL | | }); + | |__________^ mutable borrow occurs here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/nll/issue-51345-2.rs b/src/test/ui/nll/issue-51345-2.rs new file mode 100644 index 000000000..52f342a85 --- /dev/null +++ b/src/test/ui/nll/issue-51345-2.rs @@ -0,0 +1,8 @@ +// run-fail +// error-pattern: thread 'main' panicked at 'explicit panic' +// ignore-emscripten no processes + +fn main() { + let mut vec = vec![]; + vec.push((vec.len(), panic!())); +} diff --git a/src/test/ui/nll/issue-51351.rs b/src/test/ui/nll/issue-51351.rs new file mode 100644 index 000000000..591d49584 --- /dev/null +++ b/src/test/ui/nll/issue-51351.rs @@ -0,0 +1,21 @@ +// +// Regression test for #51351 and #52133: In the case of #51351, +// late-bound regions (like 'a) that were unused within the arguments of +// a function were overlooked and could case an ICE. In the case of #52133, +// LBR defined on the creator function needed to be added to the free regions +// of the closure, as they were not present in the closure's generic +// declarations otherwise. +// +// check-pass + +fn creash<'a>() { + let x: &'a () = &(); +} + +fn produce<'a>() { + move || { + let x: &'a () = &(); + }; +} + +fn main() {} diff --git a/src/test/ui/nll/issue-51512.rs b/src/test/ui/nll/issue-51512.rs new file mode 100644 index 000000000..691760eb9 --- /dev/null +++ b/src/test/ui/nll/issue-51512.rs @@ -0,0 +1,6 @@ +fn main() { + let range = 0..1; + let r = range; + let x = range.start; + //~^ ERROR use of moved value: `range` [E0382] +} diff --git a/src/test/ui/nll/issue-51512.stderr b/src/test/ui/nll/issue-51512.stderr new file mode 100644 index 000000000..e591ca082 --- /dev/null +++ b/src/test/ui/nll/issue-51512.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of moved value: `range` + --> $DIR/issue-51512.rs:4:13 + | +LL | let range = 0..1; + | ----- move occurs because `range` has type `std::ops::Range<i32>`, which does not implement the `Copy` trait +LL | let r = range; + | ----- value moved here +LL | let x = range.start; + | ^^^^^^^^^^^ value used here after move + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/nll/issue-51770.rs b/src/test/ui/nll/issue-51770.rs new file mode 100644 index 000000000..3d6bc82f1 --- /dev/null +++ b/src/test/ui/nll/issue-51770.rs @@ -0,0 +1,19 @@ +// check-pass + +#![crate_type = "lib"] + +// In an older version, when NLL was still a feature, the following previously did not compile + +use std::ops::Index; + +pub struct Test<T> { + a: T, +} + +impl<T> Index<usize> for Test<T> { + type Output = T; + + fn index(&self, _index: usize) -> &Self::Output { + &self.a + } +} diff --git a/src/test/ui/nll/issue-52057.rs b/src/test/ui/nll/issue-52057.rs new file mode 100644 index 000000000..98f49fe8f --- /dev/null +++ b/src/test/ui/nll/issue-52057.rs @@ -0,0 +1,22 @@ +// Regression test for #52057. There is an implied bound +// that `I: 'a` where `'a` is the lifetime of `self` in `parse_first`; +// but to observe that, one must normalize first. +// +// run-pass + +pub trait Parser { + type Input; + + fn parse_first(input: &mut Self::Input); +} + +impl<'a, I, P: ?Sized> Parser for &'a mut P +where + P: Parser<Input = I>, +{ + type Input = I; + + fn parse_first(_: &mut Self::Input) {} +} + +fn main() {} diff --git a/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.rs b/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.rs new file mode 100644 index 000000000..7ea1c445d --- /dev/null +++ b/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.rs @@ -0,0 +1,30 @@ +// rust-lang/rust#52059: Regardless of whether you are moving out of a +// Drop type or just introducing an inadvertent alias via a borrow of +// one of its fields, it is useful to be reminded of the significance +// of the fact that the type implements Drop. + +pub struct S<'a> { url: &'a mut String } + +impl<'a> Drop for S<'a> { fn drop(&mut self) { } } + +fn finish_1(s: S) -> &mut String { + s.url +} +//~^^ ERROR borrow may still be in use when destructor runs + +fn finish_2(s: S) -> &mut String { + let p = &mut *s.url; p +} +//~^^ ERROR borrow may still be in use when destructor runs + +fn finish_3(s: S) -> &mut String { + let p: &mut _ = s.url; p +} +//~^^ ERROR borrow may still be in use when destructor runs + +fn finish_4(s: S) -> &mut String { + let p = s.url; p +} +//~^^ ERROR cannot move out of type `S<'_>`, which implements the `Drop` trait + +fn main() {} diff --git a/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr b/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr new file mode 100644 index 000000000..c0a17a67e --- /dev/null +++ b/src/test/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr @@ -0,0 +1,44 @@ +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/issue-52059-report-when-borrow-and-drop-conflict.rs:11:5 + | +LL | fn finish_1(s: S) -> &mut String { + | - has type `S<'1>` +LL | s.url + | ^^^^^ returning this value requires that `*s.url` is borrowed for `'1` +LL | } + | - here, drop of `s` needs exclusive access to `*s.url`, because the type `S<'_>` implements the `Drop` trait + +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/issue-52059-report-when-borrow-and-drop-conflict.rs:16:13 + | +LL | fn finish_2(s: S) -> &mut String { + | - has type `S<'1>` +LL | let p = &mut *s.url; p + | ^^^^^^^^^^^ - returning this value requires that `*s.url` is borrowed for `'1` +LL | } + | - here, drop of `s` needs exclusive access to `*s.url`, because the type `S<'_>` implements the `Drop` trait + +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/issue-52059-report-when-borrow-and-drop-conflict.rs:21:21 + | +LL | fn finish_3(s: S) -> &mut String { + | - has type `S<'1>` +LL | let p: &mut _ = s.url; p + | ^^^^^ - returning this value requires that `*s.url` is borrowed for `'1` +LL | } + | - here, drop of `s` needs exclusive access to `*s.url`, because the type `S<'_>` implements the `Drop` trait + +error[E0509]: cannot move out of type `S<'_>`, which implements the `Drop` trait + --> $DIR/issue-52059-report-when-borrow-and-drop-conflict.rs:26:13 + | +LL | let p = s.url; p + | ^^^^^ + | | + | cannot move out of here + | move occurs because `s.url` has type `&mut String`, which does not implement the `Copy` trait + | help: consider borrowing here: `&s.url` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0509, E0713. +For more information about an error, try `rustc --explain E0509`. diff --git a/src/test/ui/nll/issue-52078.rs b/src/test/ui/nll/issue-52078.rs new file mode 100644 index 000000000..a2bcb91ac --- /dev/null +++ b/src/test/ui/nll/issue-52078.rs @@ -0,0 +1,24 @@ +// Regression test for #52078: we were failing to infer a relationship +// between `'a` and `'b` below due to inference variables introduced +// during the normalization process. +// +// check-pass + +struct Drain<'a, T: 'a> { + _marker: ::std::marker::PhantomData<&'a T>, +} + +trait Join { + type Value; + fn get(value: &mut Self::Value); +} + +impl<'a, T> Join for Drain<'a, T> { + type Value = &'a mut Option<T>; + + fn get<'b>(value: &'b mut Self::Value) { + } +} + +fn main() { +} diff --git a/src/test/ui/nll/issue-52086.rs b/src/test/ui/nll/issue-52086.rs new file mode 100644 index 000000000..0414428e4 --- /dev/null +++ b/src/test/ui/nll/issue-52086.rs @@ -0,0 +1,14 @@ +use std::rc::Rc; +use std::sync::Arc; + +struct Bar { field: Vec<i32> } + +fn main() { + let x = Rc::new(Bar { field: vec![] }); + drop(x.field); +//~^ ERROR cannot move out of an `Rc` + + let y = Arc::new(Bar { field: vec![] }); + drop(y.field); +//~^ ERROR cannot move out of an `Arc` +} diff --git a/src/test/ui/nll/issue-52086.stderr b/src/test/ui/nll/issue-52086.stderr new file mode 100644 index 000000000..3b2dae9b7 --- /dev/null +++ b/src/test/ui/nll/issue-52086.stderr @@ -0,0 +1,15 @@ +error[E0507]: cannot move out of an `Rc` + --> $DIR/issue-52086.rs:8:10 + | +LL | drop(x.field); + | ^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait + +error[E0507]: cannot move out of an `Arc` + --> $DIR/issue-52086.rs:12:10 + | +LL | drop(y.field); + | ^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0507`. diff --git a/src/test/ui/nll/issue-52113.rs b/src/test/ui/nll/issue-52113.rs new file mode 100644 index 000000000..ffaef272a --- /dev/null +++ b/src/test/ui/nll/issue-52113.rs @@ -0,0 +1,35 @@ +trait Bazinga {} +impl<F> Bazinga for F {} + +fn produce1<'a>(data: &'a u32) -> impl Bazinga + 'a { + let x = move || { + let _data: &'a u32 = data; + }; + x +} + +fn produce2<'a>(data: &'a mut Vec<&'a u32>, value: &'a u32) -> impl Bazinga + 'a { + let x = move || { + let value: &'a u32 = value; + data.push(value); + }; + x +} + +fn produce3<'a, 'b: 'a>(data: &'a mut Vec<&'a u32>, value: &'b u32) -> impl Bazinga + 'a { + let x = move || { + let value: &'a u32 = value; + data.push(value); + }; + x +} + +fn produce_err<'a, 'b: 'a>(data: &'b mut Vec<&'b u32>, value: &'a u32) -> impl Bazinga + 'b { + let x = move || { + let value: &'a u32 = value; + data.push(value); //~ ERROR lifetime may not live long enough + }; + x +} + +fn main() {} diff --git a/src/test/ui/nll/issue-52113.stderr b/src/test/ui/nll/issue-52113.stderr new file mode 100644 index 000000000..84d4eb266 --- /dev/null +++ b/src/test/ui/nll/issue-52113.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/issue-52113.rs:30:9 + | +LL | fn produce_err<'a, 'b: 'a>(data: &'b mut Vec<&'b u32>, value: &'a u32) -> impl Bazinga + 'b { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | data.push(value); + | ^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-52213.rs b/src/test/ui/nll/issue-52213.rs new file mode 100644 index 000000000..a016924a8 --- /dev/null +++ b/src/test/ui/nll/issue-52213.rs @@ -0,0 +1,15 @@ +fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T { + match (&t,) { + ((u,),) => u, + //~^ ERROR lifetime may not live long enough + } +} + +fn main() { + let x = { + let y = Box::new((42,)); + transmute_lifetime(&y) + }; + + println!("{}", x); +} diff --git a/src/test/ui/nll/issue-52213.stderr b/src/test/ui/nll/issue-52213.stderr new file mode 100644 index 000000000..da31bcd54 --- /dev/null +++ b/src/test/ui/nll/issue-52213.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/issue-52213.rs:3:20 + | +LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | match (&t,) { +LL | ((u,),) => u, + | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-52533-1.rs b/src/test/ui/nll/issue-52533-1.rs new file mode 100644 index 000000000..d15daeddc --- /dev/null +++ b/src/test/ui/nll/issue-52533-1.rs @@ -0,0 +1,11 @@ +#![allow(warnings)] + +struct Foo<'a, 'b, T: 'a + 'b> { x: &'a T, y: &'b T } + +fn gimme(_: impl for<'a, 'b, 'c> FnOnce(&'a Foo<'a, 'b, u32>, + &'a Foo<'a, 'c, u32>) -> &'a Foo<'a, 'b, u32>) { } + +fn main() { + gimme(|x, y| y) + //~^ ERROR lifetime may not live long enough +} diff --git a/src/test/ui/nll/issue-52533-1.stderr b/src/test/ui/nll/issue-52533-1.stderr new file mode 100644 index 000000000..20f19b259 --- /dev/null +++ b/src/test/ui/nll/issue-52533-1.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/issue-52533-1.rs:9:18 + | +LL | gimme(|x, y| y) + | - - ^ closure was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | | | + | | has type `&Foo<'_, '1, u32>` + | has type `&Foo<'_, '2, u32>` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-52534-1.rs b/src/test/ui/nll/issue-52534-1.rs new file mode 100644 index 000000000..d9ea3ae42 --- /dev/null +++ b/src/test/ui/nll/issue-52534-1.rs @@ -0,0 +1,48 @@ +struct Test; + +impl Test { + fn bar(&self, x: &u32) -> &u32 { + let x = 22; + &x +//~^ ERROR cannot return reference to local variable + } +} + +fn foo(x: &u32) -> &u32 { + let x = 22; + &x +//~^ ERROR cannot return reference to local variable +} + +fn baz(x: &u32) -> &&u32 { + let x = 22; + &&x +//~^ ERROR cannot return value referencing local variable +//~| ERROR cannot return reference to temporary value +} + +fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 { + let x = 22; + &x +//~^ ERROR cannot return reference to local variable +} + +fn foobar<'a>(x: &'a u32) -> &'a u32 { + let x = 22; + &x +//~^ ERROR cannot return reference to local variable +} + +fn foobaz<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { + let x = 22; + &x +//~^ ERROR cannot return reference to local variable +} + +fn foobarbaz<'a, 'b>(x: &'a u32, y: &'b u32, z: &'a u32) -> &'a u32 { + let x = 22; + &x +//~^ ERROR cannot return reference to local variable +} + +fn main() { } diff --git a/src/test/ui/nll/issue-52534-1.stderr b/src/test/ui/nll/issue-52534-1.stderr new file mode 100644 index 000000000..743179f05 --- /dev/null +++ b/src/test/ui/nll/issue-52534-1.stderr @@ -0,0 +1,57 @@ +error[E0515]: cannot return reference to local variable `x` + --> $DIR/issue-52534-1.rs:6:9 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0515]: cannot return reference to local variable `x` + --> $DIR/issue-52534-1.rs:13:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0515]: cannot return value referencing local variable `x` + --> $DIR/issue-52534-1.rs:19:5 + | +LL | &&x + | ^-- + | || + | |`x` is borrowed here + | returns a value referencing data owned by the current function + +error[E0515]: cannot return reference to temporary value + --> $DIR/issue-52534-1.rs:19:5 + | +LL | &&x + | ^-- + | || + | |temporary value created here + | returns a reference to data owned by the current function + +error[E0515]: cannot return reference to local variable `x` + --> $DIR/issue-52534-1.rs:26:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0515]: cannot return reference to local variable `x` + --> $DIR/issue-52534-1.rs:32:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0515]: cannot return reference to local variable `x` + --> $DIR/issue-52534-1.rs:38:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0515]: cannot return reference to local variable `x` + --> $DIR/issue-52534-1.rs:44:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0515`. diff --git a/src/test/ui/nll/issue-52534-2.rs b/src/test/ui/nll/issue-52534-2.rs new file mode 100644 index 000000000..e416264ed --- /dev/null +++ b/src/test/ui/nll/issue-52534-2.rs @@ -0,0 +1,14 @@ +fn foo(x: &u32) -> &u32 { + let y; + + { + let x = 32; + y = &x +//~^ ERROR does not live long enough + } + + println!("{}", y); + x +} + +fn main() { } diff --git a/src/test/ui/nll/issue-52534-2.stderr b/src/test/ui/nll/issue-52534-2.stderr new file mode 100644 index 000000000..ac385e056 --- /dev/null +++ b/src/test/ui/nll/issue-52534-2.stderr @@ -0,0 +1,15 @@ +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534-2.rs:6:13 + | +LL | y = &x + | ^^ borrowed value does not live long enough +LL | +LL | } + | - `x` dropped here while still borrowed +LL | +LL | println!("{}", y); + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-52534.rs b/src/test/ui/nll/issue-52534.rs new file mode 100644 index 000000000..559d4c859 --- /dev/null +++ b/src/test/ui/nll/issue-52534.rs @@ -0,0 +1,19 @@ +fn foo(_: impl FnOnce(&u32) -> &u32) { +} + +fn baz(_: impl FnOnce(&u32, u32) -> &u32) { +} + +fn bar() { + let x = 22; + foo(|a| &x) +//~^ ERROR does not live long enough +} + +fn foobar() { + let y = 22; + baz(|first, second| &y) +//~^ ERROR does not live long enough +} + +fn main() { } diff --git a/src/test/ui/nll/issue-52534.stderr b/src/test/ui/nll/issue-52534.stderr new file mode 100644 index 000000000..b2b727fd4 --- /dev/null +++ b/src/test/ui/nll/issue-52534.stderr @@ -0,0 +1,31 @@ +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534.rs:9:14 + | +LL | foo(|a| &x) + | - ^ `x` would have to be valid for `'0`... + | | + | has type `&'0 u32` +LL | +LL | } + | - ...but `x` will be dropped here, when the function `bar` returns + | + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit <https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#dangling-references> + +error[E0597]: `y` does not live long enough + --> $DIR/issue-52534.rs:15:26 + | +LL | baz(|first, second| &y) + | ----- ^ `y` would have to be valid for `'0`... + | | + | has type `&'0 u32` +LL | +LL | } + | - ...but `y` will be dropped here, when the function `foobar` returns + | + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit <https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#dangling-references> + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-52663-span-decl-captured-variable.rs b/src/test/ui/nll/issue-52663-span-decl-captured-variable.rs new file mode 100644 index 000000000..cd1f457a1 --- /dev/null +++ b/src/test/ui/nll/issue-52663-span-decl-captured-variable.rs @@ -0,0 +1,11 @@ +fn expect_fn<F>(f: F) where F : Fn() { + f(); +} + +fn main() { + { + let x = (vec![22], vec![44]); + expect_fn(|| drop(x.0)); + //~^ ERROR cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure [E0507] + } +} diff --git a/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr b/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr new file mode 100644 index 000000000..fb61b30f0 --- /dev/null +++ b/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -0,0 +1,13 @@ +error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure + --> $DIR/issue-52663-span-decl-captured-variable.rs:8:26 + | +LL | let x = (vec![22], vec![44]); + | - captured outer variable +LL | expect_fn(|| drop(x.0)); + | -- ^^^ move occurs because `x.0` has type `Vec<i32>`, which does not implement the `Copy` trait + | | + | captured by this `Fn` closure + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/src/test/ui/nll/issue-52663-trait-object.rs b/src/test/ui/nll/issue-52663-trait-object.rs new file mode 100644 index 000000000..f6bbb2d14 --- /dev/null +++ b/src/test/ui/nll/issue-52663-trait-object.rs @@ -0,0 +1,16 @@ +trait Foo { fn get(&self); } + +impl<A> Foo for A { + fn get(&self) { } +} + + + +fn main() { + let _ = { + let tmp0 = 3; + let tmp1 = &tmp0; + Box::new(tmp1) as Box<dyn Foo + '_> + }; + //~^^^ ERROR `tmp0` does not live long enough +} diff --git a/src/test/ui/nll/issue-52663-trait-object.stderr b/src/test/ui/nll/issue-52663-trait-object.stderr new file mode 100644 index 000000000..5cedea6e6 --- /dev/null +++ b/src/test/ui/nll/issue-52663-trait-object.stderr @@ -0,0 +1,13 @@ +error[E0597]: `tmp0` does not live long enough + --> $DIR/issue-52663-trait-object.rs:12:20 + | +LL | let tmp1 = &tmp0; + | ^^^^^ borrowed value does not live long enough +LL | Box::new(tmp1) as Box<dyn Foo + '_> + | ----------------------------------- borrow later captured here by trait object +LL | }; + | - `tmp0` 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/nll/issue-52669.rs b/src/test/ui/nll/issue-52669.rs new file mode 100644 index 000000000..e33528ac5 --- /dev/null +++ b/src/test/ui/nll/issue-52669.rs @@ -0,0 +1,17 @@ +struct A { + b: B, +} + +#[derive(Clone)] +struct B; + +fn foo(_: A) {} + +fn bar(mut a: A) -> B { + a.b = B; + foo(a); + a.b.clone() +//~^ ERROR borrow of moved value +} + +fn main() {} diff --git a/src/test/ui/nll/issue-52669.stderr b/src/test/ui/nll/issue-52669.stderr new file mode 100644 index 000000000..807b95f7e --- /dev/null +++ b/src/test/ui/nll/issue-52669.stderr @@ -0,0 +1,14 @@ +error[E0382]: borrow of moved value: `a.b` + --> $DIR/issue-52669.rs:13:5 + | +LL | fn bar(mut a: A) -> B { + | ----- move occurs because `a` has type `A`, which does not implement the `Copy` trait +LL | a.b = B; +LL | foo(a); + | - value moved here +LL | a.b.clone() + | ^^^^^^^^^^^ value borrowed here after move + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/nll/issue-52742.rs b/src/test/ui/nll/issue-52742.rs new file mode 100644 index 000000000..d3e201b8a --- /dev/null +++ b/src/test/ui/nll/issue-52742.rs @@ -0,0 +1,17 @@ +struct Foo<'a, 'b> { + x: &'a u32, + y: &'b u32, +} + +struct Bar<'b> { + z: &'b u32, +} + +impl Foo<'_, '_> { + fn take_bar(&mut self, b: Bar<'_>) { + self.y = b.z + //~^ ERROR + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-52742.stderr b/src/test/ui/nll/issue-52742.stderr new file mode 100644 index 000000000..a79738296 --- /dev/null +++ b/src/test/ui/nll/issue-52742.stderr @@ -0,0 +1,12 @@ +error: lifetime may not live long enough + --> $DIR/issue-52742.rs:12:9 + | +LL | fn take_bar(&mut self, b: Bar<'_>) { + | --------- - has type `Bar<'1>` + | | + | has type `&mut Foo<'_, '2>` +LL | self.y = b.z + | ^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-52992.rs b/src/test/ui/nll/issue-52992.rs new file mode 100644 index 000000000..530d1a61b --- /dev/null +++ b/src/test/ui/nll/issue-52992.rs @@ -0,0 +1,25 @@ +// Regression test for an NLL-related ICE (#52992) -- computing +// implied bounds was causing outlives relations that were not +// properly handled. +// +// check-pass + +fn main() {} + +fn fail<'a>() -> Struct<'a, Generic<()>> { + Struct(&Generic(())) +} + +struct Struct<'a, T>(&'a T) where + T: Trait + 'a, + T::AT: 'a; // only fails with this bound + +struct Generic<T>(T); + +trait Trait { + type AT; +} + +impl<T> Trait for Generic<T> { + type AT = T; // only fails with a generic AT +} diff --git a/src/test/ui/nll/issue-53040.rs b/src/test/ui/nll/issue-53040.rs new file mode 100644 index 000000000..e4ee6e913 --- /dev/null +++ b/src/test/ui/nll/issue-53040.rs @@ -0,0 +1,5 @@ +fn main() { + let mut v: Vec<()> = Vec::new(); + || &mut v; +//~^ ERROR captured variable cannot escape `FnMut` closure body +} diff --git a/src/test/ui/nll/issue-53040.stderr b/src/test/ui/nll/issue-53040.stderr new file mode 100644 index 000000000..87ffe9b1a --- /dev/null +++ b/src/test/ui/nll/issue-53040.stderr @@ -0,0 +1,17 @@ +error: captured variable cannot escape `FnMut` closure body + --> $DIR/issue-53040.rs:3:8 + | +LL | let mut v: Vec<()> = Vec::new(); + | ----- variable defined here +LL | || &mut v; + | - ^^^^^- + | | | | + | | | variable captured here + | | returns a reference to a captured variable which escapes the closure body + | inferred to be a `FnMut` closure + | + = note: `FnMut` closures only have access to their captured variables while they are executing... + = note: ...therefore, they cannot allow references to captured variables to escape + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-53119.rs b/src/test/ui/nll/issue-53119.rs new file mode 100644 index 000000000..03c9c071c --- /dev/null +++ b/src/test/ui/nll/issue-53119.rs @@ -0,0 +1,23 @@ +// check-pass + +use std::ops::Deref; + +pub struct TypeFieldIterator<'a, T: 'a> { + _t: &'a T, +} + +pub struct Type<Id, T> { + _types: Vec<(Id, T)>, +} + +impl<'a, Id: 'a, T> Iterator for TypeFieldIterator<'a, T> +where T: Deref<Target = Type<Id, T>> { + type Item = &'a (Id, T); + + fn next(&mut self) -> Option<&'a (Id, T)> { + || self.next(); + None + } +} + +fn main() { } diff --git a/src/test/ui/nll/issue-53123-raw-pointer-cast.rs b/src/test/ui/nll/issue-53123-raw-pointer-cast.rs new file mode 100644 index 000000000..941c9eeb4 --- /dev/null +++ b/src/test/ui/nll/issue-53123-raw-pointer-cast.rs @@ -0,0 +1,26 @@ +// run-pass + +#![allow(unused_variables)] + +pub trait TryTransform { + fn try_transform<F>(self, f: F) + where + Self: Sized, + F: FnOnce(Self); +} + +impl<'a, T> TryTransform for &'a mut T { + fn try_transform<F>(self, f: F) + where + // The bug was that `Self: Sized` caused the lifetime of `this` to "extend" for all + // of 'a instead of only lasting as long as the binding is used (for just that line). + Self: Sized, + F: FnOnce(Self), + { + let this: *mut T = self as *mut T; + f(self); + } +} + +fn main() { +} diff --git a/src/test/ui/nll/issue-53570.rs b/src/test/ui/nll/issue-53570.rs new file mode 100644 index 000000000..35860ba9c --- /dev/null +++ b/src/test/ui/nll/issue-53570.rs @@ -0,0 +1,32 @@ +// Regression test for #53570. Here, we need to propagate that `T: 'a` +// but in some versions of NLL we were propagating a stronger +// requirement that `T: 'static`. This arose because we actually had +// to propagate both that `T: 'a` but also `T: 'b` where `'b` is the +// higher-ranked lifetime that appears in the type of the closure +// parameter `x` -- since `'b` cannot be expressed in the caller's +// space, that got promoted th `'static`. +// +// check-pass + +use std::cell::{RefCell, Ref}; + +trait AnyVec<'a> { +} + +trait GenericVec<T> { + fn unwrap<'a, 'b>(vec: &'b dyn AnyVec<'a>) -> &'b [T] where T: 'a; +} + +struct Scratchpad<'a> { + buffers: RefCell<Box<dyn AnyVec<'a>>>, +} + +impl<'a> Scratchpad<'a> { + fn get<T: GenericVec<T>>(&self) -> Ref<[T]> + where T: 'a + { + Ref::map(self.buffers.borrow(), |x| T::unwrap(x.as_ref())) + } +} + +fn main() { } diff --git a/src/test/ui/nll/issue-53773.rs b/src/test/ui/nll/issue-53773.rs new file mode 100644 index 000000000..ed971b6ce --- /dev/null +++ b/src/test/ui/nll/issue-53773.rs @@ -0,0 +1,47 @@ +struct Archive; +struct ArchiveIterator<'a> { + x: &'a Archive, +} +struct ArchiveChild<'a> { + x: &'a Archive, +} + +struct A { + raw: &'static mut Archive, +} +struct Iter<'a> { + raw: &'a mut ArchiveIterator<'a>, +} +struct C<'a> { + raw: &'a mut ArchiveChild<'a>, +} + +impl A { + pub fn iter(&self) -> Iter<'_> { + panic!() + } +} +impl Drop for A { + fn drop(&mut self) {} +} +impl<'a> Drop for C<'a> { + fn drop(&mut self) {} +} + +impl<'a> Iterator for Iter<'a> { + type Item = C<'a>; + fn next(&mut self) -> Option<C<'a>> { + panic!() + } +} + +fn error(archive: &A) { + let mut members: Vec<&mut ArchiveChild<'_>> = vec![]; + for child in archive.iter() { + members.push(child.raw); + //~^ ERROR borrow may still be in use when destructor runs [E0713] + } + members.len(); +} + +fn main() {} diff --git a/src/test/ui/nll/issue-53773.stderr b/src/test/ui/nll/issue-53773.stderr new file mode 100644 index 000000000..90cba2a14 --- /dev/null +++ b/src/test/ui/nll/issue-53773.stderr @@ -0,0 +1,14 @@ +error[E0713]: borrow may still be in use when destructor runs + --> $DIR/issue-53773.rs:41:22 + | +LL | members.push(child.raw); + | -------------^^^^^^^^^- borrow later used here +LL | +LL | } + | - here, drop of `child` needs exclusive access to `*child.raw`, because the type `C<'_>` implements the `Drop` trait + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0713`. diff --git a/src/test/ui/nll/issue-53807.rs b/src/test/ui/nll/issue-53807.rs new file mode 100644 index 000000000..d494f7e15 --- /dev/null +++ b/src/test/ui/nll/issue-53807.rs @@ -0,0 +1,8 @@ +pub fn main(){ + let maybe = Some(vec![true, true]); + loop { + if let Some(thing) = maybe { +//~^ ERROR use of moved value + } + } +} diff --git a/src/test/ui/nll/issue-53807.stderr b/src/test/ui/nll/issue-53807.stderr new file mode 100644 index 000000000..574a11434 --- /dev/null +++ b/src/test/ui/nll/issue-53807.stderr @@ -0,0 +1,15 @@ +error[E0382]: use of moved value + --> $DIR/issue-53807.rs:4:21 + | +LL | if let Some(thing) = maybe { + | ^^^^^ value moved here, in previous iteration of loop + | + = note: move occurs because value has type `Vec<bool>`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `maybe.0` + | +LL | if let Some(ref thing) = maybe { + | +++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.rs b/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.rs new file mode 100644 index 000000000..312e6dce8 --- /dev/null +++ b/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.rs @@ -0,0 +1,29 @@ +fn main() { + { + let mut _thing1 = D(Box::new("thing1")); + { + let _thing2 = D("thing2"); + side_effects(); + D("other").next(&_thing1) +//~^ ERROR does not live long enough + } + } + + ; +} + +#[derive(Debug)] +struct D<T: std::fmt::Debug>(T); + +impl<T: std::fmt::Debug> Drop for D<T> { + fn drop(&mut self) { + println!("dropping {:?})", self); + } +} + +impl<T: std::fmt::Debug> D<T> { + fn next<U: std::fmt::Debug>(&self, _other: U) -> D<U> { D(_other) } + fn end(&self) { } +} + +fn side_effects() { } diff --git a/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.stderr b/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.stderr new file mode 100644 index 000000000..d8f43cbc9 --- /dev/null +++ b/src/test/ui/nll/issue-54382-use-span-of-tail-of-block.stderr @@ -0,0 +1,23 @@ +error[E0597]: `_thing1` does not live long enough + --> $DIR/issue-54382-use-span-of-tail-of-block.rs:7:29 + | +LL | D("other").next(&_thing1) + | ----------------^^^^^^^^- + | | | + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +... +LL | } + | - `_thing1` dropped here while still borrowed +LL | +LL | ; + | - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | D("other").next(&_thing1); + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-54556-niconii.rs b/src/test/ui/nll/issue-54556-niconii.rs new file mode 100644 index 000000000..cae389e8c --- /dev/null +++ b/src/test/ui/nll/issue-54556-niconii.rs @@ -0,0 +1,31 @@ +// This is a reduction of a concrete test illustrating a case that was +// annoying to Rust developer niconii (see comment thread on #21114). +// +// With resolving issue #54556, pnkfelix hopes that the new diagnostic +// output produced by NLL helps to *explain* the semantic significance +// of temp drop order, and thus why inserting a semi-colon after the +// `if let` expression in `main` works. + +struct Mutex; +struct MutexGuard<'a>(&'a Mutex); + +impl Drop for Mutex { fn drop(&mut self) { println!("Mutex::drop"); } } +impl<'a> Drop for MutexGuard<'a> { fn drop(&mut self) { println!("MutexGuard::drop"); } } + +impl Mutex { + fn lock(&self) -> Result<MutexGuard, ()> { Ok(MutexGuard(self)) } +} + +fn main() { + let counter = Mutex; + + if let Ok(_) = counter.lock() { } //~ ERROR does not live long enough + + // With this code as written, the dynamic semantics here implies + // that `Mutex::drop` for `counter` runs *before* + // `MutexGuard::drop`, which would be unsound since `MutexGuard` + // still has a reference to `counter`. + // + // The goal of #54556 is to explain that within a compiler + // diagnostic. +} diff --git a/src/test/ui/nll/issue-54556-niconii.stderr b/src/test/ui/nll/issue-54556-niconii.stderr new file mode 100644 index 000000000..a8e1edc54 --- /dev/null +++ b/src/test/ui/nll/issue-54556-niconii.stderr @@ -0,0 +1,23 @@ +error[E0597]: `counter` does not live long enough + --> $DIR/issue-54556-niconii.rs:22:20 + | +LL | if let Ok(_) = counter.lock() { } + | ^^^^^^^^^^^^^^ + | | + | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +... +LL | } + | - + | | + | `counter` dropped here while still borrowed + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Result<MutexGuard<'_>, ()>` + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | if let Ok(_) = counter.lock() { }; + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-54556-stephaneyfx.rs b/src/test/ui/nll/issue-54556-stephaneyfx.rs new file mode 100644 index 000000000..b758228e4 --- /dev/null +++ b/src/test/ui/nll/issue-54556-stephaneyfx.rs @@ -0,0 +1,35 @@ +// This is a reduction of a concrete test illustrating a case that was +// annoying to Rust developer stephaneyfx (see issue #46413). +// +// With resolving issue #54556, pnkfelix hopes that the new diagnostic +// output produced by NLL helps to *explain* the semantic significance +// of temp drop order, and thus why storing the result in `x` and then +// returning `x` works. + +pub struct Statement; + +pub struct Rows<'stmt>(&'stmt Statement); + +impl<'stmt> Drop for Rows<'stmt> { + fn drop(&mut self) {} +} + +impl<'stmt> Iterator for Rows<'stmt> { + type Item = String; + + fn next(&mut self) -> Option<Self::Item> { + None + } +} + +fn get_names() -> Option<String> { + let stmt = Statement; + let rows = Rows(&stmt); //~ ERROR does not live long enough + rows.map(|row| row).next() + // let x = rows.map(|row| row).next(); + // x + // + // Removing the map works too as does removing the Drop impl. +} + +fn main() {} diff --git a/src/test/ui/nll/issue-54556-stephaneyfx.stderr b/src/test/ui/nll/issue-54556-stephaneyfx.stderr new file mode 100644 index 000000000..036a7a0ab --- /dev/null +++ b/src/test/ui/nll/issue-54556-stephaneyfx.stderr @@ -0,0 +1,24 @@ +error[E0597]: `stmt` does not live long enough + --> $DIR/issue-54556-stephaneyfx.rs:27:21 + | +LL | let rows = Rows(&stmt); + | ^^^^^ borrowed value does not live long enough +LL | rows.map(|row| row).next() + | ------------------- a temporary with access to the borrow is created here ... +... +LL | } + | - + | | + | `stmt` dropped here while still borrowed + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Map<Rows<'_>, [closure@$DIR/issue-54556-stephaneyfx.rs:28:14: 28:19]>` + | + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let x = rows.map(|row| row).next(); x + | +++++++ +++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.rs b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.rs new file mode 100644 index 000000000..2935caaf2 --- /dev/null +++ b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.rs @@ -0,0 +1,23 @@ +fn main() { + { + let mut _thing1 = D(Box::new("thing1")); + // D("other").next(&_thing1).end() + D(&_thing1).end() //~ ERROR does not live long enough + } + + ; +} + +#[derive(Debug)] +struct D<T: std::fmt::Debug>(T); + +impl<T: std::fmt::Debug> Drop for D<T> { + fn drop(&mut self) { + println!("dropping {:?})", self); + } +} + +impl<T: std::fmt::Debug> D<T> { + fn next<U: std::fmt::Debug>(&self, _other: U) -> D<U> { D(_other) } + fn end(&self) { } +} diff --git a/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr new file mode 100644 index 000000000..92f5ffdf3 --- /dev/null +++ b/src/test/ui/nll/issue-54556-temps-in-tail-diagnostic.stderr @@ -0,0 +1,22 @@ +error[E0597]: `_thing1` does not live long enough + --> $DIR/issue-54556-temps-in-tail-diagnostic.rs:5:11 + | +LL | D(&_thing1).end() + | --^^^^^^^^- + | | | + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +LL | } + | - `_thing1` dropped here while still borrowed +LL | +LL | ; + | - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | D(&_thing1).end(); + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs b/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs new file mode 100644 index 000000000..a111acca6 --- /dev/null +++ b/src/test/ui/nll/issue-54556-used-vs-unused-tails.rs @@ -0,0 +1,56 @@ +// This test case is exploring the space of how blocks with tail +// expressions and statements can be composed, trying to keep each +// case on one line so that we can compare them via a vertical scan +// with the human eye. + +// Each comment on the right side of the line is summarizing the +// expected suggestion from the diagnostic for issue #54556. + +fn main() { + { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // suggest `;` +//~^ ERROR does not live long enough + + { { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } } ; // suggest `;` +//~^ ERROR does not live long enough + + { { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }; } // suggest `;` +//~^ ERROR does not live long enough + + let _ = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // suggest `;` +//~^ ERROR does not live long enough + + let _u = { let mut _t1 = D(Box::new("t1")); D(&_t1).unit() } ; // suggest `;` +//~^ ERROR does not live long enough + + let _x = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `let x = ...; x` +//~^ ERROR does not live long enough + let _x = { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } ; // no error + + let mut _y; + _y = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `let x = ...; x` +//~^ ERROR does not live long enough + _y = { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } ; // no error +} + +fn f_param_ref(_t1: D<Box<&'static str>>) { D(&_t1).unit() } // no error + +fn f_local_ref() { let mut _t1 = D(Box::new("t1")); D(&_t1).unit() } // suggest `;` +//~^ ERROR does not live long enough + +fn f() -> String { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } // `let x = ...; x` +//~^ ERROR does not live long enough + +#[derive(Debug)] +struct D<T: std::fmt::Debug>(T); + +impl<T: std::fmt::Debug> Drop for D<T> { + fn drop(&mut self) { + println!("dropping {:?})", self); + } +} + +impl<T: std::fmt::Debug> D<T> { + fn next<U: std::fmt::Debug>(&self, _other: U) -> D<U> { D(_other) } + fn end(&self) -> String { format!("End({:?})", self.0) } + fn unit(&self) { } +} diff --git a/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr b/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr new file mode 100644 index 000000000..25226e296 --- /dev/null +++ b/src/test/ui/nll/issue-54556-used-vs-unused-tails.stderr @@ -0,0 +1,146 @@ +error[E0597]: `_t1` does not live long enough + --> $DIR/issue-54556-used-vs-unused-tails.rs:10:55 + | +LL | { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // suggest `;` + | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | | | + | | | `_t1` dropped here while still borrowed + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | { let mut _t1 = D(Box::new("t1")); D(&_t1).end(); } ; // suggest `;` + | + + +error[E0597]: `_t1` does not live long enough + --> $DIR/issue-54556-used-vs-unused-tails.rs:13:55 + | +LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } } ; // suggest `;` + | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | | | + | | | `_t1` dropped here while still borrowed + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end(); } } ; // suggest `;` + | + + +error[E0597]: `_t1` does not live long enough + --> $DIR/issue-54556-used-vs-unused-tails.rs:16:55 + | +LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }; } // suggest `;` + | --^^^^- -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | | | + | | | `_t1` dropped here while still borrowed + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end(); }; } // suggest `;` + | + + +error[E0597]: `_t1` does not live long enough + --> $DIR/issue-54556-used-vs-unused-tails.rs:19:55 + | +LL | let _ = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // suggest `;` + | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | | | + | | | `_t1` dropped here while still borrowed + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | let _ = { let mut _t1 = D(Box::new("t1")); D(&_t1).end(); } ; // suggest `;` + | + + +error[E0597]: `_t1` does not live long enough + --> $DIR/issue-54556-used-vs-unused-tails.rs:22:55 + | +LL | let _u = { let mut _t1 = D(Box::new("t1")); D(&_t1).unit() } ; // suggest `;` + | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | | | + | | | `_t1` dropped here while still borrowed + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | let _u = { let mut _t1 = D(Box::new("t1")); D(&_t1).unit(); } ; // suggest `;` + | + + +error[E0597]: `_t1` does not live long enough + --> $DIR/issue-54556-used-vs-unused-tails.rs:25:55 + | +LL | let _x = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `let x = ...; x` + | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | | | + | | | `_t1` dropped here while still borrowed + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... + | + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let _x = { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } ; // `let x = ...; x` + | +++++++ +++ + +error[E0597]: `_t1` does not live long enough + --> $DIR/issue-54556-used-vs-unused-tails.rs:30:55 + | +LL | _y = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `let x = ...; x` + | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | | | + | | | `_t1` dropped here while still borrowed + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... + | + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | _y = { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } ; // `let x = ...; x` + | +++++++ +++ + +error[E0597]: `_t1` does not live long enough + --> $DIR/issue-54556-used-vs-unused-tails.rs:37:55 + | +LL | fn f_local_ref() { let mut _t1 = D(Box::new("t1")); D(&_t1).unit() } // suggest `;` + | --^^^^- - + | | | | + | | | `_t1` dropped here while still borrowed + | | | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | fn f_local_ref() { let mut _t1 = D(Box::new("t1")); D(&_t1).unit(); } // suggest `;` + | + + +error[E0597]: `_t1` does not live long enough + --> $DIR/issue-54556-used-vs-unused-tails.rs:40:55 + | +LL | fn f() -> String { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } // `let x = ...; x` + | --^^^^- - + | | | | + | | | `_t1` dropped here while still borrowed + | | | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D` + | | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... + | + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | fn f() -> String { let mut _t1 = D(Box::new("t1")); let x = D(&_t1).end(); x } // `let x = ...; x` + | +++++++ +++ + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-54556-wrap-it-up.rs b/src/test/ui/nll/issue-54556-wrap-it-up.rs new file mode 100644 index 000000000..11dbef0d8 --- /dev/null +++ b/src/test/ui/nll/issue-54556-wrap-it-up.rs @@ -0,0 +1,28 @@ +// This is testing how the diagnostic from issue #54556 behaves when +// the destructor code is attached to a place held in a field of the +// temporary being dropped. +// +// Eventually it would be nice if the diagnostic would actually report +// that specific place and its type that implements the `Drop` trait. +// But for the short term, it is acceptable to just print out the +// whole type of the temporary. + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +} diff --git a/src/test/ui/nll/issue-54556-wrap-it-up.stderr b/src/test/ui/nll/issue-54556-wrap-it-up.stderr new file mode 100644 index 000000000..9f27fac15 --- /dev/null +++ b/src/test/ui/nll/issue-54556-wrap-it-up.stderr @@ -0,0 +1,14 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/issue-54556-wrap-it-up.rs:27:5 + | +LL | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +LL | x = 1; + | ^^^^^ assignment to borrowed `x` occurs here +LL | } + | - borrow might be used here, when `foo` is dropped and runs the destructor for type `Foo<'_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/issue-54779-anon-static-lifetime.rs b/src/test/ui/nll/issue-54779-anon-static-lifetime.rs new file mode 100644 index 000000000..260b6b109 --- /dev/null +++ b/src/test/ui/nll/issue-54779-anon-static-lifetime.rs @@ -0,0 +1,49 @@ +// Regression test for #54779, checks if the diagnostics are confusing. + +trait DebugWith<Cx: ?Sized> { + fn debug_with<'me>(&'me self, cx: &'me Cx) -> DebugCxPair<'me, Self, Cx> { + DebugCxPair { value: self, cx } + } + + fn fmt_with(&self, cx: &Cx, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; +} + +struct DebugCxPair<'me, Value: ?Sized, Cx: ?Sized> +where + Value: DebugWith<Cx>, +{ + value: &'me Value, + cx: &'me Cx, +} + +trait DebugContext {} + +struct Foo { + bar: Bar, +} + +impl DebugWith<dyn DebugContext> for Foo { + fn fmt_with( + &self, + cx: &dyn DebugContext, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + let Foo { bar } = self; + bar.debug_with(cx); //~ ERROR: lifetime may not live long enough + Ok(()) + } +} + +struct Bar {} + +impl DebugWith<dyn DebugContext> for Bar { + fn fmt_with( + &self, + cx: &dyn DebugContext, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Ok(()) + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-54779-anon-static-lifetime.stderr b/src/test/ui/nll/issue-54779-anon-static-lifetime.stderr new file mode 100644 index 000000000..64ad7a21a --- /dev/null +++ b/src/test/ui/nll/issue-54779-anon-static-lifetime.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/issue-54779-anon-static-lifetime.rs:32:24 + | +LL | cx: &dyn DebugContext, + | - let's call the lifetime of this reference `'1` +... +LL | bar.debug_with(cx); + | ^^ cast requires that `'1` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-54943-3.rs b/src/test/ui/nll/issue-54943-3.rs new file mode 100644 index 000000000..077eb1563 --- /dev/null +++ b/src/test/ui/nll/issue-54943-3.rs @@ -0,0 +1,19 @@ +// check-pass +// FIXME(#54943) This test targets the scenario where proving the WF requirements requires +// knowing the value of the `_` type present in the user type annotation - unfortunately, figuring +// out the value of that `_` requires type-checking the surrounding code, but that code is dead, +// so our NLL region checker doesn't have access to it. This test should actually fail to compile. + +#![allow(warnings)] + +use std::fmt::Debug; + +fn foo<T: 'static + Debug>(_: T) { } + +fn bar<'a>() { + return; + + let _x = foo::<Vec<_>>(Vec::<&'a u32>::new()); +} + +fn main() {} diff --git a/src/test/ui/nll/issue-55288.rs b/src/test/ui/nll/issue-55288.rs new file mode 100644 index 000000000..aab2dc267 --- /dev/null +++ b/src/test/ui/nll/issue-55288.rs @@ -0,0 +1,9 @@ +// check-pass + +struct Slice(&'static [&'static [u8]]); + +static MAP: Slice = Slice(&[ + b"CloseEvent" as &'static [u8], +]); + +fn main() {} diff --git a/src/test/ui/nll/issue-55344.rs b/src/test/ui/nll/issue-55344.rs new file mode 100644 index 000000000..20f18dc46 --- /dev/null +++ b/src/test/ui/nll/issue-55344.rs @@ -0,0 +1,14 @@ +// check-pass + +#![deny(unused_mut)] + +pub fn foo() { + return; + + let mut v = 0; + assert_eq!(v, 0); + v = 1; + assert_eq!(v, 1); +} + +fn main() {} diff --git a/src/test/ui/nll/issue-55394.rs b/src/test/ui/nll/issue-55394.rs new file mode 100644 index 000000000..f813d1c91 --- /dev/null +++ b/src/test/ui/nll/issue-55394.rs @@ -0,0 +1,13 @@ +struct Bar; + +struct Foo<'s> { + bar: &'s mut Bar, +} + +impl Foo<'_> { + fn new(bar: &mut Bar) -> Self { + Foo { bar } //~ERROR + } +} + +fn main() { } diff --git a/src/test/ui/nll/issue-55394.stderr b/src/test/ui/nll/issue-55394.stderr new file mode 100644 index 000000000..24b8c84b4 --- /dev/null +++ b/src/test/ui/nll/issue-55394.stderr @@ -0,0 +1,12 @@ +error: lifetime may not live long enough + --> $DIR/issue-55394.rs:9:9 + | +LL | fn new(bar: &mut Bar) -> Self { + | - ---- return type is Foo<'2> + | | + | let's call the lifetime of this reference `'1` +LL | Foo { bar } + | ^^^^^^^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-55401.rs b/src/test/ui/nll/issue-55401.rs new file mode 100644 index 000000000..fc45824e9 --- /dev/null +++ b/src/test/ui/nll/issue-55401.rs @@ -0,0 +1,6 @@ +fn static_to_a_to_static_through_ref_in_tuple<'a>(x: &'a u32) -> &'static u32 { + let (ref y, _z): (&'a u32, u32) = (&22, 44); + *y //~ ERROR +} + +fn main() {} diff --git a/src/test/ui/nll/issue-55401.stderr b/src/test/ui/nll/issue-55401.stderr new file mode 100644 index 000000000..4f797f26a --- /dev/null +++ b/src/test/ui/nll/issue-55401.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/issue-55401.rs:3:5 + | +LL | fn static_to_a_to_static_through_ref_in_tuple<'a>(x: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | let (ref y, _z): (&'a u32, u32) = (&22, 44); +LL | *y + | ^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-55511.rs b/src/test/ui/nll/issue-55511.rs new file mode 100644 index 000000000..7dfa9c7bc --- /dev/null +++ b/src/test/ui/nll/issue-55511.rs @@ -0,0 +1,19 @@ +#![warn(indirect_structural_match)] +use std::cell::Cell; +trait Foo<'a> { + const C: Option<Cell<&'a u32>>; +} + +impl<'a, T> Foo<'a> for T { + const C: Option<Cell<&'a u32>> = None; +} + +fn main() { + let a = 22; + let b = Some(Cell::new(&a)); + //~^ ERROR `a` does not live long enough [E0597] + match b { + <() as Foo<'static>>::C => { } + _ => { } + } +} diff --git a/src/test/ui/nll/issue-55511.stderr b/src/test/ui/nll/issue-55511.stderr new file mode 100644 index 000000000..bf3e58e8c --- /dev/null +++ b/src/test/ui/nll/issue-55511.stderr @@ -0,0 +1,15 @@ +error[E0597]: `a` does not live long enough + --> $DIR/issue-55511.rs:13:28 + | +LL | let b = Some(Cell::new(&a)); + | ^^ borrowed value does not live long enough +... +LL | <() as Foo<'static>>::C => { } + | ----------------------- type annotation requires that `a` is borrowed for `'static` +... +LL | } + | - `a` 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/nll/issue-55651.rs b/src/test/ui/nll/issue-55651.rs new file mode 100644 index 000000000..75ba48271 --- /dev/null +++ b/src/test/ui/nll/issue-55651.rs @@ -0,0 +1,28 @@ +// check-pass + +use std::mem::ManuallyDrop; + +struct A; +struct B; + +union U { + a: ManuallyDrop<A>, + b: ManuallyDrop<B>, +} + +fn main() { + unsafe { + { + let mut u = U { a: ManuallyDrop::new(A) }; + let a = u.a; + u.a = ManuallyDrop::new(A); + let a = u.a; // OK + } + { + let mut u = U { a: ManuallyDrop::new(A) }; + let a = u.a; + u.b = ManuallyDrop::new(B); + let a = u.a; // OK + } + } +} diff --git a/src/test/ui/nll/issue-55825-const-fn.rs b/src/test/ui/nll/issue-55825-const-fn.rs new file mode 100644 index 000000000..8aaa19813 --- /dev/null +++ b/src/test/ui/nll/issue-55825-const-fn.rs @@ -0,0 +1,8 @@ +// Regression test for issue #55825 +// Tests that we don't emit a spurious warning in NLL mode + +// check-pass + +const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() } + +fn main() { } diff --git a/src/test/ui/nll/issue-55850.rs b/src/test/ui/nll/issue-55850.rs new file mode 100644 index 000000000..e6279bd02 --- /dev/null +++ b/src/test/ui/nll/issue-55850.rs @@ -0,0 +1,35 @@ +#![allow(unused_mut)] +#![feature(generators, generator_trait)] + +use std::marker::Unpin; +use std::ops::Generator; +use std::ops::GeneratorState::Yielded; +use std::pin::Pin; + +pub struct GenIter<G>(G); + +impl <G> Iterator for GenIter<G> +where + G: Generator + Unpin, +{ + type Item = G::Yield; + + fn next(&mut self) -> Option<Self::Item> { + match Pin::new(&mut self.0).resume(()) { + Yielded(y) => Some(y), + _ => None + } + } +} + +fn bug<'a>() -> impl Iterator<Item = &'a str> { + GenIter(move || { + let mut s = String::new(); + yield &s[..] //~ ERROR cannot yield value referencing local variable `s` [E0515] + //~| ERROR borrow may still be in use when generator yields + }) +} + +fn main() { + bug(); +} diff --git a/src/test/ui/nll/issue-55850.stderr b/src/test/ui/nll/issue-55850.stderr new file mode 100644 index 000000000..86a8cdc42 --- /dev/null +++ b/src/test/ui/nll/issue-55850.stderr @@ -0,0 +1,19 @@ +error[E0515]: cannot yield value referencing local variable `s` + --> $DIR/issue-55850.rs:28:9 + | +LL | yield &s[..] + | ^^^^^^^-^^^^ + | | | + | | `s` is borrowed here + | yields a value referencing data owned by the current function + +error[E0626]: borrow may still be in use when generator yields + --> $DIR/issue-55850.rs:28:16 + | +LL | yield &s[..] + | -------^---- possible yield occurs here + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0515, E0626. +For more information about an error, try `rustc --explain E0515`. diff --git a/src/test/ui/nll/issue-57100.rs b/src/test/ui/nll/issue-57100.rs new file mode 100644 index 000000000..f15929334 --- /dev/null +++ b/src/test/ui/nll/issue-57100.rs @@ -0,0 +1,67 @@ +#![allow(unused)] + + +// This tests the error messages for borrows of union fields when the unions are embedded in other +// structs or unions. + +#[derive(Clone, Copy, Default)] +struct Leaf { + l1_u8: u8, + l2_u8: u8, +} + +#[derive(Clone, Copy)] +union First { + f1_leaf: Leaf, + f2_leaf: Leaf, + f3_union: Second, +} + +#[derive(Clone, Copy)] +union Second { + s1_leaf: Leaf, + s2_leaf: Leaf, +} + +struct Root { + r1_u8: u8, + r2_union: First, +} + +// Borrow a different field of the nested union. +fn nested_union() { + unsafe { + let mut r = Root { + r1_u8: 3, + r2_union: First { f3_union: Second { s2_leaf: Leaf { l1_u8: 8, l2_u8: 4 } } } + }; + + let mref = &mut r.r2_union.f3_union.s1_leaf.l1_u8; + // ^^^^^^^ + *mref = 22; + let nref = &r.r2_union.f3_union.s2_leaf.l1_u8; + // ^^^^^^^ + //~^^ ERROR cannot borrow `r.r2_union.f3_union` (via `r.r2_union.f3_union.s2_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f3_union.s1_leaf.l1_u8`) [E0502] + println!("{} {}", mref, nref) + } +} + +// Borrow a different field of the first union. +fn first_union() { + unsafe { + let mut r = Root { + r1_u8: 3, + r2_union: First { f3_union: Second { s2_leaf: Leaf { l1_u8: 8, l2_u8: 4 } } } + }; + + let mref = &mut r.r2_union.f2_leaf.l1_u8; + // ^^^^^^^ + *mref = 22; + let nref = &r.r2_union.f1_leaf.l1_u8; + // ^^^^^^^ + //~^^ ERROR cannot borrow `r.r2_union` (via `r.r2_union.f1_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f2_leaf.l1_u8`) [E0502] + println!("{} {}", mref, nref) + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-57100.stderr b/src/test/ui/nll/issue-57100.stderr new file mode 100644 index 000000000..523c3e8d0 --- /dev/null +++ b/src/test/ui/nll/issue-57100.stderr @@ -0,0 +1,31 @@ +error[E0502]: cannot borrow `r.r2_union.f3_union` (via `r.r2_union.f3_union.s2_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f3_union.s1_leaf.l1_u8`) + --> $DIR/issue-57100.rs:42:20 + | +LL | let mref = &mut r.r2_union.f3_union.s1_leaf.l1_u8; + | -------------------------------------- mutable borrow occurs here (via `r.r2_union.f3_union.s1_leaf.l1_u8`) +... +LL | let nref = &r.r2_union.f3_union.s2_leaf.l1_u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow of `r.r2_union.f3_union.s2_leaf.l1_u8` -- which overlaps with `r.r2_union.f3_union.s1_leaf.l1_u8` -- occurs here +... +LL | println!("{} {}", mref, nref) + | ---- mutable borrow later used here + | + = note: `r.r2_union.f3_union.s2_leaf.l1_u8` is a field of the union `Second`, so it overlaps the field `r.r2_union.f3_union.s1_leaf.l1_u8` + +error[E0502]: cannot borrow `r.r2_union` (via `r.r2_union.f1_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f2_leaf.l1_u8`) + --> $DIR/issue-57100.rs:60:20 + | +LL | let mref = &mut r.r2_union.f2_leaf.l1_u8; + | ----------------------------- mutable borrow occurs here (via `r.r2_union.f2_leaf.l1_u8`) +... +LL | let nref = &r.r2_union.f1_leaf.l1_u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow of `r.r2_union.f1_leaf.l1_u8` -- which overlaps with `r.r2_union.f2_leaf.l1_u8` -- occurs here +... +LL | println!("{} {}", mref, nref) + | ---- mutable borrow later used here + | + = note: `r.r2_union.f1_leaf.l1_u8` is a field of the union `First`, so it overlaps the field `r.r2_union.f2_leaf.l1_u8` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/nll/issue-57265-return-type-wf-check.rs b/src/test/ui/nll/issue-57265-return-type-wf-check.rs new file mode 100644 index 000000000..8fb8351ce --- /dev/null +++ b/src/test/ui/nll/issue-57265-return-type-wf-check.rs @@ -0,0 +1,24 @@ +use std::any::Any; + +#[derive(Debug, Clone)] +struct S<T: 'static>(T); + +// S<&'a T> is in the return type, so we get an implied bound +// &'a T: 'static +fn foo<'a, T>(x: &'a T) -> (S<&'a T>, Box<dyn Any + 'static>) { + let y = S(x); + + let z = Box::new(y.clone()) as Box<dyn Any + 'static>; + (y, z) +} + +fn main() { + let x = 5; + + // Check that we require that the argument is of type `&'static String`, + // so that the return type is well-formed. + let (_, z) = foo(&"hello".to_string()); + //~^ ERROR temporary value dropped while borrowed + + println!("{:?}", z.downcast_ref::<S<&'static String>>()); +} diff --git a/src/test/ui/nll/issue-57265-return-type-wf-check.stderr b/src/test/ui/nll/issue-57265-return-type-wf-check.stderr new file mode 100644 index 000000000..20add62b9 --- /dev/null +++ b/src/test/ui/nll/issue-57265-return-type-wf-check.stderr @@ -0,0 +1,12 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/issue-57265-return-type-wf-check.rs:20:23 + | +LL | let (_, z) = foo(&"hello".to_string()); + | -----^^^^^^^^^^^^^^^^^^^-- temporary value is freed at the end of this statement + | | | + | | creates a temporary which is freed while still in use + | argument requires that borrow lasts for `'static` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0716`. diff --git a/src/test/ui/nll/issue-57280-1-flipped.rs b/src/test/ui/nll/issue-57280-1-flipped.rs new file mode 100644 index 000000000..ad4b8dcfd --- /dev/null +++ b/src/test/ui/nll/issue-57280-1-flipped.rs @@ -0,0 +1,23 @@ +// This test should compile, as the lifetimes +// in matches don't really matter. +// +// We currently use contravariance when checking the +// type of match arms. + +trait Foo<'a> { + const C: &'a u32; +} + +impl<'a, T> Foo<'a> for T { + const C: &'a u32 = &22; +} + +fn foo<'a>(x: &'static u32) { + match x { + <() as Foo<'a>>::C => { } + //~^ ERROR lifetime may not live long enough + &_ => { } + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-57280-1-flipped.stderr b/src/test/ui/nll/issue-57280-1-flipped.stderr new file mode 100644 index 000000000..7a2135a2a --- /dev/null +++ b/src/test/ui/nll/issue-57280-1-flipped.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/issue-57280-1-flipped.rs:17:9 + | +LL | fn foo<'a>(x: &'static u32) { + | -- lifetime `'a` defined here +LL | match x { +LL | <() as Foo<'a>>::C => { } + | ^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-57280-1.rs b/src/test/ui/nll/issue-57280-1.rs new file mode 100644 index 000000000..b8979624e --- /dev/null +++ b/src/test/ui/nll/issue-57280-1.rs @@ -0,0 +1,19 @@ +// check-pass + +trait Foo<'a> { + const C: &'a u32; +} + +impl<'a, T> Foo<'a> for T { + const C: &'a u32 = &22; +} + +fn foo() { + let a = 22; + match &a { + <() as Foo<'static>>::C => { } + &_ => { } + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-57280.rs b/src/test/ui/nll/issue-57280.rs new file mode 100644 index 000000000..b9d336ec3 --- /dev/null +++ b/src/test/ui/nll/issue-57280.rs @@ -0,0 +1,20 @@ +// check-pass + +trait Foo { + const BLAH: &'static str; +} + +struct Placeholder; + +impl Foo for Placeholder { + const BLAH: &'static str = "hi"; +} + +fn foo(x: &str) { + match x { + <Placeholder as Foo>::BLAH => { } + _ => { } + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-57642-higher-ranked-subtype.rs b/src/test/ui/nll/issue-57642-higher-ranked-subtype.rs new file mode 100644 index 000000000..eba859cde --- /dev/null +++ b/src/test/ui/nll/issue-57642-higher-ranked-subtype.rs @@ -0,0 +1,38 @@ +// Regression test for issue #57642 +// Tests that we reject a bad higher-ranked subtype + +trait X { + type G; + fn make_g() -> Self::G; +} + +impl<'a> X for fn(&'a ()) { + type G = &'a (); + + fn make_g() -> Self::G { + &() + } +} + +trait Y { + type F; + fn make_f() -> Self::F; +} + +impl<T> Y for fn(T) { + type F = fn(T); + + fn make_f() -> Self::F { + |_| {} + } +} + +fn higher_ranked_region_has_lost_its_binder() { + let x = <fn (&())>::make_g(); //~ ERROR the function +} + +fn magical() { + let x = <fn (&())>::make_f(); //~ ERROR no function +} + +fn main() {} diff --git a/src/test/ui/nll/issue-57642-higher-ranked-subtype.stderr b/src/test/ui/nll/issue-57642-higher-ranked-subtype.stderr new file mode 100644 index 000000000..0ae6b7c1d --- /dev/null +++ b/src/test/ui/nll/issue-57642-higher-ranked-subtype.stderr @@ -0,0 +1,31 @@ +error[E0599]: the function or associated item `make_g` exists for fn pointer `for<'r> fn(&'r ())`, but its trait bounds were not satisfied + --> $DIR/issue-57642-higher-ranked-subtype.rs:31:25 + | +LL | let x = <fn (&())>::make_g(); + | ^^^^^^ function or associated item cannot be called on `for<'r> fn(&'r ())` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `for<'r> fn(&'r ()): X` + = help: items from traits can only be used if the trait is implemented and in scope +note: `X` defines an item `make_g`, perhaps you need to implement it + --> $DIR/issue-57642-higher-ranked-subtype.rs:4:1 + | +LL | trait X { + | ^^^^^^^ + +error[E0599]: no function or associated item named `make_f` found for fn pointer `for<'r> fn(&'r ())` in the current scope + --> $DIR/issue-57642-higher-ranked-subtype.rs:35:25 + | +LL | let x = <fn (&())>::make_f(); + | ^^^^^^ function or associated item not found in `for<'r> fn(&'r ())` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Y` defines an item `make_f`, perhaps you need to implement it + --> $DIR/issue-57642-higher-ranked-subtype.rs:17:1 + | +LL | trait Y { + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/nll/issue-57960.rs b/src/test/ui/nll/issue-57960.rs new file mode 100644 index 000000000..32e45184a --- /dev/null +++ b/src/test/ui/nll/issue-57960.rs @@ -0,0 +1,38 @@ +// run-pass + +#![allow(dead_code)] + +trait Range { + const FIRST: u8; + const LAST: u8; +} + +struct OneDigit; +impl Range for OneDigit { + const FIRST: u8 = 0; + const LAST: u8 = 9; +} + +struct TwoDigits; +impl Range for TwoDigits { + const FIRST: u8 = 10; + const LAST: u8 = 99; +} + +struct ThreeDigits; +impl Range for ThreeDigits { + const FIRST: u8 = 100; + const LAST: u8 = 255; +} + +fn digits(x: u8) -> u32 { + match x { + OneDigit::FIRST..=OneDigit::LAST => 1, + TwoDigits::FIRST..=TwoDigits::LAST => 2, + ThreeDigits::FIRST..=ThreeDigits::LAST => 3, + } +} + +fn main() { + assert_eq!(digits(100), 3); +} diff --git a/src/test/ui/nll/issue-57989.rs b/src/test/ui/nll/issue-57989.rs new file mode 100644 index 000000000..8f3dec454 --- /dev/null +++ b/src/test/ui/nll/issue-57989.rs @@ -0,0 +1,10 @@ +// Test for ICE from issue 57989 + +fn f(x: &i32) { + let g = &x; + *x = 0; //~ ERROR cannot assign to `*x`, which is behind a `&` reference + //~| ERROR cannot assign to `*x` because it is borrowed + g; +} + +fn main() {} diff --git a/src/test/ui/nll/issue-57989.stderr b/src/test/ui/nll/issue-57989.stderr new file mode 100644 index 000000000..e85e63e52 --- /dev/null +++ b/src/test/ui/nll/issue-57989.stderr @@ -0,0 +1,24 @@ +error[E0594]: cannot assign to `*x`, which is behind a `&` reference + --> $DIR/issue-57989.rs:5:5 + | +LL | fn f(x: &i32) { + | ---- help: consider changing this to be a mutable reference: `&mut i32` +LL | let g = &x; +LL | *x = 0; + | ^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written + +error[E0506]: cannot assign to `*x` because it is borrowed + --> $DIR/issue-57989.rs:5:5 + | +LL | let g = &x; + | -- borrow of `*x` occurs here +LL | *x = 0; + | ^^^^^^ assignment to borrowed `*x` occurs here +LL | +LL | g; + | - borrow later used here + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0506, E0594. +For more information about an error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/issue-58053.rs b/src/test/ui/nll/issue-58053.rs new file mode 100644 index 000000000..d5a2fa1a3 --- /dev/null +++ b/src/test/ui/nll/issue-58053.rs @@ -0,0 +1,11 @@ +fn main() { + let i = &3; + + let f = |x: &i32| -> &i32 { x }; + //~^ ERROR lifetime may not live long enough + let j = f(i); + + let g = |x: &i32| { x }; + //~^ ERROR lifetime may not live long enough + let k = g(i); +} diff --git a/src/test/ui/nll/issue-58053.stderr b/src/test/ui/nll/issue-58053.stderr new file mode 100644 index 000000000..bf7416e1a --- /dev/null +++ b/src/test/ui/nll/issue-58053.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/issue-58053.rs:4:33 + | +LL | let f = |x: &i32| -> &i32 { x }; + | - - ^ returning this value requires that `'1` must outlive `'2` + | | | + | | let's call the lifetime of this reference `'2` + | let's call the lifetime of this reference `'1` + +error: lifetime may not live long enough + --> $DIR/issue-58053.rs:8:25 + | +LL | let g = |x: &i32| { x }; + | - - ^ returning this value requires that `'1` must outlive `'2` + | | | + | | return type of closure is &'2 i32 + | let's call the lifetime of this reference `'1` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/issue-58299.rs b/src/test/ui/nll/issue-58299.rs new file mode 100644 index 000000000..0587fe8b4 --- /dev/null +++ b/src/test/ui/nll/issue-58299.rs @@ -0,0 +1,27 @@ +struct A<'a>(&'a ()); + +trait Y { + const X: i32; +} + +impl Y for A<'static> { + const X: i32 = 10; +} + +fn foo<'a>(x: i32) { + match x { + // This uses <A<'a> as Y>::X, but `A<'a>` does not implement `Y`. + A::<'a>::X..=A::<'static>::X => (), //~ ERROR lifetime may not live long enough + _ => (), + } +} + +fn bar<'a>(x: i32) { + match x { + // This uses <A<'a> as Y>::X, but `A<'a>` does not implement `Y`. + A::<'static>::X..=A::<'a>::X => (), //~ ERROR lifetime may not live long enough + _ => (), + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-58299.stderr b/src/test/ui/nll/issue-58299.stderr new file mode 100644 index 000000000..509ba67bd --- /dev/null +++ b/src/test/ui/nll/issue-58299.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/issue-58299.rs:14:9 + | +LL | fn foo<'a>(x: i32) { + | -- lifetime `'a` defined here +... +LL | A::<'a>::X..=A::<'static>::X => (), + | ^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/issue-58299.rs:22:27 + | +LL | fn bar<'a>(x: i32) { + | -- lifetime `'a` defined here +... +LL | A::<'static>::X..=A::<'a>::X => (), + | ^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/issue-61311-normalize.rs b/src/test/ui/nll/issue-61311-normalize.rs new file mode 100644 index 000000000..77d67b07a --- /dev/null +++ b/src/test/ui/nll/issue-61311-normalize.rs @@ -0,0 +1,34 @@ +// Regression test for #61311 +// We would ICE after failing to normalize `Self::Proj` in the `impl` below. + +// check-pass + +pub struct Unit; +trait Obj {} + +trait Bound {} +impl Bound for Unit {} + +pub trait HasProj { + type Proj; +} + +impl<T> HasProj for T { + type Proj = Unit; +} + +trait HasProjFn { + type Proj; + fn the_fn(_: Self::Proj); +} + +impl HasProjFn for Unit +where + Box<dyn Obj + 'static>: HasProj, + <Box<dyn Obj + 'static> as HasProj>::Proj: Bound, +{ + type Proj = Unit; + fn the_fn(_: Self::Proj) {} +} + +fn main() {} diff --git a/src/test/ui/nll/issue-61320-normalize.rs b/src/test/ui/nll/issue-61320-normalize.rs new file mode 100644 index 000000000..095bef03f --- /dev/null +++ b/src/test/ui/nll/issue-61320-normalize.rs @@ -0,0 +1,160 @@ +// Regression test for #61320 +// This is the same issue as #61311, just a larger test case. + +// check-pass + +pub struct AndThen<A, B, F> +where + A: Future, + B: IntoFuture, +{ + state: (A, B::Future, F), +} + +pub struct FutureResult<T, E> { + inner: Option<Result<T, E>>, +} + +impl<T, E> Future for FutureResult<T, E> { + type Item = T; + type Error = E; + + fn poll(&mut self) -> Poll<T, E> { + unimplemented!() + } +} + +pub type Poll<T, E> = Result<T, E>; + +impl<A, B, F> Future for AndThen<A, B, F> +where + A: Future, + B: IntoFuture<Error = A::Error>, + F: FnOnce(A::Item) -> B, +{ + type Item = B::Item; + type Error = B::Error; + + fn poll(&mut self) -> Poll<B::Item, B::Error> { + unimplemented!() + } +} + +pub trait Future { + type Item; + + type Error; + + fn poll(&mut self) -> Poll<Self::Item, Self::Error>; + + fn and_then<F, B>(self, f: F) -> AndThen<Self, B, F> + where + F: FnOnce(Self::Item) -> B, + B: IntoFuture<Error = Self::Error>, + Self: Sized, + { + unimplemented!() + } +} + +pub trait IntoFuture { + /// The future that this type can be converted into. + type Future: Future<Item = Self::Item, Error = Self::Error>; + + /// The item that the future may resolve with. + type Item; + /// The error that the future may resolve with. + type Error; + + /// Consumes this object and produces a future. + fn into_future(self) -> Self::Future; +} + +impl<F: Future> IntoFuture for F { + type Future = F; + type Item = F::Item; + type Error = F::Error; + + fn into_future(self) -> F { + self + } +} + +impl<F: ?Sized + Future> Future for ::std::boxed::Box<F> { + type Item = F::Item; + type Error = F::Error; + + fn poll(&mut self) -> Poll<Self::Item, Self::Error> { + (**self).poll() + } +} + +impl<T, E> IntoFuture for Result<T, E> { + type Future = FutureResult<T, E>; + type Item = T; + type Error = E; + + fn into_future(self) -> FutureResult<T, E> { + unimplemented!() + } +} + +struct Request<T>(T); + +trait RequestContext {} +impl<T> RequestContext for T {} +struct NoContext; +impl AsRef<NoContext> for NoContext { + fn as_ref(&self) -> &Self { + &NoContext + } +} + +type BoxedError = Box<dyn std::error::Error + Send + Sync>; +type DefaultFuture<T, E> = Box<dyn Future<Item = T, Error = E> + Send>; + +trait Guard: Sized { + type Result: IntoFuture<Item = Self, Error = BoxedError>; + fn from_request(request: &Request<()>) -> Self::Result; +} + +trait FromRequest: Sized { + type Context; + type Future: Future<Item = Self, Error = BoxedError> + Send; + fn from_request(request: Request<()>) -> Self::Future; +} + +struct MyGuard; +impl Guard for MyGuard { + type Result = Result<Self, BoxedError>; + fn from_request(_request: &Request<()>) -> Self::Result { + Ok(MyGuard) + } +} + +struct Generic<I> { + _inner: I, +} + +impl<I> FromRequest for Generic<I> +where + MyGuard: Guard, + <MyGuard as Guard>::Result: IntoFuture<Item = MyGuard, Error = BoxedError>, + <<MyGuard as Guard>::Result as IntoFuture>::Future: Send, + I: FromRequest<Context = NoContext>, +{ + type Future = DefaultFuture<Self, BoxedError>; + type Context = NoContext; + fn from_request(headers: Request<()>) -> DefaultFuture<Self, BoxedError> { + let _future = <MyGuard as Guard>::from_request(&headers) + .into_future() + .and_then(move |_| { + <I as FromRequest>::from_request(headers) + .into_future() + .and_then(move |fld_inner| Ok(Generic { _inner: fld_inner }).into_future()) + }); + panic!(); + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-61424.fixed b/src/test/ui/nll/issue-61424.fixed new file mode 100644 index 000000000..63e00c172 --- /dev/null +++ b/src/test/ui/nll/issue-61424.fixed @@ -0,0 +1,9 @@ +// run-rustfix + +#![deny(unused_mut)] + +fn main() { + let x; //~ ERROR: variable does not need to be mutable + x = String::new(); + dbg!(x); +} diff --git a/src/test/ui/nll/issue-61424.rs b/src/test/ui/nll/issue-61424.rs new file mode 100644 index 000000000..3b64996c2 --- /dev/null +++ b/src/test/ui/nll/issue-61424.rs @@ -0,0 +1,9 @@ +// run-rustfix + +#![deny(unused_mut)] + +fn main() { + let mut x; //~ ERROR: variable does not need to be mutable + x = String::new(); + dbg!(x); +} diff --git a/src/test/ui/nll/issue-61424.stderr b/src/test/ui/nll/issue-61424.stderr new file mode 100644 index 000000000..6de6b7f3a --- /dev/null +++ b/src/test/ui/nll/issue-61424.stderr @@ -0,0 +1,16 @@ +error: variable does not need to be mutable + --> $DIR/issue-61424.rs:6:9 + | +LL | let mut x; + | ----^ + | | + | help: remove this `mut` + | +note: the lint level is defined here + --> $DIR/issue-61424.rs:3:9 + | +LL | #![deny(unused_mut)] + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-62007-assign-const-index.rs b/src/test/ui/nll/issue-62007-assign-const-index.rs new file mode 100644 index 000000000..3ea5d3a7a --- /dev/null +++ b/src/test/ui/nll/issue-62007-assign-const-index.rs @@ -0,0 +1,32 @@ +// Issue #62007: assigning over a const-index projection of an array +// (in this case, `list[I] = n;`) should in theory be able to kill all borrows +// of `list[0]`, so that `list[0]` could be borrowed on the next +// iteration through the loop. +// +// Currently the compiler does not allow this. We may want to consider +// loosening that restriction in the future. (However, doing so would +// at *least* require T-lang team approval, and probably an RFC; e.g. +// such loosening might make complicate the user's mental mode; it +// also would make code more brittle in the face of refactorings that +// replace constants with variables. + +#![allow(dead_code)] + +struct List<T> { + value: T, + next: Option<Box<List<T>>>, +} + +fn to_refs<T>(mut list: [&mut List<T>; 2]) -> Vec<&mut T> { + let mut result = vec![]; + loop { + result.push(&mut list[0].value); //~ ERROR cannot borrow `list[_].value` as mutable + if let Some(n) = list[0].next.as_mut() { //~ ERROR cannot borrow `list[_].next` as mutable + list[0] = n; + } else { + return result; + } + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-62007-assign-const-index.stderr b/src/test/ui/nll/issue-62007-assign-const-index.stderr new file mode 100644 index 000000000..12e28aa3f --- /dev/null +++ b/src/test/ui/nll/issue-62007-assign-const-index.stderr @@ -0,0 +1,27 @@ +error[E0499]: cannot borrow `list[_].value` as mutable more than once at a time + --> $DIR/issue-62007-assign-const-index.rs:23:21 + | +LL | fn to_refs<T>(mut list: [&mut List<T>; 2]) -> Vec<&mut T> { + | - let's call the lifetime of this reference `'1` +... +LL | result.push(&mut list[0].value); + | ^^^^^^^^^^^^^^^^^^ `list[_].value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list[_].value` is borrowed for `'1` + +error[E0499]: cannot borrow `list[_].next` as mutable more than once at a time + --> $DIR/issue-62007-assign-const-index.rs:24:26 + | +LL | fn to_refs<T>(mut list: [&mut List<T>; 2]) -> Vec<&mut T> { + | - let's call the lifetime of this reference `'1` +... +LL | if let Some(n) = list[0].next.as_mut() { + | ^^^^^^^^^^^^^^^^^^^^^ + | | + | `list[_].next` was mutably borrowed here in the previous iteration of the loop + | argument requires that `list[_].next` is borrowed for `'1` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/src/test/ui/nll/issue-62007-assign-differing-fields.rs b/src/test/ui/nll/issue-62007-assign-differing-fields.rs new file mode 100644 index 000000000..29d92b7b8 --- /dev/null +++ b/src/test/ui/nll/issue-62007-assign-differing-fields.rs @@ -0,0 +1,25 @@ +// Double-check we didn't go too far with our resolution to issue +// #62007: assigning over a field projection (`list.1 = n;` in this +// case) should kill only borrows of `list.1`; `list.0` can *not* +// necessarily be borrowed on the next iteration through the loop. + +#![allow(dead_code)] + +struct List<T> { + value: T, + next: Option<Box<List<T>>>, +} + +fn to_refs<'a, T>(mut list: (&'a mut List<T>, &'a mut List<T>)) -> Vec<&'a mut T> { + let mut result = vec![]; + loop { + result.push(&mut (list.0).value); //~ ERROR cannot borrow `list.0.value` as mutable + if let Some(n) = (list.0).next.as_mut() { //~ ERROR cannot borrow `list.0.next` as mutable + list.1 = n; + } else { + return result; + } + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-62007-assign-differing-fields.stderr b/src/test/ui/nll/issue-62007-assign-differing-fields.stderr new file mode 100644 index 000000000..4488431fc --- /dev/null +++ b/src/test/ui/nll/issue-62007-assign-differing-fields.stderr @@ -0,0 +1,27 @@ +error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time + --> $DIR/issue-62007-assign-differing-fields.rs:16:21 + | +LL | fn to_refs<'a, T>(mut list: (&'a mut List<T>, &'a mut List<T>)) -> Vec<&'a mut T> { + | -- lifetime `'a` defined here +... +LL | result.push(&mut (list.0).value); + | ^^^^^^^^^^^^^^^^^^^ `list.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time + --> $DIR/issue-62007-assign-differing-fields.rs:17:26 + | +LL | fn to_refs<'a, T>(mut list: (&'a mut List<T>, &'a mut List<T>)) -> Vec<&'a mut T> { + | -- lifetime `'a` defined here +... +LL | if let Some(n) = (list.0).next.as_mut() { + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | `list.0.next` was mutably borrowed here in the previous iteration of the loop + | argument requires that `list.0.next` is borrowed for `'a` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/src/test/ui/nll/issue-63154-normalize.rs b/src/test/ui/nll/issue-63154-normalize.rs new file mode 100644 index 000000000..484c12879 --- /dev/null +++ b/src/test/ui/nll/issue-63154-normalize.rs @@ -0,0 +1,34 @@ +// Regression test for rust-lang/rust#63154 +// +// Before, we would ICE after failing to normalize the destination type +// when checking call destinations and also when checking MIR +// assignment statements. + +// check-pass + +trait HasAssocType { + type Inner; +} + +impl HasAssocType for () { + type Inner = (); +} + +trait Tr<I, T>: Fn(I) -> Option<T> {} +impl<I, T, Q: Fn(I) -> Option<T>> Tr<I, T> for Q {} + +fn f<T: HasAssocType>() -> impl Tr<T, T::Inner> { + |_| None +} + +fn g<T, Y>(f: impl Tr<T, Y>) -> impl Tr<T, Y> { + f +} + +fn h() { + g(f())(()); +} + +fn main() { + h(); +} diff --git a/src/test/ui/nll/issue-67007-escaping-data.rs b/src/test/ui/nll/issue-67007-escaping-data.rs new file mode 100644 index 000000000..49ea2e596 --- /dev/null +++ b/src/test/ui/nll/issue-67007-escaping-data.rs @@ -0,0 +1,24 @@ +// Regression test for issue #67007 +// Ensures that we show information about the specific regions involved + +// Covariant over 'a, invariant over 'tcx +struct FnCtxt<'a, 'tcx: 'a>(&'a (), *mut &'tcx ()); + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + fn use_it(&self, _: &'tcx ()) {} +} + +struct Consumer<'tcx>(&'tcx ()); + +impl<'tcx> Consumer<'tcx> { + fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) { + let other = self.use_fcx(fcx); //~ ERROR lifetime may not live long enough + fcx.use_it(other); + } + + fn use_fcx<'a>(&self, _: &FnCtxt<'a, 'tcx>) -> &'a () { + &() + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-67007-escaping-data.stderr b/src/test/ui/nll/issue-67007-escaping-data.stderr new file mode 100644 index 000000000..ac9c59bf7 --- /dev/null +++ b/src/test/ui/nll/issue-67007-escaping-data.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/issue-67007-escaping-data.rs:15:21 + | +LL | impl<'tcx> Consumer<'tcx> { + | ---- lifetime `'tcx` defined here +LL | fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) { + | -- lifetime `'a` defined here +LL | let other = self.use_fcx(fcx); + | ^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'tcx` + | + = help: consider adding the following bound: `'a: 'tcx` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-68550.rs b/src/test/ui/nll/issue-68550.rs new file mode 100644 index 000000000..6bfd18de1 --- /dev/null +++ b/src/test/ui/nll/issue-68550.rs @@ -0,0 +1,15 @@ +// Regression test for issue #68550. +// +// The `&'static A:` where clause was triggering +// ICEs because it wound up being compiled to reference +// the `'empty(U0)` region. + +fn run<'a, A>(x: A) +where + A: 'static, + &'static A: , +{ + let _: &'a A = &x; //~ ERROR `x` does not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/issue-68550.stderr b/src/test/ui/nll/issue-68550.stderr new file mode 100644 index 000000000..e234ebb04 --- /dev/null +++ b/src/test/ui/nll/issue-68550.stderr @@ -0,0 +1,16 @@ +error[E0597]: `x` does not live long enough + --> $DIR/issue-68550.rs:12:20 + | +LL | fn run<'a, A>(x: A) + | -- lifetime `'a` defined here +... +LL | let _: &'a A = &x; + | ----- ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'a` +LL | } + | - `x` 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/nll/issue-69114-static-mut-ty.rs b/src/test/ui/nll/issue-69114-static-mut-ty.rs new file mode 100644 index 000000000..ce37da053 --- /dev/null +++ b/src/test/ui/nll/issue-69114-static-mut-ty.rs @@ -0,0 +1,30 @@ +// Check that borrowck ensures that `static mut` items have the expected type. + +static FOO: u8 = 42; +static mut BAR: &'static u8 = &FOO; +static mut BAR_ELIDED: &u8 = &FOO; + +fn main() { + unsafe { + println!("{} {}", BAR, BAR_ELIDED); + set_bar(); + set_bar_elided(); + println!("{} {}", BAR, BAR_ELIDED); + } +} + +fn set_bar() { + let n = 42; + unsafe { + BAR = &n; + //~^ ERROR does not live long enough + } +} + +fn set_bar_elided() { + let n = 42; + unsafe { + BAR_ELIDED = &n; + //~^ ERROR does not live long enough + } +} diff --git a/src/test/ui/nll/issue-69114-static-mut-ty.stderr b/src/test/ui/nll/issue-69114-static-mut-ty.stderr new file mode 100644 index 000000000..5e55cb502 --- /dev/null +++ b/src/test/ui/nll/issue-69114-static-mut-ty.stderr @@ -0,0 +1,27 @@ +error[E0597]: `n` does not live long enough + --> $DIR/issue-69114-static-mut-ty.rs:19:15 + | +LL | BAR = &n; + | ------^^ + | | | + | | borrowed value does not live long enough + | assignment requires that `n` is borrowed for `'static` +... +LL | } + | - `n` dropped here while still borrowed + +error[E0597]: `n` does not live long enough + --> $DIR/issue-69114-static-mut-ty.rs:27:22 + | +LL | BAR_ELIDED = &n; + | -------------^^ + | | | + | | borrowed value does not live long enough + | assignment requires that `n` is borrowed for `'static` +... +LL | } + | - `n` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-69114-static-ty.rs b/src/test/ui/nll/issue-69114-static-ty.rs new file mode 100644 index 000000000..3318433a1 --- /dev/null +++ b/src/test/ui/nll/issue-69114-static-ty.rs @@ -0,0 +1,9 @@ +// Check that borrowck ensures that `static` items have the expected type. + +static FOO: &'static (dyn Fn(&'static u8) + Send + Sync) = &drop; + +fn main() { + let n = 42; + FOO(&n); + //~^ ERROR does not live long enough +} diff --git a/src/test/ui/nll/issue-69114-static-ty.stderr b/src/test/ui/nll/issue-69114-static-ty.stderr new file mode 100644 index 000000000..0815e74b5 --- /dev/null +++ b/src/test/ui/nll/issue-69114-static-ty.stderr @@ -0,0 +1,15 @@ +error[E0597]: `n` does not live long enough + --> $DIR/issue-69114-static-ty.rs:7:9 + | +LL | FOO(&n); + | ----^^- + | | | + | | borrowed value does not live long enough + | argument requires that `n` is borrowed for `'static` +LL | +LL | } + | - `n` 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/nll/issue-73159-rpit-static.rs b/src/test/ui/nll/issue-73159-rpit-static.rs new file mode 100644 index 000000000..3002408b0 --- /dev/null +++ b/src/test/ui/nll/issue-73159-rpit-static.rs @@ -0,0 +1,13 @@ +// Regression test for issue #73159 +// Tests thar we don't suggest replacing 'a with 'static' + +struct Foo<'a>(&'a [u8]); + +impl<'a> Foo<'a> { + fn make_it(&self) -> impl Iterator<Item = u8> { + self.0.iter().copied() + //~^ ERROR: captures lifetime that does not appear in bounds + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-73159-rpit-static.stderr b/src/test/ui/nll/issue-73159-rpit-static.stderr new file mode 100644 index 000000000..260b9b597 --- /dev/null +++ b/src/test/ui/nll/issue-73159-rpit-static.stderr @@ -0,0 +1,12 @@ +error[E0700]: hidden type for `impl Iterator<Item = u8>` captures lifetime that does not appear in bounds + --> $DIR/issue-73159-rpit-static.rs:8:9 + | +LL | impl<'a> Foo<'a> { + | -- hidden type `Copied<std::slice::Iter<'a, u8>>` captures the lifetime `'a` as defined here +LL | fn make_it(&self) -> impl Iterator<Item = u8> { +LL | self.0.iter().copied() + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/nll/issue-78561.rs b/src/test/ui/nll/issue-78561.rs new file mode 100644 index 000000000..55147fcd1 --- /dev/null +++ b/src/test/ui/nll/issue-78561.rs @@ -0,0 +1,23 @@ +// check-pass +#![feature(type_alias_impl_trait)] + +pub trait Trait { + type A; + + fn f() -> Self::A; +} + +pub trait Tr2<'a, 'b> {} + +pub struct A<T>(T); +pub trait Tr { + type B; +} + +impl<'a, 'b, T: Tr<B = dyn Tr2<'a, 'b>>> Trait for A<T> { + type A = impl core::fmt::Debug; + + fn f() -> Self::A {} +} + +fn main() {} diff --git a/src/test/ui/nll/issue-95272.rs b/src/test/ui/nll/issue-95272.rs new file mode 100644 index 000000000..958cbde37 --- /dev/null +++ b/src/test/ui/nll/issue-95272.rs @@ -0,0 +1,15 @@ +use std::cell::Cell; + +fn check<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) +where + 'a: 'b, +{ +} + +fn test<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) { + let f = check; + //~^ ERROR lifetime may not live long enough + f(x, y); +} + +fn main() {} diff --git a/src/test/ui/nll/issue-95272.stderr b/src/test/ui/nll/issue-95272.stderr new file mode 100644 index 000000000..03edbc3a6 --- /dev/null +++ b/src/test/ui/nll/issue-95272.stderr @@ -0,0 +1,17 @@ +error: lifetime may not live long enough + --> $DIR/issue-95272.rs:10:13 + | +LL | fn test<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let f = check; + | ^^^^^ assignment requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a function pointer to `check` + = note: the function `check` is invariant over the parameter `'a` + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: aborting due to previous error + diff --git a/src/test/ui/nll/issue-97997.rs b/src/test/ui/nll/issue-97997.rs new file mode 100644 index 000000000..c64e720b1 --- /dev/null +++ b/src/test/ui/nll/issue-97997.rs @@ -0,0 +1,16 @@ +trait Foo { + const ASSOC: bool = true; +} +impl<T> Foo for fn(T) {} + +fn foo(_x: i32) {} + +fn impls_foo<T: Foo>(_x: T) {} + +fn main() { + impls_foo(foo as fn(i32)); + + <fn(&u8) as Foo>::ASSOC; + //~^ ERROR implementation of `Foo` is not general enough + //~| ERROR implementation of `Foo` is not general enough +} diff --git a/src/test/ui/nll/issue-97997.stderr b/src/test/ui/nll/issue-97997.stderr new file mode 100644 index 000000000..78401bbf6 --- /dev/null +++ b/src/test/ui/nll/issue-97997.stderr @@ -0,0 +1,20 @@ +error: implementation of `Foo` is not general enough + --> $DIR/issue-97997.rs:13:5 + | +LL | <fn(&u8) as Foo>::ASSOC; + | ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough + | + = note: `Foo` would have to be implemented for the type `for<'r> fn(&'r u8)` + = note: ...but `Foo` is actually implemented for the type `fn(&'0 u8)`, for some specific lifetime `'0` + +error: implementation of `Foo` is not general enough + --> $DIR/issue-97997.rs:13:5 + | +LL | <fn(&u8) as Foo>::ASSOC; + | ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough + | + = note: `Foo` would have to be implemented for the type `for<'r> fn(&'r u8)` + = note: ...but `Foo` is actually implemented for the type `fn(&'0 u8)`, for some specific lifetime `'0` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/issue-98170.rs b/src/test/ui/nll/issue-98170.rs new file mode 100644 index 000000000..6bb12f52d --- /dev/null +++ b/src/test/ui/nll/issue-98170.rs @@ -0,0 +1,25 @@ +pub struct MyStruct<'a> { + field: &'a [u32], +} + +impl MyStruct<'_> { + pub fn new<'a>(field: &'a [u32]) -> MyStruct<'a> { + Self { field } + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough + } +} + +trait Trait<'a> { + fn new(field: &'a [u32]) -> MyStruct<'a>; +} + +impl<'a> Trait<'a> for MyStruct<'_> { + fn new(field: &'a [u32]) -> MyStruct<'a> { + Self { field } + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-98170.stderr b/src/test/ui/nll/issue-98170.stderr new file mode 100644 index 000000000..0d17365e7 --- /dev/null +++ b/src/test/ui/nll/issue-98170.stderr @@ -0,0 +1,44 @@ +error: lifetime may not live long enough + --> $DIR/issue-98170.rs:7:9 + | +LL | impl MyStruct<'_> { + | -- lifetime `'1` appears in the `impl`'s self type +LL | pub fn new<'a>(field: &'a [u32]) -> MyStruct<'a> { + | -- lifetime `'a` defined here +LL | Self { field } + | ^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/issue-98170.rs:7:16 + | +LL | impl MyStruct<'_> { + | -- lifetime `'1` appears in the `impl`'s self type +LL | pub fn new<'a>(field: &'a [u32]) -> MyStruct<'a> { + | -- lifetime `'a` defined here +LL | Self { field } + | ^^^^^ this usage requires that `'a` must outlive `'1` + +error: lifetime may not live long enough + --> $DIR/issue-98170.rs:19:9 + | +LL | impl<'a> Trait<'a> for MyStruct<'_> { + | -- -- lifetime `'1` appears in the `impl`'s self type + | | + | lifetime `'a` defined here +LL | fn new(field: &'a [u32]) -> MyStruct<'a> { +LL | Self { field } + | ^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/issue-98170.rs:19:16 + | +LL | impl<'a> Trait<'a> for MyStruct<'_> { + | -- -- lifetime `'1` appears in the `impl`'s self type + | | + | lifetime `'a` defined here +LL | fn new(field: &'a [u32]) -> MyStruct<'a> { +LL | Self { field } + | ^^^^^ this usage requires that `'a` must outlive `'1` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/nll/issue-98589-closures-relate-named-regions.rs b/src/test/ui/nll/issue-98589-closures-relate-named-regions.rs new file mode 100644 index 000000000..6cc4340bb --- /dev/null +++ b/src/test/ui/nll/issue-98589-closures-relate-named-regions.rs @@ -0,0 +1,36 @@ +// Regression test for #98589. +// Previously, named lifetime `'a` that appears in the closure was unrelated to `'a` +// that appears in the parent function iff `'a` is early-bound. +// This made the following tests pass borrowck. + +// check-fail + +// The bound `'a: 'a` ensures that `'a` is early-bound. +fn test_early_early<'a: 'a, 'b: 'b>() { + || { None::<&'a &'b ()>; }; + //~^ ERROR lifetime may not live long enough +} + +fn test_early_late<'a: 'a, 'b>() { + || { None::<&'a &'b ()>; }; + //~^ ERROR lifetime may not live long enough +} + +// No early-bound lifetime; included for completeness. +fn test_late_late<'a, 'b>() { + || { None::<&'a &'b ()>; }; + //~^ ERROR lifetime may not live long enough +} + +fn test_early_type<'a: 'a, T>() { + || { None::<&'a T>; }; + //~^ ERROR the parameter type `T` may not live long enough +} + +// No early-bound lifetime; included for completeness. +fn test_late_type<'a, T>() { + || { None::<&'a T>; }; + //~^ ERROR the parameter type `T` may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/issue-98589-closures-relate-named-regions.stderr b/src/test/ui/nll/issue-98589-closures-relate-named-regions.stderr new file mode 100644 index 000000000..6def5602e --- /dev/null +++ b/src/test/ui/nll/issue-98589-closures-relate-named-regions.stderr @@ -0,0 +1,61 @@ +error: lifetime may not live long enough + --> $DIR/issue-98589-closures-relate-named-regions.rs:10:5 + | +LL | fn test_early_early<'a: 'a, 'b: 'b>() { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | || { None::<&'a &'b ()>; }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error: lifetime may not live long enough + --> $DIR/issue-98589-closures-relate-named-regions.rs:15:10 + | +LL | fn test_early_late<'a: 'a, 'b>() { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | || { None::<&'a &'b ()>; }; + | ^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error: lifetime may not live long enough + --> $DIR/issue-98589-closures-relate-named-regions.rs:21:10 + | +LL | fn test_late_late<'a, 'b>() { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | || { None::<&'a &'b ()>; }; + | ^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/issue-98589-closures-relate-named-regions.rs:26:5 + | +LL | || { None::<&'a T>; }; + | ^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn test_early_type<'a: 'a, T: 'a>() { + | ++++ + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/issue-98589-closures-relate-named-regions.rs:32:5 + | +LL | || { None::<&'a T>; }; + | ^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn test_late_type<'a, T: 'a>() { + | ++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/issue-98693.rs b/src/test/ui/nll/issue-98693.rs new file mode 100644 index 000000000..18e6ec630 --- /dev/null +++ b/src/test/ui/nll/issue-98693.rs @@ -0,0 +1,21 @@ +// Regression test for #98693. +// +// The closure encounters an obligation that `T` must outlive `!U1`, +// a placeholder from universe U1. We were ignoring this placeholder +// when promoting the constraint to the enclosing function, and +// thus incorrectly judging the closure to be safe. + +fn assert_static<T>() +where + for<'a> T: 'a, +{ +} + +fn test<T>() { + || { + //~^ ERROR the parameter type `T` may not live long enough + assert_static::<T>(); + }; +} + +fn main() {} diff --git a/src/test/ui/nll/issue-98693.stderr b/src/test/ui/nll/issue-98693.stderr new file mode 100644 index 000000000..31689620c --- /dev/null +++ b/src/test/ui/nll/issue-98693.stderr @@ -0,0 +1,17 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/issue-98693.rs:15:5 + | +LL | / || { +LL | | +LL | | assert_static::<T>(); +LL | | }; + | |_____^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn test<T: 'static>() { + | +++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/src/test/ui/nll/lint-no-err.rs b/src/test/ui/nll/lint-no-err.rs new file mode 100644 index 000000000..2d1d5cb26 --- /dev/null +++ b/src/test/ui/nll/lint-no-err.rs @@ -0,0 +1,21 @@ +// check-pass + +// mir borrowck previously incorrectly set `tainted_by_errors` +// when buffering lints, which resulted in ICE later on, +// see #94502. + +struct Repro; +impl Repro { + fn get(&self) -> &i32 { + &3 + } + + fn insert(&mut self, _: i32) {} +} + +fn main() { + let x = &0; + let mut conflict = Repro; + let prev = conflict.get(); + conflict.insert(*prev + *x); +} diff --git a/src/test/ui/nll/loan_ends_mid_block_pair.rs b/src/test/ui/nll/loan_ends_mid_block_pair.rs new file mode 100644 index 000000000..acd6ec706 --- /dev/null +++ b/src/test/ui/nll/loan_ends_mid_block_pair.rs @@ -0,0 +1,29 @@ +#![allow(warnings)] +#![feature(rustc_attrs)] + + +fn main() { +} + +fn nll_fail() { + let mut data = ('a', 'b', 'c'); + let c = &mut data.0; + capitalize(c); + data.0 = 'e'; + //~^ ERROR [E0506] + data.0 = 'f'; + data.0 = 'g'; + capitalize(c); +} + +fn nll_ok() { + let mut data = ('a', 'b', 'c'); + let c = &mut data.0; + capitalize(c); + data.0 = 'e'; + data.0 = 'f'; + data.0 = 'g'; +} + +fn capitalize(_: &mut char) { +} diff --git a/src/test/ui/nll/loan_ends_mid_block_pair.stderr b/src/test/ui/nll/loan_ends_mid_block_pair.stderr new file mode 100644 index 000000000..eb8442b31 --- /dev/null +++ b/src/test/ui/nll/loan_ends_mid_block_pair.stderr @@ -0,0 +1,15 @@ +error[E0506]: cannot assign to `data.0` because it is borrowed + --> $DIR/loan_ends_mid_block_pair.rs:12:5 + | +LL | let c = &mut data.0; + | ----------- borrow of `data.0` occurs here +LL | capitalize(c); +LL | data.0 = 'e'; + | ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here +... +LL | capitalize(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/nll/loan_ends_mid_block_vec.rs b/src/test/ui/nll/loan_ends_mid_block_vec.rs new file mode 100644 index 000000000..2edcdef0a --- /dev/null +++ b/src/test/ui/nll/loan_ends_mid_block_vec.rs @@ -0,0 +1,30 @@ +#![allow(warnings)] +#![feature(rustc_attrs)] + +fn main() { +} + +fn nll_fail() { + let mut data = vec!['a', 'b', 'c']; + let slice = &mut data; + capitalize(slice); + data.push('d'); + //~^ ERROR [E0499] + data.push('e'); + //~^ ERROR [E0499] + data.push('f'); + //~^ ERROR [E0499] + capitalize(slice); +} + +fn nll_ok() { + let mut data = vec!['a', 'b', 'c']; + let slice = &mut data; + capitalize(slice); + data.push('d'); + data.push('e'); + data.push('f'); +} + +fn capitalize(_: &mut [char]) { +} diff --git a/src/test/ui/nll/loan_ends_mid_block_vec.stderr b/src/test/ui/nll/loan_ends_mid_block_vec.stderr new file mode 100644 index 000000000..22c72af61 --- /dev/null +++ b/src/test/ui/nll/loan_ends_mid_block_vec.stderr @@ -0,0 +1,39 @@ +error[E0499]: cannot borrow `data` as mutable more than once at a time + --> $DIR/loan_ends_mid_block_vec.rs:11:5 + | +LL | let slice = &mut data; + | --------- first mutable borrow occurs here +LL | capitalize(slice); +LL | data.push('d'); + | ^^^^^^^^^^^^^^ second mutable borrow occurs here +... +LL | capitalize(slice); + | ----- first borrow later used here + +error[E0499]: cannot borrow `data` as mutable more than once at a time + --> $DIR/loan_ends_mid_block_vec.rs:13:5 + | +LL | let slice = &mut data; + | --------- first mutable borrow occurs here +... +LL | data.push('e'); + | ^^^^^^^^^^^^^^ second mutable borrow occurs here +... +LL | capitalize(slice); + | ----- first borrow later used here + +error[E0499]: cannot borrow `data` as mutable more than once at a time + --> $DIR/loan_ends_mid_block_vec.rs:15:5 + | +LL | let slice = &mut data; + | --------- first mutable borrow occurs here +... +LL | data.push('f'); + | ^^^^^^^^^^^^^^ second mutable borrow occurs here +LL | +LL | capitalize(slice); + | ----- first borrow later used here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/src/test/ui/nll/local-outlives-static-via-hrtb.rs b/src/test/ui/nll/local-outlives-static-via-hrtb.rs new file mode 100644 index 000000000..5f1f9b3a7 --- /dev/null +++ b/src/test/ui/nll/local-outlives-static-via-hrtb.rs @@ -0,0 +1,26 @@ +// Test that we handle the case when a local variable is borrowed for `'static` +// due to an outlives constraint involving a region in an incompatible universe + +pub trait Outlives<'this> {} + +impl<'this, T> Outlives<'this> for T where T: 'this {} +trait Reference { + type AssociatedType; +} + +impl<'a, T: 'a> Reference for &'a T { + type AssociatedType = &'a (); +} + +fn assert_static_via_hrtb<G>(_: G) where for<'a> G: Outlives<'a> {} + +fn assert_static_via_hrtb_with_assoc_type<T>(_: &'_ T) +where + for<'a> &'a T: Reference<AssociatedType = &'a ()>, +{} + +fn main() { + let local = 0; + assert_static_via_hrtb(&local); //~ ERROR `local` does not live long enough + assert_static_via_hrtb_with_assoc_type(&&local); //~ ERROR `local` does not live long enough +} diff --git a/src/test/ui/nll/local-outlives-static-via-hrtb.stderr b/src/test/ui/nll/local-outlives-static-via-hrtb.stderr new file mode 100644 index 000000000..61009da49 --- /dev/null +++ b/src/test/ui/nll/local-outlives-static-via-hrtb.stderr @@ -0,0 +1,26 @@ +error[E0597]: `local` does not live long enough + --> $DIR/local-outlives-static-via-hrtb.rs:24:28 + | +LL | assert_static_via_hrtb(&local); + | -----------------------^^^^^^- + | | | + | | borrowed value does not live long enough + | argument requires that `local` is borrowed for `'static` +LL | assert_static_via_hrtb_with_assoc_type(&&local); +LL | } + | - `local` dropped here while still borrowed + +error[E0597]: `local` does not live long enough + --> $DIR/local-outlives-static-via-hrtb.rs:25:45 + | +LL | assert_static_via_hrtb_with_assoc_type(&&local); + | ----------------------------------------^^^^^^- + | | | + | | borrowed value does not live long enough + | argument requires that `local` is borrowed for `'static` +LL | } + | - `local` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/lub-if.rs b/src/test/ui/nll/lub-if.rs new file mode 100644 index 000000000..50225a783 --- /dev/null +++ b/src/test/ui/nll/lub-if.rs @@ -0,0 +1,44 @@ +// Test that we correctly consider the type of `match` to be the LUB +// of the various arms, particularly in the case where regions are +// involved. + +pub fn opt_str0<'a>(maybestr: &'a Option<String>) -> &'a str { + if maybestr.is_none() { + "(none)" + } else { + let s: &'a str = maybestr.as_ref().unwrap(); + s + } +} + +pub fn opt_str1<'a>(maybestr: &'a Option<String>) -> &'a str { + if maybestr.is_some() { + let s: &'a str = maybestr.as_ref().unwrap(); + s + } else { + "(none)" + } +} + +pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str { + if maybestr.is_none() { + "(none)" + } else { + let s: &'a str = maybestr.as_ref().unwrap(); + s + //~^ ERROR lifetime may not live long enough + } +} + +pub fn opt_str3<'a>(maybestr: &'a Option<String>) -> &'static str { + if maybestr.is_some() { + let s: &'a str = maybestr.as_ref().unwrap(); + s + //~^ ERROR lifetime may not live long enough + } else { + "(none)" + } +} + + +fn main() {} diff --git a/src/test/ui/nll/lub-if.stderr b/src/test/ui/nll/lub-if.stderr new file mode 100644 index 000000000..03f7f9204 --- /dev/null +++ b/src/test/ui/nll/lub-if.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/lub-if.rs:28:9 + | +LL | pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str { + | -- lifetime `'a` defined here +... +LL | s + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/lub-if.rs:36:9 + | +LL | pub fn opt_str3<'a>(maybestr: &'a Option<String>) -> &'static str { + | -- lifetime `'a` defined here +... +LL | s + | ^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/lub-match.rs b/src/test/ui/nll/lub-match.rs new file mode 100644 index 000000000..454dd1fc6 --- /dev/null +++ b/src/test/ui/nll/lub-match.rs @@ -0,0 +1,47 @@ +// Test that we correctly consider the type of `match` to be the LUB +// of the various arms, particularly in the case where regions are +// involved. + +pub fn opt_str0<'a>(maybestr: &'a Option<String>) -> &'a str { + match *maybestr { + Some(ref s) => { + let s: &'a str = s; + s + } + None => "(none)", + } +} + +pub fn opt_str1<'a>(maybestr: &'a Option<String>) -> &'a str { + match *maybestr { + None => "(none)", + Some(ref s) => { + let s: &'a str = s; + s + } + } +} + +pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str { + match *maybestr { + None => "(none)", + Some(ref s) => { + let s: &'a str = s; + s + //~^ ERROR lifetime may not live long enough + } + } +} + +pub fn opt_str3<'a>(maybestr: &'a Option<String>) -> &'static str { + match *maybestr { + Some(ref s) => { + let s: &'a str = s; + s + //~^ ERROR lifetime may not live long enough + } + None => "(none)", + } +} + +fn main() {} diff --git a/src/test/ui/nll/lub-match.stderr b/src/test/ui/nll/lub-match.stderr new file mode 100644 index 000000000..208ec07a1 --- /dev/null +++ b/src/test/ui/nll/lub-match.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/lub-match.rs:30:13 + | +LL | pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str { + | -- lifetime `'a` defined here +... +LL | s + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/lub-match.rs:40:13 + | +LL | pub fn opt_str3<'a>(maybestr: &'a Option<String>) -> &'static str { + | -- lifetime `'a` defined here +... +LL | s + | ^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/match-cfg-fake-edges.rs b/src/test/ui/nll/match-cfg-fake-edges.rs new file mode 100644 index 000000000..252f7f8ba --- /dev/null +++ b/src/test/ui/nll/match-cfg-fake-edges.rs @@ -0,0 +1,42 @@ +// Test that we have enough false edges to avoid exposing the exact matching +// algorithm in borrow checking. + +fn guard_always_precedes_arm(y: i32) { + let mut x; + // x should always be initialized, as the only way to reach the arm is + // through the guard. + match y { + 0 | 2 if { x = 2; true } => x, + _ => 2, + }; +} + +fn guard_may_be_skipped(y: i32) { + let x; + // Even though x *is* always initialized, we don't want to have borrowck + // results be based on whether patterns are exhaustive. + match y { + _ if { x = 2; true } => 1, + _ if { + x; //~ ERROR E0381 + false + } => 2, + _ => 3, + }; +} + +fn guard_may_be_taken(y: bool) { + let x = String::new(); + // Even though x *is* never moved before the use, we don't want to have + // borrowck results be based on whether patterns are disjoint. + match y { + false if { drop(x); true } => 1, + true => { + x; //~ ERROR use of moved value: `x` + 2 + } + false => 3, + }; +} + +fn main() {} diff --git a/src/test/ui/nll/match-cfg-fake-edges.stderr b/src/test/ui/nll/match-cfg-fake-edges.stderr new file mode 100644 index 000000000..250aa482e --- /dev/null +++ b/src/test/ui/nll/match-cfg-fake-edges.stderr @@ -0,0 +1,28 @@ +error[E0381]: used binding `x` isn't initialized + --> $DIR/match-cfg-fake-edges.rs:21:13 + | +LL | let x; + | - binding declared here but left uninitialized +... +LL | _ if { x = 2; true } => 1, + | ----- binding initialized here in some conditions +LL | _ if { +LL | x; + | ^ `x` used here but it isn't initialized + +error[E0382]: use of moved value: `x` + --> $DIR/match-cfg-fake-edges.rs:35:13 + | +LL | let x = String::new(); + | - move occurs because `x` has type `String`, which does not implement the `Copy` trait +... +LL | false if { drop(x); true } => 1, + | - value moved here +LL | true => { +LL | x; + | ^ value used here after move + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0381, E0382. +For more information about an error, try `rustc --explain E0381`. diff --git a/src/test/ui/nll/match-cfg-fake-edges2.rs b/src/test/ui/nll/match-cfg-fake-edges2.rs new file mode 100644 index 000000000..48f95e03b --- /dev/null +++ b/src/test/ui/nll/match-cfg-fake-edges2.rs @@ -0,0 +1,18 @@ +// Test that we have enough false edges to avoid exposing the exact matching +// algorithm in borrow checking. + +fn all_previous_tests_may_be_done(y: &mut (bool, bool)) { + let r = &mut y.1; + // We don't actually test y.1 to select the second arm, but we don't want + // borrowck results to be based on the order we match patterns. + match y { //~ ERROR cannot use `y.1` because it was mutably borrowed + (false, true) => 1, + (true, _) => { + r; + 2 + } + (false, _) => 3, + }; +} + +fn main() {} diff --git a/src/test/ui/nll/match-cfg-fake-edges2.stderr b/src/test/ui/nll/match-cfg-fake-edges2.stderr new file mode 100644 index 000000000..c6d15a936 --- /dev/null +++ b/src/test/ui/nll/match-cfg-fake-edges2.stderr @@ -0,0 +1,15 @@ +error[E0503]: cannot use `y.1` because it was mutably borrowed + --> $DIR/match-cfg-fake-edges2.rs:8:5 + | +LL | let r = &mut y.1; + | -------- borrow of `y.1` occurs here +... +LL | match y { + | ^^^^^^^ use of borrowed `y.1` +... +LL | r; + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0503`. diff --git a/src/test/ui/nll/match-guards-always-borrow.rs b/src/test/ui/nll/match-guards-always-borrow.rs new file mode 100644 index 000000000..87dba187b --- /dev/null +++ b/src/test/ui/nll/match-guards-always-borrow.rs @@ -0,0 +1,41 @@ +// Here is arielb1's basic example from rust-lang/rust#27282 +// that AST borrowck is flummoxed by: + +fn should_reject_destructive_mutate_in_guard() { + match Some(&4) { + None => {}, + ref mut foo if { + (|| { let bar = foo; bar.take() })(); + //~^ ERROR cannot move out of `foo` in pattern guard [E0507] + false } => { }, + Some(s) => std::process::exit(*s), + } +} + +// Here below is a case that needs to keep working: we only use the +// binding via immutable-borrow in the guard, and we mutate in the arm +// body. +fn allow_mutate_in_arm_body() { + match Some(&4) { + None => {}, + ref mut foo if foo.is_some() && false => { foo.take(); () } + Some(s) => std::process::exit(*s), + } +} + +// Here below is a case that needs to keep working: we only use the +// binding via immutable-borrow in the guard, and we move into the arm +// body. +fn allow_move_into_arm_body() { + match Some(&4) { + None => {}, + mut foo if foo.is_some() && false => { foo.take(); () } + Some(s) => std::process::exit(*s), + } +} + +fn main() { + should_reject_destructive_mutate_in_guard(); + allow_mutate_in_arm_body(); + allow_move_into_arm_body(); +} diff --git a/src/test/ui/nll/match-guards-always-borrow.stderr b/src/test/ui/nll/match-guards-always-borrow.stderr new file mode 100644 index 000000000..c0fb5f653 --- /dev/null +++ b/src/test/ui/nll/match-guards-always-borrow.stderr @@ -0,0 +1,13 @@ +error[E0507]: cannot move out of `foo` in pattern guard + --> $DIR/match-guards-always-borrow.rs:8:14 + | +LL | (|| { let bar = foo; bar.take() })(); + | ^^ --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait + | | + | move out of `foo` occurs here + | + = note: variables bound in patterns cannot be moved from until after the end of the pattern guard + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/src/test/ui/nll/match-guards-partially-borrow.rs b/src/test/ui/nll/match-guards-partially-borrow.rs new file mode 100644 index 000000000..81ae19ebf --- /dev/null +++ b/src/test/ui/nll/match-guards-partially-borrow.rs @@ -0,0 +1,169 @@ +// Test that a (partially) mutably borrowed place can be matched on, so long as +// we don't have to read any values that are mutably borrowed to determine +// which arm to take. +// +// Test that we don't allow mutating the value being matched on in a way that +// changes which patterns it matches, until we have chosen an arm. + +fn ok_mutation_in_guard(mut q: i32) { + match q { + // OK, mutation doesn't change which patterns g matches + _ if { q = 1; false } => (), + _ => (), + } +} + +fn ok_mutation_in_guard2(mut u: bool) { + // OK value of u is unused before modification + match u { + _ => (), + _ if { + u = true; + false + } => (), + x => (), + } +} + +fn ok_mutation_in_guard4(mut w: (&mut bool,)) { + // OK value of u is unused before modification + match w { + _ => (), + _ if { + *w.0 = true; + false + } => (), + x => (), + } +} + +fn ok_indirect_mutation_in_guard(mut p: &bool) { + match *p { + // OK, mutation doesn't change which patterns s matches + _ if { + p = &true; + false + } => (), + _ => (), + } +} + +fn mutation_invalidates_pattern_in_guard(mut q: bool) { + match q { + // q doesn't match the pattern with the guard by the end of the guard. + false if { + q = true; //~ ERROR + true + } => (), + _ => (), + } +} + +fn mutation_invalidates_previous_pattern_in_guard(mut r: bool) { + match r { + // r matches a previous pattern by the end of the guard. + true => (), + _ if { + r = true; //~ ERROR + true + } => (), + _ => (), + } +} + +fn match_on_borrowed_early_end(mut s: bool) { + let h = &mut s; + // OK value of s is unused before modification. + match s { + _ if { + *h = !*h; + false + } => (), + true => (), + false => (), + } +} + +fn bad_mutation_in_guard(mut t: bool) { + match t { + true => (), + false if { + t = true; //~ ERROR + false + } => (), + false => (), + } +} + +fn bad_mutation_in_guard2(mut x: Option<Option<&i32>>) { + // Check that nested patterns are checked. + match x { + None => (), + Some(None) => (), + _ if { + match x { + Some(ref mut r) => *r = None, //~ ERROR + _ => return, + }; + false + } => (), + Some(Some(r)) => println!("{}", r), + } +} + +fn bad_mutation_in_guard3(mut t: bool) { + match t { + s if { + t = !t; //~ ERROR + false + } => (), // What value should `s` have in the arm? + _ => (), + } +} + +fn bad_indirect_mutation_in_guard(mut y: &bool) { + match *y { + true => (), + false if { + y = &true; //~ ERROR + false + } => (), + false => (), + } +} + +fn bad_indirect_mutation_in_guard2(mut z: &bool) { + match z { + &true => (), + &false if { + z = &true; //~ ERROR + false + } => (), + &false => (), + } +} + +fn bad_indirect_mutation_in_guard3(mut a: &bool) { + // Same as bad_indirect_mutation_in_guard2, but using match ergonomics + match a { + true => (), + false if { + a = &true; //~ ERROR + false + } => (), + false => (), + } +} + +fn bad_indirect_mutation_in_guard4(mut b: &bool) { + match b { + &_ => (), + &_ if { + b = &true; //~ ERROR + false + } => (), + &b => (), + } +} + +fn main() {} diff --git a/src/test/ui/nll/match-guards-partially-borrow.stderr b/src/test/ui/nll/match-guards-partially-borrow.stderr new file mode 100644 index 000000000..48e3a7c69 --- /dev/null +++ b/src/test/ui/nll/match-guards-partially-borrow.stderr @@ -0,0 +1,87 @@ +error[E0510]: cannot assign `q` in match guard + --> $DIR/match-guards-partially-borrow.rs:55:13 + | +LL | match q { + | - value is immutable in match guard +... +LL | q = true; + | ^^^^^^^^ cannot assign + +error[E0510]: cannot assign `r` in match guard + --> $DIR/match-guards-partially-borrow.rs:67:13 + | +LL | match r { + | - value is immutable in match guard +... +LL | r = true; + | ^^^^^^^^ cannot assign + +error[E0510]: cannot assign `t` in match guard + --> $DIR/match-guards-partially-borrow.rs:91:13 + | +LL | match t { + | - value is immutable in match guard +... +LL | t = true; + | ^^^^^^^^ cannot assign + +error[E0510]: cannot mutably borrow `x.0` in match guard + --> $DIR/match-guards-partially-borrow.rs:105:22 + | +LL | match x { + | - value is immutable in match guard +... +LL | Some(ref mut r) => *r = None, + | ^^^^^^^^^ cannot mutably borrow + +error[E0506]: cannot assign to `t` because it is borrowed + --> $DIR/match-guards-partially-borrow.rs:117:13 + | +LL | s if { + | - borrow of `t` occurs here +LL | t = !t; + | ^^^^^^ assignment to borrowed `t` occurs here +LL | false +LL | } => (), // What value should `s` have in the arm? + | - borrow later used here + +error[E0510]: cannot assign `y` in match guard + --> $DIR/match-guards-partially-borrow.rs:128:13 + | +LL | match *y { + | -- value is immutable in match guard +... +LL | y = &true; + | ^^^^^^^^^ cannot assign + +error[E0510]: cannot assign `z` in match guard + --> $DIR/match-guards-partially-borrow.rs:139:13 + | +LL | match z { + | - value is immutable in match guard +... +LL | z = &true; + | ^^^^^^^^^ cannot assign + +error[E0510]: cannot assign `a` in match guard + --> $DIR/match-guards-partially-borrow.rs:151:13 + | +LL | match a { + | - value is immutable in match guard +... +LL | a = &true; + | ^^^^^^^^^ cannot assign + +error[E0510]: cannot assign `b` in match guard + --> $DIR/match-guards-partially-borrow.rs:162:13 + | +LL | match b { + | - value is immutable in match guard +... +LL | b = &true; + | ^^^^^^^^^ cannot assign + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0506, E0510. +For more information about an error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/match-on-borrowed.rs b/src/test/ui/nll/match-on-borrowed.rs new file mode 100644 index 000000000..447dabeb4 --- /dev/null +++ b/src/test/ui/nll/match-on-borrowed.rs @@ -0,0 +1,96 @@ +// Test that a (partially) mutably borrowed place can be matched on, so long as +// we don't have to read any values that are mutably borrowed to determine +// which arm to take. +// +// Test that we don't allow mutating the value being matched on in a way that +// changes which patterns it matches, until we have chosen an arm. + +struct A(i32, i32); + +fn struct_example(mut a: A) { + let x = &mut a.0; + match a { // OK, no access of borrowed data + _ if false => (), + A(_, r) => (), + } + x; +} + +fn indirect_struct_example(mut b: &mut A) { + let x = &mut b.0; + match *b { // OK, no access of borrowed data + _ if false => (), + A(_, r) => (), + } + x; +} + +fn underscore_example(mut c: i32) { + let r = &mut c; + match c { // OK, no access of borrowed data (or any data at all) + _ if false => (), + _ => (), + } + r; +} + +enum E { + V(i32, i32), + W, +} + +fn enum_example(mut e: E) { + let x = match e { + E::V(ref mut x, _) => x, + E::W => panic!(), + }; + match e { // Don't know that E uses a tag for its discriminant + //~^ ERROR + _ if false => (), + E::V(_, r) => (), + E::W => (), + } + x; +} + +fn indirect_enum_example(mut f: &mut E) { + let x = match *f { + E::V(ref mut x, _) => x, + E::W => panic!(), + }; + match f { // Don't know that E uses a tag for its discriminant + //~^ ERROR + _ if false => (), + E::V(_, r) => (), + E::W => (), + } + x; +} + +fn match_on_muatbly_borrowed_ref(mut p: &bool) { + let r = &mut p; + match *p { // OK, no access at all + _ if false => (), + _ => (), + } + r; +} + +fn match_on_borrowed(mut t: bool) { + let x = &mut t; + match t { + //~^ ERROR + true => (), + false => (), + } + x; +} + +enum Never {} + +fn never_init() { + let n: Never; + match n {} //~ ERROR +} + +fn main() {} diff --git a/src/test/ui/nll/match-on-borrowed.stderr b/src/test/ui/nll/match-on-borrowed.stderr new file mode 100644 index 000000000..664f36f69 --- /dev/null +++ b/src/test/ui/nll/match-on-borrowed.stderr @@ -0,0 +1,47 @@ +error[E0503]: cannot use `e` because it was mutably borrowed + --> $DIR/match-on-borrowed.rs:47:11 + | +LL | E::V(ref mut x, _) => x, + | --------- borrow of `e.0` occurs here +... +LL | match e { // Don't know that E uses a tag for its discriminant + | ^ use of borrowed `e.0` +... +LL | x; + | - borrow later used here + +error[E0503]: cannot use `*f` because it was mutably borrowed + --> $DIR/match-on-borrowed.rs:61:11 + | +LL | E::V(ref mut x, _) => x, + | --------- borrow of `f.0` occurs here +... +LL | match f { // Don't know that E uses a tag for its discriminant + | ^ use of borrowed `f.0` +... +LL | x; + | - borrow later used here + +error[E0503]: cannot use `t` because it was mutably borrowed + --> $DIR/match-on-borrowed.rs:81:5 + | +LL | let x = &mut t; + | ------ borrow of `t` occurs here +LL | match t { + | ^^^^^^^ use of borrowed `t` +... +LL | x; + | - borrow later used here + +error[E0381]: used binding `n` isn't initialized + --> $DIR/match-on-borrowed.rs:93:11 + | +LL | let n: Never; + | - binding declared here but left uninitialized +LL | match n {} + | ^ `n` used here but it isn't initialized + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0381, E0503. +For more information about an error, try `rustc --explain E0381`. diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs new file mode 100644 index 000000000..1de32ddf5 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs @@ -0,0 +1,19 @@ +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + std::mem::drop(foo.b); + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones +} diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr new file mode 100644 index 000000000..80e297807 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr @@ -0,0 +1,15 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:17:5 + | +LL | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +LL | x = 1; + | ^^^^^ assignment to borrowed `x` occurs here +LL | // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones +LL | } + | - borrow might be used here, when `foo` is dropped and runs the destructor for type `Foo<'_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs new file mode 100644 index 000000000..32e07cd14 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs @@ -0,0 +1,18 @@ +// check-pass + +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + std::mem::drop(wrap); + x = 1; // OK, drop is inert +} diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs new file mode 100644 index 000000000..778212918 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs @@ -0,0 +1,20 @@ +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + std::mem::drop(foo.a); + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +} diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr new file mode 100644 index 000000000..14074472e --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr @@ -0,0 +1,14 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop-with-fragment.rs:19:5 + | +LL | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +LL | x = 1; + | ^^^^^ assignment to borrowed `x` occurs here +LL | } + | - borrow might be used here, when `foo` is dropped and runs the destructor for type `Foo<'_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs new file mode 100644 index 000000000..b0d6e27a3 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs @@ -0,0 +1,22 @@ +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + std::mem::drop(foo.a); + std::mem::drop(foo.b); + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + // FIXME ^ This currently errors and it should not. +} diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr new file mode 100644 index 000000000..91c0afc1d --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr @@ -0,0 +1,15 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop-with-uninitialized-fragments.rs:20:5 + | +LL | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +LL | x = 1; + | ^^^^^ assignment to borrowed `x` occurs here +LL | // FIXME ^ This currently errors and it should not. +LL | } + | - borrow might be used here, when `foo` is dropped and runs the destructor for type `Foo<'_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs new file mode 100644 index 000000000..44a7ede78 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.rs @@ -0,0 +1,15 @@ +#![allow(warnings)] + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +fn main() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +} diff --git a/src/test/ui/nll/maybe-initialized-drop.stderr b/src/test/ui/nll/maybe-initialized-drop.stderr new file mode 100644 index 000000000..9825ba461 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.stderr @@ -0,0 +1,13 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/maybe-initialized-drop.rs:14:5 + | +LL | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +LL | x = 1; + | ^^^^^ assignment to borrowed `x` occurs here +LL | } + | - borrow might be used here, when `wrap` is dropped and runs the `Drop` code for type `Wrap` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/nll/mir_check_cast_closure.rs b/src/test/ui/nll/mir_check_cast_closure.rs new file mode 100644 index 000000000..4aebcfdb4 --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_closure.rs @@ -0,0 +1,9 @@ +#![allow(dead_code)] + +fn bar<'a, 'b>() -> fn(&'a u32, &'b u32) -> &'a u32 { + let g: fn(_, _) -> _ = |_x, y| y; + g + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/mir_check_cast_closure.stderr b/src/test/ui/nll/mir_check_cast_closure.stderr new file mode 100644 index 000000000..72d99aad9 --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_closure.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/mir_check_cast_closure.rs:5:5 + | +LL | fn bar<'a, 'b>() -> fn(&'a u32, &'b u32) -> &'a u32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let g: fn(_, _) -> _ = |_x, y| y; +LL | g + | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/mir_check_cast_reify.rs b/src/test/ui/nll/mir_check_cast_reify.rs new file mode 100644 index 000000000..951459911 --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_reify.rs @@ -0,0 +1,39 @@ +#![allow(dead_code)] + +// Test that we relate the type of the fn type to the type of the fn +// ptr when doing a `ReifyFnPointer` cast. +// +// This test is a bit tortured, let me explain: +// + +// The `where 'a: 'a` clause here ensures that `'a` is early bound, +// which is needed below to ensure that this test hits the path we are +// concerned with. +fn foo<'a>(x: &'a u32) -> &'a u32 +where + 'a: 'a, +{ + panic!() +} + +fn bar<'a>(x: &'a u32) -> &'static u32 { + // Here, the type of `foo` is `typeof(foo::<'x>)` for some fresh variable `'x`. + // During NLL region analysis, this will get renumbered to `typeof(foo::<'?0>)` + // where `'?0` is a new region variable. + // + // (Note that if `'a` on `foo` were late-bound, the type would be + // `typeof(foo)`, which would interact differently with because + // the renumbering later.) + // + // This type is then coerced to a fn type `fn(&'?1 u32) -> &'?2 + // u32`. Here, the `'?1` and `'?2` will have been created during + // the NLL region renumbering. + // + // The MIR type checker must therefore relate `'?0` to `'?1` and `'?2` + // as part of checking the `ReifyFnPointer`. + let f: fn(_) -> _ = foo; + f(x) + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/mir_check_cast_reify.stderr b/src/test/ui/nll/mir_check_cast_reify.stderr new file mode 100644 index 000000000..9be2670fe --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_reify.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/mir_check_cast_reify.rs:35:5 + | +LL | fn bar<'a>(x: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +... +LL | f(x) + | ^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/mir_check_cast_unsafe_fn.rs b/src/test/ui/nll/mir_check_cast_unsafe_fn.rs new file mode 100644 index 000000000..8f55bedfb --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_unsafe_fn.rs @@ -0,0 +1,11 @@ +#![allow(dead_code)] + +fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 { + // Here the NLL checker must relate the types in `f` to the types + // in `g`. These are related via the `UnsafeFnPointer` cast. + let g: unsafe fn(_) -> _ = f; + unsafe { g(input) } + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/mir_check_cast_unsafe_fn.stderr b/src/test/ui/nll/mir_check_cast_unsafe_fn.stderr new file mode 100644 index 000000000..321d17ba6 --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_unsafe_fn.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/mir_check_cast_unsafe_fn.rs:7:14 + | +LL | fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +... +LL | unsafe { g(input) } + | ^^^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/mir_check_cast_unsize.rs b/src/test/ui/nll/mir_check_cast_unsize.rs new file mode 100644 index 000000000..f6c100ab6 --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_unsize.rs @@ -0,0 +1,10 @@ +#![allow(dead_code)] + +use std::fmt::Debug; + +fn bar<'a>(x: &'a u32) -> &'static dyn Debug { + x + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/mir_check_cast_unsize.stderr b/src/test/ui/nll/mir_check_cast_unsize.stderr new file mode 100644 index 000000000..1cd2579e4 --- /dev/null +++ b/src/test/ui/nll/mir_check_cast_unsize.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/mir_check_cast_unsize.rs:6:5 + | +LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug { + | -- lifetime `'a` defined here +LL | x + | ^ returning this value requires that `'a` must outlive `'static` + | +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'a` lifetime bound + | +LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug + 'a { + | ++++ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/move-errors.rs b/src/test/ui/nll/move-errors.rs new file mode 100644 index 000000000..e0fcd6250 --- /dev/null +++ b/src/test/ui/nll/move-errors.rs @@ -0,0 +1,117 @@ +struct A(String); +struct C(D); + +fn suggest_remove_deref() { + let a = &A("".to_string()); + let b = *a; + //~^ ERROR +} + +fn suggest_borrow() { + let a = [A("".to_string())]; + let b = a[0]; + //~^ ERROR +} + +fn suggest_borrow2() { + let mut a = A("".to_string()); + let r = &&mut a; + let s = **r; + //~^ ERROR +} + +fn suggest_borrow3() { + use std::rc::Rc; + let mut a = A("".to_string()); + let r = Rc::new(a); + let s = *r; + //~^ ERROR +} + +fn suggest_borrow4() { + let a = [A("".to_string())][0]; + //~^ ERROR +} + +fn suggest_borrow5() { + let a = &A("".to_string()); + let A(s) = *a; + //~^ ERROR +} + +fn suggest_ref() { + let c = C(D(String::new())); + let C(D(s)) = c; + //~^ ERROR +} + +fn suggest_nothing() { + let a = &A("".to_string()); + let b; + b = *a; + //~^ ERROR +} + +enum B { + V(String), + U(D), +} + +struct D(String); + +impl Drop for D { + fn drop(&mut self) {} +} + +struct F(String, String); + +impl Drop for F { + fn drop(&mut self) {} +} + +fn probably_suggest_borrow() { + let x = [B::V(String::new())]; + match x[0] { + //~^ ERROR + B::U(d) => (), + B::V(s) => (), + } +} + +fn have_to_suggest_ref() { + let x = B::V(String::new()); + match x { + //~^ ERROR + B::V(s) => drop(s), + B::U(D(s)) => (), + }; +} + +fn two_separate_errors() { + let x = (D(String::new()), &String::new()); + match x { + //~^ ERROR + //~^^ ERROR + (D(s), &t) => (), + _ => (), + } +} + +fn have_to_suggest_double_ref() { + let x = F(String::new(), String::new()); + match x { + //~^ ERROR + F(s, mut t) => (), + _ => (), + } +} + +fn double_binding(x: &Result<String, String>) { + match *x { + //~^ ERROR + Ok(s) | Err(s) => (), + } +} + +fn main() { +} diff --git a/src/test/ui/nll/move-errors.stderr b/src/test/ui/nll/move-errors.stderr new file mode 100644 index 000000000..b03fcf70b --- /dev/null +++ b/src/test/ui/nll/move-errors.stderr @@ -0,0 +1,152 @@ +error[E0507]: cannot move out of `*a` which is behind a shared reference + --> $DIR/move-errors.rs:6:13 + | +LL | let b = *a; + | ^^ + | | + | move occurs because `*a` has type `A`, which does not implement the `Copy` trait + | help: consider borrowing here: `&*a` + +error[E0508]: cannot move out of type `[A; 1]`, a non-copy array + --> $DIR/move-errors.rs:12:13 + | +LL | let b = a[0]; + | ^^^^ + | | + | cannot move out of here + | move occurs because `a[_]` has type `A`, which does not implement the `Copy` trait + | help: consider borrowing here: `&a[0]` + +error[E0507]: cannot move out of `**r` which is behind a shared reference + --> $DIR/move-errors.rs:19:13 + | +LL | let s = **r; + | ^^^ + | | + | move occurs because `**r` has type `A`, which does not implement the `Copy` trait + | help: consider borrowing here: `&**r` + +error[E0507]: cannot move out of an `Rc` + --> $DIR/move-errors.rs:27:13 + | +LL | let s = *r; + | ^^ + | | + | move occurs because value has type `A`, which does not implement the `Copy` trait + | help: consider borrowing here: `&*r` + +error[E0508]: cannot move out of type `[A; 1]`, a non-copy array + --> $DIR/move-errors.rs:32:13 + | +LL | let a = [A("".to_string())][0]; + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because value has type `A`, which does not implement the `Copy` trait + | help: consider borrowing here: `&[A("".to_string())][0]` + +error[E0507]: cannot move out of `a` which is behind a shared reference + --> $DIR/move-errors.rs:38:16 + | +LL | let A(s) = *a; + | - ^^ help: consider borrowing here: `&*a` + | | + | data moved here + | move occurs because `s` has type `String`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `D`, which implements the `Drop` trait + --> $DIR/move-errors.rs:44:19 + | +LL | let C(D(s)) = c; + | - ^ cannot move out of here + | | + | data moved here + | move occurs because `s` has type `String`, which does not implement the `Copy` trait + +error[E0507]: cannot move out of `*a` which is behind a shared reference + --> $DIR/move-errors.rs:51:9 + | +LL | b = *a; + | ^^ move occurs because `*a` has type `A`, which does not implement the `Copy` trait + +error[E0508]: cannot move out of type `[B; 1]`, a non-copy array + --> $DIR/move-errors.rs:74:11 + | +LL | match x[0] { + | ^^^^ + | | + | cannot move out of here + | help: consider borrowing here: `&x[0]` +LL | +LL | B::U(d) => (), + | - data moved here +LL | B::V(s) => (), + | - ...and here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait + +error[E0509]: cannot move out of type `D`, which implements the `Drop` trait + --> $DIR/move-errors.rs:83:11 + | +LL | match x { + | ^ cannot move out of here +... +LL | B::U(D(s)) => (), + | - + | | + | data moved here + | move occurs because `s` has type `String`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `D`, which implements the `Drop` trait + --> $DIR/move-errors.rs:92:11 + | +LL | match x { + | ^ cannot move out of here +... +LL | (D(s), &t) => (), + | - + | | + | data moved here + | move occurs because `s` has type `String`, which does not implement the `Copy` trait + +error[E0507]: cannot move out of `*x.1` which is behind a shared reference + --> $DIR/move-errors.rs:92:11 + | +LL | match x { + | ^ +... +LL | (D(s), &t) => (), + | - + | | + | data moved here + | move occurs because `t` has type `String`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `F`, which implements the `Drop` trait + --> $DIR/move-errors.rs:102:11 + | +LL | match x { + | ^ cannot move out of here +LL | +LL | F(s, mut t) => (), + | - ----- ...and here + | | + | data moved here + | + = note: move occurs because these variables have types that don't implement the `Copy` trait + +error[E0507]: cannot move out of `x` as enum variant `Err` which is behind a shared reference + --> $DIR/move-errors.rs:110:11 + | +LL | match *x { + | ^^ help: consider borrowing here: `&*x` +LL | +LL | Ok(s) | Err(s) => (), + | - + | | + | data moved here + | move occurs because `s` has type `String`, which does not implement the `Copy` trait + +error: aborting due to 14 previous errors + +Some errors have detailed explanations: E0507, E0508, E0509. +For more information about an error, try `rustc --explain E0507`. diff --git a/src/test/ui/nll/move-subpaths-moves-root.rs b/src/test/ui/nll/move-subpaths-moves-root.rs new file mode 100644 index 000000000..d266c6bb6 --- /dev/null +++ b/src/test/ui/nll/move-subpaths-moves-root.rs @@ -0,0 +1,5 @@ +fn main() { + let x = (vec![1, 2, 3], ); + drop(x.0); + drop(x); //~ ERROR use of partially moved value +} diff --git a/src/test/ui/nll/move-subpaths-moves-root.stderr b/src/test/ui/nll/move-subpaths-moves-root.stderr new file mode 100644 index 000000000..ae9287f92 --- /dev/null +++ b/src/test/ui/nll/move-subpaths-moves-root.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of partially moved value: `x` + --> $DIR/move-subpaths-moves-root.rs:4:10 + | +LL | drop(x.0); + | --- value partially moved here +LL | drop(x); + | ^ value used here after partial move + | + = note: partial move occurs because `x.0` has type `Vec<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/nll/mutating_references.rs b/src/test/ui/nll/mutating_references.rs new file mode 100644 index 000000000..eb46b30b6 --- /dev/null +++ b/src/test/ui/nll/mutating_references.rs @@ -0,0 +1,24 @@ +// run-pass + +struct List<T> { + value: T, + next: Option<Box<List<T>>>, +} + +fn to_refs<T>(mut list: &mut List<T>) -> Vec<&mut T> { + let mut result = vec![]; + loop { + result.push(&mut list.value); + if let Some(n) = list.next.as_mut() { + list = n; + } else { + return result; + } + } +} + +fn main() { + let mut list = List { value: 1, next: None }; + let vec = to_refs(&mut list); + assert_eq!(vec![&mut 1], vec); +} diff --git a/src/test/ui/nll/normalization-bounds-error.rs b/src/test/ui/nll/normalization-bounds-error.rs new file mode 100644 index 000000000..b6cfcd987 --- /dev/null +++ b/src/test/ui/nll/normalization-bounds-error.rs @@ -0,0 +1,15 @@ +// Check that we error when a bound from the impl is not satisfied when +// normalizing an associated type. + +trait Visitor<'d> { + type Value; +} + +impl<'a, 'd: 'a> Visitor<'d> for &'a () { + type Value = (); +} + +fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} +//~^ ERROR + +fn main() {} diff --git a/src/test/ui/nll/normalization-bounds-error.stderr b/src/test/ui/nll/normalization-bounds-error.stderr new file mode 100644 index 000000000..6da3d5d96 --- /dev/null +++ b/src/test/ui/nll/normalization-bounds-error.stderr @@ -0,0 +1,27 @@ +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'d` due to conflicting requirements + --> $DIR/normalization-bounds-error.rs:12:4 + | +LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} + | ^^^^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime `'d` as defined here... + --> $DIR/normalization-bounds-error.rs:12:14 + | +LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} + | ^^ +note: ...but the lifetime must also be valid for the lifetime `'a` as defined here... + --> $DIR/normalization-bounds-error.rs:12:18 + | +LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} + | ^^ +note: ...so that the types are compatible + --> $DIR/normalization-bounds-error.rs:12:4 + | +LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} + | ^^^^^^^^^ + = note: expected `Visitor<'d>` + found `Visitor<'_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/nll/normalization-bounds.rs b/src/test/ui/nll/normalization-bounds.rs new file mode 100644 index 000000000..bb6d981e0 --- /dev/null +++ b/src/test/ui/nll/normalization-bounds.rs @@ -0,0 +1,15 @@ +// Check that lifetime bounds get checked the right way around with NLL enabled. + +// check-pass + +trait Visitor<'d> { + type Value; +} + +impl<'a, 'd: 'a> Visitor<'d> for &'a () { + type Value = (); +} + +fn visit_seq<'d: 'a, 'a>() -> <&'a () as Visitor<'d>>::Value {} + +fn main() {} diff --git a/src/test/ui/nll/outlives-suggestion-more.rs b/src/test/ui/nll/outlives-suggestion-more.rs new file mode 100644 index 000000000..2e1359fe5 --- /dev/null +++ b/src/test/ui/nll/outlives-suggestion-more.rs @@ -0,0 +1,26 @@ +// Test the more elaborate outlives suggestions. + +// Should suggest: 'a: 'c, 'b: 'd +fn foo1<'a, 'b, 'c, 'd>(x: &'a usize, y: &'b usize) -> (&'c usize, &'d usize) { + (x, y) //~ERROR lifetime may not live long enough + //~^ERROR lifetime may not live long enough +} + +// Should suggest: 'a: 'c and use 'static instead of 'b +fn foo2<'a, 'b, 'c>(x: &'a usize, y: &'b usize) -> (&'c usize, &'static usize) { + (x, y) //~ERROR lifetime may not live long enough + //~^ERROR lifetime may not live long enough +} + +// Should suggest: 'a and 'b are the same and use 'static instead of 'c +fn foo3<'a, 'b, 'c, 'd, 'e>( + x: &'a usize, + y: &'b usize, + z: &'c usize, +) -> (&'b usize, &'a usize, &'static usize) { + (x, y, z) //~ERROR lifetime may not live long enough + //~^ERROR lifetime may not live long enough + //~^^ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/outlives-suggestion-more.stderr b/src/test/ui/nll/outlives-suggestion-more.stderr new file mode 100644 index 000000000..c8c604b5b --- /dev/null +++ b/src/test/ui/nll/outlives-suggestion-more.stderr @@ -0,0 +1,96 @@ +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-more.rs:5:5 + | +LL | fn foo1<'a, 'b, 'c, 'd>(x: &'a usize, y: &'b usize) -> (&'c usize, &'d usize) { + | -- -- lifetime `'c` defined here + | | + | lifetime `'a` defined here +LL | (x, y) + | ^^^^^^ function was supposed to return data with lifetime `'c` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'c` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-more.rs:5:5 + | +LL | fn foo1<'a, 'b, 'c, 'd>(x: &'a usize, y: &'b usize) -> (&'c usize, &'d usize) { + | -- -- lifetime `'d` defined here + | | + | lifetime `'b` defined here +LL | (x, y) + | ^^^^^^ function was supposed to return data with lifetime `'d` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'd` + +help: the following changes may resolve your lifetime errors + | + = help: add bound `'a: 'c` + = help: add bound `'b: 'd` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-more.rs:11:5 + | +LL | fn foo2<'a, 'b, 'c>(x: &'a usize, y: &'b usize) -> (&'c usize, &'static usize) { + | -- -- lifetime `'c` defined here + | | + | lifetime `'a` defined here +LL | (x, y) + | ^^^^^^ function was supposed to return data with lifetime `'c` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'c` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-more.rs:11:5 + | +LL | fn foo2<'a, 'b, 'c>(x: &'a usize, y: &'b usize) -> (&'c usize, &'static usize) { + | -- lifetime `'b` defined here +LL | (x, y) + | ^^^^^^ returning this value requires that `'b` must outlive `'static` + +help: the following changes may resolve your lifetime errors + | + = help: add bound `'a: 'c` + = help: replace `'b` with `'static` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-more.rs:21:5 + | +LL | fn foo3<'a, 'b, 'c, 'd, 'e>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | (x, y, z) + | ^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-more.rs:21:5 + | +LL | fn foo3<'a, 'b, 'c, 'd, 'e>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | (x, y, z) + | ^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-more.rs:21:5 + | +LL | fn foo3<'a, 'b, 'c, 'd, 'e>( + | -- lifetime `'c` defined here +... +LL | (x, y, z) + | ^^^^^^^^^ returning this value requires that `'c` must outlive `'static` + +help: the following changes may resolve your lifetime errors + | + = help: `'a` and `'b` must be the same: replace one with the other + = help: replace `'c` with `'static` + +error: aborting due to 7 previous errors + diff --git a/src/test/ui/nll/outlives-suggestion-simple.polonius.stderr b/src/test/ui/nll/outlives-suggestion-simple.polonius.stderr new file mode 100644 index 000000000..c00288f2e --- /dev/null +++ b/src/test/ui/nll/outlives-suggestion-simple.polonius.stderr @@ -0,0 +1,124 @@ +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:6:5 + | +LL | fn foo1<'a, 'b>(x: &'a usize) -> &'b usize { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x + | ^ returning this value requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:10:5 + | +LL | fn foo2<'a>(x: &'a usize) -> &'static usize { + | -- lifetime `'a` defined here +LL | x + | ^ returning this value requires that `'a` must outlive `'static` + | + = help: consider replacing `'a` with `'static` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:14:5 + | +LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | (x, y) + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:14:5 + | +LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | (x, y) + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +help: `'a` and `'b` must be the same: replace one with the other + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:22:5 + | +LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | (x, x) + | ^^^^^^ returning this value requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:22:5 + | +LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) { + | -- -- lifetime `'c` defined here + | | + | lifetime `'a` defined here +... +LL | (x, x) + | ^^^^^^ returning this value requires that `'a` must outlive `'c` + | + = help: consider adding the following bound: `'a: 'c` + +help: add bound `'a: 'b + 'c` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:31:9 + | +LL | pub fn foo<'a>(x: &'a usize) -> Self { + | -- lifetime `'a` defined here +LL | Foo { x } + | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + | + = help: consider replacing `'a` with `'static` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:41:9 + | +LL | impl<'a> Bar<'a> { + | -- lifetime `'a` defined here +LL | pub fn get<'b>(&self) -> &'b usize { + | -- lifetime `'b` defined here +LL | self.x + | ^^^^^^ returning this value requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:52:9 + | +LL | impl<'a> Baz<'a> { + | -- lifetime `'a` defined here +LL | fn get<'b>(&'b self) -> &'a i32 { + | -- lifetime `'b` defined here +LL | self.x + | ^^^^^^ returning this value requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error[E0521]: borrowed data escapes outside of associated function + --> $DIR/outlives-suggestion-simple.rs:73:9 + | +LL | fn get_bar(&self) -> Bar2 { + | ----- + | | + | `self` declared here, outside of the associated function body + | `self` is a reference that is only valid in the associated function body +LL | Bar2::new(&self) + | ^^^^^^^^^^^^^^^^ `self` escapes the associated function body here + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0521`. diff --git a/src/test/ui/nll/outlives-suggestion-simple.rs b/src/test/ui/nll/outlives-suggestion-simple.rs new file mode 100644 index 000000000..2a5c31e3a --- /dev/null +++ b/src/test/ui/nll/outlives-suggestion-simple.rs @@ -0,0 +1,75 @@ +// Test the simplest of outlives suggestions. + +fn foo1<'a, 'b>(x: &'a usize) -> &'b usize { + x //~ERROR lifetime may not live long enough +} + +fn foo2<'a>(x: &'a usize) -> &'static usize { + x //~ERROR lifetime may not live long enough +} + +fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { + (x, y) //~ERROR lifetime may not live long enough + //~^ERROR lifetime may not live long enough +} + +fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) { + // FIXME: ideally, we suggest 'a: 'b + 'c, but as of today (may 04, 2019), the null error + // reporting stops after the first error in a MIR def so as not to produce too many errors, so + // currently we only report 'a: 'b. The user would then re-run and get another error. + (x, x) //~ERROR lifetime may not live long enough +} + +struct Foo<'a> { + x: &'a usize, +} + +impl Foo<'static> { + pub fn foo<'a>(x: &'a usize) -> Self { + Foo { x } //~ERROR lifetime may not live long enough + } +} + +struct Bar<'a> { + x: &'a usize, +} + +impl<'a> Bar<'a> { + pub fn get<'b>(&self) -> &'b usize { + self.x //~ERROR lifetime may not live long enough + } +} + +// source: https://stackoverflow.com/questions/41417057/why-do-i-get-a-lifetime-error-when-i-use-a-mutable-reference-in-a-struct-instead +struct Baz<'a> { + x: &'a mut i32, +} + +impl<'a> Baz<'a> { + fn get<'b>(&'b self) -> &'a i32 { + self.x //~ERROR lifetime may not live long enough + } +} + +// source: https://stackoverflow.com/questions/41204134/rust-lifetime-error +struct Bar2<'a> { + bar: &'a str, +} +impl<'a> Bar2<'a> { + fn new(foo: &'a Foo2<'a>) -> Bar2<'a> { + Bar2 { bar: foo.raw } + } +} + +pub struct Foo2<'a> { + raw: &'a str, + cell: std::cell::Cell<&'a str>, +} +impl<'a> Foo2<'a> { + // should not produce outlives suggestions to name 'self + fn get_bar(&self) -> Bar2 { + Bar2::new(&self) //~ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/src/test/ui/nll/outlives-suggestion-simple.stderr b/src/test/ui/nll/outlives-suggestion-simple.stderr new file mode 100644 index 000000000..a8368c494 --- /dev/null +++ b/src/test/ui/nll/outlives-suggestion-simple.stderr @@ -0,0 +1,104 @@ +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:4:5 + | +LL | fn foo1<'a, 'b>(x: &'a usize) -> &'b usize { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x + | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:8:5 + | +LL | fn foo2<'a>(x: &'a usize) -> &'static usize { + | -- lifetime `'a` defined here +LL | x + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:12:5 + | +LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | (x, y) + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:12:5 + | +LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | (x, y) + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +help: `'a` and `'b` must be the same: replace one with the other + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:20:5 + | +LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | (x, x) + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:29:9 + | +LL | pub fn foo<'a>(x: &'a usize) -> Self { + | -- lifetime `'a` defined here +LL | Foo { x } + | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:39:9 + | +LL | impl<'a> Bar<'a> { + | -- lifetime `'a` defined here +LL | pub fn get<'b>(&self) -> &'b usize { + | -- lifetime `'b` defined here +LL | self.x + | ^^^^^^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:50:9 + | +LL | impl<'a> Baz<'a> { + | -- lifetime `'a` defined here +LL | fn get<'b>(&'b self) -> &'a i32 { + | -- lifetime `'b` defined here +LL | self.x + | ^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: lifetime may not live long enough + --> $DIR/outlives-suggestion-simple.rs:71:9 + | +LL | impl<'a> Foo2<'a> { + | -- lifetime `'a` defined here +LL | // should not produce outlives suggestions to name 'self +LL | fn get_bar(&self) -> Bar2 { + | - let's call the lifetime of this reference `'1` +LL | Bar2::new(&self) + | ^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a` + +error: aborting due to 9 previous errors + diff --git a/src/test/ui/nll/polonius/assignment-kills-loans.rs b/src/test/ui/nll/polonius/assignment-kills-loans.rs new file mode 100644 index 000000000..c4cf20389 --- /dev/null +++ b/src/test/ui/nll/polonius/assignment-kills-loans.rs @@ -0,0 +1,87 @@ +#![allow(dead_code)] + +// This tests the various kinds of assignments there are. Polonius used to generate `killed` +// facts only on simple assigments, but not projections, incorrectly causing errors to be emitted +// for code accepted by NLL. They are all variations from example code in the NLL RFC. + +// check-pass +// compile-flags: -Z polonius + +struct List<T> { + value: T, + next: Option<Box<List<T>>>, +} + +// Assignment to a local: the `list` assignment should clear the existing +// borrows of `list.value` and `list.next` +fn assignment_to_local<T>(mut list: &mut List<T>) -> Vec<&mut T> { + let mut result = vec![]; + loop { + result.push(&mut list.value); + if let Some(n) = list.next.as_mut() { + list = n; + } else { + return result; + } + } +} + +// Assignment to a deref projection: the `*list` assignment should clear the existing +// borrows of `list.value` and `list.next` +fn assignment_to_deref_projection<T>(mut list: Box<&mut List<T>>) -> Vec<&mut T> { + let mut result = vec![]; + loop { + result.push(&mut list.value); + if let Some(n) = list.next.as_mut() { + *list = n; + } else { + return result; + } + } +} + +// Assignment to a field projection: the `list.0` assignment should clear the existing +// borrows of `list.0.value` and `list.0.next` +fn assignment_to_field_projection<T>(mut list: (&mut List<T>,)) -> Vec<&mut T> { + let mut result = vec![]; + loop { + result.push(&mut list.0.value); + if let Some(n) = list.0.next.as_mut() { + list.0 = n; + } else { + return result; + } + } +} + +// Assignment to a deref field projection: the `*list.0` assignment should clear the existing +// borrows of `list.0.value` and `list.0.next` +fn assignment_to_deref_field_projection<T>(mut list: (Box<&mut List<T>>,)) -> Vec<&mut T> { + let mut result = vec![]; + loop { + result.push(&mut list.0.value); + if let Some(n) = list.0.next.as_mut() { + *list.0 = n; + } else { + return result; + } + } +} + +// Similar to `assignment_to_deref_field_projection` but through a longer projection chain +fn assignment_through_projection_chain<T>( + mut list: (((((Box<&mut List<T>>,),),),),), +) -> Vec<&mut T> { + let mut result = vec![]; + loop { + result.push(&mut ((((list.0).0).0).0).0.value); + if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { + *((((list.0).0).0).0).0 = n; + } else { + return result; + } + } +} + +fn main() { +} diff --git a/src/test/ui/nll/polonius/assignment-to-differing-field.rs b/src/test/ui/nll/polonius/assignment-to-differing-field.rs new file mode 100644 index 000000000..7ec3b9049 --- /dev/null +++ b/src/test/ui/nll/polonius/assignment-to-differing-field.rs @@ -0,0 +1,49 @@ +#![allow(dead_code)] + +// Compared to `assignment-kills-loans.rs`, we check here +// that we do not kill too many borrows. Assignments to the `.1` +// field projections should leave the borrows on `.0` intact. + +// compile-flags: -Z polonius + +struct List<T> { + value: T, + next: Option<Box<List<T>>>, +} + + +fn assignment_to_field_projection<'a, T>( + mut list: (&'a mut List<T>, &'a mut List<T>), +) -> Vec<&'a mut T> { + let mut result = vec![]; + loop { + result.push(&mut (list.0).value); + //~^ ERROR cannot borrow `list.0.value` as mutable + + if let Some(n) = (list.0).next.as_mut() { + //~^ ERROR cannot borrow `list.0.next` as mutable + list.1 = n; + } else { + return result; + } + } +} + +fn assignment_through_projection_chain<'a, T>( + mut list: (((((Box<&'a mut List<T>>, Box<&'a mut List<T>>),),),),), +) -> Vec<&'a mut T> { + let mut result = vec![]; + loop { + result.push(&mut ((((list.0).0).0).0).0.value); + //~^ ERROR cannot borrow `list.0.0.0.0.0.value` as mutable + + if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { + //~^ ERROR cannot borrow `list.0.0.0.0.0.next` as mutable + *((((list.0).0).0).0).1 = n; + } else { + return result; + } + } +} + +fn main() {} diff --git a/src/test/ui/nll/polonius/assignment-to-differing-field.stderr b/src/test/ui/nll/polonius/assignment-to-differing-field.stderr new file mode 100644 index 000000000..afa1b9344 --- /dev/null +++ b/src/test/ui/nll/polonius/assignment-to-differing-field.stderr @@ -0,0 +1,51 @@ +error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:20:21 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut (list.0).value); + | ^^^^^^^^^^^^^^^^^^^ `list.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:23:26 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = (list.0).next.as_mut() { + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | `list.0.next` was mutably borrowed here in the previous iteration of the loop + | argument requires that `list.0.next` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:37:21 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut ((((list.0).0).0).0).0.value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.0.0.0.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:40:26 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop + | argument requires that `list.0.0.0.0.0.next` is borrowed for `'a` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/src/test/ui/nll/polonius/call-kills-loans.rs b/src/test/ui/nll/polonius/call-kills-loans.rs new file mode 100644 index 000000000..f430e9211 --- /dev/null +++ b/src/test/ui/nll/polonius/call-kills-loans.rs @@ -0,0 +1,23 @@ +// `Call` terminators can write to a local which has existing loans +// and those need to be killed like a regular assignment to a local. +// This is a simplified version of issue 47680, is correctly accepted +// by NLL but was incorrectly rejected by Polonius because of these +// missing `killed` facts. + +// check-pass +// compile-flags: -Z polonius + +struct Thing; + +impl Thing { + fn next(&mut self) -> &mut Self { unimplemented!() } +} + +fn main() { + let mut temp = &mut Thing; + + loop { + let v = temp.next(); + temp = v; // accepted by NLL, was incorrectly rejected by Polonius + } +} diff --git a/src/test/ui/nll/polonius/issue-46589.rs b/src/test/ui/nll/polonius/issue-46589.rs new file mode 100644 index 000000000..648280a1d --- /dev/null +++ b/src/test/ui/nll/polonius/issue-46589.rs @@ -0,0 +1,31 @@ +// This test is a copy of `ui/nll/issue-46589.rs` which fails in NLL but succeeds in Polonius. +// As we can't have a test here which conditionally passes depending on a test +// revision/compile-flags. We ensure here that it passes in Polonius mode. + +// check-pass +// compile-flags: -Z polonius + +struct Foo; + +impl Foo { + fn get_self(&mut self) -> Option<&mut Self> { + Some(self) + } + + fn new_self(&mut self) -> &mut Self { + self + } + + fn trigger_bug(&mut self) { + let other = &mut (&mut *self); + + *other = match (*other).get_self() { + Some(s) => s, + None => (*other).new_self() + }; + + let c = other; + } +} + +fn main() {} diff --git a/src/test/ui/nll/polonius/polonius-smoke-test.rs b/src/test/ui/nll/polonius/polonius-smoke-test.rs new file mode 100644 index 000000000..c4344af71 --- /dev/null +++ b/src/test/ui/nll/polonius/polonius-smoke-test.rs @@ -0,0 +1,46 @@ +// Check that Polonius borrow check works for simple cases. +// compile-flags: -Z polonius + +pub fn return_ref_to_local() -> &'static i32 { + let x = 0; + &x //~ ERROR +} + +pub fn use_while_mut() { + let mut x = 0; + let y = &mut x; + let z = x; //~ ERROR + let w = y; +} + +pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 { + let y = &mut *x; + let z = x; //~ ERROR + y +} + +// Cases like this are why we have Polonius. +pub fn position_dependent_outlives(x: &mut i32, cond: bool) -> &mut i32 { + let y = &mut *x; + if cond { + return y; + } else { + *x = 0; + return x; + } +} + +fn foo<'a, 'b>(p: &'b &'a mut usize) -> &'b usize { + p +} + +// Check that we create constraints for well-formedness of function arguments +fn well_formed_function_inputs() { + let s = &mut 1; + let r = &mut *s; + let tmp = foo(&r); + s; //~ ERROR + tmp; +} + +fn main() {} diff --git a/src/test/ui/nll/polonius/polonius-smoke-test.stderr b/src/test/ui/nll/polonius/polonius-smoke-test.stderr new file mode 100644 index 000000000..fa1a6a9c9 --- /dev/null +++ b/src/test/ui/nll/polonius/polonius-smoke-test.stderr @@ -0,0 +1,43 @@ +error[E0515]: cannot return reference to local variable `x` + --> $DIR/polonius-smoke-test.rs:6:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/polonius-smoke-test.rs:12:13 + | +LL | let y = &mut x; + | ------ borrow of `x` occurs here +LL | let z = x; + | ^ use of borrowed `x` +LL | let w = y; + | - borrow later used here + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/polonius-smoke-test.rs:18:13 + | +LL | pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 { + | - let's call the lifetime of this reference `'1` +LL | let y = &mut *x; + | ------- borrow of `*x` occurs here +LL | let z = x; + | ^ move out of `x` occurs here +LL | y + | - returning this value requires that `*x` is borrowed for `'1` + +error[E0505]: cannot move out of `s` because it is borrowed + --> $DIR/polonius-smoke-test.rs:42:5 + | +LL | let r = &mut *s; + | ------- borrow of `*s` occurs here +LL | let tmp = foo(&r); +LL | s; + | ^ move out of `s` occurs here +LL | tmp; + | --- borrow later used here + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0503, E0505, E0515. +For more information about an error, try `rustc --explain E0503`. diff --git a/src/test/ui/nll/polonius/storagedead-kills-loans.rs b/src/test/ui/nll/polonius/storagedead-kills-loans.rs new file mode 100644 index 000000000..669e077de --- /dev/null +++ b/src/test/ui/nll/polonius/storagedead-kills-loans.rs @@ -0,0 +1,28 @@ +// Whenever a `StorageDead` MIR statement destroys a value `x`, +// we should kill all loans of `x`. This is extracted from `rand 0.4.6`, +// is correctly accepted by NLL but was incorrectly rejected by +// Polonius because of these missing `killed` facts. + +// check-pass +// compile-flags: -Z polonius + +use std::{io, mem}; +use std::io::Read; + +#[allow(dead_code)] +fn fill(r: &mut dyn Read, mut buf: &mut [u8]) -> io::Result<()> { + while buf.len() > 0 { + match r.read(buf).unwrap() { + 0 => return Err(io::Error::new(io::ErrorKind::Other, + "end of file reached")), + n => buf = &mut mem::replace(&mut buf, &mut [])[n..], + // ^- Polonius had multiple errors on the previous line (where NLL has none) + // as it didn't know `buf` was killed here, and would + // incorrectly reject both the borrow expression, and the assignment. + } + } + Ok(()) +} + +fn main() { +} diff --git a/src/test/ui/nll/polonius/subset-relations.rs b/src/test/ui/nll/polonius/subset-relations.rs new file mode 100644 index 000000000..f223ab177 --- /dev/null +++ b/src/test/ui/nll/polonius/subset-relations.rs @@ -0,0 +1,29 @@ +// Checks that Polonius can compute cases of universal regions errors: +// "illegal subset relation errors", cases where analysis finds that +// two free regions outlive each other, without any evidence that this +// relation holds. + +// compile-flags: -Z polonius + +// returning `y` requires that `'b: 'a`, but it's not known to be true +fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { + y //~ ERROR +} + +// `'b: 'a` is explicitly declared +fn valid_subset<'a, 'b: 'a>(x: &'a u32, y: &'b u32) -> &'a u32 { + y +} + +// because of `x`, it is implied that `'b: 'a` holds +fn implied_bounds_subset<'a, 'b>(x: &'a &'b mut u32) -> &'a u32 { + x +} + +// `'b: 'a` is declared, and `'a: 'c` is known via implied bounds: +// `'b: 'c` is therefore known to hold transitively +fn transitively_valid_subset<'a, 'b: 'a, 'c>(x: &'c &'a u32, y: &'b u32) -> &'c u32 { + y +} + +fn main() {} diff --git a/src/test/ui/nll/polonius/subset-relations.stderr b/src/test/ui/nll/polonius/subset-relations.stderr new file mode 100644 index 000000000..6df5563ea --- /dev/null +++ b/src/test/ui/nll/polonius/subset-relations.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/subset-relations.rs:10:5 + | +LL | fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | y + | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/process_or_insert_default.rs b/src/test/ui/nll/process_or_insert_default.rs new file mode 100644 index 000000000..84ac9bbd0 --- /dev/null +++ b/src/test/ui/nll/process_or_insert_default.rs @@ -0,0 +1,27 @@ +// run-pass + +use std::collections::HashMap; + +fn process_or_insert_default(map: &mut HashMap<usize, String>, key: usize) { + match map.get_mut(&key) { + Some(value) => { + process(value); + } + None => { + map.insert(key, "".to_string()); + } + } +} + +fn process(x: &str) { + assert_eq!(x, "Hello, world"); +} + +fn main() { + let map = &mut HashMap::new(); + map.insert(22, format!("Hello, world")); + map.insert(44, format!("Goodbye, world")); + process_or_insert_default(map, 22); + process_or_insert_default(map, 66); + assert_eq!(map[&66], ""); +} diff --git a/src/test/ui/nll/projection-return.rs b/src/test/ui/nll/projection-return.rs new file mode 100644 index 000000000..be141339a --- /dev/null +++ b/src/test/ui/nll/projection-return.rs @@ -0,0 +1,17 @@ +// check-pass + +#![feature(rustc_attrs)] + +trait Foo { + type Bar; +} + +impl Foo for () { + type Bar = u32; +} + +fn foo() -> <() as Foo>::Bar { + 22 +} + +fn main() { } diff --git a/src/test/ui/nll/promotable-mutable-zst-doesnt-conflict.rs b/src/test/ui/nll/promotable-mutable-zst-doesnt-conflict.rs new file mode 100644 index 000000000..3b06b0db3 --- /dev/null +++ b/src/test/ui/nll/promotable-mutable-zst-doesnt-conflict.rs @@ -0,0 +1,11 @@ +// Check that mutable promoted length zero arrays don't check for conflicting +// access + +// check-pass + +pub fn main() { + let mut x: Vec<&[i32; 0]> = Vec::new(); + for _ in 0..10 { + x.push(&[]); + } +} diff --git a/src/test/ui/nll/promoted-bounds.rs b/src/test/ui/nll/promoted-bounds.rs new file mode 100644 index 000000000..5f95ae13c --- /dev/null +++ b/src/test/ui/nll/promoted-bounds.rs @@ -0,0 +1,25 @@ +fn shorten_lifetime<'a, 'b, 'min>(a: &'a i32, b: &'b i32) -> &'min i32 +where + 'a: 'min, + 'b: 'min, +{ + if *a < *b { + &a + } else { + &b + } +} + +fn main() { + let promoted_fn_item_ref = &shorten_lifetime; + + let a = &5; + let ptr = { + let l = 3; + let b = &l; //~ ERROR does not live long enough + let c = promoted_fn_item_ref(a, b); + c + }; + + println!("ptr = {:?}", ptr); +} diff --git a/src/test/ui/nll/promoted-bounds.stderr b/src/test/ui/nll/promoted-bounds.stderr new file mode 100644 index 000000000..df347f4e7 --- /dev/null +++ b/src/test/ui/nll/promoted-bounds.stderr @@ -0,0 +1,15 @@ +error[E0597]: `l` does not live long enough + --> $DIR/promoted-bounds.rs:19:17 + | +LL | let ptr = { + | --- borrow later stored here +LL | let l = 3; +LL | let b = &l; + | ^^ borrowed value does not live long enough +... +LL | }; + | - `l` 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/nll/promoted-closure-pair.rs b/src/test/ui/nll/promoted-closure-pair.rs new file mode 100644 index 000000000..cc9f17fd4 --- /dev/null +++ b/src/test/ui/nll/promoted-closure-pair.rs @@ -0,0 +1,10 @@ +// Check that we handle multiple closures in the same promoted constant. + +fn foo() -> &'static i32 { + let z = 0; + let p = &(|y| y, |y| y); + p.0(&z); + p.1(&z) //~ ERROR cannot return +} + +fn main() {} diff --git a/src/test/ui/nll/promoted-closure-pair.stderr b/src/test/ui/nll/promoted-closure-pair.stderr new file mode 100644 index 000000000..000bdf858 --- /dev/null +++ b/src/test/ui/nll/promoted-closure-pair.stderr @@ -0,0 +1,12 @@ +error[E0515]: cannot return value referencing local variable `z` + --> $DIR/promoted-closure-pair.rs:7:5 + | +LL | p.1(&z) + | ^^^^--^ + | | | + | | `z` is borrowed here + | returns a value referencing data owned by the current function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/src/test/ui/nll/promoted-liveness.rs b/src/test/ui/nll/promoted-liveness.rs new file mode 100644 index 000000000..e5a8e1e5c --- /dev/null +++ b/src/test/ui/nll/promoted-liveness.rs @@ -0,0 +1,8 @@ +// Test that promoted that have larger mir bodies than their containing function +// don't cause an ICE. + +// check-pass + +fn main() { + &["0", "1", "2", "3", "4", "5", "6", "7"]; +} diff --git a/src/test/ui/nll/rc-loop.rs b/src/test/ui/nll/rc-loop.rs new file mode 100644 index 000000000..e59303d1f --- /dev/null +++ b/src/test/ui/nll/rc-loop.rs @@ -0,0 +1,28 @@ +// run-pass + +// A test for something that NLL enables. It sometimes happens that +// the `while let` pattern makes some borrows from a variable (in this +// case, `x`) that you need in order to compute the next value for +// `x`. The lexical checker makes this very painful. The NLL checker +// does not. + +use std::rc::Rc; + +#[derive(Debug, PartialEq, Eq)] +enum Foo { + Base(usize), + Next(Rc<Foo>), +} + +fn find_base(mut x: Rc<Foo>) -> Rc<Foo> { + while let Foo::Next(n) = &*x { + x = n.clone(); + } + x +} + +fn main() { + let chain = Rc::new(Foo::Next(Rc::new(Foo::Base(44)))); + let base = find_base(chain); + assert_eq!(&*base, &Foo::Base(44)); +} diff --git a/src/test/ui/nll/ref-suggestion.rs b/src/test/ui/nll/ref-suggestion.rs new file mode 100644 index 000000000..346d118f0 --- /dev/null +++ b/src/test/ui/nll/ref-suggestion.rs @@ -0,0 +1,17 @@ +fn main() { + let x = vec![1]; + let y = x; + x; //~ ERROR use of moved value + + let x = vec![1]; + let mut y = x; + x; //~ ERROR use of moved value + + let x = (Some(vec![1]), ()); + + match x { + (Some(y), ()) => {}, + _ => {}, + } + x; //~ ERROR use of partially moved value +} diff --git a/src/test/ui/nll/ref-suggestion.stderr b/src/test/ui/nll/ref-suggestion.stderr new file mode 100644 index 000000000..a973c583a --- /dev/null +++ b/src/test/ui/nll/ref-suggestion.stderr @@ -0,0 +1,38 @@ +error[E0382]: use of moved value: `x` + --> $DIR/ref-suggestion.rs:4:5 + | +LL | let x = vec![1]; + | - move occurs because `x` has type `Vec<i32>`, which does not implement the `Copy` trait +LL | let y = x; + | - value moved here +LL | x; + | ^ value used here after move + +error[E0382]: use of moved value: `x` + --> $DIR/ref-suggestion.rs:8:5 + | +LL | let x = vec![1]; + | - move occurs because `x` has type `Vec<i32>`, which does not implement the `Copy` trait +LL | let mut y = x; + | - value moved here +LL | x; + | ^ value used here after move + +error[E0382]: use of partially moved value: `x` + --> $DIR/ref-suggestion.rs:16:5 + | +LL | (Some(y), ()) => {}, + | - value partially moved here +... +LL | x; + | ^ value used here after partial move + | + = note: partial move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `x.0.0` + | +LL | (Some(ref y), ()) => {}, + | +++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/nll/reference-carried-through-struct-field.rs b/src/test/ui/nll/reference-carried-through-struct-field.rs new file mode 100644 index 000000000..effd61084 --- /dev/null +++ b/src/test/ui/nll/reference-carried-through-struct-field.rs @@ -0,0 +1,10 @@ +struct Wrap<'a> { w: &'a mut u32 } + +fn foo() { + let mut x = 22; + let wrapper = Wrap { w: &mut x }; + x += 1; //~ ERROR cannot use `x` because it was mutably borrowed [E0503] + *wrapper.w += 1; +} + +fn main() { } diff --git a/src/test/ui/nll/reference-carried-through-struct-field.stderr b/src/test/ui/nll/reference-carried-through-struct-field.stderr new file mode 100644 index 000000000..56d878e43 --- /dev/null +++ b/src/test/ui/nll/reference-carried-through-struct-field.stderr @@ -0,0 +1,13 @@ +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/reference-carried-through-struct-field.rs:6:5 + | +LL | let wrapper = Wrap { w: &mut x }; + | ------ borrow of `x` occurs here +LL | x += 1; + | ^^^^^^ use of borrowed `x` +LL | *wrapper.w += 1; + | --------------- borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0503`. diff --git a/src/test/ui/nll/region-ends-after-if-condition.rs b/src/test/ui/nll/region-ends-after-if-condition.rs new file mode 100644 index 000000000..f67de03ca --- /dev/null +++ b/src/test/ui/nll/region-ends-after-if-condition.rs @@ -0,0 +1,32 @@ +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` includes the points after `&v[0]` up to (but not +// including) the call to `use_x`. The `else` branch is not included. + +#![allow(warnings)] +#![feature(rustc_attrs)] + +struct MyStruct { + field: String +} + +fn foo1() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &my_struct.field; + if value.is_empty() { + my_struct.field.push_str("Hello, world!"); + } +} + +fn foo2() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &my_struct.field; + if value.is_empty() { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR [E0502] + } + drop(value); +} + +fn main() { } diff --git a/src/test/ui/nll/region-ends-after-if-condition.stderr b/src/test/ui/nll/region-ends-after-if-condition.stderr new file mode 100644 index 000000000..c03e38579 --- /dev/null +++ b/src/test/ui/nll/region-ends-after-if-condition.stderr @@ -0,0 +1,15 @@ +error[E0502]: cannot borrow `my_struct.field` as mutable because it is also borrowed as immutable + --> $DIR/region-ends-after-if-condition.rs:26:9 + | +LL | let value = &my_struct.field; + | ---------------- immutable borrow occurs here +LL | if value.is_empty() { +LL | my_struct.field.push_str("Hello, world!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +... +LL | drop(value); + | ----- 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/nll/relate_tys/fn-subtype.rs b/src/test/ui/nll/relate_tys/fn-subtype.rs new file mode 100644 index 000000000..ba89fa19c --- /dev/null +++ b/src/test/ui/nll/relate_tys/fn-subtype.rs @@ -0,0 +1,8 @@ +// Test that NLL produces correct spans for higher-ranked subtyping errors. +// +// compile-flags:-Zno-leak-check + +fn main() { + let x: fn(&'static ()) = |_| {}; + let y: for<'a> fn(&'a ()) = x; //~ ERROR mismatched types [E0308] +} diff --git a/src/test/ui/nll/relate_tys/fn-subtype.stderr b/src/test/ui/nll/relate_tys/fn-subtype.stderr new file mode 100644 index 000000000..21073647e --- /dev/null +++ b/src/test/ui/nll/relate_tys/fn-subtype.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/fn-subtype.rs:7:33 + | +LL | let y: for<'a> fn(&'a ()) = x; + | ^ one type is more general than the other + | + = note: expected fn pointer `for<'a> fn(&'a ())` + found fn pointer `fn(&())` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs new file mode 100644 index 000000000..7891bab09 --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs @@ -0,0 +1,24 @@ +// Test that the NLL `relate_tys` code correctly deduces that a +// function returning either argument CANNOT be upcast to one +// that returns always its first argument. +// +// compile-flags:-Zno-leak-check + +fn make_it() -> for<'a> fn(&'a u32, &'a u32) -> &'a u32 { + panic!() +} + +fn foo() { + let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); + //~^ ERROR mismatched types [E0308] + drop(a); +} + +fn bar() { + // The code path for patterns is mildly different, so go ahead and + // test that too: + let _: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); + //~^ ERROR mismatched types [E0308] +} + +fn main() {} diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr new file mode 100644 index 000000000..7d76c916d --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/hr-fn-aaa-as-aba.rs:12:58 + | +LL | let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); + | ^^^^^^^^^ one type is more general than the other + | + = note: expected fn pointer `for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32` + found fn pointer `for<'a> fn(&'a u32, &'a u32) -> &'a u32` + +error[E0308]: mismatched types + --> $DIR/hr-fn-aaa-as-aba.rs:20:12 + | +LL | let _: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected fn pointer `for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32` + found fn pointer `for<'a> fn(&'a u32, &'a u32) -> &'a u32` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs new file mode 100644 index 000000000..92730341c --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs @@ -0,0 +1,23 @@ +// Test an interesting corner case that ought to be legal (though the +// current code actually gets it wrong, see below): a fn that takes +// two arguments that are references with the same lifetime is in fact +// equivalent to a fn that takes two references with distinct +// lifetimes. This is true because the two functions can call one +// another -- effectively, the single lifetime `'a` is just inferred +// to be the intersection of the two distinct lifetimes. +// +// check-pass +// compile-flags:-Zno-leak-check + +use std::cell::Cell; + +fn make_cell_aa() -> Cell<for<'a> fn(&'a u32, &'a u32)> { + panic!() +} + +fn aa_eq_ab() { + let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa(); + drop(a); +} + +fn main() { } diff --git a/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs b/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs new file mode 100644 index 000000000..7cc0acf45 --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs @@ -0,0 +1,15 @@ +// Test that the NLL `relate_tys` code correctly deduces that a +// function returning always its first argument can be upcast to one +// that returns either first or second argument. +// +// check-pass +// compile-flags:-Zno-leak-check + +fn make_it() -> for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 { + panic!() +} + +fn main() { + let a: for<'a> fn(&'a u32, &'a u32) -> &'a u32 = make_it(); + drop(a); +} diff --git a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs new file mode 100644 index 000000000..c4db6fc97 --- /dev/null +++ b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs @@ -0,0 +1,33 @@ +// Test that the NLL solver cannot find a solution +// for `exists<R1> { forall<R1> { R2: R1 } }`. +// +// In this test, the impl should match `fn(T)` for some `T`, +// but we ask it to match `for<'a> fn(&'a ())`. Due to argument +// contravariance, this effectively requires a `T = &'b ()` where +// `forall<'a> { 'a: 'b }`. Therefore, we get an error. +// +// Note the use of `-Zno-leak-check` here. This is presently required in order +// to skip the leak-check errors. +// +// c.f. Issue #57642. +// +// compile-flags:-Zno-leak-check + +trait Y { + type F; + fn make_f() -> Self::F; +} + +impl<T> Y for fn(T) { + type F = fn(T); + + fn make_f() -> Self::F { + |_| {} + } +} + +fn main() { + let _x = <fn(&())>::make_f(); + //~^ ERROR implementation of `Y` is not general enough + //~| ERROR implementation of `Y` is not general enough +} diff --git a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr new file mode 100644 index 000000000..51adfca3e --- /dev/null +++ b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr @@ -0,0 +1,20 @@ +error: implementation of `Y` is not general enough + --> $DIR/impl-fn-ignore-binder-via-bottom.rs:30:14 + | +LL | let _x = <fn(&())>::make_f(); + | ^^^^^^^^^^^^^^^^^^^ implementation of `Y` is not general enough + | + = note: `Y` would have to be implemented for the type `for<'r> fn(&'r ())` + = note: ...but `Y` is actually implemented for the type `fn(&'0 ())`, for some specific lifetime `'0` + +error: implementation of `Y` is not general enough + --> $DIR/impl-fn-ignore-binder-via-bottom.rs:30:14 + | +LL | let _x = <fn(&())>::make_f(); + | ^^^^^^^^^^^^^^^^^^^ implementation of `Y` is not general enough + | + = note: `Y` would have to be implemented for the type `for<'r> fn(&'r ())` + = note: ...but `Y` is actually implemented for the type `fn(&'0 ())`, for some specific lifetime `'0` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/relate_tys/issue-48071.rs b/src/test/ui/nll/relate_tys/issue-48071.rs new file mode 100644 index 000000000..73361a0d3 --- /dev/null +++ b/src/test/ui/nll/relate_tys/issue-48071.rs @@ -0,0 +1,24 @@ +// Regression test for #48071. This test used to ICE because -- in +// the leak-check -- it would pass since we knew that the return type +// was `'static`, and hence `'static: 'a` was legal even for a +// placeholder region, but in NLL land it would fail because we had +// rewritten `'static` to a region variable. +// +// check-pass + +trait Foo { + fn foo(&self) { } +} + +impl Foo for () { +} + +type MakeFooFn = for<'a> fn(&'a u8) -> Box<dyn Foo + 'a>; + +fn make_foo(x: &u8) -> Box<dyn Foo + 'static> { + Box::new(()) +} + +fn main() { + let x: MakeFooFn = make_foo as MakeFooFn; +} diff --git a/src/test/ui/nll/relate_tys/opaque-hrtb.rs b/src/test/ui/nll/relate_tys/opaque-hrtb.rs new file mode 100644 index 000000000..261372523 --- /dev/null +++ b/src/test/ui/nll/relate_tys/opaque-hrtb.rs @@ -0,0 +1,14 @@ +trait MyTrait<T> {} + +struct Foo; +impl<T> MyTrait<T> for Foo {} + +fn bar<Input>() -> impl MyTrait<Input> { + Foo +} + +fn foo() -> impl for<'a> MyTrait<&'a str> { + bar() //~ ERROR implementation of `MyTrait` is not general enough +} + +fn main() {} diff --git a/src/test/ui/nll/relate_tys/opaque-hrtb.stderr b/src/test/ui/nll/relate_tys/opaque-hrtb.stderr new file mode 100644 index 000000000..d75ec2b57 --- /dev/null +++ b/src/test/ui/nll/relate_tys/opaque-hrtb.stderr @@ -0,0 +1,11 @@ +error: implementation of `MyTrait` is not general enough + --> $DIR/opaque-hrtb.rs:11:5 + | +LL | bar() + | ^^^^^ implementation of `MyTrait` is not general enough + | + = note: `impl MyTrait<&'2 str>` must implement `MyTrait<&'1 str>`, for any lifetime `'1`... + = note: ...but it actually implements `MyTrait<&'2 str>`, for some specific lifetime `'2` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/relate_tys/trait-hrtb.rs b/src/test/ui/nll/relate_tys/trait-hrtb.rs new file mode 100644 index 000000000..7f40e93cd --- /dev/null +++ b/src/test/ui/nll/relate_tys/trait-hrtb.rs @@ -0,0 +1,14 @@ +// Test that NLL generates proper error spans for trait HRTB errors +// +// compile-flags:-Zno-leak-check + +trait Foo<'a> {} + +fn make_foo<'a>() -> Box<dyn Foo<'a>> { + panic!() +} + +fn main() { + let x: Box<dyn Foo<'static>> = make_foo(); + let y: Box<dyn for<'a> Foo<'a>> = x; //~ ERROR mismatched types [E0308] +} diff --git a/src/test/ui/nll/relate_tys/trait-hrtb.stderr b/src/test/ui/nll/relate_tys/trait-hrtb.stderr new file mode 100644 index 000000000..aa1927711 --- /dev/null +++ b/src/test/ui/nll/relate_tys/trait-hrtb.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/trait-hrtb.rs:13:39 + | +LL | let y: Box<dyn for<'a> Foo<'a>> = x; + | ^ one type is more general than the other + | + = note: expected trait object `dyn for<'a> Foo<'a>` + found trait object `dyn Foo<'_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/nll/relate_tys/universe-violation.rs b/src/test/ui/nll/relate_tys/universe-violation.rs new file mode 100644 index 000000000..c5f9d4406 --- /dev/null +++ b/src/test/ui/nll/relate_tys/universe-violation.rs @@ -0,0 +1,15 @@ +// Test that the NLL `relate_tys` code correctly deduces that a +// function returning either argument CANNOT be upcast to one +// that returns always its first argument. +// +// compile-flags:-Zno-leak-check + +fn make_it() -> fn(&'static u32) -> &'static u32 { + panic!() +} + +fn main() { + let a: fn(_) -> _ = make_it(); + let b: fn(&u32) -> &u32 = a; //~ ERROR mismatched types [E0308] + drop(a); +} diff --git a/src/test/ui/nll/relate_tys/universe-violation.stderr b/src/test/ui/nll/relate_tys/universe-violation.stderr new file mode 100644 index 000000000..6f38154e3 --- /dev/null +++ b/src/test/ui/nll/relate_tys/universe-violation.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/universe-violation.rs:13:31 + | +LL | let b: fn(&u32) -> &u32 = a; + | ^ one type is more general than the other + | + = note: expected fn pointer `for<'r> fn(&'r u32) -> &'r u32` + found fn pointer `fn(&u32) -> &u32` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/nll/relate_tys/var-appears-twice.rs b/src/test/ui/nll/relate_tys/var-appears-twice.rs new file mode 100644 index 000000000..77129f446 --- /dev/null +++ b/src/test/ui/nll/relate_tys/var-appears-twice.rs @@ -0,0 +1,25 @@ +// Test that the NLL `relate_tys` code correctly deduces that a +// function returning always its first argument can be upcast to one +// that returns either first or second argument. + +use std::cell::Cell; + +type DoubleCell<A> = Cell<(A, A)>; +type DoublePair<A> = (A, A); + +fn make_cell<'b>(x: &'b u32) -> Cell<(&'static u32, &'b u32)> { + panic!() +} + +fn main() { + let a: &'static u32 = &22; + let b = 44; + + // Here we get an error because `DoubleCell<_>` requires the same type + // on both parts of the `Cell`, and we can't have that. + let x: DoubleCell<_> = make_cell(&b); //~ ERROR + + // Here we do not get an error because `DoublePair<_>` permits + // variance on the lifetimes involved. + let y: DoublePair<_> = make_cell(&b).get(); +} diff --git a/src/test/ui/nll/relate_tys/var-appears-twice.stderr b/src/test/ui/nll/relate_tys/var-appears-twice.stderr new file mode 100644 index 000000000..d032ce6f2 --- /dev/null +++ b/src/test/ui/nll/relate_tys/var-appears-twice.stderr @@ -0,0 +1,14 @@ +error[E0597]: `b` does not live long enough + --> $DIR/var-appears-twice.rs:20:38 + | +LL | let x: DoubleCell<_> = make_cell(&b); + | ------------- ^^ borrowed value does not live long enough + | | + | type annotation requires that `b` is borrowed for `'static` +... +LL | } + | - `b` 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/nll/return-ref-mut-issue-46557.rs b/src/test/ui/nll/return-ref-mut-issue-46557.rs new file mode 100644 index 000000000..dca61d39d --- /dev/null +++ b/src/test/ui/nll/return-ref-mut-issue-46557.rs @@ -0,0 +1,8 @@ +// Regression test for issue #46557 + +fn gimme_static_mut() -> &'static mut u32 { + let ref mut x = 1234543; + x //~ ERROR cannot return value referencing temporary value [E0515] +} + +fn main() {} diff --git a/src/test/ui/nll/return-ref-mut-issue-46557.stderr b/src/test/ui/nll/return-ref-mut-issue-46557.stderr new file mode 100644 index 000000000..720440a0a --- /dev/null +++ b/src/test/ui/nll/return-ref-mut-issue-46557.stderr @@ -0,0 +1,11 @@ +error[E0515]: cannot return value referencing temporary value + --> $DIR/return-ref-mut-issue-46557.rs:5:5 + | +LL | let ref mut x = 1234543; + | ------- temporary value created here +LL | x + | ^ returns a value referencing data owned by the current function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/src/test/ui/nll/return_from_loop.rs b/src/test/ui/nll/return_from_loop.rs new file mode 100644 index 000000000..495410894 --- /dev/null +++ b/src/test/ui/nll/return_from_loop.rs @@ -0,0 +1,35 @@ +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` includes the points after `&v[0]` up to (but not +// including) the call to `use_x`. The `else` branch is not included. + +#![allow(warnings)] +#![feature(rustc_attrs)] + +struct MyStruct { + field: String +} + +fn main() { +} + +fn nll_fail() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &mut my_struct.field; + loop { + my_struct.field.push_str("Hello, world!"); + //~^ ERROR [E0499] + value.len(); + return; + } +} + +fn nll_ok() { + let mut my_struct = MyStruct { field: format!("Hello") }; + + let value = &mut my_struct.field; + loop { + my_struct.field.push_str("Hello, world!"); + return; + } +} diff --git a/src/test/ui/nll/return_from_loop.stderr b/src/test/ui/nll/return_from_loop.stderr new file mode 100644 index 000000000..bd2b8b158 --- /dev/null +++ b/src/test/ui/nll/return_from_loop.stderr @@ -0,0 +1,15 @@ +error[E0499]: cannot borrow `my_struct.field` as mutable more than once at a time + --> $DIR/return_from_loop.rs:20:9 + | +LL | let value = &mut my_struct.field; + | -------------------- first mutable borrow occurs here +LL | loop { +LL | my_struct.field.push_str("Hello, world!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here +LL | +LL | value.len(); + | ----------- 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/nll/self-assign-ref-mut.rs b/src/test/ui/nll/self-assign-ref-mut.rs new file mode 100644 index 000000000..1ca4cf3a7 --- /dev/null +++ b/src/test/ui/nll/self-assign-ref-mut.rs @@ -0,0 +1,20 @@ +// Check that `*y` isn't borrowed after `y = y`. + +// check-pass + +fn main() { + let mut x = 1; + { + let mut y = &mut x; + y = y; + y; + } + x; + { + let mut y = &mut x; + y = y; + y = y; + y; + } + x; +} diff --git a/src/test/ui/nll/snocat-regression.rs b/src/test/ui/nll/snocat-regression.rs new file mode 100644 index 000000000..b2e5995aa --- /dev/null +++ b/src/test/ui/nll/snocat-regression.rs @@ -0,0 +1,16 @@ +// Regression test from https://github.com/rust-lang/rust/pull/98109 + +pub fn negotiate<S>(link: S) +where + for<'a> &'a S: 'a, +{ + || { + //~^ ERROR `S` does not live long enough + // + // FIXME(#98437). This regressed at some point and + // probably should work. + let _x = link; + }; +} + +fn main() {} diff --git a/src/test/ui/nll/snocat-regression.stderr b/src/test/ui/nll/snocat-regression.stderr new file mode 100644 index 000000000..086898473 --- /dev/null +++ b/src/test/ui/nll/snocat-regression.stderr @@ -0,0 +1,14 @@ +error: `S` does not live long enough + --> $DIR/snocat-regression.rs:7:5 + | +LL | / || { +LL | | +LL | | // +LL | | // FIXME(#98437). This regressed at some point and +LL | | // probably should work. +LL | | let _x = link; +LL | | }; + | |_____^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/trait-associated-constant.rs b/src/test/ui/nll/trait-associated-constant.rs new file mode 100644 index 000000000..e13ae80f9 --- /dev/null +++ b/src/test/ui/nll/trait-associated-constant.rs @@ -0,0 +1,31 @@ +// Test cases where we put various lifetime constraints on trait +// associated constants. + +#![feature(rustc_attrs)] + +use std::option::Option; + +trait Anything<'a: 'b, 'b> { + const AC: Option<&'b str>; +} + +struct OKStruct1 { } + +impl<'a: 'b, 'b> Anything<'a, 'b> for OKStruct1 { + const AC: Option<&'b str> = None; +} + +struct FailStruct { } + +impl<'a: 'b, 'b, 'c> Anything<'a, 'b> for FailStruct { + const AC: Option<&'c str> = None; + //~^ ERROR: const not compatible with trait +} + +struct OKStruct2 { } + +impl<'a: 'b, 'b> Anything<'a, 'b> for OKStruct2 { + const AC: Option<&'a str> = None; +} + +fn main() {} diff --git a/src/test/ui/nll/trait-associated-constant.stderr b/src/test/ui/nll/trait-associated-constant.stderr new file mode 100644 index 000000000..ae0ffd904 --- /dev/null +++ b/src/test/ui/nll/trait-associated-constant.stderr @@ -0,0 +1,22 @@ +error[E0308]: const not compatible with trait + --> $DIR/trait-associated-constant.rs:21:5 + | +LL | const AC: Option<&'c str> = None; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch + | + = note: expected enum `Option<&'b str>` + found enum `Option<&'c str>` +note: the lifetime `'c` as defined here... + --> $DIR/trait-associated-constant.rs:20:18 + | +LL | impl<'a: 'b, 'b, 'c> Anything<'a, 'b> for FailStruct { + | ^^ +note: ...does not necessarily outlive the lifetime `'b` as defined here + --> $DIR/trait-associated-constant.rs:20:14 + | +LL | impl<'a: 'b, 'b, 'c> Anything<'a, 'b> for FailStruct { + | ^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/nll/ty-outlives/impl-trait-captures.rs b/src/test/ui/nll/ty-outlives/impl-trait-captures.rs new file mode 100644 index 000000000..67b31b8bc --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-captures.rs @@ -0,0 +1,15 @@ +// compile-flags:-Zverbose + +#![allow(warnings)] + +trait Foo<'a> { +} + +impl<'a, T> Foo<'a> for T { } + +fn foo<'a, T>(x: &T) -> impl Foo<'a> { + x + //~^ ERROR captures lifetime that does not appear in bounds +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr b/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr new file mode 100644 index 000000000..330c6fafa --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr @@ -0,0 +1,16 @@ +error[E0700]: hidden type for `Opaque(DefId(0:11 ~ impl_trait_captures[1afc]::foo::{opaque#0}), [ReStatic, T, ReEarlyBound(0, 'a)])` captures lifetime that does not appear in bounds + --> $DIR/impl-trait-captures.rs:11:5 + | +LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> { + | -- hidden type `&ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_)) T` captures the anonymous lifetime defined here +LL | x + | ^ + | +help: to declare that the `impl Trait` captures `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_))`, you can add an explicit `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_))` lifetime bound + | +LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_)) { + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/nll/ty-outlives/impl-trait-outlives.rs b/src/test/ui/nll/ty-outlives/impl-trait-outlives.rs new file mode 100644 index 000000000..68ccb51fc --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-outlives.rs @@ -0,0 +1,38 @@ +// compile-flags:-Zverbose + +#![allow(warnings)] + +use std::fmt::Debug; + +fn no_region<'a, T>(x: Box<T>) -> impl Debug + 'a +where + T: Debug, +{ + x + //~^ ERROR the parameter type `T` may not live long enough [E0309] +} + +fn correct_region<'a, T>(x: Box<T>) -> impl Debug + 'a +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box<T>) -> impl Debug + 'a +where + T: 'b + Debug, +{ + x + //~^ ERROR the parameter type `T` may not live long enough [E0309] +} + +fn outlives_region<'a, 'b, T>(x: Box<T>) -> impl Debug + 'a +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr b/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr new file mode 100644 index 000000000..64b08a9b3 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr @@ -0,0 +1,25 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/impl-trait-outlives.rs:11:5 + | +LL | x + | ^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: Debug + 'a, + | ++++ + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/impl-trait-outlives.rs:26:5 + | +LL | x + | ^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: 'b + Debug + 'a, + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/issue-53789-1.rs b/src/test/ui/nll/ty-outlives/issue-53789-1.rs new file mode 100644 index 000000000..a5201d4bb --- /dev/null +++ b/src/test/ui/nll/ty-outlives/issue-53789-1.rs @@ -0,0 +1,87 @@ +// Regression test for #53789. +// +// check-pass + +use std::collections::BTreeMap; + +trait ValueTree { + type Value; +} + +trait Strategy { + type Value: ValueTree; +} + +type StrategyFor<A> = StrategyType<'static, A>; +type StrategyType<'a, A> = <A as Arbitrary<'a>>::Strategy; + +impl<K: ValueTree, V: ValueTree> Strategy for (K, V) { + type Value = TupleValueTree<(K, V)>; +} + +impl<K: ValueTree, V: ValueTree> ValueTree for TupleValueTree<(K, V)> { + type Value = BTreeMapValueTree<K, V>; +} + +struct TupleValueTree<T> { + tree: T, +} + +struct BTreeMapStrategy<K, V>(std::marker::PhantomData<(K, V)>) +where + K: Strategy, + V: Strategy; + +struct BTreeMapValueTree<K, V>(std::marker::PhantomData<(K, V)>) +where + K: ValueTree, + V: ValueTree; + +impl<K, V> Strategy for BTreeMapStrategy<K, V> +where + K: Strategy, + V: Strategy, +{ + type Value = BTreeMapValueTree<K::Value, V::Value>; +} + +impl<K, V> ValueTree for BTreeMapValueTree<K, V> +where + K: ValueTree, + V: ValueTree, +{ + type Value = BTreeMap<K::Value, V::Value>; +} + +trait Arbitrary<'a>: Sized { + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy; + type Parameters; + type Strategy: Strategy<Value = Self::ValueTree>; + type ValueTree: ValueTree<Value = Self>; +} + +impl<'a, A, B> Arbitrary<'a> for BTreeMap<A, B> +where + A: Arbitrary<'static>, + B: Arbitrary<'static>, + StrategyFor<A>: 'static, + StrategyFor<B>: 'static, +{ + type ValueTree = <Self::Strategy as Strategy>::Value; + type Parameters = (A::Parameters, B::Parameters); + type Strategy = BTreeMapStrategy<A::Strategy, B::Strategy>; + fn arbitrary_with(args: Self::Parameters) -> BTreeMapStrategy<A::Strategy, B::Strategy> { + let (a, b) = args; + btree_map(any_with::<A>(a), any_with::<B>(b)) + } +} + +fn btree_map<K: Strategy + 'static, V: Strategy>(key: K, value: V) -> BTreeMapStrategy<K, V> { + unimplemented!() +} + +fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters) -> StrategyType<'a, A> { + unimplemented!() +} + +fn main() { } diff --git a/src/test/ui/nll/ty-outlives/issue-53789-2.rs b/src/test/ui/nll/ty-outlives/issue-53789-2.rs new file mode 100644 index 000000000..5109a0e4a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/issue-53789-2.rs @@ -0,0 +1,249 @@ +// Regression test for #53789. +// +// check-pass + +use std::cmp::Ord; +use std::collections::BTreeMap; +use std::ops::Range; + +macro_rules! valuetree { + () => { + type ValueTree = <Self::Strategy as $crate::Strategy>::Value; + }; +} + +macro_rules! product_unpack { + ($factor: pat) => { + ($factor,) + }; + ($($factor: pat),*) => { + ( $( $factor ),* ) + }; + ($($factor: pat),*,) => { + ( $( $factor ),* ) + }; +} + +macro_rules! product_type { + ($factor: ty) => { + ($factor,) + }; + ($($factor: ty),*) => { + ( $( $factor, )* ) + }; + ($($factor: ty),*,) => { + ( $( $factor, )* ) + }; +} + +macro_rules! default { + ($type: ty, $val: expr) => { + impl Default for $type { + fn default() -> Self { + $val.into() + } + } + }; +} + +// Pervasive internal sugar +macro_rules! mapfn { + ($(#[$meta:meta])* [$($vis:tt)*] + fn $name:ident[$($gen:tt)*]($parm:ident: $input:ty) -> $output:ty { + $($body:tt)* + }) => { + $(#[$meta])* + #[derive(Clone, Copy)] + $($vis)* struct $name; + impl $($gen)* statics::MapFn<$input> for $name { + type Output = $output; + } + } +} + +macro_rules! opaque_strategy_wrapper { + ($(#[$smeta:meta])* pub struct $stratname:ident + [$($sgen:tt)*][$($swhere:tt)*] + ($innerstrat:ty) -> $stratvtty:ty; + + $(#[$vmeta:meta])* pub struct $vtname:ident + [$($vgen:tt)*][$($vwhere:tt)*] + ($innervt:ty) -> $actualty:ty; + ) => { + $(#[$smeta])* struct $stratname $($sgen)* (std::marker::PhantomData<(K, V)>) + $($swhere)*; + + $(#[$vmeta])* struct $vtname $($vgen)* ($innervt) $($vwhere)*; + + impl $($sgen)* Strategy for $stratname $($sgen)* $($swhere)* { + type Value = $stratvtty; + } + + impl $($vgen)* ValueTree for $vtname $($vgen)* $($vwhere)* { + type Value = $actualty; + } + } +} + +trait ValueTree { + type Value; +} + +trait Strategy { + type Value: ValueTree; +} + +#[derive(Clone)] +struct VecStrategy<T: Strategy> { + element: T, + size: Range<usize>, +} + +fn vec<T: Strategy>(element: T, size: Range<usize>) -> VecStrategy<T> { + VecStrategy { element: element, size: size } +} + +type ValueFor<S> = <<S as Strategy>::Value as ValueTree>::Value; + +trait Arbitrary<'a>: Sized { + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy; + + type Parameters: Default; + type Strategy: Strategy<Value = Self::ValueTree>; + type ValueTree: ValueTree<Value = Self>; +} + +type StrategyFor<A> = StrategyType<'static, A>; +type StrategyType<'a, A> = <A as Arbitrary<'a>>::Strategy; + +//#[derive(Clone, PartialEq, Eq, Hash, Debug, From, Into)] +struct SizeBounds(Range<usize>); +default!(SizeBounds, 0..100); + +impl From<Range<usize>> for SizeBounds { + fn from(high: Range<usize>) -> Self { + unimplemented!() + } +} + +impl From<SizeBounds> for Range<usize> { + fn from(high: SizeBounds) -> Self { + unimplemented!() + } +} + +fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters) -> StrategyType<'a, A> { + unimplemented!() +} + +impl<K: ValueTree, V: ValueTree> Strategy for (K, V) +where + <K as ValueTree>::Value: Ord, +{ + type Value = TupleValueTree<(K, V)>; +} + +impl<K: ValueTree, V: ValueTree> ValueTree for TupleValueTree<(K, V)> +where + <K as ValueTree>::Value: Ord, +{ + type Value = BTreeMapValueTree<K, V>; +} + +#[derive(Clone)] +struct VecValueTree<T: ValueTree> { + elements: Vec<T>, +} + +#[derive(Clone, Copy)] +struct TupleValueTree<T> { + tree: T, +} + +opaque_strategy_wrapper! { + #[derive(Clone)] + pub struct BTreeMapStrategy[<K, V>] + [where K : Strategy, V : Strategy, ValueFor<K> : Ord]( + statics::Filter<statics::Map<VecStrategy<(K,V)>, + VecToBTreeMap>, MinSize>) + -> BTreeMapValueTree<K::Value, V::Value>; + + #[derive(Clone)] + pub struct BTreeMapValueTree[<K, V>] + [where K : ValueTree, V : ValueTree, K::Value : Ord]( + statics::Filter<statics::Map<VecValueTree<TupleValueTree<(K, V)>>, + VecToBTreeMap>, MinSize>) + -> BTreeMap<K::Value, V::Value>; +} + +type RangedParams2<A, B> = product_type![SizeBounds, A, B]; + +impl<'a, A, B> Arbitrary<'a> for BTreeMap<A, B> +where + A: Arbitrary<'static> + Ord, + B: Arbitrary<'static>, + StrategyFor<A>: 'static, + StrategyFor<B>: 'static, +{ + valuetree!(); + type Parameters = RangedParams2<A::Parameters, B::Parameters>; + type Strategy = BTreeMapStrategy<A::Strategy, B::Strategy>; + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { + let product_unpack![range, a, b] = args; + btree_map(any_with::<A>(a), any_with::<B>(b), range.into()) + } +} + +#[derive(Clone, Copy)] +struct MinSize(usize); + +mapfn! { + [] fn VecToBTreeMap[<K : Ord, V>] + (vec: Vec<(K, V)>) -> BTreeMap<K, V> + { + vec.into_iter().collect() + } +} + +fn btree_map<K: Strategy + 'static, V: Strategy + 'static>( + key: K, + value: V, + size: Range<usize>, +) -> BTreeMapStrategy<K, V> +where + ValueFor<K>: Ord, +{ + unimplemented!() +} + +mod statics { + pub(super) trait MapFn<T> { + type Output; + } + + #[derive(Clone)] + pub struct Filter<S, F> { + source: S, + fun: F, + } + + impl<S, F> Filter<S, F> { + pub fn new(source: S, whence: String, filter: F) -> Self { + unimplemented!() + } + } + + #[derive(Clone)] + pub struct Map<S, F> { + source: S, + fun: F, + } + + impl<S, F> Map<S, F> { + pub fn new(source: S, fun: F) -> Self { + unimplemented!() + } + } +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/issue-55756.rs b/src/test/ui/nll/ty-outlives/issue-55756.rs new file mode 100644 index 000000000..e1a3bc3c4 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/issue-55756.rs @@ -0,0 +1,37 @@ +// Regression test for #55756. +// +// In this test, the result of `self.callee` is a projection `<D as +// Database<'?0>>::Guard`. As it may contain a destructor, the dropck +// rules require that this type outlivess the scope of `state`. Unfortunately, +// our region inference is not smart enough to figure out how to +// translate a requirement like +// +// <D as Database<'0>>::guard: 'r +// +// into a requirement that `'0: 'r` -- in particular, it fails to do +// so because it *also* knows that `<D as Database<'a>>::Guard: 'a` +// from the trait definition. Faced with so many choices, the current +// solver opts to do nothing. +// +// Fixed by tweaking the solver to recognize that the constraint from +// the environment duplicates one from the trait. +// +// check-pass + +#![crate_type="lib"] + +pub trait Database<'a> { + type Guard: 'a; +} + +pub struct Stateful<'a, D: 'a>(&'a D); + +impl<'b, D: for <'a> Database<'a>> Stateful<'b, D> { + pub fn callee<'a>(&'a self) -> <D as Database<'a>>::Guard { + unimplemented!() + } + pub fn caller<'a>(&'a self) -> <D as Database<'a>>::Guard { + let state = self.callee(); + unimplemented!() + } +} diff --git a/src/test/ui/nll/ty-outlives/projection-body.rs b/src/test/ui/nll/ty-outlives/projection-body.rs new file mode 100644 index 000000000..b03a539eb --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-body.rs @@ -0,0 +1,27 @@ +// Test that when we infer the lifetime to a subset of the fn body, it +// works out. +// +// check-pass + +trait MyTrait<'a> { + type Output; +} + +fn foo1<T>() +where + for<'x> T: MyTrait<'x>, +{ + // Here the region `'c` in `<T as MyTrait<'c>>::Output` will be + // inferred to a subset of the fn body. + let x = bar::<T::Output>(); + drop(x); +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-implied-bounds.rs b/src/test/ui/nll/ty-outlives/projection-implied-bounds.rs new file mode 100644 index 000000000..e1dac0824 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-implied-bounds.rs @@ -0,0 +1,40 @@ +// Test that we can deduce when projections like `T::Item` outlive the +// function body. Test that this does not imply that `T: 'a` holds. + +// compile-flags:-Zverbose + +use std::cell::Cell; + +fn twice<F, T>(mut value: T, mut f: F) +where + F: FnMut(&T, Cell<&Option<T::Item>>), + T: Iterator, +{ + let mut n = value.next(); + f(&value, Cell::new(&n)); + f(&value, Cell::new(&n)); +} + +fn generic1<T: Iterator>(value: T) { + // No error here: + twice(value, |value_ref, item| invoke1(item)); +} + +fn invoke1<'a, T>(x: Cell<&'a Option<T>>) +where + T: 'a, +{ +} + +fn generic2<T: Iterator>(value: T) { + twice(value, |value_ref, item| invoke2(value_ref, item)); + //~^ ERROR the parameter type `T` may not live long enough +} + +fn invoke2<'a, T, U>(a: &T, b: Cell<&'a Option<U>>) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr b/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr new file mode 100644 index 000000000..3b9b2956c --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr @@ -0,0 +1,14 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projection-implied-bounds.rs:30:18 + | +LL | twice(value, |value_ref, item| invoke2(value_ref, item)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn generic2<T: Iterator + 'static>(value: T) { + | +++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs new file mode 100644 index 000000000..2d9c008c7 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs @@ -0,0 +1,55 @@ +// compile-flags:-Zverbose + +// Tests closures that propagate an outlives relationship to their +// creator where the subject is a projection with no regions (`<T as +// Iterator>::Item`, to be exact). + +#![allow(warnings)] +#![feature(rustc_attrs)] + +trait Anything { } + +impl<T> Anything for T { } + +fn with_signature<'a, T, F>(x: Box<T>, op: F) -> Box<dyn Anything + 'a> + where F: FnOnce(Box<T>) -> Box<dyn Anything + 'a> +{ + op(x) +} + +#[rustc_regions] +fn no_region<'a, T>(x: Box<T>) -> Box<dyn Anything + 'a> +where + T: Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) + //~^ ERROR the associated type `<T as Iterator>::Item` may not live long enough +} + +#[rustc_regions] +fn correct_region<'a, T>(x: Box<T>) -> Box<dyn Anything + 'a> +where + T: 'a + Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) +} + +#[rustc_regions] +fn wrong_region<'a, 'b, T>(x: Box<T>) -> Box<dyn Anything + 'a> +where + T: 'b + Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) + //~^ ERROR the associated type `<T as Iterator>::Item` may not live long enough +} + +#[rustc_regions] +fn outlives_region<'a, 'b, T>(x: Box<T>) -> Box<dyn Anything + 'a> +where + T: 'b + Iterator, + 'b: 'a, +{ + with_signature(x, |mut y| Box::new(y.next())) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr new file mode 100644 index 000000000..feab24769 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -0,0 +1,132 @@ +note: external requirements + --> $DIR/projection-no-regions-closure.rs:25:23 + | +LL | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^ + | + = note: defining type: no_region::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#2r)>, + (), + ] + = note: number of external vids: 3 + = note: where <T as std::iter::Iterator>::Item: '_#2r + +note: no external requirements + --> $DIR/projection-no-regions-closure.rs:21:1 + | +LL | / fn no_region<'a, T>(x: Box<T>) -> Box<dyn Anything + 'a> +LL | | where +LL | | T: Iterator, +LL | | { +LL | | with_signature(x, |mut y| Box::new(y.next())) +LL | | +LL | | } + | |_^ + | + = note: defining type: no_region::<'_#1r, T> + +error[E0309]: the associated type `<T as Iterator>::Item` may not live long enough + --> $DIR/projection-no-regions-closure.rs:25:23 + | +LL | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`... + = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds + +note: external requirements + --> $DIR/projection-no-regions-closure.rs:34:23 + | +LL | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^ + | + = note: defining type: correct_region::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#2r)>, + (), + ] + = note: number of external vids: 3 + = note: where <T as std::iter::Iterator>::Item: '_#2r + +note: no external requirements + --> $DIR/projection-no-regions-closure.rs:30:1 + | +LL | / fn correct_region<'a, T>(x: Box<T>) -> Box<dyn Anything + 'a> +LL | | where +LL | | T: 'a + Iterator, +LL | | { +LL | | with_signature(x, |mut y| Box::new(y.next())) +LL | | } + | |_^ + | + = note: defining type: correct_region::<'_#1r, T> + +note: external requirements + --> $DIR/projection-no-regions-closure.rs:42:23 + | +LL | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^ + | + = note: defining type: wrong_region::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#3r)>, + (), + ] + = note: number of external vids: 4 + = note: where <T as std::iter::Iterator>::Item: '_#3r + +note: no external requirements + --> $DIR/projection-no-regions-closure.rs:38:1 + | +LL | / fn wrong_region<'a, 'b, T>(x: Box<T>) -> Box<dyn Anything + 'a> +LL | | where +LL | | T: 'b + Iterator, +LL | | { +LL | | with_signature(x, |mut y| Box::new(y.next())) +LL | | +LL | | } + | |_^ + | + = note: defining type: wrong_region::<'_#1r, '_#2r, T> + +error[E0309]: the associated type `<T as Iterator>::Item` may not live long enough + --> $DIR/projection-no-regions-closure.rs:42:23 + | +LL | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`... + = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds + +note: external requirements + --> $DIR/projection-no-regions-closure.rs:52:23 + | +LL | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^ + | + = note: defining type: outlives_region::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#3r)>, + (), + ] + = note: number of external vids: 4 + = note: where <T as std::iter::Iterator>::Item: '_#3r + +note: no external requirements + --> $DIR/projection-no-regions-closure.rs:47:1 + | +LL | / fn outlives_region<'a, 'b, T>(x: Box<T>) -> Box<dyn Anything + 'a> +LL | | where +LL | | T: 'b + Iterator, +LL | | 'b: 'a, +LL | | { +LL | | with_signature(x, |mut y| Box::new(y.next())) +LL | | } + | |_^ + | + = note: defining type: outlives_region::<'_#1r, '_#2r, T> + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs new file mode 100644 index 000000000..a10a0366a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs @@ -0,0 +1,40 @@ +// compile-flags:-Zverbose + +#![allow(warnings)] + +trait Anything { } + +impl<T> Anything for T { } + +fn no_region<'a, T>(mut x: T) -> Box<dyn Anything + 'a> +where + T: Iterator, +{ + Box::new(x.next()) + //~^ ERROR the associated type `<T as Iterator>::Item` may not live long enough +} + +fn correct_region<'a, T>(mut x: T) -> Box<dyn Anything + 'a> +where + T: 'a + Iterator, +{ + Box::new(x.next()) +} + +fn wrong_region<'a, 'b, T>(mut x: T) -> Box<dyn Anything + 'a> +where + T: 'b + Iterator, +{ + Box::new(x.next()) + //~^ ERROR the associated type `<T as Iterator>::Item` may not live long enough +} + +fn outlives_region<'a, 'b, T>(mut x: T) -> Box<dyn Anything + 'a> +where + T: 'b + Iterator, + 'b: 'a, +{ + Box::new(x.next()) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr new file mode 100644 index 000000000..e0ff544fe --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr @@ -0,0 +1,21 @@ +error[E0309]: the associated type `<T as Iterator>::Item` may not live long enough + --> $DIR/projection-no-regions-fn.rs:13:5 + | +LL | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`... + = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds + +error[E0309]: the associated type `<T as Iterator>::Item` may not live long enough + --> $DIR/projection-no-regions-fn.rs:28:5 + | +LL | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`... + = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs new file mode 100644 index 000000000..af361e990 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs @@ -0,0 +1,83 @@ +// Test cases where we constrain `<T as Anything<'b>>::AssocType` to +// outlive `'a` and there are no bounds in the trait definition of +// `Anything`. This means that the constraint can only be satisfied in two +// ways: +// +// - by ensuring that `T: 'a` and `'b: 'a`, or +// - by something in the where clauses. +// +// As of this writing, the where clause option does not work because +// of limitations in our region inferencing system (this is true both +// with and without NLL). See `projection_outlives`. +// +// Ensuring that both `T: 'a` and `'b: 'a` holds does work (`elements_outlive`). + +// compile-flags:-Zverbose + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // We are projecting `<T as Anything<'b>>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. + + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T: 'a, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr new file mode 100644 index 000000000..98063bd0a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -0,0 +1,167 @@ +note: external requirements + --> $DIR/projection-one-region-closure.rs:45:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: no_relationships_late::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), + ] + = note: late-bound region is '_#3r + = note: number of external vids: 4 + = note: where T: '_#2r + = note: where '_#1r: '_#2r + +note: no external requirements + --> $DIR/projection-one-region-closure.rs:41:1 + | +LL | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | { +... | +LL | | +LL | | } + | |_^ + | + = note: defining type: no_relationships_late::<'_#1r, T> + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/projection-one-region-closure.rs:45:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: Anything<'b> + 'a, + | ++++ + +error: lifetime may not live long enough + --> $DIR/projection-one-region-closure.rs:45:39 + | +LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +note: external requirements + --> $DIR/projection-one-region-closure.rs:56:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: no_relationships_early::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +note: no external requirements + --> $DIR/projection-one-region-closure.rs:51:1 + | +LL | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | 'a: 'a, +... | +LL | | +LL | | } + | |_^ + | + = note: defining type: no_relationships_early::<'_#1r, '_#2r, T> + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/projection-one-region-closure.rs:56:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: Anything<'b> + 'a, + | ++++ + +error: lifetime may not live long enough + --> $DIR/projection-one-region-closure.rs:56:39 + | +LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +note: external requirements + --> $DIR/projection-one-region-closure.rs:70:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: projection_outlives::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + = note: number of external vids: 4 + = note: where <T as Anything<ReEarlyBound(1, 'b)>>::AssocType: '_#3r + +note: no external requirements + --> $DIR/projection-one-region-closure.rs:62:1 + | +LL | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | T::AssocType: 'a, +... | +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: projection_outlives::<'_#1r, '_#2r, T> + +note: external requirements + --> $DIR/projection-one-region-closure.rs:80:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: elements_outlive::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +note: no external requirements + --> $DIR/projection-one-region-closure.rs:74:1 + | +LL | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | T: 'a, +... | +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: elements_outlive::<'_#1r, '_#2r, T> + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs new file mode 100644 index 000000000..6f8513491 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs @@ -0,0 +1,84 @@ +// Test cases where we constrain `<T as Anything<'b>>::AssocType` to +// outlive `'a` and there is a unique bound in the trait definition of +// `Anything` -- i.e., we know that `AssocType` outlives `'b`. In this +// case, the best way to satisfy the trait bound is to show that `'b: +// 'a`, which can be done in various ways. + +// compile-flags:-Zverbose + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType: 'a; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ ERROR +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ ERROR +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // We are projecting `<T as Anything<'b>>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. + + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr new file mode 100644 index 000000000..45e61bcbd --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -0,0 +1,169 @@ +note: external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:37:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: no_relationships_late::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), + ] + = note: late-bound region is '_#3r + = note: number of external vids: 4 + = note: where '_#1r: '_#2r + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:33:1 + | +LL | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | +LL | | } + | |_^ + | + = note: defining type: no_relationships_late::<'_#1r, T> + +error: lifetime may not live long enough + --> $DIR/projection-one-region-trait-bound-closure.rs:37:39 + | +LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +note: external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:47:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: no_relationships_early::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:42:1 + | +LL | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | 'a: 'a, +... | +LL | | +LL | | } + | |_^ + | + = note: defining type: no_relationships_early::<'_#1r, '_#2r, T> + +error: lifetime may not live long enough + --> $DIR/projection-one-region-trait-bound-closure.rs:47:39 + | +LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +note: external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:60:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: projection_outlives::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + = note: number of external vids: 4 + = note: where <T as Anything<ReEarlyBound(1, 'b)>>::AssocType: '_#3r + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:52:1 + | +LL | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | T::AssocType: 'a, +... | +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: projection_outlives::<'_#1r, '_#2r, T> + +note: external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:69:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: elements_outlive::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:64:1 + | +LL | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | 'b: 'a, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: elements_outlive::<'_#1r, '_#2r, T> + +note: external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:81:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: one_region::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), + ] + = note: number of external vids: 3 + = note: where '_#1r: '_#2r + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:73:1 + | +LL | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'a>, +LL | | { +... | +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: one_region::<'_#1r, T> + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs new file mode 100644 index 000000000..7c0a3bc72 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs @@ -0,0 +1,88 @@ +// Test cases where we constrain `<T as Anything<'b>>::AssocType` to +// outlive `'static`. In this case, we don't get any errors, and in fact +// we don't even propagate constraints from the closures to the callers. + +// compile-flags:-Zverbose +// check-pass + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType: 'static; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `<T as Anything<'b>>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `<T as + // Anything<'?0>>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr new file mode 100644 index 000000000..f2549205b --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr @@ -0,0 +1,130 @@ +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:36:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: no_relationships_late::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), + ] + = note: late-bound region is '_#3r + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:32:1 + | +LL | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: no_relationships_late::<'_#1r, T> + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:45:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: no_relationships_early::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:40:1 + | +LL | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | 'a: 'a, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: no_relationships_early::<'_#1r, '_#2r, T> + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:64:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: projection_outlives::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:49:1 + | +LL | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | T::AssocType: 'a, +... | +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: projection_outlives::<'_#1r, '_#2r, T> + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:73:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: elements_outlive::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:68:1 + | +LL | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b>, +LL | | 'b: 'a, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: elements_outlive::<'_#1r, '_#2r, T> + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:85:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: one_region::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), + ] + +note: no external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:77:1 + | +LL | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'a>, +LL | | { +... | +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: one_region::<'_#1r, T> + diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs new file mode 100644 index 000000000..7b4a3c03a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs @@ -0,0 +1,112 @@ +// Test cases where we constrain `<T as Anything<'a, 'b>>::AssocType` +// to outlive `'a` and there are two bounds in the trait definition of +// `Anything` -- i.e., we know that `AssocType` outlives `'a` and +// `'b`. In this case, it's not clear what is the best way to satisfy +// the trait bound, and hence we propagate it to the caller as a type +// test. + +// compile-flags:-Zverbose + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a, 'b> { + type AssocType: 'a + 'b; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, 'c, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b, 'c>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ ERROR may not live long enough +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ ERROR may not live long enough +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + T::AssocType: 'a, +{ + // We are projecting `<T as Anything<'b>>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. + + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'c: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ ERROR lifetime may not live long enough +} + +#[rustc_regions] +fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a, 'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr new file mode 100644 index 000000000..8e1b6fa2e --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -0,0 +1,260 @@ +note: external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:38:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: no_relationships_late::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + = note: late-bound region is '_#4r + = note: number of external vids: 5 + = note: where <T as Anything<ReEarlyBound(0, 'b), ReEarlyBound(1, 'c)>>::AssocType: '_#3r + +note: no external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:34:1 + | +LL | / fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b, 'c>, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | +LL | | } + | |_^ + | + = note: defining type: no_relationships_late::<'_#1r, '_#2r, T> + +error[E0309]: the associated type `<T as Anything<ReEarlyBound(0, 'b), ReEarlyBound(1, 'c)>>::AssocType` may not live long enough + --> $DIR/projection-two-region-trait-bound-closure.rs:38:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `<T as Anything<ReEarlyBound(0, 'b), ReEarlyBound(1, 'c)>>::AssocType: 'a`... + = note: ...so that the type `<T as Anything<ReEarlyBound(0, 'b), ReEarlyBound(1, 'c)>>::AssocType` will meet its required lifetime bounds + +note: external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:48:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: no_relationships_early::<'_#1r, '_#2r, '_#3r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), + (), + ] + = note: number of external vids: 5 + = note: where <T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType: '_#4r + +note: no external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:43:1 + | +LL | / fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b, 'c>, +LL | | 'a: 'a, +... | +LL | | +LL | | } + | |_^ + | + = note: defining type: no_relationships_early::<'_#1r, '_#2r, '_#3r, T> + +error[E0309]: the associated type `<T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType` may not live long enough + --> $DIR/projection-two-region-trait-bound-closure.rs:48:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `<T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType: 'a`... + = note: ...so that the type `<T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType` will meet its required lifetime bounds + +note: external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:61:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: projection_outlives::<'_#1r, '_#2r, '_#3r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), + (), + ] + = note: number of external vids: 5 + = note: where <T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType: '_#4r + +note: no external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:53:1 + | +LL | / fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b, 'c>, +LL | | T::AssocType: 'a, +... | +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: projection_outlives::<'_#1r, '_#2r, '_#3r, T> + +note: external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:70:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: elements_outlive1::<'_#1r, '_#2r, '_#3r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), + (), + ] + = note: number of external vids: 5 + = note: where <T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType: '_#4r + +note: no external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:65:1 + | +LL | / fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b, 'c>, +LL | | 'b: 'a, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: elements_outlive1::<'_#1r, '_#2r, '_#3r, T> + +note: external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:79:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: elements_outlive2::<'_#1r, '_#2r, '_#3r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), + (), + ] + = note: number of external vids: 5 + = note: where <T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType: '_#4r + +note: no external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:74:1 + | +LL | / fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b, 'c>, +LL | | 'c: 'a, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: elements_outlive2::<'_#1r, '_#2r, '_#3r, T> + +note: external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:87:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: two_regions::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), + ] + = note: late-bound region is '_#3r + = note: number of external vids: 4 + = note: where <T as Anything<ReEarlyBound(0, 'b), ReEarlyBound(0, 'b)>>::AssocType: '_#2r + +note: no external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:83:1 + | +LL | / fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b, 'b>, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | +LL | | } + | |_^ + | + = note: defining type: two_regions::<'_#1r, T> + +error: lifetime may not live long enough + --> $DIR/projection-two-region-trait-bound-closure.rs:87:29 + | +LL | fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +note: external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:97:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: two_regions_outlive::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + = note: number of external vids: 4 + = note: where <T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(1, 'b)>>::AssocType: '_#3r + +note: no external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:92:1 + | +LL | / fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'b, 'b>, +LL | | 'b: 'a, +LL | | { +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: two_regions_outlive::<'_#1r, '_#2r, T> + +note: external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:109:29 + | +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^ + | + = note: defining type: one_region::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), + ] + = note: number of external vids: 3 + = note: where <T as Anything<ReEarlyBound(0, 'a), ReEarlyBound(0, 'a)>>::AssocType: '_#2r + +note: no external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:101:1 + | +LL | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +LL | | where +LL | | T: Anything<'a, 'a>, +LL | | { +... | +LL | | with_signature(cell, t, |cell, t| require(cell, t)); +LL | | } + | |_^ + | + = note: defining type: one_region::<'_#1r, T> + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.rs new file mode 100644 index 000000000..dce88b88c --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.rs @@ -0,0 +1,34 @@ +// Test that we are able to establish that `<T as +// MyTrait<'a>>::Output` outlives `'b` here. We need to prove however +// that `<T as MyTrait<'a>>::Output` outlives `'a`, so we also have to +// prove that `'b: 'a`. + +trait MyTrait<'a> { + type Output; +} + +fn foo1<'a, 'b, T>() -> &'a () +where + T: MyTrait<'a>, + <T as MyTrait<'a>>::Output: 'b, +{ + bar::<T::Output>() //~ ERROR may not live long enough +} + +fn foo2<'a, 'b, T>() -> &'a () +where + T: MyTrait<'a>, + <T as MyTrait<'a>>::Output: 'b, + 'b: 'a, +{ + bar::<T::Output>() // OK +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr new file mode 100644 index 000000000..b4435fe06 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr @@ -0,0 +1,12 @@ +error[E0309]: the associated type `<T as MyTrait<'_>>::Output` may not live long enough + --> $DIR/projection-where-clause-env-wrong-bound.rs:15:5 + | +LL | bar::<T::Output>() + | ^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `<T as MyTrait<'_>>::Output: 'a`... + = note: ...so that the type `<T as MyTrait<'_>>::Output` will meet its required lifetime bounds + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs new file mode 100644 index 000000000..987148dce --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs @@ -0,0 +1,25 @@ +// Test that if we need to prove that `<T as MyTrait<'a>>::Output: +// 'a`, but we only know that `<T as MyTrait<'b>>::Output: 'a`, that +// doesn't suffice. + +trait MyTrait<'a> { + type Output; +} + +fn foo1<'a, 'b, T>() -> &'a () +where + for<'x> T: MyTrait<'x>, + <T as MyTrait<'b>>::Output: 'a, +{ + bar::<<T as MyTrait<'a>>::Output>() + //~^ ERROR the associated type `<T as MyTrait<'_>>::Output` may not live long enough +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr new file mode 100644 index 000000000..ddeaf3c1f --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr @@ -0,0 +1,12 @@ +error[E0309]: the associated type `<T as MyTrait<'_>>::Output` may not live long enough + --> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5 + | +LL | bar::<<T as MyTrait<'a>>::Output>() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `<T as MyTrait<'_>>::Output: 'a`... + = note: ...so that the type `<T as MyTrait<'_>>::Output` will meet its required lifetime bounds + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-env.rs new file mode 100644 index 000000000..a41116232 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env.rs @@ -0,0 +1,28 @@ +// Test that when we have a `<T as MyTrait<'a>>::Output: 'a` +// relationship in the environment we take advantage of it. In this +// case, that means we **don't** have to prove that `T: 'a`. +// +// Regression test for #53121. +// +// check-pass + +trait MyTrait<'a> { + type Output; +} + +fn foo<'a, T>() -> &'a () +where + T: MyTrait<'a>, + <T as MyTrait<'a>>::Output: 'a, +{ + bar::<T::Output>() +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-none.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-none.rs new file mode 100644 index 000000000..bb201e5c0 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-none.rs @@ -0,0 +1,24 @@ +// Test that we are NOT able to establish that `<T as +// MyTrait<'a>>::Output: 'a` outlives `'a` here -- we have only one +// recourse, which is to prove that `T: 'a` and `'a: 'a`, but we don't +// know that `T: 'a`. + +trait MyTrait<'a> { + type Output; +} + +fn foo<'a, T>() -> &'a () +where + T: MyTrait<'a>, +{ + bar::<T::Output>() //~ ERROR the parameter type `T` may not live long enough +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr new file mode 100644 index 000000000..0df44644d --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr @@ -0,0 +1,14 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/projection-where-clause-none.rs:14:5 + | +LL | bar::<T::Output>() + | ^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: MyTrait<'a> + 'a, + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-trait.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-trait.rs new file mode 100644 index 000000000..1a40d3b4c --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-trait.rs @@ -0,0 +1,25 @@ +// Test that we are able to establish that `<T as +// MyTrait<'a>>::Output: 'a` outlives `'a` (because the trait says +// so). +// +// check-pass + +trait MyTrait<'a> { + type Output: 'a; +} + +fn foo<'a, T>() -> &'a () +where + T: MyTrait<'a>, +{ + bar::<T::Output>() +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.rs new file mode 100644 index 000000000..4d8380599 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.rs @@ -0,0 +1,39 @@ +// compile-flags:-Zverbose + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Invoke in such a way that the callee knows: +// +// - 'a: 'x +// +// and it must prove that `T: 'x`. Callee passes along `T: 'a`. +fn twice<'a, F, T>(v: Cell<&'a ()>, value: T, mut f: F) +where + F: for<'x> FnMut(Option<Cell<&'a &'x ()>>, &T), +{ + f(None, &value); + f(None, &value); +} + +#[rustc_regions] +fn generic<T>(value: T) { + let cell = Cell::new(&()); + twice(cell, value, |a, b| invoke(a, b)); +} + +#[rustc_regions] +fn generic_fail<'a, T>(cell: Cell<&'a ()>, value: T) { + twice(cell, value, |a, b| invoke(a, b)); + //~^ ERROR the parameter type `T` may not live long enough +} + +fn invoke<'a, 'x, T>(x: Option<Cell<&'x &'a ()>>, y: &T) +where + T: 'x, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr new file mode 100644 index 000000000..12c76d198 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr @@ -0,0 +1,65 @@ +note: external requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:24:24 + | +LL | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^ + | + = note: defining type: generic::<T>::{closure#0} with closure substs [ + i16, + for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) ()>>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) T)), + (), + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +note: no external requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:22:1 + | +LL | / fn generic<T>(value: T) { +LL | | let cell = Cell::new(&()); +LL | | twice(cell, value, |a, b| invoke(a, b)); +LL | | } + | |_^ + | + = note: defining type: generic::<T> + +note: external requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:29:24 + | +LL | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^ + | + = note: defining type: generic_fail::<T>::{closure#0} with closure substs [ + i16, + for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) ()>>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) T)), + (), + ] + = note: late-bound region is '_#2r + = note: number of external vids: 3 + = note: where T: '_#1r + +note: no external requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:28:1 + | +LL | / fn generic_fail<'a, T>(cell: Cell<&'a ()>, value: T) { +LL | | twice(cell, value, |a, b| invoke(a, b)); +LL | | +LL | | } + | |_^ + | + = note: defining type: generic_fail::<T> + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-closure-approximate-lower-bound.rs:29:24 + | +LL | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn generic_fail<'a, T: 'a>(cell: Cell<&'a ()>, value: T) { + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs new file mode 100644 index 000000000..4343c3aee --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs @@ -0,0 +1,53 @@ +// compile-flags:-Zverbose + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::fmt::Debug; + +fn with_signature<'a, T, F>(x: Box<T>, op: F) -> Box<dyn Debug + 'a> + where F: FnOnce(Box<T>) -> Box<dyn Debug + 'a> +{ + op(x) +} + +#[rustc_regions] +fn no_region<'a, T>(x: Box<T>) -> Box<dyn Debug + 'a> +where + T: Debug, +{ + // Here, the closure winds up being required to prove that `T: + // 'a`. In principle, it could know that, except that it is + // type-checked in a fully generic way, and hence it winds up with + // a propagated requirement that `T: '_#2`, where `'_#2` appears + // in the return type. The caller makes the mapping from `'_#2` to + // `'a` (and subsequently reports an error). + + with_signature(x, |y| y) + //~^ ERROR the parameter type `T` may not live long enough +} + +fn correct_region<'a, T>(x: Box<T>) -> Box<Debug + 'a> +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a> +where + T: 'b + Debug, +{ + x + //~^ ERROR the parameter type `T` may not live long enough +} + +fn outlives_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a> +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr new file mode 100644 index 000000000..35741859d --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -0,0 +1,53 @@ +note: external requirements + --> $DIR/ty-param-closure-outlives-from-return-type.rs:26:23 + | +LL | with_signature(x, |y| y) + | ^^^ + | + = note: defining type: no_region::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn std::fmt::Debug + '_#2r)>, + (), + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +note: no external requirements + --> $DIR/ty-param-closure-outlives-from-return-type.rs:15:1 + | +LL | / fn no_region<'a, T>(x: Box<T>) -> Box<dyn Debug + 'a> +LL | | where +LL | | T: Debug, +LL | | { +... | +LL | | +LL | | } + | |_^ + | + = note: defining type: no_region::<'_#1r, T> + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-closure-outlives-from-return-type.rs:26:23 + | +LL | with_signature(x, |y| y) + | ^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: Debug + 'a, + | ++++ + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-closure-outlives-from-return-type.rs:41:5 + | +LL | x + | ^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: 'b + Debug + 'a, + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs new file mode 100644 index 000000000..d7702def3 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs @@ -0,0 +1,83 @@ +// Test that we can propagate `T: 'a` obligations to our caller. See +// `correct_region` for an explanation of how this test is setup; it's +// somewhat intricate. + +// compile-flags:-Zverbose + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +fn with_signature<'a, T, F>(a: Cell<&'a ()>, b: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(a, b) +} + +fn require<'a, T>(_a: &Cell<&'a ()>, _b: &T) +where + T: 'a, +{ +} + +#[rustc_regions] +fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { + with_signature(a, b, |x, y| { + //~^ ERROR the parameter type `T` may not live long enough + // + // See `correct_region`, which explains the point of this + // test. The only difference is that, in the case of this + // function, there is no where clause *anywhere*, and hence we + // get an error (but reported by the closure creator). + require(&x, &y) + }) +} + +#[rustc_regions] +fn correct_region<'a, T>(a: Cell<&'a ()>, b: T) +where + T: 'a, +{ + with_signature(a, b, |x, y| { + // Key point of this test: + // + // The *closure* is being type-checked with all of its free + // regions "universalized". In particular, it does not know + // that `x` has the type `Cell<&'a ()>`, but rather treats it + // as if the type of `x` is `Cell<&'A ()>`, where `'A` is some + // fresh, independent region distinct from the `'a` which + // appears in the environment. The call to `require` here + // forces us then to prove that `T: 'A`, but the closure + // cannot do it on its own. It has to surface this requirement + // to its creator (which knows that `'a == 'A`). + require(&x, &y) + }) +} + +#[rustc_regions] +fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +where + T: 'b, +{ + with_signature(a, b, |x, y| { + //~^ ERROR the parameter type `T` may not live long enough + // See `correct_region` + require(&x, &y) + }) +} + +#[rustc_regions] +fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +where + T: 'b, + 'b: 'a, +{ + with_signature(a, b, |x, y| { + // See `correct_region` + require(&x, &y) + }) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr new file mode 100644 index 000000000..0261bc39e --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr @@ -0,0 +1,151 @@ +note: external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:27:26 + | +LL | with_signature(a, b, |x, y| { + | ^^^^^^ + | + = note: defining type: no_region::<T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#1r ()>, T)), + (), + ] + = note: late-bound region is '_#2r + = note: number of external vids: 3 + = note: where T: '_#1r + +note: no external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:26:1 + | +LL | / fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { +LL | | with_signature(a, b, |x, y| { +LL | | +LL | | // +... | +LL | | }) +LL | | } + | |_^ + | + = note: defining type: no_region::<T> + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:27:26 + | +LL | with_signature(a, b, |x, y| { + | __________________________^ +LL | | +LL | | // +LL | | // See `correct_region`, which explains the point of this +... | +LL | | require(&x, &y) +LL | | }) + | |_____^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn no_region<'a, T: 'a>(a: Cell<&'a ()>, b: T) { + | ++++ + +note: external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:43:26 + | +LL | with_signature(a, b, |x, y| { + | ^^^^^^ + | + = note: defining type: correct_region::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +note: no external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:39:1 + | +LL | / fn correct_region<'a, T>(a: Cell<&'a ()>, b: T) +LL | | where +LL | | T: 'a, +LL | | { +... | +LL | | }) +LL | | } + | |_^ + | + = note: defining type: correct_region::<'_#1r, T> + +note: external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:64:26 + | +LL | with_signature(a, b, |x, y| { + | ^^^^^^ + | + = note: defining type: wrong_region::<'_#1r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), + ] + = note: late-bound region is '_#3r + = note: number of external vids: 4 + = note: where T: '_#2r + +note: no external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:60:1 + | +LL | / fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +LL | | where +LL | | T: 'b, +LL | | { +... | +LL | | }) +LL | | } + | |_^ + | + = note: defining type: wrong_region::<'_#1r, T> + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:64:26 + | +LL | with_signature(a, b, |x, y| { + | __________________________^ +LL | | +LL | | // See `correct_region` +LL | | require(&x, &y) +LL | | }) + | |_____^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: 'b + 'a, + | ++++ + +note: external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:77:26 + | +LL | with_signature(a, b, |x, y| { + | ^^^^^^ + | + = note: defining type: outlives_region::<'_#1r, '_#2r, T>::{closure#0} with closure substs [ + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), + ] + = note: number of external vids: 4 + = note: where T: '_#3r + +note: no external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:72:1 + | +LL | / fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +LL | | where +LL | | T: 'b, +LL | | 'b: 'a, +... | +LL | | }) +LL | | } + | |_^ + | + = note: defining type: outlives_region::<'_#1r, '_#2r, T> + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs new file mode 100644 index 000000000..98239f416 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs @@ -0,0 +1,27 @@ +// Test that we assume that universal types like `T` outlive the +// function body. + +#![allow(warnings)] + +use std::cell::Cell; + +// No errors here, because `'a` is local to the body. +fn region_within_body<T>(t: T) { + let some_int = 22; + let cell = Cell::new(&some_int); + outlives(cell, t) +} + +// Error here, because T: 'a is not satisfied. +fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) { + outlives(cell, t) + //~^ ERROR the parameter type `T` may not live long enough +} + +fn outlives<'a, T>(x: Cell<&'a usize>, y: T) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr new file mode 100644 index 000000000..5fb69255d --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr @@ -0,0 +1,14 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-fn-body.rs:17:5 + | +LL | outlives(cell, t) + | ^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn region_static<'a, T: 'a>(cell: Cell<&'a usize>, t: T) { + | ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.rs b/src/test/ui/nll/ty-outlives/ty-param-fn.rs new file mode 100644 index 000000000..4393a3b41 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.rs @@ -0,0 +1,36 @@ +#![allow(warnings)] + +use std::fmt::Debug; + +fn no_region<'a, T>(x: Box<T>) -> Box<Debug + 'a> +where + T: Debug, +{ + x + //~^ ERROR the parameter type `T` may not live long enough +} + +fn correct_region<'a, T>(x: Box<T>) -> Box<Debug + 'a> +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a> +where + T: 'b + Debug, +{ + x + //~^ ERROR the parameter type `T` may not live long enough +} + +fn outlives_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a> +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr new file mode 100644 index 000000000..825b26d2f --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr @@ -0,0 +1,25 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-fn.rs:9:5 + | +LL | x + | ^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: Debug + 'a, + | ++++ + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-fn.rs:24:5 + | +LL | x + | ^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | T: 'b + Debug + 'a, + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.rs b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.rs new file mode 100644 index 000000000..9042844e8 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.rs @@ -0,0 +1,28 @@ +// compile-flags:-Zverbose +// check-pass + +// Test that we assume that universal types like `T` outlive the +// function body. + +use std::cell::Cell; + +fn twice<F, T>(value: T, mut f: F) +where + F: FnMut(Cell<&T>), +{ + f(Cell::new(&value)); + f(Cell::new(&value)); +} + +fn generic<T>(value: T) { + // No error here: + twice(value, |r| invoke(r)); +} + +fn invoke<'a, T>(x: Cell<&'a T>) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/wf-unreachable.rs b/src/test/ui/nll/ty-outlives/wf-unreachable.rs new file mode 100644 index 000000000..c6f4c4afa --- /dev/null +++ b/src/test/ui/nll/ty-outlives/wf-unreachable.rs @@ -0,0 +1,52 @@ +// Test that we check that user type annotations are well-formed, even in dead +// code. + +fn uninit<'a>() { + return; + let x: &'static &'a (); //~ ERROR lifetime may not live long enough +} + +fn var_type<'a>() { + return; + let x: &'static &'a () = &&(); //~ ERROR lifetime may not live long enough +} + +fn uninit_infer<'a>() { + let x: &'static &'a _; //~ ERROR lifetime may not live long enough + x = && (); +} + +fn infer<'a>() { + return; + let x: &'static &'a _ = &&(); //~ ERROR lifetime may not live long enough +} + +fn uninit_no_var<'a>() { + return; + let _: &'static &'a (); //~ ERROR lifetime may not live long enough +} + +fn no_var<'a>() { + return; + let _: &'static &'a () = &&(); //~ ERROR lifetime may not live long enough +} + +fn infer_no_var<'a>() { + return; + let _: &'static &'a _ = &&(); //~ ERROR lifetime may not live long enough +} + +trait X<'a, 'b> {} + +struct C<'a, 'b, T: X<'a, 'b>>(T, &'a (), &'b ()); + +impl X<'_, '_> for i32 {} +impl<'a> X<'a, 'a> for () {} + +// This type annotation is not well-formed because we substitute `()` for `_`. +fn required_substs<'a>() { + return; + let _: C<'static, 'a, _> = C((), &(), &()); //~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/wf-unreachable.stderr b/src/test/ui/nll/ty-outlives/wf-unreachable.stderr new file mode 100644 index 000000000..a62157f44 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/wf-unreachable.stderr @@ -0,0 +1,73 @@ +error: lifetime may not live long enough + --> $DIR/wf-unreachable.rs:6:12 + | +LL | fn uninit<'a>() { + | -- lifetime `'a` defined here +LL | return; +LL | let x: &'static &'a (); + | ^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/wf-unreachable.rs:11:12 + | +LL | fn var_type<'a>() { + | -- lifetime `'a` defined here +LL | return; +LL | let x: &'static &'a () = &&(); + | ^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/wf-unreachable.rs:15:12 + | +LL | fn uninit_infer<'a>() { + | -- lifetime `'a` defined here +LL | let x: &'static &'a _; + | ^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/wf-unreachable.rs:21:12 + | +LL | fn infer<'a>() { + | -- lifetime `'a` defined here +LL | return; +LL | let x: &'static &'a _ = &&(); + | ^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/wf-unreachable.rs:26:12 + | +LL | fn uninit_no_var<'a>() { + | -- lifetime `'a` defined here +LL | return; +LL | let _: &'static &'a (); + | ^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/wf-unreachable.rs:31:12 + | +LL | fn no_var<'a>() { + | -- lifetime `'a` defined here +LL | return; +LL | let _: &'static &'a () = &&(); + | ^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/wf-unreachable.rs:36:12 + | +LL | fn infer_no_var<'a>() { + | -- lifetime `'a` defined here +LL | return; +LL | let _: &'static &'a _ = &&(); + | ^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/wf-unreachable.rs:49:12 + | +LL | fn required_substs<'a>() { + | -- lifetime `'a` defined here +LL | return; +LL | let _: C<'static, 'a, _> = C((), &(), &()); + | ^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/nll/type-alias-free-regions.rs b/src/test/ui/nll/type-alias-free-regions.rs new file mode 100644 index 000000000..fd5566f35 --- /dev/null +++ b/src/test/ui/nll/type-alias-free-regions.rs @@ -0,0 +1,31 @@ +// Test that we don't assume that type aliases have the same type parameters +// as the type they alias and then panic when we see this. + +type A<'a> = &'a isize; +type B<'a> = Box<A<'a>>; + +struct C<'a> { + f: Box<B<'a>> +} + +trait FromBox<'a> { + fn from_box(b: Box<B>) -> Self; +} + +impl<'a> FromBox<'a> for C<'a> { + fn from_box(b: Box<B>) -> Self { + C { f: b } //~ ERROR + } +} + +trait FromTuple<'a> { + fn from_tuple( b: (B,)) -> Self; +} + +impl<'a> FromTuple<'a> for C<'a> { + fn from_tuple(b: (B,)) -> Self { + C { f: Box::new(b.0) } //~ ERROR + } +} + +fn main() {} diff --git a/src/test/ui/nll/type-alias-free-regions.stderr b/src/test/ui/nll/type-alias-free-regions.stderr new file mode 100644 index 000000000..45fd5a2f1 --- /dev/null +++ b/src/test/ui/nll/type-alias-free-regions.stderr @@ -0,0 +1,22 @@ +error: lifetime may not live long enough + --> $DIR/type-alias-free-regions.rs:17:9 + | +LL | impl<'a> FromBox<'a> for C<'a> { + | -- lifetime `'a` defined here +LL | fn from_box(b: Box<B>) -> Self { + | - has type `Box<Box<&'1 isize>>` +LL | C { f: b } + | ^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/type-alias-free-regions.rs:27:9 + | +LL | impl<'a> FromTuple<'a> for C<'a> { + | -- lifetime `'a` defined here +LL | fn from_tuple(b: (B,)) -> Self { + | - has type `(Box<&'1 isize>,)` +LL | C { f: Box::new(b.0) } + | ^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/type-check-pointer-coercions.rs b/src/test/ui/nll/type-check-pointer-coercions.rs new file mode 100644 index 000000000..66da57248 --- /dev/null +++ b/src/test/ui/nll/type-check-pointer-coercions.rs @@ -0,0 +1,37 @@ +fn shared_to_const<'a, 'b>(x: &&'a i32) -> *const &'b i32 { + x //~ ERROR +} + +fn unique_to_const<'a, 'b>(x: &mut &'a i32) -> *const &'b i32 { + x //~ ERROR +} + +fn unique_to_mut<'a, 'b>(x: &mut &'a i32) -> *mut &'b i32 { + // Two errors because *mut is invariant + x //~ ERROR + //~| ERROR +} + +fn mut_to_const<'a, 'b>(x: *mut &'a i32) -> *const &'b i32 { + x //~ ERROR +} + +fn array_elem<'a, 'b>(x: &'a i32) -> *const &'b i32 { + let z = &[x; 3]; + let y = z as *const &i32; + y //~ ERROR +} + +fn array_coerce<'a, 'b>(x: &'a i32) -> *const [&'b i32; 3] { + let z = &[x; 3]; + let y = z as *const [&i32; 3]; + y //~ ERROR +} + +fn nested_array<'a, 'b>(x: &'a i32) -> *const [&'b i32; 2] { + let z = &[[x; 2]; 3]; + let y = z as *const [&i32; 2]; + y //~ ERROR +} + +fn main() {} diff --git a/src/test/ui/nll/type-check-pointer-coercions.stderr b/src/test/ui/nll/type-check-pointer-coercions.stderr new file mode 100644 index 000000000..ef2d92878 --- /dev/null +++ b/src/test/ui/nll/type-check-pointer-coercions.stderr @@ -0,0 +1,111 @@ +error: lifetime may not live long enough + --> $DIR/type-check-pointer-coercions.rs:2:5 + | +LL | fn shared_to_const<'a, 'b>(x: &&'a i32) -> *const &'b i32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x + | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-coercions.rs:6:5 + | +LL | fn unique_to_const<'a, 'b>(x: &mut &'a i32) -> *const &'b i32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x + | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-coercions.rs:11:5 + | +LL | fn unique_to_mut<'a, 'b>(x: &mut &'a i32) -> *mut &'b i32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | // Two errors because *mut is invariant +LL | x + | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `&i32` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-coercions.rs:11:5 + | +LL | fn unique_to_mut<'a, 'b>(x: &mut &'a i32) -> *mut &'b i32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | // Two errors because *mut is invariant +LL | x + | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `&i32` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +help: `'b` and `'a` must be the same: replace one with the other + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-coercions.rs:16:5 + | +LL | fn mut_to_const<'a, 'b>(x: *mut &'a i32) -> *const &'b i32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x + | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-coercions.rs:22:5 + | +LL | fn array_elem<'a, 'b>(x: &'a i32) -> *const &'b i32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | y + | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-coercions.rs:28:5 + | +LL | fn array_coerce<'a, 'b>(x: &'a i32) -> *const [&'b i32; 3] { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | y + | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-coercions.rs:34:5 + | +LL | fn nested_array<'a, 'b>(x: &'a i32) -> *const [&'b i32; 2] { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | y + | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/nll/type-check-pointer-comparisons.rs b/src/test/ui/nll/type-check-pointer-comparisons.rs new file mode 100644 index 000000000..7b0ffeaef --- /dev/null +++ b/src/test/ui/nll/type-check-pointer-comparisons.rs @@ -0,0 +1,31 @@ +// Check that we assert that pointers have a common subtype for comparisons + +fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { + x == y; + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough +} + +fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { + x == y; + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough +} + +fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) { + f == g; + //~^ ERROR lifetime may not live long enough + //~| ERROR lifetime may not live long enough +} + +fn compare_hr_fn_ptr<'a>(f: fn(&'a i32), g: fn(&i32)) { + // Ideally this should compile with the operands swapped as well, but HIR + // type checking prevents it (and stops compilation) for now. + f == g; // OK +} + +fn compare_const_fn_ptr<'a>(f: *const fn(&'a i32), g: *const fn(&i32)) { + f == g; // OK +} + +fn main() {} diff --git a/src/test/ui/nll/type-check-pointer-comparisons.stderr b/src/test/ui/nll/type-check-pointer-comparisons.stderr new file mode 100644 index 000000000..0d8480a42 --- /dev/null +++ b/src/test/ui/nll/type-check-pointer-comparisons.stderr @@ -0,0 +1,98 @@ +error: lifetime may not live long enough + --> $DIR/type-check-pointer-comparisons.rs:4:5 + | +LL | fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x == y; + | ^ requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable reference to `&i32` + = note: mutable references are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-comparisons.rs:4:10 + | +LL | fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x == y; + | ^ requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable reference to `&i32` + = note: mutable references are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +help: `'a` and `'b` must be the same: replace one with the other + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-comparisons.rs:10:5 + | +LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x == y; + | ^ requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `&i32` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-comparisons.rs:10:10 + | +LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x == y; + | ^ requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `&i32` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +help: `'a` and `'b` must be the same: replace one with the other + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-comparisons.rs:16:5 + | +LL | fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | f == g; + | ^ requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable reference to `&i32` + = note: mutable references are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: lifetime may not live long enough + --> $DIR/type-check-pointer-comparisons.rs:16:10 + | +LL | fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | f == g; + | ^ requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable reference to `&i32` + = note: mutable references are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +help: `'a` and `'b` must be the same: replace one with the other + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/nll/type-test-universe.rs b/src/test/ui/nll/type-test-universe.rs new file mode 100644 index 000000000..f9801c07d --- /dev/null +++ b/src/test/ui/nll/type-test-universe.rs @@ -0,0 +1,21 @@ +// Regression test for #98095: make sure that +// we detect that S needs to outlive 'static. + +fn outlives_forall<T>() +where + for<'u> T: 'u, +{ +} + +fn test1<S>() { + outlives_forall::<S>(); + //~^ ERROR `S` does not live long enough +} + +struct Value<'a>(&'a ()); +fn test2<'a>() { + outlives_forall::<Value<'a>>(); + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/type-test-universe.stderr b/src/test/ui/nll/type-test-universe.stderr new file mode 100644 index 000000000..242486c36 --- /dev/null +++ b/src/test/ui/nll/type-test-universe.stderr @@ -0,0 +1,16 @@ +error: `S` does not live long enough + --> $DIR/type-test-universe.rs:11:5 + | +LL | outlives_forall::<S>(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/type-test-universe.rs:17:5 + | +LL | fn test2<'a>() { + | -- lifetime `'a` defined here +LL | outlives_forall::<Value<'a>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/unused-mut-issue-50343.fixed b/src/test/ui/nll/unused-mut-issue-50343.fixed new file mode 100644 index 000000000..5632de1cd --- /dev/null +++ b/src/test/ui/nll/unused-mut-issue-50343.fixed @@ -0,0 +1,9 @@ +// run-rustfix + +#![deny(unused_mut)] +#![allow(unused_variables)] // for rustfix + +fn main() { + vec![(42, 22)].iter().map(|(x, _y)| ()).count(); + //~^ ERROR: variable does not need to be mutable +} diff --git a/src/test/ui/nll/unused-mut-issue-50343.rs b/src/test/ui/nll/unused-mut-issue-50343.rs new file mode 100644 index 000000000..c849ac8c7 --- /dev/null +++ b/src/test/ui/nll/unused-mut-issue-50343.rs @@ -0,0 +1,9 @@ +// run-rustfix + +#![deny(unused_mut)] +#![allow(unused_variables)] // for rustfix + +fn main() { + vec![(42, 22)].iter().map(|(mut x, _y)| ()).count(); + //~^ ERROR: variable does not need to be mutable +} diff --git a/src/test/ui/nll/unused-mut-issue-50343.stderr b/src/test/ui/nll/unused-mut-issue-50343.stderr new file mode 100644 index 000000000..cb02d7620 --- /dev/null +++ b/src/test/ui/nll/unused-mut-issue-50343.stderr @@ -0,0 +1,16 @@ +error: variable does not need to be mutable + --> $DIR/unused-mut-issue-50343.rs:7:33 + | +LL | vec![(42, 22)].iter().map(|(mut x, _y)| ()).count(); + | ----^ + | | + | help: remove this `mut` + | +note: the lint level is defined here + --> $DIR/unused-mut-issue-50343.rs:3:9 + | +LL | #![deny(unused_mut)] + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/user-annotations/adt-brace-enums.rs b/src/test/ui/nll/user-annotations/adt-brace-enums.rs new file mode 100644 index 000000000..0d9828342 --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-brace-enums.rs @@ -0,0 +1,50 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +enum SomeEnum<T> { + SomeVariant { t: T } +} + +fn no_annot() { + let c = 66; + SomeEnum::SomeVariant { t: &c }; +} + +fn annot_underscore() { + let c = 66; + SomeEnum::SomeVariant::<_> { t: &c }; +} + +fn annot_reference_any_lifetime() { + let c = 66; + SomeEnum::SomeVariant::<&u32> { t: &c }; +} + +fn annot_reference_static_lifetime() { + let c = 66; + SomeEnum::SomeVariant::<&'static u32> { t: &c }; //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let c = 66; + SomeEnum::SomeVariant::<&'a u32> { t: &c }; //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + SomeEnum::SomeVariant::<&'a u32> { t: c }; +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let _closure = || { + let c = 66; + SomeEnum::SomeVariant::<&'a u32> { t: &c }; //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let _closure = || { + SomeEnum::SomeVariant::<&'a u32> { t: c }; + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/adt-brace-enums.stderr b/src/test/ui/nll/user-annotations/adt-brace-enums.stderr new file mode 100644 index 000000000..253e38251 --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-brace-enums.stderr @@ -0,0 +1,42 @@ +error[E0597]: `c` does not live long enough + --> $DIR/adt-brace-enums.rs:25:48 + | +LL | SomeEnum::SomeVariant::<&'static u32> { t: &c }; + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-brace-enums.rs:30:43 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +LL | let c = 66; +LL | SomeEnum::SomeVariant::<&'a u32> { t: &c }; + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-brace-enums.rs:40:47 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +... +LL | SomeEnum::SomeVariant::<&'a u32> { t: &c }; + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'a` +LL | }; + | - `c` dropped here while still borrowed + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/adt-brace-structs.rs b/src/test/ui/nll/user-annotations/adt-brace-structs.rs new file mode 100644 index 000000000..bdbfd87d5 --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-brace-structs.rs @@ -0,0 +1,48 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +struct SomeStruct<T> { t: T } + +fn no_annot() { + let c = 66; + SomeStruct { t: &c }; +} + +fn annot_underscore() { + let c = 66; + SomeStruct::<_> { t: &c }; +} + +fn annot_reference_any_lifetime() { + let c = 66; + SomeStruct::<&u32> { t: &c }; +} + +fn annot_reference_static_lifetime() { + let c = 66; + SomeStruct::<&'static u32> { t: &c }; //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let c = 66; + SomeStruct::<&'a u32> { t: &c }; //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + SomeStruct::<&'a u32> { t: c }; +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let _closure = || { + let c = 66; + SomeStruct::<&'a u32> { t: &c }; //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let _closure = || { + SomeStruct::<&'a u32> { t: c }; + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/adt-brace-structs.stderr b/src/test/ui/nll/user-annotations/adt-brace-structs.stderr new file mode 100644 index 000000000..8b9d1705d --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-brace-structs.stderr @@ -0,0 +1,42 @@ +error[E0597]: `c` does not live long enough + --> $DIR/adt-brace-structs.rs:23:37 + | +LL | SomeStruct::<&'static u32> { t: &c }; + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-brace-structs.rs:28:32 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +LL | let c = 66; +LL | SomeStruct::<&'a u32> { t: &c }; + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-brace-structs.rs:38:36 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +... +LL | SomeStruct::<&'a u32> { t: &c }; + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'a` +LL | }; + | - `c` dropped here while still borrowed + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/adt-nullary-enums.rs b/src/test/ui/nll/user-annotations/adt-nullary-enums.rs new file mode 100644 index 000000000..53853668d --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-nullary-enums.rs @@ -0,0 +1,69 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +#![allow(warnings)] + +use std::cell::Cell; + +enum SomeEnum<T> { + SomeVariant(T), + SomeOtherVariant, +} + +fn combine<T>(_: T, _: T) { } + +fn no_annot() { + let c = 66; + combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant); +} + +fn annot_underscore() { + let c = 66; + combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<_>>); +} + +fn annot_reference_any_lifetime() { + let c = 66; + combine(SomeEnum::SomeVariant(Cell::new(&c)), SomeEnum::SomeOtherVariant::<Cell<&u32>>); +} + +fn annot_reference_static_lifetime() { + let c = 66; + combine( + SomeEnum::SomeVariant(Cell::new(&c)), //~ ERROR + SomeEnum::SomeOtherVariant::<Cell<&'static u32>>, + ); +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let c = 66; + combine( + SomeEnum::SomeVariant(Cell::new(&c)), //~ ERROR + SomeEnum::SomeOtherVariant::<Cell<&'a u32>>, + ); +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + combine(SomeEnum::SomeVariant(Cell::new(c)), SomeEnum::SomeOtherVariant::<Cell<&'a u32>>); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let _closure = || { + let c = 66; + combine( + SomeEnum::SomeVariant(Cell::new(&c)), //~ ERROR + SomeEnum::SomeOtherVariant::<Cell<&'a u32>>, + ); + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let _closure = || { + combine( + SomeEnum::SomeVariant(Cell::new(c)), + SomeEnum::SomeOtherVariant::<Cell<&'a u32>>, + ); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/adt-nullary-enums.stderr b/src/test/ui/nll/user-annotations/adt-nullary-enums.stderr new file mode 100644 index 000000000..ee332278c --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-nullary-enums.stderr @@ -0,0 +1,44 @@ +error[E0597]: `c` does not live long enough + --> $DIR/adt-nullary-enums.rs:33:41 + | +LL | / combine( +LL | | SomeEnum::SomeVariant(Cell::new(&c)), + | | ^^ borrowed value does not live long enough +LL | | SomeEnum::SomeOtherVariant::<Cell<&'static u32>>, +LL | | ); + | |_____- argument requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-nullary-enums.rs:41:41 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +... +LL | SomeEnum::SomeVariant(Cell::new(&c)), + | ----------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +... +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-nullary-enums.rs:54:45 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +LL | let _closure = || { + | - `c` dropped here while still borrowed +... +LL | SomeEnum::SomeVariant(Cell::new(&c)), + | ----------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/adt-tuple-enums.rs b/src/test/ui/nll/user-annotations/adt-tuple-enums.rs new file mode 100644 index 000000000..efe8dfda1 --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-tuple-enums.rs @@ -0,0 +1,53 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +#![allow(warnings)] + +enum SomeEnum<T> { + SomeVariant(T), + SomeOtherVariant, +} + +fn no_annot() { + let c = 66; + SomeEnum::SomeVariant(&c); +} + +fn annot_underscore() { + let c = 66; + SomeEnum::SomeVariant::<_>(&c); +} + +fn annot_reference_any_lifetime() { + let c = 66; + SomeEnum::SomeVariant::<&u32>(&c); +} + +fn annot_reference_static_lifetime() { + let c = 66; + SomeEnum::SomeVariant::<&'static u32>(&c); //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let c = 66; + SomeEnum::SomeVariant::<&'a u32>(&c); //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + SomeEnum::SomeVariant::<&'a u32>(c); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let _closure = || { + let c = 66; + SomeEnum::SomeVariant::<&'a u32>(&c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let _closure = || { + SomeEnum::SomeVariant::<&'a u32>(c); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/adt-tuple-enums.stderr b/src/test/ui/nll/user-annotations/adt-tuple-enums.stderr new file mode 100644 index 000000000..2fa704263 --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-tuple-enums.stderr @@ -0,0 +1,42 @@ +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-enums.rs:28:43 + | +LL | SomeEnum::SomeVariant::<&'static u32>(&c); + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-enums.rs:33:38 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +LL | let c = 66; +LL | SomeEnum::SomeVariant::<&'a u32>(&c); + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-enums.rs:43:42 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +... +LL | SomeEnum::SomeVariant::<&'a u32>(&c); + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'a` +LL | }; + | - `c` dropped here while still borrowed + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.rs b/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.rs new file mode 100644 index 000000000..116583223 --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.rs @@ -0,0 +1,71 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +struct SomeStruct<T>(T); + +fn no_annot() { + let c = 66; + let f = SomeStruct; + f(&c); +} + +fn annot_underscore() { + let c = 66; + let f = SomeStruct::<_>; + f(&c); +} + +fn annot_reference_any_lifetime() { + let c = 66; + let f = SomeStruct::<&u32>; + f(&c); +} + +fn annot_reference_static_lifetime() { + let c = 66; + let f = SomeStruct::<&'static u32>; + f(&c); //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let c = 66; + let f = SomeStruct::<&'a u32>; + f(&c); //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + let f = SomeStruct::<&'a u32>; + f(c); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let _closure = || { + let c = 66; + let f = SomeStruct::<&'a u32>; + f(&c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_across_closure<'a>(_: &'a u32) { + let f = SomeStruct::<&'a u32>; + let _closure = || { + let c = 66; + f(&c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let _closure = || { + let f = SomeStruct::<&'a u32>; + f(c); + }; +} + +fn annot_reference_named_lifetime_across_closure_ok<'a>(c: &'a u32) { + let f = SomeStruct::<&'a u32>; + let _closure = || { + f(c); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.stderr b/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.stderr new file mode 100644 index 000000000..95bbd62c4 --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-tuple-struct-calls.stderr @@ -0,0 +1,56 @@ +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct-calls.rs:27:7 + | +LL | f(&c); + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct-calls.rs:33:7 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +... +LL | f(&c); + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct-calls.rs:45:11 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +LL | let _closure = || { + | - `c` dropped here while still borrowed +... +LL | f(&c); + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct-calls.rs:53:11 + | +LL | let f = SomeStruct::<&'a u32>; + | - lifetime `'1` appears in the type of `f` +LL | let _closure = || { + | - `c` dropped here while still borrowed +LL | let c = 66; +LL | f(&c); + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'1` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/adt-tuple-struct.rs b/src/test/ui/nll/user-annotations/adt-tuple-struct.rs new file mode 100644 index 000000000..37284e1fd --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-tuple-struct.rs @@ -0,0 +1,48 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +struct SomeStruct<T>(T); + +fn no_annot() { + let c = 66; + SomeStruct(&c); +} + +fn annot_underscore() { + let c = 66; + SomeStruct::<_>(&c); +} + +fn annot_reference_any_lifetime() { + let c = 66; + SomeStruct::<&u32>(&c); +} + +fn annot_reference_static_lifetime() { + let c = 66; + SomeStruct::<&'static u32>(&c); //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let c = 66; + SomeStruct::<&'a u32>(&c); //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + SomeStruct::<&'a u32>(c); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let _closure = || { + let c = 66; + SomeStruct::<&'a u32>(&c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let _closure = || { + SomeStruct::<&'a u32>(c); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/adt-tuple-struct.stderr b/src/test/ui/nll/user-annotations/adt-tuple-struct.stderr new file mode 100644 index 000000000..76b525225 --- /dev/null +++ b/src/test/ui/nll/user-annotations/adt-tuple-struct.stderr @@ -0,0 +1,42 @@ +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct.rs:23:32 + | +LL | SomeStruct::<&'static u32>(&c); + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct.rs:28:27 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +LL | let c = 66; +LL | SomeStruct::<&'a u32>(&c); + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/adt-tuple-struct.rs:38:31 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +... +LL | SomeStruct::<&'a u32>(&c); + | ^^ + | | + | borrowed value does not live long enough + | this usage requires that `c` is borrowed for `'a` +LL | }; + | - `c` dropped here while still borrowed + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/cast_static_lifetime.rs b/src/test/ui/nll/user-annotations/cast_static_lifetime.rs new file mode 100644 index 000000000..bb6129dac --- /dev/null +++ b/src/test/ui/nll/user-annotations/cast_static_lifetime.rs @@ -0,0 +1,6 @@ +#![allow(warnings)] + +fn main() { + let x = 22_u32; + let y: &u32 = (&x) as &'static u32; //~ ERROR `x` does not live long enough +} diff --git a/src/test/ui/nll/user-annotations/cast_static_lifetime.stderr b/src/test/ui/nll/user-annotations/cast_static_lifetime.stderr new file mode 100644 index 000000000..4599d04e7 --- /dev/null +++ b/src/test/ui/nll/user-annotations/cast_static_lifetime.stderr @@ -0,0 +1,14 @@ +error[E0597]: `x` does not live long enough + --> $DIR/cast_static_lifetime.rs:5:19 + | +LL | let y: &u32 = (&x) as &'static u32; + | ^^^^---------------- + | | + | borrowed value does not live long enough + | type annotation requires that `x` is borrowed for `'static` +LL | } + | - `x` 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/nll/user-annotations/closure-substs.polonius.stderr b/src/test/ui/nll/user-annotations/closure-substs.polonius.stderr new file mode 100644 index 000000000..af159a6cd --- /dev/null +++ b/src/test/ui/nll/user-annotations/closure-substs.polonius.stderr @@ -0,0 +1,61 @@ +error: lifetime may not live long enough + --> $DIR/closure-substs.rs:8:16 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +... +LL | return x; + | ^ returning this value requires that `'a` must outlive `'static` + | + = help: consider replacing `'a` with `'static` + +error: lifetime may not live long enough + --> $DIR/closure-substs.rs:15:16 + | +LL | |x: &i32| -> &'static i32 { + | - let's call the lifetime of this reference `'1` +LL | return x; + | ^ returning this value requires that `'1` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/closure-substs.rs:15:16 + | +LL | |x: &i32| -> &'static i32 { + | - - let's call the lifetime of this reference `'2` + | | + | let's call the lifetime of this reference `'1` +LL | return x; + | ^ returning this value requires that `'1` must outlive `'2` + +error: lifetime may not live long enough + --> $DIR/closure-substs.rs:22:9 + | +LL | fn bar<'a>() { + | -- lifetime `'a` defined here +... +LL | b(x); + | ^^^^ argument requires that `'a` must outlive `'static` + | + = help: consider replacing `'a` with `'static` + +error[E0521]: borrowed data escapes outside of closure + --> $DIR/closure-substs.rs:29:9 + | +LL | |x: &i32, b: fn(&'static i32)| { + | - `x` is a reference that is only valid in the closure body +LL | b(x); + | ^^^^ `x` escapes the closure body here + +error[E0521]: borrowed data escapes outside of closure + --> $DIR/closure-substs.rs:29:9 + | +LL | |x: &i32, b: fn(&'static i32)| { + | - - `b` declared here, outside of the closure body + | | + | `x` is a reference that is only valid in the closure body +LL | b(x); + | ^^^^ `x` escapes the closure body here + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0521`. diff --git a/src/test/ui/nll/user-annotations/closure-substs.rs b/src/test/ui/nll/user-annotations/closure-substs.rs new file mode 100644 index 000000000..f7af54e8d --- /dev/null +++ b/src/test/ui/nll/user-annotations/closure-substs.rs @@ -0,0 +1,31 @@ +// Test that we enforce user-provided type annotations on closures. + +fn foo<'a>() { + // Here `x` is free in the closure sig: + |x: &'a i32| -> &'static i32 { + return x; //~ ERROR lifetime may not live long enough + }; +} + +fn foo1() { + // Here `x` is bound in the closure sig: + |x: &i32| -> &'static i32 { + return x; //~ ERROR lifetime may not live long enough + }; +} + +fn bar<'a>() { + // Here `x` is free in the closure sig: + |x: &'a i32, b: fn(&'static i32)| { + b(x); //~ ERROR lifetime may not live long enough + }; +} + +fn bar1() { + // Here `x` is bound in the closure sig: + |x: &i32, b: fn(&'static i32)| { + b(x); //~ ERROR borrowed data escapes outside of closure + }; +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/closure-substs.stderr b/src/test/ui/nll/user-annotations/closure-substs.stderr new file mode 100644 index 000000000..1e8de4ba9 --- /dev/null +++ b/src/test/ui/nll/user-annotations/closure-substs.stderr @@ -0,0 +1,42 @@ +error: lifetime may not live long enough + --> $DIR/closure-substs.rs:6:16 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +... +LL | return x; + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/closure-substs.rs:13:16 + | +LL | |x: &i32| -> &'static i32 { + | - let's call the lifetime of this reference `'1` +LL | return x; + | ^ returning this value requires that `'1` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/closure-substs.rs:20:9 + | +LL | fn bar<'a>() { + | -- lifetime `'a` defined here +... +LL | b(x); + | ^^^^ argument requires that `'a` must outlive `'static` + +error[E0521]: borrowed data escapes outside of closure + --> $DIR/closure-substs.rs:27:9 + | +LL | |x: &i32, b: fn(&'static i32)| { + | - - let's call the lifetime of this reference `'1` + | | + | `x` is a reference that is only valid in the closure body +LL | b(x); + | ^^^^ + | | + | `x` escapes the closure body here + | argument requires that `'1` must outlive `'static` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0521`. diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.rs b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.rs new file mode 100644 index 000000000..e3a8a5f58 --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.rs @@ -0,0 +1,12 @@ +struct Foo<'a> { x: &'a u32 } + +impl<'a> Foo<'a> { + const C: &'a u32 = &22; +} + +fn foo<'a>(_: &'a u32) -> &'static u32 { + <Foo<'a>>::C //~ ERROR +} + +fn main() { +} diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr new file mode 100644 index 000000000..c39301588 --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/constant-in-expr-inherent-1.rs:8:5 + | +LL | fn foo<'a>(_: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | <Foo<'a>>::C + | ^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-inherent-2.rs b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-2.rs new file mode 100644 index 000000000..90696d4b1 --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-2.rs @@ -0,0 +1,27 @@ +// Test that we still check constants are well-formed, even when we there's no +// type annotation to check. + +const FUN: fn(&'static ()) = |_| {}; +struct A; +impl A { + const ASSOCIATED_FUN: fn(&'static ()) = |_| {}; +} + +struct B<'a>(&'a ()); +impl B<'static> { + const ALSO_ASSOCIATED_FUN: fn(&'static ()) = |_| {}; +} + +trait Z: 'static { + const TRAIT_ASSOCIATED_FUN: fn(&'static Self) = |_| (); +} + +impl Z for () {} + +fn main() { + let x = (); + FUN(&x); //~ ERROR `x` does not live long enough + A::ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough + B::ALSO_ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough + <_>::TRAIT_ASSOCIATED_FUN(&x); //~ ERROR `x` does not live long enough +} diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-inherent-2.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-2.stderr new file mode 100644 index 000000000..12065a85a --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-2.stderr @@ -0,0 +1,50 @@ +error[E0597]: `x` does not live long enough + --> $DIR/constant-in-expr-inherent-2.rs:23:9 + | +LL | FUN(&x); + | ----^^- + | | | + | | borrowed value does not live long enough + | argument requires that `x` is borrowed for `'static` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/constant-in-expr-inherent-2.rs:24:23 + | +LL | A::ASSOCIATED_FUN(&x); + | ------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `x` is borrowed for `'static` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/constant-in-expr-inherent-2.rs:25:28 + | +LL | B::ALSO_ASSOCIATED_FUN(&x); + | -----------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `x` is borrowed for `'static` +LL | <_>::TRAIT_ASSOCIATED_FUN(&x); +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/constant-in-expr-inherent-2.rs:26:31 + | +LL | <_>::TRAIT_ASSOCIATED_FUN(&x); + | --------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `x` is borrowed for `'static` +LL | } + | - `x` dropped here while still borrowed + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-normalize.rs b/src/test/ui/nll/user-annotations/constant-in-expr-normalize.rs new file mode 100644 index 000000000..b7095430d --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-normalize.rs @@ -0,0 +1,22 @@ +trait Mirror { + type Me; +} + +impl<T> Mirror for T { + type Me = T; +} + +trait Foo<'a> { + const C: <&'a u32 as Mirror>::Me; +} + +impl<'a, T> Foo<'a> for T { + const C: &'a u32 = &22; +} + +fn foo<'a>(_: &'a u32) -> &'static u32 { + <() as Foo<'a>>::C //~ ERROR +} + +fn main() { +} diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-normalize.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-normalize.stderr new file mode 100644 index 000000000..541a2cfaf --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-normalize.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/constant-in-expr-normalize.rs:18:5 + | +LL | fn foo<'a>(_: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | <() as Foo<'a>>::C + | ^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-1.rs b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-1.rs new file mode 100644 index 000000000..e0400b2cc --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-1.rs @@ -0,0 +1,14 @@ +trait Foo<'a> { + const C: &'a u32; +} + +impl<'a, T> Foo<'a> for T { + const C: &'a u32 = &22; +} + +fn foo<'a>(_: &'a u32) -> &'static u32 { + <() as Foo<'a>>::C //~ ERROR +} + +fn main() { +} diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-1.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-1.stderr new file mode 100644 index 000000000..ea0fcb6d6 --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-1.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/constant-in-expr-trait-item-1.rs:10:5 + | +LL | fn foo<'a>(_: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | <() as Foo<'a>>::C + | ^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-2.rs b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-2.rs new file mode 100644 index 000000000..73c4e577b --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-2.rs @@ -0,0 +1,14 @@ +trait Foo<'a> { + const C: &'a u32; +} + +impl<'a, T> Foo<'a> for T { + const C: &'a u32 = &22; +} + +fn foo<'a, T: Foo<'a>>() -> &'static u32 { + <T as Foo<'a>>::C //~ ERROR +} + +fn main() { +} diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-2.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-2.stderr new file mode 100644 index 000000000..ff549f1d8 --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-2.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/constant-in-expr-trait-item-2.rs:10:5 + | +LL | fn foo<'a, T: Foo<'a>>() -> &'static u32 { + | -- lifetime `'a` defined here +LL | <T as Foo<'a>>::C + | ^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.rs b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.rs new file mode 100644 index 000000000..567e31ef9 --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.rs @@ -0,0 +1,14 @@ +trait Foo<'a> { + const C: &'a u32; +} + +impl<'a, T> Foo<'a> for T { + const C: &'a u32 = &22; +} + +fn foo<'a, T: Foo<'a>>() -> &'static u32 { + T::C //~ ERROR +} + +fn main() { +} diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr new file mode 100644 index 000000000..7f160d8e3 --- /dev/null +++ b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/constant-in-expr-trait-item-3.rs:10:5 + | +LL | fn foo<'a, T: Foo<'a>>() -> &'static u32 { + | -- lifetime `'a` defined here +LL | T::C + | ^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/user-annotations/downcast-infer.rs b/src/test/ui/nll/user-annotations/downcast-infer.rs new file mode 100644 index 000000000..b27429f4d --- /dev/null +++ b/src/test/ui/nll/user-annotations/downcast-infer.rs @@ -0,0 +1,11 @@ +// check-pass + +// Check that we don't try to downcast `_` when type-checking the annotation. +fn main() { + let x = Some(Some(Some(1))); + + match x { + Some::<Option<_>>(Some(Some(v))) => (), + _ => (), + } +} diff --git a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs new file mode 100644 index 000000000..ccda9129d --- /dev/null +++ b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.rs @@ -0,0 +1,20 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +// compile-flags:-Zverbose + +#![allow(warnings)] +#![feature(rustc_attrs)] + +struct SomeStruct<T> { t: T } + +#[rustc_dump_user_substs] +fn main() { + SomeStruct { t: 22 }; // Nothing given, no annotation. + + SomeStruct::<_> { t: 22 }; // Nothing interesting given, no annotation. + + SomeStruct::<u32> { t: 22 }; // No lifetime bounds given. + + SomeStruct::<&'static u32> { t: &22 }; //~ ERROR [&ReStatic u32] +} diff --git a/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr new file mode 100644 index 000000000..586062190 --- /dev/null +++ b/src/test/ui/nll/user-annotations/dump-adt-brace-struct.stderr @@ -0,0 +1,8 @@ +error: user substs: UserSubsts { substs: [&ReStatic u32], user_self_ty: None } + --> $DIR/dump-adt-brace-struct.rs:19:5 + | +LL | SomeStruct::<&'static u32> { t: &22 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/user-annotations/dump-fn-method.rs b/src/test/ui/nll/user-annotations/dump-fn-method.rs new file mode 100644 index 000000000..148d63d84 --- /dev/null +++ b/src/test/ui/nll/user-annotations/dump-fn-method.rs @@ -0,0 +1,57 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +// compile-flags:-Zverbose + +#![feature(rustc_attrs)] + +// Note: we reference the names T and U in the comments below. +trait Bazoom<T> { + fn method<U>(&self, arg: T, arg2: U) { } +} + +impl<S, T> Bazoom<T> for S { +} + +fn foo<'a, T>(_: T) { } + +#[rustc_dump_user_substs] +fn main() { + // Here: nothing is given, so we don't have any annotation. + let x = foo; + x(22); + + // Here: `u32` is given, which doesn't contain any lifetimes, so we don't + // have any annotation. + let x = foo::<u32>; + x(22); + + let x = foo::<&'static u32>; //~ ERROR [&ReStatic u32] + x(&22); + + // Here: we only want the `T` to be given, the rest should be variables. + // + // (`T` refers to the declaration of `Bazoom`) + let x = <_ as Bazoom<u32>>::method::<_>; //~ ERROR [^0, u32, ^1] + x(&22, 44, 66); + + // Here: all are given and definitely contain no lifetimes, so we + // don't have any annotation. + let x = <u8 as Bazoom<u16>>::method::<u32>; + x(&22, 44, 66); + + // Here: all are given and we have a lifetime. + let x = <u8 as Bazoom<&'static u16>>::method::<u32>; //~ ERROR [u8, &ReStatic u16, u32] + x(&22, &44, 66); + + // Here: we want in particular that *only* the method `U` + // annotation is given, the rest are variables. + // + // (`U` refers to the declaration of `Bazoom`) + let y = 22_u32; + y.method::<u32>(44, 66); //~ ERROR [^0, ^1, u32] + + // Here: nothing is given, so we don't have any annotation. + let y = 22_u32; + y.method(44, 66); +} diff --git a/src/test/ui/nll/user-annotations/dump-fn-method.stderr b/src/test/ui/nll/user-annotations/dump-fn-method.stderr new file mode 100644 index 000000000..d139efa88 --- /dev/null +++ b/src/test/ui/nll/user-annotations/dump-fn-method.stderr @@ -0,0 +1,26 @@ +error: user substs: UserSubsts { substs: [&ReStatic u32], user_self_ty: None } + --> $DIR/dump-fn-method.rs:29:13 + | +LL | let x = foo::<&'static u32>; + | ^^^^^^^^^^^^^^^^^^^ + +error: user substs: UserSubsts { substs: [^0, u32, ^1], user_self_ty: None } + --> $DIR/dump-fn-method.rs:35:13 + | +LL | let x = <_ as Bazoom<u32>>::method::<_>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: user substs: UserSubsts { substs: [u8, &ReStatic u16, u32], user_self_ty: None } + --> $DIR/dump-fn-method.rs:44:13 + | +LL | let x = <u8 as Bazoom<&'static u16>>::method::<u32>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: user substs: UserSubsts { substs: [^0, ^1, u32], user_self_ty: None } + --> $DIR/dump-fn-method.rs:52:5 + | +LL | y.method::<u32>(44, 66); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/nll/user-annotations/fns.rs b/src/test/ui/nll/user-annotations/fns.rs new file mode 100644 index 000000000..38db6d1c4 --- /dev/null +++ b/src/test/ui/nll/user-annotations/fns.rs @@ -0,0 +1,48 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +fn some_fn<T>(arg: T) { } + +fn no_annot() { + let c = 66; + some_fn(&c); // OK +} + +fn annot_underscore() { + let c = 66; + some_fn::<_>(&c); // OK +} + +fn annot_reference_any_lifetime() { + let c = 66; + some_fn::<&u32>(&c); // OK +} + +fn annot_reference_static_lifetime() { + let c = 66; + some_fn::<&'static u32>(&c); //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let c = 66; + some_fn::<&'a u32>(&c); //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + some_fn::<&'a u32>(c); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let _closure = || { + let c = 66; + some_fn::<&'a u32>(&c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let _closure = || { + some_fn::<&'a u32>(c); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/fns.stderr b/src/test/ui/nll/user-annotations/fns.stderr new file mode 100644 index 000000000..bd4d121d5 --- /dev/null +++ b/src/test/ui/nll/user-annotations/fns.stderr @@ -0,0 +1,42 @@ +error[E0597]: `c` does not live long enough + --> $DIR/fns.rs:23:29 + | +LL | some_fn::<&'static u32>(&c); + | ------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/fns.rs:28:24 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +LL | let c = 66; +LL | some_fn::<&'a u32>(&c); + | -------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/fns.rs:38:28 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +LL | let _closure = || { + | - `c` dropped here while still borrowed +LL | let c = 66; +LL | some_fn::<&'a u32>(&c); + | -------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/inherent-associated-constants.rs b/src/test/ui/nll/user-annotations/inherent-associated-constants.rs new file mode 100644 index 000000000..fe2641fd6 --- /dev/null +++ b/src/test/ui/nll/user-annotations/inherent-associated-constants.rs @@ -0,0 +1,15 @@ +struct A<'a>(&'a ()); + +impl A<'static> { + const IC: i32 = 10; +} + +fn non_wf_associated_const<'a>(x: i32) { + A::<'a>::IC; //~ ERROR lifetime may not live long enough +} + +fn wf_associated_const<'a>(x: i32) { + A::<'static>::IC; +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/inherent-associated-constants.stderr b/src/test/ui/nll/user-annotations/inherent-associated-constants.stderr new file mode 100644 index 000000000..ffbfc40f5 --- /dev/null +++ b/src/test/ui/nll/user-annotations/inherent-associated-constants.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/inherent-associated-constants.rs:8:5 + | +LL | fn non_wf_associated_const<'a>(x: i32) { + | -- lifetime `'a` defined here +LL | A::<'a>::IC; + | ^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/user-annotations/issue-54124.rs b/src/test/ui/nll/user-annotations/issue-54124.rs new file mode 100644 index 000000000..5ae03c894 --- /dev/null +++ b/src/test/ui/nll/user-annotations/issue-54124.rs @@ -0,0 +1,8 @@ +fn test<'a>() { + let _:fn(&()) = |_:&'a ()| {}; //~ ERROR lifetime may not live long enough + //~^ ERROR lifetime may not live long enough +} + +fn main() { + test(); +} diff --git a/src/test/ui/nll/user-annotations/issue-54124.stderr b/src/test/ui/nll/user-annotations/issue-54124.stderr new file mode 100644 index 000000000..2556af2dd --- /dev/null +++ b/src/test/ui/nll/user-annotations/issue-54124.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/issue-54124.rs:2:22 + | +LL | fn test<'a>() { + | -- lifetime `'a` defined here +LL | let _:fn(&()) = |_:&'a ()| {}; + | ^ - let's call the lifetime of this reference `'1` + | | + | requires that `'1` must outlive `'a` + +error: lifetime may not live long enough + --> $DIR/issue-54124.rs:2:22 + | +LL | fn test<'a>() { + | -- lifetime `'a` defined here +LL | let _:fn(&()) = |_:&'a ()| {}; + | ^ requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/user-annotations/issue-54570-bootstrapping.rs b/src/test/ui/nll/user-annotations/issue-54570-bootstrapping.rs new file mode 100644 index 000000000..ff5b2244e --- /dev/null +++ b/src/test/ui/nll/user-annotations/issue-54570-bootstrapping.rs @@ -0,0 +1,30 @@ +// check-pass + +// This test is reduced from a scenario pnkfelix encountered while +// bootstrapping the compiler. + +#[derive(Copy, Clone)] +pub struct Spanned<T> { + pub node: T, + pub span: Span, +} + +pub type Variant = Spanned<VariantKind>; +// #[derive(Clone)] pub struct Variant { pub node: VariantKind, pub span: Span, } + +#[derive(Clone)] +pub struct VariantKind { } + +#[derive(Copy, Clone)] +pub struct Span; + +pub fn variant_to_span(variant: Variant) { + match variant { + Variant { + span: _span, + .. + } => { } + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/issue-55219.rs b/src/test/ui/nll/user-annotations/issue-55219.rs new file mode 100644 index 000000000..147413663 --- /dev/null +++ b/src/test/ui/nll/user-annotations/issue-55219.rs @@ -0,0 +1,18 @@ +// Regression test for #55219: +// +// The `Self::HASH_LEN` here expands to a "self-type" where `T` is not +// known. This unbound inference variable was causing an ICE. +// +// check-pass + +pub struct Foo<T>(T); + +impl<T> Foo<T> { + const HASH_LEN: usize = 20; + + fn stuff() { + let _ = Self::HASH_LEN; + } +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/issue-55241.rs b/src/test/ui/nll/user-annotations/issue-55241.rs new file mode 100644 index 000000000..29969c7b4 --- /dev/null +++ b/src/test/ui/nll/user-annotations/issue-55241.rs @@ -0,0 +1,26 @@ +// Regression test for #55241: +// +// The reference to `C::HASHED_NULL_NODE` resulted in a type like `<C +// as NodeCodec<_>>::Out`; normalizing this type requires knowing the +// value of `_`; solving that requires having normalized, so we can +// test against `C: NodeCodec<H>` in the environment. +// +// run-pass + +pub trait Hasher { + type Out: Eq; +} + +pub trait NodeCodec<H: Hasher> { + const HASHED_NULL_NODE: H::Out; +} + +pub trait Trie<H: Hasher, C: NodeCodec<H>> { + /// Returns the root of the trie. + fn root(&self) -> &H::Out; + + /// Is the trie empty? + fn is_empty(&self) -> bool { *self.root() == C::HASHED_NULL_NODE } +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.rs b/src/test/ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.rs new file mode 100644 index 000000000..c71937a50 --- /dev/null +++ b/src/test/ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.rs @@ -0,0 +1,68 @@ +// This test is ensuring that type ascriptions on let bindings +// constrain both: +// +// 1. the input expression on the right-hand side (after any potential +// coercion, and allowing for covariance), *and* +// +// 2. the bindings (if any) nested within the pattern on the left-hand +// side (and here, the type-constraint is *invariant*). + +#![allow(dead_code, unused_mut)] +type PairUncoupled<'a, 'b, T> = (&'a T, &'b T); +type PairCoupledRegions<'a, T> = (&'a T, &'a T); +type PairCoupledTypes<T> = (T, T); + +fn uncoupled_lhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((mut y, mut _z),): (PairUncoupled<u32>,) = ((s, &_x),); // ok + // Above compiling does *not* imply below would compile. + // ::std::mem::swap(&mut y, &mut _z); + y +} + +fn swap_regions((mut y, mut _z): PairCoupledRegions<u32>) { + ::std::mem::swap(&mut y, &mut _z); +} + +fn coupled_regions_lhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((y, _z),): (PairCoupledRegions<u32>,) = ((s, &_x),); + // If above line compiled, so should line below ... + + // swap_regions((y, _z)); + + // ... but the ascribed type also invalidates this use of `y` + y //~ ERROR lifetime may not live long enough +} + +fn swap_types((mut y, mut _z): PairCoupledTypes<&u32>) { + ::std::mem::swap(&mut y, &mut _z); +} + +fn coupled_types_lhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((y, _z),): (PairCoupledTypes<&u32>,) = ((s, &_x),); + // If above line compiled, so should line below ... + + // swap_types((y, _z)); + + // ... but the ascribed type also invalidates this use of `y` + y //~ ERROR lifetime may not live long enough +} + +fn swap_wilds((mut y, mut _z): PairCoupledTypes<&u32>) { + ::std::mem::swap(&mut y, &mut _z); +} + +fn coupled_wilds_lhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((y, _z),): (PairCoupledTypes<_>,) = ((s, &_x),); + // If above line compiled, so should line below + // swap_wilds((y, _z)); + + // ... but the ascribed type also invalidates this use of `y` + y //~ ERROR lifetime may not live long enough +} + +fn main() { + uncoupled_lhs(&3, &4); + coupled_regions_lhs(&3, &4); + coupled_types_lhs(&3, &4); + coupled_wilds_lhs(&3, &4); +} diff --git a/src/test/ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.stderr b/src/test/ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.stderr new file mode 100644 index 000000000..8399ef04e --- /dev/null +++ b/src/test/ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.stderr @@ -0,0 +1,29 @@ +error: lifetime may not live long enough + --> $DIR/issue-55748-pat-types-constrain-bindings.rs:33:5 + | +LL | fn coupled_regions_lhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + | -- lifetime `'a` defined here +... +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/issue-55748-pat-types-constrain-bindings.rs:47:5 + | +LL | fn coupled_types_lhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + | -- lifetime `'a` defined here +... +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/issue-55748-pat-types-constrain-bindings.rs:60:5 + | +LL | fn coupled_wilds_lhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + | -- lifetime `'a` defined here +... +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs b/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs new file mode 100644 index 000000000..9b3ec702c --- /dev/null +++ b/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs @@ -0,0 +1,40 @@ +// Check that repeated type variables are correctly handled + +#![allow(unused)] +#![feature(type_ascription)] + +type PairUncoupled<'a, 'b, T> = (&'a T, &'b T); +type PairCoupledTypes<T> = (T, T); +type PairCoupledRegions<'a, T> = (&'a T, &'a T); + +fn uncoupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((y, _z),) = ((s, _x),): (PairUncoupled<_>,); + y // OK +} + +fn coupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((y, _z),) = ((s, _x),): (PairCoupledTypes<_>,); + y //~ ERROR lifetime may not live long enough +} + +fn coupled_regions_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((y, _z),) = ((s, _x),): (PairCoupledRegions<_>,); + y //~ ERROR lifetime may not live long enough +} + +fn cast_uncoupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((y, _z),) = ((s, _x),) as (PairUncoupled<_>,); + y // OK +} + +fn cast_coupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((y, _z),) = ((s, _x),) as (PairCoupledTypes<_>,); + y //~ ERROR lifetime may not live long enough +} + +fn cast_coupled_regions_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + let ((y, _z),) = ((s, _x),) as (PairCoupledRegions<_>,); + y //~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.stderr b/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.stderr new file mode 100644 index 000000000..c99f53c5a --- /dev/null +++ b/src/test/ui/nll/user-annotations/issue-57731-ascibed-coupled-types.stderr @@ -0,0 +1,38 @@ +error: lifetime may not live long enough + --> $DIR/issue-57731-ascibed-coupled-types.rs:17:5 + | +LL | fn coupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | let ((y, _z),) = ((s, _x),): (PairCoupledTypes<_>,); +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/issue-57731-ascibed-coupled-types.rs:22:5 + | +LL | fn coupled_regions_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | let ((y, _z),) = ((s, _x),): (PairCoupledRegions<_>,); +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/issue-57731-ascibed-coupled-types.rs:32:5 + | +LL | fn cast_coupled_wilds_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | let ((y, _z),) = ((s, _x),) as (PairCoupledTypes<_>,); +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/issue-57731-ascibed-coupled-types.rs:37:5 + | +LL | fn cast_coupled_regions_rhs<'a>(_x: &'a u32, s: &'static u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | let ((y, _z),) = ((s, _x),) as (PairCoupledRegions<_>,); +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/nll/user-annotations/method-call.rs b/src/test/ui/nll/user-annotations/method-call.rs new file mode 100644 index 000000000..beafc597a --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-call.rs @@ -0,0 +1,69 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +trait Bazoom<T> { + fn method<U>(&self, arg: T, arg2: U) { } +} + +impl<T, U> Bazoom<U> for T { +} + +fn no_annot() { + let a = 22; + let b = 44; + let c = 66; + a.method(b, &c); // OK +} + +fn annot_underscore() { + let a = 22; + let b = 44; + let c = 66; + a.method::<_>(b, &c); // OK +} + +fn annot_reference_any_lifetime() { + let a = 22; + let b = 44; + let c = 66; + a.method::<&u32>(b, &c); // OK +} + +fn annot_reference_static_lifetime() { + let a = 22; + let b = 44; + let c = 66; + a.method::<&'static u32>(b, &c); //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let a = 22; + let b = 44; + let c = 66; + a.method::<&'a u32>(b, &c); //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + let a = 22; + let b = 44; + a.method::<&'a u32>(b, c); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let a = 22; + let b = 44; + let _closure = || { + let c = 66; + a.method::<&'a u32>(b, &c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let a = 22; + let b = 44; + let _closure = || { + a.method::<&'a u32>(b, c); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/method-call.stderr b/src/test/ui/nll/user-annotations/method-call.stderr new file mode 100644 index 000000000..fcaeb465d --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-call.stderr @@ -0,0 +1,43 @@ +error[E0597]: `c` does not live long enough + --> $DIR/method-call.rs:36:34 + | +LL | a.method::<&'static u32>(b, &c); + | -----------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/method-call.rs:43:29 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +... +LL | a.method::<&'a u32>(b, &c); + | ------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/method-call.rs:57:33 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +... +LL | let _closure = || { + | - `c` dropped here while still borrowed +LL | let c = 66; +LL | a.method::<&'a u32>(b, &c); + | ------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/method-ufcs-1.rs b/src/test/ui/nll/user-annotations/method-ufcs-1.rs new file mode 100644 index 000000000..950771f35 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-1.rs @@ -0,0 +1,63 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +trait Bazoom<T>: Sized { + fn method<U>(self, arg: T, arg2: U) { } +} + +impl<T, U> Bazoom<U> for T { +} + +fn annot_underscore() { + let a = 22; + let b = 44; + let c = 66; + <_ as Bazoom<_>>::method::<_>(&a, b, c); // OK +} + +fn annot_reference_any_lifetime() { + let a = 22; + let b = 44; + let c = 66; + <&u32 as Bazoom<_>>::method(&a, b, c); // OK +} + +fn annot_reference_static_lifetime() { + let a = 22; + let b = 44; + let c = 66; + let x = <&'static u32 as Bazoom<_>>::method; + x(&a, b, c); //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let a = 22; + let b = 44; + let c = 66; + <&'a u32 as Bazoom<_>>::method(&a, b, c); //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(a: &'a u32) { + let b = 44; + let c = 66; + <&'a u32 as Bazoom<_>>::method(&a, b, c); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let a = 22; + let b = 44; + let _closure = || { + let c = 66; + <&'a u32 as Bazoom<_>>::method(&a, b, c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(a: &'a u32) { + let b = 44; + let c = 66; + let _closure = || { + <&'a u32 as Bazoom<_>>::method(&a, b, c); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/method-ufcs-1.stderr b/src/test/ui/nll/user-annotations/method-ufcs-1.stderr new file mode 100644 index 000000000..962ddfd2b --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-1.stderr @@ -0,0 +1,46 @@ +error[E0597]: `a` does not live long enough + --> $DIR/method-ufcs-1.rs:30:7 + | +LL | x(&a, b, c); + | --^^------- + | | | + | | borrowed value does not live long enough + | argument requires that `a` is borrowed for `'static` +LL | } + | - `a` dropped here while still borrowed + +error[E0597]: `a` does not live long enough + --> $DIR/method-ufcs-1.rs:37:36 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +... +LL | <&'a u32 as Bazoom<_>>::method(&a, b, c); + | -------------------------------^^------- + | | | + | | borrowed value does not live long enough + | argument requires that `a` is borrowed for `'a` +LL | } + | - `a` dropped here while still borrowed + +error[E0597]: `a` does not live long enough + --> $DIR/method-ufcs-1.rs:51:41 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +... +LL | let _closure = || { + | -- value captured here +LL | let c = 66; +LL | <&'a u32 as Bazoom<_>>::method(&a, b, c); + | --------------------------------^------- + | | | + | | borrowed value does not live long enough + | argument requires that `a` is borrowed for `'a` +LL | }; +LL | } + | - `a` dropped here while still borrowed + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/method-ufcs-2.rs b/src/test/ui/nll/user-annotations/method-ufcs-2.rs new file mode 100644 index 000000000..7dc0f0c12 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-2.rs @@ -0,0 +1,63 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +trait Bazoom<T>: Sized { + fn method<U>(self, arg: T, arg2: U) { } +} + +impl<T, U> Bazoom<U> for T { +} + +fn annot_underscore() { + let a = 22; + let b = 44; + let c = 66; + <_ as Bazoom<_>>::method(a, &b, c); // OK +} + +fn annot_reference_any_lifetime() { + let a = 22; + let b = 44; + let c = 66; + <_ as Bazoom<&u32>>::method(a, &b, c); // OK +} + +fn annot_reference_static_lifetime() { + let a = 22; + let b = 44; + let c = 66; + let x = <&'static u32 as Bazoom<_>>::method; + x(&a, b, c); //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let a = 22; + let b = 44; + let c = 66; + <_ as Bazoom<&'a u32>>::method(a, &b, c); //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(b: &'a u32) { + let a = 44; + let c = 66; + <_ as Bazoom<&'a u32>>::method(a, &b, c); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let a = 22; + let b = 44; + let _closure = || { + let c = 66; + <_ as Bazoom<&'a u32>>::method(a, &b, c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(b: &'a u32) { + let a = 44; + let c = 66; + let _closure = || { + <_ as Bazoom<&'a u32>>::method(a, &b, c); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/method-ufcs-2.stderr b/src/test/ui/nll/user-annotations/method-ufcs-2.stderr new file mode 100644 index 000000000..63d59905e --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-2.stderr @@ -0,0 +1,46 @@ +error[E0597]: `a` does not live long enough + --> $DIR/method-ufcs-2.rs:30:7 + | +LL | x(&a, b, c); + | --^^------- + | | | + | | borrowed value does not live long enough + | argument requires that `a` is borrowed for `'static` +LL | } + | - `a` dropped here while still borrowed + +error[E0597]: `b` does not live long enough + --> $DIR/method-ufcs-2.rs:37:39 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +... +LL | <_ as Bazoom<&'a u32>>::method(a, &b, c); + | ----------------------------------^^---- + | | | + | | borrowed value does not live long enough + | argument requires that `b` is borrowed for `'a` +LL | } + | - `b` dropped here while still borrowed + +error[E0597]: `b` does not live long enough + --> $DIR/method-ufcs-2.rs:51:44 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +... +LL | let _closure = || { + | -- value captured here +LL | let c = 66; +LL | <_ as Bazoom<&'a u32>>::method(a, &b, c); + | -----------------------------------^---- + | | | + | | borrowed value does not live long enough + | argument requires that `b` is borrowed for `'a` +LL | }; +LL | } + | - `b` dropped here while still borrowed + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/method-ufcs-3.rs b/src/test/ui/nll/user-annotations/method-ufcs-3.rs new file mode 100644 index 000000000..59d2009d1 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-3.rs @@ -0,0 +1,69 @@ +// Unit test for the "user substitutions" that are annotated on each +// node. + +trait Bazoom<T> { + fn method<U>(&self, arg: T, arg2: U) { } +} + +impl<T, U> Bazoom<U> for T { +} + +fn no_annot() { + let a = 22; + let b = 44; + let c = 66; + <_ as Bazoom<_>>::method(&a, b, &c); // OK +} + +fn annot_underscore() { + let a = 22; + let b = 44; + let c = 66; + <_ as Bazoom<_>>::method::<_>(&a, b, &c); // OK +} + +fn annot_reference_any_lifetime() { + let a = 22; + let b = 44; + let c = 66; + <_ as Bazoom<_>>::method::<&u32>(&a, b, &c); // OK +} + +fn annot_reference_static_lifetime() { + let a = 22; + let b = 44; + let c = 66; + <_ as Bazoom<_>>::method::<&'static u32>(&a, b, &c); //~ ERROR +} + +fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + let a = 22; + let b = 44; + let c = 66; + <_ as Bazoom<_>>::method::<&'a u32>(&a, b, &c); //~ ERROR +} + +fn annot_reference_named_lifetime_ok<'a>(c: &'a u32) { + let a = 22; + let b = 44; + <_ as Bazoom<_>>::method::<&'a u32>(&a, b, c); +} + +fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + let a = 22; + let b = 44; + let _closure = || { + let c = 66; + <_ as Bazoom<_>>::method::<&'a u32>(&a, b, &c); //~ ERROR + }; +} + +fn annot_reference_named_lifetime_in_closure_ok<'a>(c: &'a u32) { + let a = 22; + let b = 44; + let _closure = || { + <_ as Bazoom<_>>::method::<&'a u32>(&a, b, c); + }; +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/method-ufcs-3.stderr b/src/test/ui/nll/user-annotations/method-ufcs-3.stderr new file mode 100644 index 000000000..328dde980 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-3.stderr @@ -0,0 +1,43 @@ +error[E0597]: `c` does not live long enough + --> $DIR/method-ufcs-3.rs:36:53 + | +LL | <_ as Bazoom<_>>::method::<&'static u32>(&a, b, &c); + | ------------------------------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/method-ufcs-3.rs:43:48 + | +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +... +LL | <_ as Bazoom<_>>::method::<&'a u32>(&a, b, &c); + | -------------------------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed + +error[E0597]: `c` does not live long enough + --> $DIR/method-ufcs-3.rs:57:52 + | +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +... +LL | let _closure = || { + | - `c` dropped here while still borrowed +LL | let c = 66; +LL | <_ as Bazoom<_>>::method::<&'a u32>(&a, b, &c); + | -------------------------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/method-ufcs-inherent-1.rs b/src/test/ui/nll/user-annotations/method-ufcs-inherent-1.rs new file mode 100644 index 000000000..7bfed61d4 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-inherent-1.rs @@ -0,0 +1,18 @@ +// Check that substitutions given on the self type (here, `A`) carry +// through to NLL. + +struct A<'a> { x: &'a u32 } + +impl<'a> A<'a> { + fn new<'b, T>(x: &'a u32, y: T) -> Self { + Self { x } + } +} + +fn foo<'a>() { + let v = 22; + let x = A::<'a>::new(&v, 22); + //~^ ERROR +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/method-ufcs-inherent-1.stderr b/src/test/ui/nll/user-annotations/method-ufcs-inherent-1.stderr new file mode 100644 index 000000000..94861babd --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-inherent-1.stderr @@ -0,0 +1,18 @@ +error[E0597]: `v` does not live long enough + --> $DIR/method-ufcs-inherent-1.rs:14:26 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +LL | let v = 22; +LL | let x = A::<'a>::new(&v, 22); + | -------------^^----- + | | | + | | borrowed value does not live long enough + | argument requires that `v` is borrowed for `'a` +LL | +LL | } + | - `v` 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/nll/user-annotations/method-ufcs-inherent-2.rs b/src/test/ui/nll/user-annotations/method-ufcs-inherent-2.rs new file mode 100644 index 000000000..cfbc0bcf6 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-inherent-2.rs @@ -0,0 +1,19 @@ +// Check that substitutions given on the self type (here, `A`) can be +// used in combination with annotations given for method arguments. + +struct A<'a> { x: &'a u32 } + +impl<'a> A<'a> { + fn new<'b, T>(x: &'a u32, y: T) -> Self { + Self { x } + } +} + +fn foo<'a>() { + let v = 22; + let x = A::<'a>::new::<&'a u32>(&v, &v); + //~^ ERROR + //~| ERROR +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/method-ufcs-inherent-2.stderr b/src/test/ui/nll/user-annotations/method-ufcs-inherent-2.stderr new file mode 100644 index 000000000..06f20d9b2 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-inherent-2.stderr @@ -0,0 +1,33 @@ +error[E0597]: `v` does not live long enough + --> $DIR/method-ufcs-inherent-2.rs:14:37 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +LL | let v = 22; +LL | let x = A::<'a>::new::<&'a u32>(&v, &v); + | ------------------------^^----- + | | | + | | borrowed value does not live long enough + | argument requires that `v` is borrowed for `'a` +... +LL | } + | - `v` dropped here while still borrowed + +error[E0597]: `v` does not live long enough + --> $DIR/method-ufcs-inherent-2.rs:14:41 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +LL | let v = 22; +LL | let x = A::<'a>::new::<&'a u32>(&v, &v); + | ----------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `v` is borrowed for `'a` +... +LL | } + | - `v` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/method-ufcs-inherent-3.rs b/src/test/ui/nll/user-annotations/method-ufcs-inherent-3.rs new file mode 100644 index 000000000..7ddb13360 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-inherent-3.rs @@ -0,0 +1,18 @@ +// Check that inherent methods invoked with `<T>::new` style +// carry their annotations through to NLL. + +struct A<'a> { x: &'a u32 } + +impl<'a> A<'a> { + fn new<'b, T>(x: &'a u32, y: T) -> Self { + Self { x } + } +} + +fn foo<'a>() { + let v = 22; + let x = <A<'a>>::new(&v, 22); + //~^ ERROR +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/method-ufcs-inherent-3.stderr b/src/test/ui/nll/user-annotations/method-ufcs-inherent-3.stderr new file mode 100644 index 000000000..4ad61dc81 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-inherent-3.stderr @@ -0,0 +1,18 @@ +error[E0597]: `v` does not live long enough + --> $DIR/method-ufcs-inherent-3.rs:14:26 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +LL | let v = 22; +LL | let x = <A<'a>>::new(&v, 22); + | -------------^^----- + | | | + | | borrowed value does not live long enough + | argument requires that `v` is borrowed for `'a` +LL | +LL | } + | - `v` 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/nll/user-annotations/method-ufcs-inherent-4.rs b/src/test/ui/nll/user-annotations/method-ufcs-inherent-4.rs new file mode 100644 index 000000000..85e759739 --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-inherent-4.rs @@ -0,0 +1,20 @@ +// Check that inherent methods invoked with `<T>::new` style +// carry their annotations through to NLL in connection with +// method type parameters. + +struct A<'a> { x: &'a u32 } + +impl<'a> A<'a> { + fn new<'b, T>(x: &'a u32, y: T) -> Self { + Self { x } + } +} + +fn foo<'a>() { + let v = 22; + let x = <A<'a>>::new::<&'a u32>(&v, &v); + //~^ ERROR + //~| ERROR +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/method-ufcs-inherent-4.stderr b/src/test/ui/nll/user-annotations/method-ufcs-inherent-4.stderr new file mode 100644 index 000000000..0f83e99cd --- /dev/null +++ b/src/test/ui/nll/user-annotations/method-ufcs-inherent-4.stderr @@ -0,0 +1,33 @@ +error[E0597]: `v` does not live long enough + --> $DIR/method-ufcs-inherent-4.rs:15:37 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +LL | let v = 22; +LL | let x = <A<'a>>::new::<&'a u32>(&v, &v); + | ------------------------^^----- + | | | + | | borrowed value does not live long enough + | argument requires that `v` is borrowed for `'a` +... +LL | } + | - `v` dropped here while still borrowed + +error[E0597]: `v` does not live long enough + --> $DIR/method-ufcs-inherent-4.rs:15:41 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +LL | let v = 22; +LL | let x = <A<'a>>::new::<&'a u32>(&v, &v); + | ----------------------------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `v` is borrowed for `'a` +... +LL | } + | - `v` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/normalization.rs b/src/test/ui/nll/user-annotations/normalization.rs new file mode 100644 index 000000000..870e3d811 --- /dev/null +++ b/src/test/ui/nll/user-annotations/normalization.rs @@ -0,0 +1,10 @@ +// Test that we enforce a `&'static` requirement that is only visible +// after normalization. + +trait Foo { type Out; } +impl Foo for () { type Out = &'static u32; } + +fn main() { + let a = 22; + let b: <() as Foo>::Out = &a; //~ ERROR +} diff --git a/src/test/ui/nll/user-annotations/normalization.stderr b/src/test/ui/nll/user-annotations/normalization.stderr new file mode 100644 index 000000000..4c7893789 --- /dev/null +++ b/src/test/ui/nll/user-annotations/normalization.stderr @@ -0,0 +1,13 @@ +error[E0597]: `a` does not live long enough + --> $DIR/normalization.rs:9:31 + | +LL | let b: <() as Foo>::Out = &a; + | ---------------- ^^ borrowed value does not live long enough + | | + | type annotation requires that `a` is borrowed for `'static` +LL | } + | - `a` 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/nll/user-annotations/normalize-self-ty.rs b/src/test/ui/nll/user-annotations/normalize-self-ty.rs new file mode 100644 index 000000000..df905c878 --- /dev/null +++ b/src/test/ui/nll/user-annotations/normalize-self-ty.rs @@ -0,0 +1,23 @@ +// Regression test for #55183: check a case where the self type from +// the inherent impl requires normalization to be equal to the +// user-provided type. +// +// check-pass + +trait Mirror { + type Me; +} + +impl<T> Mirror for T { + type Me = T; +} + +struct Foo<A, B>(A, B); + +impl<A> Foo<A, <A as Mirror>::Me> { + fn m(_: A) { } +} + +fn main() { + <Foo<&'static u32, &u32>>::m(&22); +} diff --git a/src/test/ui/nll/user-annotations/pattern_substs_on_brace_enum_variant.rs b/src/test/ui/nll/user-annotations/pattern_substs_on_brace_enum_variant.rs new file mode 100644 index 000000000..59cd69c0c --- /dev/null +++ b/src/test/ui/nll/user-annotations/pattern_substs_on_brace_enum_variant.rs @@ -0,0 +1,22 @@ +enum Foo<'a> { + Bar { field: &'a u32 } +} + +fn in_let() { + let y = 22; + let foo = Foo::Bar { field: &y }; + //~^ ERROR `y` does not live long enough + let Foo::Bar::<'static> { field: _z } = foo; +} + +fn in_match() { + let y = 22; + let foo = Foo::Bar { field: &y }; + //~^ ERROR `y` does not live long enough + match foo { + Foo::Bar::<'static> { field: _z } => { + } + } +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/pattern_substs_on_brace_enum_variant.stderr b/src/test/ui/nll/user-annotations/pattern_substs_on_brace_enum_variant.stderr new file mode 100644 index 000000000..a97e7a9fd --- /dev/null +++ b/src/test/ui/nll/user-annotations/pattern_substs_on_brace_enum_variant.stderr @@ -0,0 +1,26 @@ +error[E0597]: `y` does not live long enough + --> $DIR/pattern_substs_on_brace_enum_variant.rs:7:33 + | +LL | let foo = Foo::Bar { field: &y }; + | ^^ borrowed value does not live long enough +LL | +LL | let Foo::Bar::<'static> { field: _z } = foo; + | --------------------------------- type annotation requires that `y` is borrowed for `'static` +LL | } + | - `y` dropped here while still borrowed + +error[E0597]: `y` does not live long enough + --> $DIR/pattern_substs_on_brace_enum_variant.rs:14:33 + | +LL | let foo = Foo::Bar { field: &y }; + | ^^ borrowed value does not live long enough +... +LL | Foo::Bar::<'static> { field: _z } => { + | --------------------------------- type annotation requires that `y` is borrowed for `'static` +... +LL | } + | - `y` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/pattern_substs_on_brace_struct.rs b/src/test/ui/nll/user-annotations/pattern_substs_on_brace_struct.rs new file mode 100644 index 000000000..1586c4ea3 --- /dev/null +++ b/src/test/ui/nll/user-annotations/pattern_substs_on_brace_struct.rs @@ -0,0 +1,20 @@ +struct Foo<'a> { field: &'a u32 } + +fn in_let() { + let y = 22; + let foo = Foo { field: &y }; + //~^ ERROR `y` does not live long enough + let Foo::<'static> { field: _z } = foo; +} + +fn in_main() { + let y = 22; + let foo = Foo { field: &y }; + //~^ ERROR `y` does not live long enough + match foo { + Foo::<'static> { field: _z } => { + } + } +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/pattern_substs_on_brace_struct.stderr b/src/test/ui/nll/user-annotations/pattern_substs_on_brace_struct.stderr new file mode 100644 index 000000000..408d7c2a5 --- /dev/null +++ b/src/test/ui/nll/user-annotations/pattern_substs_on_brace_struct.stderr @@ -0,0 +1,26 @@ +error[E0597]: `y` does not live long enough + --> $DIR/pattern_substs_on_brace_struct.rs:5:28 + | +LL | let foo = Foo { field: &y }; + | ^^ borrowed value does not live long enough +LL | +LL | let Foo::<'static> { field: _z } = foo; + | ---------------------------- type annotation requires that `y` is borrowed for `'static` +LL | } + | - `y` dropped here while still borrowed + +error[E0597]: `y` does not live long enough + --> $DIR/pattern_substs_on_brace_struct.rs:12:28 + | +LL | let foo = Foo { field: &y }; + | ^^ borrowed value does not live long enough +... +LL | Foo::<'static> { field: _z } => { + | ---------------------------- type annotation requires that `y` is borrowed for `'static` +... +LL | } + | - `y` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_enum_variant.rs b/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_enum_variant.rs new file mode 100644 index 000000000..6fa59fdd8 --- /dev/null +++ b/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_enum_variant.rs @@ -0,0 +1,22 @@ +enum Foo<'a> { + Bar(&'a u32) +} + +fn in_let() { + let y = 22; + let foo = Foo::Bar(&y); + //~^ ERROR `y` does not live long enough + let Foo::Bar::<'static>(_z) = foo; +} + +fn in_match() { + let y = 22; + let foo = Foo::Bar(&y); + //~^ ERROR `y` does not live long enough + match foo { + Foo::Bar::<'static>(_z) => { + } + } +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_enum_variant.stderr b/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_enum_variant.stderr new file mode 100644 index 000000000..920c906f6 --- /dev/null +++ b/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_enum_variant.stderr @@ -0,0 +1,26 @@ +error[E0597]: `y` does not live long enough + --> $DIR/pattern_substs_on_tuple_enum_variant.rs:7:24 + | +LL | let foo = Foo::Bar(&y); + | ^^ borrowed value does not live long enough +LL | +LL | let Foo::Bar::<'static>(_z) = foo; + | ----------------------- type annotation requires that `y` is borrowed for `'static` +LL | } + | - `y` dropped here while still borrowed + +error[E0597]: `y` does not live long enough + --> $DIR/pattern_substs_on_tuple_enum_variant.rs:14:24 + | +LL | let foo = Foo::Bar(&y); + | ^^ borrowed value does not live long enough +... +LL | Foo::Bar::<'static>(_z) => { + | ----------------------- type annotation requires that `y` is borrowed for `'static` +... +LL | } + | - `y` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_struct.rs b/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_struct.rs new file mode 100644 index 000000000..7486aab0e --- /dev/null +++ b/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_struct.rs @@ -0,0 +1,20 @@ +struct Foo<'a>(&'a u32); + +fn in_let() { + let y = 22; + let foo = Foo(&y); + //~^ ERROR `y` does not live long enough + let Foo::<'static>(_z) = foo; +} + +fn in_match() { + let y = 22; + let foo = Foo(&y); + //~^ ERROR `y` does not live long enough + match foo { + Foo::<'static>(_z) => { + } + } +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_struct.stderr b/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_struct.stderr new file mode 100644 index 000000000..3f01638d8 --- /dev/null +++ b/src/test/ui/nll/user-annotations/pattern_substs_on_tuple_struct.stderr @@ -0,0 +1,26 @@ +error[E0597]: `y` does not live long enough + --> $DIR/pattern_substs_on_tuple_struct.rs:5:19 + | +LL | let foo = Foo(&y); + | ^^ borrowed value does not live long enough +LL | +LL | let Foo::<'static>(_z) = foo; + | ------------------ type annotation requires that `y` is borrowed for `'static` +LL | } + | - `y` dropped here while still borrowed + +error[E0597]: `y` does not live long enough + --> $DIR/pattern_substs_on_tuple_struct.rs:12:19 + | +LL | let foo = Foo(&y); + | ^^ borrowed value does not live long enough +... +LL | Foo::<'static>(_z) => { + | ------------------ type annotation requires that `y` is borrowed for `'static` +... +LL | } + | - `y` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/patterns.rs b/src/test/ui/nll/user-annotations/patterns.rs new file mode 100644 index 000000000..1f635d7f5 --- /dev/null +++ b/src/test/ui/nll/user-annotations/patterns.rs @@ -0,0 +1,136 @@ +// Test that various patterns also enforce types. + +fn variable_no_initializer() { + let x = 22; + let y: &'static u32; + y = &x; //~ ERROR +} + +fn tuple_no_initializer() { + + + let x = 22; + let (y, z): (&'static u32, &'static u32); + y = &x; //~ ERROR +} + +fn ref_with_ascribed_static_type() -> u32 { + // Check the behavior in some wacky cases. + let x = 22; + let y = &x; //~ ERROR + let ref z: &'static u32 = y; + **z +} + +fn ref_with_ascribed_any_type() -> u32 { + let x = 22; + let y = &x; + let ref z: &u32 = y; + **z +} + +struct Single<T> { value: T } + +fn struct_no_initializer() { + + + let x = 22; + let Single { value: y }: Single<&'static u32>; + y = &x; //~ ERROR +} + + +fn struct_no_initializer_must_normalize() { + trait Indirect { type Assoc; } + struct StaticU32; + impl Indirect for StaticU32 { type Assoc = &'static u32; } + struct Single2<T: Indirect> { value: <T as Indirect>::Assoc } + + let x = 22; + let Single2 { value: mut _y }: Single2<StaticU32>; + _y = &x; //~ ERROR +} + +fn variable_with_initializer() { + let x = 22; + let y: &'static u32 = &x; //~ ERROR +} + +fn underscore_with_initializer() { + let x = 22; + let _: &'static u32 = &x; //~ ERROR + + let _: Vec<&'static String> = vec![&String::new()]; + //~^ ERROR temporary value dropped while borrowed [E0716] + + let (_, a): (Vec<&'static String>, _) = (vec![&String::new()], 44); + //~^ ERROR temporary value dropped while borrowed [E0716] + + let (_a, b): (Vec<&'static String>, _) = (vec![&String::new()], 44); + //~^ ERROR temporary value dropped while borrowed [E0716] +} + +fn pair_underscores_with_initializer() { + let x = 22; + let (_, _): (&'static u32, u32) = (&x, 44); //~ ERROR +} + +fn pair_variable_with_initializer() { + let x = 22; + let (y, _): (&'static u32, u32) = (&x, 44); //~ ERROR +} + +fn struct_single_field_variable_with_initializer() { + let x = 22; + let Single { value: y }: Single<&'static u32> = Single { value: &x }; //~ ERROR +} + +fn struct_single_field_underscore_with_initializer() { + let x = 22; + let Single { value: _ }: Single<&'static u32> = Single { value: &x }; //~ ERROR +} + +struct Double<T> { value1: T, value2: T } + +fn struct_double_field_underscore_with_initializer() { + let x = 22; + let Double { value1: _, value2: _ }: Double<&'static u32> = Double { + value1: &x, //~ ERROR + value2: &44, + }; +} + +fn static_to_a_to_static_through_variable<'a>(x: &'a u32) -> &'static u32 { + + + + + + + let y: &'a u32 = &22; + y //~ ERROR +} + +fn static_to_a_to_static_through_tuple<'a>(x: &'a u32) -> &'static u32 { + + + + + + + + let (y, _z): (&'a u32, u32) = (&22, 44); + y //~ ERROR +} + +fn static_to_a_to_static_through_struct<'a>(_x: &'a u32) -> &'static u32 { + let Single { value: y }: Single<&'a u32> = Single { value: &22 }; + y //~ ERROR +} + +fn a_to_static_then_static<'a>(x: &'a u32) -> &'static u32 { + let (y, _z): (&'static u32, u32) = (x, 44); //~ ERROR + y +} + +fn main() { } diff --git a/src/test/ui/nll/user-annotations/patterns.stderr b/src/test/ui/nll/user-annotations/patterns.stderr new file mode 100644 index 000000000..60d6e6db3 --- /dev/null +++ b/src/test/ui/nll/user-annotations/patterns.stderr @@ -0,0 +1,189 @@ +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:6:9 + | +LL | let y: &'static u32; + | ------------ type annotation requires that `x` is borrowed for `'static` +LL | y = &x; + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:14:9 + | +LL | let (y, z): (&'static u32, &'static u32); + | ---------------------------- type annotation requires that `x` is borrowed for `'static` +LL | y = &x; + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:20:13 + | +LL | let y = &x; + | ^^ borrowed value does not live long enough +LL | let ref z: &'static u32 = y; + | ------------ type annotation requires that `x` is borrowed for `'static` +LL | **z +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:39:9 + | +LL | let Single { value: y }: Single<&'static u32>; + | -------------------- type annotation requires that `x` is borrowed for `'static` +LL | y = &x; + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:51:10 + | +LL | let Single2 { value: mut _y }: Single2<StaticU32>; + | ------------------ type annotation requires that `x` is borrowed for `'static` +LL | _y = &x; + | ^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:56:27 + | +LL | let y: &'static u32 = &x; + | ------------ ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:61:27 + | +LL | let _: &'static u32 = &x; + | ------------ ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +... +LL | } + | - `x` dropped here while still borrowed + +error[E0716]: temporary value dropped while borrowed + --> $DIR/patterns.rs:63:41 + | +LL | let _: Vec<&'static String> = vec![&String::new()]; + | -------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement + | | | + | | creates a temporary which is freed while still in use + | type annotation requires that borrow lasts for `'static` + +error[E0716]: temporary value dropped while borrowed + --> $DIR/patterns.rs:66:52 + | +LL | let (_, a): (Vec<&'static String>, _) = (vec![&String::new()], 44); + | ------------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement + | | | + | | creates a temporary which is freed while still in use + | type annotation requires that borrow lasts for `'static` + +error[E0716]: temporary value dropped while borrowed + --> $DIR/patterns.rs:69:53 + | +LL | let (_a, b): (Vec<&'static String>, _) = (vec![&String::new()], 44); + | ------------------------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement + | | | + | | creates a temporary which is freed while still in use + | type annotation requires that borrow lasts for `'static` + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:75:40 + | +LL | let (_, _): (&'static u32, u32) = (&x, 44); + | ------------------- ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:80:40 + | +LL | let (y, _): (&'static u32, u32) = (&x, 44); + | ------------------- ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:85:69 + | +LL | let Single { value: y }: Single<&'static u32> = Single { value: &x }; + | -------------------- ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:90:69 + | +LL | let Single { value: _ }: Single<&'static u32> = Single { value: &x }; + | -------------------- ^^ borrowed value does not live long enough + | | + | type annotation requires that `x` is borrowed for `'static` +LL | } + | - `x` dropped here while still borrowed + +error[E0597]: `x` does not live long enough + --> $DIR/patterns.rs:98:17 + | +LL | let Double { value1: _, value2: _ }: Double<&'static u32> = Double { + | -------------------- type annotation requires that `x` is borrowed for `'static` +LL | value1: &x, + | ^^ borrowed value does not live long enough +... +LL | } + | - `x` dropped here while still borrowed + +error: lifetime may not live long enough + --> $DIR/patterns.rs:111:5 + | +LL | fn static_to_a_to_static_through_variable<'a>(x: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +... +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/patterns.rs:123:5 + | +LL | fn static_to_a_to_static_through_tuple<'a>(x: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +... +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/patterns.rs:128:5 + | +LL | fn static_to_a_to_static_through_struct<'a>(_x: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | let Single { value: y }: Single<&'a u32> = Single { value: &22 }; +LL | y + | ^ returning this value requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/patterns.rs:132:18 + | +LL | fn a_to_static_then_static<'a>(x: &'a u32) -> &'static u32 { + | -- lifetime `'a` defined here +LL | let (y, _z): (&'static u32, u32) = (x, 44); + | ^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + +error: aborting due to 19 previous errors + +Some errors have detailed explanations: E0597, E0716. +For more information about an error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/user-annotations/promoted-annotation.rs b/src/test/ui/nll/user-annotations/promoted-annotation.rs new file mode 100644 index 000000000..b92f8bfd2 --- /dev/null +++ b/src/test/ui/nll/user-annotations/promoted-annotation.rs @@ -0,0 +1,10 @@ +// Test that type annotations are checked in promoted constants correctly. + +fn foo<'a>() { + let x = 0; + let f = &drop::<&'a i32>; + f(&x); + //~^ ERROR `x` does not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/promoted-annotation.stderr b/src/test/ui/nll/user-annotations/promoted-annotation.stderr new file mode 100644 index 000000000..cb99a6a36 --- /dev/null +++ b/src/test/ui/nll/user-annotations/promoted-annotation.stderr @@ -0,0 +1,17 @@ +error[E0597]: `x` does not live long enough + --> $DIR/promoted-annotation.rs:6:7 + | +LL | fn foo<'a>() { + | -- lifetime `'a` defined here +LL | let x = 0; +LL | let f = &drop::<&'a i32>; + | ---------------- assignment requires that `x` is borrowed for `'a` +LL | f(&x); + | ^^ borrowed value does not live long enough +LL | +LL | } + | - `x` 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/nll/user-annotations/type-annotation-with-hrtb.rs b/src/test/ui/nll/user-annotations/type-annotation-with-hrtb.rs new file mode 100644 index 000000000..1f7c06038 --- /dev/null +++ b/src/test/ui/nll/user-annotations/type-annotation-with-hrtb.rs @@ -0,0 +1,33 @@ +// Regression test for issue #69490 + +// check-pass + +pub trait Trait<T> { + const S: &'static str; +} + +impl<T> Trait<()> for T +where + T: for<'a> Trait<&'a ()>, +{ + // Use of `T::S` here caused an ICE + const S: &'static str = T::S; +} + +// Some similar cases that didn't ICE: + +impl<'a, T> Trait<()> for (T,) +where + T: Trait<&'a ()>, +{ + const S: &'static str = T::S; +} + +impl<T> Trait<()> for [T; 1] +where + T: Trait<for<'a> fn(&'a ())>, +{ + const S: &'static str = T::S; +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.rs b/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.rs new file mode 100644 index 000000000..101b5cfab --- /dev/null +++ b/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.rs @@ -0,0 +1,7 @@ +#![allow(warnings)] +#![feature(type_ascription)] + +fn main() { + let x = 22_u32; + let y: &u32 = &x: &'static u32; //~ ERROR E0597 +} diff --git a/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.stderr b/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.stderr new file mode 100644 index 000000000..133bbef52 --- /dev/null +++ b/src/test/ui/nll/user-annotations/type_ascription_static_lifetime.stderr @@ -0,0 +1,14 @@ +error[E0597]: `x` does not live long enough + --> $DIR/type_ascription_static_lifetime.rs:6:19 + | +LL | let y: &u32 = &x: &'static u32; + | ^^-------------- + | | + | borrowed value does not live long enough + | type annotation requires that `x` is borrowed for `'static` +LL | } + | - `x` 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/nll/user-annotations/wf-self-type.rs b/src/test/ui/nll/user-annotations/wf-self-type.rs new file mode 100644 index 000000000..539226aab --- /dev/null +++ b/src/test/ui/nll/user-annotations/wf-self-type.rs @@ -0,0 +1,13 @@ +struct Foo<'a, 'b: 'a>(&'a &'b ()); + +impl<'a, 'b> Foo<'a, 'b> { + fn xmute(a: &'b ()) -> &'a () { + unreachable!() + } +} + +pub fn foo<'a, 'b>(u: &'b ()) -> &'a () { + Foo::xmute(u) //~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/user-annotations/wf-self-type.stderr b/src/test/ui/nll/user-annotations/wf-self-type.stderr new file mode 100644 index 000000000..1d3ae7cfb --- /dev/null +++ b/src/test/ui/nll/user-annotations/wf-self-type.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/wf-self-type.rs:10:5 + | +LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | Foo::xmute(u) + | ^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/vimwiki-core-regression.rs b/src/test/ui/nll/vimwiki-core-regression.rs new file mode 100644 index 000000000..0a4ed7e0a --- /dev/null +++ b/src/test/ui/nll/vimwiki-core-regression.rs @@ -0,0 +1,37 @@ +// check-pass +// +// Regression test from crater run for +// <https://github.com/rust-lang/rust/pull/98109>. + + +pub trait ElementLike {} + +pub struct Located<T> where T: ElementLike { + inner: T, +} + +pub struct BlockElement<'a>(&'a str); + +impl ElementLike for BlockElement<'_> {} + + +pub struct Page<'a> { + /// Comprised of the elements within a page + pub elements: Vec<Located<BlockElement<'a>>>, +} + +impl<'a, __IdxT> std::ops::Index<__IdxT> for Page<'a> where + Vec<Located<BlockElement<'a>>>: std::ops::Index<__IdxT> +{ + type Output = + <Vec<Located<BlockElement<'a>>> as + std::ops::Index<__IdxT>>::Output; + + #[inline] + fn index(&self, idx: __IdxT) -> &Self::Output { + <Vec<Located<BlockElement<'a>>> as + std::ops::Index<__IdxT>>::index(&self.elements, idx) + } +} + +fn main() {} diff --git a/src/test/ui/nll/where_clauses_in_functions.rs b/src/test/ui/nll/where_clauses_in_functions.rs new file mode 100644 index 000000000..826065d02 --- /dev/null +++ b/src/test/ui/nll/where_clauses_in_functions.rs @@ -0,0 +1,15 @@ +#![allow(dead_code)] + +fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) +where + 'a: 'b, +{ + (x, y) +} + +fn bar<'a, 'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { + foo(x, y) + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/where_clauses_in_functions.stderr b/src/test/ui/nll/where_clauses_in_functions.stderr new file mode 100644 index 000000000..afb25e3bc --- /dev/null +++ b/src/test/ui/nll/where_clauses_in_functions.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/where_clauses_in_functions.rs:11:5 + | +LL | fn bar<'a, 'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | foo(x, y) + | ^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/nll/where_clauses_in_structs.rs b/src/test/ui/nll/where_clauses_in_structs.rs new file mode 100644 index 000000000..fae5d3811 --- /dev/null +++ b/src/test/ui/nll/where_clauses_in_structs.rs @@ -0,0 +1,15 @@ +#![allow(dead_code)] + +use std::cell::Cell; + +struct Foo<'a: 'b, 'b> { + x: Cell<&'a u32>, + y: Cell<&'b u32>, +} + +fn bar<'a, 'b>(x: Cell<&'a u32>, y: Cell<&'b u32>) { + Foo { x, y }; + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/src/test/ui/nll/where_clauses_in_structs.stderr b/src/test/ui/nll/where_clauses_in_structs.stderr new file mode 100644 index 000000000..c46cfcb41 --- /dev/null +++ b/src/test/ui/nll/where_clauses_in_structs.stderr @@ -0,0 +1,17 @@ +error: lifetime may not live long enough + --> $DIR/where_clauses_in_structs.rs:11:11 + | +LL | fn bar<'a, 'b>(x: Cell<&'a u32>, y: Cell<&'b u32>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | Foo { x, y }; + | ^ this usage requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `Cell<&u32>`, which makes the generic argument `&u32` invariant + = note: the struct `Cell<T>` is invariant over the parameter `T` + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: aborting due to previous error + |