simplify and cleanup error-reporting walk code
This commit is contained in:
parent
ea0224f5ab
commit
09f431fad5
@ -17,7 +17,7 @@ use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
|
|||||||
use rustc::infer::InferCtxt;
|
use rustc::infer::InferCtxt;
|
||||||
use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
|
use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
|
||||||
use rustc::ty::RegionVid;
|
use rustc::ty::RegionVid;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_data_structures::indexed_vec::IndexVec;
|
use rustc_data_structures::indexed_vec::IndexVec;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
@ -48,140 +48,107 @@ impl fmt::Display for ConstraintCategory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
/// When reporting an error, it is useful to be able to determine which constraints influenced
|
/// Walks the graph of constraints (where `'a: 'b` is considered
|
||||||
/// the region being reported as an error. This function finds all of the paths from the
|
/// an edge `'b -> 'a`) to find all paths from `from_region` to
|
||||||
|
/// `to_region`. The paths are accumulated into the vector
|
||||||
|
/// `results`. The paths are stored as a series of
|
||||||
|
/// `ConstraintIndex` values -- in other words, a list of *edges*.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// - `from_region`
|
||||||
|
/// When reporting an error, it is useful to be able to determine
|
||||||
|
/// which constraints influenced the region being reported as an
|
||||||
|
/// error. This function finds all of the paths from the
|
||||||
/// constraint.
|
/// constraint.
|
||||||
fn find_constraint_paths_from_region(&self, r0: RegionVid) -> Vec<Vec<ConstraintIndex>> {
|
fn find_constraint_paths_between_regions(
|
||||||
let constraints = self.constraints.clone();
|
&self,
|
||||||
|
from_region: RegionVid,
|
||||||
// Mapping of regions to the previous region and constraint index that led to it.
|
to_region: RegionVid,
|
||||||
let mut previous = FxHashMap();
|
) -> Vec<Vec<ConstraintIndex>> {
|
||||||
// Regions yet to be visited.
|
let mut results = vec![];
|
||||||
let mut next = vec![r0];
|
self.find_constraint_paths_between_regions_helper(
|
||||||
// Regions that have been visited.
|
from_region,
|
||||||
let mut visited = FxHashSet();
|
from_region,
|
||||||
// Ends of paths.
|
to_region,
|
||||||
let mut end_regions = FxHashSet();
|
&mut FxHashSet::default(),
|
||||||
|
&mut vec![],
|
||||||
// When we've still got points to visit...
|
&mut results,
|
||||||
while let Some(current) = next.pop() {
|
|
||||||
// ...take the next point...
|
|
||||||
debug!(
|
|
||||||
"find_constraint_paths_from_region: current={:?} visited={:?} next={:?}",
|
|
||||||
current, visited, next
|
|
||||||
);
|
|
||||||
// ...but make sure not to visit this point again...
|
|
||||||
visited.insert(current);
|
|
||||||
|
|
||||||
// ...find the edges containing it...
|
|
||||||
let mut upcoming = Vec::new();
|
|
||||||
for (index, constraint) in constraints.iter_enumerated() {
|
|
||||||
if constraint.sub == current {
|
|
||||||
// ...add the regions that join us with to the path we've taken...
|
|
||||||
debug!(
|
|
||||||
"find_constraint_paths_from_region: index={:?} constraint={:?}",
|
|
||||||
index, constraint
|
|
||||||
);
|
|
||||||
let next_region = constraint.sup.clone();
|
|
||||||
|
|
||||||
// ...unless we've visited it since this was added...
|
|
||||||
if visited.contains(&next_region) {
|
|
||||||
debug!("find_constraint_paths_from_region: skipping as visited");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
previous.insert(next_region, (index, Some(current)));
|
|
||||||
upcoming.push(next_region);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if upcoming.is_empty() {
|
|
||||||
// If we didn't find any edges then this is the end of a path...
|
|
||||||
debug!(
|
|
||||||
"find_constraint_paths_from_region: new end region current={:?}",
|
|
||||||
current
|
|
||||||
);
|
|
||||||
end_regions.insert(current);
|
|
||||||
} else {
|
|
||||||
// ...but, if we did find edges, then add these to the regions yet to visit.
|
|
||||||
debug!(
|
|
||||||
"find_constraint_paths_from_region: extend next upcoming={:?}",
|
|
||||||
upcoming
|
|
||||||
);
|
|
||||||
next.extend(upcoming);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we've visited each point, compute the final paths.
|
|
||||||
let mut paths: Vec<Vec<ConstraintIndex>> = Vec::new();
|
|
||||||
debug!(
|
|
||||||
"find_constraint_paths_from_region: end_regions={:?}",
|
|
||||||
end_regions
|
|
||||||
);
|
);
|
||||||
for end_region in end_regions {
|
results
|
||||||
debug!(
|
}
|
||||||
"find_constraint_paths_from_region: end_region={:?}",
|
|
||||||
end_region
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get the constraint and region that led to this end point.
|
/// Helper for `find_constraint_paths_between_regions`.
|
||||||
// We can unwrap as we know if end_point was in the vector that it
|
fn find_constraint_paths_between_regions_helper(
|
||||||
// must also be in our previous map.
|
&self,
|
||||||
let (mut index, mut region) = previous.get(&end_region).unwrap();
|
from_region: RegionVid,
|
||||||
debug!(
|
current_region: RegionVid,
|
||||||
"find_constraint_paths_from_region: index={:?} region={:?}",
|
to_region: RegionVid,
|
||||||
index, region
|
visited: &mut FxHashSet<RegionVid>,
|
||||||
);
|
stack: &mut Vec<ConstraintIndex>,
|
||||||
|
results: &mut Vec<Vec<ConstraintIndex>>,
|
||||||
|
) {
|
||||||
|
let dependency_map = self.dependency_map.as_ref().unwrap();
|
||||||
|
|
||||||
// Keep track of the indices.
|
// Check if we already visited this region.
|
||||||
let mut path: Vec<ConstraintIndex> = vec![index];
|
if !visited.insert(current_region) {
|
||||||
|
return;
|
||||||
while region.is_some() && region != Some(r0) {
|
|
||||||
let p = previous.get(®ion.unwrap()).unwrap();
|
|
||||||
index = p.0;
|
|
||||||
region = p.1;
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"find_constraint_paths_from_region: index={:?} region={:?}",
|
|
||||||
index, region
|
|
||||||
);
|
|
||||||
path.push(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to our paths.
|
|
||||||
paths.push(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("find_constraint_paths_from_region: paths={:?}", paths);
|
// Check if we reached the region we were looking for.
|
||||||
paths
|
if current_region == to_region {
|
||||||
|
// Unless we started out searching for `'a ~> 'a`, which shouldn't have caused
|
||||||
|
// en error, then we must have traversed at least *some* constraint:
|
||||||
|
assert!(!stack.is_empty());
|
||||||
|
|
||||||
|
// The first constraint should be like `X: from_region`.
|
||||||
|
assert_eq!(self.constraints[stack[0]].sub, from_region);
|
||||||
|
|
||||||
|
// The last constraint should be like `to_region: Y`.
|
||||||
|
assert_eq!(self.constraints[*stack.last().unwrap()].sup, to_region);
|
||||||
|
|
||||||
|
results.push(stack.clone());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.constraints
|
||||||
|
.each_affected_by_dirty(dependency_map[current_region], |constraint| {
|
||||||
|
assert_eq!(self.constraints[constraint].sub, current_region);
|
||||||
|
stack.push(constraint);
|
||||||
|
self.find_constraint_paths_between_regions_helper(
|
||||||
|
from_region,
|
||||||
|
self.constraints[constraint].sup,
|
||||||
|
to_region,
|
||||||
|
visited,
|
||||||
|
stack,
|
||||||
|
results,
|
||||||
|
);
|
||||||
|
stack.pop();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function will return true if a constraint is interesting and false if a constraint
|
/// This function will return true if a constraint is interesting and false if a constraint
|
||||||
/// is not. It is useful in filtering constraint paths to only interesting points.
|
/// is not. It is useful in filtering constraint paths to only interesting points.
|
||||||
fn constraint_is_interesting(&self, index: &ConstraintIndex) -> bool {
|
fn constraint_is_interesting(&self, index: ConstraintIndex) -> bool {
|
||||||
self.constraints
|
let constraint = self.constraints[index];
|
||||||
.get(*index)
|
debug!(
|
||||||
.filter(|constraint| {
|
"constraint_is_interesting: locations={:?} constraint={:?}",
|
||||||
debug!(
|
constraint.locations, constraint
|
||||||
"constraint_is_interesting: locations={:?} constraint={:?}",
|
);
|
||||||
constraint.locations, constraint
|
if let Locations::Interesting(_) = constraint.locations {
|
||||||
);
|
true
|
||||||
if let Locations::Interesting(_) = constraint.locations {
|
} else {
|
||||||
true
|
false
|
||||||
} else {
|
}
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.is_some()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function classifies a constraint from a location.
|
/// This function classifies a constraint from a location.
|
||||||
fn classify_constraint(
|
fn classify_constraint(
|
||||||
&self,
|
&self,
|
||||||
index: &ConstraintIndex,
|
index: ConstraintIndex,
|
||||||
mir: &Mir<'tcx>,
|
mir: &Mir<'tcx>,
|
||||||
) -> Option<(ConstraintCategory, Span)> {
|
) -> Option<(ConstraintCategory, Span)> {
|
||||||
let constraint = self.constraints.get(*index)?;
|
let constraint = self.constraints[index];
|
||||||
let span = constraint.locations.span(mir);
|
let span = constraint.locations.span(mir);
|
||||||
let location = constraint.locations.from_location()?;
|
let location = constraint.locations.from_location()?;
|
||||||
|
|
||||||
@ -238,7 +205,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
outlived_fr: RegionVid,
|
outlived_fr: RegionVid,
|
||||||
blame_span: Span,
|
blame_span: Span,
|
||||||
) {
|
) {
|
||||||
// Obviously uncool error reporting.
|
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||||
|
|
||||||
let fr_name = self.to_error_region(fr);
|
let fr_name = self.to_error_region(fr);
|
||||||
let outlived_fr_name = self.to_error_region(outlived_fr);
|
let outlived_fr_name = self.to_error_region(outlived_fr);
|
||||||
@ -261,19 +228,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
None => format!("free region `{:?}`", outlived_fr),
|
None => format!("free region `{:?}`", outlived_fr),
|
||||||
};
|
};
|
||||||
|
|
||||||
let constraints = self.find_constraint_paths_from_region(fr.clone());
|
// Find all paths
|
||||||
let path = constraints.iter().min_by_key(|p| p.len()).unwrap();
|
let constraint_paths = self.find_constraint_paths_between_regions(outlived_fr, fr);
|
||||||
|
debug!("report_error: constraint_paths={:#?}", constraint_paths);
|
||||||
|
|
||||||
|
// Find the shortest such path.
|
||||||
|
let path = constraint_paths.iter().min_by_key(|p| p.len()).unwrap();
|
||||||
debug!("report_error: shortest_path={:?}", path);
|
debug!("report_error: shortest_path={:?}", path);
|
||||||
|
|
||||||
let mut categorized_path = path.iter()
|
// Classify each of the constraints along the path.
|
||||||
.filter_map(|index| self.classify_constraint(index, mir))
|
let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
|
||||||
.collect::<Vec<(ConstraintCategory, Span)>>();
|
.filter_map(|&index| self.classify_constraint(index, mir))
|
||||||
|
.collect();
|
||||||
debug!("report_error: categorized_path={:?}", categorized_path);
|
debug!("report_error: categorized_path={:?}", categorized_path);
|
||||||
|
|
||||||
|
// Find what appears to be the most interesting path to report to the user.
|
||||||
categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
|
categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
|
||||||
debug!("report_error: sorted_path={:?}", categorized_path);
|
debug!("report_error: sorted_path={:?}", categorized_path);
|
||||||
|
|
||||||
if let Some((category, span)) = &categorized_path.first() {
|
// If we found something, cite that as the main cause of the problem.
|
||||||
|
if let Some((category, span)) = categorized_path.first() {
|
||||||
let mut diag = infcx.tcx.sess.struct_span_err(
|
let mut diag = infcx.tcx.sess.struct_span_err(
|
||||||
*span,
|
*span,
|
||||||
&format!(
|
&format!(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user