about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/infer/opaque_types/mod.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/constraints/graph.rs7
-rw-r--r--src/librustc_mir/borrow_check/nll/constraints/mod.rs39
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs146
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/mod.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs6
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs5
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/input_output.rs59
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs9
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs387
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs13
12 files changed, 377 insertions, 304 deletions
diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs
index 9d65c395062..c6f910c4ad7 100644
--- a/src/librustc/infer/opaque_types/mod.rs
+++ b/src/librustc/infer/opaque_types/mod.rs
@@ -284,7 +284,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
+    pub fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
         &self,
         def_id: DefId,
         opaque_defn: &OpaqueTypeDecl<'tcx>,
diff --git a/src/librustc_mir/borrow_check/nll/constraints/graph.rs b/src/librustc_mir/borrow_check/nll/constraints/graph.rs
index b1e8b974379..2e018f746f3 100644
--- a/src/librustc_mir/borrow_check/nll/constraints/graph.rs
+++ b/src/librustc_mir/borrow_check/nll/constraints/graph.rs
@@ -9,10 +9,12 @@
 // except according to those terms.
 
 use borrow_check::nll::type_check::Locations;
-use borrow_check::nll::constraints::{ConstraintIndex, ConstraintSet, OutlivesConstraint};
+use borrow_check::nll::constraints::{ConstraintCategory, ConstraintIndex};
+use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
 use rustc::ty::RegionVid;
 use rustc_data_structures::graph;
 use rustc_data_structures::indexed_vec::IndexVec;
+use syntax_pos::DUMMY_SP;
 
 /// The construct graph organizes the constraints by their end-points.
 /// It can be used to view a `R1: R2` constraint as either an edge `R1
