diff --git a/src/Cargo.lock b/src/Cargo.lock
index 8594e4ff1306d..09baaeadaee43 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -87,7 +87,7 @@ dependencies = [
 
 [[package]]
 name = "atty"
-version = "0.2.11"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -187,7 +187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 name = "cargo"
 version = "0.30.0"
 dependencies = [
- "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -303,7 +303,7 @@ version = "2.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -622,7 +622,7 @@ name = "env_logger"
 version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1844,7 +1844,7 @@ name = "rustc-ap-rustc_errors"
 version = "182.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-ap-rustc_data_structures 182.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-ap-serialize 182.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-ap-syntax_pos 182.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2097,7 +2097,7 @@ dependencies = [
 name = "rustc_errors"
 version = "0.0.0"
 dependencies = [
- "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc_data_structures 0.0.0",
  "serialize 0.0.0",
  "syntax_pos 0.0.0",
@@ -3056,7 +3056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
 "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
 "checksum assert_cli 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98589b0e465a6c510d95fceebd365bb79bedece7f6e18a480897f2015f85ec51"
-"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
+"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1"
 "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
 "checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e"
 "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs
index 2390d7eccce76..1721d1dd0e9c5 100644
--- a/src/librustc/dep_graph/graph.rs
+++ b/src/librustc/dep_graph/graph.rs
@@ -39,7 +39,6 @@ pub struct DepGraph {
     fingerprints: Lrc<Lock<IndexVec<DepNodeIndex, Fingerprint>>>
 }
 
-
 newtype_index!(DepNodeIndex);
 
 impl DepNodeIndex {
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index 43ed80b474ad5..cb4e1ab65e759 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -617,6 +617,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         debug!("leak_check: skol_map={:?}",
                skol_map);
 
+        // If the user gave `-Zno-leak-check`, then skip the leak
+        // check completely. This is wildly unsound and also not
+        // unlikely to cause an ICE or two. It is intended for use
+        // only during a transition period, in which the MIR typeck
+        // uses the "universe-style" check, and the rest of typeck
+        // uses the more conservative leak check.  Since the leak
+        // check is more conservative, we can't test the
+        // universe-style check without disabling it.
+        if self.tcx.sess.opts.debugging_opts.no_leak_check {
+            return Ok(());
+        }
+
         let new_vars = self.region_vars_confined_to_snapshot(snapshot);
         for (&skol_br, &skol) in skol_map {
             // The inputs to a skolemized variable can only
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 9283705b7b78e..0b84c6a0aa77a 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -377,7 +377,23 @@ pub enum NLLRegionVariableOrigin {
     // elsewhere. This origin indices we've got one of those.
     FreeRegion,
 
-    Inferred(::mir::visit::TyContext),
+    BoundRegion(ty::UniverseIndex),
+
+    Existential,
+}
+
+impl NLLRegionVariableOrigin {
+    pub fn is_universal(self) -> bool {
+        match self {
+            NLLRegionVariableOrigin::FreeRegion => true,
+            NLLRegionVariableOrigin::BoundRegion(..) => true,
+            NLLRegionVariableOrigin::Existential => false,
+        }
+    }
+
+    pub fn is_existential(self) -> bool {
+        !self.is_universal()
+    }
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -1381,6 +1397,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     fn universe(&self) -> ty::UniverseIndex {
         self.universe.get()
     }
+
+    /// Create and return a new subunivese of the current universe;
+    /// update `self.universe` to that new subuniverse. At present,
+    /// used only in the NLL subtyping code, which uses the new
+    /// universe-based scheme instead of the more limited leak-check
+    /// scheme.
+    pub fn create_subuniverse(&self) -> ty::UniverseIndex {
+        let u = self.universe.get().subuniverse();
+        self.universe.set(u);
+        u
+    }
 }
 
 impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> {
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index c72952efc6144..d709a4debd1d3 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -69,6 +69,7 @@
 #![feature(trusted_len)]
 #![feature(vec_remove_item)]
 #![feature(catch_expr)]
+#![feature(step_trait)]
 #![feature(integer_atomics)]
 #![feature(test)]
 #![feature(in_band_lifetimes)]
diff --git a/src/librustc/mir/traversal.rs b/src/librustc/mir/traversal.rs
index 92888ed99e472..87ba3420eeceb 100644
--- a/src/librustc/mir/traversal.rs
+++ b/src/librustc/mir/traversal.rs
@@ -9,7 +9,6 @@
 // except according to those terms.
 
 use rustc_data_structures::bitvec::BitVector;
-use rustc_data_structures::indexed_vec::Idx;
 
 use super::*;
 
@@ -33,7 +32,7 @@ use super::*;
 #[derive(Clone)]
 pub struct Preorder<'a, 'tcx: 'a> {
     mir: &'a Mir<'tcx>,
-    visited: BitVector,
+    visited: BitVector<BasicBlock>,
     worklist: Vec<BasicBlock>,
 }
 
@@ -58,7 +57,7 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
 
     fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
         while let Some(idx) = self.worklist.pop() {
-            if !self.visited.insert(idx.index()) {
+            if !self.visited.insert(idx) {
                 continue;
             }
 
@@ -107,7 +106,7 @@ impl<'a, 'tcx> ExactSizeIterator for Preorder<'a, 'tcx> {}
 /// A Postorder traversal of this graph is `D B C A` or `D C B A`
 pub struct Postorder<'a, 'tcx: 'a> {
     mir: &'a Mir<'tcx>,
-    visited: BitVector,
+    visited: BitVector<BasicBlock>,
     visit_stack: Vec<(BasicBlock, Successors<'a>)>
 }
 
@@ -123,7 +122,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
         let data = &po.mir[root];
 
         if let Some(ref term) = data.terminator {
-            po.visited.insert(root.index());
+            po.visited.insert(root);
             po.visit_stack.push((root, term.successors()));
             po.traverse_successor();
         }
@@ -190,8 +189,8 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
                 break;
             };
 
-            if self.visited.insert(bb.index()) {
-                if let Some(ref term) = self.mir[bb].terminator {
+            if self.visited.insert(bb) {
+                if let Some(term) = &self.mir[bb].terminator {
                     self.visit_stack.push((bb, term.successors()));
                 }
             }
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 293b5c63cf06a..54d9e24bbc0e2 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1353,6 +1353,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "generate build artifacts that are compatible with linker-based LTO."),
     no_parallel_llvm: bool = (false, parse_bool, [UNTRACKED],
           "don't run LLVM in parallel (while keeping codegen-units and ThinLTO)"),
+    no_leak_check: bool = (false, parse_bool, [UNTRACKED],
+        "disables the 'leak check' for subtyping; unsound, but useful for tests"),
 }
 
 pub fn default_lib_output() -> CrateType {
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index bd24b93f0293f..d23bd2e51fbd5 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1481,7 +1481,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
 /// type name in a non-zero universe is a skolemized type -- an
 /// idealized representative of "types in general" that we use for
 /// checking generic functions.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub struct UniverseIndex(u32);
 
 impl UniverseIndex {
@@ -1489,6 +1489,19 @@ impl UniverseIndex {
     /// visible.
     pub const ROOT: Self = UniverseIndex(0);
 
+    /// The "max universe" -- this isn't really a valid universe, but
+    /// it's useful sometimes as a "starting value" when you are
+    /// taking the minimum of a (non-empty!) set of universes.
+    pub const MAX: Self = UniverseIndex(::std::u32::MAX);
+
+    /// Creates a universe index from the given integer.  Not to be
+    /// used lightly lest you pick a bad value. But sometimes we
+    /// convert universe indicies into integers and back for various
+    /// reasons.
+    pub fn from_u32(index: u32) -> Self {
+        UniverseIndex(index)
+    }
+
     /// A "subuniverse" corresponds to being inside a `forall` quantifier.
     /// So, for example, suppose we have this type in universe `U`:
     ///
@@ -1504,6 +1517,11 @@ impl UniverseIndex {
         UniverseIndex(self.0.checked_add(1).unwrap())
     }
 
+    /// True if the names in this universe are a subset of the names in `other`.
+    pub fn is_subset_of(self, other: UniverseIndex) -> bool {
+        self.0 <= other.0
+    }
+
     pub fn as_u32(&self) -> u32 {
         self.0
     }
@@ -1513,6 +1531,12 @@ impl UniverseIndex {
     }
 }
 
+impl fmt::Debug for UniverseIndex {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "U{}", self.as_u32())
+    }
+}
+
 impl From<u32> for UniverseIndex {
     fn from(index: u32) -> Self {
         UniverseIndex(index)
diff --git a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs
index 9ced0f5f4eca1..fcde7f9bbc33d 100644
--- a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs
+++ b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs
@@ -65,7 +65,7 @@ pub fn create_mir_scopes(cx: &CodegenCx, mir: &Mir, debug_context: &FunctionDebu
     let mut has_variables = BitVector::new(mir.source_scopes.len());
     for var in mir.vars_iter() {
         let decl = &mir.local_decls[var];
-        has_variables.insert(decl.visibility_scope.index());
+        has_variables.insert(decl.visibility_scope);
     }
 
     // Instantiate all scopes.
@@ -79,7 +79,7 @@ pub fn create_mir_scopes(cx: &CodegenCx, mir: &Mir, debug_context: &FunctionDebu
 
 fn make_mir_scope(cx: &CodegenCx,
                   mir: &Mir,
-                  has_variables: &BitVector,
+                  has_variables: &BitVector<SourceScope>,
                   debug_context: &FunctionDebugContextData,
                   scope: SourceScope,
                   scopes: &mut IndexVec<SourceScope, MirDebugScope>) {
@@ -102,7 +102,7 @@ fn make_mir_scope(cx: &CodegenCx,
         return;
     };
 
-    if !has_variables.contains(scope.index()) {
+    if !has_variables.contains(scope) {
         // Do not create a DIScope if there are no variables
         // defined in this MIR Scope, to avoid debuginfo bloat.
 
diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs
index efd829c283f06..e105baba8aa2e 100644
--- a/src/librustc_codegen_llvm/mir/analyze.rs
+++ b/src/librustc_codegen_llvm/mir/analyze.rs
@@ -22,7 +22,7 @@ use rustc::ty::layout::LayoutOf;
 use type_of::LayoutLlvmExt;
 use super::FunctionCx;
 
-pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector {
+pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector<mir::Local> {
     let mir = fx.mir;
     let mut analyzer = LocalAnalyzer::new(fx);
 
@@ -54,7 +54,7 @@ pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector {
 struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> {
     fx: &'mir FunctionCx<'a, 'tcx>,
     dominators: Dominators<mir::BasicBlock>,
-    non_ssa_locals: BitVector,
+    non_ssa_locals: BitVector<mir::Local>,
     // The location of the first visited direct assignment to each
     // local, or an invalid location (out of bounds `block` index).
     first_assignment: IndexVec<mir::Local, Location>
@@ -90,7 +90,7 @@ impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> {
 
     fn not_ssa(&mut self, local: mir::Local) {
         debug!("marking {:?} as non-SSA", local);
-        self.non_ssa_locals.insert(local.index());
+        self.non_ssa_locals.insert(local);
     }
 
     fn assign(&mut self, local: mir::Local, location: Location) {
diff --git a/src/librustc_codegen_llvm/mir/mod.rs b/src/librustc_codegen_llvm/mir/mod.rs
index 608539dd3fa7e..312939408c62c 100644
--- a/src/librustc_codegen_llvm/mir/mod.rs
+++ b/src/librustc_codegen_llvm/mir/mod.rs
@@ -268,7 +268,7 @@ pub fn codegen_mir<'a, 'tcx: 'a>(
                 let debug_scope = fx.scopes[decl.visibility_scope];
                 let dbg = debug_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo;
 
-                if !memory_locals.contains(local.index()) && !dbg {
+                if !memory_locals.contains(local) && !dbg {
                     debug!("alloc: {:?} ({}) -> operand", local, name);
                     return LocalRef::new_operand(bx.cx, layout);
                 }
@@ -291,7 +291,7 @@ pub fn codegen_mir<'a, 'tcx: 'a>(
                     debug!("alloc: {:?} (return place) -> place", local);
                     let llretptr = llvm::get_param(llfn, 0);
                     LocalRef::Place(PlaceRef::new_sized(llretptr, layout, layout.align))
-                } else if memory_locals.contains(local.index()) {
+                } else if memory_locals.contains(local) {
                     debug!("alloc: {:?} -> place", local);
                     LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local)))
                 } else {
@@ -415,7 +415,7 @@ fn create_funclets<'a, 'tcx>(
 fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>,
                             fx: &FunctionCx<'a, 'tcx>,
                             scopes: &IndexVec<mir::SourceScope, debuginfo::MirDebugScope>,
-                            memory_locals: &BitVector)
+                            memory_locals: &BitVector<mir::Local>)
                             -> Vec<LocalRef<'tcx>> {
     let mir = fx.mir;
     let tcx = bx.tcx();
@@ -487,7 +487,7 @@ fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>,
             llarg_idx += 1;
         }
 
-        if arg_scope.is_none() && !memory_locals.contains(local.index()) {
+        if arg_scope.is_none() && !memory_locals.contains(local) {
             // We don't have to cast or keep the argument in the alloca.
             // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
             // of putting everything in allocas just so we can use llvm.dbg.declare.
diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs
index ee903e49642fc..04d6cb5e2a6d2 100644
--- a/src/librustc_data_structures/bitvec.rs
+++ b/src/librustc_data_structures/bitvec.rs
@@ -17,16 +17,18 @@ const WORD_BITS: usize = 128;
 
 /// A very simple BitVector type.
 #[derive(Clone, Debug, PartialEq)]
-pub struct BitVector {
+pub struct BitVector<C: Idx> {
     data: Vec<Word>,
+    marker: PhantomData<C>,
 }
 
-impl BitVector {
+impl<C: Idx> BitVector<C> {
     #[inline]
-    pub fn new(num_bits: usize) -> BitVector {
+    pub fn new(num_bits: usize) -> BitVector<C> {
         let num_words = words(num_bits);
         BitVector {
             data: vec![0; num_words],
+            marker: PhantomData,
         }
     }
 
@@ -41,15 +43,30 @@ impl BitVector {
         self.data.iter().map(|e| e.count_ones() as usize).sum()
     }
 
+    /// True if `self` contains the bit `bit`.
     #[inline]
-    pub fn contains(&self, bit: usize) -> bool {
+    pub fn contains(&self, bit: C) -> bool {
         let (word, mask) = word_mask(bit);
         (self.data[word] & mask) != 0
     }
 
+    /// True if `self` contains all the bits in `other`.
+    ///
+    /// The two vectors must have the same length.
+    #[inline]
+    pub fn contains_all(&self, other: &BitVector<C>) -> bool {
+        assert_eq!(self.data.len(), other.data.len());
+        self.data.iter().zip(&other.data).all(|(a, b)| (a & b) == *b)
+    }
+
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.data.iter().all(|a| *a == 0)
+    }
+
     /// Returns true if the bit has changed.
     #[inline]
-    pub fn insert(&mut self, bit: usize) -> bool {
+    pub fn insert(&mut self, bit: C) -> bool {
         let (word, mask) = word_mask(bit);
         let data = &mut self.data[word];
         let value = *data;
@@ -58,9 +75,16 @@ impl BitVector {
         new_value != value
     }
 
+    /// Sets all bits to true.
+    pub fn insert_all(&mut self) {
+        for data in &mut self.data {
+            *data = u128::max_value();
+        }
+    }
+
     /// Returns true if the bit has changed.
     #[inline]
-    pub fn remove(&mut self, bit: usize) -> bool {
+    pub fn remove(&mut self, bit: C) -> bool {
         let (word, mask) = word_mask(bit);
         let data = &mut self.data[word];
         let value = *data;
@@ -70,7 +94,7 @@ impl BitVector {
     }
 
     #[inline]
-    pub fn merge(&mut self, all: &BitVector) -> bool {
+    pub fn merge(&mut self, all: &BitVector<C>) -> bool {
         assert!(self.data.len() == all.data.len());
         let mut changed = false;
         for (i, j) in self.data.iter_mut().zip(&all.data) {
@@ -84,7 +108,7 @@ impl BitVector {
     }
 
     #[inline]
-    pub fn grow(&mut self, num_bits: usize) {
+    pub fn grow(&mut self, num_bits: C) {
         let num_words = words(num_bits);
         if self.data.len() < num_words {
             self.data.resize(num_words, 0)
@@ -93,24 +117,26 @@ impl BitVector {
 
     /// Iterates over indexes of set bits in a sorted order
     #[inline]
-    pub fn iter<'a>(&'a self) -> BitVectorIter<'a> {
+    pub fn iter<'a>(&'a self) -> BitVectorIter<'a, C> {
         BitVectorIter {
             iter: self.data.iter(),
             current: 0,
             idx: 0,
+            marker: PhantomData,
         }
     }
 }
 
-pub struct BitVectorIter<'a> {
+pub struct BitVectorIter<'a, C: Idx> {
     iter: ::std::slice::Iter<'a, Word>,
     current: Word,
     idx: usize,
+    marker: PhantomData<C>
 }
 
-impl<'a> Iterator for BitVectorIter<'a> {
-    type Item = usize;
-    fn next(&mut self) -> Option<usize> {
+impl<'a, C: Idx> Iterator for BitVectorIter<'a, C> {
+    type Item = C;
+    fn next(&mut self) -> Option<C> {
         while self.current == 0 {
             self.current = if let Some(&i) = self.iter.next() {
                 if i == 0 {
@@ -128,7 +154,7 @@ impl<'a> Iterator for BitVectorIter<'a> {
         self.current >>= offset;
         self.current >>= 1; // shift otherwise overflows for 0b1000_0000_…_0000
         self.idx += offset + 1;
-        return Some(self.idx - 1);
+        return Some(C::new(self.idx - 1));
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
@@ -137,8 +163,8 @@ impl<'a> Iterator for BitVectorIter<'a> {
     }
 }
 
-impl FromIterator<bool> for BitVector {
-    fn from_iter<I>(iter: I) -> BitVector
+impl<C: Idx> FromIterator<bool> for BitVector<C> {
+    fn from_iter<I>(iter: I) -> BitVector<C>
     where
         I: IntoIterator<Item = bool>,
     {
@@ -150,10 +176,10 @@ impl FromIterator<bool> for BitVector {
         let mut bv = BitVector::new(len);
         for (idx, val) in iter.enumerate() {
             if idx > len {
-                bv.grow(idx);
+                bv.grow(C::new(idx));
             }
             if val {
-                bv.insert(idx);
+                bv.insert(C::new(idx));
             }
         }
 
@@ -165,25 +191,28 @@ impl FromIterator<bool> for BitVector {
 /// one gigantic bitvector. In other words, it is as if you have
 /// `rows` bitvectors, each of length `columns`.
 #[derive(Clone, Debug)]
-pub struct BitMatrix {
+pub struct BitMatrix<R: Idx, C: Idx> {
     columns: usize,
     vector: Vec<Word>,
+    phantom: PhantomData<(R, C)>,
 }
 
-impl BitMatrix {
+impl<R: Idx, C: Idx> BitMatrix<R, C> {
     /// Create a new `rows x columns` matrix, initially empty.
-    pub fn new(rows: usize, columns: usize) -> BitMatrix {
+    pub fn new(rows: usize, columns: usize) -> BitMatrix<R, C> {
         // For every element, we need one bit for every other
         // element. Round up to an even number of words.
         let words_per_row = words(columns);
         BitMatrix {
             columns,
             vector: vec![0; rows * words_per_row],
+            phantom: PhantomData,
         }
     }
 
     /// The range of bits for a given row.
-    fn range(&self, row: usize) -> (usize, usize) {
+    fn range(&self, row: R) -> (usize, usize) {
+        let row = row.index();
         let words_per_row = words(self.columns);
         let start = row * words_per_row;
         (start, start + words_per_row)
@@ -193,7 +222,7 @@ impl BitMatrix {
     /// `column` to the bitset for `row`.
     ///
     /// Returns true if this changed the matrix, and false otherwise.
-    pub fn add(&mut self, row: usize, column: usize) -> bool {
+    pub fn add(&mut self, row: R, column: R) -> bool {
         let (start, _) = self.range(row);
         let (word, mask) = word_mask(column);
         let vector = &mut self.vector[..];
@@ -207,7 +236,7 @@ impl BitMatrix {
     /// the matrix cell at `(row, column)` true?  Put yet another way,
     /// if the matrix represents (transitive) reachability, can
     /// `row` reach `column`?
-    pub fn contains(&self, row: usize, column: usize) -> bool {
+    pub fn contains(&self, row: R, column: R) -> bool {
         let (start, _) = self.range(row);
         let (word, mask) = word_mask(column);
         (self.vector[start + word] & mask) != 0
@@ -217,7 +246,7 @@ impl BitMatrix {
     /// is an O(n) operation where `n` is the number of elements
     /// (somewhat independent from the actual size of the
     /// intersection, in particular).
-    pub fn intersection(&self, a: usize, b: usize) -> Vec<usize> {
+    pub fn intersection(&self, a: R, b: R) -> Vec<C> {
         let (a_start, a_end) = self.range(a);
         let (b_start, b_end) = self.range(b);
         let mut result = Vec::with_capacity(self.columns);
@@ -228,7 +257,7 @@ impl BitMatrix {
                     break;
                 }
                 if v & 0x1 != 0 {
-                    result.push(base * WORD_BITS + bit);
+                    result.push(C::new(base * WORD_BITS + bit));
                 }
                 v >>= 1;
             }
@@ -243,7 +272,7 @@ impl BitMatrix {
     /// you have an edge `write -> read`, because in that case
     /// `write` can reach everything that `read` can (and
     /// potentially more).
-    pub fn merge(&mut self, read: usize, write: usize) -> bool {
+    pub fn merge(&mut self, read: R, write: R) -> bool {
         let (read_start, read_end) = self.range(read);
         let (write_start, write_end) = self.range(write);
         let vector = &mut self.vector[..];
@@ -259,12 +288,13 @@ impl BitMatrix {
 
     /// Iterates through all the columns set to true in a given row of
     /// the matrix.
-    pub fn iter<'a>(&'a self, row: usize) -> BitVectorIter<'a> {
+    pub fn iter<'a>(&'a self, row: R) -> BitVectorIter<'a, C> {
         let (start, end) = self.range(row);
         BitVectorIter {
             iter: self.vector[start..end].iter(),
             current: 0,
             idx: 0,
+            marker: PhantomData,
         }
     }
 }
@@ -278,8 +308,7 @@ where
     C: Idx,
 {
     columns: usize,
-    vector: IndexVec<R, BitVector>,
-    marker: PhantomData<C>,
+    vector: IndexVec<R, BitVector<C>>,
 }
 
 impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
@@ -288,19 +317,22 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
         Self {
             columns,
             vector: IndexVec::new(),
-            marker: PhantomData,
         }
     }
 
+    fn ensure_row(&mut self, row: R) {
+        let columns = self.columns;
+        self.vector
+            .ensure_contains_elem(row, || BitVector::new(columns));
+    }
+
     /// Sets the cell at `(row, column)` to true. Put another way, insert
     /// `column` to the bitset for `row`.
     ///
     /// Returns true if this changed the matrix, and false otherwise.
     pub fn add(&mut self, row: R, column: C) -> bool {
-        let columns = self.columns;
-        self.vector
-            .ensure_contains_elem(row, || BitVector::new(columns));
-        self.vector[row].insert(column.index())
+        self.ensure_row(row);
+        self.vector[row].insert(column)
     }
 
     /// Do the bits from `row` contain `column`? Put another way, is
@@ -308,7 +340,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
     /// if the matrix represents (transitive) reachability, can
     /// `row` reach `column`?
     pub fn contains(&self, row: R, column: C) -> bool {
-        self.vector.get(row).map_or(false, |r| r.contains(column.index()))
+        self.vector.get(row).map_or(false, |r| r.contains(column))
     }
 
     /// Add the bits from row `read` to the bits from row `write`,
@@ -323,45 +355,56 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
             return false;
         }
 
-        let columns = self.columns;
-        self.vector
-            .ensure_contains_elem(write, || BitVector::new(columns));
+        self.ensure_row(write);
         let (bitvec_read, bitvec_write) = self.vector.pick2_mut(read, write);
         bitvec_write.merge(bitvec_read)
     }
 
     /// Merge a row, `from`, into the `into` row.
-    pub fn merge_into(&mut self, into: R, from: &BitVector) -> bool {
-        let columns = self.columns;
-        self.vector
-            .ensure_contains_elem(into, || BitVector::new(columns));
+    pub fn merge_into(&mut self, into: R, from: &BitVector<C>) -> bool {
+        self.ensure_row(into);
         self.vector[into].merge(from)
     }
 
+    /// Add all bits to the given row.
+    pub fn add_all(&mut self, row: R) {
+        self.ensure_row(row);
+        self.vector[row].insert_all();
+    }
+
     /// Number of elements in the matrix.
     pub fn len(&self) -> usize {
         self.vector.len()
     }
 
+    pub fn rows(&self) -> impl Iterator<Item = R> {
+        self.vector.indices()
+    }
+
     /// Iterates through all the columns set to true in a given row of
     /// the matrix.
     pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a {
-        self.vector.get(row).into_iter().flat_map(|r| r.iter().map(|n| C::new(n)))
+        self.vector.get(row).into_iter().flat_map(|r| r.iter())
     }
 
     /// Iterates through each row and the accompanying bit set.
-    pub fn iter_enumerated<'a>(&'a self) -> impl Iterator<Item = (R, &'a BitVector)> + 'a {
+    pub fn iter_enumerated<'a>(&'a self) -> impl Iterator<Item = (R, &'a BitVector<C>)> + 'a {
         self.vector.iter_enumerated()
     }
+
+    pub fn row(&self, row: R) -> Option<&BitVector<C>> {
+        self.vector.get(row)
+    }
 }
 
 #[inline]
-fn words(elements: usize) -> usize {
-    (elements + WORD_BITS - 1) / WORD_BITS
+fn words<C: Idx>(elements: C) -> usize {
+    (elements.index() + WORD_BITS - 1) / WORD_BITS
 }
 
 #[inline]
-fn word_mask(index: usize) -> (usize, Word) {
+fn word_mask<C: Idx>(index: C) -> (usize, Word) {
+    let index = index.index();
     let word = index / WORD_BITS;
     let mask = 1 << (index % WORD_BITS);
     (word, mask)
@@ -369,7 +412,7 @@ fn word_mask(index: usize) -> (usize, Word) {
 
 #[test]
 fn bitvec_iter_works() {
-    let mut bitvec = BitVector::new(100);
+    let mut bitvec: BitVector<usize> = BitVector::new(100);
     bitvec.insert(1);
     bitvec.insert(10);
     bitvec.insert(19);
@@ -387,7 +430,7 @@ fn bitvec_iter_works() {
 
 #[test]
 fn bitvec_iter_works_2() {
-    let mut bitvec = BitVector::new(319);
+    let mut bitvec: BitVector<usize> = BitVector::new(319);
     bitvec.insert(0);
     bitvec.insert(127);
     bitvec.insert(191);
@@ -398,8 +441,8 @@ fn bitvec_iter_works_2() {
 
 #[test]
 fn union_two_vecs() {
-    let mut vec1 = BitVector::new(65);
-    let mut vec2 = BitVector::new(65);
+    let mut vec1: BitVector<usize> = BitVector::new(65);
+    let mut vec2: BitVector<usize> = BitVector::new(65);
     assert!(vec1.insert(3));
     assert!(!vec1.insert(3));
     assert!(vec2.insert(5));
@@ -415,7 +458,7 @@ fn union_two_vecs() {
 
 #[test]
 fn grow() {
-    let mut vec1 = BitVector::new(65);
+    let mut vec1: BitVector<usize> = BitVector::new(65);
     for index in 0..65 {
         assert!(vec1.insert(index));
         assert!(!vec1.insert(index));
@@ -441,7 +484,7 @@ fn grow() {
 
 #[test]
 fn matrix_intersection() {
-    let mut vec1 = BitMatrix::new(200, 200);
+    let mut vec1: BitMatrix<usize, usize> = BitMatrix::new(200, 200);
 
     // (*) Elements reachable from both 2 and 65.
 
@@ -472,7 +515,49 @@ fn matrix_intersection() {
 
 #[test]
 fn matrix_iter() {
-    let mut matrix = BitMatrix::new(64, 100);
+    let mut matrix: BitMatrix<usize, usize> = BitMatrix::new(64, 100);
+    matrix.add(3, 22);
+    matrix.add(3, 75);
+    matrix.add(2, 99);
+    matrix.add(4, 0);
+    matrix.merge(3, 5);
+
+    let expected = [99];
+    let mut iter = expected.iter();
+    for i in matrix.iter(2) {
+        let j = *iter.next().unwrap();
+        assert_eq!(i, j);
+    }
+    assert!(iter.next().is_none());
+
+    let expected = [22, 75];
+    let mut iter = expected.iter();
+    for i in matrix.iter(3) {
+        let j = *iter.next().unwrap();
+        assert_eq!(i, j);
+    }
+    assert!(iter.next().is_none());
+
+    let expected = [0];
+    let mut iter = expected.iter();
+    for i in matrix.iter(4) {
+        let j = *iter.next().unwrap();
+        assert_eq!(i, j);
+    }
+    assert!(iter.next().is_none());
+
+    let expected = [22, 75];
+    let mut iter = expected.iter();
+    for i in matrix.iter(5) {
+        let j = *iter.next().unwrap();
+        assert_eq!(i, j);
+    }
+    assert!(iter.next().is_none());
+}
+
+#[test]
+fn sparse_matrix_iter() {
+    let mut matrix: SparseBitMatrix<usize, usize> = SparseBitMatrix::new(100);
     matrix.add(3, 22);
     matrix.add(3, 75);
     matrix.add(2, 99);
diff --git a/src/librustc_data_structures/graph/implementation/mod.rs b/src/librustc_data_structures/graph/implementation/mod.rs
index e2b393071ff5c..dbfc09116bbaa 100644
--- a/src/librustc_data_structures/graph/implementation/mod.rs
+++ b/src/librustc_data_structures/graph/implementation/mod.rs
@@ -348,7 +348,7 @@ where
 {
     graph: &'g Graph<N, E>,
     stack: Vec<NodeIndex>,
-    visited: BitVector,
+    visited: BitVector<usize>,
     direction: Direction,
 }
 
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
index e7a75c149ccf0..c358f2f852e18 100644
--- a/src/librustc_data_structures/indexed_vec.rs
+++ b/src/librustc_data_structures/indexed_vec.rs
@@ -25,7 +25,13 @@ use rustc_serialize as serialize;
 /// (purpose: avoid mixing indexes for different bitvector domains.)
 pub trait Idx: Copy + 'static + Ord + Debug + Hash {
     fn new(idx: usize) -> Self;
+
     fn index(self) -> usize;
+
+    fn increment_by(&mut self, amount: usize) {
+        let v = self.index() + amount;
+        *self = Self::new(v);
+    }
 }
 
 impl Idx for usize {
@@ -89,6 +95,35 @@ macro_rules! newtype_index {
             }
         }
 
+        impl ::std::iter::Step for $type {
+            fn steps_between(start: &Self, end: &Self) -> Option<usize> {
+                <usize as ::std::iter::Step>::steps_between(
+                    &Idx::index(*start),
+                    &Idx::index(*end),
+                )
+            }
+
+            fn replace_one(&mut self) -> Self {
+                ::std::mem::replace(self, Self::new(1))
+            }
+
+            fn replace_zero(&mut self) -> Self {
+                ::std::mem::replace(self, Self::new(0))
+            }
+
+            fn add_one(&self) -> Self {
+                Self::new(Idx::index(*self) + 1)
+            }
+
+            fn sub_one(&self) -> Self {
+                Self::new(Idx::index(*self) - 1)
+            }
+
+            fn add_usize(&self, u: usize) -> Option<Self> {
+                Idx::index(*self).checked_add(u).map(Self::new)
+            }
+        }
+
         newtype_index!(
             @handle_debug
             @derives      [$($derives,)*]
@@ -475,8 +510,8 @@ impl<I: Idx, T> IndexVec<I, T> {
     }
 
     #[inline]
-    pub fn swap(&mut self, a: usize, b: usize) {
-        self.raw.swap(a, b)
+    pub fn swap(&mut self, a: I, b: I) {
+        self.raw.swap(a.index(), b.index())
     }
 
     #[inline]
diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs
index 6d63bc4436fe8..a8124fb7c5b62 100644
--- a/src/librustc_data_structures/transitive_relation.rs
+++ b/src/librustc_data_structures/transitive_relation.rs
@@ -39,7 +39,7 @@ pub struct TransitiveRelation<T: Clone + Debug + Eq + Hash> {
     // are added with new elements. Perhaps better would be to ask the
     // user for a batch of edges to minimize this effect, but I
     // already wrote the code this way. :P -nmatsakis
-    closure: Lock<Option<BitMatrix>>,
+    closure: Lock<Option<BitMatrix<usize, usize>>>,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)]
@@ -354,7 +354,7 @@ impl<T: Clone + Debug + Eq + Hash> TransitiveRelation<T> {
     }
 
     fn with_closure<OP, R>(&self, op: OP) -> R
-        where OP: FnOnce(&BitMatrix) -> R
+        where OP: FnOnce(&BitMatrix<usize, usize>) -> R
     {
         let mut closure_cell = self.closure.borrow_mut();
         let mut closure = closure_cell.take();
@@ -366,7 +366,7 @@ impl<T: Clone + Debug + Eq + Hash> TransitiveRelation<T> {
         result
     }
 
-    fn compute_closure(&self) -> BitMatrix {
+    fn compute_closure(&self) -> BitMatrix<usize, usize> {
         let mut matrix = BitMatrix::new(self.elements.len(),
                                         self.elements.len());
         let mut changed = true;
@@ -396,7 +396,7 @@ impl<T: Clone + Debug + Eq + Hash> TransitiveRelation<T> {
 /// - Input: `[a, b, x]`. Output: `[a, x]`.
 /// - Input: `[b, a, x]`. Output: `[b, a, x]`.
 /// - Input: `[a, x, b, y]`. Output: `[a, x]`.
-fn pare_down(candidates: &mut Vec<usize>, closure: &BitMatrix) {
+fn pare_down(candidates: &mut Vec<usize>, closure: &BitMatrix<usize, usize>) {
     let mut i = 0;
     while i < candidates.len() {
         let candidate_i = candidates[i];
diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
index f274f8e91894e..457499ded5657 100644
--- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
@@ -12,7 +12,7 @@ use borrow_check::borrow_set::BorrowSet;
 use borrow_check::location::LocationTable;
 use borrow_check::nll::ToRegionVid;
 use borrow_check::nll::facts::AllFacts;
-use borrow_check::nll::region_infer::RegionInferenceContext;
+use borrow_check::nll::region_infer::values::LivenessValues;
 use rustc::infer::InferCtxt;
 use rustc::mir::visit::TyContext;
 use rustc::mir::visit::Visitor;
@@ -20,11 +20,11 @@ use rustc::mir::{BasicBlock, BasicBlockData, Location, Mir, Place, Rvalue};
 use rustc::mir::{Local, Statement, Terminator};
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::subst::Substs;
-use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorSubsts};
+use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorSubsts, RegionVid};
 
 pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
     infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
-    regioncx: &mut RegionInferenceContext<'tcx>,
+    liveness_constraints: &mut LivenessValues<RegionVid>,
     all_facts: &mut Option<AllFacts>,
     location_table: &LocationTable,
     mir: &Mir<'tcx>,
@@ -33,7 +33,7 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
     let mut cg = ConstraintGeneration {
         borrow_set,
         infcx,
-        regioncx,
+        liveness_constraints,
         location_table,
         all_facts,
     };
@@ -48,7 +48,7 @@ struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> {
     infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
     all_facts: &'cg mut Option<AllFacts>,
     location_table: &'cg LocationTable,
-    regioncx: &'cg mut RegionInferenceContext<'tcx>,
+    liveness_constraints: &'cg mut LivenessValues<RegionVid>,
     borrow_set: &'cg BorrowSet<'tcx>,
 }
 
@@ -202,7 +202,7 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
             .tcx
             .for_each_free_region(&live_ty, |live_region| {
                 let vid = live_region.to_region_vid();
-                self.regioncx.add_live_element(vid, location);
+                self.liveness_constraints.add_element(vid, location);
             });
     }
 }
diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
index bc4646b7c789a..0039958b6b14d 100644
--- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
@@ -54,7 +54,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             borrow_region_vid
         );
 
-        let region_sub = regioncx.find_constraint(borrow_region_vid, context.loc);
+        let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, context.loc);
 
         debug!(
             "explain_why_borrow_contains_point: region_sub={:?}",
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index edc701bad1eb3..63d5de4f2e525 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -104,7 +104,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
         None
     };
 
-    let elements = &Rc::new(RegionValueElements::new(mir, universal_regions.len()));
+    let elements = &Rc::new(RegionValueElements::new(mir));
 
     // Run the MIR type-checker.
     let liveness_map = NllLivenessMap::compute(&mir);
@@ -136,10 +136,20 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     // base constraints generated by the type-check.
     let var_origins = infcx.take_region_var_origins();
     let MirTypeckRegionConstraints {
-        liveness_constraints,
+        mut liveness_constraints,
         outlives_constraints,
         type_tests,
     } = constraint_sets;
+
+    constraint_generation::generate_constraints(
+        infcx,
+        &mut liveness_constraints,
+        &mut all_facts,
+        location_table,
+        &mir,
+        borrow_set,
+    );
+
     let mut regioncx = RegionInferenceContext::new(
         var_origins,
         universal_regions,
@@ -151,14 +161,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     );
 
     // Generate various additional constraints.
-    constraint_generation::generate_constraints(
-        infcx,
-        &mut regioncx,
-        &mut all_facts,
-        location_table,
-        &mir,
-        borrow_set,
-    );
     invalidation::generate_invalidates(
         infcx,
         &mut all_facts,
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 3c73203706dcb..88b34c767324c 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
@@ -13,6 +13,7 @@
 //! state of region inference. This code handles emitting the region
 //! context internal state.
 
+use rustc::infer::NLLRegionVariableOrigin;
 use std::io::{self, Write};
 use super::{OutlivesConstraint, RegionInferenceContext};
 
@@ -27,8 +28,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         writeln!(out, "| Free Region Mapping")?;
 
         for region in self.regions() {
-            if self.definitions[region].is_universal {
-                let classification = self.universal_regions.region_classification(region).unwrap();
+            if let NLLRegionVariableOrigin::FreeRegion = self.definitions[region].origin {
+                let classification = self
+                    .universal_regions
+                    .region_classification(region)
+                    .unwrap();
                 let outlived_by = self.universal_regions.regions_outlived_by(region);
                 writeln!(
                     out,
@@ -47,9 +51,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         for region in self.regions() {
             writeln!(
                 out,
-                "| {r:rw$} | {v}",
+                "| {r:rw$} | {ui:4?} | {v}",
                 r = format!("{:?}", region),
                 rw = REGION_WIDTH,
+                ui = self.region_universe(region),
                 v = self.region_value_str(region),
             )?;
         }
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 131e1defc1f9e..1b540ef74be49 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,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use borrow_check::nll::region_infer::values::ToElementIndex;
 use borrow_check::nll::region_infer::{ConstraintIndex, RegionInferenceContext};
 use borrow_check::nll::type_check::Locations;
 use rustc::hir::def_id::DefId;
@@ -16,9 +15,9 @@ use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
 use rustc::infer::InferCtxt;
 use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
 use rustc::ty::RegionVid;
-use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_errors::Diagnostic;
+use std::collections::VecDeque;
 use std::fmt;
 use syntax_pos::Span;
 
@@ -28,7 +27,7 @@ mod var_name;
 /// 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(Debug, Eq, PartialEq, PartialOrd, Ord)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
 enum ConstraintCategory {
     Cast,
     Assignment,
@@ -43,77 +42,178 @@ enum ConstraintCategory {
 impl fmt::Display for ConstraintCategory {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
-            ConstraintCategory::Assignment |
-            ConstraintCategory::AssignmentToUpvar => write!(f, "assignment"),
+            ConstraintCategory::Assignment | ConstraintCategory::AssignmentToUpvar => {
+                write!(f, "assignment")
+            }
             ConstraintCategory::Return => write!(f, "return"),
             ConstraintCategory::Cast => write!(f, "cast"),
-            ConstraintCategory::CallArgument |
-            ConstraintCategory::CallArgumentToUpvar => write!(f, "argument"),
+            ConstraintCategory::CallArgument | ConstraintCategory::CallArgumentToUpvar => {
+                write!(f, "argument")
+            }
             _ => write!(f, "free region"),
         }
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq)]
+enum Trace {
+    StartRegion,
+    FromConstraint(ConstraintIndex),
+    NotVisited,
+}
+
 impl<'tcx> RegionInferenceContext<'tcx> {
+    /// Tries to find the best constraint to blame for the fact that
+    /// `R: from_region`, where `R` is some region that meets
+    /// `target_test`. This works by following the constraint graph,
+    /// creating a constraint path that forces `R` to outlive
+    /// `from_region`, and then finding the best choices within that
+    /// path to blame.
+    fn best_blame_constraint(
+        &self,
+        mir: &Mir<'tcx>,
+        from_region: RegionVid,
+        target_test: impl Fn(RegionVid) -> bool,
+    ) -> (ConstraintCategory, Span, RegionVid) {
+        debug!("best_blame_constraint(from_region={:?})", from_region);
+
+        // Find all paths
+        let (path, target_region) = self
+            .find_constraint_paths_between_regions(from_region, target_test)
+            .unwrap();
+        debug!(
+            "best_blame_constraint: path={:#?}",
+            path.iter()
+                .map(|&ci| format!(
+                    "{:?}: {:?} ({:?}: {:?})",
+                    ci,
+                    &self.constraints[ci],
+                    self.constraint_sccs.scc(self.constraints[ci].sup),
+                    self.constraint_sccs.scc(self.constraints[ci].sub),
+                ))
+                .collect::<Vec<_>>()
+        );
+
+        // Classify each of the constraints along the path.
+        let mut categorized_path: Vec<(ConstraintCategory, Span)> = path
+            .iter()
+            .map(|&index| self.classify_constraint(index, mir))
+            .collect();
+        debug!(
+            "best_blame_constraint: categorized_path={:#?}",
+            categorized_path
+        );
+
+        // To find the best span to cite, we first try to look for the
+        // final constraint that is interesting and where the `sup` is
+        // not unified with the ultimate target region. The reason
+        // for this is that we have a chain of constraints that lead
+        // from the source to the target region, something like:
+        //
+        //    '0: '1 ('0 is the source)
+        //    '1: '2
+        //    '2: '3
+        //    '3: '4
+        //    '4: '5
+        //    '5: '6 ('6 is the target)
+        //
+        // Some of those regions are unified with `'6` (in the same
+        // SCC).  We want to screen those out. After that point, the
+        // "closest" constraint we have to the end is going to be the
+        // most likely to be the point where the value escapes -- but
+        // we still want to screen for an "interesting" point to
+        // highlight (e.g., a call site or something).
+        let target_scc = self.constraint_sccs.scc(target_region);
+        let best_choice = (0..path.len()).rev().find(|&i| {
+            let constraint = &self.constraints[path[i]];
+
+            let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
+            if constraint_sup_scc == target_scc {
+                return false;
+            }
+
+            match categorized_path[i].0 {
+                ConstraintCategory::Boring => false,
+                _ => true,
+            }
+        });
+        if let Some(i) = best_choice {
+            let (category, span) = categorized_path[i];
+            return (category, span, target_region);
+        }
+
+        // If that search fails, that is.. unusual. Maybe everything
+        // is in the same SCC or something. In that case, find what
+        // appears to be the most interesting point to report to the
+        // user via an even more ad-hoc guess.
+        categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
+        debug!("best_blame_constraint: sorted_path={:#?}", categorized_path);
+
+        let &(category, span) = categorized_path.first().unwrap();
+
+        (category, span, target_region)
+    }
+
     /// Walks the graph of constraints (where `'a: 'b` is considered
     /// an edge `'a -> 'b`) to find all paths from `from_region` to
     /// `to_region`. The paths are accumulated into the vector
     /// `results`. The paths are stored as a series of
     /// `ConstraintIndex` values -- in other words, a list of *edges*.
+    ///
+    /// Returns: a series of constraints as well as the region `R`
+    /// that passed the target test.
     fn find_constraint_paths_between_regions(
         &self,
         from_region: RegionVid,
         target_test: impl Fn(RegionVid) -> bool,
-    ) -> Vec<Vec<ConstraintIndex>> {
-        let mut results = vec![];
-        self.find_constraint_paths_between_regions_helper(
-            from_region,
-            from_region,
-            &target_test,
-            &mut FxHashSet::default(),
-            &mut vec![],
-            &mut results,
-        );
-        results
-    }
+    ) -> Option<(Vec<ConstraintIndex>, RegionVid)> {
+        let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
+        context[from_region] = Trace::StartRegion;
+
+        // Use a deque so that we do a breadth-first search. We will
+        // stop at the first match, which ought to be the shortest
+        // path (fewest constraints).
+        let mut deque = VecDeque::new();
+        deque.push_back(from_region);
+
+        while let Some(r) = deque.pop_front() {
+            // Check if we reached the region we were looking for. If so,
+            // we can reconstruct the path that led to it and return it.
+            if target_test(r) {
+                let mut result = vec![];
+                let mut p = r;
+                loop {
+                    match context[p] {
+                        Trace::NotVisited => {
+                            bug!("found unvisited region {:?} on path to {:?}", p, r)
+                        }
+                        Trace::FromConstraint(c) => {
+                            result.push(c);
+                            p = self.constraints[c].sup;
+                        }
 
-    /// Helper for `find_constraint_paths_between_regions`.
-    fn find_constraint_paths_between_regions_helper(
-        &self,
-        from_region: RegionVid,
-        current_region: RegionVid,
-        target_test: &impl Fn(RegionVid) -> bool,
-        visited: &mut FxHashSet<RegionVid>,
-        stack: &mut Vec<ConstraintIndex>,
-        results: &mut Vec<Vec<ConstraintIndex>>,
-    ) {
-        // Check if we already visited this region.
-        if !visited.insert(current_region) {
-            return;
-        }
+                        Trace::StartRegion => {
+                            result.reverse();
+                            return Some((result, r));
+                        }
+                    }
+                }
+            }
 
-        // Check if we reached the region we were looking for.
-        if target_test(current_region) {
-            if !stack.is_empty() {
-                assert_eq!(self.constraints[stack[0]].sup, from_region);
-                results.push(stack.clone());
+            // Otherwise, walk over the outgoing constraints and
+            // enqueue any regions we find, keeping track of how we
+            // reached them.
+            for constraint in self.constraint_graph.outgoing_edges(r) {
+                assert_eq!(self.constraints[constraint].sup, r);
+                let sub_region = self.constraints[constraint].sub;
+                if let Trace::NotVisited = context[sub_region] {
+                    context[sub_region] = Trace::FromConstraint(constraint);
+                    deque.push_back(sub_region);
+                }
             }
-            return;
         }
 
-        for constraint in self.constraint_graph.outgoing_edges(current_region) {
-            assert_eq!(self.constraints[constraint].sup, current_region);
-            stack.push(constraint);
-            self.find_constraint_paths_between_regions_helper(
-                from_region,
-                self.constraints[constraint].sub,
-                target_test,
-                visited,
-                stack,
-                results,
-            );
-            stack.pop();
-        }
+        None
     }
 
     /// This function will return true if a constraint is interesting and false if a constraint
@@ -136,19 +236,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         index: ConstraintIndex,
         mir: &Mir<'tcx>,
-        _infcx: &InferCtxt<'_, '_, 'tcx>,
     ) -> (ConstraintCategory, Span) {
         let constraint = self.constraints[index];
         debug!("classify_constraint: constraint={:?}", constraint);
         let span = constraint.locations.span(mir);
-        let location = constraint.locations.from_location().unwrap_or(Location::START);
+        let location = constraint
+            .locations
+            .from_location()
+            .unwrap_or(Location::START);
 
         if !self.constraint_is_interesting(index) {
             return (ConstraintCategory::Boring, span);
         }
 
         let data = &mir[location.block];
-        debug!("classify_constraint: location={:?} data={:?}", location, data);
+        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);
@@ -171,8 +276,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     } else {
                         match rvalue {
                             Rvalue::Cast(..) => ConstraintCategory::Cast,
-                            Rvalue::Use(..) |
-                            Rvalue::Aggregate(..) => ConstraintCategory::Assignment,
+                            Rvalue::Use(..) | Rvalue::Aggregate(..) => {
+                                ConstraintCategory::Assignment
+                            }
                             _ => ConstraintCategory::Other,
                         }
                     }
@@ -199,61 +305,56 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mir_def_id: DefId,
         fr: RegionVid,
         outlived_fr: RegionVid,
-        blame_span: Span,
         errors_buffer: &mut Vec<Diagnostic>,
     ) {
         debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
 
+        let (category, span, _) = self.best_blame_constraint(mir, fr, |r| r == outlived_fr);
+
+        // Check if we can use one of the "nice region errors".
         if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
             let tables = infcx.tcx.typeck_tables_of(mir_def_id);
-            let nice = NiceRegionError::new_from_span(infcx.tcx, blame_span, o, f, Some(tables));
+            let nice = NiceRegionError::new_from_span(infcx.tcx, span, o, f, Some(tables));
             if let Some(_error_reported) = nice.try_report() {
                 return;
             }
         }
 
-        // Find all paths
-        let constraint_paths = self.find_constraint_paths_between_regions(fr, |r| r == outlived_fr);
-        debug!("report_error: constraint_paths={:#?}", constraint_paths);
-
-        // Find the shortest such path.
-        let path = constraint_paths.iter().min_by_key(|p| p.len()).unwrap();
-        debug!("report_error: shortest_path={:?}", path);
-
-        // Classify each of the constraints along the path.
-        let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
-            .map(|&index| self.classify_constraint(index, mir, infcx))
-            .collect();
-        debug!("report_error: categorized_path={:?}", categorized_path);
-
-        // Find what appears to be the most interesting path to report to the user.
-        categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
-        debug!("report_error: sorted_path={:?}", categorized_path);
-
-        // Get a span
-        let (category, span) = categorized_path.first().unwrap();
-
         let category = match (
             category,
             self.universal_regions.is_local_free_region(fr),
             self.universal_regions.is_local_free_region(outlived_fr),
         ) {
-            (ConstraintCategory::Assignment, true, false) =>
-                &ConstraintCategory::AssignmentToUpvar,
-            (ConstraintCategory::CallArgument, true, false) =>
-                &ConstraintCategory::CallArgumentToUpvar,
+            (ConstraintCategory::Assignment, true, false) => ConstraintCategory::AssignmentToUpvar,
+            (ConstraintCategory::CallArgument, true, false) => {
+                ConstraintCategory::CallArgumentToUpvar
+            }
             (category, _, _) => category,
         };
 
         debug!("report_error: category={:?}", category);
         match category {
-            ConstraintCategory::AssignmentToUpvar |
-            ConstraintCategory::CallArgumentToUpvar =>
-                self.report_closure_error(
-                    mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
-            _ =>
-                self.report_general_error(
-                    mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
+            ConstraintCategory::AssignmentToUpvar | ConstraintCategory::CallArgumentToUpvar => self
+                .report_closure_error(
+                    mir,
+                    infcx,
+                    mir_def_id,
+                    fr,
+                    outlived_fr,
+                    category,
+                    span,
+                    errors_buffer,
+                ),
+            _ => self.report_general_error(
+                mir,
+                infcx,
+                mir_def_id,
+                fr,
+                outlived_fr,
+                category,
+                span,
+                errors_buffer,
+            ),
         }
     }
 
@@ -264,23 +365,31 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mir_def_id: DefId,
         fr: RegionVid,
         outlived_fr: RegionVid,
-        category: &ConstraintCategory,
-        span: &Span,
+        category: ConstraintCategory,
+        span: Span,
         errors_buffer: &mut Vec<Diagnostic>,
     ) {
-        let fr_name_and_span  = self.get_var_name_and_span_for_region(
-            infcx.tcx, mir, fr);
-        let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
-            infcx.tcx, mir,outlived_fr);
+        let fr_name_and_span = self.get_var_name_and_span_for_region(infcx.tcx, mir, fr);
+        let outlived_fr_name_and_span =
+            self.get_var_name_and_span_for_region(infcx.tcx, mir, outlived_fr);
 
         if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
             return self.report_general_error(
-                mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer);
+                mir,
+                infcx,
+                mir_def_id,
+                fr,
+                outlived_fr,
+                category,
+                span,
+                errors_buffer,
+            );
         }
 
-        let mut diag = infcx.tcx.sess.struct_span_err(
-            *span, &format!("borrowed data escapes outside of closure"),
-        );
+        let mut diag = infcx
+            .tcx
+            .sess
+            .struct_span_err(span, &format!("borrowed data escapes outside of closure"));
 
         if let Some((outlived_fr_name, outlived_fr_span)) = outlived_fr_name_and_span {
             if let Some(name) = outlived_fr_name {
@@ -295,10 +404,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             if let Some(name) = fr_name {
                 diag.span_label(
                     fr_span,
-                    format!("`{}` is a reference that is only valid in the closure body", name),
+                    format!(
+                        "`{}` is a reference that is only valid in the closure body",
+                        name
+                    ),
                 );
 
-                diag.span_label(*span, format!("`{}` escapes the closure body here", name));
+                diag.span_label(span, format!("`{}` escapes the closure body here", name));
             }
         }
 
@@ -312,104 +424,50 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mir_def_id: DefId,
         fr: RegionVid,
         outlived_fr: RegionVid,
-        category: &ConstraintCategory,
-        span: &Span,
+        category: ConstraintCategory,
+        span: Span,
         errors_buffer: &mut Vec<Diagnostic>,
     ) {
         let mut diag = infcx.tcx.sess.struct_span_err(
-            *span, &format!("unsatisfied lifetime constraints"), // FIXME
+            span,
+            &format!("unsatisfied lifetime constraints"), // FIXME
         );
 
         let counter = &mut 1;
-        let fr_name = self.give_region_a_name(
-            infcx.tcx, mir, mir_def_id, fr, counter, &mut diag);
-        let outlived_fr_name = self.give_region_a_name(
-            infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag);
-
-        diag.span_label(*span, format!(
-            "{} requires that `{}` must outlive `{}`",
-            category, fr_name, outlived_fr_name,
-        ));
+        let fr_name = self.give_region_a_name(infcx.tcx, mir, mir_def_id, fr, counter, &mut diag);
+        let outlived_fr_name =
+            self.give_region_a_name(infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag);
+
+        diag.span_label(
+            span,
+            format!(
+                "{} requires that `{}` must outlive `{}`",
+                category, fr_name, outlived_fr_name,
+            ),
+        );
 
         diag.buffer(errors_buffer);
     }
 
-    // Find some constraint `X: Y` where:
-    // - `fr1: X` transitively
-    // - and `Y` is live at `elem`
-    crate fn find_constraint(&self, fr1: RegionVid, elem: Location) -> RegionVid {
-        let index = self.blame_constraint(fr1, elem);
-        self.constraints[index].sub
+    // Finds some region R such that `fr1: R` and `R` is live at
+    // `elem`.
+    crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
+        // Find all paths
+        let (_path, r) =
+            self.find_constraint_paths_between_regions(fr1, |r| {
+                self.liveness_constraints.contains(r, elem)
+            }).unwrap();
+        r
     }
 
-    /// Tries to finds a good span to blame for the fact that `fr1`
-    /// contains `fr2`.
-    pub(super) fn blame_constraint(
+    // Finds a good span to blame for the fact that `fr1` outlives `fr2`.
+    crate fn find_outlives_blame_span(
         &self,
+        mir: &Mir<'tcx>,
         fr1: RegionVid,
-        elem: impl ToElementIndex,
-    ) -> ConstraintIndex {
-        // Find everything that influenced final value of `fr`.
-        let influenced_fr1 = self.dependencies(fr1);
-
-        // Try to find some outlives constraint `'X: fr2` where `'X`
-        // influenced `fr1`. Blame that.
-        //
-        // NB, this is a pretty bad choice most of the time. In
-        // particular, the connection between `'X` and `fr1` may not
-        // be obvious to the user -- not to mention the naive notion
-        // of dependencies, which doesn't account for the locations of
-        // contraints at all. But it will do for now.
-        let relevant_constraint = self.constraints
-            .iter_enumerated()
-            .filter_map(|(i, constraint)| {
-                if !self.liveness_constraints.contains(constraint.sub, elem) {
-                    None
-                } else {
-                    influenced_fr1[constraint.sup]
-                        .map(|distance| (distance, i))
-                }
-            })
-            .min() // constraining fr1 with fewer hops *ought* to be more obvious
-            .map(|(_dist, i)| i);
-
-        relevant_constraint.unwrap_or_else(|| {
-            bug!(
-                "could not find any constraint to blame for {:?}: {:?}",
-                fr1,
-                elem,
-            );
-        })
-    }
-
-    /// Finds all regions whose values `'a` may depend on in some way.
-    /// For each region, returns either `None` (does not influence
-    /// `'a`) or `Some(d)` which indicates that it influences `'a`
-    /// with distinct `d` (minimum number of edges that must be
-    /// traversed).
-    ///
-    /// Used during error reporting, extremely naive and inefficient.
-    fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, Option<usize>> {
-        let mut result_set = IndexVec::from_elem(None, &self.definitions);
-        let mut changed = true;
-        result_set[r0] = Some(0); // distance 0 from `r0`
-
-        while changed {
-            changed = false;
-            for constraint in self.constraints.iter() {
-                if let Some(n) = result_set[constraint.sup] {
-                    let m = n + 1;
-                    if result_set[constraint.sub]
-                        .map(|distance| m < distance)
-                        .unwrap_or(true)
-                    {
-                        result_set[constraint.sub] = Some(m);
-                        changed = true;
-                    }
-                }
-            }
-        }
-
-        result_set
+        fr2: RegionVid,
+    ) -> Span {
+        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 52f596f61c2e8..2ab72f655352f 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -9,18 +9,16 @@
 // except according to those terms.
 
 use super::universal_regions::UniversalRegions;
+use borrow_check::nll::constraints::graph::ConstraintGraph;
 use borrow_check::nll::constraints::{
     ConstraintIndex, ConstraintSccIndex, ConstraintSet, OutlivesConstraint,
 };
-use borrow_check::nll::constraints::graph::ConstraintGraph;
-use borrow_check::nll::region_infer::values::ToElementIndex;
+use borrow_check::nll::region_infer::values::{RegionElement, ToElementIndex};
 use borrow_check::nll::type_check::Locations;
 use rustc::hir::def_id::DefId;
 use rustc::infer::canonical::QueryRegionConstraint;
 use rustc::infer::region_constraints::{GenericKind, VarInfos};
-use rustc::infer::InferCtxt;
-use rustc::infer::NLLRegionVariableOrigin;
-use rustc::infer::RegionVariableOrigin;
+use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin};
 use rustc::mir::{
     ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location,
     Mir,
@@ -39,7 +37,7 @@ mod dump_mir;
 mod error_reporting;
 mod graphviz;
 pub mod values;
-use self::values::{RegionValueElements, RegionValues};
+use self::values::{LivenessValues, RegionValueElements, RegionValues};
 
 use super::ToRegionVid;
 
@@ -50,14 +48,11 @@ pub struct RegionInferenceContext<'tcx> {
     /// from as well as its final inferred value.
     definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
 
-    /// Maps from points/universal-regions to a `RegionElementIndex`.
-    elements: Rc<RegionValueElements>,
-
     /// The liveness constraints added to each region. For most
     /// regions, these start out empty and steadily grow, though for
     /// each universally quantified region R they start out containing
     /// the entire CFG and `end(R)`.
-    liveness_constraints: RegionValues<RegionVid>,
+    liveness_constraints: LivenessValues<RegionVid>,
 
     /// The outlives constraints computed by the type-check.
     constraints: Rc<ConstraintSet>,
@@ -71,6 +66,11 @@ pub struct RegionInferenceContext<'tcx> {
     /// of each region.
     constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
 
+    /// Contains the minimum universe of any variable within the same
+    /// SCC. We will ensure that no SCC contains values that are not
+    /// visible from this index.
+    scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
+
     /// The final inferred values of the region variables; we compute
     /// one value per SCC. To get the value for any given *region*,
     /// you first find which scc it is a part of.
@@ -85,17 +85,16 @@ pub struct RegionInferenceContext<'tcx> {
 }
 
 struct RegionDefinition<'tcx> {
-    /// Why we created this variable. Mostly these will be
-    /// `RegionVariableOrigin::NLL`, but some variables get created
-    /// elsewhere in the code with other causes (e.g., instantiation
-    /// late-bound-regions).
-    origin: RegionVariableOrigin,
-
-    /// True if this is a universally quantified region. This means a
-    /// lifetime parameter that appears in the function signature (or,
-    /// in the case of a closure, in the closure environment, which of
-    /// course is also in the function signature).
-    is_universal: bool,
+    /// What kind of variable is this -- a free region? existential
+    /// variable? etc. (See the `NLLRegionVariableOrigin` for more
+    /// info.)
+    origin: NLLRegionVariableOrigin,
+
+    /// Which universe is this region variable defined in? This is
+    /// most often `ty::UniverseIndex::ROOT`, but when we encounter
+    /// forall-quantifiers like `for<'a> { 'a = 'b }`, we would create
+    /// the variable for `'a` in a subuniverse.
+    universe: ty::UniverseIndex,
 
     /// If this is 'static or an early-bound region, then this is
     /// `Some(X)` where `X` is the name of the region.
@@ -211,7 +210,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         _mir: &Mir<'tcx>,
         outlives_constraints: ConstraintSet,
         type_tests: Vec<TypeTest<'tcx>>,
-        liveness_constraints: RegionValues<RegionVid>,
+        liveness_constraints: LivenessValues<RegionVid>,
         elements: &Rc<RegionValueElements>,
     ) -> Self {
         let universal_regions = Rc::new(universal_regions);
@@ -219,37 +218,71 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // Create a RegionDefinition for each inference variable.
         let definitions: IndexVec<_, _> = var_infos
             .into_iter()
-            .map(|info| RegionDefinition::new(info.origin))
+            .map(|info| RegionDefinition::new(info.universe, info.origin))
             .collect();
 
+        // Compute the max universe used anywhere amongst the regions.
+        let max_universe = definitions
+            .iter()
+            .map(|d| d.universe)
+            .max()
+            .unwrap_or(ty::UniverseIndex::ROOT);
+
         let constraints = Rc::new(outlives_constraints); // freeze constraints
         let constraint_graph = Rc::new(constraints.graph(definitions.len()));
         let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph));
 
-        let mut scc_values = RegionValues::new(elements);
+        let mut scc_values = RegionValues::new(elements, universal_regions.len(), max_universe);
 
-        for (region, location_set) in liveness_constraints.iter_enumerated() {
+        for region in liveness_constraints.rows() {
             let scc = constraint_sccs.scc(region);
-            scc_values.merge_into(scc, location_set);
+            scc_values.merge_liveness(scc, region, &liveness_constraints);
         }
 
+        let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions);
+
         let mut result = Self {
             definitions,
-            elements: elements.clone(),
             liveness_constraints,
             constraints,
             constraint_graph,
             constraint_sccs,
+            scc_universes,
             scc_values,
             type_tests,
             universal_regions,
         };
 
-        result.init_universal_regions();
+        result.init_free_and_bound_regions();
 
         result
     }
 
+    /// Each SCC is the combination of many region variables which
+    /// have been equated. Therefore, we can associate a universe with
+    /// each SCC which is minimum of all the universes of its
+    /// constituent regions -- this is because whatever value the SCC
+    /// takes on must be a value that each of the regions within the
+    /// SCC could have as well. This implies that the SCC must have
+    /// the minimum, or narrowest, universe.
+    fn compute_scc_universes(
+        constraints_scc: &Sccs<RegionVid, ConstraintSccIndex>,
+        definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
+    ) -> IndexVec<ConstraintSccIndex, ty::UniverseIndex> {
+        let num_sccs = constraints_scc.num_sccs();
+        let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs);
+
+        for (region_vid, region_definition) in definitions.iter_enumerated() {
+            let scc = constraints_scc.scc(region_vid);
+            let scc_universe = &mut scc_universes[scc];
+            *scc_universe = ::std::cmp::min(*scc_universe, region_definition.universe);
+        }
+
+        debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes);
+
+        scc_universes
+    }
+
     /// Initializes the region variables for each universally
     /// quantified region (lifetime parameter). The first N variables
     /// always correspond to the regions appearing in the function
@@ -270,7 +303,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// and (b) any universally quantified regions that it outlives,
     /// which in this case is just itself. R1 (`'b`) in contrast also
     /// outlives `'a` and hence contains R0 and R1.
-    fn init_universal_regions(&mut self) {
+    fn init_free_and_bound_regions(&mut self) {
         // Update the names (if any)
         for (external_name, variable) in self.universal_regions.named_universal_regions() {
             debug!(
@@ -281,25 +314,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             self.definitions[variable].external_name = Some(external_name);
         }
 
-        // For each universally quantified region X:
-        let elements = self.elements.clone();
-        let universal_regions = self.universal_regions.clone();
-        for variable in universal_regions.universal_regions() {
-            // These should be free-region variables.
-            assert!(match self.definitions[variable].origin {
-                RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
-                _ => false,
-            });
+        for variable in self.definitions.indices() {
+            match self.definitions[variable].origin {
+                NLLRegionVariableOrigin::FreeRegion => {
+                    // For each free, universally quantified region X:
 
-            self.definitions[variable].is_universal = true;
+                    // Add all nodes in the CFG to liveness constraints
+                    let variable_scc = self.constraint_sccs.scc(variable);
+                    self.liveness_constraints.add_all_points(variable);
+                    self.scc_values.add_all_points(variable_scc);
 
-            // Add all nodes in the CFG to liveness constraints
-            for point_index in elements.all_point_indices() {
-                self.add_live_element(variable, point_index);
-            }
+                    // Add `end(X)` into the set for X.
+                    self.add_element_to_scc_of(variable, variable);
+                }
+
+                NLLRegionVariableOrigin::BoundRegion(ui) => {
+                    // Each placeholder region X outlives its
+                    // associated universe but nothing else.
+                    self.add_element_to_scc_of(variable, ui);
+                }
 
-            // Add `end(X)` into the set for X.
-            self.add_live_element(variable, variable);
+                NLLRegionVariableOrigin::Existential => {
+                    // For existential, regions, nothing to do.
+                }
+            }
         }
     }
 
@@ -330,27 +368,17 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         self.scc_values.region_value_str(scc)
     }
 
-    /// Indicates that the region variable `v` is live at the point `point`.
-    ///
-    /// Returns `true` if this constraint is new and `false` is the
-    /// constraint was already present.
-    pub(super) fn add_live_element(
-        &mut self,
-        v: RegionVid,
-        elem: impl ToElementIndex,
-    ) -> bool {
-        debug!("add_live_element({:?}, {:?})", v, elem);
-
-        // Add to the liveness values for `v`...
-        if self.liveness_constraints.add_element(v, elem) {
-            // ...but also add to the SCC in which `v` appears.
-            let scc = self.constraint_sccs.scc(v);
-            self.scc_values.add_element(scc, elem);
+    /// Returns access to the value of `r` for debugging purposes.
+    crate fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
+        let scc = self.constraint_sccs.scc(r.to_region_vid());
+        self.scc_universes[scc]
+    }
 
-            true
-        } else {
-            false
-        }
+    /// Adds `elem` to the value of the SCC in which `v` appears.
+    fn add_element_to_scc_of(&mut self, v: RegionVid, elem: impl ToElementIndex) {
+        debug!("add_live_element({:?}, {:?})", v, elem);
+        let scc = self.constraint_sccs.scc(v);
+        self.scc_values.add_element(scc, elem);
     }
 
     /// Perform region inference and report errors if we see any
@@ -460,8 +488,32 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             // ...compute the value of `B`...
             self.propagate_constraint_sccs_if_new(scc_b, visited);
 
-            // ...and add elements from `B` into `A`.
-            self.scc_values.add_region(scc_a, scc_b);
+            // ...and add elements from `B` into `A`. One complication
+            // arises because of universes: If `B` contains something
+            // that `A` cannot name, then `A` can only contain `B` if
+            // it outlives static.
+            if self.universe_compatible(scc_b, scc_a) {
+                // `A` can name everything that is in `B`, so just
+                // merge the bits.
+                self.scc_values.add_region(scc_a, scc_b);
+            } else {
+                // Otherwise, the only way for `A` to outlive `B`
+                // is for it to outlive static. This is actually stricter
+                // than necessary: ideally, we'd support bounds like `for<'a: 'b`>`
+                // that might then allow us to approximate `'a` with `'b` and not
+                // `'static`. But it will have to do for now.
+                //
+                // The code here is a bit hacky: we grab the current
+                // value of the SCC in which `'static` appears, but
+                // this value may not be fully computed yet. That's ok
+                // though: it will contain the base liveness values,
+                // which include (a) the static free region element
+                // and (b) all the points in the CFG, so it is "good
+                // enough" to bring it in here for our purposes.
+                let fr_static = self.universal_regions.fr_static;
+                let scc_static = constraint_sccs.scc(fr_static);
+                self.scc_values.add_region(scc_a, scc_static);
+            }
         }
 
         debug!(
@@ -471,6 +523,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         );
     }
 
+    /// True if all the elements in the value of `scc_b` are nameable
+    /// in `scc_a`. Used during constraint propagation, and only once
+    /// the value of `scc_b` has been computed.
+    fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
+        let universe_a = self.scc_universes[scc_a];
+
+        // Quick check: if scc_b's declared universe is a subset of
+        // scc_a's declared univese (typically, both are ROOT), then
+        // it cannot contain any problematic universe elements.
+        if self.scc_universes[scc_b].is_subset_of(universe_a) {
+            return true;
+        }
+
+        // Otherwise, we have to iterate over the universe elements in
+        // B's value, and check whether all of them are nameable
+        // from universe_a
+        self.scc_values
+            .subuniverses_contained_in(scc_b)
+            .all(|u| u.is_subset_of(universe_a))
+    }
+
     /// Once regions have been propagated, this method is used to see
     /// whether the "type tests" produced by typeck were satisfied;
     /// type tests encode type-outlives relationships like `T:
@@ -793,7 +866,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // now). Therefore, the sup-region outlives the sub-region if,
         // for each universal region R1 in the sub-region, there
         // exists some region R2 in the sup-region that outlives R1.
-        let universal_outlives = self.scc_values
+        let universal_outlives = self
+            .scc_values
             .universal_regions_outlived_by(sub_region_scc)
             .all(|r1| {
                 self.scc_values
@@ -813,8 +887,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             return true;
         }
 
-        self.scc_values
-            .contains_points(sup_region_scc, sub_region_scc)
+        self.scc_values.contains_points(sup_region_scc, sub_region_scc)
     }
 
     /// Once regions have been propagated, this method is used to see
@@ -842,24 +915,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
         errors_buffer: &mut Vec<Diagnostic>,
     ) {
-        // The universal regions are always found in a prefix of the
-        // full list.
-        let universal_definitions = self.definitions
-            .iter_enumerated()
-            .take_while(|(_, fr_definition)| fr_definition.is_universal);
-
-        // Go through each of the universal regions `fr` and check that
-        // they did not grow too large, accumulating any requirements
-        // for our caller into the `outlives_requirements` vector.
-        for (fr, _) in universal_definitions {
-            self.check_universal_region(
-                infcx,
-                mir,
-                mir_def_id,
-                fr,
-                &mut propagated_outlives_requirements,
-                errors_buffer,
-            );
+        for (fr, fr_definition) in self.definitions.iter_enumerated() {
+            match fr_definition.origin {
+                NLLRegionVariableOrigin::FreeRegion => {
+                    // Go through each of the universal regions `fr` and check that
+                    // they did not grow too large, accumulating any requirements
+                    // for our caller into the `outlives_requirements` vector.
+                    self.check_universal_region(
+                        infcx,
+                        mir,
+                        mir_def_id,
+                        fr,
+                        &mut propagated_outlives_requirements,
+                        errors_buffer,
+                    );
+                }
+
+                NLLRegionVariableOrigin::BoundRegion(universe) => {
+                    self.check_bound_universal_region(infcx, mir, mir_def_id, fr, universe);
+                }
+
+                NLLRegionVariableOrigin::Existential => {
+                    // nothing to check here
+                }
+            }
         }
     }
 
@@ -884,6 +963,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         let longer_fr_scc = self.constraint_sccs.scc(longer_fr);
 
+        // Because this free region must be in the ROOT universe, we
+        // know it cannot contain any bound universes.
+        assert!(self.scc_universes[longer_fr_scc] == ty::UniverseIndex::ROOT);
+        debug_assert!(
+            self.scc_values
+                .subuniverses_contained_in(longer_fr_scc)
+                .next()
+                .is_none()
+        );
+
         // Find every region `o` such that `fr: o`
         // (because `fr` includes `end(o)`).
         for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) {
@@ -897,8 +986,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 longer_fr, shorter_fr,
             );
 
-            let blame_index = self.blame_constraint(longer_fr, shorter_fr);
-            let blame_span = self.constraints[blame_index].locations.span(mir);
+            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).
@@ -934,19 +1022,83 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             // to report the error. This gives better error messages
             // in some cases.
             self.report_error(
-                mir, infcx, mir_def_id, longer_fr, shorter_fr, blame_span, errors_buffer);
+                mir, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer);
         }
     }
+
+    fn check_bound_universal_region<'gcx>(
+        &self,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+        mir: &Mir<'tcx>,
+        _mir_def_id: DefId,
+        longer_fr: RegionVid,
+        universe: ty::UniverseIndex,
+    ) {
+        debug!("check_bound_universal_region(fr={:?})", longer_fr);
+
+        let longer_fr_scc = self.constraint_sccs.scc(longer_fr);
+
+        // If we have some bound universal region `'a`, then the only
+        // elements it can contain is itself -- we don't know anything
+        // else about it!
+        let error_element = match {
+            self.scc_values
+                .elements_contained_in(longer_fr_scc)
+                .find(|element| match element {
+                    RegionElement::Location(_) => true,
+                    RegionElement::RootUniversalRegion(_) => true,
+                    RegionElement::SubUniversalRegion(ui) => *ui != universe,
+                })
+        } {
+            Some(v) => v,
+            None => return,
+        };
+
+        // Find the region that introduced this `error_element`.
+        let error_region = match error_element {
+            RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l),
+            RegionElement::RootUniversalRegion(r) => r,
+            RegionElement::SubUniversalRegion(error_ui) => self
+                .definitions
+                .iter_enumerated()
+                .filter_map(|(r, definition)| match definition.origin {
+                    NLLRegionVariableOrigin::BoundRegion(ui) if error_ui == ui => Some(r),
+                    _ => None,
+                })
+                .next()
+                .unwrap(),
+        };
+
+        // Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
+        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 --
+        // the AST-based checker uses a more conservative check,
+        // so to even see this error, one must pass in a special
+        // flag.
+        let mut diag = infcx
+            .tcx
+            .sess
+            .struct_span_err(span, &format!("higher-ranked subtype error"));
+        diag.emit();
+    }
 }
 
 impl<'tcx> RegionDefinition<'tcx> {
-    fn new(origin: RegionVariableOrigin) -> Self {
+    fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self {
         // Create a new region definition. Note that, for free
-        // regions, these fields get updated later in
+        // regions, the `external_name` field gets updated later in
         // `init_universal_regions`.
+
+        let origin = match rv_origin {
+            RegionVariableOrigin::NLL(origin) => origin,
+            _ => NLLRegionVariableOrigin::Existential,
+        };
+
         Self {
             origin,
-            is_universal: false,
+            universe,
             external_name: None,
         }
     }
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
index 20b188424f9f3..8db5809e53f5e 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
@@ -9,24 +9,22 @@
 // except according to those terms.
 
 use rustc::mir::{BasicBlock, Location, Mir};
-use rustc::ty::RegionVid;
-use rustc_data_structures::bitvec::{BitVector, SparseBitMatrix};
+use rustc::ty::{self, RegionVid};
+use rustc_data_structures::bitvec::SparseBitMatrix;
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::indexed_vec::IndexVec;
 use std::fmt::Debug;
 use std::rc::Rc;
 
-/// Maps between the various kinds of elements of a region value to
-/// the internal indices that w use.
+/// Maps between a `Location` and a `PointIndex` (and vice versa).
 crate struct RegionValueElements {
     /// For each basic block, how many points are contained within?
     statements_before_block: IndexVec<BasicBlock, usize>,
     num_points: usize,
-    num_universal_regions: usize,
 }
 
 impl RegionValueElements {
-    crate fn new(mir: &Mir<'_>, num_universal_regions: usize) -> Self {
+    crate fn new(mir: &Mir<'_>) -> Self {
         let mut num_points = 0;
         let statements_before_block = mir
             .basic_blocks()
@@ -37,11 +35,6 @@ impl RegionValueElements {
                 v
             })
             .collect();
-
-        debug!(
-            "RegionValueElements(num_universal_regions={:?})",
-            num_universal_regions
-        );
         debug!(
             "RegionValueElements: statements_before_block={:#?}",
             statements_before_block
@@ -50,90 +43,66 @@ impl RegionValueElements {
 
         Self {
             statements_before_block,
-            num_universal_regions,
             num_points,
         }
     }
 
-    /// Total number of element indices that exist.
-    crate fn num_elements(&self) -> usize {
-        self.num_points + self.num_universal_regions
-    }
-
-    /// Converts an element of a region value into a `RegionElementIndex`.
-    crate fn index<T: ToElementIndex>(&self, elem: T) -> RegionElementIndex {
-        elem.to_element_index(self)
-    }
-
-    /// Iterates over the `RegionElementIndex` for all points in the CFG.
-    crate fn all_point_indices<'a>(&'a self) -> impl Iterator<Item = RegionElementIndex> + 'a {
-        (0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions))
-    }
-
-    /// Converts a particular `RegionElementIndex` to the `RegionElement` it represents.
-    crate fn to_element(&self, i: RegionElementIndex) -> RegionElement {
-        debug!("to_element(i={:?})", i);
-
-        if let Some(r) = self.to_universal_region(i) {
-            RegionElement::UniversalRegion(r)
-        } else {
-            let point_index = i.index() - self.num_universal_regions;
-
-            // Find the basic block. We have a vector with the
-            // starting index of the statement in each block. Imagine
-            // we have statement #22, and we have a vector like:
-            //
-            // [0, 10, 20]
-            //
-            // In that case, this represents point_index 2 of
-            // basic block BB2. We know this because BB0 accounts for
-            // 0..10, BB1 accounts for 11..20, and BB2 accounts for
-            // 20...
-            //
-            // To compute this, we could do a binary search, but
-            // because I am lazy we instead iterate through to find
-            // the last point where the "first index" (0, 10, or 20)
-            // was less than the statement index (22). In our case, this will
-            // be (BB2, 20).
-            //
-            // Nit: we could do a binary search here but I'm too lazy.
-            let (block, &first_index) = self
-                .statements_before_block
-                .iter_enumerated()
-                .filter(|(_, first_index)| **first_index <= point_index)
-                .last()
-                .unwrap();
-
-            RegionElement::Location(Location {
-                block,
-                statement_index: point_index - first_index,
-            })
-        }
+    /// Converts a `Location` into a `PointIndex`. O(1).
+    fn point_from_location(&self, location: Location) -> PointIndex {
+        let Location {
+            block,
+            statement_index,
+        } = location;
+        let start_index = self.statements_before_block[block];
+        PointIndex::new(start_index + statement_index)
     }
 
-    /// Converts a particular `RegionElementIndex` to a universal
-    /// region, if that is what it represents. Returns `None`
-    /// otherwise.
-    crate fn to_universal_region(&self, i: RegionElementIndex) -> Option<RegionVid> {
-        if i.index() < self.num_universal_regions {
-            Some(RegionVid::new(i.index()))
-        } else {
-            None
+    /// Converts a `PointIndex` back to a location. O(N) where N is
+    /// the number of blocks; could be faster if we ever cared.
+    crate fn to_location(&self, i: PointIndex) -> Location {
+        let point_index = i.index();
+
+        // Find the basic block. We have a vector with the
+        // starting index of the statement in each block. Imagine
+        // we have statement #22, and we have a vector like:
+        //
+        // [0, 10, 20]
+        //
+        // In that case, this represents point_index 2 of
+        // basic block BB2. We know this because BB0 accounts for
+        // 0..10, BB1 accounts for 11..20, and BB2 accounts for
+        // 20...
+        //
+        // To compute this, we could do a binary search, but
+        // because I am lazy we instead iterate through to find
+        // the last point where the "first index" (0, 10, or 20)
+        // was less than the statement index (22). In our case, this will
+        // be (BB2, 20).
+        //
+        // Nit: we could do a binary search here but I'm too lazy.
+        let (block, &first_index) = self
+            .statements_before_block
+            .iter_enumerated()
+            .filter(|(_, first_index)| **first_index <= point_index)
+            .last()
+            .unwrap();
+
+        Location {
+            block,
+            statement_index: point_index - first_index,
         }
     }
 }
 
-/// A newtype for the integers that represent one of the possible
-/// elements in a region. These are the rows in the `SparseBitMatrix` that
-/// is used to store the values of all regions. They have the following
-/// convention:
-///
-/// - The first N indices represent free regions (where N = universal_regions.len()).
-/// - The remainder represent the points in the CFG (see `point_indices` map).
-///
-/// You can convert a `RegionElementIndex` into a `RegionElement`
-/// using the `to_region_elem` method.
-newtype_index!(RegionElementIndex { DEBUG_FORMAT = "RegionElementIndex({})" });
+/// A single integer representing a `Location` in the MIR control-flow
+/// graph. Constructed efficiently from `RegionValueElements`.
+newtype_index!(PointIndex { DEBUG_FORMAT = "PointIndex({})" });
+
+/// A single integer representing a (non-zero) `UniverseIndex`.
+/// Computed just by subtracting one from `UniverseIndex`; this is
+/// because the `0` value for `UniverseIndex` represents the root
+/// universe, and we don't need/want a bit for that one.
+newtype_index!(PlaceholderIndex { DEBUG_FORMAT = "PlaceholderIndex({})" });
 
 /// An individual element in a region value -- the value of a
 /// particular region variable consists of a set of these elements.
@@ -142,113 +111,173 @@ crate enum RegionElement {
     /// A point in the control-flow graph.
     Location(Location),
 
-    /// An in-scope, universally quantified region (e.g., a lifetime parameter).
-    UniversalRegion(RegionVid),
+    /// A universally quantified region from the root universe (e.g.,
+    /// a lifetime parameter).
+    RootUniversalRegion(RegionVid),
+
+    /// A subuniverse from a subuniverse (e.g., instantiated from a
+    /// `for<'a> fn(&'a u32)` type).
+    SubUniversalRegion(ty::UniverseIndex),
 }
 
-crate trait ToElementIndex: Debug + Copy {
-    fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex;
+/// When we initially compute liveness, we use a bit matrix storing
+/// points for each region-vid.
+crate struct LivenessValues<N: Idx> {
+    elements: Rc<RegionValueElements>,
+    points: SparseBitMatrix<N, PointIndex>,
 }
 
-impl ToElementIndex for Location {
-    fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex {
-        let Location {
-            block,
-            statement_index,
-        } = self;
-        let start_index = elements.statements_before_block[block];
-        RegionElementIndex::new(elements.num_universal_regions + start_index + statement_index)
+impl<N: Idx> LivenessValues<N> {
+    /// Creates a new set of "region values" that tracks causal information.
+    /// Each of the regions in num_region_variables will be initialized with an
+    /// empty set of points and no causal information.
+    crate fn new(elements: &Rc<RegionValueElements>) -> Self {
+        Self {
+            elements: elements.clone(),
+            points: SparseBitMatrix::new(elements.num_points),
+        }
     }
-}
 
-impl ToElementIndex for RegionVid {
-    fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex {
-        assert!(self.index() < elements.num_universal_regions);
-        RegionElementIndex::new(self.index())
+    /// Iterate through each region that has a value in this set.
+    crate fn rows<'a>(&'a self) -> impl Iterator<Item = N> {
+        self.points.rows()
     }
-}
 
-impl ToElementIndex for RegionElementIndex {
-    fn to_element_index(self, _elements: &RegionValueElements) -> RegionElementIndex {
-        self
+    /// Adds the given element to the value for the given region. Returns true if
+    /// the element is newly added (i.e., was not already present).
+    crate fn add_element(&mut self, row: N, location: Location) -> bool {
+        debug!("LivenessValues::add(r={:?}, location={:?})", row, location);
+        let index = self.elements.point_from_location(location);
+        self.points.add(row, index)
+    }
+
+    /// Adds all the control-flow points to the values for `r`.
+    crate fn add_all_points(&mut self, row: N) {
+        self.points.add_all(row);
+    }
+
+    /// True if the region `r` contains the given element.
+    crate fn contains(&self, row: N, location: Location) -> bool {
+        let index = self.elements.point_from_location(location);
+        self.points.contains(row, index)
+    }
+
+    /// Returns a "pretty" string value of the region. Meant for debugging.
+    crate fn region_value_str(&self, r: N) -> String {
+        region_value_str(
+            self.points
+                .row(r)
+                .into_iter()
+                .flat_map(|set| set.iter())
+                .map(|p| self.elements.to_location(p))
+                .map(RegionElement::Location),
+        )
     }
 }
 
-/// Stores the values for a set of regions. These are stored in a
-/// compact `SparseBitMatrix` representation, with one row per region
-/// variable. The columns consist of either universal regions or
-/// points in the CFG.
+/// Stores the full values for a set of regions (in contrast to
+/// `LivenessValues`, which only stores those points in the where a
+/// region is live). The full value for a region may contain points in
+/// the CFG, but also free regions as well as bound universe
+/// placeholders.
+///
+/// Example:
+///
+/// ```text
+/// fn foo(x: &'a u32) -> &'a u32 {
+///    let y: &'0 u32 = x; // let's call this `'0`
+///    y
+/// }
+/// ```
+///
+/// Here, the variable `'0` would contain the free region `'a`,
+/// because (since it is returned) it must live for at least `'a`. But
+/// it would also contain various points from within the function.
 #[derive(Clone)]
 crate struct RegionValues<N: Idx> {
     elements: Rc<RegionValueElements>,
-    matrix: SparseBitMatrix<N, RegionElementIndex>,
+    points: SparseBitMatrix<N, PointIndex>,
+    free_regions: SparseBitMatrix<N, RegionVid>,
+
+    /// Placeholders represent bound regions -- so something like `'a`
+    /// in for<'a> fn(&'a u32)`.
+    placeholders: SparseBitMatrix<N, PlaceholderIndex>,
 }
 
 impl<N: Idx> RegionValues<N> {
     /// Creates a new set of "region values" that tracks causal information.
     /// Each of the regions in num_region_variables will be initialized with an
     /// empty set of points and no causal information.
-    crate fn new(elements: &Rc<RegionValueElements>) -> Self {
+    crate fn new(
+        elements: &Rc<RegionValueElements>,
+        num_universal_regions: usize,
+        max_universe: ty::UniverseIndex,
+    ) -> Self {
+        let num_placeholders = max_universe.as_usize();
         Self {
             elements: elements.clone(),
-            matrix: SparseBitMatrix::new(elements.num_elements()),
+            points: SparseBitMatrix::new(elements.num_points),
+            free_regions: SparseBitMatrix::new(num_universal_regions),
+            placeholders: SparseBitMatrix::new(num_placeholders),
         }
     }
 
     /// Adds the given element to the value for the given region. Returns true if
     /// the element is newly added (i.e., was not already present).
-    crate fn add_element(
-        &mut self,
-        r: N,
-        elem: impl ToElementIndex,
-    ) -> bool {
-        let i = self.elements.index(elem);
+    crate fn add_element(&mut self, r: N, elem: impl ToElementIndex) -> bool {
         debug!("add(r={:?}, elem={:?})", r, elem);
-        self.matrix.add(r, i)
+        elem.add_to_row(self, r)
+    }
+
+    /// Adds all the control-flow points to the values for `r`.
+    crate fn add_all_points(&mut self, r: N) {
+        self.points.add_all(r);
     }
 
     /// Add all elements in `r_from` to `r_to` (because e.g. `r_to:
     /// r_from`).
     crate fn add_region(&mut self, r_to: N, r_from: N) -> bool {
-        self.matrix.merge(r_from, r_to)
+        self.points.merge(r_from, r_to)
+            | self.free_regions.merge(r_from, r_to)
+            | self.placeholders.merge(r_from, r_to)
     }
 
     /// True if the region `r` contains the given element.
     crate fn contains(&self, r: N, elem: impl ToElementIndex) -> bool {
-        let i = self.elements.index(elem);
-        self.matrix.contains(r, i)
+        elem.contained_in_row(self, r)
     }
 
-    /// Iterates through each row and the accompanying bit set.
-    pub fn iter_enumerated<'a>(
-        &'a self
-    ) -> impl Iterator<Item = (N, &'a BitVector)> + 'a {
-        self.matrix.iter_enumerated()
-    }
-
-    /// Merge a row, `from`, originating in another `RegionValues` into the `into` row.
-    pub fn merge_into(&mut self, into: N, from: &BitVector) -> bool {
-        self.matrix.merge_into(into, from)
+    /// `self[to] |= values[from]`, essentially: that is, take all the
+    /// elements for the region `from` from `values` and add them to
+    /// the region `to` in `self`.
+    crate fn merge_liveness<M: Idx>(&mut self, to: N, from: M, values: &LivenessValues<M>) {
+        if let Some(set) = values.points.row(from) {
+            self.points.merge_into(to, set);
+        }
     }
 
     /// True if `sup_region` contains all the CFG points that
     /// `sub_region` contains. Ignores universal regions.
     crate fn contains_points(&self, sup_region: N, sub_region: N) -> bool {
-        // This could be done faster by comparing the bitsets. But I
-        // am lazy.
-        self.element_indices_contained_in(sub_region)
-            .skip_while(|&i| self.elements.to_universal_region(i).is_some())
-            .all(|e| self.contains(sup_region, e))
+        if let Some(sub_row) = self.points.row(sub_region) {
+            if let Some(sup_row) = self.points.row(sup_region) {
+                sup_row.contains_all(sub_row)
+            } else {
+                // sup row is empty, so sub row must be empty
+                sub_row.is_empty()
+            }
+        } else {
+            // sub row is empty, always true
+            true
+        }
     }
 
-    /// Iterate over the value of the region `r`, yielding up element
-    /// indices. You may prefer `universal_regions_outlived_by` or
-    /// `elements_contained_in`.
-    crate fn element_indices_contained_in<'a>(
-        &'a self,
-        r: N,
-    ) -> impl Iterator<Item = RegionElementIndex> + 'a {
-        self.matrix.iter(r).map(move |i| i)
+    /// Returns the locations contained within a given region `r`.
+    crate fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a {
+        self.points
+            .row(r)
+            .into_iter()
+            .flat_map(move |set| set.iter().map(move |p| self.elements.to_location(p)))
     }
 
     /// Returns just the universal regions that are contained in a given region's value.
@@ -256,79 +285,154 @@ impl<N: Idx> RegionValues<N> {
         &'a self,
         r: N,
     ) -> impl Iterator<Item = RegionVid> + 'a {
-        self.element_indices_contained_in(r)
-            .map(move |i| self.elements.to_universal_region(i))
-            .take_while(move |v| v.is_some()) // universal regions are a prefix
-            .map(move |v| v.unwrap())
+        self.free_regions
+            .row(r)
+            .into_iter()
+            .flat_map(|set| set.iter())
     }
 
     /// Returns all the elements contained in a given region's value.
-    crate fn elements_contained_in<'a>(
+    crate fn subuniverses_contained_in<'a>(
         &'a self,
         r: N,
-    ) -> impl Iterator<Item = RegionElement> + 'a {
-        self.element_indices_contained_in(r)
-            .map(move |r| self.elements.to_element(r))
+    ) -> impl Iterator<Item = ty::UniverseIndex> + 'a {
+        self.placeholders
+            .row(r)
+            .into_iter()
+            .flat_map(|set| set.iter())
+            .map(|p| ty::UniverseIndex::from_u32((p.index() + 1) as u32))
+    }
+
+    /// Returns all the elements contained in a given region's value.
+    crate fn elements_contained_in<'a>(&'a self, r: N) -> impl Iterator<Item = RegionElement> + 'a {
+        let points_iter = self.locations_outlived_by(r).map(RegionElement::Location);
+
+        let free_regions_iter = self
+            .universal_regions_outlived_by(r)
+            .map(RegionElement::RootUniversalRegion);
+
+        let subuniverses_iter = self
+            .subuniverses_contained_in(r)
+            .map(RegionElement::SubUniversalRegion);
+
+        points_iter
+            .chain(free_regions_iter)
+            .chain(subuniverses_iter)
     }
 
     /// Returns a "pretty" string value of the region. Meant for debugging.
     crate fn region_value_str(&self, r: N) -> String {
-        let mut result = String::new();
-        result.push_str("{");
-
-        // Set to Some(l1, l2) when we have observed all the locations
-        // from l1..=l2 (inclusive) but not yet printed them. This
-        // gets extended if we then see l3 where l3 is the successor
-        // to l2.
-        let mut open_location: Option<(Location, Location)> = None;
-
-        let mut sep = "";
-        let mut push_sep = |s: &mut String| {
-            s.push_str(sep);
-            sep = ", ";
-        };
-
-        for element in self.elements_contained_in(r) {
-            match element {
-                RegionElement::Location(l) => {
-                    if let Some((location1, location2)) = open_location {
-                        if location2.block == l.block
-                            && location2.statement_index == l.statement_index - 1
-                        {
-                            open_location = Some((location1, l));
-                            continue;
-                        }
-
-                        push_sep(&mut result);
-                        Self::push_location_range(&mut result, location1, location2);
+        region_value_str(self.elements_contained_in(r))
+    }
+}
+
+crate trait ToElementIndex: Debug + Copy {
+    fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool;
+
+    fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool;
+}
+
+impl ToElementIndex for Location {
+    fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
+        let index = values.elements.point_from_location(self);
+        values.points.add(row, index)
+    }
+
+    fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
+        let index = values.elements.point_from_location(self);
+        values.points.contains(row, index)
+    }
+}
+
+impl ToElementIndex for RegionVid {
+    fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
+        values.free_regions.add(row, self)
+    }
+
+    fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
+        values.free_regions.contains(row, self)
+    }
+}
+
+impl ToElementIndex for ty::UniverseIndex {
+    fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
+        let index = PlaceholderIndex::new(self.as_usize() - 1);
+        values.placeholders.add(row, index)
+    }
+
+    fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
+        let index = PlaceholderIndex::new(self.as_usize() - 1);
+        values.placeholders.contains(row, index)
+    }
+}
+
+fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String {
+    let mut result = String::new();
+    result.push_str("{");
+
+    // Set to Some(l1, l2) when we have observed all the locations
+    // from l1..=l2 (inclusive) but not yet printed them. This
+    // gets extended if we then see l3 where l3 is the successor
+    // to l2.
+    let mut open_location: Option<(Location, Location)> = None;
+
+    let mut sep = "";
+    let mut push_sep = |s: &mut String| {
+        s.push_str(sep);
+        sep = ", ";
+    };
+
+    for element in elements {
+        match element {
+            RegionElement::Location(l) => {
+                if let Some((location1, location2)) = open_location {
+                    if location2.block == l.block
+                        && location2.statement_index == l.statement_index - 1
+                    {
+                        open_location = Some((location1, l));
+                        continue;
                     }
 
-                    open_location = Some((l, l));
+                    push_sep(&mut result);
+                    push_location_range(&mut result, location1, location2);
                 }
 
-                RegionElement::UniversalRegion(fr) => {
-                    if let Some((location1, location2)) = open_location {
-                        push_sep(&mut result);
-                        Self::push_location_range(&mut result, location1, location2);
-                        open_location = None;
-                    }
+                open_location = Some((l, l));
+            }
 
+            RegionElement::RootUniversalRegion(fr) => {
+                if let Some((location1, location2)) = open_location {
                     push_sep(&mut result);
-                    result.push_str(&format!("{:?}", fr));
+                    push_location_range(&mut result, location1, location2);
+                    open_location = None;
                 }
+
+                push_sep(&mut result);
+                result.push_str(&format!("{:?}", fr));
             }
-        }
 
-        if let Some((location1, location2)) = open_location {
-            push_sep(&mut result);
-            Self::push_location_range(&mut result, location1, location2);
-        }
+            RegionElement::SubUniversalRegion(ur) => {
+                if let Some((location1, location2)) = open_location {
+                    push_sep(&mut result);
+                    push_location_range(&mut result, location1, location2);
+                    open_location = None;
+                }
 
-        result.push_str("}");
+                push_sep(&mut result);
+                result.push_str(&format!("{:?}", ur));
+            }
+        }
+    }
 
-        result
+    if let Some((location1, location2)) = open_location {
+        push_sep(&mut result);
+        push_location_range(&mut result, location1, location2);
     }
 
+    result.push_str("}");
+
+    return result;
+
     fn push_location_range(str: &mut String, location1: Location, location2: Location) {
         if location1 == location2 {
             str.push_str(&format!("{:?}", location1));
diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs
index 7edee42b78ff7..e1bd8530629d9 100644
--- a/src/librustc_mir/borrow_check/nll/renumber.rs
+++ b/src/librustc_mir/borrow_check/nll/renumber.rs
@@ -28,7 +28,6 @@ pub fn renumber_mir<'tcx>(infcx: &InferCtxt<'_, '_, 'tcx>, mir: &mut Mir<'tcx>)
 /// variables.
 pub fn renumber_regions<'tcx, T>(
     infcx: &InferCtxt<'_, '_, 'tcx>,
-    ty_context: TyContext,
     value: &T,
 ) -> T
 where
@@ -39,7 +38,7 @@ where
     infcx
         .tcx
         .fold_regions(value, &mut false, |_region, _depth| {
-            let origin = NLLRegionVariableOrigin::Inferred(ty_context);
+            let origin = NLLRegionVariableOrigin::Existential;
             infcx.next_nll_region_var(origin)
         })
 }
@@ -49,11 +48,11 @@ struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
 }
 
 impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
-    fn renumber_regions<T>(&mut self, ty_context: TyContext, value: &T) -> T
+    fn renumber_regions<T>(&mut self, value: &T) -> T
     where
         T: TypeFoldable<'tcx>,
     {
-        renumber_regions(self.infcx, ty_context, value)
+        renumber_regions(self.infcx, value)
     }
 }
 
@@ -61,7 +60,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
     fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
         debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context);
 
-        *ty = self.renumber_regions(ty_context, ty);
+        *ty = self.renumber_regions(ty);
 
         debug!("visit_ty: ty={:?}", ty);
     }
@@ -69,8 +68,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
     fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
         debug!("visit_substs(substs={:?}, location={:?})", substs, location);
 
-        let ty_context = TyContext::Location(location);
-        *substs = self.renumber_regions(ty_context, &{ *substs });
+        *substs = self.renumber_regions(&{ *substs });
 
         debug!("visit_substs: substs={:?}", substs);
     }
@@ -79,15 +77,13 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
         debug!("visit_region(region={:?}, location={:?})", region, location);
 
         let old_region = *region;
-        let ty_context = TyContext::Location(location);
-        *region = self.renumber_regions(ty_context, &old_region);
+        *region = self.renumber_regions(&old_region);
 
         debug!("visit_region: region={:?}", region);
     }
 
-    fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, location: Location) {
-        let ty_context = TyContext::Location(location);
-        *constant = self.renumber_regions(ty_context, &*constant);
+    fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _location: Location) {
+        *constant = self.renumber_regions(&*constant);
     }
 
     fn visit_generator_substs(&mut self,
@@ -99,8 +95,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
             location,
         );
 
-        let ty_context = TyContext::Location(location);
-        *substs = self.renumber_regions(ty_context, substs);
+        *substs = self.renumber_regions(substs);
 
         debug!("visit_generator_substs: substs={:?}", substs);
     }
@@ -112,8 +107,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
             location
         );
 
-        let ty_context = TyContext::Location(location);
-        *substs = self.renumber_regions(ty_context, substs);
+        *substs = self.renumber_regions(substs);
 
         debug!("visit_closure_substs: substs={:?}", substs);
     }
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 770a0614811dc..3d831bce5ce17 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,7 +21,6 @@ use borrow_check::nll::renumber;
 use borrow_check::nll::universal_regions::UniversalRegions;
 use rustc::hir::def_id::DefId;
 use rustc::infer::InferOk;
-use rustc::mir::visit::TyContext;
 use rustc::mir::*;
 use rustc::traits::query::type_op::custom::CustomTypeOp;
 use rustc::traits::{ObligationCause, PredicateObligations};
@@ -117,7 +116,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                             let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs);
                             let anon_defn_ty = renumber::renumber_regions(
                                 infcx,
-                                TyContext::Location(Location::START),
                                 &anon_defn_ty,
                             );
                             debug!(
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 b6c8ffcf88d65..b67de34593f80 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -15,8 +15,8 @@ use borrow_check::borrow_set::BorrowSet;
 use borrow_check::location::LocationTable;
 use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
 use borrow_check::nll::facts::AllFacts;
+use borrow_check::nll::region_infer::values::{RegionValueElements, LivenessValues};
 use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
-use borrow_check::nll::region_infer::values::{RegionValues, RegionValueElements};
 use borrow_check::nll::universal_regions::UniversalRegions;
 use borrow_check::nll::ToRegionVid;
 use borrow_check::nll::LocalWithRegion;
@@ -35,7 +35,7 @@ use rustc::mir::*;
 use rustc::traits::query::type_op;
 use rustc::traits::query::{Fallible, NoSolution};
 use rustc::ty::fold::TypeFoldable;
-use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants, RegionVid};
+use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TypeVariants};
 use std::fmt;
 use std::rc::Rc;
 use syntax_pos::{Span, DUMMY_SP};
@@ -73,6 +73,7 @@ macro_rules! span_mirbug_and_err {
 mod constraint_conversion;
 mod input_output;
 mod liveness;
+mod relate_tys;
 
 /// Type checks the given `mir` in the context of the inference
 /// context `infcx`. Returns any region constraints that have yet to
@@ -121,7 +122,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
 ) -> MirTypeckRegionConstraints<'tcx> {
     let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
     let mut constraints = MirTypeckRegionConstraints {
-        liveness_constraints: RegionValues::new(elements),
+        liveness_constraints: LivenessValues::new(elements),
         outlives_constraints: ConstraintSet::default(),
         type_tests: Vec::default(),
     };
@@ -638,7 +639,7 @@ 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.
-    crate liveness_constraints: RegionValues<RegionVid>,
+    crate liveness_constraints: LivenessValues<RegionVid>,
 
     crate outlives_constraints: ConstraintSet,
 
@@ -796,16 +797,38 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
     }
 
     fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>, locations: Locations) -> Fallible<()> {
-        let param_env = self.param_env;
-        self.fully_perform_op(
+        relate_tys::sub_types(
+            self.infcx,
+            sub,
+            sup,
             locations,
-            param_env.and(type_op::subtype::Subtype::new(sub, sup)),
+            self.borrowck_context.as_mut().map(|x| &mut **x),
         )
     }
 
     fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> Fallible<()> {
-        let param_env = self.param_env;
-        self.fully_perform_op(locations, param_env.and(type_op::eq::Eq::new(b, a)))
+        relate_tys::eq_types(
+            self.infcx,
+            a,
+            b,
+            locations,
+            self.borrowck_context.as_mut().map(|x| &mut **x),
+        )
+    }
+
+    fn eq_canonical_type_and_type(
+        &mut self,
+        a: CanonicalTy<'tcx>,
+        b: Ty<'tcx>,
+        locations: Locations,
+    ) -> Fallible<()> {
+        relate_tys::eq_canonical_type_and_type(
+            self.infcx,
+            a,
+            b,
+            locations,
+            self.borrowck_context.as_mut().map(|x| &mut **x),
+        )
     }
 
     fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
@@ -877,20 +900,14 @@ 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
-                    .instantiate_canonical_with_fresh_inference_vars(stmt.source_info.span, c_ty);
-                debug!(
-                    "check_stmt: user_assert_ty ty={:?} local_ty={:?}",
-                    ty, local_ty
-                );
-                if let Err(terr) = self.eq_types(ty, local_ty, Locations::All) {
+            StatementKind::UserAssertTy(c_ty, local) => {
+                let local_ty = mir.local_decls()[local].ty;
+                if let Err(terr) = self.eq_canonical_type_and_type(c_ty, local_ty, Locations::All) {
                     span_mirbug!(
                         self,
                         stmt,
                         "bad type assert ({:?} = {:?}): {:?}",
-                        ty,
+                        c_ty,
                         local_ty,
                         terr
                     );
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
new file mode 100644
index 0000000000000..ac9bf65b61b0f
--- /dev/null
+++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs
@@ -0,0 +1,512 @@
+// 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::nll::constraints::OutlivesConstraint;
+use borrow_check::nll::type_check::{BorrowCheckContext, Locations};
+use borrow_check::nll::universal_regions::UniversalRegions;
+use borrow_check::nll::ToRegionVid;
+use rustc::infer::canonical::{Canonical, CanonicalVarInfos};
+use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
+use rustc::traits::query::Fallible;
+use rustc::ty::fold::{TypeFoldable, TypeVisitor};
+use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation};
+use rustc::ty::subst::Kind;
+use rustc::ty::{self, CanonicalTy, CanonicalVar, RegionVid, Ty, TyCtxt};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use std::mem;
+
+pub(super) fn sub_types<'tcx>(
+    infcx: &InferCtxt<'_, '_, 'tcx>,
+    a: Ty<'tcx>,
+    b: Ty<'tcx>,
+    locations: Locations,
+    borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
+) -> Fallible<()> {
+    debug!("sub_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
+    TypeRelating::new(
+        infcx,
+        ty::Variance::Covariant,
+        locations,
+        borrowck_context,
+        ty::Slice::empty(),
+    ).relate(&a, &b)?;
+    Ok(())
+}
+
+pub(super) fn eq_types<'tcx>(
+    infcx: &InferCtxt<'_, '_, 'tcx>,
+    a: Ty<'tcx>,
+    b: Ty<'tcx>,
+    locations: Locations,
+    borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
+) -> Fallible<()> {
+    debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
+    TypeRelating::new(
+        infcx,
+        ty::Variance::Invariant,
+        locations,
+        borrowck_context,
+        ty::Slice::empty(),
+    ).relate(&a, &b)?;
+    Ok(())
+}
+
+pub(super) fn eq_canonical_type_and_type<'tcx>(
+    infcx: &InferCtxt<'_, '_, 'tcx>,
+    a: CanonicalTy<'tcx>,
+    b: Ty<'tcx>,
+    locations: Locations,
+    borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
+) -> Fallible<()> {
+    debug!(
+        "eq_canonical_type_and_type(a={:?}, b={:?}, locations={:?})",
+        a, b, locations
+    );
+    let Canonical {
+        variables: a_variables,
+        value: a_value,
+    } = a;
+    TypeRelating::new(
+        infcx,
+        ty::Variance::Invariant,
+        locations,
+        borrowck_context,
+        a_variables,
+    ).relate(&a_value, &b)?;
+    Ok(())
+}
+
+struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+
+    /// How are we relating `a` and `b`?
+    ///
+    /// - covariant means `a <: b`
+    /// - contravariant means `b <: a`
+    /// - invariant means `a == b
+    /// - bivariant means that it doesn't matter
+    ambient_variance: ty::Variance,
+
+    /// When we pass through a set of binders (e.g., when looking into
+    /// a `fn` type), we push a new bound region scope onto here.  This
+    /// will contain the instantiated region for each region in those
+    /// binders. When we then encounter a `ReLateBound(d, br)`, we can
+    /// use the debruijn index `d` to find the right scope, and then
+    /// bound region name `br` to find the specific instantiation from
+    /// within that scope. See `replace_bound_region`.
+    ///
+    /// This field stores the instantiations for late-bound regions in
+    /// the `a` type.
+    a_scopes: Vec<BoundRegionScope>,
+
+    /// Same as `a_scopes`, but for the `b` type.
+    b_scopes: Vec<BoundRegionScope>,
+
+    /// Where (and why) is this relation taking place?
+    locations: Locations,
+
+    /// 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>>,
+
+    /// As we execute, the type on the LHS *may* come from a canonical
+    /// source. In that case, we will sometimes find a constraint like
+    /// `?0 = B`, where `B` is a type from the RHS. The first time we
+    /// find that, we simply record `B` (and the list of scopes that
+    /// tells us how to *interpret* `B`). The next time we encounter
+    /// `?0`, then, we can read this value out and use it.
+    ///
+    /// One problem: these variables may be in some other universe,
+    /// how can we enforce that? I guess I could add some kind of
+    /// "minimum universe constraint" that we can feed to the NLL checker.
+    /// --> also, we know this doesn't happen
+    canonical_var_values: IndexVec<CanonicalVar, Option<ScopesAndKind<'tcx>>>,
+}
+
+#[derive(Clone, Debug)]
+struct ScopesAndKind<'tcx> {
+    scopes: Vec<BoundRegionScope>,
+    kind: Kind<'tcx>,
+}
+
+#[derive(Clone, Debug, Default)]
+struct BoundRegionScope {
+    map: FxHashMap<ty::BoundRegion, RegionVid>,
+}
+
+#[derive(Copy, Clone)]
+struct UniversallyQuantified(bool);
+
+impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
+    fn new(
+        infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+        ambient_variance: ty::Variance,
+        locations: Locations,
+        borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
+        canonical_var_infos: CanonicalVarInfos<'tcx>,
+    ) -> Self {
+        let canonical_var_values = IndexVec::from_elem_n(None, canonical_var_infos.len());
+        Self {
+            infcx,
+            ambient_variance,
+            borrowck_context,
+            locations,
+            canonical_var_values,
+            a_scopes: vec![],
+            b_scopes: vec![],
+        }
+    }
+
+    fn ambient_covariance(&self) -> bool {
+        match self.ambient_variance {
+            ty::Variance::Covariant | ty::Variance::Invariant => true,
+            ty::Variance::Contravariant | ty::Variance::Bivariant => false,
+        }
+    }
+
+    fn ambient_contravariance(&self) -> bool {
+        match self.ambient_variance {
+            ty::Variance::Contravariant | ty::Variance::Invariant => true,
+            ty::Variance::Covariant | ty::Variance::Bivariant => false,
+        }
+    }
+
+    fn create_scope(
+        &mut self,
+        value: &ty::Binder<impl TypeFoldable<'tcx>>,
+        universally_quantified: UniversallyQuantified,
+    ) -> BoundRegionScope {
+        let mut scope = BoundRegionScope::default();
+        value.skip_binder().visit_with(&mut ScopeInstantiator {
+            infcx: self.infcx,
+            target_index: ty::INNERMOST,
+            universally_quantified,
+            bound_region_scope: &mut scope,
+        });
+        scope
+    }
+
+    fn replace_bound_region(
+        &self,
+        universal_regions: &UniversalRegions<'tcx>,
+        r: ty::Region<'tcx>,
+        scopes: &[BoundRegionScope],
+    ) -> RegionVid {
+        match r {
+            ty::ReLateBound(debruijn, br) => {
+                // The debruijn index is a "reverse index" into the
+                // scopes listing. So when we have INNERMOST (0), we
+                // want the *last* scope pushed, and so forth.
+                let debruijn_index = debruijn.index() - ty::INNERMOST.index();
+                let scope = &scopes[scopes.len() - debruijn_index - 1];
+
+                // Find this bound region in that scope to map to a
+                // particular region.
+                scope.map[br]
+            }
+
+            ty::ReVar(v) => *v,
+
+            _ => universal_regions.to_region_vid(r),
+        }
+    }
+
+    fn push_outlives(&mut self, sup: RegionVid, sub: RegionVid) {
+        debug!("push_outlives({:?}: {:?})", sup, sub);
+
+        if let Some(borrowck_context) = &mut self.borrowck_context {
+            borrowck_context
+                .constraints
+                .outlives_constraints
+                .push(OutlivesConstraint {
+                    sup,
+                    sub,
+                    locations: self.locations,
+                });
+
+            // FIXME all facts!
+        }
+    }
+
+    fn equate_var(
+        &mut self,
+        var: CanonicalVar,
+        b_kind: Kind<'tcx>,
+    ) -> RelateResult<'tcx, Kind<'tcx>> {
+        debug!("equate_var(var={:?}, b_kind={:?})", var, b_kind);
+
+        // We only encounter canonical variables when equating.
+        assert_eq!(self.ambient_variance, ty::Variance::Invariant);
+
+        // The canonical variable already had a value. Equate that
+        // value with `b`.
+        let old_value = self.canonical_var_values[var].clone();
+        if let Some(ScopesAndKind { scopes, kind }) = old_value {
+            debug!("equate_var: installing kind={:?} scopes={:?}", kind, scopes);
+            let old_a_scopes = mem::replace(&mut self.a_scopes, scopes);
+            let result = self.relate(&kind, &b_kind);
+            self.a_scopes = old_a_scopes;
+            debug!("equate_var: complete, result = {:?}", result);
+            return result;
+        }
+
+        // Not yet. Capture the value from the RHS and carry on.
+        self.canonical_var_values[var] = Some(ScopesAndKind {
+            scopes: self.b_scopes.clone(),
+            kind: b_kind,
+        });
+        debug!(
+            "equate_var: capturing value {:?}",
+            self.canonical_var_values[var]
+        );
+
+        // FIXME -- technically, we should add some sort of
+        // assertion that this value can be named in the universe
+        // of the canonical variable. But in practice these
+        // canonical variables only arise presently in cases where
+        // they are in the root universe and the main typeck has
+        // ensured there are no universe errors. So we just kind
+        // of over look this right now.
+        Ok(b_kind)
+    }
+}
+
+impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
+    for TypeRelating<'cx, 'bccx, 'gcx, 'tcx>
+{
+    fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
+        self.infcx.tcx
+    }
+
+    fn tag(&self) -> &'static str {
+        "nll::subtype"
+    }
+
+    fn a_is_expected(&self) -> bool {
+        true
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        variance: ty::Variance,
+        a: &T,
+        b: &T,
+    ) -> RelateResult<'tcx, T> {
+        debug!(
+            "relate_with_variance(variance={:?}, a={:?}, b={:?})",
+            variance, a, b
+        );
+
+        let old_ambient_variance = self.ambient_variance;
+        self.ambient_variance = self.ambient_variance.xform(variance);
+
+        debug!(
+            "relate_with_variance: ambient_variance = {:?}",
+            self.ambient_variance
+        );
+
+        let r = self.relate(a, b)?;
+
+        self.ambient_variance = old_ambient_variance;
+
+        debug!("relate_with_variance: r={:?}", r);
+
+        Ok(r)
+    }
+
+    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        // Watch out for the case that we are matching a `?T` against the
+        // right-hand side.
+        if let ty::TyInfer(ty::CanonicalTy(var)) = a.sty {
+            self.equate_var(var, b.into())?;
+            Ok(a)
+        } else {
+            debug!(
+                "tys(a={:?}, b={:?}, variance={:?})",
+                a, b, self.ambient_variance
+            );
+
+            relate::super_relate_tys(self, a, b)
+        }
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        if let Some(&mut BorrowCheckContext {
+            universal_regions, ..
+        }) = self.borrowck_context
+        {
+            if let ty::ReCanonical(var) = a {
+                self.equate_var(*var, b.into())?;
+                return Ok(a);
+            }
+
+            debug!(
+                "regions(a={:?}, b={:?}, variance={:?})",
+                a, b, self.ambient_variance
+            );
+
+            let v_a = self.replace_bound_region(universal_regions, a, &self.a_scopes);
+            let v_b = self.replace_bound_region(universal_regions, b, &self.b_scopes);
+
+            debug!("regions: v_a = {:?}", v_a);
+            debug!("regions: v_b = {:?}", v_b);
+
+            if self.ambient_covariance() {
+                // Covariance: a <= b. Hence, `b: a`.
+                self.push_outlives(v_b, v_a);
+            }
+
+            if self.ambient_contravariance() {
+                // Contravariant: b <= a. Hence, `a: b`.
+                self.push_outlives(v_a, v_b);
+            }
+        }
+
+        Ok(a)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: &ty::Binder<T>,
+        b: &ty::Binder<T>,
+    ) -> RelateResult<'tcx, ty::Binder<T>>
+    where
+        T: Relate<'tcx>,
+    {
+        // We want that
+        //
+        // ```
+        // for<'a> fn(&'a u32) -> &'a u32 <:
+        //   fn(&'b u32) -> &'b u32
+        // ```
+        //
+        // but not
+        //
+        // ```
+        // fn(&'a u32) -> &'a u32 <:
+        //   for<'b> fn(&'b u32) -> &'b u32
+        // ```
+        //
+        // We therefore proceed as follows:
+        //
+        // - Instantiate binders on `b` universally, yielding a universe U1.
+        // - Instantiate binders on `a` existentially in U1.
+
+        debug!(
+            "binders({:?}: {:?}, ambient_variance={:?})",
+            a, b, self.ambient_variance
+        );
+
+        if self.ambient_covariance() {
+            // Covariance, so we want `for<..> A <: for<..> B` --
+            // therefore we compare any instantiation of A (i.e., A
+            // instantiated with existentials) against every
+            // instantiation of B (i.e., B instantiated with
+            // universals).
+
+            let b_scope = self.create_scope(b, UniversallyQuantified(true));
+            let a_scope = self.create_scope(a, UniversallyQuantified(false));
+
+            debug!("binders: a_scope = {:?} (existential)", a_scope);
+            debug!("binders: b_scope = {:?} (universal)", b_scope);
+
+            self.b_scopes.push(b_scope);
+            self.a_scopes.push(a_scope);
+
+            // FIXME -- to be fully correct, we would set the ambient
+            // variance to Covariant here. As is, we will sometimes
+            // propagate down an ambient variance of Equal -- this in
+            // turn causes us to report errors in some cases where
+            // types perhaps *ought* to be equal. See the
+            // `hr-fn-aau-eq-abu.rs` test for an example. Fixing this
+            // though is a bit nontrivial: in particular, it would
+            // require a more involved handling of canonical
+            // variables, since we would no longer be able to rely on
+            // having an `==` relationship for canonical variables.
+
+            self.relate(a.skip_binder(), b.skip_binder())?;
+
+            self.b_scopes.pop().unwrap();
+            self.a_scopes.pop().unwrap();
+        }
+
+        if self.ambient_contravariance() {
+            // Contravariance, so we want `for<..> A :> for<..> B`
+            // -- therefore we compare every instantiation of A (i.e.,
+            // A instantiated with universals) against any
+            // instantiation of B (i.e., B instantiated with
+            // existentials). Opposite of above.
+
+            let a_scope = self.create_scope(a, UniversallyQuantified(true));
+            let b_scope = self.create_scope(b, UniversallyQuantified(false));
+
+            debug!("binders: a_scope = {:?} (universal)", a_scope);
+            debug!("binders: b_scope = {:?} (existential)", b_scope);
+
+            self.a_scopes.push(a_scope);
+            self.b_scopes.push(b_scope);
+
+            self.relate(a.skip_binder(), b.skip_binder())?;
+
+            self.b_scopes.pop().unwrap();
+            self.a_scopes.pop().unwrap();
+        }
+
+        Ok(a.clone())
+    }
+}
+
+struct ScopeInstantiator<'cx, 'gcx: 'cx + 'tcx, 'tcx: 'cx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    // The debruijn index of the scope we are instantiating.
+    target_index: ty::DebruijnIndex,
+    universally_quantified: UniversallyQuantified,
+    bound_region_scope: &'cx mut BoundRegionScope,
+}
+
+impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> {
+    fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> bool {
+        self.target_index.shift_in(1);
+        t.super_visit_with(self);
+        self.target_index.shift_out(1);
+
+        false
+    }
+
+    fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
+        let ScopeInstantiator {
+            infcx,
+            universally_quantified,
+            ..
+        } = *self;
+
+        match r {
+            ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => {
+                self.bound_region_scope.map.entry(*br).or_insert_with(|| {
+                    let origin = if universally_quantified.0 {
+                        NLLRegionVariableOrigin::BoundRegion(infcx.create_subuniverse())
+                    } else {
+                        NLLRegionVariableOrigin::Existential
+                    };
+                    infcx.next_nll_region_var(origin).to_region_vid()
+                });
+            }
+
+            _ => {}
+        }
+
+        false
+    }
+}
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index f1591535fa1aa..5de316a664033 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -487,7 +487,7 @@ enum TestKind<'tcx> {
     // test the branches of enum
     Switch {
         adt_def: &'tcx ty::AdtDef,
-        variants: BitVector,
+        variants: BitVector<usize>,
     },
 
     // test the branches of enum
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index e2b460f69fd74..3ff209c872fac 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -149,7 +149,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn add_variants_to_switch<'pat>(&mut self,
                                         test_place: &Place<'tcx>,
                                         candidate: &Candidate<'pat, 'tcx>,
-                                        variants: &mut BitVector)
+                                        variants: &mut BitVector<usize>)
                                         -> bool
     {
         let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) {
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index d58affbae75ed..382248c2d15dc 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -27,6 +27,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
 #![feature(core_intrinsics)]
 #![feature(decl_macro)]
 #![feature(fs_read_write)]
+#![feature(in_band_lifetimes)]
 #![feature(macro_vis_matcher)]
 #![feature(exhaustive_patterns)]
 #![feature(range_contains)]
@@ -36,6 +37,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
 #![feature(specialization)]
 #![feature(try_trait)]
 #![feature(unicode_internals)]
+#![feature(step_trait)]
 
 #![recursion_limit="256"]
 
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index 2f983e25acd74..5f05783b15cce 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -231,7 +231,7 @@ pub struct InliningMap<'tcx> {
 
     // Contains one bit per mono item in the `targets` field. That bit
     // is true if that mono item needs to be inlined into every CGU.
-    inlines: BitVector,
+    inlines: BitVector<usize>,
 }
 
 impl<'tcx> InliningMap<'tcx> {
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 97467e003850a..6a9258fe2c918 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -292,8 +292,10 @@ fn make_generator_state_argument_indirect<'a, 'tcx>(
     DerefArgVisitor.visit_mir(mir);
 }
 
-fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>,
-                            mir: &mut Mir<'tcx>) -> Local {
+fn replace_result_variable<'tcx>(
+    ret_ty: Ty<'tcx>,
+    mir: &mut Mir<'tcx>,
+) -> Local {
     let source_info = source_info(mir);
     let new_ret = LocalDecl {
         mutability: Mutability::Mut,
@@ -306,7 +308,7 @@ fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>,
     };
     let new_ret_local = Local::new(mir.local_decls.len());
     mir.local_decls.push(new_ret);
-    mir.local_decls.swap(0, new_ret_local.index());
+    mir.local_decls.swap(RETURN_PLACE, new_ret_local);
 
     RenameLocalVisitor {
         from: RETURN_PLACE,
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 4e1129ea7e945..8a12a604ef202 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -116,7 +116,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     param_env: ty::ParamEnv<'tcx>,
     local_qualif: IndexVec<Local, Option<Qualif>>,
     qualif: Qualif,
-    const_fn_arg_vars: BitVector,
+    const_fn_arg_vars: BitVector<Local>,
     temp_promotion_state: IndexVec<Local, TempState>,
     promotion_candidates: Vec<Candidate>
 }
@@ -344,7 +344,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                         // Make sure there are no extra unassigned variables.
                         self.qualif = Qualif::NOT_CONST;
                         for index in mir.vars_iter() {
-                            if !self.const_fn_arg_vars.contains(index.index()) {
+                            if !self.const_fn_arg_vars.contains(index) {
                                 debug!("unassigned variable {:?}", index);
                                 self.assign(&Place::Local(index), Location {
                                     block: bb,
@@ -1021,7 +1021,7 @@ This does not pose a problem by itself because they can't be accessed directly."
         // Check the allowed const fn argument forms.
         if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
             if self.mir.local_kind(index) == LocalKind::Var &&
-               self.const_fn_arg_vars.insert(index.index()) &&
+               self.const_fn_arg_vars.insert(index) &&
                !self.tcx.sess.features_untracked().const_let {
 
                 // Direct use of an argument is permitted.
diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs
index 680b60b972841..a7ef93eaec6b9 100644
--- a/src/librustc_mir/transform/remove_noop_landing_pads.rs
+++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs
@@ -11,7 +11,6 @@
 use rustc::ty::TyCtxt;
 use rustc::mir::*;
 use rustc_data_structures::bitvec::BitVector;
-use rustc_data_structures::indexed_vec::Idx;
 use transform::{MirPass, MirSource};
 use util::patch::MirPatch;
 
@@ -42,9 +41,12 @@ impl MirPass for RemoveNoopLandingPads {
 }
 
 impl RemoveNoopLandingPads {
-    fn is_nop_landing_pad(&self, bb: BasicBlock, mir: &Mir, nop_landing_pads: &BitVector)
-                          -> bool
-    {
+    fn is_nop_landing_pad(
+        &self,
+        bb: BasicBlock,
+        mir: &Mir,
+        nop_landing_pads: &BitVector<BasicBlock>,
+    ) -> bool {
         for stmt in &mir[bb].statements {
             match stmt.kind {
                 StatementKind::ReadForMatch(_) |
@@ -79,8 +81,8 @@ impl RemoveNoopLandingPads {
             TerminatorKind::SwitchInt { .. } |
             TerminatorKind::FalseEdges { .. } |
             TerminatorKind::FalseUnwind { .. } => {
-                terminator.successors().all(|succ| {
-                    nop_landing_pads.contains(succ.index())
+                terminator.successors().all(|&succ| {
+                    nop_landing_pads.contains(succ)
                 })
             },
             TerminatorKind::GeneratorDrop |
@@ -117,7 +119,7 @@ impl RemoveNoopLandingPads {
         for bb in postorder {
             debug!("  processing {:?}", bb);
             for target in mir[bb].terminator_mut().successors_mut() {
-                if *target != resume_block && nop_landing_pads.contains(target.index()) {
+                if *target != resume_block && nop_landing_pads.contains(*target) {
                     debug!("    folding noop jump to {:?} to resume block", target);
                     *target = resume_block;
                     jumps_folded += 1;
@@ -138,7 +140,7 @@ impl RemoveNoopLandingPads {
 
             let is_nop_landing_pad = self.is_nop_landing_pad(bb, mir, &nop_landing_pads);
             if is_nop_landing_pad {
-                nop_landing_pads.insert(bb.index());
+                nop_landing_pads.insert(bb);
             }
             debug!("    is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad);
         }
diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs
index 7e23a5cb1a895..6b8d5a1489388 100644
--- a/src/librustc_mir/transform/simplify.rs
+++ b/src/librustc_mir/transform/simplify.rs
@@ -288,15 +288,15 @@ impl MirPass for SimplifyLocals {
         let mut marker = DeclMarker { locals: BitVector::new(mir.local_decls.len()) };
         marker.visit_mir(mir);
         // Return pointer and arguments are always live
-        marker.locals.insert(RETURN_PLACE.index());
+        marker.locals.insert(RETURN_PLACE);
         for arg in mir.args_iter() {
-            marker.locals.insert(arg.index());
+            marker.locals.insert(arg);
         }
 
         // We may need to keep dead user variables live for debuginfo.
         if tcx.sess.opts.debuginfo == FullDebugInfo {
             for local in mir.vars_iter() {
-                marker.locals.insert(local.index());
+                marker.locals.insert(local);
             }
         }
 
@@ -308,35 +308,38 @@ impl MirPass for SimplifyLocals {
 }
 
 /// Construct the mapping while swapping out unused stuff out from the `vec`.
-fn make_local_map<'tcx, I: Idx, V>(vec: &mut IndexVec<I, V>, mask: BitVector) -> Vec<usize> {
-    let mut map: Vec<usize> = ::std::iter::repeat(!0).take(vec.len()).collect();
-    let mut used = 0;
+fn make_local_map<'tcx, V>(
+    vec: &mut IndexVec<Local, V>,
+    mask: BitVector<Local>,
+) -> IndexVec<Local, Option<Local>> {
+    let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*vec);
+    let mut used = Local::new(0);
     for alive_index in mask.iter() {
-        map[alive_index] = used;
+        map[alive_index] = Some(used);
         if alive_index != used {
             vec.swap(alive_index, used);
         }
-        used += 1;
+        used.increment_by(1);
     }
-    vec.truncate(used);
+    vec.truncate(used.index());
     map
 }
 
 struct DeclMarker {
-    pub locals: BitVector,
+    pub locals: BitVector<Local>,
 }
 
 impl<'tcx> Visitor<'tcx> for DeclMarker {
     fn visit_local(&mut self, local: &Local, ctx: PlaceContext<'tcx>, _: Location) {
         // ignore these altogether, they get removed along with their otherwise unused decls.
         if ctx != PlaceContext::StorageLive && ctx != PlaceContext::StorageDead {
-            self.locals.insert(local.index());
+            self.locals.insert(*local);
         }
     }
 }
 
 struct LocalUpdater {
-    map: Vec<usize>,
+    map: IndexVec<Local, Option<Local>>,
 }
 
 impl<'tcx> MutVisitor<'tcx> for LocalUpdater {
@@ -345,7 +348,7 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater {
         data.statements.retain(|stmt| {
             match stmt.kind {
                 StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
-                    self.map[l.index()] != !0
+                    self.map[l].is_some()
                 }
                 _ => true
             }
@@ -353,6 +356,6 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater {
         self.super_basic_block_data(block, data);
     }
     fn visit_local(&mut self, l: &mut Local, _: PlaceContext<'tcx>, _: Location) {
-        *l = Local::new(self.map[l.index()]);
+        *l = self.map[*l].unwrap();
     }
 }
diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs
index 5cca3e55259d0..adc0249a40cfc 100644
--- a/src/test/mir-opt/nll/named-lifetimes-basic.rs
+++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs
@@ -34,15 +34,15 @@ fn main() {
 // | '_#4r    | Local    | ['_#4r]
 // |
 // | Inferred Region Values
-// | '_#0r    | {'_#0r, bb0[0..=1]}
-// | '_#1r    | {'_#1r, bb0[0..=1]}
-// | '_#2r    | {'_#2r, bb0[0..=1]}
-// | '_#3r    | {'_#3r, bb0[0..=1]}
-// | '_#4r    | {'_#4r, bb0[0..=1]}
-// | '_#5r    | {'_#1r, bb0[0..=1]}
-// | '_#6r    | {'_#2r, bb0[0..=1]}
-// | '_#7r    | {'_#1r, bb0[0..=1]}
-// | '_#8r    | {'_#3r, bb0[0..=1]}
+// | '_#0r    | U0 | {bb0[0..=127], '_#0r}
+// | '_#1r    | U0 | {bb0[0..=127], '_#1r}
+// | '_#2r    | U0 | {bb0[0..=127], '_#2r}
+// | '_#3r    | U0 | {bb0[0..=127], '_#3r}
+// | '_#4r    | U0 | {bb0[0..=127], '_#4r}
+// | '_#5r    | U0 | {bb0[0..=127], '_#1r}
+// | '_#6r    | U0 | {bb0[0..=127], '_#2r}
+// | '_#7r    | U0 | {bb0[0..=127], '_#1r}
+// | '_#8r    | U0 | {bb0[0..=127], '_#3r}
 // |
 // ...
 // fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r u32) -> bool {
diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs
index 94a6a9799cf9d..8a7ea8962fc5d 100644
--- a/src/test/mir-opt/nll/reborrow-basic.rs
+++ b/src/test/mir-opt/nll/reborrow-basic.rs
@@ -28,9 +28,9 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-// | '_#7r    | {bb0[4], bb0[8..=17]}
+// | '_#7r    | U0 | {bb0[4], bb0[8..=17]}
 // ...
-// | '_#9r    | {bb0[10], bb0[14..=17]}
+// | '_#9r    | U0 | {bb0[10], bb0[14..=17]}
 // ...
 // let _4: &'_#9r mut i32;
 // ...
diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs
index a8dd6c73ef017..e554024efef43 100644
--- a/src/test/mir-opt/nll/region-subtyping-basic.rs
+++ b/src/test/mir-opt/nll/region-subtyping-basic.rs
@@ -32,9 +32,9 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-// | '_#2r    | {bb2[0..=3], bb3[0..=1]}
-// | '_#3r    | {bb2[1..=3], bb3[0..=1]}
-// | '_#4r    | {bb2[3], bb3[0..=1]}
+// | '_#2r    | U0 | {bb2[0..=3], bb3[0..=1]}
+// | '_#3r    | U0 | {bb2[1..=3], bb3[0..=1]}
+// | '_#4r    | U0 | {bb2[3], bb3[0..=1]}
 // END rustc.main.nll.0.mir
 // START rustc.main.nll.0.mir
 // let _6: &'_#4r usize;
diff --git a/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr b/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr
index 2284f0784c545..92225369c9271 100644
--- a/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr
+++ b/src/test/ui/borrowck/mut-borrow-in-loop.nll.stderr
@@ -2,28 +2,37 @@ error[E0499]: cannot borrow `*arg` as mutable more than once at a time
   --> $DIR/mut-borrow-in-loop.rs:20:25
    |
 LL |             (self.func)(arg) //~ ERROR cannot borrow
-   |             ------------^^^-
-   |             |           |
-   |             |           mutable borrow starts here in previous iteration of loop
-   |             borrow later used here
+   |                         ^^^ mutable borrow starts here in previous iteration of loop
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the impl at 17:6...
+  --> $DIR/mut-borrow-in-loop.rs:17:6
+   |
+LL | impl<'a, T : 'a> FuncWrapper<'a, T> {
+   |      ^^
 
 error[E0499]: cannot borrow `*arg` as mutable more than once at a time
   --> $DIR/mut-borrow-in-loop.rs:26:25
    |
 LL |             (self.func)(arg) //~ ERROR cannot borrow
-   |             ------------^^^-
-   |             |           |
-   |             |           mutable borrow starts here in previous iteration of loop
-   |             borrow later used here
+   |                         ^^^ mutable borrow starts here in previous iteration of loop
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the impl at 17:6...
+  --> $DIR/mut-borrow-in-loop.rs:17:6
+   |
+LL | impl<'a, T : 'a> FuncWrapper<'a, T> {
+   |      ^^
 
 error[E0499]: cannot borrow `*arg` as mutable more than once at a time
   --> $DIR/mut-borrow-in-loop.rs:33:25
    |
 LL |             (self.func)(arg) //~ ERROR cannot borrow
-   |             ------------^^^-
-   |             |           |
-   |             |           mutable borrow starts here in previous iteration of loop
-   |             borrow later used here
+   |                         ^^^ mutable borrow starts here in previous iteration of loop
+   |
+note: borrowed value must be valid for the lifetime 'a as defined on the impl at 17:6...
+  --> $DIR/mut-borrow-in-loop.rs:17:6
+   |
+LL | impl<'a, T : 'a> FuncWrapper<'a, T> {
+   |      ^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr
index 4c0b3a5d93120..c6f8d2e519ca1 100644
--- a/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr
+++ b/src/test/ui/impl-trait/static-return-lifetime-infered.nll.stderr
@@ -10,19 +10,21 @@ warning: not reporting region error due to nll
 LL |         self.x.iter().map(|a| a.0)
    |                ^^^^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/static-return-lifetime-infered.rs:17:9
    |
 LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
-   |                         - let's call the lifetime of this reference `'1`
+   |                         ----- `self` is a reference that is only valid in the closure body
 LL |         self.x.iter().map(|a| a.0)
-   |         ^^^^^^ cast requires that `'1` must outlive `'static`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` escapes the closure body here
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/static-return-lifetime-infered.rs:21:9
    |
+LL |     fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> {
+   |                        -------- `self` is a reference that is only valid in the closure body
 LL |         self.x.iter().map(|a| a.0)
-   |         ^^^^^^ cast requires that `'a` must outlive `'static`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` escapes the closure body here
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issue-40288-2.nll.stderr b/src/test/ui/issue-40288-2.nll.stderr
index fcff5c6a8edad..1d2b26603426f 100644
--- a/src/test/ui/issue-40288-2.nll.stderr
+++ b/src/test/ui/issue-40288-2.nll.stderr
@@ -35,20 +35,22 @@ LL |     let mut out = Struct { head: x, _tail: [()] };
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0621]: explicit lifetime required in the type of `y`
-  --> $DIR/issue-40288-2.rs:14:9
+  --> $DIR/issue-40288-2.rs:17:9
    |
 LL | fn lifetime_transmute_slice<'a, T: ?Sized>(x: &'a T, y: &T) -> &'a T {
    |                                                      - consider changing the type of `y` to `&'a T`
-LL |     let mut out = [x];
-   |         ^^^^^^^ lifetime `'a` required
+...
+LL |         slice[0] = y;
+   |         ^^^^^^^^^^^^ lifetime `'a` required
 
 error[E0621]: explicit lifetime required in the type of `y`
-  --> $DIR/issue-40288-2.rs:29:9
+  --> $DIR/issue-40288-2.rs:32:9
    |
 LL | fn lifetime_transmute_struct<'a, T: ?Sized>(x: &'a T, y: &T) -> &'a T {
    |                                                       - consider changing the type of `y` to `&'a T`
-LL |     let mut out = Struct { head: x, _tail: [()] };
-   |         ^^^^^^^ lifetime `'a` required
+...
+LL |         dst.head = y;
+   |         ^^^^^^^^^^^^ lifetime `'a` required
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.nll.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.nll.stderr
index 1e45914138de5..4d9517eca6032 100644
--- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.nll.stderr
@@ -5,12 +5,12 @@ LL |     if x > y { x } else { y } //~ ERROR explicit lifetime
    |                           ^
 
 error[E0621]: explicit lifetime required in parameter type
-  --> $DIR/ex1-return-one-existing-name-if-else-3.rs:11:13
+  --> $DIR/ex1-return-one-existing-name-if-else-3.rs:11:16
    |
 LL | fn foo<'a>((x, y): (&'a i32, &i32)) -> &'a i32 {
-   |            -^----
-   |            ||
-   |            |lifetime `'a` required
+   |            ----^-
+   |            |   |
+   |            |   lifetime `'a` required
    |            consider changing type to `(&'a i32, &'a i32)`
 
 error: aborting due to previous error
diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.nll.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.nll.stderr
index a51d9307d074d..087c9eb389b55 100644
--- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.nll.stderr
@@ -10,7 +10,7 @@ error[E0621]: explicit lifetime required in the type of `x`
 LL | fn foo<'a>(x: Ref<i32>, y: &mut Vec<Ref<'a, i32>>) {
    |            - consider changing the type of `x` to `Ref<'a, i32>`
 LL |     y.push(x); //~ ERROR explicit lifetime
-   |     ^ lifetime `'a` required
+   |     ^^^^^^^^^ lifetime `'a` required
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.nll.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.nll.stderr
index e50fd74faf4ba..80192af221755 100644
--- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.nll.stderr
@@ -10,7 +10,7 @@ error[E0621]: explicit lifetime required in the type of `y`
 LL | fn foo<'a>(x: &mut Vec<Ref<'a, i32>>, y: Ref<i32>) {
    |                                       - consider changing the type of `y` to `Ref<'a, i32>`
 LL |     x.push(y); //~ ERROR explicit lifetime
-   |     ^ lifetime `'a` required
+   |     ^^^^^^^^^ lifetime `'a` required
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.nll.stderr b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.nll.stderr
index 283192c684392..4b4fdde940f7e 100644
--- a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.nll.stderr
@@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch
 LL | fn foo(x: &mut Vec<Ref<i32>>, y: Ref<i32>) {
    |                    --------      -------- these two types are declared with different lifetimes...
 LL |     x.push(y); //~ ERROR lifetime mismatch
-   |     ^ ...but data from `y` flows into `x` here
+   |     ^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.nll.stderr b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.nll.stderr
index 2ca202b402cee..f55fd291249c6 100644
--- a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.nll.stderr
@@ -5,12 +5,13 @@ LL |     let z = Ref { data: y.data };
    |             ^^^
 
 error[E0623]: lifetime mismatch
-  --> $DIR/ex2c-push-inference-variable.rs:16:9
+  --> $DIR/ex2c-push-inference-variable.rs:17:5
    |
 LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
    |                                   ------------      ------------ these two types are declared with different lifetimes...
 LL |     let z = Ref { data: y.data };
-   |         ^ ...but data from `y` flows into `x` here
+LL |     x.push(z); //~ ERROR lifetime mismatch
+   |     ^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.nll.stderr b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.nll.stderr
index 712c25f8929d4..85b5f3e890008 100644
--- a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.nll.stderr
@@ -5,12 +5,13 @@ LL |     let b = Ref { data: y.data };
    |             ^^^
 
 error[E0623]: lifetime mismatch
-  --> $DIR/ex2d-push-inference-variable-2.rs:16:9
+  --> $DIR/ex2d-push-inference-variable-2.rs:18:5
    |
 LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
    |                                   ------------      ------------ these two types are declared with different lifetimes...
-LL |     let a: &mut Vec<Ref<i32>> = x; //~ ERROR lifetime mismatch
-   |         ^ ...but data from `y` flows into `x` here
+...
+LL |     a.push(b);
+   |     ^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.nll.stderr b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.nll.stderr
index 351966902a4fb..7e5182a5d30c9 100644
--- a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.nll.stderr
@@ -5,12 +5,13 @@ LL |     let b = Ref { data: y.data };
    |             ^^^
 
 error[E0623]: lifetime mismatch
-  --> $DIR/ex2e-push-inference-variable-3.rs:16:9
+  --> $DIR/ex2e-push-inference-variable-3.rs:18:5
    |
 LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
    |                                   ------------      ------------ these two types are declared with different lifetimes...
-LL |     let a: &mut Vec<Ref<i32>> = x; //~ ERROR lifetime mismatch
-   |         ^ ...but data from `y` flows into `x` here
+...
+LL |     Vec::push(a, b);
+   |     ^^^^^^^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.nll.stderr
index da171577e2d8b..36317c4570b98 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.nll.stderr
@@ -5,12 +5,12 @@ LL |     *v = x; //~ ERROR lifetime mismatch
    |          ^
 
 error[E0623]: lifetime mismatch
-  --> $DIR/ex3-both-anon-regions-2.rs:11:14
+  --> $DIR/ex3-both-anon-regions-2.rs:12:5
    |
 LL | fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) {
-   |              ^^^^^^^^^            ---           --- these two types are declared with different lifetimes...
-   |              |
-   |              ...but data from `x` flows here
+   |                                   ---           --- these two types are declared with different lifetimes...
+LL |     *v = x; //~ ERROR lifetime mismatch
+   |     ^^^^^^ ...but data from `x` flows here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.nll.stderr
index 102981977e557..c43c4ce3a0c21 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.nll.stderr
@@ -11,20 +11,20 @@ LL |     z.push((x,y)); //~ ERROR lifetime mismatch
    |               ^
 
 error[E0623]: lifetime mismatch
-  --> $DIR/ex3-both-anon-regions-3.rs:11:33
+  --> $DIR/ex3-both-anon-regions-3.rs:12:5
    |
 LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
-   |                     ---         ^       --- these two types are declared with different lifetimes...
-   |                                 |
-   |                                 ...but data flows into `z` here
+   |                     ---                 --- these two types are declared with different lifetimes...
+LL |     z.push((x,y)); //~ ERROR lifetime mismatch
+   |     ^^^^^^^^^^^^^ ...but data flows into `z` here
 
 error[E0623]: lifetime mismatch
-  --> $DIR/ex3-both-anon-regions-3.rs:11:33
+  --> $DIR/ex3-both-anon-regions-3.rs:12:5
    |
 LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
-   |                         ---     ^            --- these two types are declared with different lifetimes...
-   |                                 |
-   |                                 ...but data flows into `z` here
+   |                         ---                  --- these two types are declared with different lifetimes...
+LL |     z.push((x,y)); //~ ERROR lifetime mismatch
+   |     ^^^^^^^^^^^^^ ...but data flows into `z` here
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.nll.stderr
index 9d1f6a3e36f1b..2a5729952e33d 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.nll.stderr
@@ -11,7 +11,7 @@ LL | fn foo<'a, 'b>(mut x: Vec<Ref<'a>>, y: Ref<'b>)
    |                           -------      ------- these two types are declared with different lifetimes...
 ...
 LL |     x.push(y); //~ ERROR lifetime mismatch
-   |     ^ ...but data from `y` flows into `x` here
+   |     ^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.nll.stderr
index 5df93fd5547c7..6efc8d3da0677 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.nll.stderr
@@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch
 LL | fn foo<'a, 'b>(mut x: Vec<Ref<'a>>, y: Ref<'b>) {
    |                           -------      ------- these two types are declared with different lifetimes...
 LL |     x.push(y); //~ ERROR lifetime mismatch
-   |     ^ ...but data from `y` flows into `x` here
+   |     ^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr
index cd602cf950b18..0f555020822cb 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-both-are-structs.nll.stderr
@@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch
 LL | fn foo(mut x: Vec<Ref>, y: Ref) {
    |                   ---      --- these two types are declared with different lifetimes...
 LL |     x.push(y); //~ ERROR lifetime mismatch
-   |     ^ ...but data from `y` flows into `x` here
+   |     ^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.nll.stderr
index 52c90839c32af..4400644e7fb65 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-latebound-regions.nll.stderr
@@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch
 LL | fn foo<'a,'b>(x: &mut Vec<&'a u8>, y: &'b u8) {
    |                           ------      ------ these two types are declared with different lifetimes...
 LL |     x.push(y); //~ ERROR lifetime mismatch
-   |     ^ ...but data from `y` flows into `x` here
+   |     ^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
index d5bba6649a2c7..a0aa1e28d9bc0 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
@@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch
 LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
    |                               ---      --- these two types are declared with different lifetimes...
 LL |   y.push(z); //~ ERROR lifetime mismatch
-   |   ^ ...but data from `z` flows into `y` here
+   |   ^^^^^^^^^ ...but data from `z` flows into `y` here
 
 error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
   --> $DIR/ex3-both-anon-regions-using-fn-items.rs:11:3
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr
index 4d54f6fe0375c..5d4492701beb3 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-impl-items.nll.stderr
@@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch
 LL |     fn foo(x: &mut Vec<&u8>, y: &u8) {
    |                        ---      --- these two types are declared with different lifetimes...
 LL |         x.push(y); //~ ERROR lifetime mismatch
-   |         ^ ...but data from `y` flows into `x` here
+   |         ^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
index 0608b3be8b33a..37b79cee72f75 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
@@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch
 LL | fn foo(x:Box<Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
    |                 ---  --- these two types are declared with different lifetimes...
 LL |   y.push(z); //~ ERROR lifetime mismatch
-   |   ^ ...but data from `z` flows into `y` here
+   |   ^^^^^^^^^ ...but data from `z` flows into `y` here
 
 error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
   --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:11:3
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions.nll.stderr
index c25eedc770d48..c11d81a4c13da 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions.nll.stderr
@@ -10,7 +10,7 @@ error[E0623]: lifetime mismatch
 LL | fn foo(x: &mut Vec<&u8>, y: &u8) {
    |                    ---      --- these two types are declared with different lifetimes...
 LL |     x.push(y); //~ ERROR lifetime mismatch
-   |     ^ ...but data from `y` flows into `x` here
+   |     ^^^^^^^^^ ...but data from `y` flows into `x` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs
index 39050864768ae..b879f9a33986d 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs
@@ -52,9 +52,9 @@ fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell
         cell_c,
         |_outlives1, _outlives2, _outlives3, x, y| {
             // Only works if 'x: 'y:
-            let p = x.get(); //~ ERROR
+            let p = x.get();
             //~^ WARN not reporting region error due to nll
-            demand_y(x, y, p)
+            demand_y(x, y, p) //~ ERROR
         },
     );
 }
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
index 6588cbe8bdf26..a7a50a3a02981 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
@@ -1,28 +1,28 @@
 warning: not reporting region error due to nll
   --> $DIR/propagate-approximated-fail-no-postdom.rs:55:21
    |
-LL |             let p = x.get(); //~ ERROR
+LL |             let p = x.get();
    |                     ^^^^^^^
 
 error: unsatisfied lifetime constraints
-  --> $DIR/propagate-approximated-fail-no-postdom.rs:55:21
+  --> $DIR/propagate-approximated-fail-no-postdom.rs:57:13
    |
 LL |         |_outlives1, _outlives2, _outlives3, x, y| {
    |          ----------              ---------- lifetime `'2` appears in this argument
    |          |
    |          lifetime `'1` appears in this argument
-LL |             // Only works if 'x: 'y:
-LL |             let p = x.get(); //~ ERROR
-   |                     ^^^^^^^ argument requires that `'1` must outlive `'2`
+...
+LL |             demand_y(x, y, p) //~ ERROR
+   |             ^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
 
 note: No external requirements
   --> $DIR/propagate-approximated-fail-no-postdom.rs:53:9
    |
 LL | /         |_outlives1, _outlives2, _outlives3, x, y| {
 LL | |             // Only works if 'x: 'y:
-LL | |             let p = x.get(); //~ ERROR
+LL | |             let p = x.get();
 LL | |             //~^ WARN not reporting region error due to nll
-LL | |             demand_y(x, y, p)
+LL | |             demand_y(x, y, p) //~ ERROR
 LL | |         },
    | |_________^
    |
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
index 840b4071a8776..d713a37fa9f41 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
@@ -24,14 +24,19 @@ LL | |     });
    = note: where '_#1r: '_#2r
 
 error[E0623]: lifetime mismatch
-  --> $DIR/propagate-approximated-ref.rs:53:29
+  --> $DIR/propagate-approximated-ref.rs:53:5
    |
-LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
-   |                                -------                -------
-   |                                |
-   |                                these two types are declared with different lifetimes...
-LL |     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
-   |                             ^^^^^^^ ...but data from `cell_a` flows into `cell_b` here
+LL |   fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+   |                                  -------                -------
+   |                                  |
+   |                                  these two types are declared with different lifetimes...
+LL | /     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+LL | |         //~^ ERROR lifetime mismatch
+LL | |
+LL | |         // Only works if 'x: 'y:
+LL | |         demand_y(x, y, x.get()) //~ WARNING not reporting region error due to nll
+LL | |     });
+   | |______^ ...but data from `cell_a` flows into `cell_b` here
 
 note: No external requirements
   --> $DIR/propagate-approximated-ref.rs:52:1
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
index d51ba8201aaa6..b8e8fae14b005 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
@@ -5,7 +5,7 @@ LL |     foo(cell, |cell_a, cell_x| {
    |     ^^^
 
 error: borrowed data escapes outside of closure
-  --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:33:20
+  --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:33:9
    |
 LL |     foo(cell, |cell_a, cell_x| {
    |                ------  ------ `cell_x` is a reference that is only valid in the closure body
@@ -13,7 +13,7 @@ LL |     foo(cell, |cell_a, cell_x| {
    |                `cell_a` is declared here, outside of the closure body
 LL |         //~^ WARNING not reporting region error due to nll
 LL |         cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
-   |                    ^^^^^^^^^^^^ `cell_x` escapes the closure body here
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^ `cell_x` escapes the closure body here
 
 note: No external requirements
   --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
index cf5f4d415b669..f6ad6e46c6299 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
@@ -24,14 +24,19 @@ LL | |     });
    = note: where '_#1r: '_#2r
 
 error[E0623]: lifetime mismatch
-  --> $DIR/propagate-approximated-val.rs:46:29
+  --> $DIR/propagate-approximated-val.rs:46:5
    |
-LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
-   |                              -------                -------
-   |                              |
-   |                              these two types are declared with different lifetimes...
-LL |     establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| {
-   |                             ^^^^^^ ...but data from `cell_a` flows into `cell_b` here
+LL |   fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+   |                                -------                -------
+   |                                |
+   |                                these two types are declared with different lifetimes...
+LL | /     establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| {
+LL | |         //~^ ERROR lifetime mismatch
+LL | |
+LL | |         // Only works if 'x: 'y:
+LL | |         demand_y(outlives1, outlives2, x.get()) //~ WARNING not reporting region error due to nll
+LL | |     });
+   | |______^ ...but data from `cell_a` flows into `cell_b` here
 
 note: No external requirements
   --> $DIR/propagate-approximated-val.rs:45:1
diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
index c75b3e6670cdc..fb98c506c7d28 100644
--- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
@@ -5,7 +5,7 @@ LL |         demand_y(x, y, x.get())
    |         ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unsatisfied lifetime constraints
-  --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:24
+  --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:9
    |
 LL |     establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
    |                                                ---------  - lifetime `'1` appears in this argument
@@ -13,7 +13,7 @@ LL |     establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
    |                                                lifetime `'2` appears in this argument
 LL |         // Only works if 'x: 'y:
 LL |         demand_y(x, y, x.get())
-   |                        ^^^^^^^ argument requires that `'1` must outlive `'2`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
 
 note: No external requirements
   --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:45:47
diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
index 2465219ee552a..73d39a8502b64 100644
--- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
@@ -5,7 +5,7 @@ LL |         demand_y(x, y, x.get())
    |         ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unsatisfied lifetime constraints
-  --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:24
+  --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:9
    |
 LL |     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
    |                                                ----------  ---------- lifetime `'2` appears in this argument
@@ -13,7 +13,7 @@ LL |     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y
    |                                                lifetime `'1` appears in this argument
 LL |         // Only works if 'x: 'y:
 LL |         demand_y(x, y, x.get())
-   |                        ^^^^^^^ argument requires that `'1` must outlive `'2`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
 
 note: No external requirements
   --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:49:47
diff --git a/src/test/compile-fail/mir_check_cast_closure.rs b/src/test/ui/nll/mir_check_cast_closure.rs
similarity index 100%
rename from src/test/compile-fail/mir_check_cast_closure.rs
rename to src/test/ui/nll/mir_check_cast_closure.rs
diff --git a/src/test/ui/nll/mir_check_cast_closure.stderr b/src/test/ui/nll/mir_check_cast_closure.stderr
new file mode 100644
index 0000000000000..fc2a3c43f7589
--- /dev/null
+++ b/src/test/ui/nll/mir_check_cast_closure.stderr
@@ -0,0 +1,14 @@
+warning: not reporting region error due to nll
+  --> $DIR/mir_check_cast_closure.rs:18:5
+   |
+LL |     g
+   |     ^
+
+error: unsatisfied lifetime constraints
+  --> $DIR/mir_check_cast_closure.rs:16:28
+   |
+LL |     let g: fn(_, _) -> _ = |_x, y| y;
+   |                            ^^^^^^^^^ cast requires that `'b` must outlive `'a`
+
+error: aborting due to previous error
+
diff --git a/src/test/compile-fail/mir_check_cast_reify.rs b/src/test/ui/nll/mir_check_cast_reify.rs
similarity index 97%
rename from src/test/compile-fail/mir_check_cast_reify.rs
rename to src/test/ui/nll/mir_check_cast_reify.rs
index f85104dff867f..3a530c1e7473d 100644
--- a/src/test/compile-fail/mir_check_cast_reify.rs
+++ b/src/test/ui/nll/mir_check_cast_reify.rs
@@ -45,8 +45,8 @@ fn bar<'a>(x: &'a u32) -> &'static u32 {
     // as part of checking the `ReifyFnPointer`.
     let f: fn(_) -> _ = foo;
     //~^ WARNING not reporting region error due to nll
-    //~| ERROR unsatisfied lifetime constraints
     f(x)
+    //~^ ERROR
 }
 
 fn main() {}
diff --git a/src/test/ui/nll/mir_check_cast_reify.stderr b/src/test/ui/nll/mir_check_cast_reify.stderr
new file mode 100644
index 0000000000000..13f90e1f159d2
--- /dev/null
+++ b/src/test/ui/nll/mir_check_cast_reify.stderr
@@ -0,0 +1,17 @@
+warning: not reporting region error due to nll
+  --> $DIR/mir_check_cast_reify.rs:46:25
+   |
+LL |     let f: fn(_) -> _ = foo;
+   |                         ^^^
+
+error: borrowed data escapes outside of closure
+  --> $DIR/mir_check_cast_reify.rs:48:5
+   |
+LL | fn bar<'a>(x: &'a u32) -> &'static u32 {
+   |            - `x` is a reference that is only valid in the closure body
+...
+LL |     f(x)
+   |     ^^^^ `x` escapes the closure body here
+
+error: aborting due to previous error
+
diff --git a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs b/src/test/ui/nll/mir_check_cast_unsafe_fn.rs
similarity index 94%
rename from src/test/compile-fail/mir_check_cast_unsafe_fn.rs
rename to src/test/ui/nll/mir_check_cast_unsafe_fn.rs
index e90242f3f87da..4a840da028d81 100644
--- a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs
+++ b/src/test/ui/nll/mir_check_cast_unsafe_fn.rs
@@ -17,8 +17,8 @@ fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 {
     // in `g`. These are related via the `UnsafeFnPointer` cast.
     let g: unsafe fn(_) -> _ = f;
     //~^ WARNING not reporting region error due to nll
-    //~| ERROR unsatisfied lifetime constraints
     unsafe { g(input) }
+    //~^ ERROR
 }
 
 fn main() {}
diff --git a/src/test/ui/nll/mir_check_cast_unsafe_fn.stderr b/src/test/ui/nll/mir_check_cast_unsafe_fn.stderr
new file mode 100644
index 0000000000000..b08c6f32e6b43
--- /dev/null
+++ b/src/test/ui/nll/mir_check_cast_unsafe_fn.stderr
@@ -0,0 +1,17 @@
+warning: not reporting region error due to nll
+  --> $DIR/mir_check_cast_unsafe_fn.rs:18:32
+   |
+LL |     let g: unsafe fn(_) -> _ = f;
+   |                                ^
+
+error: borrowed data escapes outside of closure
+  --> $DIR/mir_check_cast_unsafe_fn.rs:20:14
+   |
+LL | fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 {
+   |            ----- `input` is a reference that is only valid in the closure body
+...
+LL |     unsafe { g(input) }
+   |              ^^^^^^^^ `input` escapes the closure body here
+
+error: aborting due to previous error
+
diff --git a/src/test/compile-fail/mir_check_cast_unsize.rs b/src/test/ui/nll/mir_check_cast_unsize.rs
similarity index 92%
rename from src/test/compile-fail/mir_check_cast_unsize.rs
rename to src/test/ui/nll/mir_check_cast_unsize.rs
index d242186a6f7ea..695dddbf7e9d5 100644
--- a/src/test/compile-fail/mir_check_cast_unsize.rs
+++ b/src/test/ui/nll/mir_check_cast_unsize.rs
@@ -15,7 +15,8 @@
 use std::fmt::Debug;
 
 fn bar<'a>(x: &'a u32) -> &'static dyn Debug {
-    x //~ ERROR unsatisfied lifetime constraints
+    //~^ ERROR unsatisfied lifetime constraints
+    x
     //~^ WARNING not reporting region error due to nll
 }
 
diff --git a/src/test/ui/nll/mir_check_cast_unsize.stderr b/src/test/ui/nll/mir_check_cast_unsize.stderr
new file mode 100644
index 0000000000000..7bd0595f3b5cc
--- /dev/null
+++ b/src/test/ui/nll/mir_check_cast_unsize.stderr
@@ -0,0 +1,19 @@
+warning: not reporting region error due to nll
+  --> $DIR/mir_check_cast_unsize.rs:19:5
+   |
+LL |     x
+   |     ^
+
+error: unsatisfied lifetime constraints
+  --> $DIR/mir_check_cast_unsize.rs:17:46
+   |
+LL |   fn bar<'a>(x: &'a u32) -> &'static dyn Debug {
+   |  ______________________________________________^
+LL | |     //~^ ERROR unsatisfied lifetime constraints
+LL | |     x
+LL | |     //~^ WARNING not reporting region error due to nll
+LL | | }
+   | |_^ return requires that `'a` must outlive `'static`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs
new file mode 100644
index 0000000000000..84c305f5907d1
--- /dev/null
+++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs
@@ -0,0 +1,27 @@
+// 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.
+
+// Test that the NLL `relate_tys` code correctly deduces that a
+// function returning either argument CANNOT be upcast to one
+// that returns always its first argument.
+//
+// compile-flags:-Zno-leak-check
+
+#![feature(nll)]
+
+fn make_it() -> for<'a> fn(&'a u32, &'a u32) -> &'a u32 {
+    panic!()
+}
+
+fn main() {
+    let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it();
+    //~^ ERROR higher-ranked subtype error
+    drop(a);
+}
diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr
new file mode 100644
index 0000000000000..e08d848b47140
--- /dev/null
+++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr
@@ -0,0 +1,8 @@
+error: higher-ranked subtype error
+  --> $DIR/hr-fn-aaa-as-aba.rs:24:58
+   |
+LL |     let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it();
+   |                                                          ^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs
new file mode 100644
index 0000000000000..9b8268d9736aa
--- /dev/null
+++ b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs
@@ -0,0 +1,38 @@
+// 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.
+
+// Test an interesting corner case that ought to be legal (though the
+// current code actually gets it wrong, see below): a fn that takes
+// two arguments that are references with the same lifetime is in fact
+// equivalent to a fn that takes two references with distinct
+// lifetimes. This is true because the two functions can call one
+// another -- effectively, the single lifetime `'a` is just inferred
+// to be the intersection of the two distinct lifetimes.
+//
+// FIXME: However, we currently reject this example with an error,
+// because of how we handle binders and equality in `relate_tys`.
+//
+// compile-flags:-Zno-leak-check
+
+#![feature(nll)]
+
+use std::cell::Cell;
+
+fn make_cell_aa() -> Cell<for<'a> fn(&'a u32, &'a u32)> {
+    panic!()
+}
+
+fn aa_eq_ab() {
+    let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
+    //~^ ERROR higher-ranked subtype error
+    drop(a);
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr
new file mode 100644
index 0000000000000..17e8a32cb2ad9
--- /dev/null
+++ b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr
@@ -0,0 +1,8 @@
+error: higher-ranked subtype error
+  --> $DIR/hr-fn-aau-eq-abu.rs:33:53
+   |
+LL |     let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
+   |                                                     ^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs b/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs
new file mode 100644
index 0000000000000..4f73ca3a53921
--- /dev/null
+++ b/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs
@@ -0,0 +1,27 @@
+// 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.
+
+// Test that the NLL `relate_tys` code correctly deduces that a
+// function returning always its first argument can be upcast to one
+// that returns either first or second argument.
+//
+// compile-pass
+// compile-flags:-Zno-leak-check
+
+#![feature(nll)]
+
+fn make_it() -> for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 {
+    panic!()
+}
+
+fn main() {
+    let a: for<'a> fn(&'a u32, &'a u32) -> &'a u32 = make_it();
+    drop(a);
+}
diff --git a/src/test/ui/nll/relate_tys/issue-48071.rs b/src/test/ui/nll/relate_tys/issue-48071.rs
new file mode 100644
index 0000000000000..c2498cbe50f7e
--- /dev/null
+++ b/src/test/ui/nll/relate_tys/issue-48071.rs
@@ -0,0 +1,38 @@
+// Copyright 2018 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.
+
+// Regression test for #48071. This test used to ICE because -- in
+// the leak-check -- it would pass since we knew that the return type
+// was `'static`, and hence `'static: 'a` was legal even for a
+// placeholder region, but in NLL land it would fail because we had
+// rewritten `'static` to a region variable.
+//
+// compile-pass
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+#![feature(nll)]
+
+trait Foo {
+    fn foo(&self) { }
+}
+
+impl Foo for () {
+}
+
+type MakeFooFn = for<'a> fn(&'a u8) -> Box<dyn Foo + 'a>;
+
+fn make_foo(x: &u8) -> Box<dyn Foo + 'static> {
+    Box::new(())
+}
+
+fn main() {
+    let x: MakeFooFn = make_foo as MakeFooFn;
+}
diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr
index 6385578698cf7..5b4c669c66ecd 100644
--- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr
+++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr
@@ -23,13 +23,16 @@ LL |     Box::new(items.iter()) //~ ERROR cannot infer an appropriate lifetime
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: unsatisfied lifetime constraints
-  --> $DIR/dyn-trait-underscore.rs:18:5
+  --> $DIR/dyn-trait-underscore.rs:16:52
    |
-LL | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T>> {
-   |                - let's call the lifetime of this reference `'1`
-LL |     //                      ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static`
-LL |     Box::new(items.iter()) //~ ERROR cannot infer an appropriate lifetime
-   |     ^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`
+LL |   fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T>> {
+   |  ________________-___________________________________^
+   | |                |
+   | |                let's call the lifetime of this reference `'1`
+LL | |     //                      ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static`
+LL | |     Box::new(items.iter()) //~ ERROR cannot infer an appropriate lifetime
+LL | | }
+   | |_^ return requires that `'1` must outlive `'static`
 
 error: aborting due to previous error