Skip to content

Commit 801c3f0

Browse files
committed
Fix erroneous loop diagnostic in nll
This commit fixes the logic of detecting when a use happen in a later iteration of where a borrow was defined Fixes #53773
1 parent cbc865d commit 801c3f0

13 files changed

+205
-73
lines changed

src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs

+123-56
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::VecDeque;
2+
13
use crate::borrow_check::borrow_set::BorrowData;
24
use crate::borrow_check::error_reporting::UseSpans;
35
use crate::borrow_check::nll::ConstraintDescription;
@@ -9,6 +11,7 @@ use rustc::mir::{
911
Place, Projection, ProjectionElem, Rvalue, Statement, StatementKind,
1012
TerminatorKind
1113
};
14+
use rustc_data_structures::fx::FxHashSet;
1215
use rustc_errors::DiagnosticBuilder;
1316
use syntax_pos::Span;
1417

@@ -220,7 +223,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
220223
let spans = self.move_spans(&Place::Local(local), location)
221224
.or_else(|| self.borrow_spans(span, location));
222225

223-
if self.is_borrow_location_in_loop(context.loc) {
226+
let borrow_location = context.loc;
227+
if self.is_use_in_later_iteration_of_loop(borrow_location, location) {
224228
let later_use = self.later_use_kind(borrow, spans, location);
225229
BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1)
226230
} else {
@@ -285,76 +289,139 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
285289
}
286290
}
287291

