Skip to content

Add a lot of NLL known-bug tests #145053

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0499]: cannot borrow `self.iter` as mutable more than once at a time
--> $DIR/filtering-lending-iterator-issue-92985.rs:49:32
|
LL | fn next(&mut self) -> Option<I::Item<'_>> {
| - let's call the lifetime of this reference `'1`
LL | while let Some(item) = self.iter.next() {
| ^^^^^^^^^ `self.iter` was mutably borrowed here in the previous iteration of the loop
LL | if (self.predicate)(&item) {
LL | return Some(item);
| ---------- returning this value requires that `self.iter` is borrowed for `'1`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0499`.
56 changes: 56 additions & 0 deletions tests/ui/nll/polonius/filtering-lending-iterator-issue-92985.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#![crate_type = "lib"]

// This test is an example of a filtering lending iterator with GATs from #92985 (that is similar to
// NLL problem case #3) to ensure it "works" with the polonius alpha analysis as with the datalog
// implementation.
//
// The polonius analysis only changes how the `Filter::next` function is borrowcked, not the bounds
// on the predicate from using the GAT. So even if the #92985 limitation is removed, the unrelated
// 'static limitation on the predicate argument is still there, and the pattern is still impractical
// to use in the real world.

//@ ignore-compare-mode-polonius (explicit revisions)
//@ revisions: nll polonius legacy
//@ [nll] known-bug: #92985
//@ [polonius] check-pass
//@ [polonius] compile-flags: -Z polonius=next
//@ [legacy] check-pass
//@ [legacy] compile-flags: -Z polonius=legacy

