about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ich/impls_mir.rs12
-rw-r--r--src/librustc/ich/impls_ty.rs10
-rw-r--r--src/librustc/infer/mod.rs49
-rw-r--r--src/librustc/mir/mod.rs69
-rw-r--r--src/librustc/traits/project.rs17
-rw-r--r--src/librustc/traits/select.rs3
-rw-r--r--src/librustc/ty/instance.rs4
-rw-r--r--src/librustc/ty/maps/mod.rs6
-rw-r--r--src/librustc/ty/sty.rs44
-rw-r--r--src/librustc_driver/driver.rs2
-rw-r--r--src/librustc_mir/borrow_check/mod.rs29
-rw-r--r--src/librustc_mir/borrow_check/nll/mod.rs147
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/annotation.rs48
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs100
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs71
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/mod.rs305
-rw-r--r--src/librustc_mir/borrow_check/nll/renumber.rs66
-rw-r--r--src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs6
-rw-r--r--src/librustc_mir/borrow_check/nll/universal_regions.rs704
-rw-r--r--src/librustc_mir/transform/type_check.rs83
-rw-r--r--src/librustc_mir/util/pretty.rs140
-rw-r--r--src/librustc_trans/common.rs4
-rw-r--r--src/librustc_trans/mir/constant.rs5
-rw-r--r--src/librustc_trans_utils/monomorphize.rs4
-rw-r--r--src/librustc_typeck/check/callee.rs3
-rw-r--r--src/librustc_typeck/check/coercion.rs3
-rw-r--r--src/librustc_typeck/collect.rs26
-rw-r--r--src/libsyntax/feature_gate.rs6
-rw-r--r--src/libsyntax_pos/symbol.rs11
-rw-r--r--src/test/compile-fail/regions-static-bound.rs16
-rw-r--r--src/test/mir-opt/nll/liveness-call-subtlety.rs8
-rw-r--r--src/test/mir-opt/nll/liveness-drop-intra-block.rs10
-rw-r--r--src/test/mir-opt/nll/liveness-interblock.rs8
-rw-r--r--src/test/mir-opt/nll/named-lifetimes-basic.rs17
-rw-r--r--src/test/mir-opt/nll/reborrow-basic.rs7
-rw-r--r--src/test/mir-opt/nll/region-liveness-basic.rs14
-rw-r--r--src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs2
-rw-r--r--src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs2
-rw-r--r--src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs6
-rw-r--r--src/test/mir-opt/nll/region-subtyping-basic.rs6
-rw-r--r--src/test/ui/nll/capture-ref-in-struct.rs50
-rw-r--r--src/test/ui/nll/capture-ref-in-struct.stderr13
-rw-r--r--src/test/ui/nll/closure-requirements/escape-argument-callee.rs53
-rw-r--r--src/test/ui/nll/closure-requirements/escape-argument-callee.stderr40
-rw-r--r--src/test/ui/nll/closure-requirements/escape-argument.rs52
-rw-r--r--src/test/ui/nll/closure-requirements/escape-argument.stderr39
-rw-r--r--src/test/ui/nll/closure-requirements/escape-upvar-nested.rs43
-rw-r--r--src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr61
-rw-r--r--src/test/ui/nll/closure-requirements/escape-upvar-ref.rs42
-rw-r--r--src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr42
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs63
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr46
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs64
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr36
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs53
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr75
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs51
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr36
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs54
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr36
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-val.rs52
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr36
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs59
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr37
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs53
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr46
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs57
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr46
-rw-r--r--src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs24
-rw-r--r--src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr14
-rw-r--r--src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs24
-rw-r--r--src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr14
-rw-r--r--src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs (renamed from src/test/ui/nll/named-region-basic.rs)8
-rw-r--r--src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr14
-rw-r--r--src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs23
-rw-r--r--src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs34
-rw-r--r--src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr38
-rw-r--r--src/test/ui/nll/named-region-basic.stderr31
-rw-r--r--src/tools/compiletest/src/runtest.rs11
79 files changed, 3288 insertions, 355 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index 331b44ac119..beee34e11b7 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -572,3 +572,15 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Literal<'gcx> {
 }
 
 impl_stable_hash_for!(struct mir::Location { block, statement_index });
+
+impl_stable_hash_for!(struct mir::ClosureRegionRequirements {
+    num_external_vids,
+    outlives_requirements
+});
+
+impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement {
+    free_region,
+    outlived_free_region,
+    blame_span
+});
+
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 9609ae5a0be..2655e2acbbd 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -84,6 +84,16 @@ for ty::RegionKind {
     }
 }
 
+impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::RegionVid {
+    #[inline]
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'gcx>,
+                                          hasher: &mut StableHasher<W>) {
+        use rustc_data_structures::indexed_vec::Idx;
+        self.index().hash_stable(hcx, hasher);
+    }
+}
+
 impl<'gcx> HashStable<StableHashingContext<'gcx>>
 for ty::adjustment::AutoBorrow<'gcx> {
     fn hash_stable<W: StableHasherResult>(&self,
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 96a980a1545..f5595d07340 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -1062,6 +1062,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin)))
     }
 
+    /// Number of region variables created so far.
+    pub fn num_region_vars(&self) -> usize {
+        self.borrow_region_constraints().var_origins().len()
+    }
+
     /// Just a convenient wrapper of `next_region_var` for using during NLL.
     pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin)
                                -> ty::Region<'tcx> {
@@ -1475,38 +1480,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         closure_kind_ty.to_opt_closure_kind()
     }
 
-    /// Obtain the signature of a function or closure.
-    /// For closures, unlike `tcx.fn_sig(def_id)`, this method will
-    /// work during the type-checking of the enclosing function and
-    /// return the closure signature in its partially inferred state.
-    pub fn fn_sig(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> {
-        // Do we have an in-progress set of tables we are inferring?
-        if let Some(tables) = self.in_progress_tables {
-            // Is this a local item?
-            if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
-                // Is it a local *closure*?
-                if self.tcx.is_closure(def_id) {
-                    let hir_id = self.tcx.hir.node_to_hir_id(id);
-                    // Is this local closure contained within the tables we are inferring?
-                    if tables.borrow().local_id_root == Some(DefId::local(hir_id.owner)) {
-                        // if so, extract signature from there.
-                        let closure_ty = tables.borrow().node_id_to_type(hir_id);
-                        let (closure_def_id, closure_substs) = match closure_ty.sty {
-                            ty::TyClosure(closure_def_id, closure_substs) =>
-                                (closure_def_id, closure_substs),
-                            _ =>
-                                bug!("closure with non-closure type: {:?}", closure_ty),
-                        };
-                        assert_eq!(def_id, closure_def_id);
-                        let closure_sig_ty = closure_substs.closure_sig_ty(def_id, self.tcx);
-                        let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
-                        return closure_sig_ty.fn_sig(self.tcx);
-                    }
-                }
-            }
-        }
-
-        self.tcx.fn_sig(def_id)
+    /// Obtain the signature of a closure.  For closures, unlike
+    /// `tcx.fn_sig(def_id)`, this method will work during the
+    /// type-checking of the enclosing function and return the closure
+    /// signature in its partially inferred state.
+    pub fn closure_sig(
+        &self,
+        def_id: DefId,
+        substs: ty::ClosureSubsts<'tcx>
+    ) -> ty::PolyFnSig<'tcx> {
+        let closure_sig_ty = substs.closure_sig_ty(def_id, self.tcx);
+        let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
+        closure_sig_ty.fn_sig(self.tcx)
     }
 
     /// Normalizes associated types in `value`, potentially returning
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index c803e76aebe..720d831a245 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1789,6 +1789,75 @@ pub struct GeneratorLayout<'tcx> {
     pub fields: Vec<LocalDecl<'tcx>>,
 }
 
+/// After we borrow check a closure, we are left with various
+/// requirements that we have inferred between the free regions that
+/// appear in the closure's signature or on its field types.  These
+/// requirements are then verified and proved by the closure's
+/// creating function. This struct encodes those requirements.
+///
+/// The requirements are listed as being between various
+/// `RegionVid`. The 0th region refers to `'static`; subsequent region
+/// vids refer to the free regions that appear in the closure (or
+/// generator's) type, in order of appearance. (This numbering is
+/// actually defined by the `UniversalRegions` struct in the NLL
+/// region checker. See for example
+/// `UniversalRegions::closure_mapping`.) Note that we treat the free
+/// regions in the closure's type "as if" they were erased, so their
+/// precise identity is not important, only their position.
+///
+/// Example: If type check produces a closure with the closure substs:
+///
+/// ```
+/// ClosureSubsts = [
+///     i8,                                  // the "closure kind"
+///     for<'x> fn(&'a &'x u32) -> &'x u32,  // the "closure signature"
+///     &'a String,                          // some upvar
+/// ]
+/// ```
+///
+/// here, there is one unique free region (`'a`) but it appears
+/// twice. We would "renumber" each occurence to a unique vid, as follows:
+///
+/// ```
+/// ClosureSubsts = [
+///     i8,                                  // the "closure kind"
+///     for<'x> fn(&'1 &'x u32) -> &'x u32,  // the "closure signature"
+///     &'2 String,                          // some upvar
+/// ]
+/// ```
+///
+/// Now the code might impose a requirement like `'1: '2`. When an
+/// instance of the closure is created, the corresponding free regions
+/// can be extracted from its type and constrained to have the given
+/// outlives relationship.
+#[derive(Clone, Debug)]
+pub struct ClosureRegionRequirements {
+    /// The number of external regions defined on the closure.  In our
+    /// example above, it would be 3 -- one for `'static`, then `'1`
+    /// and `'2`. This is just used for a sanity check later on, to
+    /// make sure that the number of regions we see at the callsite
+    /// matches.
+    pub num_external_vids: usize,
+
+    /// Requirements between the various free regions defined in
+    /// indices.
+    pub outlives_requirements: Vec<ClosureOutlivesRequirement>,
+}
+
+/// Indicates an outlives constraint between two free-regions declared
+/// on the closure.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct ClosureOutlivesRequirement {
+    // This region ...
+    pub free_region: ty::RegionVid,
+
+    // .. must outlive this one.
+    pub outlived_free_region: ty::RegionVid,
+
+    // If not, report an error here.
+    pub blame_span: Span,
+}
+
 /*
  * TypeFoldable implementations for MIR types
  */
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 429771cca98..3342d13dd6e 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -1339,26 +1339,27 @@ fn confirm_closure_candidate<'cx, 'gcx, 'tcx>(
     vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>)
     -> Progress<'tcx>
 {
-    let closure_typer = selcx.closure_typer();
-    let closure_type = closure_typer.fn_sig(vtable.closure_def_id)
-        .subst(selcx.tcx(), vtable.substs.substs);
+    let tcx = selcx.tcx();
+    let infcx = selcx.infcx();
+    let closure_sig_ty = vtable.substs.closure_sig_ty(vtable.closure_def_id, tcx);
+    let closure_sig = infcx.shallow_resolve(&closure_sig_ty).fn_sig(tcx);
     let Normalized {
-        value: closure_type,
+        value: closure_sig,
         obligations
     } = normalize_with_depth(selcx,
                              obligation.param_env,
                              obligation.cause.clone(),
                              obligation.recursion_depth+1,
-                             &closure_type);
+                             &closure_sig);
 
-    debug!("confirm_closure_candidate: obligation={:?},closure_type={:?},obligations={:?}",
+    debug!("confirm_closure_candidate: obligation={:?},closure_sig={:?},obligations={:?}",
            obligation,
-           closure_type,
+           closure_sig,
            obligations);
 
     confirm_callable_candidate(selcx,
                                obligation,
-                               closure_type,
+                               closure_sig,
                                util::TupleArgumentsFlag::No)
         .with_addl_obligations(vtable.nested)
         .with_addl_obligations(obligations)
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 0c4071b8b5d..e70de0e566e 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -3183,8 +3183,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                       substs: ty::ClosureSubsts<'tcx>)
                                       -> ty::PolyTraitRef<'tcx>
     {
-        let closure_type = self.infcx.fn_sig(closure_def_id)
-            .subst(self.tcx(), substs.substs);
+        let closure_type = self.infcx.closure_sig(closure_def_id, substs);
         let ty::Binder((trait_ref, _)) =
             self.tcx().closure_trait_ref_and_return_type(obligation.predicate.def_id(),
                                                          obligation.predicate.0.self_ty(), // (1)
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs
index 70636f8b6fe..177c25ac5db 100644
--- a/src/librustc/ty/instance.rs
+++ b/src/librustc/ty/instance.rs
@@ -10,7 +10,7 @@
 
 use hir::def_id::DefId;
 use ty::{self, Ty, TypeFoldable, Substs, TyCtxt};
-use ty::subst::{Kind, Subst};
+use ty::subst::Kind;
 use traits;
 use syntax::abi::Abi;
 use util::ppaux;
@@ -311,7 +311,7 @@ fn fn_once_adapter_instance<'a, 'tcx>(
     let self_ty = tcx.mk_closure_from_closure_substs(
         closure_did, substs);
 
-    let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs);
+    let sig = substs.closure_sig(closure_did, tcx);
     let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
     assert_eq!(sig.inputs().len(), 1);
     let substs = tcx.mk_substs([
diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs
index fb3600182d8..848d2a0a7de 100644
--- a/src/librustc/ty/maps/mod.rs
+++ b/src/librustc/ty/maps/mod.rs
@@ -190,8 +190,10 @@ define_maps! { <'tcx>
     [] fn coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (),
 
     [] fn borrowck: BorrowCheck(DefId) -> Rc<BorrowCheckResult>,
-    // FIXME: shouldn't this return a `Result<(), BorrowckErrors>` instead?
-    [] fn mir_borrowck: MirBorrowCheck(DefId) -> (),
+
+    /// Borrow checks the function body. If this is a closure, returns
+    /// additional requirements that the closure's creator must verify.
+    [] fn mir_borrowck: MirBorrowCheck(DefId) -> Option<mir::ClosureRegionRequirements>,
 
     /// Gets a complete map from all types to their inherent impls.
     /// Not meant to be used directly outside of coherence.
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 05aab27dc2a..4f733b8f68a 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -356,6 +356,8 @@ impl<'tcx> ClosureSubsts<'tcx> {
     /// Returns the closure kind for this closure; only usable outside
     /// of an inference context, because in that context we know that
     /// there are no type variables.
+    ///
+    /// If you have an inference context, use `infcx.closure_kind()`.
     pub fn closure_kind(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::ClosureKind {
         self.split(def_id, tcx).closure_kind_ty.to_opt_closure_kind().unwrap()
     }
@@ -363,6 +365,8 @@ impl<'tcx> ClosureSubsts<'tcx> {
     /// Extracts the signature from the closure; only usable outside
     /// of an inference context, because in that context we know that
     /// there are no type variables.
+    ///
+    /// If you have an inference context, use `infcx.closure_sig()`.
     pub fn closure_sig(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::PolyFnSig<'tcx> {
         match self.closure_sig_ty(def_id, tcx).sty {
             ty::TyFnPtr(sig) => sig,
@@ -646,6 +650,17 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
 pub struct Binder<T>(pub T);
 
 impl<T> Binder<T> {
+    /// Wraps `value` in a binder, asserting that `value` does not
+    /// contain any bound regions that would be bound by the
+    /// binder. This is commonly used to 'inject' a value T into a
+    /// different binding level.
+    pub fn dummy<'tcx>(value: T) -> Binder<T>
+        where T: TypeFoldable<'tcx>
+    {
+        assert!(!value.has_escaping_regions());
+        Binder(value)
+    }
+
     /// Skips the binder and returns the "bound" value. This is a
     /// risky thing to do because it's easy to get confused about
     /// debruijn indices and the like. It is usually better to
@@ -700,6 +715,32 @@ impl<T> Binder<T> {
             Some(self.skip_binder().clone())
         }
     }
+
+    /// Given two things that have the same binder level,
+    /// and an operation that wraps on their contents, execute the operation
+    /// and then wrap its result.
+    ///
+    /// `f` should consider bound regions at depth 1 to be free, and
+    /// anything it produces with bound regions at depth 1 will be
+    /// bound in the resulting return value.
+    pub fn fuse<U,F,R>(self, u: Binder<U>, f: F) -> Binder<R>
+        where F: FnOnce(T, U) -> R
+    {
+        ty::Binder(f(self.0, u.0))
+    }
+
+    /// Split the contents into two things that share the same binder
+    /// level as the original, returning two distinct binders.
+    ///
+    /// `f` should consider bound regions at depth 1 to be free, and
+    /// anything it produces with bound regions at depth 1 will be
+    /// bound in the resulting return values.
+    pub fn split<U,V,F>(self, f: F) -> (Binder<U>, Binder<V>)
+        where F: FnOnce(T) -> (U, V)
+    {
+        let (u, v) = f(self.0);
+        (ty::Binder(u), ty::Binder(v))
+    }
 }
 
 /// Represents the projection of an associated type. In explicit UFCS
@@ -799,6 +840,9 @@ impl<'tcx> PolyFnSig<'tcx> {
     pub fn input(&self, index: usize) -> ty::Binder<Ty<'tcx>> {
         self.map_bound_ref(|fn_sig| fn_sig.inputs()[index])
     }
+    pub fn inputs_and_output(&self) -> ty::Binder<&'tcx Slice<Ty<'tcx>>> {
+        self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output)
+    }
     pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
         self.map_bound_ref(|fn_sig| fn_sig.output().clone())
     }
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index b1e9bc7e47c..b0f61e9a191 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -1069,7 +1069,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(control: &CompileController,
 
         time(time_passes,
              "MIR borrow checking",
-             || for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id) });
+             || for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id); });
 
         time(time_passes,
              "MIR effect checking",
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 8d3491bd1d9..97d8a677fe8 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -19,6 +19,7 @@ use rustc::ty::maps::Providers;
 use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place};
 use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
 use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
+use rustc::mir::ClosureRegionRequirements;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::indexed_set::{self, IdxSetBuf};
@@ -51,7 +52,10 @@ pub fn provide(providers: &mut Providers) {
     };
 }
 
-fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
+fn mir_borrowck<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    def_id: DefId,
+) -> Option<ClosureRegionRequirements> {
     let input_mir = tcx.mir_validated(def_id);
     debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
 
@@ -59,21 +63,23 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
         !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir()
             && !tcx.sess.opts.debugging_opts.nll
     } {
-        return;
+        return None;
     }
 
-    tcx.infer_ctxt().enter(|infcx| {
+    let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
         let input_mir: &Mir = &input_mir.borrow();
-        do_mir_borrowck(&infcx, input_mir, def_id);
+        do_mir_borrowck(&infcx, input_mir, def_id)
     });
     debug!("mir_borrowck done");
+
+    opt_closure_req
 }
 
 fn do_mir_borrowck<'a, 'gcx, 'tcx>(
     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     input_mir: &Mir<'gcx>,
     def_id: DefId,
-) {
+) -> Option<ClosureRegionRequirements> {
     let tcx = infcx.tcx;
     let attributes = tcx.get_attrs(def_id);
     let param_env = tcx.param_env(def_id);
@@ -91,7 +97,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         let mir = &mut mir;
 
         // Replace all regions with fresh inference variables.
-        Some(nll::replace_regions_in_mir(infcx, def_id, mir))
+        Some(nll::replace_regions_in_mir(infcx, def_id, param_env, mir))
     };
     let mir = &mir;
 