288-
/// Checks if a borrow location is within a loop.
289-
fn is_borrow_location_in_loop(
292+
/// true if `borrow_location` can reach `use_location` by going through a loop and
293+
/// `use_location` is also inside of that loop
294+
fn is_use_in_later_iteration_of_loop(
290295
&self,
291296
borrow_location: Location,
297+
use_location: Location,
292298
) -> bool {
293-
let mut visited_locations = Vec::new();
294-
let mut pending_locations = vec![ borrow_location ];
295-
debug!("is_in_loop: borrow_location={:?}", borrow_location);
296-
297-
while let Some(location) = pending_locations.pop() {
298-
debug!("is_in_loop: location={:?} pending_locations={:?} visited_locations={:?}",
299-
location, pending_locations, visited_locations);
300-
if location == borrow_location && visited_locations.contains(&borrow_location) {
301-
// We've managed to return to where we started (and this isn't the start of the
302-
// search).
303-
debug!("is_in_loop: found!");
304-
return true;
305-
}
299+
let back_edge = self.reach_through_backedge(borrow_location, use_location);
300+
back_edge.map_or(false, |back_edge| {
301+
self.can_reach_head_of_loop(use_location, back_edge)
302+
})
303+
}
306304

307-
// Skip locations we've been.
308-
if visited_locations.contains(&location) { continue; }
305+
/// Returns the outmost back edge if `from` location can reach `to` location passing through
306+
/// that back edge
307+
fn reach_through_backedge(&self, from: Location, to: Location) -> Option<Location> {
308+
let mut visited_locations = FxHashSet::default();
309+
let mut pending_locations = VecDeque::new();
310+
visited_locations.insert(from);
311+
pending_locations.push_back(from);
312+
debug!("reach_through_backedge: from={:?} to={:?}", from, to,);
313+
314+
let mut outmost_back_edge = None;
315+
while let Some(location) = pending_locations.pop_front() {
316+
debug!(
317+
"reach_through_backedge: location={:?} outmost_back_edge={:?}
318+
pending_locations={:?} visited_locations={:?}",
319+
location, outmost_back_edge, pending_locations, visited_locations
320+
);
321+
322+
if location == to && outmost_back_edge.is_some() {
323+
// We've managed to reach the use location
324+
debug!("reach_through_backedge: found!");
325+
return outmost_back_edge;
326+
}
309327

310328
let block = &self.mir.basic_blocks()[location.block];
311-
if location.statement_index == block.statements.len() {
312-
// Add start location of the next blocks to pending locations.
313-
match block.terminator().kind {
314-
TerminatorKind::Goto { target } => {
315-
pending_locations.push(target.start_location());
316-
},
317-
TerminatorKind::SwitchInt { ref targets, .. } => {
318-
pending_locations.extend(
319-
targets.into_iter().map(|target| target.start_location()));
320-
},
321-
TerminatorKind::Drop { target, unwind, .. } |
322-
TerminatorKind::DropAndReplace { target, unwind, .. } |
323-
TerminatorKind::Assert { target, cleanup: unwind, .. } |
324-
TerminatorKind::Yield { resume: target, drop: unwind, .. } |
325-
TerminatorKind::FalseUnwind { real_target: target, unwind, .. } => {
326-
pending_locations.push(target.start_location());
327-
if let Some(unwind) = unwind {
328-
pending_locations.push(unwind.start_location());
329-
}
330-
},
331-
TerminatorKind::Call { ref destination, cleanup, .. } => {
332-
if let Some((_, destination)) = destination {
333-
pending_locations.push(destination.start_location());
334-
}
335-
if let Some(cleanup) = cleanup {
336-
pending_locations.push(cleanup.start_location());
337-
}
338-
},
339-
TerminatorKind::FalseEdges { real_target, ref imaginary_targets, .. } => {
340-
pending_locations.push(real_target.start_location());
341-
pending_locations.extend(
342-
imaginary_targets.into_iter().map(|target| target.start_location()));
343-
},
344-
_ => {},
329+
330+
if location.statement_index < block.statements.len() {
331+
let successor = location.successor_within_block();
332+
if visited_locations.insert(successor) {
333+
pending_locations.push_back(successor);
345334
}
346335
} else {
347-
// Add the next statement to pending locations.
348-
pending_locations.push(location.successor_within_block());
336+
pending_locations.extend(
337+
block
338+
.terminator()
339+
.successors()
340+
.map(|bb| Location {
341+
statement_index: 0,
342+
block: *bb,
343+
})
344+
.filter(|s| visited_locations.insert(*s))
345+
.map(|s| {
346+
if self.is_back_edge(location, s) {
347+
match outmost_back_edge {
348+
None => {
349+
outmost_back_edge = Some(location);
350+
}
351+
352+
Some(back_edge)
353+
if location.dominates(back_edge, &self.dominators) =>
354+
{
355+
outmost_back_edge = Some(location);
356+
}
357+
358+
Some(_) => {}
359+
}
360+
}
361+
362+
s
363+
}),
364+
);
349365
}
366+
}
367+
368+
None
369+
}
370+
371+
/// true if `from` location can reach `loop_head` location and `loop_head` dominates all the
372+
/// intermediate nodes
373+
fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool {
374+
self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default())
375+
}
350376

351-
// Keep track of where we have visited.
352-
visited_locations.push(location);
377+
fn find_loop_head_dfs(
378+
&self,
379+
from: Location,
380+
loop_head: Location,
381+
visited_locations: &mut FxHashSet<Location>,
382+
) -> bool {
383+
visited_locations.insert(from);
384+
385+
if from == loop_head {
386+
return true;
387+
}
388+
389+
if loop_head.dominates(from, &self.dominators) {
390+
let block = &self.mir.basic_blocks()[from.block];
391+
392+
if from.statement_index < block.statements.len() {
393+
let successor = from.successor_within_block();
394+
395+
if !visited_locations.contains(&successor)
396+
&& self.find_loop_head_dfs(successor, loop_head, visited_locations)
397+
{
398+
return true;
399+
}
400+
} else {
401+
for bb in block.terminator().successors() {
402+
let successor = Location {
403+
statement_index: 0,
404+
block: *bb,
405+
};
406+
407+
if !visited_locations.contains(&successor)
408+
&& self.find_loop_head_dfs(successor, loop_head, visited_locations)
409+
{
410+
return true;
411+
}
412+
}
413+
}
353414
}
354415

355416
false
356417
}
357418

419+
/// True if an edge `source -> target` is a backedge -- in other words, if the target
420+
/// dominates the source.
421+
fn is_back_edge(&self, source: Location, target: Location) -> bool {
422+
target.dominates(source, &self.mir.dominators())
423+
}
424+
358425
/// Determine how the borrow was later used.
359426
fn later_use_kind(
360427
&self,

src/test/ui/borrowck/borrowck-for-loop-head-linkage.nll.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | for &x in &vector {
55
| -------
66
| |
77
| immutable borrow occurs here
8-
| immutable borrow used here, in later iteration of loop
8+
| immutable borrow later used here
99
LL | let cap = vector.capacity();
1010
LL | vector.extend(repeat(0)); //~ ERROR cannot borrow
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
@@ -17,7 +17,7 @@ LL | for &x in &vector {
1717
| -------
1818
| |
1919
| immutable borrow occurs here
20-
| immutable borrow used here, in later iteration of loop
20+
| immutable borrow later used here
2121
...
2222
LL | vector[1] = 5; //~ ERROR cannot borrow
2323
| ^^^^^^ mutable borrow occurs here

src/test/ui/borrowck/borrowck-lend-flow-loop.nll.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ LL | borrow(&*v); //[ast]~ ERROR cannot borrow
88
| ^^^ immutable borrow occurs here
99
...
1010
LL | *x = box 5;
11-
| -- mutable borrow used here, in later iteration of loop
11+
| -- mutable borrow later used here
1212

1313
error[E0502]: cannot borrow `*v` as immutable because it is also borrowed as mutable
1414
--> $DIR/borrowck-lend-flow-loop.rs:109:16
1515
|
1616
LL | **x += 1;
17-
| -------- mutable borrow used here, in later iteration of loop
17+
| -------- mutable borrow later used here
1818
LL | borrow(&*v); //[ast]~ ERROR cannot borrow
1919
| ^^^ immutable borrow occurs here
2020
...

src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.ast.nll.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time
44
LL | 1 => { addr.push(&mut x); } //[ast]~ ERROR [E0499]
55
| ---- ^^^^^^ second mutable borrow occurs here
66
| |
7-
| first borrow used here, in later iteration of loop
7+
| first borrow later used here
88
...
99
LL | _ => { addr.push(&mut x); } //[ast]~ ERROR [E0499]
1010
| ------ first mutable borrow occurs here
@@ -13,7 +13,7 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time
1313
--> $DIR/borrowck-mut-borrow-linear-errors.rs:15:30
1414
|
1515
LL | 1 => { addr.push(&mut x); } //[ast]~ ERROR [E0499]
16-
| ---- first borrow used here, in later iteration of loop
16+
| ---- first borrow later used here
1717
LL | //[mir]~^ ERROR [E0499]
1818
LL | 2 => { addr.push(&mut x); } //[ast]~ ERROR [E0499]
1919
| ^^^^^^ second mutable borrow occurs here

src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.mir.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time
44
LL | 1 => { addr.push(&mut x); } //[ast]~ ERROR [E0499]
55
| ---- ^^^^^^ second mutable borrow occurs here
66
| |
7-
| first borrow used here, in later iteration of loop
7+
| first borrow later used here
88
...
99
LL | _ => { addr.push(&mut x); } //[ast]~ ERROR [E0499]
1010
| ------ first mutable borrow occurs here
@@ -13,7 +13,7 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time
1313
--> $DIR/borrowck-mut-borrow-linear-errors.rs:15:30
1414
|
1515
LL | 1 => { addr.push(&mut x); } //[ast]~ ERROR [E0499]
16-
| ---- first borrow used here, in later iteration of loop
16+
| ---- first borrow later used here
1717
LL | //[mir]~^ ERROR [E0499]
1818
LL | 2 => { addr.push(&mut x); } //[ast]~ ERROR [E0499]
1919
| ^^^^^^ second mutable borrow occurs here

src/test/ui/borrowck/mut-borrow-outside-loop.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ LL | let inner_second = &mut inner_void; //~ ERROR cannot borrow
1717
| ^^^^^^^^^^^^^^^ second mutable borrow occurs here
1818
LL | inner_second.use_mut();
1919
LL | inner_first.use_mut();
20-
| ----------- first borrow used here, in later iteration of loop
20+
| ----------- first borrow later used here
2121

2222
error: aborting due to 2 previous errors
2323

src/test/ui/issues/issue-52126-assign-op-invariance.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | let v: Vec<&str> = line.split_whitespace().collect();
55
| ^^^^ borrowed value does not live long enough
66
...
77
LL | acc += cnt2;
8-
| --- borrow used here, in later iteration of loop
8+
| --- borrow later used here
99
...
1010
LL | }
1111
| - `line` dropped here while still borrowed

src/test/ui/nll/issue-53773.rs

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#![feature(nll)]
2+
3+
struct Archive;
4+
struct ArchiveIterator<'a> {
5+
x: &'a Archive,
6+
}
7+
struct ArchiveChild<'a> {
8+
x: &'a Archive,
9+
}
10+
11+
struct A {
12+
raw: &'static mut Archive,
13+
}
14+
struct Iter<'a> {
15+
raw: &'a mut ArchiveIterator<'a>,
16+
}
17+
struct C<'a> {
18+
raw: &'a mut ArchiveChild<'a>,
19+
}
20+
21+
impl A {
22+
pub fn iter(&self) -> Iter<'_> {
23+
panic!()
24+
}
25+
}
26+
impl Drop for A {
27+
fn drop(&mut self) {}
28+
}
29+
impl<'a> Drop for C<'a> {
30+
fn drop(&mut self) {}
31+
}
32+
33+
impl<'a> Iterator for Iter<'a> {
34+
type Item = C<'a>;
35+
fn next(&mut self) -> Option<C<'a>> {
36+
panic!()
37+
}
38+
}
39+
40+
fn error(archive: &A) {
41+
let mut members: Vec<&mut ArchiveChild<'_>> = vec![];
42+
for child in archive.iter() {
43+
members.push(child.raw);
44+
//~^ ERROR borrow may still be in use when destructor runs [E0713]
45+
}
46+
members.len();
47+
}
48+
49+
fn main() {}

