]> git.lizzy.rs Git - rust.git/commitdiff
Constraints are now being categorized, sorted and the error labelled. Categorization...
authorDavid Wood <david@davidtw.co>
Tue, 12 Jun 2018 19:43:14 +0000 (20:43 +0100)
committerDavid Wood <david@davidtw.co>
Sun, 1 Jul 2018 14:33:05 +0000 (15:33 +0100)
src/librustc_mir/borrow_check/nll/constraint_set.rs
src/librustc_mir/borrow_check/nll/region_infer/mod.rs

index 3bdf78ff3db54dc323ee1c178d22689ae887d464..232242b552f7f8ffc2644c7087f07c98317ed4c3 100644 (file)
@@ -13,6 +13,7 @@
 use borrow_check::nll::type_check::Locations;
 
 use std::fmt;
+use std::cmp::Ordering;
 use std::ops::Deref;
 
 #[derive(Clone, Default)]
@@ -109,4 +110,39 @@ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+/// Constraints that are considered interesting can be categorized to
+/// determine why they are interesting.
+#[derive(Debug, Eq, PartialEq)]
+crate enum ConstraintCategory {
+    Assignment,
+    CallArgument,
+    Cast,
+    Other,
+}
+
+impl Ord for ConstraintCategory {
+    fn cmp(&self, other: &Self) -> Ordering {
+        if self == other {
+            return Ordering::Equal;
+        }
+
+        match (self, other) {
+            (ConstraintCategory::Assignment, _) => Ordering::Greater,
+            (_, ConstraintCategory::Assignment) => Ordering::Less,
+            (ConstraintCategory::CallArgument, _) => Ordering::Greater,
+            (_, ConstraintCategory::CallArgument) => Ordering::Less,
+            (ConstraintCategory::Cast, _) => Ordering::Greater,
+            (_, ConstraintCategory::Cast) => Ordering::Less,
+            (ConstraintCategory::Other, _) => Ordering::Greater,
+        }
+    }
+}
+
+impl PartialOrd for ConstraintCategory {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+
 newtype_index!(ConstraintIndex { DEBUG_FORMAT = "ConstraintIndex({})" });
index 1fa530f5492ab688fd3b171e0d3282651ef5ff8a..713ff7002b3246100e5167ce3761c271b01b8692 100644 (file)
@@ -10,7 +10,8 @@
 
 use super::universal_regions::UniversalRegions;
 use borrow_check::nll::region_infer::values::ToElementIndex;
-use borrow_check::nll::constraint_set::{ConstraintIndex, ConstraintSet, OutlivesConstraint};
+use borrow_check::nll::constraint_set::{ConstraintIndex, ConstraintCategory, ConstraintSet};
+use borrow_check::nll::constraint_set::{OutlivesConstraint};
 use borrow_check::nll::type_check::Locations;
 use rustc::hir::def_id::DefId;
 use rustc::infer::canonical::QueryRegionConstraint;
 use rustc::infer::RegionVariableOrigin;
 use rustc::mir::{
     ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location,
-    Mir,
+    Mir, StatementKind, TerminatorKind, Rvalue
 };
 use rustc::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable};
 use rustc::util::common::{self, ErrorReported};
 use rustc_data_structures::bitvec::BitVector;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+
 use std::rc::Rc;
 use syntax_pos::Span;
 
@@ -961,10 +963,13 @@ fn check_universal_region<'gcx>(
             // Note: in this case, we use the unapproximated regions
             // to report the error. This gives better error messages
             // in some cases.
-            self.report_error(infcx, mir_def_id, longer_fr, shorter_fr, blame_span);
+            self.report_error(mir, infcx, mir_def_id, longer_fr, shorter_fr, blame_span);
         }
     }
 
+    /// 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
@@ -1055,6 +1060,8 @@ fn find_constraint_paths_from_region(
         paths
     }
 
+    /// 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={:?}",
@@ -1063,6 +1070,32 @@ fn constraint_is_interesting(&self, index: &ConstraintIndex) -> bool {
         }).is_some()
     }
 