@@ -177,8 +183,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
     ));
 
     // If we are in non-lexical mode, compute the non-lexical lifetimes.
-    let opt_regioncx = if let Some(free_regions) = free_regions {
-        Some(nll::compute_regions(
+    let (opt_regioncx, opt_closure_req) = if let Some(free_regions) = free_regions {
+        let (regioncx, opt_closure_req) = nll::compute_regions(
             infcx,
             def_id,
             free_regions,
@@ -186,10 +192,11 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
             param_env,
             &mut flow_inits,
             &mdpe.move_data,
-        ))
+        );
+        (Some(regioncx), opt_closure_req)
     } else {
         assert!(!tcx.sess.opts.debugging_opts.nll);
-        None
+        (None, None)
     };
     let flow_inits = flow_inits; // remove mut
 
@@ -226,6 +233,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
     );
 
     mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
+
+    opt_closure_req
 }
 
 #[allow(dead_code)]
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index 804f5e26875..4ff299efb95 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -9,11 +9,12 @@
 // except according to those terms.
 
 use rustc::hir::def_id::DefId;
-use rustc::mir::Mir;
+use rustc::mir::{ClosureRegionRequirements, Mir};
 use rustc::infer::InferCtxt;
 use rustc::ty::{self, RegionKind, RegionVid};
 use rustc::util::nodemap::FxHashMap;
 use std::collections::BTreeSet;
+use std::io;
 use transform::MirSource;
 use transform::type_check;
 use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
@@ -22,6 +23,7 @@ use dataflow::MaybeInitializedLvals;
 use dataflow::move_paths::MoveData;
 
 use util as mir_util;
+use util::pretty::{self, ALIGN};
 use self::mir_util::PassWhere;
 
 mod constraint_generation;
@@ -35,20 +37,26 @@ use self::region_infer::RegionInferenceContext;
 mod renumber;
 
 /// Rewrites the regions in the MIR to use NLL variables, also
-/// scraping out the set of free regions (e.g., region parameters)
+/// scraping out the set of universal regions (e.g., region parameters)
 /// declared on the function. That set will need to be given to
 /// `compute_regions`.
 pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
     infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
     def_id: DefId,
+    param_env: ty::ParamEnv<'tcx>,
     mir: &mut Mir<'tcx>,
 ) -> UniversalRegions<'tcx> {
-    // Compute named region information.
-    let universal_regions = universal_regions::universal_regions(infcx, def_id);
+    debug!("replace_regions_in_mir(def_id={:?})", def_id);
 
-    // Replace all regions with fresh inference variables.
+    // Compute named region information. This also renumbers the inputs/outputs.
+    let universal_regions = UniversalRegions::new(infcx, def_id, param_env);
+
+    // Replace all remaining regions with fresh inference variables.
     renumber::renumber_mir(infcx, &universal_regions, mir);
 
+    let source = MirSource::item(def_id);
+    mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, mir, |_, _| Ok(()));
+
     universal_regions
 }
 
@@ -63,7 +71,10 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     param_env: ty::ParamEnv<'gcx>,
     flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
     move_data: &MoveData<'tcx>,
-) -> RegionInferenceContext<'tcx> {
+) -> (
+    RegionInferenceContext<'tcx>,
+    Option<ClosureRegionRequirements>,
+) {
     // Run the MIR type-checker.
     let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
     let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
@@ -71,13 +82,8 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     // Create the region inference context, taking ownership of the region inference
     // data that was contained in `infcx`.
     let var_origins = infcx.take_region_var_origins();
-    let mut regioncx = RegionInferenceContext::new(var_origins, &universal_regions, mir);
-    subtype_constraint_generation::generate(
-        &mut regioncx,
-        &universal_regions,
-        mir,
-        constraint_sets,
-    );
+    let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir);
+    subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets);
 
     // Compute what is live where.
     let liveness = &LivenessResults {
@@ -110,13 +116,24 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     );
 
     // Solve the region constraints.
-    regioncx.solve(infcx, &mir);
+    let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
 
     // Dump MIR results into a file, if that is enabled. This let us
-    // write unit-tests.
-    dump_mir_results(infcx, liveness, MirSource::item(def_id), &mir, &regioncx);
+    // write unit-tests, as well as helping with debugging.
+    dump_mir_results(
+        infcx,
+        liveness,
+        MirSource::item(def_id),
+        &mir,
+        &regioncx,
+        &closure_region_requirements,
+    );
 
-    regioncx
+    // We also have a `#[rustc_nll]` annotation that causes us to dump
+    // information
+    dump_annotation(infcx, &mir, def_id, &regioncx, &closure_region_requirements);
+
+    (regioncx, closure_region_requirements)
 }
 
 struct LivenessResults {
@@ -130,6 +147,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
     source: MirSource,
     mir: &Mir<'tcx>,
     regioncx: &RegionInferenceContext,
+    closure_region_requirements: &Option<ClosureRegionRequirements>,
 ) {
     if !mir_util::dump_enabled(infcx.tcx, "nll", source) {
         return;
@@ -164,9 +182,17 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
     mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| {
         match pass_where {
             // Before the CFG, dump out the values for each region variable.
-            PassWhere::BeforeCFG => for region in regioncx.regions() {
-                writeln!(out, "| {:?}: {}", region, regioncx.region_value_str(region))?;
-            },
+            PassWhere::BeforeCFG => {
+                regioncx.dump_mir(out)?;
+
+                if let Some(closure_region_requirements) = closure_region_requirements {
+                    writeln!(out, "|")?;
+                    writeln!(out, "| Free Region Constraints")?;
+                    for_each_region_constraint(closure_region_requirements, &mut |msg| {
+                        writeln!(out, "| {}", msg)
+                    })?;
+                }
+            }
 
             // Before each basic block, dump out the values
             // that are live on entry to the basic block.
@@ -180,19 +206,96 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
                     &regular_liveness_per_location[&location],
                     &drop_liveness_per_location[&location],
                 );
-                writeln!(out, "            | Live variables at {:?}: {}", location, s)?;
+                writeln!(
+                    out,
+                    "{:ALIGN$} | Live variables on entry to {:?}: {}",
+                    "",
+                    location,
+                    s,
+                    ALIGN = ALIGN
+                )?;
             }
 
             PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
         }
         Ok(())
     });
+
+    // Also dump the inference graph constraints as a graphviz file.
+    let _: io::Result<()> = do catch {
+        let mut file =
+            pretty::create_dump_file(infcx.tcx, "regioncx.dot", None, "nll", &0, source)?;
+        regioncx.dump_graphviz(&mut file)
+    };
+}
+
+fn dump_annotation<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    mir: &Mir<'tcx>,
+    mir_def_id: DefId,
+    regioncx: &RegionInferenceContext,
+    closure_region_requirements: &Option<ClosureRegionRequirements>,
+) {
+    let tcx = infcx.tcx;
+    let base_def_id = tcx.closure_base_def_id(mir_def_id);
+    if !tcx.has_attr(base_def_id, "rustc_regions") {
+        return;
+    }
+
+    // When the enclosing function is tagged with `#[rustc_regions]`,
+    // we dump out various bits of state as warnings. This is useful
+    // for verifying that the compiler is behaving as expected.  These
+    // warnings focus on the closure region requirements -- for
+    // viewing the intraprocedural state, the -Zdump-mir output is
+    // better.
+
+    if let Some(closure_region_requirements) = closure_region_requirements {
+        let mut err = tcx.sess
+            .diagnostic()
+            .span_note_diag(mir.span, "External requirements");
+
+        regioncx.annotate(&mut err);
+
+        err.note(&format!(
+            "number of external vids: {}",
+            closure_region_requirements.num_external_vids
+        ));
+
+        // Dump the region constraints we are imposing *between* those
+        // newly created variables.
+        for_each_region_constraint(closure_region_requirements, &mut |msg| {
+            err.note(msg);
+            Ok(())
+        }).unwrap();
+
+        err.emit();
+    } else {
+        let mut err = tcx.sess
+            .diagnostic()
+            .span_note_diag(mir.span, "No external requirements");
+        regioncx.annotate(&mut err);
+        err.emit();
+    }
+}
+
+fn for_each_region_constraint(
+    closure_region_requirements: &ClosureRegionRequirements,
+    with_msg: &mut FnMut(&str) -> io::Result<()>,
+) -> io::Result<()> {
+    for req in &closure_region_requirements.outlives_requirements {
+        with_msg(&format!(
+            "where {:?}: {:?}",
+            req.free_region,
+            req.outlived_free_region,
+        ))?;
+    }
+    Ok(())
 }
 
 /// Right now, we piggy back on the `ReVar` to store our NLL inference
 /// regions. These are indexed with `RegionVid`. This method will
 /// assert that the region is a `ReVar` and extract its interal index.