src/test/ui/nll/issue-53773.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0713]: borrow may still be in use when destructor runs
2+
--> $DIR/issue-53773.rs:43:22
3+
|
4+
LL | members.push(child.raw);
5+
| ^^^^^^^^^
6+
LL | //~^ ERROR borrow may still be in use when destructor runs [E0713]
7+
LL | }
8+
| - here, drop of `child` needs exclusive access to `*child.raw`, because the type `C<'_>` implements the `Drop` trait
9+
LL | members.len();
10+
| ------- borrow later used here
11+
|
12+
= note: consider using a `let` binding to create a longer lived value
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0713`.

src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | foo.mutate();
77
| ^^^^^^^^^^^^ mutable borrow occurs here
88
LL | //~^ ERROR cannot borrow `foo` as mutable
99
LL | println!("foo={:?}", *string);
10-
| ------- immutable borrow used here, in later iteration of loop
10+
| ------- immutable borrow later used here
1111

1212
error: aborting due to previous error
1313

src/test/ui/span/regions-escape-loop-via-variable.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0597]: `x` does not live long enough
22
--> $DIR/regions-escape-loop-via-variable.rs:11:13
33
|
44
LL | let x = 1 + *p;
5-
| -- borrow used here, in later iteration of loop
5+
| -- borrow later used here
66
LL | p = &x;
77
| ^^ borrowed value does not live long enough
88
LL | }

0 commit comments

Comments
 (0)