Skip to content

Commit e415b5e

Browse files
committed
Auto merge of #52948 - davidtwco:issue-52633-later-loop-iteration, r=pnkfelix
NLL: Better Diagnostic When "Later" means "A Future Loop Iteration" Part of #52663. r? @pnkfelix
2 parents 4dae470 + 1863cb7 commit e415b5e

File tree

6 files changed

+92
-13
lines changed

6 files changed

+92
-13
lines changed

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

+84-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use borrow_check::borrow_set::BorrowData;
1212
use borrow_check::nll::region_infer::Cause;
1313
use borrow_check::{Context, MirBorrowckCtxt, WriteKind};
14-
use rustc::mir::Place;
14+
use rustc::mir::{Location, Place, TerminatorKind};
1515
use rustc_errors::DiagnosticBuilder;
1616

1717
mod find_use;
@@ -63,10 +63,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
6363

6464
match find_use::find(mir, regioncx, tcx, region_sub, context.loc) {
6565
Some(Cause::LiveVar(_local, location)) => {
66-
err.span_label(
67-
mir.source_info(location).span,
68-
"borrow later used here".to_string(),
69-
);
66+
if self.is_borrow_location_in_loop(context.loc) {
67+
err.span_label(
68+
mir.source_info(location).span,
69+
"borrow used here in later iteration of loop".to_string(),
70+
);
71+
} else {
72+
err.span_label(
73+
mir.source_info(location).span,
74+
"borrow later used here".to_string(),
75+
);
76+
}
7077
}
7178

7279
Some(Cause::DropVar(local, location)) => match &mir.local_decls[local].name {
@@ -107,4 +114,76 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
107114
}
108115
}
109116
}
117+
118+
/// Check if a borrow location is within a loop.
119+
fn is_borrow_location_in_loop(
120+
&self,
121+
borrow_location: Location,
122+
) -> bool {
123+
let mut visited_locations = Vec::new();
124+
let mut pending_locations = vec![ borrow_location ];
125+
debug!("is_in_loop: borrow_location={:?}", borrow_location);
126+
127+
while let Some(location) = pending_locations.pop() {
128+
debug!("is_in_loop: location={:?} pending_locations={:?} visited_locations={:?}",
129+
location, pending_locations, visited_locations);
130+
if location == borrow_location && visited_locations.contains(&borrow_location) {
131+
// We've managed to return to where we started (and this isn't the start of the
132+
// search).
133+
debug!("is_in_loop: found!");
134+
return true;
135+
}
136+
137+
// Skip locations we've been.
138+
if visited_locations.contains(&location) { continue; }
139+
140+
let block = &self.mir.basic_blocks()[location.block];
141+
if location.statement_index == block.statements.len() {
142+
// Add start location of the next blocks to pending locations.
143+
match block.terminator().kind {
144+
TerminatorKind::Goto { target } => {
145+
pending_locations.push(target.start_location());
146+
},
147+
TerminatorKind::SwitchInt { ref targets, .. } => {
148+
for target in targets {
149+
pending_locations.push(target.start_location());
150+
}
151+
},
152+
TerminatorKind::Drop { target, unwind, .. } |
153+
TerminatorKind::DropAndReplace { target, unwind, .. } |
154+
TerminatorKind::Assert { target, cleanup: unwind, .. } |
155+
TerminatorKind::Yield { resume: target, drop: unwind, .. } |
156+
TerminatorKind::FalseUnwind { real_target: target, unwind, .. } => {
157+
pending_locations.push(target.start_location());
158+
if let Some(unwind) = unwind {
159+
pending_locations.push(unwind.start_location());
160+
}
161+
},
162+
TerminatorKind::Call { ref destination, cleanup, .. } => {
163+
if let Some((_, destination)) = destination {
164+
pending_locations.push(destination.start_location());
165+
}
166+
if let Some(cleanup) = cleanup {
167+
pending_locations.push(cleanup.start_location());
168+
}
169+
},
170+
TerminatorKind::FalseEdges { real_target, ref imaginary_targets, .. } => {
171+
pending_locations.push(real_target.start_location());
172+
for target in imaginary_targets {
173+
pending_locations.push(target.start_location());
174+
}
175+
},
176+
_ => {},
177+
}
178+
} else {
179+
// Add the next statement to pending locations.
180+
pending_locations.push(location.successor_within_block());
181+
}
182+
183+
// Keep track of where we have visited.
184+
visited_locations.push(location);
185+
}
186+
187+
false
188+
}
110189
}

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-
| ----------- borrow later used here
20+
| ----------- borrow used here in later iteration of loop
2121

2222
error: aborting due to 2 previous errors
2323

src/test/ui/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
LL | //~^ ERROR `line` does not live long enough
77
LL | println!("accumulator before add_assign {:?}", acc.map);
8-
| ------- borrow later used here
8+
| ------- borrow used here in later iteration of loop
99
...
1010
LL | }
1111
| - `line` dropped here while still borrowed

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-
| ------- borrow later used here
10+
| ------- borrow used here in later iteration of loop
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:21:13
33
|
44
LL | let x = 1 + *p;
5-
| -- borrow later used here
5+
| -- borrow used here in later iteration of loop
66
LL | p = &x;
77
| ^^ borrowed value does not live long enough
88
LL | }

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | while x < 10 { //~ ERROR cannot use `x` because it was mutably borrowed
77
| ^ use of borrowed `x`
88
LL | let mut z = x; //~ ERROR cannot use `x` because it was mutably borrowed
99
LL | _y.push(&mut z);
10-
| -- borrow later used here
10+
| -- borrow used here in later iteration of loop
1111

1212
error[E0503]: cannot use `x` because it was mutably borrowed
1313
--> $DIR/regions-escape-loop-via-vec.rs:16:21
@@ -18,15 +18,15 @@ LL | while x < 10 { //~ ERROR cannot use `x` because it was mutably borrowed
1818
LL | let mut z = x; //~ ERROR cannot use `x` because it was mutably borrowed
1919
| ^ use of borrowed `x`
2020
LL | _y.push(&mut z);
21-
| -- borrow later used here
21+
| -- borrow used here in later iteration of loop
2222

2323
error[E0597]: `z` does not live long enough
2424
--> $DIR/regions-escape-loop-via-vec.rs:17:17
2525
|
2626
LL | _y.push(&mut z);
2727
| -- ^^^^^^ borrowed value does not live long enough
2828
| |
29-
| borrow later used here
29+
| borrow used here in later iteration of loop
3030
...
3131
LL | }
3232
| - `z` dropped here while still borrowed
@@ -38,7 +38,7 @@ LL | let mut _y = vec![&mut x];
3838
| ------ borrow of `x` occurs here
3939
...
4040
LL | _y.push(&mut z);
41-
| -- borrow later used here
41+
| -- borrow used here in later iteration of loop
4242
LL | //~^ ERROR `z` does not live long enough
4343
LL | x += 1; //~ ERROR cannot assign
4444
| ^^^^^^ use of borrowed `x`

0 commit comments

Comments
 (0)