Skip to content

Commit b3bf931

Browse files
author
Yan Chen
committed
Fix missing explanation of where borrowed reference is used when the borrow occurs in loop iteration
1 parent 432abd8 commit b3bf931

File tree

5 files changed

+63
-142
lines changed

5 files changed

+63
-142
lines changed

Diff for: compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

+23-133
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
//! Print diagnostics to explain why values are borrowed.
22
3-
use std::collections::VecDeque;
4-
5-
use rustc_data_structures::fx::FxHashSet;
63
use rustc_errors::{Applicability, Diagnostic};
74
use rustc_index::vec::IndexVec;
85
use rustc_infer::infer::NllRegionVariableOrigin;
@@ -359,19 +356,37 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
359356
let borrow_region_vid = borrow.region;
360357
debug!(?borrow_region_vid);
361358

362-
let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
359+
let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
363360
debug!(?region_sub);
364361

365-
match find_use::find(body, regioncx, tcx, region_sub, location) {
362+
let mut use_location = location;
363+
let mut use_in_later_iteration_of_loop = false;
364+
365+
if region_sub == borrow_region_vid {
366+
// When `region_sub` is the same as `borrow_region_vid` (the location where the borrow is
367+
// issued is the same location that invalidates the reference), this is likely a loop iteration
368+
// - in this case, try using the loop terminator location in `find_sub_region_live_at`.
369+
if let Some(loop_terminator_location) =
370+
regioncx.find_loop_terminator_location(borrow.region, body)
371+
{
372+
region_sub = self
373+
.regioncx
374+
.find_sub_region_live_at(borrow_region_vid, loop_terminator_location);
375+
debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub);
376+
use_location = loop_terminator_location;
377+
use_in_later_iteration_of_loop = true;
378+
}
379+
}
380+
381+
match find_use::find(body, regioncx, tcx, region_sub, use_location) {
366382
Some(Cause::LiveVar(local, location)) => {
367383
let span = body.source_info(location).span;
368384
let spans = self
369385
.move_spans(Place::from(local).as_ref(), location)
370386
.or_else(|| self.borrow_spans(span, location));
371387

372-
let borrow_location = location;
373-
if self.is_use_in_later_iteration_of_loop(borrow_location, location) {
374-
let later_use = self.later_use_kind(borrow, spans, location);
388+
if use_in_later_iteration_of_loop {
389+
let later_use = self.later_use_kind(borrow, spans, use_location);
375390
BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2)
376391
} else {
377392
// Check if the location represents a `FakeRead`, and adapt the error
@@ -425,131 +440,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
425440
}
426441
}
427442

428-
/// true if `borrow_location` can reach `use_location` by going through a loop and
429-
/// `use_location` is also inside of that loop
430-
fn is_use_in_later_iteration_of_loop(
431-
&self,
432-
borrow_location: Location,
433-
use_location: Location,
434-
) -> bool {
435-
let back_edge = self.reach_through_backedge(borrow_location, use_location);
436-
back_edge.map_or(false, |back_edge| self.can_reach_head_of_loop(use_location, back_edge))
437-
}
438-
439-
/// Returns the outmost back edge if `from` location can reach `to` location passing through
440-
/// that back edge
441-
fn reach_through_backedge(&self, from: Location, to: Location) -> Option<Location> {
442-
let mut visited_locations = FxHashSet::default();
443-
let mut pending_locations = VecDeque::new();
444-
visited_locations.insert(from);
445-
pending_locations.push_back(from);
446-
debug!("reach_through_backedge: from={:?} to={:?}", from, to,);
447-
448-
let mut outmost_back_edge = None;
449-
while let Some(location) = pending_locations.pop_front() {
450-
debug!(
451-
"reach_through_backedge: location={:?} outmost_back_edge={:?}
452-
pending_locations={:?} visited_locations={:?}",
453-
location, outmost_back_edge, pending_locations, visited_locations
454-
);
455-
456-
if location == to && outmost_back_edge.is_some() {
457-
// We've managed to reach the use location
458-
debug!("reach_through_backedge: found!");
459-
return outmost_back_edge;
460-
}
461-
462-
let block = &self.body.basic_blocks[location.block];
463-
464-
if location.statement_index < block.statements.len() {
465-
let successor = location.successor_within_block();
466-
if visited_locations.insert(successor) {
467-
pending_locations.push_back(successor);
468-
}
469-
} else {
470-
pending_locations.extend(
471-
block
472-
.terminator()
473-
.successors()
474-
.map(|bb| Location { statement_index: 0, block: bb })
475-
.filter(|s| visited_locations.insert(*s))
476-
.map(|s| {
477-
if self.is_back_edge(location, s) {
478-
match outmost_back_edge {
479-
None => {
480-
outmost_back_edge = Some(location);
481-
}
482-
483-
Some(back_edge)
484-
if location.dominates(back_edge, &self.dominators) =>
485-
{
486-
outmost_back_edge = Some(location);
487-
}
488-
489-
Some(_) => {}
490-
}
491-
}
492-
493-
s
494-
}),
495-
);
496-
}
497-
}
498-
499-
None
500-
}
501-
502-
/// true if `from` location can reach `loop_head` location and `loop_head` dominates all the
503-
/// intermediate nodes
504-
fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool {
505-
self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default())
506-
}
507-
508-
fn find_loop_head_dfs(
509-
&self,
510-
from: Location,
511-
loop_head: Location,
512-
visited_locations: &mut FxHashSet<Location>,
513-
) -> bool {
514-
visited_locations.insert(from);
515-
516-
if from == loop_head {
517-
return true;
518-
}
519-
520-
if loop_head.dominates(from, &self.dominators) {
521-
let block = &self.body.basic_blocks[from.block];
522-
523-
if from.statement_index < block.statements.len() {
524-
let successor = from.successor_within_block();
525-
526-
if !visited_locations.contains(&successor)
527-
&& self.find_loop_head_dfs(successor, loop_head, visited_locations)
528-
{
529-
return true;
530-
}
531-
} else {
532-
for bb in block.terminator().successors() {
533-
let successor = Location { statement_index: 0, block: bb };
534-
535-
if !visited_locations.contains(&successor)
536-
&& self.find_loop_head_dfs(successor, loop_head, visited_locations)
537-
{
538-
return true;
539-
}
540-
}
541-
}
542-
}
543-
544-
false
545-
}
546-
547-
/// True if an edge `source -> target` is a backedge -- in other words, if the target
548-
/// dominates the source.
549-
fn is_back_edge(&self, source: Location, target: Location) -> bool {
550-
target.dominates(source, &self.dominators)
551-
}
552-
553443
/// Determine how the borrow was later used.
554444
/// First span returned points to the location of the conflicting use
555445
/// Second span if `Some` is returned in the case of closures and points

