Skip to content

Commit 9ba4d33

Browse files
committed
rewrite the "constraint graph search" to use a BFS instead of a DFS
This way we find the shortest path without having to sort etc
1 parent 4fce59f commit 9ba4d33

File tree

1 file changed

+51
-52
lines changed
  • src/librustc_mir/borrow_check/nll/region_infer/error_reporting

1 file changed

+51
-52
lines changed

src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs

Lines changed: 51 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
1616
use rustc::infer::InferCtxt;
1717
use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
1818
use rustc::ty::RegionVid;
19-
use rustc_data_structures::fx::FxHashSet;
2019
use rustc_data_structures::indexed_vec::IndexVec;
2120
use rustc_errors::Diagnostic;
21+
use std::collections::VecDeque;
2222
use std::fmt;
2323
use syntax_pos::Span;
2424

@@ -54,6 +54,13 @@ impl fmt::Display for ConstraintCategory {
5454
}
5555
}
5656

57+
#[derive(Copy, Clone, PartialEq, Eq)]
58+
enum Trace {
59+
StartRegion,
60+
FromConstraint(ConstraintIndex),
61+
NotVisited,
62+
}
63+
5764
impl<'tcx> RegionInferenceContext<'tcx> {
5865
/// Walks the graph of constraints (where `'a: 'b` is considered
5966
/// an edge `'a -> 'b`) to find all paths from `from_region` to
@@ -64,56 +71,52 @@ impl<'tcx> RegionInferenceContext<'tcx> {
6471
&self,
6572
from_region: RegionVid,
6673
target_test: impl Fn(RegionVid) -> bool,
67-
) -> Vec<Vec<ConstraintIndex>> {
68-
let mut results = vec![];
69-
self.find_constraint_paths_between_regions_helper(
70-
from_region,
71-
from_region,
72-
&target_test,
73-
&mut FxHashSet::default(),
74-
&mut vec![],
75-
&mut results,
76-
);
77-
results
78-
}
74+
) -> Option<Vec<ConstraintIndex>> {
75+
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
76+
context[from_region] = Trace::StartRegion;
77+
78+
// Use a deque so that we do a breadth-first search. We will
79+
// stop at the first match, which ought to be the shortest
80+
// path (fewest constraints).
81+
let mut deque = VecDeque::new();
82+
deque.push_back(from_region);
83+
84+
while let Some(r) = deque.pop_front() {
85+
// Check if we reached the region we were looking for. If so,
86+
// we can reconstruct the path that led to it and return it.
87+
if target_test(r) {
88+
let mut result = vec![];
89+
let mut p = r;
90+
loop {
91+
match context[p] {
92+
Trace::NotVisited => bug!("found unvisited region {:?} on path to {:?}", p, r),
93+
Trace::FromConstraint(c) => {
94+
result.push(c);
95+
p = self.constraints[c].sup;
96+
}
7997

80-
/// Helper for `find_constraint_paths_between_regions`.
81-
fn find_constraint_paths_between_regions_helper(
82-
&self,
83-
from_region: RegionVid,
84-
current_region: RegionVid,
85-
target_test: &impl Fn(RegionVid) -> bool,
86-
visited: &mut FxHashSet<RegionVid>,
87-
stack: &mut Vec<ConstraintIndex>,
88-
results: &mut Vec<Vec<ConstraintIndex>>,
89-
) {
90-
// Check if we already visited this region.
91-
if !visited.insert(current_region) {
92-
return;
93-
}
98+
Trace::StartRegion => {
99+
result.reverse();
100+
return Some(result);
101+
}
102+
}
103+
}
104+
}
94105

95-
// Check if we reached the region we were looking for.
96-
if target_test(current_region) {
97-
if !stack.is_empty() {
98-
assert_eq!(self.constraints[stack[0]].sup, from_region);
99-
results.push(stack.clone());
106+
// Otherwise, walk over the outgoing constraints and
107+
// enqueue any regions we find, keeping track of how we
108+
// reached them.
109+
for constraint in self.constraint_graph.outgoing_edges(r) {
110+
assert_eq!(self.constraints[constraint].sup, r);
111+
let sub_region = self.constraints[constraint].sub;
112+
if let Trace::NotVisited = context[sub_region] {
113+
context[sub_region] = Trace::FromConstraint(constraint);
114+
deque.push_back(sub_region);
115+
}
100116
}
101-
return;
102117
}
103118

104-
for constraint in self.constraint_graph.outgoing_edges(current_region) {
105-
assert_eq!(self.constraints[constraint].sup, current_region);
106-
stack.push(constraint);
107-
self.find_constraint_paths_between_regions_helper(
108-
from_region,
109-
self.constraints[constraint].sub,
110-
target_test,
111-
visited,
112-
stack,
113-
results,
114-
);
115-
stack.pop();
116-
}
119+
None
117120
}
118121

119122
/// This function will return true if a constraint is interesting and false if a constraint
@@ -204,12 +207,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
204207
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
205208

206209
// Find all paths
207-
let constraint_paths = self.find_constraint_paths_between_regions(fr, |r| r == outlived_fr);
208-
debug!("report_error: constraint_paths={:#?}", constraint_paths);
209-
210-
// Find the shortest such path.
211-
let path = constraint_paths.iter().min_by_key(|p| p.len()).unwrap();
212-
debug!("report_error: shortest_path={:?}", path);
210+
let path = self.find_constraint_paths_between_regions(fr, |r| r == outlived_fr).unwrap();
211+
debug!("report_error: path={:#?}", path);
213212

214213
// Classify each of the constraints along the path.
215214
let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()

0 commit comments

Comments
 (0)