Skip to content

Commit 11432fe

Browse files
committed
Auto merge of #102080 - yanchen4791:issue-99824-fix, r=cjgillot
Fix missing explanation of where the borrowed reference is used when the same borrow occurs multiple times due to loop iterations Fix #99824. Problem of the issue: If a borrow occurs in a loop, the borrowed reference could be invalidated at the same place at next iteration of the loop. When this happens, the point where the borrow occurs is the same as the intervening point that might invalidate the reference in the loop. This causes a problem for the current code finding the point where the resulting reference is used, so that the explanation of the cause will be missing. As the second point of "explain all errors in terms of three points" (see [leveraging intuition framing errors in terms of points"](https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points), this explanation is very helpful for user to understand the error. In the current implementation, the searching region for finding the location where the borrowed reference is used is limited to between the place where the borrow occurs and the place where the reference is invalidated. If those two places happen to be the same, which indicates that the borrow and invalidation occur at the same place in a loop, the search will fail. One solution to the problem is when these two places are the same, find the terminator of the loop, and then use the location of the loop terminator instead of the location of the borrow for the region to find the place where the borrowed reference is used.
2 parents e928a46 + b3bf931 commit 11432fe

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)