about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-07-03 05:29:11 -0400
committerNiko Matsakis <niko@alum.mit.edu>2018-07-03 18:10:08 -0400
commit09f431fad5b8d67f278aa3130d3eccd0cd526cce (patch)
tree3b110e957589440446f8ed3a5c0d58caad27ecbe
parentea0224f5ab5e0fce6f83ae395c30058dcd434be7 (diff)
downloadrust-09f431fad5b8d67f278aa3130d3eccd0cd526cce.tar.gz
rust-09f431fad5b8d67f278aa3130d3eccd0cd526cce.zip
simplify and cleanup error-reporting walk code
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting.rs216
1 files changed, 95 insertions, 121 deletions
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting.rs
index fdeb1c47ea6..d8258afc24f 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting.rs
@@ -17,7 +17,7 @@ use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
 use rustc::infer::InferCtxt;
 use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
 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 std::fmt;
 use syntax_pos::Span;
@@ -48,140 +48,107 @@ impl fmt::Display for ConstraintCategory {
 }
 
 impl<'tcx> RegionInferenceContext<'tcx> {
-    /// 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
+    /// Walks the graph of constraints (where `'a: 'b` is considered
+    /// 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.
-    fn find_constraint_paths_from_region(&self, r0: RegionVid) -> Vec<Vec<ConstraintIndex>> {
-        let constraints = self.constraints.clone();
-
-        // Mapping of regions to the previous region and constraint index that led to it.
-        let mut previous = FxHashMap();
-        // Regions yet to be visited.
-        let mut next = vec![r0];
-        // Regions that have been visited.
-        let mut visited = FxHashSet();
-        // Ends of paths.
-        let mut end_regions = FxHashSet();
-
-        // When we've still got points to visit...
-        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;
-                    }
+    fn find_constraint_paths_between_regions(
+        &self,
+        from_region: RegionVid,
+        to_region: RegionVid,
+    ) -> Vec<Vec<ConstraintIndex>> {
+        let mut results = vec![];
+        self.find_constraint_paths_between_regions_helper(
+            from_region,
+            from_region,
+            to_region,
+            &mut FxHashSet::default(),
+            &mut vec![],
+            &mut results,
+        );
+        results
+    }
 
-                    previous.insert(next_region, (index, Some(current)));
-                    upcoming.push(next_region);
-                }
-            }
+    /// Helper for `find_constraint_paths_between_regions`.
+    fn find_constraint_paths_between_regions_helper(
+        &self,
+        from_region: RegionVid,
+        current_region: RegionVid,
+        to_region: RegionVid,
+        visited: &mut FxHashSet<RegionVid>,
+        stack: &mut Vec<ConstraintIndex>,
+        results: &mut Vec<Vec<ConstraintIndex>>,
+    ) {
+        let dependency_map = self.dependency_map.as_ref().unwrap();
 
-            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);
-            }
+        // Check if we already visited this region.
+        if !visited.insert(current_region) {
+            return;
         }
 
-        // 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 {
-            debug!(
-                "find_constraint_paths_from_region: end_region={:?}",
-                end_region
-            );
+        // Check if we reached the region we were looking for.
+        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());
 
-            // Get the constraint and region that led to this end point.
-            // We can unwrap as we know if end_point was in the vector that it
-            // must also be in our previous map.
-            let (mut index, mut region) = previous.get(&end_region).unwrap();
-            debug!(
-                "find_constraint_paths_from_region: index={:?} region={:?}",
-                index, region
-            );
-
-            // Keep track of the indices.
-            let mut path: Vec<ConstraintIndex> = vec![index];
-
-            while region.is_some() && region != Some(r0) {
-                let p = previous.get(&region.unwrap()).unwrap();
-                index = p.0;
-                region = p.1;
+            // The first constraint should be like `X: from_region`.
+            assert_eq!(self.constraints[stack[0]].sub, from_region);
 
-                debug!(
-                    "find_constraint_paths_from_region: index={:?} region={:?}",
-                    index, region
-                );
-                path.push(index);
-            }
+            // The last constraint should be like `to_region: Y`.
+            assert_eq!(self.constraints[*stack.last().unwrap()].sup, to_region);
 
-            // Add to our paths.
-            paths.push(path);
+            results.push(stack.clone());
+            return;
         }
 
-        debug!("find_constraint_paths_from_region: paths={:?}", paths);
-        paths
+        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
     /// is not. It is useful in filtering constraint paths to only interesting points.
-    fn constraint_is_interesting(&self, index: &ConstraintIndex) -> bool {
-        self.constraints
-            .get(*index)
-            .filter(|constraint| {
-                debug!(
-                    "constraint_is_interesting: locations={:?} constraint={:?}",
-                    constraint.locations, constraint
-                );
-                if let Locations::Interesting(_) = constraint.locations {
-                    true
-                } else {
-                    false
-                }
-            })
-            .is_some()
+    fn constraint_is_interesting(&self, index: ConstraintIndex) -> bool {
+        let constraint = self.constraints[index];
+        debug!(
+            "constraint_is_interesting: locations={:?} constraint={:?}",
+            constraint.locations, constraint
+        );
+        if let Locations::Interesting(_) = constraint.locations {
+            true
+        } else {
+            false
+        }
     }
 
     /// This function classifies a constraint from a location.
     fn classify_constraint(
         &self,
-        index: &ConstraintIndex,
+        index: ConstraintIndex,
         mir: &Mir<'tcx>,
     ) -> Option<(ConstraintCategory, Span)> {
-        let constraint = self.constraints.get(*index)?;
+        let constraint = self.constraints[index];
         let span = constraint.locations.span(mir);
         let location = constraint.locations.from_location()?;
 
@@ -238,7 +205,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         outlived_fr: RegionVid,
         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 outlived_fr_name = self.to_error_region(outlived_fr);
@@ -261,19 +228,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             None => format!("free region `{:?}`", outlived_fr),
         };
 
-        let constraints = self.find_constraint_paths_from_region(fr.clone());
-        let path = constraints.iter().min_by_key(|p| p.len()).unwrap();
+        // Find all paths
+        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);
 
-        let mut categorized_path = path.iter()
-            .filter_map(|index| self.classify_constraint(index, mir))
-            .collect::<Vec<(ConstraintCategory, Span)>>();
+        // Classify each of the constraints along the path.
+        let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
+            .filter_map(|&index| self.classify_constraint(index, mir))
+            .collect();
         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));
         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(
                 *span,
                 &format!(