Skip to content

Commit 09f431f

Browse files
committed
simplify and cleanup error-reporting walk code
1 parent ea0224f commit 09f431f

File tree

1 file changed

+95
-121
lines changed

1 file changed

+95
-121
lines changed

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

Lines changed: 95 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
1717
use rustc::infer::InferCtxt;
1818
use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
1919
use rustc::ty::RegionVid;
20-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
20+
use rustc_data_structures::fx::FxHashSet;
2121
use rustc_data_structures::indexed_vec::IndexVec;
2222
use std::fmt;
2323
use syntax_pos::Span;
@@ -48,140 +48,107 @@ impl fmt::Display for ConstraintCategory {
4848
}
4949

5050
impl<'tcx> RegionInferenceContext<'tcx> {
51-
/// When reporting an error, it is useful to be able to determine which constraints influenced
52-
/// the region being reported as an error. This function finds all of the paths from the
51+
/// Walks the graph of constraints (where `'a: 'b` is considered
52+
/// an edge `'b -> 'a`) to find all paths from `from_region` to
53+
/// `to_region`. The paths are accumulated into the vector
54+
/// `results`. The paths are stored as a series of
55+
/// `ConstraintIndex` values -- in other words, a list of *edges*.
56+
///
57+
/// # Parameters
58+
///
59+
/// - `from_region`
60+
/// When reporting an error, it is useful to be able to determine
61+
/// which constraints influenced the region being reported as an
62+
/// error. This function finds all of the paths from the
5363
/// constraint.
54-
fn find_constraint_paths_from_region(&self, r0: RegionVid) -> Vec<Vec<ConstraintIndex>> {
55-
let constraints = self.constraints.clone();
56-
57-
// Mapping of regions to the previous region and constraint index that led to it.
58-
let mut previous = FxHashMap();
59-
// Regions yet to be visited.
60-
let mut next = vec![r0];
61-
// Regions that have been visited.
62-
let mut visited = FxHashSet();
63-
// Ends of paths.
64-
let mut end_regions = FxHashSet();
65-
66-
// When we've still got points to visit...
67-
while let Some(current) = next.pop() {
68-
// ...take the next point...
69-
debug!(
70-
"find_constraint_paths_from_region: current={:?} visited={:?} next={:?}",
71-
current, visited, next
72-
);
73-
// ...but make sure not to visit this point again...
74-
visited.insert(current);
75-
76-
// ...find the edges containing it...
77-
let mut upcoming = Vec::new();
78-
for (index, constraint) in constraints.iter_enumerated() {
79-
if constraint.sub == current {
80-
// ...add the regions that join us with to the path we've taken...
81-
debug!(
82-
"find_constraint_paths_from_region: index={:?} constraint={:?}",
83-
index, constraint
84-
);
85-
let next_region = constraint.sup.clone();
86-
87-
// ...unless we've visited it since this was added...
88-
if visited.contains(&next_region) {
89-
debug!("find_constraint_paths_from_region: skipping as visited");
90-
continue;
91-
}
64+
fn find_constraint_paths_between_regions(
65+
&self,
66+
from_region: RegionVid,
67+
to_region: RegionVid,
68+
) -> Vec<Vec<ConstraintIndex>> {
69+
let mut results = vec![];
70+
self.find_constraint_paths_between_regions_helper(
71+
from_region,
72+
from_region,
73+
to_region,
74+
&mut FxHashSet::default(),
75+
&mut vec![],
76+
&mut results,
77+
);
78+
results
79+
}
9280

93-
previous.insert(next_region, (index, Some(current)));
94-
upcoming.push(next_region);
95-
}
96-
}
81+
/// Helper for `find_constraint_paths_between_regions`.
82+
fn find_constraint_paths_between_regions_helper(
83+
&self,
84+
from_region: RegionVid,
85+
current_region: RegionVid,
86+
to_region: RegionVid,
87+
visited: &mut FxHashSet<RegionVid>,
88+
stack: &mut Vec<ConstraintIndex>,
89+
results: &mut Vec<Vec<ConstraintIndex>>,
90+
) {
91+
let dependency_map = self.dependency_map.as_ref().unwrap();
9792

98-
if upcoming.is_empty() {
99-
// If we didn't find any edges then this is the end of a path...
100-
debug!(
101-
"find_constraint_paths_from_region: new end region current={:?}",
102-
current
103-
);
104-
end_regions.insert(current);
105-
} else {
106-
// ...but, if we did find edges, then add these to the regions yet to visit.
107-
debug!(
108-
"find_constraint_paths_from_region: extend next upcoming={:?}",
109-
upcoming
110-
);
111-
next.extend(upcoming);
112-
}
93+
// Check if we already visited this region.
94+
if !visited.insert(current_region) {
95+
return;
11396
}
11497

115-
// Now we've visited each point, compute the final paths.
116-
let mut paths: Vec<Vec<ConstraintIndex>> = Vec::new();
117-
debug!(
118-
"find_constraint_paths_from_region: end_regions={:?}",
119-
end_regions
120-
);
121-
for end_region in end_regions {
122-
debug!(
123-
"find_constraint_paths_from_region: end_region={:?}",
124-
end_region
125-
);
98+
// Check if we reached the region we were looking for.
99+
if current_region == to_region {
100+
// Unless we started out searching for `'a ~> 'a`, which shouldn't have caused
101+
// en error, then we must have traversed at least *some* constraint:
102+
assert!(!stack.is_empty());
126103

127-
// Get the constraint and region that led to this end point.
128-
// We can unwrap as we know if end_point was in the vector that it
129-
// must also be in our previous map.
130-
let (mut index, mut region) = previous.get(&end_region).unwrap();
131-
debug!(
132-
"find_constraint_paths_from_region: index={:?} region={:?}",
133-
index, region
134-
);
135-
136-
// Keep track of the indices.
137-
let mut path: Vec<ConstraintIndex> = vec![index];
138-
139-
while region.is_some() && region != Some(r0) {
140-
let p = previous.get(&region.unwrap()).unwrap();
141-
index = p.0;
142-
region = p.1;
104+
// The first constraint should be like `X: from_region`.
105+
assert_eq!(self.constraints[stack[0]].sub, from_region);
143106

144-
debug!(
145-
"find_constraint_paths_from_region: index={:?} region={:?}",
146-
index, region
147-
);
148-
path.push(index);
149-
}
107+
// The last constraint should be like `to_region: Y`.
108+
assert_eq!(self.constraints[*stack.last().unwrap()].sup, to_region);
150109

151-
// Add to our paths.
152-
paths.push(path);
110+
results.push(stack.clone());
111+
return;
153112
}
154113

155-
debug!("find_constraint_paths_from_region: paths={:?}", paths);
156-
paths
114+
self.constraints
115+
.each_affected_by_dirty(dependency_map[current_region], |constraint| {
116+
assert_eq!(self.constraints[constraint].sub, current_region);
117+
stack.push(constraint);
118+
self.find_constraint_paths_between_regions_helper(
119+
from_region,
120+
self.constraints[constraint].sup,
121+
to_region,
122+
visited,
123+
stack,
124+
results,
125+
);
126+
stack.pop();
127+
});
157128
}
158129

159130
/// This function will return true if a constraint is interesting and false if a constraint
160131
/// is not. It is useful in filtering constraint paths to only interesting points.
161-
fn constraint_is_interesting(&self, index: &ConstraintIndex) -> bool {
162-
self.constraints
163-
.get(*index)
164-
.filter(|constraint| {
165-
debug!(
166-
"constraint_is_interesting: locations={:?} constraint={:?}",
167-
constraint.locations, constraint
168-
);
169-
if let Locations::Interesting(_) = constraint.locations {
170-
true
171-
} else {
172-
false
173-
}
174-
})
175-
.is_some()
132+
fn constraint_is_interesting(&self, index: ConstraintIndex) -> bool {
133+
let constraint = self.constraints[index];
134+
debug!(
135+
"constraint_is_interesting: locations={:?} constraint={:?}",
136+
constraint.locations, constraint
137+
);
138+
if let Locations::Interesting(_) = constraint.locations {
139+
true
140+
} else {
141+
false
142+
}
176143
}
177144

178145
/// This function classifies a constraint from a location.
179146
fn classify_constraint(
180147
&self,
181-
index: &ConstraintIndex,
148+
index: ConstraintIndex,
182149
mir: &Mir<'tcx>,
183150
) -> Option<(ConstraintCategory, Span)> {
184-
let constraint = self.constraints.get(*index)?;
151+
let constraint = self.constraints[index];
185152
let span = constraint.locations.span(mir);
186153
let location = constraint.locations.from_location()?;
187154

@@ -238,7 +205,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
238205
outlived_fr: RegionVid,
239206
blame_span: Span,
240207
) {
241-
// Obviously uncool error reporting.
208+
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
242209

243210
let fr_name = self.to_error_region(fr);
244211
let outlived_fr_name = self.to_error_region(outlived_fr);
@@ -261,19 +228,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
261228
None => format!("free region `{:?}`", outlived_fr),
262229
};
263230

264-
let constraints = self.find_constraint_paths_from_region(fr.clone());
265-
let path = constraints.iter().min_by_key(|p| p.len()).unwrap();
231+
// Find all paths
232+
let constraint_paths = self.find_constraint_paths_between_regions(outlived_fr, fr);
233+
debug!("report_error: constraint_paths={:#?}", constraint_paths);
234+
235+
// Find the shortest such path.
236+
let path = constraint_paths.iter().min_by_key(|p| p.len()).unwrap();
266237
debug!("report_error: shortest_path={:?}", path);
267238

268-
let mut categorized_path = path.iter()
269-
.filter_map(|index| self.classify_constraint(index, mir))
270-
.collect::<Vec<(ConstraintCategory, Span)>>();
239+
// Classify each of the constraints along the path.
240+
let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
241+
.filter_map(|&index| self.classify_constraint(index, mir))
242+
.collect();
271243
debug!("report_error: categorized_path={:?}", categorized_path);
272244

245+
// Find what appears to be the most interesting path to report to the user.
273246
categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
274247
debug!("report_error: sorted_path={:?}", categorized_path);
275248

276-
if let Some((category, span)) = &categorized_path.first() {
249+
// If we found something, cite that as the main cause of the problem.
250+
if let Some((category, span)) = categorized_path.first() {
277251
let mut diag = infcx.tcx.sess.struct_span_err(
278252
*span,
279253
&format!(

0 commit comments

Comments
 (0)