@@ -174,7 +176,8 @@ impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> {
             Some(OutlivesConstraint {
                 sup: self.static_region,
                 sub: next_static_idx.into(),
-                locations: Locations::All,
+                locations: Locations::All(DUMMY_SP),
+                category: ConstraintCategory::Internal,
             })
         } else {
             None
diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs
index 41c846509cd..76ebc06bfd2 100644
--- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs
@@ -23,6 +23,42 @@ crate struct ConstraintSet {
     constraints: IndexVec<ConstraintIndex, OutlivesConstraint>,
 }
 
+/// Constraints can be categorized to determine whether and why they are
+/// interesting. Order of variants indicates sort order of the category,
+/// thereby influencing diagnostic output.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
+pub enum ConstraintCategory {
+    Return,
+    TypeAnnotation,
+    Cast,
+    CallArgument,
+
+    /// A constraint that came from checking the body of a closure.
+    ///
+    /// Ideally we would give an explanation that points to the relevant part
+    /// of the closure's body.
+    ClosureBounds,
+    CopyBound,
+    SizedBound,
+    Assignment,
+    OpaqueType,
+
+    /// A "boring" constraint (caused by the given location) is one that
+    /// the user probably doesn't want to see described in diagnostics,
+    /// because it is kind of an artifact of the type system setup.
+    /// Example: `x = Foo { field: y }` technically creates
+    /// intermediate regions representing the "type of `Foo { field: y
+    /// }`", and data flows from `y` into those variables, but they
+    /// are not very interesting. The assignment into `x` on the other
+    /// hand might be.
+    Boring,
+    // Boring and applicable everywhere.
+    BoringNoLocation,
+
+    /// A constraint that doesn't correspond to anything the user sees.
+    Internal,
+}
+
 impl ConstraintSet {
     crate fn push(&mut self, constraint: OutlivesConstraint) {
         debug!(
@@ -87,6 +123,9 @@ pub struct OutlivesConstraint {
 
     /// Where did this constraint arise?
     pub locations: Locations,
+
+    /// What caused this constraint?
+    pub category: ConstraintCategory,
 }
 
 impl fmt::Debug for OutlivesConstraint {
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs
index d3b4f0a0447..ee900afc44d 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs
@@ -88,11 +88,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 sup,
                 sub,
                 locations,
+                category,
             } = constraint;
             with_msg(&format!(
-                "{:?}: {:?} due to {:?}",
+                "{:?}: {:?} due to {:?} at {:?}",
                 sup,
                 sub,
+                category,
                 locations,
             ))?;
         }
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
index b7000b254a7..1abc105ac28 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
@@ -8,14 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use borrow_check::nll::constraints::OutlivesConstraint;
+use borrow_check::nll::constraints::{OutlivesConstraint, ConstraintCategory};
 use borrow_check::nll::region_infer::RegionInferenceContext;
-use borrow_check::nll::type_check::Locations;
 use rustc::hir::def_id::DefId;
 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::{self, TyCtxt, RegionVid};
+use rustc::mir::{Location, Mir};
+use rustc::ty::{self, RegionVid};
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_errors::{Diagnostic, DiagnosticBuilder};
 use std::collections::VecDeque;
@@ -28,19 +27,6 @@ mod var_name;
 
 use self::region_name::RegionName;
 
-/// Constraints that are considered interesting can be categorized to
-/// determine why they are interesting. Order of variants indicates
-/// sort order of the category, thereby influencing diagnostic output.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
-enum ConstraintCategory {
-    Cast,
-    Assignment,
-    Return,
-    CallArgument,
-    Other,
-    Boring,
-}
-
 impl fmt::Display for ConstraintCategory {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         // Must end with a space. Allows for empty names to be provided.
@@ -49,7 +35,14 @@ impl fmt::Display for ConstraintCategory {
             ConstraintCategory::Return => write!(f, "returning this value "),
             ConstraintCategory::Cast => write!(f, "cast "),
             ConstraintCategory::CallArgument => write!(f, "argument "),
-            _ => write!(f, ""),
+            ConstraintCategory::TypeAnnotation => write!(f, "type annotation "),
+            ConstraintCategory::ClosureBounds => write!(f, "closure body "),
+            ConstraintCategory::SizedBound => write!(f, "proving this value is `Sized` "),
+            ConstraintCategory::CopyBound => write!(f, "copying this value "),
+            ConstraintCategory::OpaqueType => write!(f, "opaque type "),
+            ConstraintCategory::Boring
+            | ConstraintCategory::BoringNoLocation
+            | ConstraintCategory::Internal => write!(f, ""),
         }
     }
 }
@@ -71,7 +64,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     fn best_blame_constraint(
         &self,
         mir: &Mir<'tcx>,
-        tcx: TyCtxt<'_, '_, 'tcx>,
         from_region: RegionVid,
         target_test: impl Fn(RegionVid) -> bool,
     ) -> (ConstraintCategory, Span, RegionVid) {
@@ -96,7 +88,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // Classify each of the constraints along the path.
         let mut categorized_path: Vec<(ConstraintCategory, Span)> = path
             .iter()
-            .map(|&index| self.classify_constraint(index, mir, tcx))
+            .map(|constraint| (constraint.category, constraint.locations.span(mir)))
             .collect();
         debug!(
             "best_blame_constraint: categorized_path={:#?}",
@@ -129,12 +121,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
 
             match categorized_path[i].0 {
-                ConstraintCategory::Boring => false,
-                ConstraintCategory::Other => {
-                    // other isn't interesting when the two lifetimes
-                    // are unified.
-                    constraint_sup_scc != self.constraint_sccs.scc(constraint.sub)
-                }
+                ConstraintCategory::OpaqueType
+                | ConstraintCategory::Boring
+                | ConstraintCategory::BoringNoLocation
+                | ConstraintCategory::Internal => false,
                 _ => constraint_sup_scc != target_scc,
             }
         });
@@ -220,106 +210,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         None
     }
 
-    /// 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, constraint: OutlivesConstraint) -> bool {
-        debug!(
-            "constraint_is_interesting: locations={:?} constraint={:?}",
-            constraint.locations, constraint
-        );
-
-        match constraint.locations {
-            Locations::Interesting(_) | Locations::All => true,
-            _ => false,
-        }
-    }
-
-    /// This function classifies a constraint from a location.
-    fn classify_constraint(
-        &self,
-        constraint: OutlivesConstraint,
-        mir: &Mir<'tcx>,
-        tcx: TyCtxt<'_, '_, 'tcx>,
-    ) -> (ConstraintCategory, Span) {
-        debug!("classify_constraint: constraint={:?}", constraint);
-        let span = constraint.locations.span(mir);
-        let location = constraint
-            .locations
-            .from_location()
-            .unwrap_or(Location::START);
-
-        if !self.constraint_is_interesting(constraint) {
-            return (ConstraintCategory::Boring, span);
-        }
-
-        let data = &mir[location.block];
-        debug!(
-            "classify_constraint: location={:?} data={:?}",
-            location, data
-        );
-        let category = if location.statement_index == data.statements.len() {
-            if let Some(ref terminator) = data.terminator {
-                debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
-                match terminator.kind {
-                    TerminatorKind::DropAndReplace { .. } => ConstraintCategory::Assignment,
-                    // Classify calls differently depending on whether or not
-                    // the sub region appears in the destination type (so the
-                    // sup region is in the return type). If the return type
-                    // contains the sub-region, then this is either an
-                    // assignment or a return, depending on whether we are
-                    // writing to the RETURN_PLACE or not.
-                    //
-                    // The idea here is that the region is being propagated
-                    // from an input into the output place, so it's a kind of
-                    // assignment. Otherwise, if the sub-region only appears in
-                    // the argument types, then use the CallArgument
-                    // classification.
-                    TerminatorKind::Call { destination: Some((ref place, _)), .. } => {
-                        if tcx.any_free_region_meets(
-                            &place.ty(mir, tcx).to_ty(tcx),
-                            |region| self.to_region_vid(region) == constraint.sub,
-                        ) {
-                            match place {
-                                Place::Local(mir::RETURN_PLACE) => ConstraintCategory::Return,
-                                _ => ConstraintCategory::Assignment,
-                            }
-                        } else {
-                            ConstraintCategory::CallArgument
-                        }
-                    }
-                    TerminatorKind::Call { destination: None, .. } => {
-                        ConstraintCategory::CallArgument
-                    }
-                    _ => ConstraintCategory::Other,
-                }
-            } else {
-                ConstraintCategory::Other
-            }
-        } else {
-            let statement = &data.statements[location.statement_index];
-            debug!("classify_constraint: statement.kind={:?}", statement.kind);
-            match statement.kind {
-                StatementKind::Assign(ref place, ref rvalue) => {
-                    debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue);
-                    if *place == Place::Local(mir::RETURN_PLACE) {
-                        ConstraintCategory::Return
-                    } else {
-                        match rvalue {
-                            Rvalue::Cast(..) => ConstraintCategory::Cast,
-                            Rvalue::Use(..) | Rvalue::Aggregate(..) => {
-                                ConstraintCategory::Assignment
-                            }
-                            _ => ConstraintCategory::Other,
-                        }
-                    }
-                }
-                _ => ConstraintCategory::Other,
-            }
-        };
-
-        (category, span)
-    }
-
     /// Report an error because the universal region `fr` was required to outlive
     /// `outlived_fr` but it is not known to do so. For example:
     ///
@@ -341,7 +231,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         let (category, span, _) = self.best_blame_constraint(
             mir,
-            infcx.tcx,
             fr,
             |r| r == outlived_fr
         );
@@ -574,11 +463,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     crate fn find_outlives_blame_span(
         &self,
         mir: &Mir<'tcx>,
-        tcx: TyCtxt<'_, '_, 'tcx>,
         fr1: RegionVid,
         fr2: RegionVid,
     ) -> Span {
-        let (_, span, _) = self.best_blame_constraint(mir, tcx, fr1, |r| r == fr2);
+        let (_, span, _) = self.best_blame_constraint(mir, fr1, |r| r == fr2);
         span
     }
 }
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
index d1713a520a7..75f14a6bbda 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -1062,7 +1062,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 longer_fr, shorter_fr,
             );
 
