about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-06-18 16:37:10 +0000
committerbors <bors@rust-lang.org>2018-06-18 16:37:10 +0000
commitb36917b33104dc0af4d26d53899b7cd064a40b22 (patch)
tree6f781b30387cbb2d331220334840ca49feca7e15
parent862703e05e275d77b0b594bb5d1a26a6340933f2 (diff)
parent2e25bed9b1552a8af58fb3fe21d8db69a6114a18 (diff)
downloadrust-b36917b33104dc0af4d26d53899b7cd064a40b22.tar.gz
rust-b36917b33104dc0af4d26d53899b7cd064a40b22.zip
Auto merge of #51460 - nikomatsakis:nll-perf-examination-refactor-1, r=pnkfelix
Improve memoization and refactor NLL type check

I have a big branch that is refactoring NLL type check with the goal of introducing canonicalization-based memoization for all of the operations it does. This PR contains an initial prefix of that branch which, I believe, stands alone. It does introduce a few smaller optimizations of its own:

- Skip operations that are trivially a no-op
- Cache the results of the dropck-outlives computations done by liveness
- Skip resetting unifications if nothing changed

r? @pnkfelix
-rw-r--r--src/librustc/infer/region_constraints/mod.rs15
-rw-r--r--src/librustc_data_structures/indexed_vec.rs5
-rw-r--r--src/librustc_mir/borrow_check/nll/constraint_generation.rs47
-rw-r--r--src/librustc_mir/borrow_check/nll/mod.rs48
-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/graphviz.rs12
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/mod.rs62
-rw-r--r--src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs199
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs190
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/input_output.rs142
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/liveness.rs113
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs315
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr7
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr7
14 files changed, 685 insertions, 481 deletions
diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs
index 99d7b4eaf7d..296808cea2b 100644
--- a/src/librustc/infer/region_constraints/mod.rs
+++ b/src/librustc/infer/region_constraints/mod.rs
@@ -69,6 +69,10 @@ pub struct RegionConstraintCollector<'tcx> {
     /// would wind up with a fresh stream of region variables that
     /// have been equated but appear distinct.
     unification_table: ut::UnificationTable<ut::InPlace<ty::RegionVid>>,
+
+    /// a flag set to true when we perform any unifications; this is used
+    /// to micro-optimize `take_and_reset_data`
+    any_unifications: bool,
 }
 
 pub type VarInfos = IndexVec<RegionVid, RegionVariableInfo>;
@@ -234,6 +238,7 @@ pub struct RegionVariableInfo {
 pub struct RegionSnapshot {
     length: usize,
     region_snapshot: ut::Snapshot<ut::InPlace<ty::RegionVid>>,
+    any_unifications: bool,
 }
 
 /// When working with skolemized regions, we often wish to find all of
@@ -280,6 +285,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
             bound_count: 0,
             undo_log: Vec::new(),
             unification_table: ut::UnificationTable::new(),
+            any_unifications: false,
         }
     }
 
@@ -325,6 +331,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
             bound_count: _,
             undo_log: _,
             unification_table,
+            any_unifications,
         } = self;
 
         // Clear the tables of (lubs, glbs), so that we will create
@@ -338,7 +345,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         // un-unified" state. Note that when we unify `a` and `b`, we
         // also insert `a <= b` and a `b <= a` edges, so the
         // `RegionConstraintData` contains the relationship here.
-        unification_table.reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid });
+        if *any_unifications {
+            unification_table.reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid });
+            *any_unifications = false;
+        }
 
         mem::replace(data, RegionConstraintData::default())
     }
@@ -358,6 +368,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         RegionSnapshot {
             length,
             region_snapshot: self.unification_table.snapshot(),
+            any_unifications: self.any_unifications,
         }
     }
 
@@ -385,6 +396,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         let c = self.undo_log.pop().unwrap();
         assert!(c == OpenSnapshot);
         self.unification_table.rollback_to(snapshot.region_snapshot);
+        self.any_unifications = snapshot.any_unifications;
     }
 
     fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) {
@@ -623,6 +635,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
 
             if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) {
                 self.unification_table.union(sub, sup);
+                self.any_unifications = true;
             }
         }
     }
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
index 1fb63afc72f..ad3710e9536 100644
--- a/src/librustc_data_structures/indexed_vec.rs
+++ b/src/librustc_data_structures/indexed_vec.rs
@@ -368,6 +368,11 @@ impl<I: Idx, T> IndexVec<I, T> {
     }
 
     #[inline]
+    pub fn from_raw(raw: Vec<T>) -> Self {
+        IndexVec { raw, _marker: PhantomData }
+    }
+
+    #[inline]
     pub fn with_capacity(capacity: usize) -> Self {
         IndexVec { raw: Vec::with_capacity(capacity), _marker: PhantomData }
     }
diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
index 4f87a2b30ae..72db9f8da98 100644
--- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
@@ -11,6 +11,8 @@
 use borrow_check::borrow_set::BorrowSet;
 use borrow_check::location::LocationTable;
 use borrow_check::nll::facts::AllFacts;
+use borrow_check::nll::region_infer::{Cause, RegionInferenceContext};
+use borrow_check::nll::ToRegionVid;
 use rustc::hir;
 use rustc::infer::InferCtxt;
 use rustc::mir::visit::TyContext;
@@ -21,9 +23,7 @@ use rustc::mir::{Local, PlaceProjection, ProjectionElem, Statement, Terminator};
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorSubsts};
-
-use super::region_infer::{Cause, RegionInferenceContext};
-use super::ToRegionVid;
+use std::iter;
 
 pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
     infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
@@ -32,6 +32,7 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
     location_table: &LocationTable,
     mir: &Mir<'tcx>,
     borrow_set: &BorrowSet<'tcx>,
+    liveness_set_from_typeck: &[(ty::Region<'tcx>, Location, Cause)],
 ) {
     let mut cg = ConstraintGeneration {
         borrow_set,
@@ -42,6 +43,8 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
         mir,
     };
 
+    cg.add_region_liveness_constraints_from_type_check(liveness_set_from_typeck);
+
     for (bb, data) in mir.basic_blocks().iter_enumerated() {
         cg.visit_basic_block_data(bb, data);
     }
@@ -209,7 +212,7 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
                 self.add_reborrow_constraint(location, region, borrowed_place);
             }
 
-            _ => { }
+            _ => {}
         }
 
         self.super_rvalue(rvalue, location);
@@ -225,6 +228,42 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
 }
 
 impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