trait LendingIterator {
type Item<'a>
where
Self: 'a;
fn next(&mut self) -> Option<Self::Item<'_>>;

fn filter<P>(self, predicate: P) -> Filter<Self, P>
where
Self: Sized,
P: FnMut(&Self::Item<'_>) -> bool,
{
Filter { iter: self, predicate }
}
}

pub struct Filter<I, P> {
iter: I,
predicate: P,
}
impl<I: LendingIterator, P> LendingIterator for Filter<I, P>
where
P: FnMut(&I::Item<'_>) -> bool,
{
type Item<'a>
= I::Item<'a>
where
Self: 'a;

fn next(&mut self) -> Option<I::Item<'_>> {
while let Some(item) = self.iter.next() {
if (self.predicate)(&item) {
return Some(item);
}
}
return None;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0499]: cannot borrow `*elements` as mutable more than once at a time
--> $DIR/iterating-updating-cursor-issue-108704.rs:40:26
|
LL | for (idx, el) in elements.iter_mut().enumerate() {
| ^^^^^^^^
| |
| `*elements` was mutably borrowed here in the previous iteration of the loop
| first borrow used here, in later iteration of loop

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0499`.
47 changes: 47 additions & 0 deletions tests/ui/nll/polonius/iterating-updating-cursor-issue-108704.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#![crate_type = "lib"]

// An example from #108704 of the linked-list cursor-like pattern of #46859/#48001.

//@ ignore-compare-mode-polonius (explicit revisions)
//@ revisions: nll polonius legacy
//@ [nll] known-bug: #108704
//@ [polonius] check-pass
//@ [polonius] compile-flags: -Z polonius=next
//@ [legacy] check-pass
//@ [legacy] compile-flags: -Z polonius=legacy

struct Root {
children: Vec<Node>,
}

struct Node {
name: String,
children: Vec<Node>,
}

fn merge_tree_ok(root: &mut Root, path: Vec<String>) {
let mut elements = &mut root.children;

for p in path.iter() {
for (idx, el) in elements.iter_mut().enumerate() {
if el.name == *p {
elements = &mut elements[idx].children;
break;
}
}
}
}

// NLLs fail here
fn merge_tree_ko(root: &mut Root, path: Vec<String>) {
let mut elements = &mut root.children;

for p in path.iter() {
for (idx, el) in elements.iter_mut().enumerate() {
if el.name == *p {
elements = &mut el.children;
break;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0499]: cannot borrow `p.0` as mutable more than once at a time
--> $DIR/iterating-updating-cursor-issue-57165.rs:29:20
|
LL | while let Some(now) = p {
| ^^^ - first borrow used here, in later iteration of loop
| |
| `p.0` was mutably borrowed here in the previous iteration of the loop

error[E0503]: cannot use `*p` because it was mutably borrowed
--> $DIR/iterating-updating-cursor-issue-57165.rs:29:27
|
LL | while let Some(now) = p {
| --- ^
| | |
| | use of borrowed `p.0`
| | borrow later used here
| `p.0` is borrowed here

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0499, E0503.
For more information about an error, try `rustc --explain E0499`.
44 changes: 44 additions & 0 deletions tests/ui/nll/polonius/iterating-updating-cursor-issue-57165.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#![crate_type = "lib"]

// An example from #57165 of the linked-list cursor-like pattern of #46859/#48001.

//@ ignore-compare-mode-polonius (explicit revisions)
//@ revisions: nll polonius legacy
//@ [nll] known-bug: #57165
//@ [polonius] check-pass
//@ [polonius] compile-flags: -Z polonius=next
//@ [legacy] check-pass
//@ [legacy] compile-flags: -Z polonius=legacy

struct X {
next: Option<Box<X>>,
}

fn no_control_flow() {
let mut b = Some(Box::new(X { next: None }));
let mut p = &mut b;
while let Some(now) = p {
p = &mut now.next;
}
}

// NLLs fail here
fn conditional() {
let mut b = Some(Box::new(X { next: None }));
let mut p = &mut b;
while let Some(now) = p {
if true {
p = &mut now.next;
}
}
}

fn conditional_with_indirection() {
let mut b = Some(Box::new(X { next: None }));
let mut p = &mut b;
while let Some(now) = p {
if true {
p = &mut p.as_mut().unwrap().next;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0506]: cannot assign to `*node_ref` because it is borrowed
--> $DIR/iterating-updating-cursor-issue-63908.rs:42:5
|
LL | fn remove_last_node_iterative<T>(mut node_ref: &mut List<T>) {
| - let's call the lifetime of this reference `'1`
LL | loop {
LL | let next_ref = &mut node_ref.as_mut().unwrap().next;
| -------- `*node_ref` is borrowed here
...
LL | node_ref = next_ref;
| ------------------- assignment requires that `*node_ref` is borrowed for `'1`
...
LL | *node_ref = None;
| ^^^^^^^^^ `*node_ref` is assigned to here but it was already borrowed

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0506`.
43 changes: 43 additions & 0 deletions tests/ui/nll/polonius/iterating-updating-cursor-issue-63908.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#![crate_type = "lib"]

// An example from #63908 of the linked-list cursor-like pattern of #46859/#48001.

//@ ignore-compare-mode-polonius (explicit revisions)
//@ revisions: nll polonius legacy
//@ [nll] known-bug: #63908
//@ [polonius] check-pass
//@ [polonius] compile-flags: -Z polonius=next
//@ [legacy] check-pass
//@ [legacy] compile-flags: -Z polonius=legacy

struct Node<T> {
value: T,
next: Option<Box<Self>>,
}

type List<T> = Option<Box<Node<T>>>;

fn remove_last_node_recursive<T>(node_ref: &mut List<T>) {
let next_ref = &mut node_ref.as_mut().unwrap().next;

if next_ref.is_some() {
remove_last_node_recursive(next_ref);
} else {
*node_ref = None;
}
}

// NLLs fail here
fn remove_last_node_iterative<T>(mut node_ref: &mut List<T>) {
loop {
let next_ref = &mut node_ref.as_mut().unwrap().next;

if next_ref.is_some() {
node_ref = next_ref;
} else {
break;
}
}

*node_ref = None;
}
17 changes: 17 additions & 0 deletions tests/ui/nll/polonius/nll-problem-case-3-issue-112087.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0506]: cannot assign to `*opt` because it is borrowed
--> $DIR/nll-problem-case-3-issue-112087.rs:23:5
|
LL | fn issue_112087<'a>(opt: &'a mut Option<i32>, b: bool) -> Result<&'a mut Option<i32>, &'a mut i32> {
| -- lifetime `'a` defined here
LL | if let Some(v) = opt {
| - `*opt` is borrowed here
LL | if b {
LL | return Err(v);
| ------ returning this value requires that `opt.0` is borrowed for `'a`
...
LL | *opt = None;
| ^^^^^^^^^^^ `*opt` is assigned to here but it was already borrowed

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0506`.
25 changes: 25 additions & 0 deletions tests/ui/nll/polonius/nll-problem-case-3-issue-112087.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![crate_type = "lib"]

// This is part of a collection of regression tests related to the NLL problem case 3 that was
// deferred from the implementation of the NLL RFC, and left to be implemented by polonius. They are
// from open issues, e.g. tagged fixed-by-polonius, to ensure that the polonius alpha analysis does
// handle them, as does the datalog implementation.

//@ ignore-compare-mode-polonius (explicit revisions)
//@ revisions: nll polonius legacy
//@ [nll] known-bug: #112087
//@ [polonius] check-pass
//@ [polonius] compile-flags: -Z polonius=next
//@ [legacy] check-pass
//@ [legacy] compile-flags: -Z polonius=legacy

fn issue_112087<'a>(opt: &'a mut Option<i32>, b: bool) -> Result<&'a mut Option<i32>, &'a mut i32> {
if let Some(v) = opt {
if b {
return Err(v);
}
}

*opt = None;
return Ok(opt);
}
16 changes: 16 additions & 0 deletions tests/ui/nll/polonius/nll-problem-case-3-issue-123839.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0506]: cannot assign to `self.status` because it is borrowed
--> $DIR/nll-problem-case-3-issue-123839.rs:37:9
|
LL | fn foo(self: &mut Self) -> Result<(), &str> {
| - let's call the lifetime of this reference `'1`
LL | self.bar()?; // rust reports this line conflicts with the next line
| -----------
| |
| `self.status` is borrowed here
| returning this value requires that `*self` is borrowed for `'1`
LL | self.status = 1; // and this line is the victim
| ^^^^^^^^^^^^^^^ `self.status` is assigned to here but it was already borrowed

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0506`.
40 changes: 40 additions & 0 deletions tests/ui/nll/polonius/nll-problem-case-3-issue-123839.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![crate_type = "lib"]

// This is part of a collection of regression tests related to the NLL problem case 3 that was
// deferred from the implementation of the NLL RFC, and left to be implemented by polonius. They are
// from open issues, e.g. tagged fixed-by-polonius, to ensure that the polonius alpha analysis does
// handle them, as does the datalog implementation.

//@ ignore-compare-mode-polonius (explicit revisions)
//@ revisions: nll polonius legacy
//@ [nll] known-bug: #123839
//@ [polonius] check-pass
//@ [polonius] compile-flags: -Z polonius=next
//@ [legacy] check-pass
//@ [legacy] compile-flags: -Z polonius=legacy

struct Foo {
val: i32,
status: i32,
err_str: String,
}

impl Foo {
fn bar(self: &mut Self) -> Result<(), &str> {
if self.val == 0 {
self.status = -1;
Err("val is zero")
} else if self.val < 0 {
self.status = -2;
self.err_str = format!("unexpected negative val {}", self.val);
Err(&self.err_str)
} else {
Ok(())
}
}
fn foo(self: &mut Self) -> Result<(), &str> {
self.bar()?; // rust reports this line conflicts with the next line
self.status = 1; // and this line is the victim
Ok(())
}
}
17 changes: 17 additions & 0 deletions tests/ui/nll/polonius/nll-problem-case-3-issue-124070.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0502]: cannot borrow `self.field` as immutable because it is also borrowed as mutable
--> $DIR/nll-problem-case-3-issue-124070.rs:28:16
|
LL | fn f(&mut self) -> &str {
| - let's call the lifetime of this reference `'1`
LL | let a = &mut self.field;
| --------------- mutable borrow occurs here
...
LL | return a;
| - returning this value requires that `self.field` is borrowed for `'1`
...
LL | return &self.field;
| ^^^^^^^^^^^ immutable borrow occurs here

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0502`.
Loading
Loading