-            let blame_span = self.find_outlives_blame_span(mir, infcx.tcx, longer_fr, shorter_fr);
+            let blame_span = self.find_outlives_blame_span(mir, longer_fr, shorter_fr);
 
             if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
                 // Shrink `fr` until we find a non-local region (if we do).
@@ -1147,7 +1147,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         };
 
         // Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
-        let span = self.find_outlives_blame_span(mir, infcx.tcx, longer_fr, error_region);
+        let span = self.find_outlives_blame_span(mir, longer_fr, error_region);
 
         // Obviously, this error message is far from satisfactory.
         // At present, though, it only appears in unit tests --
diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs
index 64a61972a22..430c8d67392 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use borrow_check::location::LocationTable;
-use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
+use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, OutlivesConstraint};
 use borrow_check::nll::facts::AllFacts;
 use borrow_check::nll::region_infer::{RegionTest, TypeTest};
 use borrow_check::nll::type_check::Locations;
@@ -30,6 +30,7 @@ crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> {
     implicit_region_bound: Option<ty::Region<'tcx>>,
     param_env: ty::ParamEnv<'tcx>,
     locations: Locations,
+    category: ConstraintCategory,
     outlives_constraints: &'a mut ConstraintSet,
     type_tests: &'a mut Vec<TypeTest<'tcx>>,
     all_facts: &'a mut Option<AllFacts>,
@@ -44,6 +45,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
         implicit_region_bound: Option<ty::Region<'tcx>>,
         param_env: ty::ParamEnv<'tcx>,
         locations: Locations,
+        category: ConstraintCategory,
         outlives_constraints: &'a mut ConstraintSet,
         type_tests: &'a mut Vec<TypeTest<'tcx>>,
         all_facts: &'a mut Option<AllFacts>,
@@ -56,6 +58,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
             implicit_region_bound,
             param_env,
             locations,
+            category,
             outlives_constraints,
             type_tests,
             all_facts,
@@ -183,6 +186,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
     fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) {
         self.outlives_constraints.push(OutlivesConstraint {
             locations: self.locations,
+            category: self.category,
             sub,
             sup,
         });
diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs
index e21c490622c..61c612b3c01 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs
@@ -14,6 +14,7 @@ use borrow_check::nll::type_check::constraint_conversion;
 use borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints};
 use borrow_check::nll::universal_regions::UniversalRegions;
 use borrow_check::nll::ToRegionVid;
+use borrow_check::nll::constraints::ConstraintCategory;
 use rustc::infer::canonical::QueryRegionConstraint;
 use rustc::infer::outlives::free_region_map::FreeRegionRelations;
 use rustc::infer::region_constraints::GenericKind;
@@ -23,6 +24,7 @@ use rustc::traits::query::type_op::{self, TypeOp};
 use rustc::ty::{self, RegionVid, Ty};
 use rustc_data_structures::transitive_relation::TransitiveRelation;
 use std::rc::Rc;
+use syntax_pos::DUMMY_SP;
 
 #[derive(Debug)]
 crate struct UniversalRegionRelations<'tcx> {
@@ -283,7 +285,8 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> {
                 &self.region_bound_pairs,
                 self.implicit_region_bound,
                 self.param_env,
-                Locations::All,
+                Locations::All(DUMMY_SP),
+                ConstraintCategory::Internal,
                 &mut self.constraints.outlives_constraints,
                 &mut self.constraints.type_tests,
                 &mut self.all_facts,
diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs
index 265cd305eb9..6c7e2e9b72e 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs
@@ -29,8 +29,9 @@ use rustc::ty::subst::Subst;
 use rustc::ty::Ty;
 
 use rustc_data_structures::indexed_vec::Idx;
+use syntax_pos::Span;
 
-use super::{Locations, TypeChecker};
+use super::{ConstraintCategory, Locations, TypeChecker};
 
 impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     pub(super) fn equate_inputs_and_outputs(
@@ -56,7 +57,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             );
 
             let mir_input_ty = mir.local_decls[local].ty;
-            self.equate_normalized_input_or_output(normalized_input_ty, mir_input_ty);
+            let mir_input_span = mir.local_decls[local].source_info.span;
+            self.equate_normalized_input_or_output(
+                normalized_input_ty,
+                mir_input_ty,
+                mir_input_span,
+            );
         }
 
         assert!(
@@ -65,16 +71,19 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         );
         if let Some(mir_yield_ty) = mir.yield_ty {
             let ur_yield_ty = universal_regions.yield_ty.unwrap();
-            self.equate_normalized_input_or_output(ur_yield_ty, mir_yield_ty);
+            let yield_span = mir.local_decls[RETURN_PLACE].source_info.span;
+            self.equate_normalized_input_or_output(ur_yield_ty, mir_yield_ty, yield_span);
         }
 
         // Return types are a bit more complex. They may contain existential `impl Trait`
         // types.
         let param_env = self.param_env;
         let mir_output_ty = mir.local_decls[RETURN_PLACE].ty;