+    /// The MIR type checker generates region liveness constraints
+    /// that we also have to respect.
+    fn add_region_liveness_constraints_from_type_check(
+        &mut self,
+        liveness_set: &[(ty::Region<'tcx>, Location, Cause)],
+    ) {
+        debug!(
+            "add_region_liveness_constraints_from_type_check(liveness_set={} items)",
+            liveness_set.len(),
+        );
+
+        let ConstraintGeneration {
+            regioncx,
+            location_table,
+            all_facts,
+            ..
+        } = self;
+
+        for (region, location, cause) in liveness_set {
+            debug!("generate: {:#?} is live at {:#?}", region, location);
+            let region_vid = regioncx.to_region_vid(region);
+            regioncx.add_live_point(region_vid, *location, &cause);
+        }
+
+        if let Some(all_facts) = all_facts {
+            all_facts
+                .region_live_at
+                .extend(liveness_set.into_iter().flat_map(|(region, location, _)| {
+                    let r = regioncx.to_region_vid(region);
+                    let p1 = location_table.start_index(*location);
+                    let p2 = location_table.mid_index(*location);
+                    iter::once((r, p1)).chain(iter::once((r, p2)))
+                }));
+        }
+    }
+
     /// Some variable with type `live_ty` is "regular live" at
     /// `location` -- i.e., it may be used later. This means that all
     /// regions appearing in the type `live_ty` must be live at
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index c3d9dd8378d..dcb52a3b18a 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -11,6 +11,7 @@
 use borrow_check::borrow_set::BorrowSet;
 use borrow_check::location::{LocationIndex, LocationTable};
 use borrow_check::nll::facts::AllFactsExt;
+use borrow_check::nll::type_check::MirTypeckRegionConstraints;
 use dataflow::indexes::BorrowIndex;
 use dataflow::move_paths::MoveData;
 use dataflow::FlowAtLocation;
@@ -41,7 +42,6 @@ mod facts;
 mod invalidation;
 crate mod region_infer;
 mod renumber;
-mod subtype_constraint_generation;
 crate mod type_check;
 mod universal_regions;
 
@@ -91,46 +91,53 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
     Option<ClosureRegionRequirements<'gcx>>,
 ) {
+    let mut all_facts = if infcx.tcx.sess.opts.debugging_opts.nll_facts
+        || infcx.tcx.sess.opts.debugging_opts.polonius
+    {
+        Some(AllFacts::default())
+    } else {
+        None
+    };
+
     // Run the MIR type-checker.
     let liveness = &LivenessResults::compute(mir);
-    let constraint_sets = &type_check::type_check(
+    let constraint_sets = type_check::type_check(
         infcx,
         param_env,
         mir,
         def_id,
         &universal_regions,
+        location_table,
         &liveness,
+        &mut all_facts,
         flow_inits,
         move_data,
     );
 
-    let mut all_facts = if infcx.tcx.sess.opts.debugging_opts.nll_facts
-        || infcx.tcx.sess.opts.debugging_opts.polonius
-    {
-        Some(AllFacts::default())
-    } else {
-        None
-    };
-
     if let Some(all_facts) = &mut all_facts {
         all_facts
             .universal_region
             .extend(universal_regions.universal_regions());
     }
 
-    // Create the region inference context, taking ownership of the region inference
-    // data that was contained in `infcx`.
+    // Create the region inference context, taking ownership of the
+    // region inference data that was contained in `infcx`, and the
+    // base constraints generated by the type-check.
     let var_origins = infcx.take_region_var_origins();
-    let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir);
-
-    // Generate various constraints.
-    subtype_constraint_generation::generate(
-        &mut regioncx,
-        &mut all_facts,
-        location_table,
+    let MirTypeckRegionConstraints {
+        liveness_set,
+        outlives_constraints,
+        type_tests,
+    } = constraint_sets;
+    let mut regioncx = RegionInferenceContext::new(
+        var_origins,
+        universal_regions,
         mir,
-        constraint_sets,
+        outlives_constraints,
+        type_tests,
     );
+
+    // Generate various additional constraints.
     constraint_generation::generate_constraints(
         infcx,
         &mut regioncx,
@@ -138,6 +145,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
         location_table,
         &mir,
         borrow_set,
+        &liveness_set,
     );
     invalidation::generate_invalidates(
         infcx,
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 b0346abee5a..6c796ea4c73 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
@@ -14,7 +14,7 @@
 //! context internal state.
 
 use std::io::{self, Write};
-use super::{Constraint, RegionInferenceContext};
+use super::{OutlivesConstraint, RegionInferenceContext};
 
 // Room for "'_#NNNNr" before things get misaligned.
 // Easy enough to fix if this ever doesn't seem like
@@ -79,7 +79,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let mut constraints: Vec<_> = self.constraints.iter().collect();
         constraints.sort();
         for constraint in &constraints {
-            let Constraint {
+            let OutlivesConstraint {
                 sup,
                 sub,
                 point,
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
index 6c4c02a36a0..c02e4ff3156 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
@@ -27,7 +27,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
 impl<'this, 'tcx> dot::Labeller<'this> for RegionInferenceContext<'tcx> {
     type Node = RegionVid;
-    type Edge = Constraint;
+    type Edge = OutlivesConstraint;
 
     fn graph_id(&'this self) -> dot::Id<'this> {
         dot::Id::new(format!("RegionInferenceContext")).unwrap()
@@ -41,31 +41,31 @@ impl<'this, 'tcx> dot::Labeller<'this> for RegionInferenceContext<'tcx> {
     fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
         dot::LabelText::LabelStr(format!("{:?}", n).into_cow())
     }
-    fn edge_label(&'this self, e: &Constraint) -> dot::LabelText<'this> {
+    fn edge_label(&'this self, e: &OutlivesConstraint) -> dot::LabelText<'this> {
         dot::LabelText::LabelStr(format!("{:?}", e.point).into_cow())
     }
 }
 
 impl<'this, 'tcx> dot::GraphWalk<'this> for RegionInferenceContext<'tcx> {
     type Node = RegionVid;
-    type Edge = Constraint;
+    type Edge = OutlivesConstraint;
 
     fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> {
         let vids: Vec<RegionVid> = self.definitions.indices().collect();
         vids.into_cow()
     }
-    fn edges(&'this self) -> dot::Edges<'this, Constraint> {
+    fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint> {
         (&self.constraints.raw[..]).into_cow()
     }
 
     // Render `a: b` as `a <- b`, indicating the flow
     // of data during inference.
 
-    fn source(&'this self, edge: &Constraint) -> RegionVid {
+    fn source(&'this self, edge: &OutlivesConstraint) -> RegionVid {
         edge.sub
     }
 
-    fn target(&'this self, edge: &Constraint) -> RegionVid {
+    fn target(&'this self, edge: &OutlivesConstraint) -> RegionVid {
         edge.sup
     }
 }
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 dea2683789b..0eeacda467e 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -68,7 +68,7 @@ pub struct RegionInferenceContext<'tcx> {
     dependency_map: Option<IndexVec<RegionVid, Option<ConstraintIndex>>>,
 
     /// The constraints we have accumulated and used during solving.
-    constraints: IndexVec<ConstraintIndex, Constraint>,
+    constraints: IndexVec<ConstraintIndex, OutlivesConstraint>,
 
     /// Type constraints that we check after solving.
     type_tests: Vec<TypeTest<'tcx>>,
@@ -118,19 +118,19 @@ pub(crate) enum Cause {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct Constraint {
+pub struct OutlivesConstraint {
     // NB. The ordering here is not significant for correctness, but
     // it is for convenience. Before we dump the constraints in the
     // debugging logs, we sort them, and we'd like the "super region"
     // to be first, etc. (In particular, span should remain last.)
     /// The region SUP must outlive SUB...
-    sup: RegionVid,
+    pub sup: RegionVid,
 
     /// Region that must be outlived.
-    sub: RegionVid,
+    pub sub: RegionVid,
 
     /// At this location.
-    point: Location,
+    pub point: Location,
 
     /// Later on, we thread the constraints onto a linked list
     /// grouped by their `sub` field. So if you had:
@@ -140,10 +140,10 @@ pub struct Constraint {
     /// 0     | `'a: 'b`   | Some(2)
     /// 1     | `'b: 'c`   | None
     /// 2     | `'c: 'b`   | None
-    next: Option<ConstraintIndex>,
+    pub next: Option<ConstraintIndex>,
 
     /// Where did this constraint arise?
-    span: Span,
+    pub span: Span,
 }
 
 newtype_index!(ConstraintIndex { DEBUG_FORMAT = "ConstraintIndex({})" });
@@ -239,11 +239,19 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// `num_region_variables` valid inference variables; the first N
     /// of those will be constant regions representing the free
     /// regions defined in `universal_regions`.
+    ///
+    /// The `outlives_constraints` and `type_tests` are an initial set
+    /// of constraints produced by the MIR type check.
     pub(crate) fn new(
         var_infos: VarInfos,
         universal_regions: UniversalRegions<'tcx>,
         mir: &Mir<'tcx>,
+        outlives_constraints: Vec<OutlivesConstraint>,
+        type_tests: Vec<TypeTest<'tcx>>,
     ) -> Self {
+        // The `next` field should not yet have been initialized:
+        debug_assert!(outlives_constraints.iter().all(|c| c.next.is_none()));
+
         let num_region_variables = var_infos.len();
         let num_universal_regions = universal_regions.len();
 
@@ -261,8 +269,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             liveness_constraints: RegionValues::new(elements, num_region_variables),
             inferred_values: None,
             dependency_map: None,
-            constraints: IndexVec::new(),
-            type_tests: Vec::new(),
+            constraints: IndexVec::from_raw(outlives_constraints),
+            type_tests,
             universal_regions,
         };
 
@@ -345,7 +353,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     where
         R: ToRegionVid,
     {
-        let inferred_values = self.inferred_values
+        let inferred_values = self
+            .inferred_values
             .as_ref()
             .expect("region values not yet inferred");
         inferred_values.contains(r.to_region_vid(), p)
@@ -353,7 +362,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
     /// Returns access to the value of `r` for debugging purposes.
     crate fn region_value_str(&self, r: RegionVid) -> String {
-        let inferred_values = self.inferred_values
+        let inferred_values = self
+            .inferred_values
             .as_ref()
             .expect("region values not yet inferred");
 
@@ -387,7 +397,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ) {
         debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
         assert!(self.inferred_values.is_none(), "values already inferred");
-        self.constraints.push(Constraint {
+        self.constraints.push(OutlivesConstraint {
             span,
             sup,
             sub,
@@ -396,11 +406,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         });
     }
 
-    /// Add a "type test" that must be satisfied.
-    pub(super) fn add_type_test(&mut self, type_test: TypeTest<'tcx>) {
-        self.type_tests.push(type_test);
-    }
-
     /// Perform region inference and report errors if we see any
     /// unsatisfiable constraints. If this is a closure, returns the
     /// region requirements to propagate to our creator, if any.
@@ -465,7 +470,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         self.inferred_values = Some(inferred_values);
     }
 
-    #[inline(never)] // ensure dfs is identifiable in profiles
     fn compute_region_values(&self, _mir: &Mir<'tcx>) -> RegionValues {
         debug!("compute_region_values()");
         debug!("compute_region_values: constraints={:#?}", {
@@ -516,7 +520,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// indices of constraints that need to be re-evaluated when X changes.
     /// These are constraints like Y: X @ P -- so if X changed, we may
     /// need to grow Y.
-    #[inline(never)] // ensure dfs is identifiable in profiles
     fn build_dependency_map(&mut self) -> IndexVec<RegionVid, Option<ConstraintIndex>> {
         let mut map = IndexVec::from_elem(None, &self.definitions);
 
@@ -595,7 +598,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         if self.universal_regions.is_universal_region(r) {
             return self.definitions[r].external_name;
         } else {
-            let inferred_values = self.inferred_values
+            let inferred_values = self
+                .inferred_values
                 .as_ref()
                 .expect("region values not yet inferred");
             let upper_bound = self.universal_upper_bound(r);
@@ -634,8 +638,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // region, which ensures it can be encoded in a `ClosureOutlivesRequirement`.
         let lower_bound_plus = self.non_local_universal_upper_bound(*lower_bound);
         assert!(self.universal_regions.is_universal_region(lower_bound_plus));
-        assert!(!self.universal_regions
-            .is_local_free_region(lower_bound_plus));
+        assert!(
+            !self
+                .universal_regions
+                .is_local_free_region(lower_bound_plus)
+        );
 
         propagated_outlives_requirements.push(ClosureOutlivesRequirement {
             subject,
@@ -663,7 +670,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ) -> Option<ClosureOutlivesSubject<'gcx>> {
         let tcx = infcx.tcx;
         let gcx = tcx.global_tcx();
-        let inferred_values = self.inferred_values
+        let inferred_values = self
+            .inferred_values
             .as_ref()
             .expect("region values not yet inferred");
 
@@ -844,7 +852,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             sup_region, sub_region, point
         );
 
-        let inferred_values = self.inferred_values
+        let inferred_values = self
+            .inferred_values
             .as_ref()
             .expect("values for regions not yet inferred");
 
@@ -911,7 +920,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ) {
         // The universal regions are always found in a prefix of the
         // full list.
-        let universal_definitions = self.definitions
+        let universal_definitions = self
+            .definitions
             .iter_enumerated()
             .take_while(|(_, fr_definition)| fr_definition.is_universal);
 
@@ -1139,7 +1149,7 @@ impl<'tcx> RegionDefinition<'tcx> {
     }
 }
 
-impl fmt::Debug for Constraint {
+impl fmt::Debug for OutlivesConstraint {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         write!(
             formatter,
diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
deleted file mode 100644
index 9db19085a39..00000000000
--- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use borrow_check::location::LocationTable;
-use borrow_check::nll::facts::AllFacts;
-use rustc::infer::region_constraints::Constraint;
-use rustc::infer::region_constraints::RegionConstraintData;
-use rustc::infer::region_constraints::{Verify, VerifyBound};
-use rustc::mir::{Location, Mir};
-use rustc::ty;
-use std::iter;
-use syntax::codemap::Span;
-
-use super::region_infer::{RegionInferenceContext, RegionTest, TypeTest};
-use super::type_check::Locations;
-use super::type_check::MirTypeckRegionConstraints;
-use super::type_check::OutlivesSet;
-
-/// When the MIR type-checker executes, it validates all the types in
-/// the MIR, and in the process generates a set of constraints that
-/// must hold regarding the regions in the MIR, along with locations
-/// *where* they must hold. This code takes those constriants and adds
-/// them into the NLL `RegionInferenceContext`.
-pub(super) fn generate<'tcx>(
-    regioncx: &mut RegionInferenceContext<'tcx>,
-    all_facts: &mut Option<AllFacts>,
-    location_table: &LocationTable,
-    mir: &Mir<'tcx>,
-    constraints: &MirTypeckRegionConstraints<'tcx>,
-) {
-    SubtypeConstraintGenerator {
-        regioncx,
-        location_table,
-        mir,
-    }.generate(constraints, all_facts);
-}
-
-struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
-    regioncx: &'cx mut RegionInferenceContext<'tcx>,
-    location_table: &'cx LocationTable,
-    mir: &'cx Mir<'tcx>,
-}
-
-impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
-    fn generate(
-        &mut self,
-        constraints: &MirTypeckRegionConstraints<'tcx>,
-        all_facts: &mut Option<AllFacts>,
-    ) {
-        let MirTypeckRegionConstraints {
-            liveness_set,
-            outlives_sets,
-        } = constraints;
-
-        debug!(
-            "generate(liveness_set={} items, outlives_sets={} items)",
-            liveness_set.len(),
-            outlives_sets.len()
-        );
-
-        for (region, location, cause) in liveness_set {
-            debug!("generate: {:#?} is live at {:#?}", region, location);
-            let region_vid = self.to_region_vid(region);
-            self.regioncx.add_live_point(region_vid, *location, &cause);
-        }
-
-        if let Some(all_facts) = all_facts {
-            all_facts
-                .region_live_at
-                .extend(liveness_set.into_iter().flat_map(|(region, location, _)| {
-                    let r = self.to_region_vid(region);
-                    let p1 = self.location_table.start_index(*location);
-                    let p2 = self.location_table.mid_index(*location);
-                    iter::once((r, p1)).chain(iter::once((r, p2)))
-                }));
-        }
-
-        for OutlivesSet { locations, data } in outlives_sets {
-            debug!("generate: constraints at: {:#?}", locations);
-            let RegionConstraintData {
-                constraints,
-                verifys,
-                givens,
-            } = data;
-
-            let span = self.mir
-                .source_info(locations.from_location().unwrap_or(Location::START))
-                .span;
-
-            let at_location = locations.at_location().unwrap_or(Location::START);
-
-            for constraint in constraints.keys() {
-                debug!("generate: constraint: {:?}", constraint);
-                let (a_vid, b_vid) = match constraint {
-                    Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid),
-                    Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid),
-                    Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)),
-                    Constraint::RegSubReg(a_r, b_r) => {
-                        (self.to_region_vid(a_r), self.to_region_vid(b_r))
-                    }
-                };
-
-                // We have the constraint that `a_vid <= b_vid`. Add
-                // `b_vid: a_vid` to our region checker. Note that we
-                // reverse direction, because `regioncx` talks about
-                // "outlives" (`>=`) whereas the region constraints
-                // talk about `<=`.
-                self.regioncx.add_outlives(span, b_vid, a_vid, at_location);
-
-                // In the new analysis, all outlives relations etc
-                // "take effect" at the mid point of the statement
-                // that requires them, so ignore the `at_location`.
-                if let Some(all_facts) = all_facts {
-                    if let Some(from_location) = locations.from_location() {
-                        all_facts.outlives.push((
-                            b_vid,
-                            a_vid,
-                            self.location_table.mid_index(from_location),
-                        ));
-                    } else {
-                        for location in self.location_table.all_points() {
-                            all_facts.outlives.push((b_vid, a_vid, location));
-                        }
-                    }
-                }
-            }
-
-            for verify in verifys {
-                let type_test = self.verify_to_type_test(verify, span, locations);
-                self.regioncx.add_type_test(type_test);
-            }
-
-            assert!(
-                givens.is_empty(),
-                "MIR type-checker does not use givens (thank goodness)"
-            );
-        }
-    }
-
-    fn verify_to_type_test(
-        &self,
-        verify: &Verify<'tcx>,
-        span: Span,
-        locations: &Locations,
-    ) -> TypeTest<'tcx> {
-        let generic_kind = verify.kind;
-
-        let lower_bound = self.to_region_vid(verify.region);
-
-        let point = locations.at_location().unwrap_or(Location::START);
-
-        let test = self.verify_bound_to_region_test(&verify.bound);
-
-        TypeTest {
-            generic_kind,
-            lower_bound,
-            point,
-            span,
-            test,
-        }
-    }
-
-    fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest {
-        match verify_bound {
-            VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn(
-                regions.iter().map(|r| self.to_region_vid(r)).collect(),
-            ),
-
-            VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn(
-                regions.iter().map(|r| self.to_region_vid(r)).collect(),
-            ),
-
-            VerifyBound::AnyBound(bounds) => RegionTest::Any(
-                bounds
-                    .iter()
-                    .map(|b| self.verify_bound_to_region_test(b))
-                    .collect(),
-            ),
-
-            VerifyBound::AllBounds(bounds) => RegionTest::All(
-                bounds
-                    .iter()
-                    .map(|b| self.verify_bound_to_region_test(b))
-                    .collect(),
-            ),
-        }
-    }
-
-    fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid {
-        self.regioncx.to_region_vid(r)
-    }
-}
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
new file mode 100644
index 00000000000..06aaf6810fa
--- /dev/null
+++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs
@@ -0,0 +1,190 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use borrow_check::location::LocationTable;
+use borrow_check::nll::facts::AllFacts;
+use borrow_check::nll::region_infer::{OutlivesConstraint, RegionTest, TypeTest};
+use borrow_check::nll::type_check::Locations;
+use borrow_check::nll::universal_regions::UniversalRegions;
+use rustc::infer::region_constraints::Constraint;
+use rustc::infer::region_constraints::RegionConstraintData;
+use rustc::infer::region_constraints::{Verify, VerifyBound};
+use rustc::mir::{Location, Mir};
+use rustc::ty;
+use syntax::codemap::Span;
+
+crate struct ConstraintConversion<'a, 'tcx: 'a> {
+    mir: &'a Mir<'tcx>,
+    universal_regions: &'a UniversalRegions<'tcx>,
+    location_table: &'a LocationTable,
+    outlives_constraints: &'a mut Vec<OutlivesConstraint>,
+    type_tests: &'a mut Vec<TypeTest<'tcx>>,
+    all_facts: &'a mut Option<AllFacts>,
+
+}
+
+impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
+    crate fn new(
+        mir: &'a Mir<'tcx>,
+        universal_regions: &'a UniversalRegions<'tcx>,
+        location_table: &'a LocationTable,
+        outlives_constraints: &'a mut Vec<OutlivesConstraint>,
+        type_tests: &'a mut Vec<TypeTest<'tcx>>,
+        all_facts: &'a mut Option<AllFacts>,
+    ) -> Self {
+        Self {
+            mir,
+            universal_regions,
+            location_table,
+            outlives_constraints,
+            type_tests,
+            all_facts,
+        }
+    }
+
+    crate fn convert(
+        &mut self,
+        locations: Locations,
+        data: &RegionConstraintData<'tcx>,
+    ) {
+        debug!("generate: constraints at: {:#?}", locations);
+        let RegionConstraintData {
+            constraints,
+            verifys,
+            givens,
+        } = data;
+
+        let span = self
+            .mir
+            .source_info(locations.from_location().unwrap_or(Location::START))
+            .span;
+
+        let at_location = locations.at_location().unwrap_or(Location::START);
+
+        for constraint in constraints.keys() {
+            debug!("generate: constraint: {:?}", constraint);
+            let (a_vid, b_vid) = match constraint {
+                Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid),
+                Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid),
+                Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)),
+                Constraint::RegSubReg(a_r, b_r) => {
+                    (self.to_region_vid(a_r), self.to_region_vid(b_r))
+                }
+            };
+
+            // We have the constraint that `a_vid <= b_vid`. Add
+            // `b_vid: a_vid` to our region checker. Note that we
+            // reverse direction, because `regioncx` talks about
+            // "outlives" (`>=`) whereas the region constraints
+            // talk about `<=`.
+            self.add_outlives(span, b_vid, a_vid, at_location);
+
+            // In the new analysis, all outlives relations etc
+            // "take effect" at the mid point of the statement
+            // that requires them, so ignore the `at_location`.
+            if let Some(all_facts) = &mut self.all_facts {
+                if let Some(from_location) = locations.from_location() {
+                    all_facts.outlives.push((
+                        b_vid,
+                        a_vid,
+                        self.location_table.mid_index(from_location),
+                    ));
+                } else {
+                    for location in self.location_table.all_points() {
+                        all_facts.outlives.push((b_vid, a_vid, location));
+                    }
+                }
+            }
+        }
+
+        for verify in verifys {
+            let type_test = self.verify_to_type_test(verify, span, locations);
+            self.add_type_test(type_test);
+        }
+
+        assert!(
+            givens.is_empty(),
+            "MIR type-checker does not use givens (thank goodness)"
+        );
+    }
+
+    fn verify_to_type_test(
+        &self,
+        verify: &Verify<'tcx>,
+        span: Span,
+        locations: Locations,
+    ) -> TypeTest<'tcx> {
+        let generic_kind = verify.kind;
+
+        let lower_bound = self.to_region_vid(verify.region);
+
+        let point = locations.at_location().unwrap_or(Location::START);
+
+        let test = self.verify_bound_to_region_test(&verify.bound);
+
+        TypeTest {
+            generic_kind,
+            lower_bound,
+            point,
+            span,
+            test,
+        }
+    }
+
+    fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest {
+        match verify_bound {
+            VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn(
+                regions.iter().map(|r| self.to_region_vid(r)).collect(),
+            ),
+
+            VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn(
+                regions.iter().map(|r| self.to_region_vid(r)).collect(),
+            ),
+
+            VerifyBound::AnyBound(bounds) => RegionTest::Any(
+                bounds
+                    .iter()
+                    .map(|b| self.verify_bound_to_region_test(b))
+                    .collect(),
+            ),
+
+            VerifyBound::AllBounds(bounds) => RegionTest::All(
+                bounds
+                    .iter()
+                    .map(|b| self.verify_bound_to_region_test(b))
+                    .collect(),
+            ),
+        }
+    }
+
+    fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid {
+        self.universal_regions.to_region_vid(r)
+    }
+
+    fn add_outlives(
+        &mut self,
+        span: Span,
+        sup: ty::RegionVid,
+        sub: ty::RegionVid,
+        point: Location,
+    ) {
+        self.outlives_constraints.push(OutlivesConstraint {
+            span,
+            sub,
+            sup,
+            point,
+            next: None,
+        });
+    }
+
+    fn add_type_test(&mut self, type_test: TypeTest<'tcx>) {
+        self.type_tests.push(type_test);
+    }
+}
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 2b1878c33e9..d44eed65201 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
@@ -21,11 +21,11 @@ use borrow_check::nll::renumber;
 use borrow_check::nll::universal_regions::UniversalRegions;
 use rustc::hir::def_id::DefId;
 use rustc::infer::InferOk;
-use rustc::ty::Ty;
-use rustc::ty::subst::Subst;
-use rustc::mir::*;
 use rustc::mir::visit::TyContext;
-use rustc::traits::PredicateObligations;
+use rustc::mir::*;
+use rustc::traits::{ObligationCause, PredicateObligations};
+use rustc::ty::subst::Subst;
+use rustc::ty::Ty;
 
 use rustc_data_structures::indexed_vec::Idx;
 
@@ -56,8 +56,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         }
 
         assert!(
-            mir.yield_ty.is_some() && universal_regions.yield_ty.is_some() ||
-            mir.yield_ty.is_none() && universal_regions.yield_ty.is_none()
+            mir.yield_ty.is_some() && universal_regions.yield_ty.is_some()
+                || mir.yield_ty.is_none() && universal_regions.yield_ty.is_none()
         );
         if let Some(mir_yield_ty) = mir.yield_ty {
             let ur_yield_ty = universal_regions.yield_ty.unwrap();
@@ -76,57 +76,67 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             output_ty
         );
         let mir_output_ty = mir.local_decls[RETURN_PLACE].ty;
-        let anon_type_map = self.fully_perform_op(Locations::All, |cx| {
-            let mut obligations = ObligationAccumulator::default();
-
-            let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types(
-                mir_def_id,
-                cx.body_id,
-                cx.param_env,
-                &output_ty,
-            ));
-            debug!(
-                "equate_inputs_and_outputs: instantiated output_ty={:?}",
-                output_ty
-            );
-            debug!(
-                "equate_inputs_and_outputs: anon_type_map={:#?}",
-                anon_type_map
-            );
-
-            debug!(
-                "equate_inputs_and_outputs: mir_output_ty={:?}",
-                mir_output_ty
-            );
-            obligations.add(infcx
-                .at(&cx.misc(cx.last_span), cx.param_env)
-                .eq(output_ty, mir_output_ty)?);
-
-            for (&anon_def_id, anon_decl) in &anon_type_map {
-                let anon_defn_ty = tcx.type_of(anon_def_id);
-                let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs);
-                let anon_defn_ty = renumber::renumber_regions(
-                    cx.infcx,
-                    TyContext::Location(Location::START),
-                    &anon_defn_ty,
-                );
-                debug!(
-                    "equate_inputs_and_outputs: concrete_ty={:?}",
-                    anon_decl.concrete_ty
-                );
-                debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty);
-                obligations.add(infcx
-                    .at(&cx.misc(cx.last_span), cx.param_env)
-                    .eq(anon_decl.concrete_ty, anon_defn_ty)?);
-            }
-
-            debug!("equate_inputs_and_outputs: equated");
-
-            Ok(InferOk {
-                value: Some(anon_type_map),
-                obligations: obligations.into_vec(),
-            })
-        }).unwrap_or_else(|terr| {
+        let anon_type_map =
+            self.fully_perform_op(
+                Locations::All,
+                || format!("input_output"),
+                |cx| {
+                    let mut obligations = ObligationAccumulator::default();
+
+                    let dummy_body_id = ObligationCause::dummy().body_id;
+                    let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types(
+                        mir_def_id,
+                        dummy_body_id,
+                        cx.param_env,
+                        &output_ty,
+                    ));
+                    debug!(
+                        "equate_inputs_and_outputs: instantiated output_ty={:?}",
+                        output_ty
+                    );
+                    debug!(
+                        "equate_inputs_and_outputs: anon_type_map={:#?}",
+                        anon_type_map
+                    );
+
+                    debug!(
+                        "equate_inputs_and_outputs: mir_output_ty={:?}",
+                        mir_output_ty
+                    );
+                    obligations.add(
+                        infcx
+                            .at(&ObligationCause::dummy(), cx.param_env)
+                            .eq(output_ty, mir_output_ty)?,
+                    );
+
+                    for (&anon_def_id, anon_decl) in &anon_type_map {
+                        let anon_defn_ty = tcx.type_of(anon_def_id);
+                        let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs);
+                        let anon_defn_ty = renumber::renumber_regions(
+                            cx.infcx,
+                            TyContext::Location(Location::START),
+                            &anon_defn_ty,
+                        );
+                        debug!(
+                            "equate_inputs_and_outputs: concrete_ty={:?}",
+                            anon_decl.concrete_ty
+                        );
+                        debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty);
+                        obligations.add(
+                            infcx
+                                .at(&ObligationCause::dummy(), cx.param_env)
+                                .eq(anon_decl.concrete_ty, anon_defn_ty)?,
+                        );
+                    }
+
+                    debug!("equate_inputs_and_outputs: equated");
+
+                    Ok(InferOk {
+                        value: Some(anon_type_map),
+                        obligations: obligations.into_vec(),
+                    })
+                },
+            ).unwrap_or_else(|terr| {
                 span_mirbug!(
                     self,
                     Location::START,
@@ -143,13 +153,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         // prove that `T: Iterator` where `T` is the type we
         // instantiated it with).
         if let Some(anon_type_map) = anon_type_map {
-            self.fully_perform_op(Locations::All, |_cx| {
-                infcx.constrain_anon_types(&anon_type_map, universal_regions);
-                Ok(InferOk {
-                    value: (),
-                    obligations: vec![],
-                })
-            }).unwrap();
+            self.fully_perform_op(
+                Locations::All,
+                || format!("anon_type_map"),
+                |_cx| {
+                    infcx.constrain_anon_types(&anon_type_map, universal_regions);
+                    Ok(InferOk {
+                        value: (),
+                        obligations: vec![],
+                    })
+                },
+            ).unwrap();
         }
     }
 
diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs
index 76320c6a2ea..80f5fe4184f 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs
@@ -8,15 +8,19 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use dataflow::{FlowAtLocation, FlowsAtLocation};
 use borrow_check::nll::region_infer::Cause;
-use dataflow::MaybeInitializedPlaces;
+use borrow_check::nll::type_check::AtLocation;
 use dataflow::move_paths::{HasMoveData, MoveData};
-use rustc::mir::{BasicBlock, Location, Mir};
+use dataflow::MaybeInitializedPlaces;
+use dataflow::{FlowAtLocation, FlowsAtLocation};
+use rustc::infer::region_constraints::RegionConstraintData;
 use rustc::mir::Local;
-use rustc::ty::{Ty, TyCtxt, TypeFoldable};
-use rustc::infer::InferOk;
-use borrow_check::nll::type_check::AtLocation;
+use rustc::mir::{BasicBlock, Location, Mir};
+use rustc::traits::ObligationCause;
+use rustc::ty::subst::Kind;
+use rustc::ty::{Ty, TypeFoldable};
+use rustc_data_structures::fx::FxHashMap;
+use std::rc::Rc;
 use util::liveness::LivenessResults;
 
 use super::TypeChecker;
@@ -36,14 +40,13 @@ pub(super) fn generate<'gcx, 'tcx>(
     flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
     move_data: &MoveData<'tcx>,
 ) {
-    let tcx = cx.tcx();
     let mut generator = TypeLivenessGenerator {
         cx,
-        tcx,
         mir,
         liveness,
         flow_inits,
         move_data,
+        drop_data: FxHashMap(),
     };
 
     for bb in mir.basic_blocks().indices() {
@@ -59,11 +62,16 @@ where
     'gcx: 'tcx,
 {
     cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
-    tcx: TyCtxt<'typeck, 'gcx, 'tcx>,
     mir: &'gen Mir<'tcx>,
     liveness: &'gen LivenessResults,
     flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
     move_data: &'gen MoveData<'tcx>,
+    drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>,
+}
+
+struct DropData<'tcx> {
+    dropped_kinds: Vec<Kind<'tcx>>,
+    region_constraint_data: Option<Rc<RegionConstraintData<'tcx>>>,
 }
 
 impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> {
@@ -80,7 +88,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
                 for live_local in live_locals.iter() {
                     let live_local_ty = self.mir.local_decls[live_local].ty;
                     let cause = Cause::LiveVar(live_local, location);
-                    self.push_type_live_constraint(live_local_ty, location, cause);
+                    Self::push_type_live_constraint(&mut self.cx, live_local_ty, location, cause);
                 }
             });
 
@@ -148,8 +156,12 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
     /// `location` -- i.e., it may be used later. This means that all
     /// regions appearing in the type `live_ty` must be live at
     /// `location`.
-    fn push_type_live_constraint<T>(&mut self, value: T, location: Location, cause: Cause)
-    where
+    fn push_type_live_constraint<T>(
+        cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
+        value: T,
+        location: Location,
+        cause: Cause,
+    ) where
         T: TypeFoldable<'tcx>,
     {
         debug!(
@@ -157,8 +169,8 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
             value, location
         );
 
-        self.tcx.for_each_free_region(&value, |live_region| {
-            self.cx
+        cx.tcx().for_each_free_region(&value, |live_region| {
+            cx
                 .constraints
                 .liveness_set
                 .push((live_region, location, cause.clone()));
@@ -181,47 +193,44 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
             dropped_local, dropped_ty, location
         );
 
-        // If we end visiting the same type twice (usually due to a cycle involving
-        // associated types), we need to ensure that its region types match up with the type
-        // we added to the 'known' map the first time around. For this reason, we need
-        // our infcx to hold onto its calculated region constraints after each call
-        // to dtorck_constraint_for_ty. Otherwise, normalizing the corresponding associated
-        // type will end up instantiating the type with a new set of inference variables
-        // Since this new type will never be in 'known', we end up looping forever.
-        //
-        // For this reason, we avoid calling TypeChecker.normalize, instead doing all normalization
-        // ourselves in one large 'fully_perform_op' callback.
-        let kind_constraints = self.cx
-            .fully_perform_op(location.at_self(), |cx| {
-                let span = cx.last_span;
-
-                let mut final_obligations = Vec::new();
-                let mut kind_constraints = Vec::new();
-
-                let InferOk {
-                    value: kinds,
-                    obligations,
-                } = cx.infcx
-                    .at(&cx.misc(span), cx.param_env)
-                    .dropck_outlives(dropped_ty);
-                for kind in kinds {
-                    // All things in the `outlives` array may be touched by
-                    // the destructor and must be live at this point.
-                    let cause = Cause::DropVar(dropped_local, location);
-                    kind_constraints.push((kind, location, cause));
-                }
+        let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
+            let cx = &mut self.cx;
+            move || Self::compute_drop_data(cx, dropped_ty)
+        });
 
-                final_obligations.extend(obligations);
+        if let Some(data) = &drop_data.region_constraint_data {
+            self.cx
+                .push_region_constraints(location.at_self(), data.clone());
+        }
 
-                Ok(InferOk {
-                    value: kind_constraints,
-                    obligations: final_obligations,
-                })
-            })
-            .unwrap();
+        // All things in the `outlives` array may be touched by
+        // the destructor and must be live at this point.
+        let cause = Cause::DropVar(dropped_local, location);
+        for &kind in &drop_data.dropped_kinds {
+            Self::push_type_live_constraint(&mut self.cx, kind, location, cause);
+        }
+    }
 
-        for (kind, location, cause) in kind_constraints {
-            self.push_type_live_constraint(kind, location, cause);
+    fn compute_drop_data(
+        cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
+        dropped_ty: Ty<'tcx>,
+    ) -> DropData<'tcx> {
+        debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,);
+
+        let (dropped_kinds, region_constraint_data) =
+            cx.fully_perform_op_and_get_region_constraint_data(
+                || format!("compute_drop_data(dropped_ty={:?})", dropped_ty),
+                |cx| {
+                    Ok(cx
+                        .infcx
+                        .at(&ObligationCause::dummy(), cx.param_env)
+                        .dropck_outlives(dropped_ty))
+                },
+            ).unwrap();
+
+        DropData {
+            dropped_kinds,
+            region_constraint_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 04f5024b769..d25cec79791 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -11,8 +11,10 @@
 //! This pass type-checks the MIR to ensure it is not broken.
 #![allow(unreachable_code)]
 
+use borrow_check::location::LocationTable;
+use borrow_check::nll::facts::AllFacts;
 use borrow_check::nll::region_infer::Cause;
-use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
+use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, OutlivesConstraint, TypeTest};
 use borrow_check::nll::universal_regions::UniversalRegions;
 use dataflow::move_paths::MoveData;
 use dataflow::FlowAtLocation;
@@ -20,17 +22,17 @@ use dataflow::MaybeInitializedPlaces;
 use rustc::hir::def_id::DefId;
 use rustc::infer::region_constraints::{GenericKind, RegionConstraintData};
 use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
+use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
 use rustc::mir::tcx::PlaceTy;
 use rustc::mir::visit::{PlaceContext, Visitor};
-use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
 use rustc::mir::*;
 use rustc::traits::query::NoSolution;
-use rustc::traits::{self, Normalized, TraitEngine};
+use rustc::traits::{self, ObligationCause, Normalized, TraitEngine};
 use rustc::ty::error::TypeError;
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants};
 use std::fmt;
-use syntax::ast;
+use std::rc::Rc;
 use syntax_pos::{Span, DUMMY_SP};
 use transform::{MirPass, MirSource};
 use util::liveness::LivenessResults;
@@ -45,7 +47,7 @@ macro_rules! span_mirbug {
             $context.last_span,
             &format!(
                 "broken MIR in {:?} ({:?}): {}",
-                $context.body_id,
+                $context.mir_def_id,
                 $elem,
                 format_args!($($message)*),
             ),
@@ -62,6 +64,7 @@ macro_rules! span_mirbug_and_err {
     })
 }
 
+mod constraint_conversion;
 mod input_output;
 mod liveness;
 
@@ -100,19 +103,25 @@ pub(crate) fn type_check<'gcx, 'tcx>(
     mir: &Mir<'tcx>,
     mir_def_id: DefId,
     universal_regions: &UniversalRegions<'tcx>,
+    location_table: &LocationTable,
     liveness: &LivenessResults,
+    all_facts: &mut Option<AllFacts>,
     flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
     move_data: &MoveData<'tcx>,
 ) -> MirTypeckRegionConstraints<'tcx> {
-    let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap();
     let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
     type_check_internal(
         infcx,
-        body_id,
+        mir_def_id,
         param_env,
         mir,
         &universal_regions.region_bound_pairs,
         Some(implicit_region_bound),
+        Some(BorrowCheckContext {
+            universal_regions,
+            location_table,
+            all_facts,
+        }),
         &mut |cx| {
             liveness::generate(cx, mir, liveness, flow_inits, move_data);
 
@@ -123,19 +132,22 @@ pub(crate) fn type_check<'gcx, 'tcx>(
 
 fn type_check_internal<'gcx, 'tcx>(
     infcx: &InferCtxt<'_, 'gcx, 'tcx>,
-    body_id: ast::NodeId,
+    mir_def_id: DefId,
     param_env: ty::ParamEnv<'gcx>,
     mir: &Mir<'tcx>,
     region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)],
     implicit_region_bound: Option<ty::Region<'tcx>>,
+    borrowck_context: Option<BorrowCheckContext<'_, 'tcx>>,
     extra: &mut dyn FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>),
 ) -> MirTypeckRegionConstraints<'tcx> {
     let mut checker = TypeChecker::new(
         infcx,
-        body_id,
+        mir_def_id,
         param_env,
         region_bound_pairs,
         implicit_region_bound,
+        borrowck_context,
+        mir,
     );
     let errors_reported = {
         let mut verifier = TypeVerifier::new(&mut checker, mir);
@@ -173,7 +185,7 @@ struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b + 'tcx, 'tcx: 'b> {
     cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
     mir: &'a Mir<'tcx>,
     last_span: Span,
-    body_id: ast::NodeId,
+    mir_def_id: DefId,
     errors_reported: bool,
 }
 
@@ -221,7 +233,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
     fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
         TypeVerifier {
             mir,
-            body_id: cx.body_id,
+            mir_def_id: cx.mir_def_id,
             cx,
             last_span: mir.span,
             errors_reported: false,
@@ -300,7 +312,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
 
         debug!("sanitize_constant: expected_ty={:?}", expected_ty);
 
-        if let Err(terr) = self.cx
+        if let Err(terr) = self
+            .cx
             .eq_types(expected_ty, constant.ty, location.at_self())
         {
             span_mirbug!(
@@ -580,17 +593,25 @@ struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
     param_env: ty::ParamEnv<'gcx>,
     last_span: Span,
-    body_id: ast::NodeId,
+    mir_def_id: DefId,
     region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
     implicit_region_bound: Option<ty::Region<'tcx>>,
     reported_errors: FxHashSet<(Ty<'tcx>, Span)>,
     constraints: MirTypeckRegionConstraints<'tcx>,
+    borrowck_context: Option<BorrowCheckContext<'a, 'tcx>>,
+    mir: &'a Mir<'tcx>,
+}
+
+struct BorrowCheckContext<'a, 'tcx: 'a> {
+    universal_regions: &'a UniversalRegions<'tcx>,
+    location_table: &'a LocationTable,
+    all_facts: &'a mut Option<AllFacts>,
 }
 
 /// A collection of region constraints that must be satisfied for the
 /// program to be considered well-typed.
 #[derive(Default)]
-pub(crate) struct MirTypeckRegionConstraints<'tcx> {
+crate struct MirTypeckRegionConstraints<'tcx> {
     /// In general, the type-checker is not responsible for enforcing
     /// liveness constraints; this job falls to the region inferencer,
     /// which performs a liveness analysis. However, in some limited
@@ -598,24 +619,11 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> {
     /// not otherwise appear in the MIR -- in particular, the
     /// late-bound regions that it instantiates at call-sites -- and
     /// hence it must report on their liveness constraints.
-    pub liveness_set: Vec<(ty::Region<'tcx>, Location, Cause)>,
-
-    /// During the course of type-checking, we will accumulate region
-    /// constraints due to performing subtyping operations or solving
-    /// traits. These are accumulated into this vector for later use.
-    pub outlives_sets: Vec<OutlivesSet<'tcx>>,
-}
+    crate liveness_set: Vec<(ty::Region<'tcx>, Location, Cause)>,
 
-/// Outlives relationships between regions and types created at a
-/// particular point within the control-flow graph.
-pub struct OutlivesSet<'tcx> {
-    /// The locations associated with these constraints.
-    pub locations: Locations,
+    crate outlives_constraints: Vec<OutlivesConstraint>,
 
-    /// Constraints generated. In terms of the NLL RFC, when you have
-    /// a constraint `R1: R2 @ P`, the data in there specifies things
-    /// like `R1: R2`.
-    pub data: RegionConstraintData<'tcx>,
+    crate type_tests: Vec<TypeTest<'tcx>>,
 }
 
 /// The `Locations` type summarizes *where* region constraints are
@@ -667,7 +675,7 @@ pub enum Locations {
         /// NLL RFC, when you have a constraint `R1: R2 @ P`, this field
         /// is the `P` value.
         at_location: Location,
-    }
+    },
 }
 
 impl Locations {
@@ -689,37 +697,99 @@ impl Locations {
 impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     fn new(
         infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
-        body_id: ast::NodeId,
+        mir_def_id: DefId,
         param_env: ty::ParamEnv<'gcx>,
         region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
         implicit_region_bound: Option<ty::Region<'tcx>>,
+        borrowck_context: Option<BorrowCheckContext<'a, 'tcx>>,
+        mir: &'a Mir<'tcx>,
     ) -> Self {
         TypeChecker {
             infcx,
             last_span: DUMMY_SP,
-            body_id,
+            mir_def_id,
             param_env,
             region_bound_pairs,
             implicit_region_bound,
+            borrowck_context,
+            mir,
             reported_errors: FxHashSet(),
             constraints: MirTypeckRegionConstraints::default(),
         }
     }
 
-    fn misc(&self, span: Span) -> traits::ObligationCause<'tcx> {
-        traits::ObligationCause::misc(span, self.body_id)
+    /// Given some operation `op` that manipulates types, proves
+    /// predicates, or otherwise uses the inference context, executes
+    /// `op` and then executes all the further obligations that `op`
+    /// returns. This will yield a set of outlives constraints amongst
+    /// regions which are extracted and stored as having occured at
+    /// `locations`.
+    ///
+    /// **Any `rustc::infer` operations that might generate region
+    /// constraints should occur within this method so that those
+    /// constraints can be properly localized!**
+    fn fully_perform_op<R>(
+        &mut self,
+        locations: Locations,
+        describe_op: impl Fn() -> String,
+        op: impl FnOnce(&mut Self) -> InferResult<'tcx, R>,
+    ) -> Result<R, TypeError<'tcx>> {
+        let (r, opt_data) = self.fully_perform_op_and_get_region_constraint_data(
+            || format!("{} at {:?}", describe_op(), locations),
+            op,
+        )?;
+
+        if let Some(data) = opt_data {
+            self.push_region_constraints(locations, data);
+        }
+
+        Ok(r)
     }
 
-    fn fully_perform_op<OP, R>(
+    fn push_region_constraints(
         &mut self,
         locations: Locations,
-        op: OP,
-    ) -> Result<R, TypeError<'tcx>>
-    where
-        OP: FnOnce(&mut Self) -> InferResult<'tcx, R>,
-    {
+        data: Rc<RegionConstraintData<'tcx>>,
+    ) {
+        debug!(
+            "push_region_constraints: constraints generated at {:?} are {:#?}",
+            locations, data
+        );
+
+        if let Some(borrowck_context) = &mut self.borrowck_context {
+            constraint_conversion::ConstraintConversion::new(
+                self.mir,
+                borrowck_context.universal_regions,
+                borrowck_context.location_table,
+                &mut self.constraints.outlives_constraints,
+                &mut self.constraints.type_tests,
+                &mut borrowck_context.all_facts,
+            ).convert(locations, &data);
+        }
+    }
+
+    /// Helper for `fully_perform_op`, but also used on its own
+    /// sometimes to enable better caching: executes `op` fully (along
+    /// with resulting obligations) and returns the full set of region
+    /// obligations. If the same `op` were to be performed at some
+    /// other location, then the same set of region obligations would
+    /// be generated there, so this can be useful for caching.
+    fn fully_perform_op_and_get_region_constraint_data<R>(
+        &mut self,
+        describe_op: impl Fn() -> String,
+        op: impl FnOnce(&mut Self) -> InferResult<'tcx, R>,
+    ) -> Result<(R, Option<Rc<RegionConstraintData<'tcx>>>), TypeError<'tcx>> {
+        if cfg!(debug_assertions) {
+            info!(
+                "fully_perform_op_and_get_region_constraint_data({})",
+                describe_op(),
+            );
+        }
+
         let mut fulfill_cx = TraitEngine::new(self.infcx.tcx);
+        let dummy_body_id = ObligationCause::dummy().body_id;
         let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op(self))?;
+        debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id));
         fulfill_cx.register_predicate_obligations(self.infcx, obligations);
         if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) {
             span_mirbug!(self, "", "errors selecting obligation: {:?}", e);
@@ -729,21 +799,15 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             self.region_bound_pairs,
             self.implicit_region_bound,
             self.param_env,
-            self.body_id,
+            dummy_body_id,
         );
 
         let data = self.infcx.take_and_reset_region_constraints();
-        if !data.is_empty() {
-            debug!(
-                "fully_perform_op: constraints generated at {:?} are {:#?}",
-                locations, data
-            );
-            self.constraints
-                .outlives_sets
-                .push(OutlivesSet { locations, data });
+        if data.is_empty() {
+            Ok((value, None))
+        } else {
+            Ok((value, Some(Rc::new(data))))
         }
-
-        Ok(value)
     }
 
     fn sub_types(
@@ -752,19 +816,37 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         sup: Ty<'tcx>,
         locations: Locations,
     ) -> UnitResult<'tcx> {
-        self.fully_perform_op(locations, |this| {
-            this.infcx
-                .at(&this.misc(this.last_span), this.param_env)
-                .sup(sup, sub)
-        })
+        // Micro-optimization.
+        if sub == sup {
+            return Ok(());
+        }
+
+        self.fully_perform_op(
+            locations,
+            || format!("sub_types({:?} <: {:?})", sub, sup),
+            |this| {
+                this.infcx
+                    .at(&ObligationCause::dummy(), this.param_env)
+                    .sup(sup, sub)
+            },
+        )
     }
 
     fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> {
-        self.fully_perform_op(locations, |this| {
-            this.infcx
-                .at(&this.misc(this.last_span), this.param_env)
-                .eq(b, a)
-        })
+        // Micro-optimization.
+        if a == b {
+            return Ok(());
+        }
+
+        self.fully_perform_op(
+            locations,
+            || format!("eq_types({:?} = {:?})", a, b),
+            |this| {
+                this.infcx
+                    .at(&ObligationCause::dummy(), this.param_env)
+                    .eq(b, a)
+            },
+        )
     }
 
     fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
@@ -819,7 +901,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             }
             StatementKind::UserAssertTy(ref c_ty, ref local) => {
                 let local_ty = mir.local_decls()[*local].ty;
-                let (ty, _) = self.infcx
+                let (ty, _) = self
+                    .infcx
                     .instantiate_canonical_with_fresh_inference_vars(stmt.source_info.span, c_ty);
                 debug!(
                     "check_stmt: user_assert_ty ty={:?} local_ty={:?}",
@@ -1431,9 +1514,7 @@ 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.at_self())
-            {
+            if let Err(terr) = self.sub_types(operand_ty, field_ty, location.at_self()) {
                 span_mirbug!(
                     self,
                     rvalue,
@@ -1487,9 +1568,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 if let Some(closure_region_requirements) =
                     tcx.mir_borrowck(*def_id).closure_requirements
                 {
+                    let dummy_body_id = ObligationCause::dummy().body_id;
                     closure_region_requirements.apply_requirements(
                         self.infcx,
-                        self.body_id,
+                        dummy_body_id,
                         location,
                         *def_id,
                         *substs,
@@ -1522,27 +1604,44 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
 
     fn prove_predicates<T>(&mut self, predicates: T, location: Location)
     where
-        T: IntoIterator<Item = ty::Predicate<'tcx>>,
-        T::IntoIter: Clone,
+        T: IntoIterator<Item = ty::Predicate<'tcx>> + Clone,
     {
-        let predicates = predicates.into_iter();
+        let cause = ObligationCause::dummy();
+        let obligations: Vec<_> = predicates
+            .into_iter()
+            .map(|p| traits::Obligation::new(cause.clone(), self.param_env, p))
+            .collect();
+
+        // Micro-optimization
+        if obligations.is_empty() {
+            return;
+        }
+
+        // This intermediate vector is mildly unfortunate, in that we
+        // sometimes create it even when logging is disabled, but only
+        // if debug-info is enabled, and I doubt it is actually
+        // expensive. -nmatsakis
+        let predicates_vec: Vec<_> = if cfg!(debug_assertions) {
+            obligations.iter().map(|o| o.predicate).collect()
+        } else {
+            Vec::new()
+        };
 
         debug!(
             "prove_predicates(predicates={:?}, location={:?})",
-            predicates.clone().collect::<Vec<_>>(),
-            location,
+            predicates_vec, location,
         );
-        self.fully_perform_op(location.at_self(), |this| {
-            let cause = this.misc(this.last_span);
-            let obligations = predicates
-                .into_iter()
-                .map(|p| traits::Obligation::new(cause.clone(), this.param_env, p))
-                .collect();
-            Ok(InferOk {
-                value: (),
-                obligations,
-            })
-        }).unwrap()
+
+        self.fully_perform_op(
+            location.at_self(),
+            || format!("prove_predicates({:?})", predicates_vec),
+            |_this| {
+                Ok(InferOk {
+                    value: (),
+                    obligations,
+                })
+            },
+        ).unwrap()
     }
 
     fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
@@ -1575,21 +1674,31 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     where
         T: fmt::Debug + TypeFoldable<'tcx>,
     {
+        // Micro-optimization: avoid work when we don't have to
+        if !value.has_projections() {
+            return value.clone();
+        }
+
         debug!("normalize(value={:?}, location={:?})", value, location);
-        self.fully_perform_op(location.to_locations(), |this| {
-            let Normalized { value, obligations } = this.infcx
-                .at(&this.misc(this.last_span), this.param_env)
-                .normalize(value)
-                .unwrap_or_else(|NoSolution| {
-                    span_bug!(
-                        this.last_span,
-                        "normalization of `{:?}` failed at {:?}",
-                        value,
-                        location,
-                    );
-                });
-            Ok(InferOk { value, obligations })
-        }).unwrap()
+        self.fully_perform_op(
+            location.to_locations(),
+            || format!("normalize(value={:?})", value),
+            |this| {
+                let Normalized { value, obligations } = this
+                    .infcx
+                    .at(&ObligationCause::dummy(), this.param_env)
+                    .normalize(value)
+                    .unwrap_or_else(|NoSolution| {
+                        span_bug!(
+                            this.last_span,
+                            "normalization of `{:?}` failed at {:?}",
+                            value,
+                            location,
+                        );
+                    });
+                Ok(InferOk { value, obligations })
+            },
+        ).unwrap()
     }
 }
 
@@ -1598,7 +1707,6 @@ pub struct TypeckMir;
 impl MirPass for TypeckMir {
     fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
         let def_id = src.def_id;
-        let id = tcx.hir.as_local_node_id(def_id).unwrap();
         debug!("run_pass: {:?}", def_id);
 
         // When NLL is enabled, the borrow checker runs the typeck
@@ -1614,7 +1722,16 @@ impl MirPass for TypeckMir {
         }
         let param_env = tcx.param_env(def_id);
         tcx.infer_ctxt().enter(|infcx| {
-            let _ = type_check_internal(&infcx, id, param_env, mir, &[], None, &mut |_| ());
+            let _ = type_check_internal(
+                &infcx,
+                def_id,
+                param_env,
+                mir,
+                &[],
+                None,
+                None,
+                &mut |_| (),
+            );
 
             // For verification purposes, we just ignore the resulting
             // region constraint sets. Not our problem. =)
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
index 4081ec65f80..5dd1889ef95 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
@@ -24,16 +24,15 @@ LL | |     });
    = note: where '_#1r: '_#0r
 
 error: free region `ReFree(DefId(0/0:6 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]), BrNamed(crate0:DefIndex(1:16), 'a))` does not outlive free region `ReStatic`
-  --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:47
+  --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:5
    |
-LL |       establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
-   |  _______________________________________________^
+LL | /     establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
 LL | |         //~^ ERROR does not outlive free region
 LL | |
 LL | |         // Only works if 'x: 'y:
 LL | |         demand_y(x, y, x.get()) //~ WARNING not reporting region error due to nll
 LL | |     });
-   | |_____^
+   | |______^
 
 note: No external requirements
   --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:44:1
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
index 7a745bb0964..e44df9c7af1 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
@@ -24,16 +24,15 @@ LL | |     });
    = note: where '_#1r: '_#0r
 
 error: free region `ReFree(DefId(0/0:6 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]), BrNamed(crate0:DefIndex(1:16), 'a))` does not outlive free region `ReStatic`
-  --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:47
+  --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:5
    |
-LL |       establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
-   |  _______________________________________________^
+LL | /     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
 LL | |         //~^ ERROR does not outlive free region
 LL | |         // Only works if 'x: 'y:
 LL | |         demand_y(x, y, x.get())
 LL | |         //~^ WARNING not reporting region error due to nll
 LL | |     });
-   | |_____^
+   | |______^
 
 note: No external requirements
   --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:47:1