-/// This is reasonable because in our MIR we replace all free regions
+/// This is reasonable because in our MIR we replace all universal regions
 /// with inference variables.
 pub trait ToRegionVid {
     fn to_region_vid(&self) -> RegionVid;
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs b/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs
new file mode 100644
index 00000000000..906efaef887
--- /dev/null
+++ b/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs
@@ -0,0 +1,48 @@
+// 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.
+
+//! As part of the NLL unit tests, you can annotate a function with
+//! `#[rustc_regions]`, and we will emit information about the region
+//! inference context and -- in particular -- the external constraints
+//! that this region imposes on others. The methods in this file
+//! handle the part about dumping the inference context internal
+//! state.
+
+use rustc::ty;
+use rustc_errors::DiagnosticBuilder;
+use super::RegionInferenceContext;
+
+impl<'gcx, 'tcx> RegionInferenceContext<'tcx> {
+    /// Write out our state into the `.mir` files.
+    pub(crate) fn annotate(&self, err: &mut DiagnosticBuilder<'_>) {
+        match self.universal_regions.defining_ty.sty {
+            ty::TyClosure(def_id, substs) => {
+                err.note(&format!(
+                    "defining type: {:?} with closure substs {:#?}",
+                    def_id,
+                    &substs.substs[..]
+                ));
+            }
+            ty::TyFnDef(def_id, substs) => {
+                err.note(&format!(
+                    "defining type: {:?} with substs {:#?}",
+                    def_id,
+                    &substs[..]
+                ));
+            }
+            _ => {
+                err.note(&format!(
+                    "defining type: {:?}",
+                    self.universal_regions.defining_ty
+                ));
+            }
+        }
+    }
+}
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
new file mode 100644
index 00000000000..5477308bde9
--- /dev/null
+++ b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs
@@ -0,0 +1,100 @@
+// 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.
+
+//! As part of generating the regions, if you enable `-Zdump-mir=nll`,
+//! we will generate an annotated copy of the MIR that includes the
+//! state of region inference. This code handles emitting the region
+//! context internal state.
+
+use std::io::{self, Write};
+use super::{Constraint, RegionInferenceContext};
+
+// Room for "'_#NNNNr" before things get misaligned.
+// Easy enough to fix if this ever doesn't seem like
+// enough.
+const REGION_WIDTH: usize = 8;
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+    /// Write out our state into the `.mir` files.
+    pub(crate) fn dump_mir(&self, out: &mut Write) -> io::Result<()> {
+        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();
+                let outlived_by = self.universal_regions.regions_outlived_by(region);
+                writeln!(
+                    out,
+                    "| {r:rw$} | {c:cw$} | {ob}",
+                    r = format!("{:?}", region),
+                    rw = REGION_WIDTH,
+                    c = format!("{:?}", classification),
+                    cw = 8, // "External" at most
+                    ob = format!("{:?}", outlived_by)
+                )?;
+            }
+        }
+
+        writeln!(out, "|")?;
+        writeln!(out, "| Inferred Region Values")?;
+        for region in self.regions() {
+            writeln!(
+                out,
+                "| {r:rw$} | {v}",
+                r = format!("{:?}", region),
+                rw = REGION_WIDTH,
+                v = self.region_value_str(region),
+            )?;
+        }
+
+        writeln!(out, "|")?;
+        writeln!(out, "| Inference Constraints")?;
+        self.for_each_constraint(&mut |msg| writeln!(out, "| {}", msg))?;
+
+        Ok(())
+    }
+
+    /// Debugging aid: Invokes the `with_msg` callback repeatedly with
+    /// our internal region constraints.  These are dumped into the
+    /// -Zdump-mir file so that we can figure out why the region
+    /// inference resulted in the values that it did when debugging.
+    fn for_each_constraint(
+        &self,
+        with_msg: &mut FnMut(&str) -> io::Result<()>,
+    ) -> io::Result<()> {
+        for region in self.definitions.indices() {
+            let value = self.region_value_str_from_matrix(&self.liveness_constraints, region);
+            if value != "{}" {
+                with_msg(&format!("{:?} live at {}", region, value))?;
+            }
+        }
+
+        let mut constraints: Vec<_> = self.constraints.iter().collect();
+        constraints.sort();
+        for constraint in &constraints {
+            let Constraint {
+                sup,
+                sub,
+                point,
+                span,
+            } = constraint;
+            with_msg(&format!(
+                "{:?}: {:?} @ {:?} due to {:?}",
+                sup,
+                sub,
+                point,
+                span
+            ))?;
+        }
+
+        Ok(())
+    }
+}
+
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
new file mode 100644
index 00000000000..7923b159d80
--- /dev/null
+++ b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
@@ -0,0 +1,71 @@
+// Copyright 2014 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.
+
+//! This module provides linkage between RegionInferenceContext and
+//! libgraphviz traits, specialized to attaching borrowck analysis
+//! data to rendered labels.
+
+use dot::{self, IntoCow};
+use rustc_data_structures::indexed_vec::Idx;
+use std::borrow::Cow;
+use std::io::{self, Write};
+use super::*;
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+    /// Write out the region constraint graph.
+    pub(crate) fn dump_graphviz(&self, mut w: &mut Write) -> io::Result<()> {
+        dot::render(self, &mut w)
+    }
+}
+
+impl<'this, 'tcx> dot::Labeller<'this> for RegionInferenceContext<'tcx> {
+    type Node = RegionVid;
+    type Edge = Constraint;
+
+    fn graph_id(&'this self) -> dot::Id<'this> {
+        dot::Id::new(format!("RegionInferenceContext")).unwrap()
+    }
+    fn node_id(&'this self, n: &RegionVid) -> dot::Id<'this> {
+        dot::Id::new(format!("r{}", n.index())).unwrap()
+    }
+    fn node_shape(&'this self, _node: &RegionVid) -> Option<dot::LabelText<'this>> {
+        Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
+    }
+    fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
+        dot::LabelText::LabelStr(format!("{:?}", n).into_cow())
+    }
+    fn edge_label(&'this self, e: &Constraint) -> dot::LabelText<'this> {
+        dot::LabelText::LabelStr(format!("{:?}", e.point).into_cow())
+    }
+}
+
+impl<'this, 'tcx> dot::GraphWalk<'this> for RegionInferenceContext<'tcx> {
+    type Node = RegionVid;
+    type Edge = Constraint;
+
+    fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> {
+        let vids: Vec<RegionVid> = self.definitions.indices().collect();
+        vids.into_cow()
+    }
+    fn edges(&'this self) -> dot::Edges<'this, Constraint> {
+        (&self.constraints[..]).into_cow()
+    }
+
+    // Render `a: b` as `a <- b`, indicating the flow
+    // of data during inference.
+
+    fn source(&'this self, edge: &Constraint) -> RegionVid {
+        edge.sub
+    }
+
+    fn target(&'this self, edge: &Constraint) -> RegionVid {
+        edge.sup
+    }
+}
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
index d1faaf75a53..b2e2ccc5d0b 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -9,12 +9,13 @@
 // except according to those terms.
 
 use super::universal_regions::UniversalRegions;
+use rustc::hir::def_id::DefId;
 use rustc::infer::InferCtxt;
-use rustc::infer::RegionVariableOrigin;
 use rustc::infer::NLLRegionVariableOrigin;
+use rustc::infer::RegionVariableOrigin;
+use rustc::infer::SubregionOrigin;
 use rustc::infer::region_constraints::VarOrigins;
-use rustc::infer::outlives::free_region_map::FreeRegionMap;
-use rustc::mir::{Location, Mir};
+use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir};
 use rustc::ty::{self, RegionVid};
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_data_structures::fx::FxHashSet;
@@ -24,6 +25,10 @@ use std::collections::BTreeMap;
 use std::fmt;
 use syntax_pos::Span;
 
+mod annotation;
+mod dump_mir;
+mod graphviz;
+
 pub struct RegionInferenceContext<'tcx> {
     /// Contains the definition for every region variable.  Region
     /// variables are identified by their index (`RegionVid`). The
@@ -52,12 +57,9 @@ pub struct RegionInferenceContext<'tcx> {
     /// the free regions.)
     point_indices: BTreeMap<Location, usize>,
 
-    /// Number of universally quantified regions. This is used to
-    /// determine the meaning of the bits in `inferred_values` and
-    /// friends.
-    num_universal_regions: usize,
-
-    free_region_map: &'tcx FreeRegionMap<'tcx>,
+    /// Information about the universally quantified regions in scope
+    /// on this function and their (known) relations to one another.
+    universal_regions: UniversalRegions<'tcx>,
 }
 
 struct RegionDefinition<'tcx> {
@@ -67,9 +69,15 @@ struct RegionDefinition<'tcx> {
     /// late-bound-regions).
     origin: RegionVariableOrigin,
 
-    /// If this is a free-region, then this is `Some(X)` where `X` is
-    /// the name of the region.
-    name: Option<ty::Region<'tcx>>,
+    /// 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,
+
+    /// If this is 'static or an early-bound region, then this is
+    /// `Some(X)` where `X` is the name of the region.
+    external_name: Option<ty::Region<'tcx>>,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -98,11 +106,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// regions defined in `universal_regions`.
     pub fn new(
         var_origins: VarOrigins,
-        universal_regions: &UniversalRegions<'tcx>,
+        universal_regions: UniversalRegions<'tcx>,
         mir: &Mir<'tcx>,
     ) -> Self {
         let num_region_variables = var_origins.len();
-        let num_universal_regions = universal_regions.indices.len();
+        let num_universal_regions = universal_regions.len();
 
         let mut num_points = 0;
         let mut point_indices = BTreeMap::new();
@@ -133,11 +141,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             inferred_values: None,
             constraints: Vec::new(),
             point_indices,
-            num_universal_regions,
-            free_region_map: universal_regions.free_region_map,
+            universal_regions,
         };
 
-        result.init_universal_regions(universal_regions);
+        result.init_universal_regions();
 
         result
     }
@@ -159,25 +166,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ///     R1 = { CFG, R0, R1 } // 'b
     ///
     /// Here, R0 represents `'a`, and it contains (a) the entire CFG
-    /// and (b) any free 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, universal_regions: &UniversalRegions<'tcx>) {
-        let UniversalRegions {
-            indices,
-            free_region_map: _,
-        } = universal_regions;
+    /// 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) {
+        // Update the names (if any)
+        for (external_name, variable) in self.universal_regions.named_universal_regions() {
+            self.definitions[variable].external_name = Some(external_name);
+        }
 
         // For each universally quantified region X:
-        for (free_region, &variable) in indices {
+        for variable in self.universal_regions.universal_regions() {
             // These should be free-region variables.
             assert!(match self.definitions[variable].origin {
                 RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
                 _ => false,
             });
 
-            // Initialize the name and a few other details.
-            self.definitions[variable].name = Some(free_region);
+            self.definitions[variable].is_universal = true;
 
             // Add all nodes in the CFG to liveness constraints
             for (_location, point_index) in &self.point_indices {
@@ -196,6 +202,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         self.definitions.indices()
     }
 
+    /// Given a universal region in scope on the MIR, returns the
+    /// corresponding index.
+    ///
+    /// (Panics if `r` is not a registered universal region.)
+    pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
+        self.universal_regions.to_region_vid(r)
+    }
+
     /// Returns true if the region `r` contains the point `p`.
     ///
     /// Panics if called before `solve()` executes,
@@ -237,19 +251,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             .as_ref()
             .expect("region values not yet inferred");
 
+        self.region_value_str_from_matrix(inferred_values, r)
+    }
+
+    fn region_value_str_from_matrix(&self,
+                                    matrix: &BitMatrix,
+                                    r: RegionVid) -> String {
         let mut result = String::new();
         result.push_str("{");
         let mut sep = "";
 
         for &point in self.point_indices.keys() {
-            if self.region_contains_point_in_matrix(inferred_values, r, point) {
+            if self.region_contains_point_in_matrix(matrix, r, point) {
                 result.push_str(&format!("{}{:?}", sep, point));
                 sep = ", ";
             }
         }
 
-        for fr in (0..self.num_universal_regions).map(RegionVid::new) {
-            if self.region_contains_region_in_matrix(inferred_values, r, fr) {
+        for fr in (0..self.universal_regions.len()).map(RegionVid::new) {
+            if self.region_contains_region_in_matrix(matrix, r, fr) {
                 result.push_str(&format!("{}{:?}", sep, fr));
                 sep = ", ";
             }
@@ -289,8 +309,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     }
 
     /// Perform region inference.
-    pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
+    pub(super) fn solve(
+        &mut self,
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        mir: &Mir<'tcx>,
+        mir_def_id: DefId,
+    ) -> Option<ClosureRegionRequirements> {
         assert!(self.inferred_values.is_none(), "values already inferred");
+        let tcx = infcx.tcx;
 
         // Find the minimal regions that can solve the constraints. This is infallible.
         self.propagate_constraints(mir);
@@ -310,57 +336,135 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         // The universal regions are always found in a prefix of the
         // full list.
-        let free_region_definitions = self.definitions
+        let universal_definitions = self.definitions
             .iter_enumerated()
-            .take_while(|(_, fr_definition)| fr_definition.name.is_some());
+            .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.
+        let mut outlives_requirements = vec![];
+        for (fr, _) in universal_definitions {
+            self.check_universal_region(infcx, fr, &mut outlives_requirements);
+        }
 
-        for (fr, fr_definition) in free_region_definitions {
-            self.check_free_region(infcx, fr, fr_definition);
+        // If this is not a closure, then there is no caller to which we can
+        // "pass the buck". So if there are any outlives-requirements that were
+        // not satisfied, we just have to report a hard error here.
+        if !tcx.is_closure(mir_def_id) {
+            for outlives_requirement in outlives_requirements {
+                self.report_error(
+                    infcx,
+                    outlives_requirement.free_region,
+                    outlives_requirement.outlived_free_region,
+                    outlives_requirement.blame_span,
+                );
+            }
+            return None;
         }
+
+        let num_external_vids = self.universal_regions.num_global_and_external_regions();
+
+        Some(ClosureRegionRequirements {
+            num_external_vids,
+            outlives_requirements,
+        })
     }
 
-    fn check_free_region(
+    /// Check the final value for the free region `fr` to see if it
+    /// grew too large. In particular, examine what `end(X)` points
+    /// wound up in `fr`'s final value; for each `end(X)` where `X !=
+    /// fr`, we want to check that `fr: X`. If not, that's either an
+    /// error, or something we have to propagate to our creator.
+    ///
+    /// Things that are to be propagated are accumulated into the
+    /// `outlives_requirements` vector.
+    fn check_universal_region(
         &self,
         infcx: &InferCtxt<'_, '_, 'tcx>,
         longer_fr: RegionVid,
-        longer_definition: &RegionDefinition<'tcx>,
+        outlives_requirements: &mut Vec<ClosureOutlivesRequirement>,
     ) {
         let inferred_values = self.inferred_values.as_ref().unwrap();
-        let longer_name = longer_definition.name.unwrap();
         let longer_value = inferred_values.iter(longer_fr.index());
 
-        // Find every region `shorter` such that `longer: shorter`
-        // (because `longer` includes `end(shorter)`).
-        for shorter_fr in longer_value.take_while(|&i| i < self.num_universal_regions) {
-            let shorter_fr = RegionVid::new(shorter_fr);
+        debug!("check_universal_region(fr={:?})", longer_fr);
 
-            // `fr` includes `end(fr)`, that's not especially
-            // interesting.
-            if longer_fr == shorter_fr {
+        // Find every region `o` such that `fr: o`
+        // (because `fr` includes `end(o)`).
+        let shorter_frs = longer_value
+            .take_while(|&i| i < self.universal_regions.len())
+            .map(RegionVid::new);
+        for shorter_fr in shorter_frs {
+            // If it is known that `fr: o`, carry on.
+            if self.universal_regions.outlives(longer_fr, shorter_fr) {
                 continue;
             }
 
-            let shorter_definition = &self.definitions[shorter_fr];
-            let shorter_name = shorter_definition.name.unwrap();
-
-            // Check that `o <= fr`. If not, report an error.
-            if !self.free_region_map
-                .sub_free_regions(shorter_name, longer_name)
-            {
-                // FIXME: worst error msg ever
-                let blame_span = self.blame_span(longer_fr, shorter_fr);
-                infcx.tcx.sess.span_err(
-                    blame_span,
-                    &format!(
-                        "free region `{}` does not outlive `{}`",
-                        longer_name,
-                        shorter_name
-                    ),
+            debug!(
+                "check_universal_region: fr={:?} does not outlive shorter_fr={:?}",
+                longer_fr,
+                shorter_fr,
+            );
+
+            let blame_span = self.blame_span(longer_fr, shorter_fr);
+
+            // Shrink `fr` until we find a non-local region (if we do).
+            // We'll call that `fr-` -- it's ever so slightly smaller than `fr`.
+            if let Some(fr_minus) = self.universal_regions.non_local_lower_bound(longer_fr) {
+                debug!("check_universal_region: fr_minus={:?}", fr_minus);
+
+                // Grow `shorter_fr` until we find a non-local
+                // regon. (We always will.)  We'll call that
+                // `shorter_fr+` -- it's ever so slightly larger than
+                // `fr`.
+                let shorter_fr_plus = self.universal_regions.non_local_upper_bound(shorter_fr);
+                debug!(
+                    "check_universal_region: shorter_fr_plus={:?}",
+                    shorter_fr_plus
                 );
+
+                // Push the constraint `fr-: shorter_fr+`
+                outlives_requirements.push(ClosureOutlivesRequirement {
+                    free_region: fr_minus,
+                    outlived_free_region: shorter_fr_plus,
+                    blame_span: blame_span,
+                });
+                return;
             }
+
+            // If we could not shrink `fr` to something smaller that
+            // the external users care about, then we can't pass the
+            // buck; just report an error.
+            self.report_error(infcx, longer_fr, shorter_fr, blame_span);
         }
     }
 
+    fn report_error(
+        &self,
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        fr: RegionVid,
+        outlived_fr: RegionVid,
+        blame_span: Span,
+    ) {
+        // Obviously uncool error reporting.
+
+        let fr_string = match self.definitions[fr].external_name {
+            Some(r) => format!("free region `{}`", r),
+            None => format!("free region `{:?}`", fr),
+        };
+
+        let outlived_fr_string = match self.definitions[outlived_fr].external_name {
+            Some(r) => format!("free region `{}`", r),
+            None => format!("free region `{:?}`", outlived_fr),
+        };
+
+        infcx.tcx.sess.span_err(
+            blame_span,
+            &format!("{} does not outlive {}", fr_string, outlived_fr_string,),
+        );
+    }
+
     /// Propagate the region constraints: this will grow the values
     /// for each region variable until all the constraints are
     /// satisfied. Note that some values may grow **too** large to be
@@ -421,8 +525,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         stack.push(start_point);
         while let Some(p) = stack.pop() {
-            debug!("        copy: p={:?}", p);
-
             if !self.region_contains_point_in_matrix(inferred_values, from_region, p) {
                 debug!("            not in from-region");
                 continue;
@@ -464,7 +566,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 // and make sure they are included in the `to_region`.
                 let universal_region_indices = inferred_values
                     .iter(from_region.index())
-                    .take_while(|&i| i < self.num_universal_regions)
+                    .take_while(|&i| i < self.universal_regions.len())
                     .collect::<Vec<_>>();
                 for fr in &universal_region_indices {
                     changed |= inferred_values.add(to_region.index(), *fr);
@@ -535,7 +637,11 @@ impl<'tcx> RegionDefinition<'tcx> {
         // Create a new region definition. Note that, for free
         // regions, these fields get updated later in
         // `init_universal_regions`.
-        Self { origin, name: None }
+        Self {
+            origin,
+            is_universal: false,
+            external_name: None,
+        }
     }
 }
 
@@ -551,3 +657,70 @@ impl fmt::Debug for Constraint {
         )
     }
 }
+
+pub trait ClosureRegionRequirementsExt {
+    fn apply_requirements<'tcx>(
+        &self,
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        location: Location,
+        closure_def_id: DefId,
+        closure_substs: ty::ClosureSubsts<'tcx>,
+    );
+}
+
+impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
+    /// Given an instance T of the closure type, this method
+    /// instantiates the "extra" requirements that we computed for the
+    /// closure into the inference context. This has the effect of
+    /// adding new subregion obligations to existing variables.
+    ///
+    /// As described on `ClosureRegionRequirements`, the extra
+    /// requirements are expressed in terms of regionvids that index
+    /// into the free regions that appear on the closure type. So, to
+    /// do this, we first copy those regions out from the type T into
+    /// a vector. Then we can just index into that vector to extract
+    /// out the corresponding region from T and apply the
+    /// requirements.
+    fn apply_requirements<'tcx>(
+        &self,
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        location: Location,
+        closure_def_id: DefId,
+        closure_substs: ty::ClosureSubsts<'tcx>,
+    ) {
+        let tcx = infcx.tcx;
+
+        debug!(
+            "apply_requirements(location={:?}, closure_def_id={:?}, closure_substs={:?})",
+            location,
+            closure_def_id,
+            closure_substs
+        );
+
+        // Get Tu.
+        let user_closure_ty = tcx.mk_closure(closure_def_id, closure_substs);
+        debug!("apply_requirements: user_closure_ty={:?}", user_closure_ty);
+
+        // Extract the values of the free regions in `user_closure_ty`
+        // into a vector.  These are the regions that we will be
+        // relating to one another.
+        let closure_mapping =
+            UniversalRegions::closure_mapping(infcx, user_closure_ty, self.num_external_vids);
+        debug!("apply_requirements: closure_mapping={:?}", closure_mapping);
+
+        // Create the predicates.
+        for outlives_requirement in &self.outlives_requirements {
+            let region = closure_mapping[outlives_requirement.free_region];
+            let outlived_region = closure_mapping[outlives_requirement.outlived_free_region];
+            debug!(
+                "apply_requirements: region={:?} outlived_region={:?} outlives_requirements={:?}",
+                region,
+                outlived_region,
+                outlives_requirement
+            );
+            // FIXME, this origin is not entirely suitable.
+            let origin = SubregionOrigin::CallRcvr(outlives_requirement.blame_span);
+            infcx.sub_regions(origin, outlived_region, region);
+        }
+    }
+}
diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs
index 371419da024..1262c238a13 100644
--- a/src/librustc_mir/borrow_check/nll/renumber.rs
+++ b/src/librustc_mir/borrow_check/nll/renumber.rs
@@ -8,10 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_data_structures::indexed_vec::Idx;
 use rustc::ty::subst::Substs;
-use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
+use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable};
 use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
+use rustc::mir::RETURN_PLACE;
 use rustc::mir::visit::{MutVisitor, TyContext};
 use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
 
@@ -25,25 +26,24 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
     universal_regions: &UniversalRegions<'tcx>,
     mir: &mut Mir<'tcx>,
 ) {
-    // Create inference variables for each of the free regions
-    // declared on the function signature.
-    let free_region_inference_vars = (0..universal_regions.indices.len())
-        .map(RegionVid::new)
-        .map(|vid_expected| {
-            let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
-            assert_eq!(vid_expected, r.to_region_vid());
-            r
-        })
-        .collect();
-
     debug!("renumber_mir()");
-    debug!("renumber_mir: universal_regions={:#?}", universal_regions);
     debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
 
+    // Update the return type and types of the arguments based on the
+    // `universal_regions` computation.
+    debug!("renumber_mir: output_ty={:?}", universal_regions.output_ty);
+    mir.local_decls[RETURN_PLACE].ty = universal_regions.output_ty;
+    for (&input_ty, local) in universal_regions
+        .input_tys
+        .iter()
+        .zip((1..).map(Local::new))
+    {
+        debug!("renumber_mir: input_ty={:?} local={:?}", input_ty, local);
+        mir.local_decls[local].ty = input_ty;
+    }
+
     let mut visitor = NLLVisitor {
         infcx,
-        universal_regions,
-        free_region_inference_vars,
         arg_count: mir.arg_count,
     };
     visitor.visit_mir(mir);
@@ -51,8 +51,6 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
 
 struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
-    universal_regions: &'a UniversalRegions<'tcx>,
-    free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
     arg_count: usize,
 }
 
@@ -74,20 +72,17 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
             })
     }
 
-    /// Renumbers the regions appearing in `value`, but those regions
-    /// are expected to be free regions from the function signature.
-    fn renumber_universal_regions<T>(&mut self, value: &T) -> T
+    /// Checks that all the regions appearing in `value` have already
+    /// been renumbered. `FreeRegions` code should have done this.
+    fn assert_free_regions_are_renumbered<T>(&self, value: &T)
     where
         T: TypeFoldable<'tcx>,
     {
-        debug!("renumber_universal_regions(value={:?})", value);
+        debug!("assert_free_regions_are_renumbered(value={:?})", value);
 
-        self.infcx
-            .tcx
-            .fold_regions(value, &mut false, |region, _depth| {
-                let index = self.universal_regions.indices[&region];
-                self.free_region_inference_vars[index]
-            })
+        self.infcx.tcx.for_each_free_region(value, |region| {
+            region.to_region_vid(); // will panic if `region` is not renumbered
+        });
     }
 
     fn is_argument_or_return_slot(&self, local: Local) -> bool {
@@ -110,12 +105,12 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
             ty_context
         );
 
-        let old_ty = *ty;
-        *ty = if is_arg {
-            self.renumber_universal_regions(&old_ty)
+        if is_arg {
+            self.assert_free_regions_are_renumbered(ty);
         } else {
-            self.renumber_regions(ty_context, &old_ty)
-        };
+            *ty = self.renumber_regions(ty_context, ty);
+        }
+
         debug!("visit_ty: ty={:?}", ty);
     }
 
@@ -138,6 +133,11 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
         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_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
         debug!(
             "visit_closure_substs(substs={:?}, location={:?})",
diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
index dbae40be6fe..c98a94fa8bc 100644
--- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
@@ -15,7 +15,6 @@ use rustc::ty;
 use transform::type_check::MirTypeckRegionConstraints;
 use transform::type_check::OutlivesSet;
 
-use super::universal_regions::UniversalRegions;
 use super::region_infer::RegionInferenceContext;
 
 /// When the MIR type-checker executes, it validates all the types in
@@ -25,20 +24,17 @@ use super::region_infer::RegionInferenceContext;
 /// them into the NLL `RegionInferenceContext`.
 pub(super) fn generate<'tcx>(
     regioncx: &mut RegionInferenceContext<'tcx>,
-    universal_regions: &UniversalRegions<'tcx>,
     mir: &Mir<'tcx>,
     constraints: &MirTypeckRegionConstraints<'tcx>,
 ) {
     SubtypeConstraintGenerator {
         regioncx,
-        universal_regions,
         mir,
     }.generate(constraints);
 }
 
 struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
     regioncx: &'cx mut RegionInferenceContext<'tcx>,
-    universal_regions: &'cx UniversalRegions<'tcx>,
     mir: &'cx Mir<'tcx>,
 }
 
@@ -106,7 +102,7 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
         if let ty::ReVar(vid) = r {
             *vid
         } else {
-            self.universal_regions.indices[&r]
+            self.regioncx.to_region_vid(r)
         }
     }
 }
diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs
index 3be95a114c3..857a620cead 100644
--- a/src/librustc_mir/borrow_check/nll/universal_regions.rs
+++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs
@@ -22,69 +22,681 @@
 //! The code in this file doesn't *do anything* with those results; it
 //! just returns them for other code to use.
 
+use rustc::hir::HirId;
 use rustc::hir::def_id::DefId;
-use rustc::infer::InferCtxt;
-use rustc::infer::outlives::free_region_map::FreeRegionMap;
-use rustc::ty::{self, RegionVid};
+use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
+use rustc::infer::region_constraints::GenericKind;
+use rustc::infer::outlives::bounds::{self, OutlivesBound};
+use rustc::ty::{self, RegionVid, Ty, TyCtxt};
+use rustc::ty::fold::TypeFoldable;
 use rustc::ty::subst::Substs;
 use rustc::util::nodemap::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_data_structures::transitive_relation::TransitiveRelation;
+use std::iter;
+use syntax::ast;
+
+use super::ToRegionVid;
 
 #[derive(Debug)]
 pub struct UniversalRegions<'tcx> {