Diff for: compiler/rustc_borrowck/src/region_infer/mod.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound,
1515
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
1616
use rustc_middle::mir::{
1717
Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
18-
ConstraintCategory, Local, Location, ReturnConstraint,
18+
ConstraintCategory, Local, Location, ReturnConstraint, TerminatorKind,
1919
};
2020
use rustc_middle::traits::ObligationCause;
2121
use rustc_middle::traits::ObligationCauseCode;
@@ -2236,6 +2236,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
22362236
pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
22372237
self.universe_causes[&universe].clone()
22382238
}
2239+
2240+
/// Tries to find the terminator of the loop in which the region 'r' resides.
2241+
/// Returns the location of the terminator if found.
2242+
pub(crate) fn find_loop_terminator_location(
2243+
&self,
2244+
r: RegionVid,
2245+
body: &Body<'_>,
2246+
) -> Option<Location> {
2247+
let scc = self.constraint_sccs.scc(r.to_region_vid());
2248+
let locations = self.scc_values.locations_outlived_by(scc);
2249+
for location in locations {
2250+
let bb = &body[location.block];
2251+
if let Some(terminator) = &bb.terminator {
2252+
// terminator of a loop should be TerminatorKind::FalseUnwind
2253+
if let TerminatorKind::FalseUnwind { .. } = terminator.kind {
2254+
return Some(location);
2255+
}
2256+
}
2257+
}
2258+
None
2259+
}
22392260
}
22402261

22412262
impl<'tcx> RegionDefinition<'tcx> {

Diff for: src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time
2525
--> $DIR/borrowck-mut-borrow-linear-errors.rs:12:30
2626
|
2727
LL | _ => { addr.push(&mut x); }
28-
| ^^^^^^ `x` was mutably borrowed here in the previous iteration of the loop
28+
| ----------^^^^^^-
29+
| | |
30+
| | `x` was mutably borrowed here in the previous iteration of the loop
31+
| first borrow used here, in later iteration of loop
2932

3033
error: aborting due to 3 previous errors
3134

Diff for: src/test/ui/borrowck/two-phase-across-loop.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ error[E0499]: cannot borrow `foo` as mutable more than once at a time
22
--> $DIR/two-phase-across-loop.rs:17:22
33
|
44
LL | strings.push(foo.get_string());
5-
| ^^^^^^^^^^^^^^^^ `foo` was mutably borrowed here in the previous iteration of the loop
5+
| -------------^^^^^^^^^^^^^^^^-
6+
| | |
7+
| | `foo` was mutably borrowed here in the previous iteration of the loop
8+
| first borrow used here, in later iteration of loop
69

710
error: aborting due to previous error
811

Diff for: src/test/ui/nll/closures-in-loops.stderr

+10-6
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,21 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time
1313
--> $DIR/closures-in-loops.rs:13:16
1414
|
1515
LL | v.push(|| x = String::new());
16-
| ^^ - borrows occur due to use of `x` in closure
17-
| |
18-
| `x` was mutably borrowed here in the previous iteration of the loop
16+
| -------^^-------------------
17+
| | | |
18+
| | | borrows occur due to use of `x` in closure
19+
| | `x` was mutably borrowed here in the previous iteration of the loop
20+
| first borrow used here, in later iteration of loop
1921

2022
error[E0524]: two closures require unique access to `x` at the same time
2123
--> $DIR/closures-in-loops.rs:20:16
2224
|
2325
LL | v.push(|| *x = String::new());
24-
| ^^ -- borrows occur due to use of `x` in closure
25-
| |
26-
| closures are constructed here in different iterations of loop
26+
| -------^^--------------------
27+
| | | |
28+
| | | borrows occur due to use of `x` in closure
29+
| | closures are constructed here in different iterations of loop
30+
| first borrow used here, in later iteration of loop
2731

2832
error: aborting due to 3 previous errors
2933

0 commit comments

Comments
 (0)