summaryrefslogtreecommitdiffstats
path: root/src/test/ui/nll/polonius
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/ui/nll/polonius')
-rw-r--r--src/test/ui/nll/polonius/assignment-kills-loans.rs87
-rw-r--r--src/test/ui/nll/polonius/assignment-to-differing-field.rs49
-rw-r--r--src/test/ui/nll/polonius/assignment-to-differing-field.stderr51
-rw-r--r--src/test/ui/nll/polonius/call-kills-loans.rs23
-rw-r--r--src/test/ui/nll/polonius/issue-46589.rs31
-rw-r--r--src/test/ui/nll/polonius/polonius-smoke-test.rs46
-rw-r--r--src/test/ui/nll/polonius/polonius-smoke-test.stderr43
-rw-r--r--src/test/ui/nll/polonius/storagedead-kills-loans.rs28
-rw-r--r--src/test/ui/nll/polonius/subset-relations.rs29
-rw-r--r--src/test/ui/nll/polonius/subset-relations.stderr14
10 files changed, 401 insertions, 0 deletions
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
+