-    /// Given a universally quantified region defined on this function
-    /// (either early- or late-bound), this maps it to its internal
-    /// region index. When the region context is created, the first N
-    /// variables will be created based on these indices.
-    pub indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
-
-    /// The map from the typeck tables telling us how to relate universal regions.
-    pub free_region_map: &'tcx FreeRegionMap<'tcx>,
+    indices: UniversalRegionIndices<'tcx>,
+
+    /// The vid assigned to `'static`
+    pub fr_static: RegionVid,
+
+    /// We create region variables such that they are ordered by their
+    /// `RegionClassification`. The first block are globals, then
+    /// externals, then locals. So things from:
+    /// - `FIRST_GLOBAL_INDEX..first_extern_index` are global;
+    /// - `first_extern_index..first_local_index` are external; and
+    /// - first_local_index..num_universals` are local.
+    first_extern_index: usize,
+
+    /// See `first_extern_index`.
+    first_local_index: usize,
+
+    /// The total number of universal region variables instantiated.
+    num_universals: usize,
+
+    /// The "defining" type for this function, with all universal
+    /// regions instantiated.  For a closure or generator, this is the
+    /// closure type, but for a top-level function it's the `TyFnDef`.
+    pub defining_ty: Ty<'tcx>,
+
+    /// The return type of this function, with all regions replaced
+    /// by their universal `RegionVid` equivalents.
+    pub output_ty: Ty<'tcx>,
+
+    /// The fully liberated input types of this function, with all
+    /// regions replaced by their universal `RegionVid` equivalents.
+    pub input_tys: &'tcx [Ty<'tcx>],
+
+    /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to
+    /// be true. These encode relationships like `T: 'a` that are
+    /// added via implicit bounds.
+    ///
+    /// Each region here is guaranteed to be a key in the `indices`
+    /// map.  We use the "original" regions (i.e., the keys from the
+    /// map, and not the values) because the code in
+    /// `process_registered_region_obligations` has some special-cased
+    /// logic expecting to see (e.g.) `ReStatic`, and if we supplied
+    /// our special inference variable there, we would mess that up.
+    pub region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
+
+    relations: UniversalRegionRelations,
+}
+
+#[derive(Debug)]
+struct UniversalRegionIndices<'tcx> {
+    /// For those regions that may appear in the parameter environment
+    /// ('static and early-bound regions), we maintain a map from the
+    /// `ty::Region` to the internal `RegionVid` we are using. This is
+    /// used because trait matching and type-checking will feed us
+    /// region constraints that reference those regions and we need to
+    /// be able to map them our internal `RegionVid`. This is
+    /// basically equivalent to a `Substs`, except that it also
+    /// contains an entry for `ReStatic` -- it might be nice to just
+    /// use a substs, and then handle `ReStatic` another way.
+    indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
+}
+
+#[derive(Debug)]
+struct UniversalRegionRelations {
+    /// Stores the outlives relations that are known to hold from the
+    /// implied bounds, in-scope where clauses, and that sort of
+    /// thing.
+    outlives: TransitiveRelation<RegionVid>,
+
+    /// This is the `<=` relation; that is, if `a: b`, then `b <= a`,
+    /// and we store that here. This is useful when figuring out how
+    /// to express some local region in terms of external regions our
+    /// caller will understand.
+    inverse_outlives: TransitiveRelation<RegionVid>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum RegionClassification {
+    /// A **global** region is one that can be named from
+    /// anywhere. There is only one, `'static`.
+    Global,
+
+    /// An **external** region is only relevant for closures. In that
+    /// case, it refers to regions that are free in the closure type
+    /// -- basically, something bound in the surrounding context.
+    ///
+    /// Consider this example:
+    ///
+    /// ```
+    /// fn foo<'a, 'b>(a: &'a u32, b: &'b u32, c: &'static u32) {
+    ///   let closure = for<'x> |x: &'x u32| { .. };
+    ///                 ^^^^^^^ pretend this were legal syntax
+    ///                         for declaring a late-bound region in
+    ///                         a closure signature
+    /// }
+    /// ```
+    ///
+    /// Here, the lifetimes `'a` and `'b` would be **external** to the
+    /// closure.
+    ///
+    /// If we are not analyzing a closure, there are no external
+    /// lifetimes.
+    External,
+
+    /// A **local** lifetime is one about which we know the full set
+    /// of relevant constraints (that is, relationships to other named
+    /// regions).  For a closure, this includes any region bound in
+    /// the closure's signature.  For a fn item, this includes all
+    /// regions other than global ones.
+    ///
+    /// Continuing with the example from `External`, if we were
+    /// analyzing the closure, then `'x` would be local (and `'a` and
+    /// `'b` are external).  If we are analyzing the function item
+    /// `foo`, then `'a` and `'b` are local (and `'x` is not in
+    /// scope).
+    Local,
 }
 
-pub fn universal_regions<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    item_def_id: DefId,
-) -> UniversalRegions<'tcx> {
-    debug!("universal_regions(item_def_id={:?})", item_def_id);
+const FIRST_GLOBAL_INDEX: usize = 0;
+
+impl<'tcx> UniversalRegions<'tcx> {
+    /// Creates a new and fully initialized `UniversalRegions` that
+    /// contains indices for all the free regions found in the given
+    /// MIR -- that is, all the regions that appear in the function's
+    /// signature. This will also compute the relationships that are
+    /// known between those regions.
+    pub fn new(
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        mir_def_id: DefId,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Self {
+        let tcx = infcx.tcx;
+        let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).unwrap();
+        let mir_hir_id = tcx.hir.node_to_hir_id(mir_node_id);
+        UniversalRegionsBuilder {
+            infcx,
+            mir_def_id,
+            mir_node_id,
+            mir_hir_id,
+            param_env,
+            region_bound_pairs: vec![],
+            relations: UniversalRegionRelations {
+                outlives: TransitiveRelation::new(),
+                inverse_outlives: TransitiveRelation::new(),
+            },
+        }.build()
+    }
+
+    /// Given a reference to a closure type, extracts all the values
+    /// from its free regions and returns a vector with them. This is
+    /// used when the closure's creator checks that the
+    /// `ClosureRegionRequirements` are met. The requirements from
+    /// `ClosureRegionRequirements` are expressed in terms of
+    /// `RegionVid` entries that map into the returned vector `V`: so
+    /// if the `ClosureRegionRequirements` contains something like
+    /// `'1: '2`, then the caller would impose the constraint that
+    /// `V[1]: V[2]`.
+    pub fn closure_mapping(
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        closure_ty: Ty<'tcx>,
+        expected_num_vars: usize,
+    ) -> IndexVec<RegionVid, ty::Region<'tcx>> {
+        let mut region_mapping = IndexVec::with_capacity(expected_num_vars);
+        region_mapping.push(infcx.tcx.types.re_static);
+        infcx.tcx.for_each_free_region(&closure_ty, |fr| {
+            region_mapping.push(fr);
+        });
+
+        assert_eq!(
+            region_mapping.len(),
+            expected_num_vars,
+            "index vec had unexpected number of variables"
+        );
 
-    let mut indices = FxHashMap();
+        region_mapping
+    }
 
-    // `'static` is always free.
-    insert_free_region(&mut indices, infcx.tcx.types.re_static);
+    /// True if `r` is a member of this set of universal regions.
+    pub fn is_universal_region(&self, r: RegionVid) -> bool {
+        (FIRST_GLOBAL_INDEX..self.num_universals).contains(r.index())
+    }
 
-    // Extract the early regions.
-    let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
-    for item_subst in item_substs {
-        if let Some(region) = item_subst.as_region() {
-            insert_free_region(&mut indices, region);
+    /// Classifies `r` as a universal region, returning `None` if this
+    /// is not a member of this set of universal regions.
+    pub fn region_classification(&self, r: RegionVid) -> Option<RegionClassification> {
+        let index = r.index();
+        if (FIRST_GLOBAL_INDEX..self.first_extern_index).contains(index) {
+            Some(RegionClassification::Global)
+        } else if (self.first_extern_index..self.first_local_index).contains(index) {
+            Some(RegionClassification::External)
+        } else if (self.first_local_index..self.num_universals).contains(index) {
+            Some(RegionClassification::Local)
+        } else {
+            None
         }
     }
 
-    // Extract the late-bound regions. Use the liberated fn sigs,
-    // where the late-bound regions will have been converted into free
-    // regions, and add them to the map.
-    let item_id = infcx.tcx.hir.as_local_node_id(item_def_id).unwrap();
-    let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id);
-    let tables = infcx.tcx.typeck_tables_of(item_def_id);
-    let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone();
-    infcx
-        .tcx
-        .for_each_free_region(&fn_sig.inputs_and_output, |region| {
-            if let ty::ReFree(_) = *region {
-                insert_free_region(&mut indices, region);
+    /// Returns an iterator over all the RegionVids corresponding to
+    /// universally quantified free regions.
+    pub fn universal_regions(&self) -> impl Iterator<Item = RegionVid> {
+        (FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new)
+    }
+
+    /// True if `r` is classied as a global region.
+    pub fn is_global_free_region(&self, r: RegionVid) -> bool {
+        self.region_classification(r) == Some(RegionClassification::Global)
+    }
+
+    /// True if `r` is classied as an external region.
+    pub fn is_extern_free_region(&self, r: RegionVid) -> bool {
+        self.region_classification(r) == Some(RegionClassification::External)
+    }
+
+    /// True if `r` is classied as an local region.
+    pub fn is_local_free_region(&self, r: RegionVid) -> bool {
+        self.region_classification(r) == Some(RegionClassification::Local)
+    }
+
+    /// Returns the number of universal regions created in any category.
+    pub fn len(&self) -> usize {
+        self.num_universals
+    }
+
+    /// Finds an "upper bound" for `fr` that is not local. In other
+    /// words, returns the smallest (*) known region `fr1` that (a)
+    /// outlives `fr` and (b) is not local. This cannot fail, because
+    /// we will always find `'static` at worst.
+    ///
+    /// (*) If there are multiple competing choices, we pick the "postdominating"
+    /// one. See `TransitiveRelation::postdom_upper_bound` for details.
+    pub fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid {
+        debug!("non_local_upper_bound(fr={:?})", fr);
+        self.non_local_bound(&self.relations.inverse_outlives, fr)
+            .unwrap_or(self.fr_static)
+    }
+
+    /// Finds a "lower bound" for `fr` that is not local. In other
+    /// words, returns the largest (*) known region `fr1` that (a) is
+    /// outlived by `fr` and (b) is not local. This cannot fail,
+    /// because we will always find `'static` at worst.
+    ///
+    /// (*) If there are multiple competing choices, we pick the "postdominating"
+    /// one. See `TransitiveRelation::postdom_upper_bound` for details.
+    pub fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> {
+        debug!("non_local_lower_bound(fr={:?})", fr);
+        self.non_local_bound(&self.relations.outlives, fr)
+    }
+
+    /// Returns the number of global plus external universal regions.
+    /// For closures, these are the regions that appear free in the
+    /// closure type (versus those bound in the closure
+    /// signature). They are therefore the regions between which the
+    /// closure may impose constraints that its creator must verify.
+    pub fn num_global_and_external_regions(&self) -> usize {
+        self.first_local_index
+    }
+
+    /// Helper for `non_local_upper_bound` and
+    /// `non_local_lower_bound`.  Repeatedly invokes `postdom_parent`
+    /// until we find something that is not local. Returns None if we
+    /// never do so.
+    fn non_local_bound(
+        &self,
+        relation: &TransitiveRelation<RegionVid>,
+        fr0: RegionVid,
+    ) -> Option<RegionVid> {
+        let mut external_parents = vec![];
+        let mut queue = vec![&fr0];
+
+        // Keep expanding `fr` into its parents until we reach
+        // non-local regions.
+        while let Some(fr) = queue.pop() {
+            if !self.is_local_free_region(*fr) {
+                external_parents.push(fr);
+                continue;
             }
-        });
 
-    debug!("universal_regions: indices={:#?}", indices);
+            queue.extend(relation.parents(fr));
+        }
+
+        debug!("non_local_bound: external_parents={:?}", external_parents);
+
+        // In case we find more than one, reduce to one for
+        // convenience.  This is to prevent us from generating more
+        // complex constraints, but it will cause spurious errors.
+        let post_dom = relation
+            .mutual_immediate_postdominator(external_parents)
+            .cloned();
+
+        debug!("non_local_bound: post_dom={:?}", post_dom);
+
+        post_dom.and_then(|post_dom| {
+            // If the mutual immediate postdom is not local, then
+            // there is no non-local result we can return.
+            if !self.is_local_free_region(post_dom) {
+                Some(post_dom)
+            } else {
+                None
+            }
+        })
+    }
+
+    /// True if fr1 is known to outlive fr2.
+    ///
+    /// This will only ever be true for universally quantified regions.
+    pub fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
+        self.relations.outlives.contains(&fr1, &fr2)
+    }
+
+    /// Returns a vector of free regions `x` such that `fr1: x` is
+    /// known to hold.
+    pub fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<&RegionVid> {
+        self.relations.outlives.reachable_from(&fr1)
+    }
+
+    /// Get an iterator over all the early-bound regions that have names.
+    pub fn named_universal_regions<'s>(
+        &'s self,
+    ) -> impl Iterator<Item = (ty::Region<'tcx>, ty::RegionVid)> + 's {
+        self.indices.indices.iter().map(|(&r, &v)| (r, v))
+    }
+
+    /// See `UniversalRegionIndices::to_region_vid`.
+    pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
+        self.indices.to_region_vid(r)
+    }
+}
+
+struct UniversalRegionsBuilder<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    mir_def_id: DefId,
+    mir_hir_id: HirId,
+    mir_node_id: ast::NodeId,
+    param_env: ty::ParamEnv<'tcx>,
+    region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
+    relations: UniversalRegionRelations,
+}
+
+const FR: NLLRegionVariableOrigin = NLLRegionVariableOrigin::FreeRegion;
+
+impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
+    fn build(mut self) -> UniversalRegions<'tcx> {
+        debug!("build(mir_def_id={:?})", self.mir_def_id);
+
+        let param_env = self.param_env;
+        debug!("build: param_env={:?}", param_env);
+
+        assert_eq!(FIRST_GLOBAL_INDEX, self.infcx.num_region_vars());
+
+        // Create the "global" region that is always free in all contexts: 'static.
+        let fr_static = self.infcx.next_nll_region_var(FR).to_region_vid();
+
+        // We've now added all the global regions. The next ones we
+        // add will be external.
+        let first_extern_index = self.infcx.num_region_vars();
+
+        let defining_ty = self.defining_ty();
+        debug!("build: defining_ty={:?}", defining_ty);
+
+        let indices = self.compute_indices(fr_static, defining_ty);
+        debug!("build: indices={:?}", indices);
+
+        let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);
+
+        // "Liberate" the late-bound regions. These correspond to
+        // "local" free regions.
+        let first_local_index = self.infcx.num_region_vars();
+        let inputs_and_output = self.infcx
+            .replace_bound_regions_with_nll_infer_vars(FR, &bound_inputs_and_output);
+        let num_universals = self.infcx.num_region_vars();
+
+        // Insert the facts we know from the predicates. Why? Why not.
+        self.add_outlives_bounds(&indices, bounds::explicit_outlives_bounds(param_env));
+
+        // Add the implied bounds from inputs and outputs.
+        for ty in inputs_and_output {
+            debug!("build: input_or_output={:?}", ty);
+            self.add_implied_bounds(&indices, ty);
+        }
+
+        // Finally, outlives is reflexive, and static outlives every
+        // other free region.
+        for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) {
+            debug!("build: relating free region {:?} to itself and to 'static", fr);
+            self.relations.relate_universal_regions(fr, fr);
+            self.relations.relate_universal_regions(fr_static, fr);
+        }
+
+        let (output_ty, input_tys) = inputs_and_output.split_last().unwrap();
+
+        // we should not have created any more variables
+        assert_eq!(self.infcx.num_region_vars(), num_universals);
+
+        debug!("build: global regions = {}..{}",
+               FIRST_GLOBAL_INDEX,
+               first_extern_index);
+        debug!("build: extern regions = {}..{}",
+               first_extern_index,
+               first_local_index);
+        debug!("build: local regions  = {}..{}",
+               first_local_index,
+               num_universals);
+
+        UniversalRegions {
+            indices,
+            fr_static,
+            first_extern_index,
+            first_local_index,
+            num_universals,
+            defining_ty,
+            output_ty,
+            input_tys,
+            region_bound_pairs: self.region_bound_pairs,
+            relations: self.relations,
+        }
+    }
+
+    fn defining_ty(&self) -> ty::Ty<'tcx> {
+        let tcx = self.infcx.tcx;
+        let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
+
+        let defining_ty = if self.mir_def_id == closure_base_def_id {
+            tcx.type_of(closure_base_def_id)
+        } else {
+            let tables = tcx.typeck_tables_of(self.mir_def_id);
+            tables.node_id_to_type(self.mir_hir_id)
+        };
+
+        self.infcx
+            .replace_free_regions_with_nll_infer_vars(FR, &defining_ty)
+    }
+
+    fn compute_indices(
+        &self,
+        fr_static: RegionVid,
+        defining_ty: Ty<'tcx>,
+    ) -> UniversalRegionIndices<'tcx> {
+        let tcx = self.infcx.tcx;
+        let gcx = tcx.global_tcx();
+        let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
+        let identity_substs = Substs::identity_for_item(gcx, closure_base_def_id);
+        let fr_substs = match defining_ty.sty {
+            ty::TyClosure(_, substs) | ty::TyGenerator(_, substs, ..) => {
+                // In the case of closures, we rely on the fact that
+                // the first N elements in the ClosureSubsts are
+                // inherited from the `closure_base_def_id`.
+                // Therefore, when we zip together (below) with
+                // `identity_substs`, we will get only those regions
+                // that correspond to early-bound regions declared on
+                // the `closure_base_def_id`.
+                assert!(substs.substs.len() >= identity_substs.len());
+                substs.substs
+            }
+            ty::TyFnDef(_, substs) => substs,
+            _ => bug!(),
+        };
+
+        let global_mapping = iter::once((gcx.types.re_static, fr_static));
+        let subst_mapping = identity_substs
+            .regions()
+            .zip(fr_substs.regions().map(|r| r.to_region_vid()));
+
+        UniversalRegionIndices {
+            indices: global_mapping.chain(subst_mapping).collect(),
+        }
+    }
+
+    fn compute_inputs_and_output(
+        &self,
+        indices: &UniversalRegionIndices<'tcx>,
+        defining_ty: Ty<'tcx>,
+    ) -> ty::Binder<&'tcx ty::Slice<Ty<'tcx>>> {
+        let tcx = self.infcx.tcx;
+        match defining_ty.sty {
+            ty::TyClosure(def_id, substs) => {
+                assert_eq!(self.mir_def_id, def_id);
+                let closure_sig = substs.closure_sig_ty(def_id, tcx).fn_sig(tcx);
+                let inputs_and_output = closure_sig.inputs_and_output();
+                let closure_ty = tcx.closure_env_ty(def_id, substs).unwrap();
+                ty::Binder::fuse(
+                    closure_ty,
+                    inputs_and_output,
+                    |closure_ty, inputs_and_output| {
+                        // The "inputs" of the closure in the
+                        // signature appear as a tuple.  The MIR side
+                        // flattens this tuple.
+                        let (&output, tuplized_inputs) = inputs_and_output.split_last().unwrap();
+                        assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs");
+                        let inputs = match tuplized_inputs[0].sty {
+                            ty::TyTuple(inputs, _) => inputs,
+                            _ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]),
+                        };
 
-    UniversalRegions { indices, free_region_map: &tables.free_region_map }
+                        tcx.mk_type_list(
+                            iter::once(closure_ty)
+                                .chain(inputs.iter().cloned())
+                                .chain(iter::once(output)),
+                        )
+                    },
+                )
+            }
+
+            ty::TyGenerator(def_id, substs, ..) => {
+                assert_eq!(self.mir_def_id, def_id);
+                let output = substs.generator_return_ty(def_id, tcx);
+                let inputs_and_output = self.infcx.tcx.intern_type_list(&[defining_ty, output]);
+                ty::Binder::dummy(inputs_and_output)
+            }
+
+            ty::TyFnDef(def_id, _) => {
+                let sig = tcx.fn_sig(def_id);
+                let sig = indices.fold_to_region_vids(tcx, &sig);
+                return sig.inputs_and_output();
+            }
+
+            _ => span_bug!(
+                tcx.def_span(self.mir_def_id),
+                "unexpected defining type: {:?}",
+                defining_ty
+            ),
+        }
+    }
+
+    /// Update the type of a single local, which should represent
+    /// either the return type of the MIR or one of its arguments. At
+    /// the same time, compute and add any implied bounds that come
+    /// from this local.
+    ///
+    /// Assumes that `universal_regions` indices map is fully constructed.
+    fn add_implied_bounds(&mut self, indices: &UniversalRegionIndices<'tcx>, ty: Ty<'tcx>) {
+        debug!("add_implied_bounds(ty={:?})", ty);
+        let span = self.infcx.tcx.def_span(self.mir_def_id);
+        let bounds = self.infcx
+            .implied_outlives_bounds(self.param_env, self.mir_node_id, ty, span);
+        self.add_outlives_bounds(indices, bounds);
+    }
+
+    /// Registers the `OutlivesBound` items from `outlives_bounds` in
+    /// the outlives relation as well as the region-bound pairs
+    /// listing.
+    fn add_outlives_bounds<I>(&mut self, indices: &UniversalRegionIndices<'tcx>, outlives_bounds: I)
+    where
+        I: IntoIterator<Item = OutlivesBound<'tcx>>,
+    {
+        for outlives_bound in outlives_bounds {
+            debug!("add_outlives_bounds(bound={:?})", outlives_bound);
+
+            match outlives_bound {
+                OutlivesBound::RegionSubRegion(r1, r2) => {
+                    // The bound says that `r1 <= r2`; we store `r2: r1`.
+                    let r1 = indices.to_region_vid(r1);
+                    let r2 = indices.to_region_vid(r2);
+                    self.relations.relate_universal_regions(r2, r1);
+                }
+
+                OutlivesBound::RegionSubParam(r_a, param_b) => {
+                    self.region_bound_pairs
+                        .push((r_a, GenericKind::Param(param_b)));
+                }
+
+                OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+                    self.region_bound_pairs
+                        .push((r_a, GenericKind::Projection(projection_b)));
+                }
+            }
+        }
+    }
+}
+
+impl UniversalRegionRelations {
+    /// Records in the `outlives_relation` (and
+    /// `inverse_outlives_relation`) that `fr_a: fr_b`.
+    fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
+        debug!(
+            "relate_universal_regions: fr_a={:?} outlives fr_b={:?}",
+            fr_a,
+            fr_b
+        );
+        self.outlives.add(fr_a, fr_b);
+        self.inverse_outlives.add(fr_b, fr_a);
+    }
+}
+
+pub(crate) trait InferCtxtExt<'tcx> {
+    fn replace_free_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NLLRegionVariableOrigin,
+        value: &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>;
+
+    fn replace_bound_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NLLRegionVariableOrigin,
+        value: &ty::Binder<T>,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>;
+}
+
+impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> {
+    fn replace_free_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NLLRegionVariableOrigin,
+        value: &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.tcx.fold_regions(
+            value,
+            &mut false,
+            |_region, _depth| self.next_nll_region_var(origin),
+        )
+    }
+
+    fn replace_bound_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NLLRegionVariableOrigin,
+        value: &ty::Binder<T>,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        let (value, _map) = self.tcx
+            .replace_late_bound_regions(value, |_br| self.next_nll_region_var(origin));
+        value
+    }
 }
 