+    /// This function classifies a constraint from a location.
+    fn classify_constraint(&self, location: Location, mir: &Mir<'tcx>) -> ConstraintCategory {
+        let data = &mir[location.block];
+        if location.statement_index == data.statements.len() {
+            if let Some(ref terminator) = data.terminator {
+                match terminator.kind {
+                    TerminatorKind::DropAndReplace { .. } => ConstraintCategory::Assignment,
+                    TerminatorKind::Call { .. } => ConstraintCategory::CallArgument,
+                    _ => ConstraintCategory::Other,
+                }
+            } else {
+                ConstraintCategory::Other
+            }
+        } else {
+            let statement = &data.statements[location.statement_index];
+            match statement.kind {
+                StatementKind::Assign(_, ref rvalue) => match rvalue {
+                    Rvalue::Cast(..) => ConstraintCategory::Cast,
+                    Rvalue::Use(..) => ConstraintCategory::Assignment,
+                    _ => ConstraintCategory::Other,
+                },
+                _ => ConstraintCategory::Other,
+            }
+        }
+    }
+
     /// Report an error because the universal region `fr` was required to outlive
     /// `outlived_fr` but it is not known to do so. For example:
     ///
@@ -1073,6 +1106,7 @@ fn constraint_is_interesting(&self, index: &ConstraintIndex) -> bool {
     /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
     fn report_error(
         &self,
+        mir: &Mir<'tcx>,
         infcx: &InferCtxt<'_, '_, 'tcx>,
         mir_def_id: DefId,
         fr: RegionVid,
@@ -1095,27 +1129,57 @@ fn report_error(
         let constraints = self.find_constraint_paths_from_region(fr.clone());
         let path = constraints.iter().min_by_key(|p| p.len()).unwrap();
         debug!("report_error: path={:?}", path);
+
         let path = path.iter()
             .filter(|index| self.constraint_is_interesting(index))
             .collect::<Vec<&ConstraintIndex>>();
         debug!("report_error: path={:?}", path);
 
-        let fr_string = match fr_name {
-            Some(r) => format!("free region `{}`", r),
-            None => format!("free region `{:?}`", fr),
-        };
+        let mut categorized_path = path.iter().filter_map(|index| {
+            self.constraints.get(**index).iter().filter_map(|constraint| {
+                let span = constraint.locations.span(mir);
+                constraint.locations.from_location().iter().filter_map(|location| {
+                    let classification = self.classify_constraint(*location, mir);
+                    Some((classification, span))
+                }).next()
+            }).next()
+        }).collect::<Vec<(ConstraintCategory, Span)>>();
+        debug!("report_error: categorized_path={:?}", categorized_path);
+
+        categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
+        debug!("report_error: sorted_path={:?}", categorized_path);
+
+        if categorized_path.len() > 0 {
+            let blame_constraint = &categorized_path[0];
+
+            let mut diag = infcx.tcx.sess.struct_span_err(
+                blame_constraint.1,
+                &format!("{:?}", blame_constraint.0),
+            );
 
-        let outlived_fr_string = match outlived_fr_name {
-            Some(r) => format!("free region `{}`", r),
-            None => format!("free region `{:?}`", outlived_fr),
-        };
+            for secondary in categorized_path.iter().skip(1) {
+                diag.span_label(secondary.1, format!("{:?}", secondary.0));
+            }
 
-        let mut diag = infcx.tcx.sess.struct_span_err(
-            blame_span,
-            &format!("{} does not outlive {}", fr_string, outlived_fr_string,),
-        );
+            diag.emit();
+        } else {
+            let fr_string = match fr_name {
+                Some(r) => format!("free region `{}`", r),
+                None => format!("free region `{:?}`", fr),
+            };
+
+            let outlived_fr_string = match outlived_fr_name {
+                Some(r) => format!("free region `{}`", r),
+                None => format!("free region `{:?}`", outlived_fr),
+            };
+
+            let mut diag = infcx.tcx.sess.struct_span_err(
+                blame_span,
+                &format!("{} does not outlive {}", fr_string, outlived_fr_string,),
+            );
 
-        diag.emit();
+            diag.emit();
+        }
     }
 
     crate fn why_region_contains_point(&self, fr1: RegionVid, elem: Location) -> Option<Cause> {