+        let output_span = mir.local_decls[RETURN_PLACE].source_info.span;
         let opaque_type_map =
             self.fully_perform_op(
-                Locations::All,
+                Locations::All(output_span),
+                ConstraintCategory::BoringNoLocation,
                 CustomTypeOp::new(
                     |infcx| {
                         let mut obligations = ObligationAccumulator::default();
@@ -152,26 +161,38 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         // prove that `T: Iterator` where `T` is the type we
         // instantiated it with).
         if let Some(opaque_type_map) = opaque_type_map {
-            self.fully_perform_op(
-                Locations::All,
-                CustomTypeOp::new(
-                    |_cx| {
-                        infcx.constrain_opaque_types(&opaque_type_map, universal_region_relations);
-                        Ok(InferOk {
-                            value: (),
-                            obligations: vec![],
-                        })
-                    },
-                    || "opaque_type_map".to_string(),
-                ),
-            ).unwrap();
+            for (opaque_def_id, opaque_decl) in opaque_type_map {
+                self.fully_perform_op(
+                    Locations::All(infcx.tcx.def_span(opaque_def_id)),
+                    ConstraintCategory::OpaqueType,
+                    CustomTypeOp::new(
+                        |_cx| {
+                            infcx.constrain_opaque_type(
+                                opaque_def_id,
+                                &opaque_decl,
+                                universal_region_relations
+                            );
+                            Ok(InferOk {
+                                value: (),
+                                obligations: vec![],
+                            })
+                        },
+                        || "opaque_type_map".to_string(),
+                    ),
+                ).unwrap();
+            }
         }
     }
 
-    fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
+    fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
         debug!("equate_normalized_input_or_output(a={:?}, b={:?})", a, b);
 
-        if let Err(terr) = self.eq_types(a, b, Locations::All) {
+        if let Err(terr) = self.eq_types(
+            a,
+            b,
+            Locations::All(span),
+            ConstraintCategory::BoringNoLocation,
+        ) {
             span_mirbug!(
                 self,
                 Location::START,
diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
index 47e6ce05cec..e706c1adadd 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
@@ -8,10 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use borrow_check::nll::constraints::ConstraintCategory;
 use borrow_check::nll::region_infer::values::{self, PointIndex, RegionValueElements};
 use borrow_check::nll::type_check::liveness::liveness_map::{LiveVar, NllLivenessMap};
 use borrow_check::nll::type_check::liveness::local_use_map::LocalUseMap;
-use borrow_check::nll::type_check::AtLocation;
+use borrow_check::nll::type_check::NormalizeLocation;
 use borrow_check::nll::type_check::TypeChecker;
 use dataflow::move_paths::indexes::MovePathIndex;
 use dataflow::move_paths::MoveData;
@@ -487,7 +488,11 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
         if let Some(data) = &drop_data.region_constraint_data {
             for &drop_location in drop_locations {
                 self.typeck
-                    .push_region_constraints(drop_location.boring(), data);
+                    .push_region_constraints(
+                        drop_location.to_locations(),
+                        ConstraintCategory::Boring,
+                        data,
+                    );
             }
         }
 
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index de96539ec30..c58518011aa 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -13,7 +13,7 @@
 
 use borrow_check::borrow_set::BorrowSet;
 use borrow_check::location::LocationTable;
-use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
+use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, OutlivesConstraint};
 use borrow_check::nll::facts::AllFacts;
 use borrow_check::nll::region_infer::values::{LivenessValues, RegionValueElements};
 use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
@@ -252,7 +252,8 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
                 constant.ty,
                 ty::Variance::Invariant,
                 user_ty,
-                location.boring(),
+                location.to_locations(),
+                ConstraintCategory::Boring,
             ) {
                 span_mirbug!(
                     self,
@@ -281,7 +282,8 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
                 local_decl.ty,
                 ty::Variance::Invariant,
                 user_ty,
-                Locations::All,
+                Locations::All(local_decl.source_info.span),
+                ConstraintCategory::TypeAnnotation,
             ) {
                 span_mirbug!(
                     self,
@@ -364,14 +366,19 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
             let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
             type_checker.normalize_and_prove_instantiated_predicates(
                 instantiated_predicates,
-                location.boring(),
+                location.to_locations(),
             );
         }
 
         debug!("sanitize_constant: expected_ty={:?}", constant.literal.ty);
 
         if let Err(terr) = self.cx
-            .eq_types(constant.literal.ty, constant.ty, location.boring())
+            .eq_types(
+                constant.literal.ty,
+                constant.ty,
+                location.to_locations(),
+                ConstraintCategory::Boring,
+            )
         {
             span_mirbug!(
                 self,
@@ -417,7 +424,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
                 let sty = self.sanitize_type(place, sty);
                 let ty = self.tcx().type_of(def_id);
                 let ty = self.cx.normalize(ty, location);
-                if let Err(terr) = self.cx.eq_types(ty, sty, location.boring()) {
+                if let Err(terr) = self.cx.eq_types(
+                    ty,
+                    sty,
+                    location.to_locations(),
+                    ConstraintCategory::Boring,
+                ) {
                     span_mirbug!(
                         self,
                         place,
@@ -461,7 +473,11 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
             // (e.g., #29149). Note that we decide to use Copy before knowing whether the bounds
             // fully apply: in effect, the rule is that if a value of some type could implement
             // Copy, then it must.
-            self.cx.prove_trait_ref(trait_ref, location.interesting());
+            self.cx.prove_trait_ref(
+                trait_ref,
+                location.to_locations(),
+                ConstraintCategory::CopyBound,
+            );
         }
         place_ty
     }
@@ -560,7 +576,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
             ProjectionElem::Field(field, fty) => {
                 let fty = self.sanitize_type(place, fty);
                 match self.field_ty(place, base, field, location) {
-                    Ok(ty) => if let Err(terr) = self.cx.eq_types(ty, fty, location.boring()) {
+                    Ok(ty) => if let Err(terr) = self.cx.eq_types(
+                        ty,
+                        fty,
+                        location.to_locations(),
+                        ConstraintCategory::Boring,
+                    ) {
                         span_mirbug!(
                             self,
                             place,
@@ -740,43 +761,32 @@ pub enum Locations {
     /// user-given type annotations; e.g., if the user wrote `let mut
     /// x: &'static u32 = ...`, we would ensure that all values
     /// assigned to `x` are of `'static` lifetime.
-    All,
-
-    /// A "boring" constraint (caused by the given location) is one that
-    /// the user probably doesn't want to see described in diagnostics,
-    /// because it is kind of an artifact of the type system setup.
     ///
-    /// Example: `x = Foo { field: y }` technically creates
-    /// intermediate regions representing the "type of `Foo { field: y
-    /// }`", and data flows from `y` into those variables, but they
-    /// are not very interesting. The assignment into `x` on the other
-    /// hand might be.
-    Boring(Location),
-
-    /// An *important* outlives constraint (caused by the given
-    /// location) is one that would be useful to highlight in
-    /// diagnostics, because it represents a point where references
-    /// flow from one spot to another (e.g., `x = y`)
-    Interesting(Location),
+    /// The span points to the place the constraint arose. For example,
+    /// it points to the type in a user-given type annotation. If
+    /// there's no sensible span then it's DUMMY_SP.
+    All(Span),
+
+    /// An outlives constraint that only has to hold at a single location,
+    /// usually it represents a point where references flow from one spot to
+    /// another (e.g., `x = y`)
+    Single(Location),
 }
 
 impl Locations {
     pub fn from_location(&self) -> Option<Location> {
         match self {
-            Locations::All => None,
-            Locations::Boring(from_location) | Locations::Interesting(from_location) => {
-                Some(*from_location)
-            }
+            Locations::All(_) => None,
+            Locations::Single(from_location) => Some(*from_location),
         }
     }
 
     /// Gets a span representing the location.
     pub fn span(&self, mir: &Mir<'_>) -> Span {
-        let span_location = match self {
-            Locations::All => Location::START,
-            Locations::Boring(l) | Locations::Interesting(l) => *l,
-        };
-        mir.source_info(span_location).span
+        match self {
+            Locations::All(span) => *span,
+            Locations::Single(l) => mir.source_info(*l).span,
+        }
     }
 }
 
@@ -816,12 +826,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     fn fully_perform_op<R>(
         &mut self,
         locations: Locations,
+        category: ConstraintCategory,
         op: impl type_op::TypeOp<'gcx, 'tcx, Output = R>,
     ) -> Fallible<R> {
         let (r, opt_data) = op.fully_perform(self.infcx)?;
 
         if let Some(data) = &opt_data {
-            self.push_region_constraints(locations, data);
+            self.push_region_constraints(locations, category, data);
         }
 
         Ok(r)
@@ -830,6 +841,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     fn push_region_constraints(
         &mut self,
         locations: Locations,
+        category: ConstraintCategory,
         data: &[QueryRegionConstraint<'tcx>],
     ) {
         debug!(
@@ -846,6 +858,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 self.implicit_region_bound,
                 self.param_env,
                 locations,
+                category,
                 &mut borrowck_context.constraints.outlives_constraints,
                 &mut borrowck_context.constraints.type_tests,
                 &mut borrowck_context.all_facts,
@@ -853,22 +866,36 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>, locations: Locations) -> Fallible<()> {
+    fn sub_types(
+        &mut self,
+        sub: Ty<'tcx>,
+        sup: Ty<'tcx>,
+        locations: Locations,
+        category: ConstraintCategory,
+    ) -> Fallible<()> {
         relate_tys::sub_types(
             self.infcx,
             sub,
             sup,
             locations,
+            category,
             self.borrowck_context.as_mut().map(|x| &mut **x),
         )
     }
 
-    fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> Fallible<()> {
+    fn eq_types(
+        &mut self,
+        a: Ty<'tcx>,
+        b: Ty<'tcx>,
+        locations: Locations,
+        category: ConstraintCategory,
+    ) -> Fallible<()> {
         relate_tys::eq_types(
             self.infcx,
             a,
             b,
             locations,
+            category,
             self.borrowck_context.as_mut().map(|x| &mut **x),
         )
     }
@@ -879,6 +906,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         v: ty::Variance,
         b: CanonicalTy<'tcx>,
         locations: Locations,
+        category: ConstraintCategory,
     ) -> Fallible<()> {
         relate_tys::relate_type_and_user_type(
             self.infcx,
@@ -886,6 +914,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             v,
             b,
             locations,
+            category,
             self.borrowck_context.as_mut().map(|x| &mut **x),
         )
     }
@@ -903,21 +932,22 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 // they are not caused by the user, but rather artifacts
                 // of lowering. Assignments to other sorts of places *are* interesting
                 // though.
-                let is_temp = if let Place::Local(l) = *place {
-                    l != RETURN_PLACE && !mir.local_decls[l].is_user_variable.is_some()
-                } else {
-                    false
-                };
-
-                let locations = if is_temp {
-                    location.boring()
-                } else {
-                    location.interesting()
+                let category = match *place {
+                    Place::Local(RETURN_PLACE) => ConstraintCategory::Return,
+                    Place::Local(l) if !mir.local_decls[l].is_user_variable.is_some() => {
+                        ConstraintCategory::Boring
+                    }
+                    _ => ConstraintCategory::Assignment,
                 };
 
                 let place_ty = place.ty(mir, tcx).to_ty(tcx);
                 let rv_ty = rv.ty(mir, tcx);
-                if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) {
+                if let Err(terr) = self.sub_types(
+                    rv_ty,
+                    place_ty,
+                    location.to_locations(),
+                    category,
+                ) {
                     span_mirbug!(
                         self,
                         stmt,
@@ -933,7 +963,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                         rv_ty,
                         ty::Variance::Invariant,
                         user_ty,
-                        location.boring(),
+                        location.to_locations(),
+                        ConstraintCategory::Boring,
                     ) {
                         span_mirbug!(
                             self,
@@ -952,7 +983,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                         def_id: tcx.lang_items().sized_trait().unwrap(),
                         substs: tcx.mk_substs_trait(place_ty, &[]),
                     };
-                    self.prove_trait_ref(trait_ref, location.interesting());
+                    self.prove_trait_ref(
+                        trait_ref,
+                        location.to_locations(),
+                    ConstraintCategory::SizedBound,
+                    );
                 }
             }
             StatementKind::SetDiscriminant {
@@ -983,7 +1018,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             StatementKind::AscribeUserType(ref place, variance, c_ty) => {
                 let place_ty = place.ty(mir, tcx).to_ty(tcx);
                 if let Err(terr) =
-                    self.relate_type_and_user_type(place_ty, variance, c_ty, Locations::All)
+                    self.relate_type_and_user_type(
+                        place_ty,
+                        variance,
+                        c_ty,
+                        Locations::All(stmt.source_info.span),
+                        ConstraintCategory::TypeAnnotation,
+                    )
                 {
                     span_mirbug!(
                         self,
@@ -1035,8 +1076,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 let place_ty = location.ty(mir, tcx).to_ty(tcx);
                 let rv_ty = value.ty(mir, tcx);
 
-                let locations = term_location.interesting();
-                if let Err(terr) = self.sub_types(rv_ty, place_ty, locations) {
+                let locations = term_location.to_locations();
+                if let Err(terr) = self.sub_types(
+                    rv_ty,
+                    place_ty,
+                    locations,
+                    ConstraintCategory::Assignment,
+                ) {
                     span_mirbug!(
                         self,
                         term,
@@ -1053,7 +1099,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 ..
             } => {
                 let discr_ty = discr.ty(mir, tcx);
-                if let Err(terr) = self.sub_types(discr_ty, switch_ty, term_location.boring()) {
+                if let Err(terr) = self.sub_types(
+                    discr_ty,
+                    switch_ty,
+                    term_location.to_locations(),
+                    ConstraintCategory::Assignment,
+                ) {
                     span_mirbug!(
                         self,
                         term,
@@ -1093,7 +1144,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
 
                 self.prove_predicates(
                     sig.inputs().iter().map(|ty| ty::Predicate::WellFormed(ty)),
-                    term_location.boring(),
+                    term_location.to_locations(),
+                    ConstraintCategory::Boring,
                 );
 
                 // The ordinary liveness rules will ensure that all
@@ -1139,7 +1191,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 match mir.yield_ty {
                     None => span_mirbug!(self, term, "yield in non-generator"),
                     Some(ty) => {
-                        if let Err(terr) = self.sub_types(value_ty, ty, term_location.interesting())
+                        if let Err(terr) = self.sub_types(
+                            value_ty,
+                            ty,
+                            term_location.to_locations(),
+                            ConstraintCategory::Return,
+                        )
                         {
                             span_mirbug!(
                                 self,
@@ -1168,19 +1225,22 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         match *destination {
             Some((ref dest, _target_block)) => {
                 let dest_ty = dest.ty(mir, tcx).to_ty(tcx);
-                let is_temp = if let Place::Local(l) = *dest {
-                    l != RETURN_PLACE && !mir.local_decls[l].is_user_variable.is_some()
-                } else {
-                    false
+                let category = match *dest {
+                    Place::Local(RETURN_PLACE) => ConstraintCategory::Return,
+                    Place::Local(l) if !mir.local_decls[l].is_user_variable.is_some() => {
+                        ConstraintCategory::Boring
+                    }
+                    _ => ConstraintCategory::Assignment,
                 };
 
-                let locations = if is_temp {
-                    term_location.boring()
-                } else {
-                    term_location.interesting()
-                };
+                let locations = term_location.to_locations();
 
-                if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations) {
+                if let Err(terr) = self.sub_types(
+                    sig.output(),
+                    dest_ty,
+                    locations,
+                    category,
+                ) {
                     span_mirbug!(
                         self,
                         term,
@@ -1221,7 +1281,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         }
         for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() {
             let op_arg_ty = op_arg.ty(mir, self.tcx());
-            if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, term_location.interesting()) {
+            if let Err(terr) = self.sub_types(
+                op_arg_ty,
+                fn_arg,
+                term_location.to_locations(),
+                ConstraintCategory::CallArgument,
+            ) {
                 span_mirbug!(
                     self,
                     term,
@@ -1470,7 +1535,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     substs: tcx.mk_substs_trait(operand_ty, &[]),
                 };
 
-                self.prove_trait_ref(trait_ref, location.interesting());
+                self.prove_trait_ref(
+                    trait_ref,
+                    location.to_locations(),
+                    ConstraintCategory::CopyBound,
+                );
             },
 
             Rvalue::NullaryOp(_, ty) => {
@@ -1485,24 +1554,34 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     substs: tcx.mk_substs_trait(ty, &[]),
                 };
 
-                self.prove_trait_ref(trait_ref, location.interesting());
+                self.prove_trait_ref(
+                    trait_ref,
+                    location.to_locations(),
+                    ConstraintCategory::SizedBound,
+                );
             }
 
-            Rvalue::Cast(cast_kind, op, ty) => match cast_kind {
-                CastKind::ReifyFnPointer => {
-                    let fn_sig = op.ty(mir, tcx).fn_sig(tcx);
+            Rvalue::Cast(cast_kind, op, ty) => {
+                match cast_kind {
+                    CastKind::ReifyFnPointer => {
+                        let fn_sig = op.ty(mir, tcx).fn_sig(tcx);
 
-                    // The type that we see in the fcx is like
-                    // `foo::<'a, 'b>`, where `foo` is the path to a
-                    // function definition. When we extract the
-                    // signature, it comes from the `fn_sig` query,
-                    // and hence may contain unnormalized results.
-                    let fn_sig = self.normalize(fn_sig, location);
+                        // The type that we see in the fcx is like
+                        // `foo::<'a, 'b>`, where `foo` is the path to a
+                        // function definition. When we extract the
+                        // signature, it comes from the `fn_sig` query,
+                        // and hence may contain unnormalized results.
+                        let fn_sig = self.normalize(fn_sig, location);
 
-                    let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);
+                        let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);
 
-                    if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.interesting()) {
-                        span_mirbug!(
+                        if let Err(terr) = self.eq_types(
+                            ty_fn_ptr_from,
+                            ty,
+                            location.to_locations(),
+                            ConstraintCategory::Cast,
+                        ) {
+                            span_mirbug!(
                             self,
                             rvalue,
                             "equating {:?} with {:?} yields {:?}",
@@ -1510,20 +1589,25 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                             ty,
                             terr
                         );
+                        }
                     }
-                }
 
-                CastKind::ClosureFnPointer => {
-                    let sig = match op.ty(mir, tcx).sty {
-                        ty::Closure(def_id, substs) => {
-                            substs.closure_sig_ty(def_id, tcx).fn_sig(tcx)
-                        }
-                        _ => bug!(),
-                    };
-                    let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig);
+                    CastKind::ClosureFnPointer => {
+                        let sig = match op.ty(mir, tcx).sty {
+                            ty::Closure(def_id, substs) => {
+                                substs.closure_sig_ty(def_id, tcx).fn_sig(tcx)
+                            }
+                            _ => bug!(),
+                        };
+                        let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig);
 
-                    if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.interesting()) {
-                        span_mirbug!(
+                        if let Err(terr) = self.eq_types(
+                            ty_fn_ptr_from,
+                            ty,
+                            location.to_locations(),
+                            ConstraintCategory::Cast,
+                        ) {
+                            span_mirbug!(
                             self,
                             rvalue,
                             "equating {:?} with {:?} yields {:?}",
@@ -1531,23 +1615,28 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                             ty,
                             terr
                         );
+                        }
                     }
-                }
 
-                CastKind::UnsafeFnPointer => {
-                    let fn_sig = op.ty(mir, tcx).fn_sig(tcx);
+                    CastKind::UnsafeFnPointer => {
+                        let fn_sig = op.ty(mir, tcx).fn_sig(tcx);
 
-                    // The type that we see in the fcx is like
-                    // `foo::<'a, 'b>`, where `foo` is the path to a
-                    // function definition. When we extract the
-                    // signature, it comes from the `fn_sig` query,
-                    // and hence may contain unnormalized results.
-                    let fn_sig = self.normalize(fn_sig, location);
+                        // The type that we see in the fcx is like
+                        // `foo::<'a, 'b>`, where `foo` is the path to a
+                        // function definition. When we extract the
+                        // signature, it comes from the `fn_sig` query,
+                        // and hence may contain unnormalized results.
+                        let fn_sig = self.normalize(fn_sig, location);
 
-                    let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig);
+                        let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig);
 
-                    if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.interesting()) {
-                        span_mirbug!(
+                        if let Err(terr) = self.eq_types(
+                            ty_fn_ptr_from,
+                            ty,
+                            location.to_locations(),
+                            ConstraintCategory::Cast,
+                        ) {
+                            span_mirbug!(
                             self,
                             rvalue,
                             "equating {:?} with {:?} yields {:?}",
@@ -1555,21 +1644,26 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                             ty,
                             terr
                         );
+                        }
                     }
-                }
 
-                CastKind::Unsize => {
-                    let &ty = ty;
-                    let trait_ref = ty::TraitRef {
-                        def_id: tcx.lang_items().coerce_unsized_trait().unwrap(),
-                        substs: tcx.mk_substs_trait(op.ty(mir, tcx), &[ty.into()]),
-                    };
+                    CastKind::Unsize => {
+                        let &ty = ty;
+                        let trait_ref = ty::TraitRef {
+                            def_id: tcx.lang_items().coerce_unsized_trait().unwrap(),
+                            substs: tcx.mk_substs_trait(op.ty(mir, tcx), &[ty.into()]),
+                        };
 
-                    self.prove_trait_ref(trait_ref, location.interesting());
-                }
+                        self.prove_trait_ref(
+                            trait_ref,
+                            location.to_locations(),
+                            ConstraintCategory::Cast,
+                        );
+                    }
 
-                CastKind::Misc => {}
-            },
+                    CastKind::Misc => {}
+                }
+            }
 
             Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
                 self.add_reborrow_constraint(location, region, borrowed_place);
@@ -1644,7 +1738,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             };
             let operand_ty = operand.ty(mir, tcx);
 
-            if let Err(terr) = self.sub_types(operand_ty, field_ty, location.boring()) {
+            if let Err(terr) = self.sub_types(
+                operand_ty,
+                field_ty,
+                location.to_locations(),
+                ConstraintCategory::Boring,
+            ) {
                 span_mirbug!(
                     self,
                     rvalue,
@@ -1723,7 +1822,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                             constraints.outlives_constraints.push(OutlivesConstraint {
                                 sup: ref_region.to_region_vid(),
                                 sub: borrow_region.to_region_vid(),
-                                locations: location.boring(),
+                                locations: location.to_locations(),
+                                category: ConstraintCategory::Boring,
                             });
 
                             if let Some(all_facts) = all_facts {
@@ -1839,8 +1939,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                         *substs,
                     );
 
-                    // Hmm, are these constraints *really* boring?
-                    self.push_region_constraints(location.boring(), &closure_constraints);
+                    self.push_region_constraints(
+                        location.to_locations(),
+                        ConstraintCategory::ClosureBounds,
+                        &closure_constraints,
+                    );
                 }
 
                 tcx.predicates_of(*def_id).instantiate(tcx, substs.substs)
@@ -1855,16 +1958,22 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
 
         self.normalize_and_prove_instantiated_predicates(
             instantiated_predicates,
-            location.boring(),
+            location.to_locations(),
         );
     }
 
-    fn prove_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>, locations: Locations) {
+    fn prove_trait_ref(
+        &mut self,
+        trait_ref: ty::TraitRef<'tcx>,
+        locations: Locations,
+        category: ConstraintCategory,
+    ) {
         self.prove_predicates(
             Some(ty::Predicate::Trait(
                 trait_ref.to_poly_trait_ref().to_poly_trait_predicate(),
             )),
             locations,
+            category,
         );
     }
 
@@ -1875,7 +1984,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     ) {
         for predicate in instantiated_predicates.predicates {
             let predicate = self.normalize(predicate, locations);
-            self.prove_predicate(predicate, locations);
+            self.prove_predicate(predicate, locations, ConstraintCategory::Boring);
         }
     }
 
@@ -1883,6 +1992,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         &mut self,
         predicates: impl IntoIterator<Item = ty::Predicate<'tcx>>,
         locations: Locations,
+        category: ConstraintCategory,
     ) {
         for predicate in predicates {
             debug!(
@@ -1890,11 +2000,16 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 predicate, locations,
             );
 
-            self.prove_predicate(predicate, locations);
+            self.prove_predicate(predicate, locations, category);
         }
     }
 
-    fn prove_predicate(&mut self, predicate: ty::Predicate<'tcx>, locations: Locations) {
+    fn prove_predicate(
+        &mut self,
+        predicate: ty::Predicate<'tcx>,
+        locations: Locations,
+        category: ConstraintCategory,
+    ) {
         debug!(
             "prove_predicate(predicate={:?}, location={:?})",
             predicate, locations,
@@ -1903,6 +2018,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         let param_env = self.param_env;
         self.fully_perform_op(
             locations,
+            category,
             param_env.and(type_op::prove_predicate::ProvePredicate::new(predicate)),
         ).unwrap_or_else(|NoSolution| {
             span_mirbug!(self, NoSolution, "could not prove {:?}", predicate);
@@ -1943,6 +2059,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         let param_env = self.param_env;
         self.fully_perform_op(
             location.to_locations(),
+            ConstraintCategory::Boring,
             param_env.and(type_op::normalize::Normalize::new(value)),
         ).unwrap_or_else(|NoSolution| {
             span_mirbug!(self, NoSolution, "failed to normalize `{:?}`", value);
@@ -1996,26 +2113,6 @@ impl MirPass for TypeckMir {
     }
 }
 
-pub trait AtLocation {
-    /// Indicates a "boring" constraint that the user probably
-    /// woudln't want to see highlights.
-    fn boring(self) -> Locations;
-
-    /// Indicates an "interesting" edge, which is of significance only
-    /// for diagnostics.
-    fn interesting(self) -> Locations;
-}
-
-impl AtLocation for Location {
-    fn boring(self) -> Locations {
-        Locations::Boring(self)
-    }
-
-    fn interesting(self) -> Locations {
-        Locations::Interesting(self)
-    }
-}
-
 trait NormalizeLocation: fmt::Debug + Copy {
     fn to_locations(self) -> Locations;
 }
@@ -2028,6 +2125,6 @@ impl NormalizeLocation for Locations {
 
 impl NormalizeLocation for Location {
     fn to_locations(self) -> Locations {
-        self.boring()
+        Locations::Single(self)
     }
 }
diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs
index 06cb44ac971..130b4b31d08 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use borrow_check::nll::constraints::OutlivesConstraint;
+use borrow_check::nll::constraints::{ConstraintCategory, OutlivesConstraint};
 use borrow_check::nll::type_check::{BorrowCheckContext, Locations};
 use borrow_check::nll::universal_regions::UniversalRegions;
 use borrow_check::nll::ToRegionVid;
@@ -28,6 +28,7 @@ pub(super) fn sub_types<'tcx>(
     a: Ty<'tcx>,
     b: Ty<'tcx>,
     locations: Locations,
+    category: ConstraintCategory,
     borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
 ) -> Fallible<()> {
     debug!("sub_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
@@ -35,6 +36,7 @@ pub(super) fn sub_types<'tcx>(
         infcx,
         ty::Variance::Covariant,
         locations,
+        category,
         borrowck_context,
         ty::List::empty(),
     ).relate(&a, &b)?;
@@ -47,6 +49,7 @@ pub(super) fn eq_types<'tcx>(
     a: Ty<'tcx>,
     b: Ty<'tcx>,
     locations: Locations,
+    category: ConstraintCategory,
     borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
 ) -> Fallible<()> {
     debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
@@ -54,6 +57,7 @@ pub(super) fn eq_types<'tcx>(
         infcx,
         ty::Variance::Invariant,
         locations,
+        category,
         borrowck_context,
         ty::List::empty(),
     ).relate(&a, &b)?;
@@ -69,6 +73,7 @@ pub(super) fn relate_type_and_user_type<'tcx>(
     v: ty::Variance,
     b: CanonicalTy<'tcx>,
     locations: Locations,
+    category: ConstraintCategory,
     borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
 ) -> Fallible<()> {
     debug!(
@@ -89,6 +94,7 @@ pub(super) fn relate_type_and_user_type<'tcx>(
         infcx,
         v1,
         locations,
+        category,
         borrowck_context,
         b_variables,
     ).relate(&b_value, &a)?;
@@ -124,6 +130,8 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
     /// Where (and why) is this relation taking place?
     locations: Locations,
 
+    category: ConstraintCategory,
+
     /// This will be `Some` when we are running the type check as part
     /// of NLL, and `None` if we are running a "sanity check".
     borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
@@ -161,6 +169,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
         infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
         ambient_variance: ty::Variance,
         locations: Locations,
+        category: ConstraintCategory,
         borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
         canonical_var_infos: CanonicalVarInfos<'tcx>,
     ) -> Self {
@@ -171,6 +180,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
             borrowck_context,
             locations,
             canonical_var_values,
+            category,
             a_scopes: vec![],
             b_scopes: vec![],
         }
@@ -264,6 +274,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
                     sup,
                     sub,
                     locations: self.locations,
+                    category: self.category,
                 });
 
             // FIXME all facts!