-fn insert_free_region<'tcx>(
-    universal_regions: &mut FxHashMap<ty::Region<'tcx>, RegionVid>,
-    region: ty::Region<'tcx>,
-) {
-    let next = RegionVid::new(universal_regions.len());
-    universal_regions.entry(region).or_insert(next);
+impl<'tcx> UniversalRegionIndices<'tcx> {
+    /// Converts `r` into a local inference variable: `r` can either
+    /// by a `ReVar` (i.e., already a reference to an inference
+    /// variable) or it can be `'static` or some early-bound
+    /// region. This is useful when taking the results from
+    /// type-checking and trait-matching, which may sometimes
+    /// reference those regions from the `ParamEnv`. It is also used
+    /// during initialization. Relies on the `indices` map having been
+    /// fully initialized.
+    pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
+        match r {
+            ty::ReEarlyBound(..) | ty::ReStatic => *self.indices.get(&r).unwrap(),
+            ty::ReVar(..) => r.to_region_vid(),
+            _ => bug!("cannot convert `{:?}` to a region vid", r),
+        }
+    }
+
+    /// Replace all free regions in `value` with region vids, as
+    /// returned by `to_region_vid`.
+    pub fn fold_to_region_vids<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: &T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        tcx.fold_regions(
+            value,
+            &mut false,
+            |region, _| tcx.mk_region(ty::ReVar(self.to_region_vid(region))),
+        )
+    }
 }
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index f0b62e28a0d..1a74f327001 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -11,6 +11,7 @@
 //! This pass type-checks the MIR to ensure it is not broken.
 #![allow(unreachable_code)]
 
+use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
 use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
 use rustc::infer::region_constraints::RegionConstraintData;
 use rustc::traits::{self, FulfillmentContext};
@@ -110,6 +111,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
 
     fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
         self.super_constant(constant, location);
+        self.sanitize_constant(constant, location);
         self.sanitize_type(constant, constant.ty);
     }
 
@@ -159,6 +161,52 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
         }
     }
 
