From 218caa410aa38c29984be31a5229b9fa717560ee Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:13 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- tests/ui/nll/polonius/assignment-kills-loans.rs | 87 ++++++++++++++++++++++ .../nll/polonius/assignment-to-differing-field.rs | 49 ++++++++++++ .../polonius/assignment-to-differing-field.stderr | 51 +++++++++++++ tests/ui/nll/polonius/call-kills-loans.rs | 23 ++++++ tests/ui/nll/polonius/issue-46589.rs | 31 ++++++++ tests/ui/nll/polonius/polonius-smoke-test.rs | 46 ++++++++++++ tests/ui/nll/polonius/polonius-smoke-test.stderr | 43 +++++++++++ tests/ui/nll/polonius/storagedead-kills-loans.rs | 28 +++++++ tests/ui/nll/polonius/subset-relations.rs | 29 ++++++++ tests/ui/nll/polonius/subset-relations.stderr | 14 ++++ 10 files changed, 401 insertions(+) create mode 100644 tests/ui/nll/polonius/assignment-kills-loans.rs create mode 100644 tests/ui/nll/polonius/assignment-to-differing-field.rs create mode 100644 tests/ui/nll/polonius/assignment-to-differing-field.stderr create mode 100644 tests/ui/nll/polonius/call-kills-loans.rs create mode 100644 tests/ui/nll/polonius/issue-46589.rs create mode 100644 tests/ui/nll/polonius/polonius-smoke-test.rs create mode 100644 tests/ui/nll/polonius/polonius-smoke-test.stderr create mode 100644 tests/ui/nll/polonius/storagedead-kills-loans.rs create mode 100644 tests/ui/nll/polonius/subset-relations.rs create mode 100644 tests/ui/nll/polonius/subset-relations.stderr (limited to 'tests/ui/nll/polonius') diff --git a/tests/ui/nll/polonius/assignment-kills-loans.rs b/tests/ui/nll/polonius/assignment-kills-loans.rs new file mode 100644 index 000000000..696bf61ce --- /dev/null +++ b/tests/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 assignments, 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 { + value: T, + next: Option>>, +} + +// Assignment to a local: the `list` assignment should clear the existing +// borrows of `list.value` and `list.next` +fn assignment_to_local(mut list: &mut List) -> 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(mut list: Box<&mut List>) -> 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(mut list: (&mut List,)) -> 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(mut list: (Box<&mut List>,)) -> 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( + mut list: (((((Box<&mut List>,),),),),), +) -> 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/tests/ui/nll/polonius/assignment-to-differing-field.rs b/tests/ui/nll/polonius/assignment-to-differing-field.rs new file mode 100644 index 000000000..7ec3b9049 --- /dev/null +++ b/tests/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 { + value: T, + next: Option>>, +} + + +fn assignment_to_field_projection<'a, T>( + mut list: (&'a mut List, &'a mut List), +) -> 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>, Box<&'a mut List>),),),),), +) -> 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/tests/ui/nll/polonius/assignment-to-differing-field.stderr b/tests/ui/nll/polonius/assignment-to-differing-field.stderr new file mode 100644 index 000000000..afa1b9344 --- /dev/null +++ b/tests/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/tests/ui/nll/polonius/call-kills-loans.rs b/tests/ui/nll/polonius/call-kills-loans.rs new file mode 100644 index 000000000..f430e9211 --- /dev/null +++ b/tests/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/tests/ui/nll/polonius/issue-46589.rs b/tests/ui/nll/polonius/issue-46589.rs new file mode 100644 index 000000000..648280a1d --- /dev/null +++ b/tests/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/tests/ui/nll/polonius/polonius-smoke-test.rs b/tests/ui/nll/polonius/polonius-smoke-test.rs new file mode 100644 index 000000000..c4344af71 --- /dev/null +++ b/tests/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/tests/ui/nll/polonius/polonius-smoke-test.stderr b/tests/ui/nll/polonius/polonius-smoke-test.stderr new file mode 100644 index 000000000..fa1a6a9c9 --- /dev/null +++ b/tests/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/tests/ui/nll/polonius/storagedead-kills-loans.rs b/tests/ui/nll/polonius/storagedead-kills-loans.rs new file mode 100644 index 000000000..669e077de --- /dev/null +++ b/tests/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/tests/ui/nll/polonius/subset-relations.rs b/tests/ui/nll/polonius/subset-relations.rs new file mode 100644 index 000000000..f223ab177 --- /dev/null +++ b/tests/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/tests/ui/nll/polonius/subset-relations.stderr b/tests/ui/nll/polonius/subset-relations.stderr new file mode 100644 index 000000000..6df5563ea --- /dev/null +++ b/tests/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 + -- cgit v1.2.3