+    /// Checks that the constant's `ty` field matches up with what
+    /// would be expected from its literal.
+    fn sanitize_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
+        debug!(
+            "sanitize_constant(constant={:?}, location={:?})",
+            constant,
+            location
+        );
+
+        let expected_ty = match constant.literal {
+            Literal::Value { value } => value.ty,
+            Literal::Promoted { .. } => {
+                // FIXME -- promoted MIR return types reference
+                // various "free regions" (e.g., scopes and things)
+                // that they ought not to do. We have to figure out
+                // how best to handle that -- probably we want treat
+                // promoted MIR much like closures, renumbering all
+                // their free regions and propagating constraints
+                // upwards. We have the same acyclic guarantees, so
+                // that should be possible. But for now, ignore them.
+                //
+                // let promoted_mir = &self.mir.promoted[index];
+                // promoted_mir.return_ty()
+                return;
+            }
+        };
+
+        debug!("sanitize_constant: expected_ty={:?}", expected_ty);
+
+        if let Err(terr) = self.cx
+            .eq_types(expected_ty, constant.ty, location.at_self())
+        {
+            span_mirbug!(
+                self,
+                constant,
+                "constant {:?} should have type {:?} but has {:?} ({:?})",
+                constant,
+                expected_ty,
+                constant.ty,
+                terr,
+            );
+        }
+    }
+
+    /// Checks that the types internal to the `place` match up with
+    /// what would be expected.
     fn sanitize_place(
         &mut self,
         place: &Place<'tcx>,
@@ -1088,14 +1136,45 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         operands: &[Operand<'tcx>],
         location: Location,
     ) {
+        let tcx = self.tcx();
+
         match aggregate_kind {
             // tuple rvalue field type is always the type of the op. Nothing to check here.
             AggregateKind::Tuple => return,
+
+            // For closures, we have some **extra requirements** we
+            // have to check. In particular, in their upvars and
+            // signatures, closures often reference various regions
+            // from the surrounding function -- we call those the
+            // closure's free regions. When we borrow-check (and hence
+            // region-check) closures, we may find that the closure
+            // requires certain relationships between those free
+            // regions. However, because those free regions refer to
+            // portions of the CFG of their caller, the closure is not
+            // in a position to verify those relationships. In that
+            // case, the requirements get "propagated" to us, and so
+            // we have to solve them here where we instantiate the
+            // closure.
+            //
+            // Despite the opacity of the previous parapgrah, this is
+            // actually relatively easy to understand in terms of the
+            // desugaring. A closure gets desugared to a struct, and
+            // these extra requirements are basically like where
+            // clauses on the struct.
+            AggregateKind::Closure(def_id, substs) => {
+                if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) {
+                    closure_region_requirements.apply_requirements(
+                        self.infcx,
+                        location,
+                        *def_id,
+                        *substs,
+                    );
+                }
+            }
+
             _ => {}
         }
 
-        let tcx = self.tcx();
-
         for (i, operand) in operands.iter().enumerate() {
             let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) {
                 Ok(field_ty) => field_ty,
diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs
index 8a3db0eb25b..37f59773cd6 100644
--- a/src/librustc_mir/util/pretty.rs
+++ b/src/librustc_mir/util/pretty.rs
@@ -11,7 +11,8 @@
 use rustc::hir;
 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
 use rustc::mir::*;
-use rustc::ty::TyCtxt;
+use rustc::mir::visit::Visitor;
+use rustc::ty::{self, TyCtxt};
 use rustc::ty::item_path;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::indexed_vec::Idx;
@@ -56,9 +57,19 @@ pub enum PassWhere {
 /// where `<filter>` takes the following forms:
 ///
 /// - `all` -- dump MIR for all fns, all passes, all everything
-/// - `substring1&substring2,...` -- `&`-separated list of substrings
-///   that can appear in the pass-name or the `item_path_str` for the given
-///   node-id. If any one of the substrings match, the data is dumped out.
+/// - a filter defined by a set of substrings combined with `&` and `|`
+///   (`&` has higher precedence). At least one of the `|`-separated groups
+///   must match; an `|`-separated group matches if all of its `&`-separated
+///   substrings are matched.
+///
+/// Example:
+///
+/// - `nll` == match if `nll` appears in the name
+/// - `foo & nll` == match if `foo` and `nll` both appear in the name
+/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
+///   or `typeck` appears in the name.
+/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
+///   or `typeck` and `bar` both appear in the name.
 pub fn dump_mir<'a, 'gcx, 'tcx, F>(
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
     pass_num: Option<&Display>,
@@ -103,8 +114,10 @@ pub fn dump_enabled<'a, 'gcx, 'tcx>(
         // see notes on #41697 below
         tcx.item_path_str(source.def_id)
     });
-    filters.split("&").any(|filter| {
-        filter == "all" || pass_name.contains(filter) || node_path.contains(filter)
+    filters.split("|").any(|or_filter| {
+        or_filter.split("&").all(|and_filter| {
+            and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter)
+        })
     })
 }
 
@@ -125,14 +138,7 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(
     F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
 {
     let _: io::Result<()> = do catch {
-        let mut file = create_dump_file(
-            tcx,
-            "mir",
-            pass_num,
-            pass_name,
-            disambiguator,
-            source,
-        )?;
+        let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?;
         writeln!(file, "// MIR for `{}`", node_path)?;
         writeln!(file, "// source = {:?}", source)?;
         writeln!(file, "// pass_name = {}", pass_name)?;
@@ -148,15 +154,9 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(
     };
 
     if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
-    let _: io::Result<()> = do catch {
-            let mut file = create_dump_file(
-                tcx,
-                "dot",
-                pass_num,
-                pass_name,
-                disambiguator,
-                source,
-            )?;
+        let _: io::Result<()> = do catch {
+            let mut file =
+                create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?;
             write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?;
             Ok(())
         };
@@ -297,10 +297,10 @@ where
 }
 
 /// Write out a human-readable textual representation for the given basic block.
-pub fn write_basic_block<F>(
-    tcx: TyCtxt,
+pub fn write_basic_block<'cx, 'gcx, 'tcx, F>(
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
     block: BasicBlock,
-    mir: &Mir,
+    mir: &Mir<'tcx>,
     extra_data: &mut F,
     w: &mut Write,
 ) -> io::Result<()>
@@ -330,6 +330,11 @@ where
             comment(tcx, statement.source_info),
             A = ALIGN,
         )?;
+
+        write_extra(tcx, w, |visitor| {
+            visitor.visit_statement(current_location.block, statement, current_location);
+        })?;
+
         extra_data(PassWhere::AfterLocation(current_location), w)?;
 
         current_location.statement_index += 1;
@@ -346,11 +351,94 @@ where
         comment(tcx, data.terminator().source_info),
         A = ALIGN,
     )?;
+
+    write_extra(tcx, w, |visitor| {
+        visitor.visit_terminator(current_location.block, data.terminator(), current_location);
+    })?;
+
     extra_data(PassWhere::AfterLocation(current_location), w)?;
 
     writeln!(w, "{}}}", INDENT)
 }
 
+/// After we print the main statement, we sometimes dump extra
+/// information. There's often a lot of little things "nuzzled up" in
+/// a statement.
+fn write_extra<'cx, 'gcx, 'tcx, F>(
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+    write: &mut Write,
+    mut visit_op: F,
+) -> io::Result<()>
+where
+    F: FnMut(&mut ExtraComments<'cx, 'gcx, 'tcx>),
+{
+    let mut extra_comments = ExtraComments {
+        _tcx: tcx,
+        comments: vec![],
+    };
+    visit_op(&mut extra_comments);
+    for comment in extra_comments.comments {
+        writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
+    }
+    Ok(())
+}
+
+struct ExtraComments<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    _tcx: TyCtxt<'cx, 'gcx, 'tcx>, // don't need it now, but bet we will soon
+    comments: Vec<String>,
+}
+
+impl<'cx, 'gcx, 'tcx> ExtraComments<'cx, 'gcx, 'tcx> {
+    fn push(&mut self, lines: &str) {
+        for line in lines.split("\n") {
+            self.comments.push(line.to_string());
+        }
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ExtraComments<'cx, 'gcx, 'tcx> {
+    fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
+        self.super_constant(constant, location);
+        let Constant { span, ty, literal } = constant;
+        self.push(&format!("mir::Constant"));
+        self.push(&format!("â”” span: {:?}", span));
+        self.push(&format!("â”” ty: {:?}", ty));
+        self.push(&format!("â”” literal: {:?}", literal));
+    }
+
+    fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
+        self.super_const(constant);
+        let ty::Const { ty, val } = constant;
+        self.push(&format!("ty::Const"));
+        self.push(&format!("â”” ty: {:?}", ty));
+        self.push(&format!("â”” val: {:?}", val));
+    }
+
+    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
+        self.super_rvalue(rvalue, location);
+        match rvalue {
+            Rvalue::Aggregate(kind, _) => match **kind {
+                AggregateKind::Closure(def_id, substs) => {
+                    self.push(&format!("closure"));
+                    self.push(&format!("â”” def_id: {:?}", def_id));
+                    self.push(&format!("â”” substs: {:#?}", substs));
+                }
+
+                AggregateKind::Generator(def_id, substs, interior) => {
+                    self.push(&format!("generator"));
+                    self.push(&format!("â”” def_id: {:?}", def_id));
+                    self.push(&format!("â”” substs: {:#?}", substs));
+                    self.push(&format!("â”” interior: {:?}", interior));
+                }
+
+                _ => {}
+            },
+
+            _ => {}
+        }
+    }
+}
+
 fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
     format!(
         "scope {} at {}",
diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs
index 405647af324..762cf7a0055 100644
--- a/src/librustc_trans/common.rs
+++ b/src/librustc_trans/common.rs
@@ -29,7 +29,7 @@ use value::Value;
 use rustc::traits;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::layout::{HasDataLayout, LayoutOf};
-use rustc::ty::subst::{Kind, Subst, Substs};
+use rustc::ty::subst::{Kind, Substs};
 use rustc::hir;
 
 use libc::{c_uint, c_char};
@@ -393,7 +393,7 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         ty::TyFnPtr(_) => ty.fn_sig(ccx.tcx()),
         ty::TyClosure(def_id, substs) => {
             let tcx = ccx.tcx();
-            let sig = tcx.fn_sig(def_id).subst(tcx, substs.substs);
+            let sig = substs.closure_sig(def_id, tcx);
 
             let env_ty = tcx.closure_env_ty(def_id, substs).unwrap();
             sig.map_bound(|sig| tcx.mk_fn_sig(
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index 764021983e9..0799a388a8b 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -20,7 +20,7 @@ use rustc::mir::tcx::PlaceTy;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc::ty::layout::{self, LayoutOf, Size};
 use rustc::ty::cast::{CastTy, IntTy};
-use rustc::ty::subst::{Kind, Substs, Subst};
+use rustc::ty::subst::{Kind, Substs};
 use rustc_apfloat::{ieee, Float, Status};
 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use base;
@@ -658,8 +658,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                                     .find(|it| it.kind == ty::AssociatedKind::Method)
                                     .unwrap().def_id;
                                 // Now create its substs [Closure, Tuple]
-                                let input = tcx.fn_sig(def_id)
-                                    .subst(tcx, substs.substs).input(0);
+                                let input = substs.closure_sig(def_id, tcx).input(0);
                                 let input = tcx.erase_late_bound_regions_and_normalize(&input);
                                 let substs = tcx.mk_substs([operand.ty, input]
                                     .iter().cloned().map(Kind::from));
diff --git a/src/librustc_trans_utils/monomorphize.rs b/src/librustc_trans_utils/monomorphize.rs
index 66833a1a7c2..d586d1ac315 100644
--- a/src/librustc_trans_utils/monomorphize.rs
+++ b/src/librustc_trans_utils/monomorphize.rs
@@ -12,7 +12,7 @@ use rustc::hir::def_id::DefId;
 use rustc::middle::lang_items::DropInPlaceFnLangItem;
 use rustc::traits;
 use rustc::ty::adjustment::CustomCoerceUnsized;
-use rustc::ty::subst::{Kind, Subst};
+use rustc::ty::subst::Kind;
 use rustc::ty::{self, Ty, TyCtxt};
 
 pub use rustc::ty::Instance;
@@ -34,7 +34,7 @@ fn fn_once_adapter_instance<'a, 'tcx>(
     let self_ty = tcx.mk_closure_from_closure_substs(
         closure_did, substs);
 
-    let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs);
+    let sig = substs.closure_sig(closure_did, tcx);
     let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
     assert_eq!(sig.inputs().len(), 1);
     let substs = tcx.mk_substs([
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 8f409b68752..df1694a6010 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -16,7 +16,6 @@ use hir::def::Def;
 use hir::def_id::{DefId, LOCAL_CRATE};
 use rustc::{infer, traits};
 use rustc::ty::{self, TyCtxt, TypeFoldable, LvaluePreference, Ty};
-use rustc::ty::subst::Subst;
 use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
 use syntax::abi;
 use syntax::symbol::Symbol;
@@ -109,7 +108,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 // haven't yet decided on whether the closure is fn vs
                 // fnmut vs fnonce. If so, we have to defer further processing.
                 if self.closure_kind(def_id, substs).is_none() {
-                    let closure_ty = self.fn_sig(def_id).subst(self.tcx, substs.substs);
+                    let closure_ty = self.closure_sig(def_id, substs);
                     let fn_sig = self.replace_late_bound_regions_with_fresh_var(call_expr.span,
                                                                    infer::FnCall,
                                                                    &closure_ty)
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 3e725d7ef41..dc5d3141d4c 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -74,7 +74,6 @@ use rustc::ty::{self, LvaluePreference, TypeAndMut,
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::error::TypeError;
 use rustc::ty::relate::RelateResult;
-use rustc::ty::subst::Subst;
 use errors::DiagnosticBuilder;
 use syntax::abi;
 use syntax::feature_gate;
@@ -670,7 +669,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                 //     `extern "rust-call" fn((arg0,arg1,...)) -> _`
                 // to
                 //     `fn(arg0,arg1,...) -> _`
-                let sig = self.fn_sig(def_id_a).subst(self.tcx, substs_a.substs);
+                let sig = self.closure_sig(def_id_a, substs_a);
                 let converted_sig = sig.map_bound(|s| {
                     let params_iter = match s.inputs()[0].sty {
                         ty::TyTuple(params, _) => {
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index b754c981b21..85b926a707d 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1268,15 +1268,23 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             ))
         }
 
-        NodeExpr(&hir::Expr { node: hir::ExprClosure(..), hir_id, .. }) => {
-            let tables = tcx.typeck_tables_of(def_id);
-            match tables.node_id_to_type(hir_id).sty {
-                ty::TyClosure(closure_def_id, closure_substs) => {
-                    assert_eq!(def_id, closure_def_id);
-                    return closure_substs.closure_sig(closure_def_id, tcx);
-                }
-                ref t => bug!("closure with non-closure type: {:?}", t),
-            }
+        NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) => {
+            // Closure signatures are not like other function
+            // signatures and cannot be accessed through `fn_sig`. For
+            // example, a closure signature excludes the `self`
+            // argument. In any case they are embedded within the
+            // closure type as part of the `ClosureSubsts`.
+            //
+            // To get
+            // the signature of a closure, you should use the
+            // `closure_sig` method on the `ClosureSubsts`:
+            //
+            //    closure_substs.closure_sig(def_id, tcx)
+            //
+            // or, inside of an inference context, you can use
+            //
+            //    infcx.closure_sig(def_id, closure_substs)
+            bug!("to get the signature of a closure, use `closure_sig()` not `fn_sig()`");
         }
 
         x => {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index e5296e7d88c..22cef25320e 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -717,6 +717,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
                                       is just used for rustc unit tests \
                                       and will never be stable",
                                      cfg_fn!(rustc_attrs))),
+    ("rustc_regions", Normal, Gated(Stability::Unstable,
+                                    "rustc_attrs",
+                                    "the `#[rustc_regions]` attribute \
+                                     is just used for rustc unit tests \
+                                     and will never be stable",
+                                    cfg_fn!(rustc_attrs))),
     ("rustc_error", Whitelisted, Gated(Stability::Unstable,
                                        "rustc_attrs",
                                        "the `#[rustc_error]` attribute \
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 69ddd560213..72760360711 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -123,7 +123,12 @@ impl<'a> From<&'a str> for Symbol {
 
 impl fmt::Debug for Symbol {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}({})", self, self.0)
+        let is_gensymed = with_interner(|interner| interner.is_gensymed(*self));
+        if is_gensymed {
+            write!(f, "{}({})", self, self.0)
+        } else {
+            write!(f, "{}", self)
+        }
     }
 }
 
@@ -201,6 +206,10 @@ impl Interner {
         Symbol(!0 - self.gensyms.len() as u32 + 1)
     }
 
+    fn is_gensymed(&mut self, symbol: Symbol) -> bool {
+        symbol.0 as usize >= self.strings.len()
+    }
+
     pub fn get(&self, symbol: Symbol) -> &str {
         match self.strings.get(symbol.0 as usize) {
             Some(ref string) => string,
diff --git a/src/test/compile-fail/regions-static-bound.rs b/src/test/compile-fail/regions-static-bound.rs
index de695e72d07..9de8ca196f8 100644
--- a/src/test/compile-fail/regions-static-bound.rs
+++ b/src/test/compile-fail/regions-static-bound.rs
@@ -8,17 +8,27 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// revisions: ll nll
+//[nll] compile-flags: -Znll -Zborrowck=mir
+
 fn static_id<'a,'b>(t: &'a ()) -> &'static ()
     where 'a: 'static { t }
 fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
     where 'a: 'b, 'b: 'static { t }
 fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
-    t //~ ERROR E0312
+    t //[ll]~ ERROR E0312
+        //[nll]~^ WARNING not reporting region error due to -Znll
+        //[nll]~| ERROR free region `'a` does not outlive free region `'static`
 }
 
 fn error(u: &(), v: &()) {
-    static_id(&u); //~ ERROR cannot infer an appropriate lifetime
-    static_id_indirect(&v); //~ ERROR cannot infer an appropriate lifetime
+    static_id(&u); //[ll]~ ERROR cannot infer an appropriate lifetime
+    //[nll]~^ WARNING not reporting region error due to -Znll
+    static_id_indirect(&v); //[ll]~ ERROR cannot infer an appropriate lifetime
+    //[nll]~^ WARNING not reporting region error due to -Znll
+
+    // FIXME(#45827) -- MIR type checker shortcomings mean we don't
+    // see these errors (yet) in nll mode.
 }
 
 fn main() {}
diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs
index e4dd99f5a1e..09288cf69ff 100644
--- a/src/test/mir-opt/nll/liveness-call-subtlety.rs
+++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs
@@ -28,18 +28,18 @@ fn main() {
 // START rustc.main.nll.0.mir
 //    | Live variables on entry to bb0: []
 //    bb0: {
-//            | Live variables at bb0[0]: []
+//            | Live variables on entry to bb0[0]: []
 //        StorageLive(_1);
-//            | Live variables at bb0[1]: []
+//            | Live variables on entry to bb0[1]: []
 //        _1 = const <std::boxed::Box<T>>::new(const 22usize) -> [return: bb2, unwind: bb1];
 //    }
 // END rustc.main.nll.0.mir
 // START rustc.main.nll.0.mir
 //    | Live variables on entry to bb2: [_1 (drop)]
 //    bb2: {
-//            | Live variables at bb2[0]: [_1 (drop)]
+//            | Live variables on entry to bb2[0]: [_1 (drop)]
 //        StorageLive(_2);
-//            | Live variables at bb2[1]: [_1 (drop)]
+//            | Live variables on entry to bb2[1]: [_1 (drop)]
 //        _2 = const can_panic() -> [return: bb3, unwind: bb4];
 //    }
 // END rustc.main.nll.0.mir
diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs
index 8dae7738067..b060222a95f 100644
--- a/src/test/mir-opt/nll/liveness-drop-intra-block.rs
+++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs
@@ -27,15 +27,15 @@ fn main() {
 // START rustc.main.nll.0.mir
 //    | Live variables on entry to bb2: []
 //    bb2: {
-//            | Live variables at bb2[0]: []
+//            | Live variables on entry to bb2[0]: []
 //        _1 = const 55usize;
-//            | Live variables at bb2[1]: [_1]
+//            | Live variables on entry to bb2[1]: [_1]
 //        StorageLive(_3);
-//            | Live variables at bb2[2]: [_1]
+//            | Live variables on entry to bb2[2]: [_1]
 //        StorageLive(_4);
-//            | Live variables at bb2[3]: [_1]
+//            | Live variables on entry to bb2[3]: [_1]
 //        _4 = _1;
-//            | Live variables at bb2[4]: [_4]
+//            | Live variables on entry to bb2[4]: [_4]
 //        _3 = const use_x(move _4) -> [return: bb3, unwind: bb1];
 //    }
 // END rustc.main.nll.0.mir
diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs
index 5d799d3d90b..671f5e5292a 100644
--- a/src/test/mir-opt/nll/liveness-interblock.rs
+++ b/src/test/mir-opt/nll/liveness-interblock.rs
@@ -31,18 +31,18 @@ fn main() {
 // START rustc.main.nll.0.mir
 //     | Live variables on entry to bb3: [_1]
 //     bb3: {
-//             | Live variables at bb3[0]: [_1]
+//             | Live variables on entry to bb3[0]: [_1]
 //         StorageLive(_4);
-//             | Live variables at bb3[1]: [_1]
+//             | Live variables on entry to bb3[1]: [_1]
 //         _4 = _1;
-//             | Live variables at bb3[2]: [_4]
+//             | Live variables on entry to bb3[2]: [_4]
 //         _3 = const make_live(move _4) -> [return: bb5, unwind: bb1];
 //     }
 // END rustc.main.nll.0.mir
 // START rustc.main.nll.0.mir
 //     | Live variables on entry to bb4: []
 //     bb4: {
-//             | Live variables at bb4[0]: []
+//             | Live variables on entry to bb4[0]: []
 //         _5 = const make_dead() -> [return: bb6, unwind: bb1];
 //     }
 // END rustc.main.nll.0.mir
diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs
index 7039de727fa..0c42585a528 100644
--- a/src/test/mir-opt/nll/named-lifetimes-basic.rs
+++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs
@@ -26,9 +26,18 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.use_x.nll.0.mir
-// | '_#0r: {bb0[0], bb0[1], '_#0r}
-// | '_#1r: {bb0[0], bb0[1], '_#1r}
-// | '_#2r: {bb0[0], bb0[1], '_#2r}
-// | '_#3r: {bb0[0], bb0[1], '_#3r}
+// | Free Region Mapping
+// | '_#0r    | Global   | ['_#2r, '_#1r, '_#0r, '_#3r]
+// | '_#1r    | External | ['_#1r]
+// | '_#2r    | External | ['_#2r, '_#1r]
+// | '_#3r    | Local    | ['_#3r]
+// |
+// | Inferred Region Values
+// | '_#0r    | {bb0[0], bb0[1], '_#0r}
+// | '_#1r    | {bb0[0], bb0[1], '_#1r}
+// | '_#2r    | {bb0[0], bb0[1], '_#2r}
+// | '_#3r    | {bb0[0], bb0[1], '_#3r}
+// |
+// ...
 // fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool {
 // END rustc.use_x.nll.0.mir
diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs
index f51e839e4fc..d203472f20c 100644
--- a/src/test/mir-opt/nll/reborrow-basic.rs
+++ b/src/test/mir-opt/nll/reborrow-basic.rs
@@ -28,11 +28,10 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-// | '_#6r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]}
+// | '_#6r    | {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]}
+// ...
+// | '_#8r    | {bb0[11], bb0[12], bb0[13], bb0[14]}
 // ...
-// | '_#8r: {bb0[11], bb0[12], bb0[13], bb0[14]}
-// END rustc.main.nll.0.mir
-// START rustc.main.nll.0.mir
 // let _2: &'_#6r mut i32;
 // ...
 // let _4: &'_#8r mut i32;
diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs
index cfbc51f9e18..c04cedbc04b 100644
--- a/src/test/mir-opt/nll/region-liveness-basic.rs
+++ b/src/test/mir-opt/nll/region-liveness-basic.rs
@@ -31,26 +31,26 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]}
-// | '_#2r: {bb2[1], bb3[0], bb3[1]}
+// | '_#1r    | {bb2[0], bb2[1], bb3[0], bb3[1]}
+// | '_#2r    | {bb2[1], bb3[0], bb3[1]}
 // ...
 //             let _2: &'_#2r usize;
 // END rustc.main.nll.0.mir
 // START rustc.main.nll.0.mir
 //    bb2: {
-//            | Live variables at bb2[0]: [_1, _3]
+//            | Live variables on entry to bb2[0]: [_1, _3]
 //        _2 = &'_#1r _1[_3];
-//            | Live variables at bb2[1]: [_2]
+//            | Live variables on entry to bb2[1]: [_2]
 //        switchInt(const true) -> [0u8: bb4, otherwise: bb3];
 //    }
 // END rustc.main.nll.0.mir
 // START rustc.main.nll.0.mir
 //    bb3: {
-//            | Live variables at bb3[0]: [_2]
+//            | Live variables on entry to bb3[0]: [_2]
 //        StorageLive(_7);
-//            | Live variables at bb3[1]: [_2]
+//            | Live variables on entry to bb3[1]: [_2]
 //        _7 = (*_2);
-//            | Live variables at bb3[2]: [_7]
+//            | Live variables on entry to bb3[2]: [_7]
 //        _6 = const use_x(move _7) -> [return: bb5, unwind: bb1];
 //    }
 // END rustc.main.nll.0.mir
diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
index 04a30dc284d..e2ad49a4436 100644
--- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
+++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
@@ -44,5 +44,5 @@ unsafe impl<#[may_dangle] T> Drop for Wrap<T> {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-// | '_#5r: {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]}
+// | '_#5r    | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]}
 // END rustc.main.nll.0.mir
diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs
index 5569fe7f574..e0272a51d03 100644
--- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs
+++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs
@@ -46,5 +46,5 @@ impl<T> Drop for Wrap<T> {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-// | '_#5r: {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]}
+// | '_#5r    | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]}
 // END rustc.main.nll.0.mir
diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs
index 679f31fdab9..8aa0eb1a3a9 100644
--- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs
+++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs
@@ -36,10 +36,10 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]}
+// | '_#1r    | {bb2[0], bb2[1], bb3[0], bb3[1]}
 // ...
-// | '_#3r: {bb8[1], bb8[2], bb8[3], bb8[4]}
-// | '_#4r: {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]}
+// | '_#3r    | {bb8[1], bb8[2], bb8[3], bb8[4]}
+// | '_#4r    | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]}
 // ...
 // let mut _2: &'_#4r usize;
 // ...
diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs
index 471d77aefac..2bc165bd3c4 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
-// | '_#1r: {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
-// | '_#2r: {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
-// | '_#3r: {bb2[5], bb2[6], bb3[0], bb3[1]}
+// | '_#1r    | {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
+// | '_#2r    | {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
+// | '_#3r    | {bb2[5], bb2[6], bb3[0], bb3[1]}
 // END rustc.main.nll.0.mir
 // START rustc.main.nll.0.mir
 // let _2: &'_#2r usize;
diff --git a/src/test/ui/nll/capture-ref-in-struct.rs b/src/test/ui/nll/capture-ref-in-struct.rs
new file mode 100644
index 00000000000..00a0c94d221
--- /dev/null
+++ b/src/test/ui/nll/capture-ref-in-struct.rs
@@ -0,0 +1,50 @@
+// Copyright 2016 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.
+
+// compile-flags:-Znll -Zborrowck=mir
+
+// Test that a structure which tries to store a pointer to `y` into
+// `p` (indirectly) fails to compile.
+
+#![feature(rustc_attrs)]
+
+struct SomeStruct<'a, 'b: 'a> {
+    p: &'a mut &'b i32,
+    y: &'b i32,
+}
+
+fn test() {
+    let x = 44;
+    let mut p = &x;
+
+    {
+        let y = 22;
+
+        let closure = SomeStruct {
+            p: &mut p,
+            y: &y,
+        };
+
+        closure.invoke();
+    }
+    //~^ ERROR borrowed value does not live long enough [E0597]
+
+    deref(p);
+}
+
+impl<'a, 'b> SomeStruct<'a, 'b> {
+    fn invoke(self) {
+        *self.p = self.y;
+    }
+}
+
+fn deref(_: &i32) { }
+
+fn main() { }
diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr
new file mode 100644
index 00000000000..f10e52e05f1
--- /dev/null
+++ b/src/test/ui/nll/capture-ref-in-struct.stderr
@@ -0,0 +1,13 @@
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/capture-ref-in-struct.rs:36:6
+   |
+28 |         let y = 22;
+   |             - temporary value created here
+...
+36 |     }
+   |      ^ temporary value dropped here while still borrowed
+   |
+   = note: consider using a `let` binding to increase its lifetime
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.rs b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs
new file mode 100644
index 00000000000..1e34aaf1ea0
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs
@@ -0,0 +1,53 @@
+// Copyright 2016 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 closure that:
+//
+// - takes an argument `y` with lifetime `'a` (in the code, it's anonymous)
+// - stores `y` into another, longer-lived spot with lifetime `'b`
+//
+// Because `'a` and `'b` are two different, unrelated higher-ranked
+// regions with no relationship to one another, this is an error. This
+// error is reported by the closure itself and is not propagated to
+// its creator: this is because `'a` and `'b` are higher-ranked
+// (late-bound) regions and the closure is not allowed to propagate
+// additional where clauses between higher-ranked regions, only those
+// that appear free in its type (hence, we see it before the closure's
+// "external requirements" report).
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+#[rustc_regions]
+fn test() {
+    let x = 44;
+    let mut p = &x;
+
+    {
+        let y = 22;
+        let mut closure = expect_sig(|p, y| *p = y);
+        //~^ ERROR free region `'_#4r` does not outlive free region `'_#3r`
+        //~| WARNING not reporting region error due to -Znll
+        closure(&mut p, &y);
+    }
+
+    deref(p);
+}
+
+fn expect_sig<F>(f: F) -> F
+    where F: FnMut(&mut &i32, &i32)
+{
+    f
+}
+
+fn deref(_p: &i32) { }
+
+fn main() { }
diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr
new file mode 100644
index 00000000000..c842d51a2ad
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr
@@ -0,0 +1,40 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/escape-argument-callee.rs:36:50
+   |
+36 |         let mut closure = expect_sig(|p, y| *p = y);
+   |                                                  ^
+
+error: free region `'_#4r` does not outlive free region `'_#3r`
+  --> $DIR/escape-argument-callee.rs:36:45
+   |
+36 |         let mut closure = expect_sig(|p, y| *p = y);
+   |                                             ^^^^^^
+
+note: External requirements
+  --> $DIR/escape-argument-callee.rs:36:38
+   |
+36 |         let mut closure = expect_sig(|p, y| *p = y);
+   |                                      ^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:9 ~ escape_argument_callee[317d]::test[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) i32))
+           ]
+   = note: number of external vids: 1
+
+note: No external requirements
+  --> $DIR/escape-argument-callee.rs:30:1
+   |
+30 | / fn test() {
+31 | |     let x = 44;
+32 | |     let mut p = &x;
+33 | |
+...  |
+42 | |     deref(p);
+43 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:3 ~ escape_argument_callee[317d]::test[0]) with substs []
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/escape-argument.rs b/src/test/ui/nll/closure-requirements/escape-argument.rs
new file mode 100644
index 00000000000..1d8a916345e
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/escape-argument.rs
@@ -0,0 +1,52 @@
+// Copyright 2016 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 closure that:
+//
+// - takes an argument `y`
+// - stores `y` into another, longer-lived spot
+//
+// but is invoked with a spot that doesn't live long
+// enough to store `y`.
+//
+// The error is reported in the caller: invoking the closure links the
+// lifetime of the variable that is given as `y` (via subtyping) and
+// thus forces the corresponding borrow to live too long. This is
+// basically checking that the MIR type checker correctly enforces the
+// closure signature.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+#[rustc_regions]
+fn test() {
+    let x = 44;
+    let mut p = &x;
+
+    {
+        let y = 22;
+        let mut closure = expect_sig(|p, y| *p = y);
+        closure(&mut p, &y);
+    }
+    //~^ ERROR borrowed value does not live long enough [E0597]
+
+    deref(p);
+}
+
+fn expect_sig<F>(f: F) -> F
+    where F: for<'a, 'b> FnMut(&'a mut &'b i32, &'b i32)
+{
+    f
+}
+
+fn deref(_p: &i32) { }
+
+fn main() { }
diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr
new file mode 100644
index 00000000000..e5c7139573e
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr
@@ -0,0 +1,39 @@
+note: External requirements
+  --> $DIR/escape-argument.rs:36:38
+   |
+36 |         let mut closure = expect_sig(|p, y| *p = y);
+   |                                      ^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:9 ~ escape_argument[317d]::test[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32))
+           ]
+   = note: number of external vids: 1
+
+note: No external requirements
+  --> $DIR/escape-argument.rs:30:1
+   |
+30 | / fn test() {
+31 | |     let x = 44;
+32 | |     let mut p = &x;
+33 | |
+...  |
+41 | |     deref(p);
+42 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:3 ~ escape_argument[317d]::test[0]) with substs []
+
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/escape-argument.rs:38:6
+   |
+35 |         let y = 22;
+   |             - temporary value created here
+...
+38 |     }
+   |      ^ temporary value dropped here while still borrowed
+   |
+   = note: consider using a `let` binding to increase its lifetime
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs
new file mode 100644
index 00000000000..9f4585bfbab
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs
@@ -0,0 +1,43 @@
+// Copyright 2016 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.
+
+// As in `escape-upvar-ref.rs`, test closure that:
+//
+// - captures a variable `y`
+// - stores reference to `y` into another, longer-lived spot
+//
+// except that the closure does so via a second closure.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+#[rustc_regions]
+fn test() {
+    let x = 44;
+    let mut p = &x;
+
+    {
+        let y = 22;
+
+        let mut closure = || {
+            let mut closure1 = || p = &y;
+            closure1();
+        };
+
+        closure();
+    } //~ ERROR borrowed value does not live long enough
+
+    deref(p);
+}
+
+fn deref(_p: &i32) { }
+
+fn main() { }
diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr
new file mode 100644
index 00000000000..201590f01f3
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr
@@ -0,0 +1,61 @@
+note: External requirements
+  --> $DIR/escape-upvar-nested.rs:31:32
+   |
+31 |             let mut closure1 = || p = &y;
+   |                                ^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:10 ~ escape_upvar_nested[317d]::test[0]::{{closure}}[0]::{{closure}}[0]) with closure substs [
+               i16,
+               extern "rust-call" fn(()),
+               &'_#1r mut &'_#2r i32,
+               &'_#3r i32
+           ]
+   = note: number of external vids: 4
+   = note: where '_#3r: '_#2r
+
+note: External requirements
+  --> $DIR/escape-upvar-nested.rs:30:27
+   |
+30 |           let mut closure = || {
+   |  ___________________________^
+31 | |             let mut closure1 = || p = &y;
+32 | |             closure1();
+33 | |         };
+   | |_________^
+   |
+   = note: defining type: DefId(0/1:9 ~ escape_upvar_nested[317d]::test[0]::{{closure}}[0]) with closure substs [
+               i16,
+               extern "rust-call" fn(()),
+               &'_#1r mut &'_#2r i32,
+               &'_#3r i32
+           ]
+   = note: number of external vids: 4
+   = note: where '_#3r: '_#2r
+
+note: No external requirements
+  --> $DIR/escape-upvar-nested.rs:23:1
+   |
+23 | / fn test() {
+24 | |     let x = 44;
+25 | |     let mut p = &x;
+26 | |
+...  |
+38 | |     deref(p);
+39 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:3 ~ escape_upvar_nested[317d]::test[0]) with substs []
+
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/escape-upvar-nested.rs:36:6
+   |
+28 |         let y = 22;
+   |             - temporary value created here
+...
+36 |     } //~ ERROR borrowed value does not live long enough
+   |      ^ temporary value dropped here while still borrowed
+   |
+   = note: consider using a `let` binding to increase its lifetime
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs
new file mode 100644
index 00000000000..548a5ae5969
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs
@@ -0,0 +1,42 @@
+// Copyright 2016 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 closure that:
+//
+// - captures a variable `y` by reference
+// - stores that reference to `y` into another, longer-lived place (`p`)
+//
+// Both of these are upvars of reference type (the capture of `y` is
+// of type `&'a i32`, the capture of `p` is of type `&mut &'b
+// i32`). The closure thus computes a relationship between `'a` and
+// `'b`.  This relationship is propagated to the closure creator,
+// which reports an error.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+#[rustc_regions]
+fn test() {
+    let x = 44;
+    let mut p = &x;
+
+    {
+        let y = 22;
+        let mut closure = || p = &y;
+        closure();
+    } //~ ERROR borrowed value does not live long enough
+
+    deref(p);
+}
+
+fn deref(_p: &i32) { }
+
+fn main() { }
diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
new file mode 100644
index 00000000000..47ba66ade65
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
@@ -0,0 +1,42 @@
+note: External requirements
+  --> $DIR/escape-upvar-ref.rs:33:27
+   |
+33 |         let mut closure = || p = &y;
+   |                           ^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:9 ~ escape_upvar_ref[317d]::test[0]::{{closure}}[0]) with closure substs [
+               i16,
+               extern "rust-call" fn(()),
+               &'_#1r mut &'_#2r i32,
+               &'_#3r i32
+           ]
+   = note: number of external vids: 4
+   = note: where '_#3r: '_#2r
+
+note: No external requirements
+  --> $DIR/escape-upvar-ref.rs:27:1
+   |
+27 | / fn test() {
+28 | |     let x = 44;
+29 | |     let mut p = &x;
+30 | |
+...  |
+37 | |     deref(p);
+38 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:3 ~ escape_upvar_ref[317d]::test[0]) with substs []
+
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/escape-upvar-ref.rs:35:6
+   |
+32 |         let y = 22;
+   |             - temporary value created here
+...
+35 |     } //~ ERROR borrowed value does not live long enough
+   |      ^ temporary value dropped here while still borrowed
+   |
+   = note: consider using a `let` binding to increase its lifetime
+
+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
new file mode 100644
index 00000000000..c2f071cc029
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs
@@ -0,0 +1,63 @@
+// Copyright 2016 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 where we fail to approximate due to demanding a postdom
+// relationship between our upper bounds.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+// Callee knows that:
+//
+// 'x: 'a
+// 'x: 'b
+// 'c: 'y
+//
+// we have to prove that `'x: 'y`. We currently can only approximate
+// via a postdominator -- hence we fail to choose between `'a` and
+// `'b` here and report the error in the closure.
+fn establish_relationships<'a, 'b, 'c, F>(
+    _cell_a: Cell<&'a u32>,
+    _cell_b: Cell<&'b u32>,
+    _cell_c: Cell<&'c u32>,
+    _closure: F,
+) where
+    F: for<'x, 'y> FnMut(
+        Cell<&'a &'x u32>, // shows that 'x: 'a
+        Cell<&'b &'x u32>, // shows that 'x: 'b
+        Cell<&'y &'c u32>, // shows that 'c: 'y
+        Cell<&'x u32>,
+        Cell<&'y u32>,
+    ),
+{
+}
+
+fn demand_y<'x, 'y>(_cell_x: Cell<&'x u32>, _cell_y: Cell<&'y u32>, _y: &'y u32) {}
+
+#[rustc_regions]
+fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) {
+    establish_relationships(
+        cell_a,
+        cell_b,
+        cell_c,
+        |_outlives1, _outlives2, _outlives3, x, y| {
+            // Only works if 'x: 'y:
+            let p = x.get();
+            //~^ WARN not reporting region error due to -Znll
+            demand_y(x, y, p)
+            //~^ ERROR free region `'_#5r` does not outlive free region `'_#6r`
+        },
+    );
+}
+
+fn main() {}
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
new file mode 100644
index 00000000000..d581622c4c6
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
@@ -0,0 +1,46 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-approximated-fail-no-postdom.rs:55:21
+   |
+55 |             let p = x.get();
+   |                     ^^^^^^^
+
+error: free region `'_#5r` does not outlive free region `'_#6r`
+  --> $DIR/propagate-approximated-fail-no-postdom.rs:57:25
+   |
+57 |             demand_y(x, y, p)
+   |                         ^
+
+note: External requirements
+  --> $DIR/propagate-approximated-fail-no-postdom.rs:53:9
+   |
+53 | /         |_outlives1, _outlives2, _outlives3, x, y| {
+54 | |             // Only works if 'x: 'y:
+55 | |             let p = x.get();
+56 | |             //~^ WARN not reporting region error due to -Znll
+57 | |             demand_y(x, y, p)
+58 | |             //~^ ERROR free region `'_#5r` does not outlive free region `'_#6r`
+59 | |         },
+   | |_________^
+   |
+   = note: defining type: DefId(0/1:20 ~ propagate_approximated_fail_no_postdom[317d]::supply[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
+           ]
+   = note: number of external vids: 4
+
+note: No external requirements
+  --> $DIR/propagate-approximated-fail-no-postdom.rs:48:1
+   |
+48 | / fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) {
+49 | |     establish_relationships(
+50 | |         cell_a,
+51 | |         cell_b,
+...  |
+60 | |     );
+61 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_approximated_fail_no_postdom[317d]::supply[0]) with substs []
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs
new file mode 100644
index 00000000000..76a0762461a
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs
@@ -0,0 +1,64 @@
+// Copyright 2016 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.
+
+// Rather convoluted setup where we infer a relationship between two
+// free regions in the closure signature (`'a` and `'b`) on the basis
+// of a relationship between two bound regions (`'x` and `'y`).
+//
+// The idea is that, thanks to invoking `demand_y`, `'x: 'y` must
+// hold, where `'x` and `'y` are bound regions. The closure can't
+// prove that directly, and because `'x` and `'y` are bound it cannot
+// ask the caller to prove it either. But it has bounds on `'x` and
+// `'y` in terms of `'a` and `'b`, and it can propagate a relationship
+// between `'a` and `'b` to the caller.
+//
+// Note: the use of `Cell` here is to introduce invariance. One less
+// variable.
+//
+// FIXME(#45827): The `supply` function *ought* to generate an error, but it
+// currently does not. This is I believe a shortcoming of the MIR type
+// checker: the closure inference is expressing the correct
+// requirement, as you can see from the `#[rustc_regions]` output.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+// Callee knows that:
+//
+// 'x: 'a
+// 'b: 'y
+//
+// so if we are going to ensure that `'x: 'y`, then `'a: 'b` must
+// hold.
+fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
+where
+    F: for<'x, 'y> FnMut(
+        &Cell<&'a &'x u32>, // shows that 'x: 'a
+        &Cell<&'y &'b u32>, // shows that 'b: 'y
+        &Cell<&'x u32>,
+        &Cell<&'y u32>,
+    ),
+{
+}
+
+fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
+
+#[rustc_regions]
+fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+    establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+        // Only works if 'x: 'y:
+        demand_y(x, y, x.get())
+    });
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
new file mode 100644
index 00000000000..7553ac5b0c3
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
@@ -0,0 +1,36 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-approximated-ref.rs:60:9
+   |
+60 |         demand_y(x, y, x.get())
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+note: External requirements
+  --> $DIR/propagate-approximated-ref.rs:58:47
+   |
+58 |       establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+   |  _______________________________________________^
+59 | |         // Only works if 'x: 'y:
+60 | |         demand_y(x, y, x.get())
+61 | |     });
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:18 ~ propagate_approximated_ref[317d]::supply[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>))
+           ]
+   = note: number of external vids: 3
+   = note: where '_#1r: '_#2r
+
+note: No external requirements
+  --> $DIR/propagate-approximated-ref.rs:57:1
+   |
+57 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+58 | |     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+59 | |         // Only works if 'x: 'y:
+60 | |         demand_y(x, y, x.get())
+61 | |     });
+62 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_approximated_ref[317d]::supply[0]) with substs []
+
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs
new file mode 100644
index 00000000000..0a47ee80256
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs
@@ -0,0 +1,53 @@
+// Copyright 2016 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 a case where we setup relationships like `'x: 'a` or `'a: 'x`,
+// where `'x` is bound in closure type but `'a` is free. This forces
+// us to approximate `'x` one way or the other.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+fn foo<'a, F>(_cell: Cell<&'a u32>, _f: F)
+where
+    F: for<'x> FnOnce(Cell<&'a u32>, Cell<&'x u32>),
+{
+}
+
+#[rustc_regions]
+fn case1() {
+    let a = 0;
+    let cell = Cell::new(&a);
+    foo(cell, |cell_a, cell_x| {
+        //~^ WARNING not reporting region error due to -Znll
+        cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
+        //~^ ERROR free region `'_#2r` does not outlive free region `'_#1r`
+    })
+}
+
+#[rustc_regions]
+fn case2() {
+    let a = 0;
+    let cell = Cell::new(&a);
+
+    // As you can see in the stderr output, this closure propoagates a
+    // requirement that `'a: 'static'.
+    //
+    // FIXME(#45827) However, because of shortcomings in the MIR type
+    // checker, this does not result in errors later on (yet).
+    foo(cell, |cell_a, cell_x| {
+        cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error
+    })
+}
+
+fn main() { }
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
new file mode 100644
index 00000000000..e2de72ffe93
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
@@ -0,0 +1,75 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:5
+   |
+31 |     foo(cell, |cell_a, cell_x| {
+   |     ^^^
+
+error: free region `'_#2r` does not outlive free region `'_#1r`
+  --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:33:9
+   |
+33 |         cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
+   |         ^^^^^^
+
+note: External requirements
+  --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15
+   |
+31 |       foo(cell, |cell_a, cell_x| {
+   |  _______________^
+32 | |         //~^ WARNING not reporting region error due to -Znll
+33 | |         cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
+34 | |         //~^ ERROR free region `'_#2r` does not outlive free region `'_#1r`
+35 | |     })
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:12 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case1[0]::{{closure}}[0]) with closure substs [
+               i32,
+               for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>))
+           ]
+   = note: number of external vids: 2
+
+note: No external requirements
+  --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:28:1
+   |
+28 | / fn case1() {
+29 | |     let a = 0;
+30 | |     let cell = Cell::new(&a);
+31 | |     foo(cell, |cell_a, cell_x| {
+...  |
+35 | |     })
+36 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:5 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case1[0]) with substs []
+
+note: External requirements
+  --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:48:15
+   |
+48 |       foo(cell, |cell_a, cell_x| {
+   |  _______________^
+49 | |         cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error
+50 | |     })
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:13 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case2[0]::{{closure}}[0]) with closure substs [
+               i32,
+               for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>))
+           ]
+   = note: number of external vids: 2
+   = note: where '_#1r: '_#0r
+
+note: No external requirements
+  --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:39:1
+   |
+39 | / fn case2() {
+40 | |     let a = 0;
+41 | |     let cell = Cell::new(&a);
+42 | |
+...  |
+50 | |     })
+51 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case2[0]) with substs []
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs
new file mode 100644
index 00000000000..f776ddc8b15
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs
@@ -0,0 +1,51 @@
+// Copyright 2016 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 a case where we are trying to prove `'x: 'y` and are forced to
+// approximate the shorter end-point (`'y`) to with `'static`. This is
+// because `'y` is higher-ranked but we know of no relations to other
+// regions. Note that `'static` shows up in the stderr output as `'0`.
+//
+// FIXME(#45827) Because of shortcomings in the MIR type checker,
+// these errors are not (yet) reported.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+// Callee knows that:
+//
+// 'x: 'a
+//
+// so the only way we can ensure that `'x: 'y` is to show that
+// `'a: 'static`.
+fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
+where
+    F: for<'x, 'y> FnMut(
+        &Cell<&'a &'x u32>, // shows that 'x: 'a
+        &Cell<&'x u32>,
+        &Cell<&'y u32>,
+    ),
+{
+}
+
+fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
+
+#[rustc_regions]
+fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+    establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
+        // Only works if 'x: 'y:
+        demand_y(x, y, x.get())
+    });
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
new file mode 100644
index 00000000000..8d1b9a94ea6
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
@@ -0,0 +1,36 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:47:9
+   |
+47 |         demand_y(x, y, x.get())
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+note: External requirements
+  --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:47
+   |
+45 |       establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
+   |  _______________________________________________^
+46 | |         // Only works if 'x: 'y:
+47 | |         demand_y(x, y, x.get())
+48 | |     });
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:18 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) u32>))
+           ]
+   = note: number of external vids: 2
+   = note: where '_#1r: '_#0r
+
+note: No external requirements
+  --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:44:1
+   |
+44 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+45 | |     establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
+46 | |         // Only works if 'x: 'y:
+47 | |         demand_y(x, y, x.get())
+48 | |     });
+49 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]) with substs []
+
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs
new file mode 100644
index 00000000000..54b501c9ab6
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs
@@ -0,0 +1,54 @@
+// Copyright 2016 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 a case where we are trying to prove `'x: 'y` and are forced to
+// approximate the shorter end-point (`'y`) to with `'static`. This is
+// because `'y` is higher-ranked but we know of only irrelevant
+// relations to other regions. Note that `'static` shows up in the
+// stderr output as `'0`.
+//
+// FIXME(#45827) Because of shortcomings in the MIR type checker,
+// these errors are not (yet) reported.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+// Callee knows that:
+//
+// 'x: 'a
+// 'y: 'b
+//
+// so the only way we can ensure that `'x: 'y` is to show that
+// `'a: 'static`.
+fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
+where
+    F: for<'x, 'y> FnMut(
+        &Cell<&'a &'x u32>, // shows that 'x: 'a
+        &Cell<&'b &'y u32>, // shows that 'y: 'b
+        &Cell<&'x u32>,
+        &Cell<&'y u32>,
+    ),
+{
+}
+
+fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
+
+#[rustc_regions]
+fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+    establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+        // Only works if 'x: 'y:
+        demand_y(x, y, x.get())
+    });
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
new file mode 100644
index 00000000000..9d318a63d8f
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
@@ -0,0 +1,36 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:50:9
+   |
+50 |         demand_y(x, y, x.get())
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+note: External requirements
+  --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:47
+   |
+48 |       establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+   |  _______________________________________________^
+49 | |         // Only works if 'x: 'y:
+50 | |         demand_y(x, y, x.get())
+51 | |     });
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:18 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>))
+           ]
+   = note: number of external vids: 3
+   = note: where '_#1r: '_#0r
+
+note: No external requirements
+  --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:47:1
+   |
+47 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+48 | |     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+49 | |         // Only works if 'x: 'y:
+50 | |         demand_y(x, y, x.get())
+51 | |     });
+52 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]) with substs []
+
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs
new file mode 100644
index 00000000000..48d446b00af
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs
@@ -0,0 +1,52 @@
+// Copyright 2016 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.
+
+// A simpler variant of `outlives-from-argument` where cells are
+// passed by value.
+//
+// This is simpler because there are no "extraneous" region
+// relationships. In the 'main' variant, there are a number of
+// anonymous regions as well.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+// Callee knows that:
+//
+// 'x: 'a
+// 'b: 'y
+//
+// so if we are going to ensure that `'x: 'y`, then `'a: 'b` must
+// hold.
+fn establish_relationships<'a, 'b, F>(_cell_a: Cell<&'a u32>, _cell_b: Cell<&'b u32>, _closure: F)
+where
+    F: for<'x, 'y> FnMut(
+        Cell<&'a &'x u32>, // shows that 'x: 'a
+        Cell<&'y &'b u32>, // shows that 'b: 'y
+        Cell<&'x u32>,
+        Cell<&'y u32>,
+    ),
+{
+}
+
+fn demand_y<'x, 'y>(_outlives1: Cell<&&'x u32>, _outlives2: Cell<&'y &u32>, _y: &'y u32) {}
+
+#[rustc_regions]
+fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+    establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| {
+        // Only works if 'x: 'y:
+        demand_y(outlives1, outlives2, x.get())
+    });
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
new file mode 100644
index 00000000000..ae5ad6f4b96
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
@@ -0,0 +1,36 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-approximated-val.rs:48:9
+   |
+48 |         demand_y(outlives1, outlives2, x.get())
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: External requirements
+  --> $DIR/propagate-approximated-val.rs:46:45
+   |
+46 |       establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| {
+   |  _____________________________________________^
+47 | |         // Only works if 'x: 'y:
+48 | |         demand_y(outlives1, outlives2, x.get())
+49 | |     });
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:18 ~ propagate_approximated_val[317d]::test[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
+           ]
+   = note: number of external vids: 3
+   = note: where '_#1r: '_#2r
+
+note: No external requirements
+  --> $DIR/propagate-approximated-val.rs:45:1
+   |
+45 | / fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+46 | |     establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| {
+47 | |         // Only works if 'x: 'y:
+48 | |         demand_y(outlives1, outlives2, x.get())
+49 | |     });
+50 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_approximated_val[317d]::test[0]) with substs []
+
diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs
new file mode 100644
index 00000000000..a28b5f4c0f9
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs
@@ -0,0 +1,59 @@
+// Copyright 2016 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 where we might in theory be able to see that the relationship
+// between two bound regions is true within closure and hence have no
+// need to propagate; but in fact we do because identity of free
+// regions is erased.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+// In theory, callee knows that:
+//
+// 'x: 'a
+// 'a: 'y
+//
+// and hence could satisfy that `'x: 'y` locally. However, in our
+// checking, we ignore the precise free regions that come into the
+// region and just assign each position a distinct universally bound
+// region. Hence, we propagate a constraint to our caller that will
+// wind up being solvable.
+fn establish_relationships<'a, F>(
+    _cell_a: Cell<&'a u32>,
+    _closure: F,
+) where
+    F: for<'x, 'y> FnMut(
+        Cell<&'a &'x u32>, // shows that 'x: 'a
+        Cell<&'y &'a u32>, // shows that 'a: 'y
+        Cell<&'x u32>,
+        Cell<&'y u32>,
+    ),
+{
+}
+
+fn demand_y<'x, 'y>(_cell_x: Cell<&'x u32>, _cell_y: Cell<&'y u32>, _y: &'y u32) {}
+
+#[rustc_regions]
+fn supply<'a>(cell_a: Cell<&'a u32>) {
+    establish_relationships(
+        cell_a,
+        |_outlives1, _outlives2, x, y| {
+            // Only works if 'x: 'y:
+            let p = x.get();
+            demand_y(x, y, p)
+        },
+    );
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr
new file mode 100644
index 00000000000..64f3bb08c62
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr
@@ -0,0 +1,37 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-despite-same-free-region.rs:53:21
+   |
+53 |             let p = x.get();
+   |                     ^^^^^^^
+
+note: External requirements
+  --> $DIR/propagate-despite-same-free-region.rs:51:9
+   |
+51 | /         |_outlives1, _outlives2, x, y| {
+52 | |             // Only works if 'x: 'y:
+53 | |             let p = x.get();
+54 | |             demand_y(x, y, p)
+55 | |         },
+   | |_________^
+   |
+   = note: defining type: DefId(0/1:16 ~ propagate_despite_same_free_region[317d]::supply[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
+           ]
+   = note: number of external vids: 3
+   = note: where '_#1r: '_#2r
+
+note: No external requirements
+  --> $DIR/propagate-despite-same-free-region.rs:48:1
+   |
+48 | / fn supply<'a>(cell_a: Cell<&'a u32>) {
+49 | |     establish_relationships(
+50 | |         cell_a,
+51 | |         |_outlives1, _outlives2, x, y| {
+...  |
+56 | |     );
+57 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_despite_same_free_region[317d]::supply[0]) with substs []
+
diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs
new file mode 100644
index 00000000000..4bbdcc44944
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs
@@ -0,0 +1,53 @@
+// Copyright 2016 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.
+
+// Similarly to escape-argument-callee, a test case where the closure
+// requires a relationship between 2 unrelated higher-ranked regions,
+// with no helpful relations between the HRRs and free regions.
+//
+// In this case, the error is reported by the closure itself. This is
+// because it is unable to approximate the higher-ranked region `'x`,
+// as it knows of no relationships between `'x` and any
+// non-higher-ranked regions.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+// Callee knows that:
+//
+// 'b: 'y
+//
+// but this doesn't really help us in proving that `'x: 'y`, so closure gets an error.
+fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
+where
+    F: for<'x, 'y> FnMut(
+        &Cell<&'y &'b u32>, // shows that 'b: 'y
+        &Cell<&'x u32>,
+        &Cell<&'y u32>,
+    ),
+{
+}
+
+fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
+
+#[rustc_regions]
+fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+    establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
+        // Only works if 'x: 'y:
+        demand_y(x, y, x.get())
+        //~^ WARN not reporting region error due to -Znll
+        //~| ERROR free region `'_#6r` does not outlive free region `'_#4r`
+    });
+}
+
+fn main() {}
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
new file mode 100644
index 00000000000..6094f9aad81
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
@@ -0,0 +1,46 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:9
+   |
+47 |         demand_y(x, y, x.get())
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: free region `'_#6r` does not outlive free region `'_#4r`
+  --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:21
+   |
+47 |         demand_y(x, y, x.get())
+   |                     ^
+
+note: External requirements
+  --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:45:47
+   |
+45 |       establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
+   |  _______________________________________________^
+46 | |         // Only works if 'x: 'y:
+47 | |         demand_y(x, y, x.get())
+48 | |         //~^ WARN not reporting region error due to -Znll
+49 | |         //~| ERROR free region `'_#6r` does not outlive free region `'_#4r`
+50 | |     });
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:18 ~ propagate_fail_to_approximate_longer_no_bounds[317d]::supply[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
+           ]
+   = note: number of external vids: 2
+
+note: No external requirements
+  --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:44:1
+   |
+44 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+45 | |     establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
+46 | |         // Only works if 'x: 'y:
+47 | |         demand_y(x, y, x.get())
+...  |
+50 | |     });
+51 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_fail_to_approximate_longer_no_bounds[317d]::supply[0]) with substs []
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs
new file mode 100644
index 00000000000..69fad354792
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs
@@ -0,0 +1,57 @@
+// Copyright 2016 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.
+
+// Similarly to escape-argument-callee, a test case where the closure
+// requires a relationship between 2 unrelated higher-ranked regions,
+// with no helpful relations between the HRRs and free regions.
+//
+// In this case, the error is reported by the closure itself. This is
+// because it is unable to approximate the higher-ranked region `'x`,
+// as it only knows of regions that `'x` is outlived by, and none that
+// `'x` outlives.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+// Callee knows that:
+//
+// 'a: 'x
+// 'b: 'y
+//
+// but this doesn't really help us in proving that `'x: 'y`, so
+// closure gets an error.  In particular, we would need to know that
+// `'x: 'a`, so that we could approximate `'x` "downwards" to `'a`.
+fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
+where
+    F: for<'x, 'y> FnMut(
+        &Cell<&'x &'a u32>, // shows that 'a: 'x
+        &Cell<&'y &'b u32>, // shows that 'b: 'y
+        &Cell<&'x u32>,
+        &Cell<&'y u32>,
+    ),
+{
+}
+
+fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
+
+#[rustc_regions]
+fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+    establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+        // Only works if 'x: 'y:
+        demand_y(x, y, x.get())
+        //~^ WARN not reporting region error due to -Znll
+        //~| ERROR free region `'_#5r` does not outlive free region `'_#7r`
+    });
+}
+
+fn main() {}
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
new file mode 100644
index 00000000000..6658ee63abd
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
@@ -0,0 +1,46 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:9
+   |
+51 |         demand_y(x, y, x.get())
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: free region `'_#5r` does not outlive free region `'_#7r`
+  --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:21
+   |
+51 |         demand_y(x, y, x.get())
+   |                     ^
+
+note: External requirements
+  --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:49:47
+   |
+49 |       establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+   |  _______________________________________________^
+50 | |         // Only works if 'x: 'y:
+51 | |         demand_y(x, y, x.get())
+52 | |         //~^ WARN not reporting region error due to -Znll
+53 | |         //~| ERROR free region `'_#5r` does not outlive free region `'_#7r`
+54 | |     });
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:18 ~ propagate_fail_to_approximate_longer_wrong_bounds[317d]::supply[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>))
+           ]
+   = note: number of external vids: 3
+
+note: No external requirements
+  --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:48:1
+   |
+48 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+49 | |     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
+50 | |         // Only works if 'x: 'y:
+51 | |         demand_y(x, y, x.get())
+...  |
+54 | |     });
+55 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_fail_to_approximate_longer_wrong_bounds[317d]::supply[0]) with substs []
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs
new file mode 100644
index 00000000000..c61cf8a940f
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs
@@ -0,0 +1,24 @@
+// Copyright 2016 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.
+
+// Basic test for free regions in the NLL code. This test ought to
+// report an error due to a reborrowing constraint. Right now, we get
+// a variety of errors from the older, AST-based machinery (notably
+// borrowck), and then we get the NLL error at the end.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+fn foo(x: &u32) -> &'static u32 {
+    &*x
+        //~^ WARN not reporting region error due to -Znll
+        //~| ERROR free region `'_#1r` does not outlive free region `ReStatic`
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr
new file mode 100644
index 00000000000..ef7ea923912
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr
@@ -0,0 +1,14 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/region-lbr-anon-does-not-outlive-static.rs:19:5
+   |
+19 |     &*x
+   |     ^^^
+
+error: free region `'_#1r` does not outlive free region `ReStatic`
+  --> $DIR/region-lbr-anon-does-not-outlive-static.rs:19:5
+   |
+19 |     &*x
+   |     ^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs
new file mode 100644
index 00000000000..fcda5c5420b
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs
@@ -0,0 +1,24 @@
+// Copyright 2016 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.
+
+// Basic test for free regions in the NLL code. This test ought to
+// report an error due to a reborrowing constraint. Right now, we get
+// a variety of errors from the older, AST-based machinery (notably
+// borrowck), and then we get the NLL error at the end.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+fn foo<'a>(x: &'a u32) -> &'static u32 {
+    &*x
+        //~^ WARN not reporting region error due to -Znll
+        //~| ERROR free region `'_#1r` does not outlive free region `ReStatic`
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr
new file mode 100644
index 00000000000..6dcb8e7cf12
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr
@@ -0,0 +1,14 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/region-lbr-named-does-not-outlive-static.rs:19:5
+   |
+19 |     &*x
+   |     ^^^
+
+error: free region `'_#1r` does not outlive free region `ReStatic`
+  --> $DIR/region-lbr-named-does-not-outlive-static.rs:19:5
+   |
+19 |     &*x
+   |     ^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/named-region-basic.rs b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs
index 001ce41c277..c1e4dee0065 100644
--- a/src/test/ui/nll/named-region-basic.rs
+++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs
@@ -13,12 +13,12 @@
 // a variety of errors from the older, AST-based machinery (notably
 // borrowck), and then we get the NLL error at the end.
 
-// compile-flags:-Znll
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
 
 fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
-    &*x //~ ERROR free region `'a` does not outlive `'b`
-    //~^ ERROR `*x` does not live long enough
-    //~| WARN not reporting region error due to -Znll
+    &*x
+        //~^ WARN not reporting region error due to -Znll
+        //~| ERROR free region `'_#1r` does not outlive free region `'_#2r`
 }
 
 fn main() { }
diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr
new file mode 100644
index 00000000000..c1b2f440309
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr
@@ -0,0 +1,14 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/region-lbr1-does-not-outlive-ebr2.rs:19:5
+   |
+19 |     &*x
+   |     ^^^
+
+error: free region `'_#1r` does not outlive free region `'_#2r`
+  --> $DIR/region-lbr1-does-not-outlive-ebr2.rs:19:5
+   |
+19 |     &*x
+   |     ^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs b/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs
new file mode 100644
index 00000000000..ffb1935e75e
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs
@@ -0,0 +1,23 @@
+// Copyright 2016 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.
+
+// Basic test for free regions in the NLL code. This test does not
+// report an error because of the (implied) bound that `'b: 'a`.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+// must-compile-successfully
+
+#![allow(warnings)]
+
+fn foo<'a, 'b>(x: &'a &'b u32) -> &'a u32 {
+    &**x
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs
new file mode 100644
index 00000000000..9314bbf9432
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs
@@ -0,0 +1,34 @@
+// Copyright 2016 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 closure that takes two references and is supposed to return
+// the first, but actually returns the second. This should fail within
+// the closure.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+
+#[rustc_regions]
+fn test() {
+    expect_sig(|a, b| b); // ought to return `a`
+    //~^ WARN not reporting region error due to -Znll
+    //~| ERROR free region `'_#3r` does not outlive free region `'_#2r`
+}
+
+fn expect_sig<F>(f: F) -> F
+    where F: for<'a> FnMut(&'a i32, &i32) -> &'a i32
+{
+    f
+}
+
+fn deref(_p: &i32) { }
+
+fn main() { }
diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr
new file mode 100644
index 00000000000..8999f69e8de
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr
@@ -0,0 +1,38 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/return-wrong-bound-region.rs:21:23
+   |
+21 |     expect_sig(|a, b| b); // ought to return `a`
+   |                       ^
+
+error: free region `'_#3r` does not outlive free region `'_#2r`
+  --> $DIR/return-wrong-bound-region.rs:21:23
+   |
+21 |     expect_sig(|a, b| b); // ought to return `a`
+   |                       ^
+
+note: External requirements
+  --> $DIR/return-wrong-bound-region.rs:21:16
+   |
+21 |     expect_sig(|a, b| b); // ought to return `a`
+   |                ^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:9 ~ return_wrong_bound_region[317d]::test[0]::{{closure}}[0]) with closure substs [
+               i16,
+               for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32)) -> &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32
+           ]
+   = note: number of external vids: 1
+
+note: No external requirements
+  --> $DIR/return-wrong-bound-region.rs:20:1
+   |
+20 | / fn test() {
+21 | |     expect_sig(|a, b| b); // ought to return `a`
+22 | |     //~^ WARN not reporting region error due to -Znll
+23 | |     //~| ERROR free region `'_#3r` does not outlive free region `'_#2r`
+24 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:3 ~ return_wrong_bound_region[317d]::test[0]) with substs []
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/named-region-basic.stderr b/src/test/ui/nll/named-region-basic.stderr
deleted file mode 100644
index 9c1de6c366c..00000000000
--- a/src/test/ui/nll/named-region-basic.stderr
+++ /dev/null
@@ -1,31 +0,0 @@
-warning: not reporting region error due to -Znll
-  --> $DIR/named-region-basic.rs:19:5
-   |
-19 |     &*x //~ ERROR free region `'a` does not outlive `'b`
-   |     ^^^
-
-error[E0597]: `*x` does not live long enough
-  --> $DIR/named-region-basic.rs:19:6
-   |
-19 |     &*x //~ ERROR free region `'a` does not outlive `'b`
-   |      ^^ does not live long enough
-   |
-   = note: borrowed value must be valid for the static lifetime...
-note: ...but borrowed value is only valid for the lifetime 'a as defined on the function body at 18:1
-  --> $DIR/named-region-basic.rs:18:1
-   |
-18 | / fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
-19 | |     &*x //~ ERROR free region `'a` does not outlive `'b`
-20 | |     //~^ ERROR `*x` does not live long enough
-21 | |     //~| WARN not reporting region error due to -Znll
-22 | | }
-   | |_^
-
-error: free region `'a` does not outlive `'b`
-  --> $DIR/named-region-basic.rs:19:5
-   |
-19 |     &*x //~ ERROR free region `'a` does not outlive `'b`
-   |     ^^^
-
-error: aborting due to 2 previous errors
-
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 91d51d359ec..a18f4ec1aad 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2535,7 +2535,10 @@ impl<'test> TestCx<'test> {
         let mut dumped_file = fs::File::open(output_file.clone()).unwrap();
         let mut dumped_string = String::new();
         dumped_file.read_to_string(&mut dumped_string).unwrap();
-        let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty());
+        let mut dumped_lines = dumped_string
+            .lines()
+            .map(|l| nocomment_mir_line(l))
+            .filter(|l| !l.is_empty());
         let mut expected_lines = expected_content
             .iter()
             .filter(|&l| {
@@ -2573,7 +2576,7 @@ impl<'test> TestCx<'test> {
                 .join("\n");
             panic!(
                 "Did not find expected line, error: {}\n\
-                 Actual Line: {:?}\n\
+                 Expected Line: {:?}\n\
                  Expected:\n{}\n\
                  Actual:\n{}",
                 extra_msg,
@@ -2599,7 +2602,9 @@ impl<'test> TestCx<'test> {
                         error(
                             expected_line,
                             format!(
-                                "Mismatch in lines\nCurrnt block: {}\nExpected Line: {:?}",
+                                "Mismatch in lines\n\
+                                 Current block: {}\n\
+                                 Actual Line: {:?}",
                                 start_block_line.unwrap_or("None"),
                                 dumped_line
                             ),