about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-12-20 03:58:15 +0000
committerbors <bors@rust-lang.org>2017-12-20 03:58:15 +0000
commit588f7db8ef588ea7e349817bb44b4e37bfd92745 (patch)
tree8f533a5308ce9b27e5cbc37d2db8632f786e433a /src
parentedbd7d232ee1272285be332b8a38eb47b4c8f5c6 (diff)
parent1816ede386c6dd6e61f50e7b0f9bdba19adc0e24 (diff)
downloadrust-588f7db8ef588ea7e349817bb44b4e37bfd92745.tar.gz
rust-588f7db8ef588ea7e349817bb44b4e37bfd92745.zip
Auto merge of #46733 - nikomatsakis:nll-master-to-rust-master-5, r=arielb1
nll part 5

Next round of changes from the nll-master branch.

Extensions:

- we now propagate ty-region-outlives constraints out of closures and into their creator when necessary
- we fix a few ICEs that can occur by doing liveness analysis (and the resulting normalization) during type-checking
- we handle the implicit region bound that assumes that each type `T` outlives the fn body
- we handle normalization of inputs/outputs in fn signatures

Not included in this PR (will come next):

- handling `impl Trait`
- tracking causal information
- extended errors

r? @arielb1
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ich/impls_mir.rs21
-rw-r--r--src/librustc/ich/impls_ty.rs3
-rw-r--r--src/librustc/infer/combine.rs8
-rw-r--r--src/librustc/infer/error_reporting/mod.rs8
-rw-r--r--src/librustc/infer/freshen.rs7
-rw-r--r--src/librustc/infer/lexical_region_resolve/mod.rs7
-rw-r--r--src/librustc/mir/mod.rs42
-rw-r--r--src/librustc/ty/fold.rs15
-rw-r--r--src/librustc/ty/maps/mod.rs2
-rw-r--r--src/librustc/ty/mod.rs10
-rw-r--r--src/librustc/ty/sty.rs26
-rw-r--r--src/librustc/ty/util.rs2
-rw-r--r--src/librustc/util/ppaux.rs8
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/mod.rs1
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs1
-rw-r--r--src/librustc_mir/borrow_check/mod.rs8
-rw-r--r--src/librustc_mir/borrow_check/nll/constraint_generation.rs173
-rw-r--r--src/librustc_mir/borrow_check/nll/mod.rs73
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/dfs.rs234
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/mod.rs862
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/values.rs63
-rw-r--r--src/librustc_mir/borrow_check/nll/renumber.rs68
-rw-r--r--src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs72
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/liveness.rs220
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs (renamed from src/librustc_mir/transform/type_check.rs)105
-rw-r--r--src/librustc_mir/borrow_check/nll/universal_regions.rs82
-rw-r--r--src/librustc_mir/dataflow/at_location.rs42
-rw-r--r--src/librustc_mir/transform/mod.rs2
-rw-r--r--src/librustc_mir/util/liveness.rs33
-rw-r--r--src/librustc_typeck/variance/constraints.rs1
-rw-r--r--src/librustdoc/clean/mod.rs1
-rw-r--r--src/test/compile-fail/mir_check_cast_closure.rs2
-rw-r--r--src/test/compile-fail/mir_check_cast_reify.rs2
-rw-r--r--src/test/compile-fail/mir_check_cast_unsafe_fn.rs2
-rw-r--r--src/test/mir-opt/nll/named-lifetimes-basic.rs24
-rw-r--r--src/test/mir-opt/nll/reborrow-basic.rs8
-rw-r--r--src/test/mir-opt/nll/region-liveness-basic.rs8
-rw-r--r--src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs4
-rw-r--r--src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs4
-rw-r--r--src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs12
-rw-r--r--src/test/mir-opt/nll/region-subtyping-basic.rs12
-rw-r--r--src/test/ui/nll/capture-ref-in-struct.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/escape-argument-callee.stderr3
-rw-r--r--src/test/ui/nll/closure-requirements/escape-argument.stderr5
-rw-r--r--src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr13
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr4
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr5
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr45
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr4
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr7
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr7
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs60
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr60
-rw-r--r--src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr3
-rw-r--r--src/test/ui/nll/projection-return.rs29
-rw-r--r--src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs68
-rw-r--r--src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr157
-rw-r--r--src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs53
-rw-r--r--src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr26
-rw-r--r--src/test/ui/nll/ty-outlives/projection-one-region-closure.rs106
-rw-r--r--src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr194
-rw-r--r--src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs106
-rw-r--r--src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr204
-rw-r--r--src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs99
-rw-r--r--src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr155
-rw-r--r--src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs135
-rw-r--r--src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr326
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs66
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr58
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs96
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr191
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-fn-body.rs41
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr14
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-fn.rs51
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-fn.stderr26
78 files changed, 4057 insertions, 646 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index 32577ac46f3..df67c3abbe8 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -536,14 +536,29 @@ 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 {
+impl_stable_hash_for!(struct mir::ClosureRegionRequirements<'tcx> {
     num_external_vids,
     outlives_requirements
 });
 
-impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement {
-    free_region,
+impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement<'tcx> {
+    subject,
     outlived_free_region,
     blame_span
 });
 
+impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::ClosureOutlivesSubject<'gcx> {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'gcx>,
+                                          hasher: &mut StableHasher<W>) {
+        mem::discriminant(self).hash_stable(hcx, hasher);
+        match *self {
+            mir::ClosureOutlivesSubject::Ty(ref ty) => {
+                ty.hash_stable(hcx, hasher);
+            }
+            mir::ClosureOutlivesSubject::Region(ref region) => {
+                region.hash_stable(hcx, hasher);
+            }
+        }
+    }
+}
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 2655e2acbbd..ea3a1074aa2 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -75,6 +75,9 @@ for ty::RegionKind {
             ty::ReFree(ref free_region) => {
                 free_region.hash_stable(hcx, hasher);
             }
+            ty::ReClosureBound(vid) => {
+                vid.hash_stable(hcx, hasher);
+            }
             ty::ReLateBound(..) |
             ty::ReVar(..) |
             ty::ReSkolemized(..) => {
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 50a37e12531..f7bc092a3d7 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -475,6 +475,14 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                     ty::Bivariant | ty::Covariant | ty::Contravariant => (),
                 }
             }
+
+            ty::ReClosureBound(..) => {
+                span_bug!(
+                    self.span,
+                    "encountered unexpected ReClosureBound: {:?}",
+                    r,
+                );
+            }
         }
 
         // FIXME: This is non-ideal because we don't give a
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 514b29120a9..3e3aea0256e 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -240,6 +240,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             ty::ReErased => {
                 (format!("lifetime {:?}", region), None)
             }
+
+            // We shouldn't encounter an error message with ReClosureBound.
+            ty::ReClosureBound(..) => {
+                bug!(
+                    "encountered unexpected ReClosureBound: {:?}",
+                    region,
+                );
+            }
         };
         let message = format!("{}{}{}", prefix, description, suffix);
         if let Some(span) = span {
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index 426c61e9ac0..1783d5abfc7 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -113,6 +113,13 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
                 // replace all free regions with 'erased
                 self.tcx().types.re_erased
             }
+
+            ty::ReClosureBound(..) => {
+                bug!(
+                    "encountered unexpected ReClosureBound: {:?}",
+                    r,
+                );
+            }
         }
     }
 
diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs
index 5a4f2157298..3ac4ec5bee4 100644
--- a/src/librustc/infer/lexical_region_resolve/mod.rs
+++ b/src/librustc/infer/lexical_region_resolve/mod.rs
@@ -258,7 +258,12 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
     fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
         let tcx = self.region_rels.tcx;
         match (a, b) {
-            (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => {
+            (&ty::ReClosureBound(..), _) |
+            (_, &ty::ReClosureBound(..)) |
+            (&ReLateBound(..), _) |
+            (_, &ReLateBound(..)) |
+            (&ReErased, _) |
+            (_, &ReErased) => {
                 bug!("cannot relate region: LUB({:?}, {:?})", a, b);
             }
 
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 0ac2c2d4de8..efde224c3e1 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1832,8 +1832,17 @@ pub struct GeneratorLayout<'tcx> {
 /// instance of the closure is created, the corresponding free regions
 /// can be extracted from its type and constrained to have the given
 /// outlives relationship.
+///
+/// In some cases, we have to record outlives requirements between
+/// types and regions as well. In that case, if those types include
+/// any regions, those regions are recorded as `ReClosureBound`
+/// instances assigned one of these same indices. Those regions will
+/// be substituted away by the creator. We use `ReClosureBound` in
+/// that case because the regions must be allocated in the global
+/// TyCtxt, and hence we cannot use `ReVar` (which is what we use
+/// internally within the rest of the NLL code).
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
-pub struct ClosureRegionRequirements {
+pub struct ClosureRegionRequirements<'gcx> {
     /// 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
@@ -1843,15 +1852,15 @@ pub struct ClosureRegionRequirements {
 
     /// Requirements between the various free regions defined in
     /// indices.
-    pub outlives_requirements: Vec<ClosureOutlivesRequirement>,
+    pub outlives_requirements: Vec<ClosureOutlivesRequirement<'gcx>>,
 }
 
-/// Indicates an outlives constraint between two free-regions declared
-/// on the closure.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
-pub struct ClosureOutlivesRequirement {
-    // This region ...
-    pub free_region: ty::RegionVid,
+/// Indicates an outlives constraint between a type or between two
+/// free-regions declared on the closure.
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
+pub struct ClosureOutlivesRequirement<'tcx> {
+    // This region or type ...
+    pub subject: ClosureOutlivesSubject<'tcx>,
 
     // .. must outlive this one.
     pub outlived_free_region: ty::RegionVid,
@@ -1860,6 +1869,23 @@ pub struct ClosureOutlivesRequirement {
     pub blame_span: Span,
 }
 
+/// The subject of a ClosureOutlivesRequirement -- that is, the thing
+/// that must outlive some region.
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
+pub enum ClosureOutlivesSubject<'tcx> {
+    /// Subject is a type, typically a type parameter, but could also
+    /// be a projection. Indicates a requirement like `T: 'a` being
+    /// passed to the caller, where the type here is `T`.
+    ///
+    /// The type here is guaranteed not to contain any free regions at
+    /// present.
+    Ty(Ty<'tcx>),
+
+    /// Subject is a free region from the closure. Indicates a requirement
+    /// like `'a: 'b` being passed to the caller; the region here is `'a`.
+    Region(ty::RegionVid),
+}
+
 /*
  * TypeFoldable implementations for MIR types
  */
diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs
index 069dc0275cb..c5b82730e48 100644
--- a/src/librustc/ty/fold.rs
+++ b/src/librustc/ty/fold.rs
@@ -97,14 +97,19 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
     fn has_closure_types(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_TY_CLOSURE)
     }
+    /// "Free" regions in this context means that it has any region
+    /// that is not (a) erased or (b) late-bound.
+    fn has_free_regions(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
+    }
+
+    /// True if there any any un-erased free regions.
     fn has_erasable_regions(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND |
-                            TypeFlags::HAS_RE_INFER |
-                            TypeFlags::HAS_FREE_REGIONS)
+        self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
     }
+
     fn is_normalized_for_trans(&self) -> bool {
-        !self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND |
-                             TypeFlags::HAS_RE_INFER |
+        !self.has_type_flags(TypeFlags::HAS_RE_INFER |
                              TypeFlags::HAS_FREE_REGIONS |
                              TypeFlags::HAS_TY_INFER |
                              TypeFlags::HAS_PARAMS |
diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs
index f6dd8b6e021..23531b7b942 100644
--- a/src/librustc/ty/maps/mod.rs
+++ b/src/librustc/ty/maps/mod.rs
@@ -193,7 +193,7 @@ define_maps! { <'tcx>
 
     /// 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>,
+    [] fn mir_borrowck: MirBorrowCheck(DefId) -> Option<mir::ClosureRegionRequirements<'tcx>>,
 
     /// 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/mod.rs b/src/librustc/ty/mod.rs
index 220e1e971ac..3ab322b55c7 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -421,8 +421,18 @@ bitflags! {
         const HAS_TY_INFER       = 1 << 2;
         const HAS_RE_INFER       = 1 << 3;
         const HAS_RE_SKOL        = 1 << 4;
+
+        /// Does this have any `ReEarlyBound` regions? Used to
+        /// determine whether substitition is required, since those
+        /// represent regions that are bound in a `ty::Generics` and
+        /// hence may be substituted.
         const HAS_RE_EARLY_BOUND = 1 << 5;
+
+        /// Does this have any region that "appears free" in the type?
+        /// Basically anything but `ReLateBound` and `ReErased`.
         const HAS_FREE_REGIONS   = 1 << 6;
+
+        /// Is an error type reachable?
         const HAS_TY_ERR         = 1 << 7;
         const HAS_PROJECTION     = 1 << 8;
 
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index e085d1311c3..02729c6d600 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -1036,6 +1036,12 @@ pub enum RegionKind {
 
     /// Erased region, used by trait selection, in MIR and during trans.
     ReErased,
+
+    /// These are regions bound in the "defining type" for a
+    /// closure. They are used ONLY as part of the
+    /// `ClosureRegionRequirements` that are produced by MIR borrowck.
+    /// See `ClosureRegionRequirements` for more details.
+    ReClosureBound(RegionVid),
 }
 
 impl<'tcx> serialize::UseSpecializedDecodable for Region<'tcx> {}
@@ -1184,18 +1190,32 @@ impl RegionKind {
 
         match *self {
             ty::ReVar(..) => {
+                flags = flags | TypeFlags::HAS_FREE_REGIONS;
                 flags = flags | TypeFlags::HAS_RE_INFER;
                 flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX;
             }
             ty::ReSkolemized(..) => {
+                flags = flags | TypeFlags::HAS_FREE_REGIONS;
                 flags = flags | TypeFlags::HAS_RE_INFER;
                 flags = flags | TypeFlags::HAS_RE_SKOL;
                 flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX;
             }
             ty::ReLateBound(..) => { }
-            ty::ReEarlyBound(..) => { flags = flags | TypeFlags::HAS_RE_EARLY_BOUND; }
-            ty::ReStatic | ty::ReErased => { }
-            _ => { flags = flags | TypeFlags::HAS_FREE_REGIONS; }
+            ty::ReEarlyBound(..) => {
+                flags = flags | TypeFlags::HAS_FREE_REGIONS;
+                flags = flags | TypeFlags::HAS_RE_EARLY_BOUND;
+            }
+            ty::ReEmpty |
+            ty::ReStatic |
+            ty::ReFree { .. } |
+            ty::ReScope { .. } => {
+                flags = flags | TypeFlags::HAS_FREE_REGIONS;
+            }
+            ty::ReErased => {
+            }
+            ty::ReClosureBound(..) => {
+                flags = flags | TypeFlags::HAS_FREE_REGIONS;
+            }
         }
 
         match *self {
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 46bc54b7f6a..9b930233fad 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -822,6 +822,8 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W>
             ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => {
                 self.def_id(def_id);
             }
+
+            ty::ReClosureBound(..) |
             ty::ReLateBound(..) |
             ty::ReFree(..) |
             ty::ReScope(..) |
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 9ff3d73f5c4..5bfa6464568 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -733,6 +733,9 @@ define_print! {
                 ty::ReErased => Ok(()),
                 ty::ReStatic => write!(f, "'static"),
                 ty::ReEmpty => write!(f, "'<empty>"),
+
+                // The user should never encounter these in unsubstituted form.
+                ty::ReClosureBound(vid) => write!(f, "{:?}", vid),
             }
         }
         debug {
@@ -743,6 +746,11 @@ define_print! {
                            data.name)
                 }
 
+                ty::ReClosureBound(ref vid) => {
+                    write!(f, "ReClosureBound({:?})",
+                           vid)
+                }
+
                 ty::ReLateBound(binder_id, ref bound_region) => {
                     write!(f, "ReLateBound({:?}, {:?})",
                            binder_id,
diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
index 8654f2a50e4..5cbe2822e5c 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
@@ -367,6 +367,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
                     ty::ReStatic => self.item_ub,
 
                     ty::ReEmpty |
+                    ty::ReClosureBound(..) |
                     ty::ReLateBound(..) |
                     ty::ReVar(..) |
                     ty::ReSkolemized(..) |
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 09694f84448..c144962fb2d 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -381,6 +381,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             },
             (RegionKind::ReLateBound(_, _), _) |
             (RegionKind::ReSkolemized(_, _), _) |
+            (RegionKind::ReClosureBound(_), _) |
             (RegionKind::ReErased, _) => {
                 span_bug!(drop_span, "region does not make sense in this context");
             },
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 39bcd2b6ae0..e0b03aec69a 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -22,16 +22,16 @@ use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
 use rustc::mir::ClosureRegionRequirements;
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::indexed_set::{IdxSetBuf};
+use rustc_data_structures::indexed_set::IdxSetBuf;
 use rustc_data_structures::indexed_vec::Idx;
 
 use syntax::ast;
 use syntax_pos::Span;
 
 use dataflow::{do_dataflow, DebugFormatted};
+use dataflow::FlowAtLocation;
 use dataflow::MoveDataParamEnv;
 use dataflow::{DataflowAnalysis, DataflowResultsConsumer};
-use dataflow::{FlowAtLocation, FlowsAtLocation};
 use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
 use dataflow::{EverInitializedLvals, MovingOutStatements};
 use dataflow::{Borrows, BorrowData, ReserveOrActivateIndex};
@@ -65,7 +65,7 @@ pub fn provide(providers: &mut Providers) {
 fn mir_borrowck<'a, 'tcx>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     def_id: DefId,
-) -> Option<ClosureRegionRequirements> {
+) -> Option<ClosureRegionRequirements<'tcx>> {
     let input_mir = tcx.mir_validated(def_id);
     debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
 
@@ -89,7 +89,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     input_mir: &Mir<'gcx>,
     def_id: DefId,
-) -> Option<ClosureRegionRequirements> {
+) -> Option<ClosureRegionRequirements<'gcx>> {
     let tcx = infcx.tcx;
     let attributes = tcx.get_attrs(def_id);
     let param_env = tcx.param_env(def_id);
diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
index 59e862a56af..673e85e6b61 100644
--- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
@@ -12,21 +12,13 @@ use rustc::hir;
 use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue};
 use rustc::mir::visit::Visitor;
 use rustc::mir::Place::Projection;
-use rustc::mir::{Local, PlaceProjection, ProjectionElem};
+use rustc::mir::{PlaceProjection, ProjectionElem};
 use rustc::mir::visit::TyContext;
 use rustc::infer::InferCtxt;
-use rustc::traits::{self, ObligationCause};
-use rustc::ty::{self, ClosureSubsts, Ty};
+use rustc::ty::{self, ClosureSubsts};
 use rustc::ty::subst::Substs;
 use rustc::ty::fold::TypeFoldable;
-use rustc::util::common::ErrorReported;
-use rustc_data_structures::fx::FxHashSet;
-use syntax::codemap::DUMMY_SP;
-use borrow_check::{FlowAtLocation, FlowsAtLocation};
-use dataflow::MaybeInitializedLvals;
-use dataflow::move_paths::{HasMoveData, MoveData};
 
-use super::LivenessResults;
 use super::ToRegionVid;
 use super::region_infer::RegionInferenceContext;
 
@@ -34,19 +26,11 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
     infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
     regioncx: &mut RegionInferenceContext<'tcx>,
     mir: &Mir<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    liveness: &LivenessResults,
-    flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
-    move_data: &MoveData<'tcx>,
 ) {
     let mut cg = ConstraintGeneration {
         infcx,
         regioncx,
         mir,
-        liveness,
-        param_env,
-        flow_inits,
-        move_data,
     };
 
     for (bb, data) in mir.basic_blocks().iter_enumerated() {
@@ -59,16 +43,10 @@ struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> {
     infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
     regioncx: &'cg mut RegionInferenceContext<'tcx>,
     mir: &'cg Mir<'tcx>,
-    liveness: &'cg LivenessResults,
-    param_env: ty::ParamEnv<'tcx>,
-    flow_inits: &'cg mut FlowAtLocation<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
-    move_data: &'cg MoveData<'tcx>,
 }
 
-
 impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> {
     fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
-        self.add_liveness_constraints(bb);
         self.super_basic_block_data(bb, data);
     }
 
@@ -130,84 +108,6 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
 }
 
 impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
-    /// Liveness constraints:
-    ///
-    /// > If a variable V is live at point P, then all regions R in the type of V
-    /// > must include the point P.
-    fn add_liveness_constraints(&mut self, bb: BasicBlock) {
-        debug!("add_liveness_constraints(bb={:?})", bb);
-
-        self.liveness
-            .regular
-            .simulate_block(self.mir, bb, |location, live_locals| {
-                for live_local in live_locals.iter() {
-                    let live_local_ty = self.mir.local_decls[live_local].ty;
-                    self.add_regular_live_constraint(live_local_ty, location);
-                }
-            });
-
-        let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
-        self.liveness
-            .drop
-            .simulate_block(self.mir, bb, |location, live_locals| {
-                all_live_locals.push((location, live_locals.iter().collect()));
-            });
-        debug!(
-            "add_liveness_constraints: all_live_locals={:#?}",
-            all_live_locals
-        );
-
-        let terminator_index = self.mir.basic_blocks()[bb].statements.len();
-        self.flow_inits.reset_to_entry_of(bb);
-        while let Some((location, live_locals)) = all_live_locals.pop() {
-            for live_local in live_locals {
-                debug!(
-                    "add_liveness_constraints: location={:?} live_local={:?}",
-                    location,
-                    live_local
-                );
-
-                self.flow_inits.each_state_bit(|mpi_init| {
-                    debug!(
-                        "add_liveness_constraints: location={:?} initialized={:?}",
-                        location,
-                        &self.flow_inits
-                            .operator()
-                            .move_data()
-                            .move_paths[mpi_init]
-                    );
-                });
-
-                let mpi = self.move_data.rev_lookup.find_local(live_local);
-                if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
-                    debug!(
-                        "add_liveness_constraints: mpi={:?} has initialized child {:?}",
-                        self.move_data.move_paths[mpi],
-                        self.move_data.move_paths[initialized_child]
-                    );
-
-                    let live_local_ty = self.mir.local_decls[live_local].ty;
-                    self.add_drop_live_constraint(live_local_ty, location);
-                }
-            }
-
-            if location.statement_index == terminator_index {
-                debug!(
-                    "add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
-                    location
-                );
-                self.flow_inits.reconstruct_terminator_effect(location);
-            } else {
-                debug!(
-                    "add_liveness_constraints: reconstruct_statement_effect from {:#?}",
-                    location
-                );
-                self.flow_inits.reconstruct_statement_effect(location);
-            }
-            self.flow_inits.apply_local_effect(location);
-        }
-    }
-
     /// Some variable with type `live_ty` is "regular live" at
     /// `location` -- i.e., it may be used later. This means that all
     /// regions appearing in the type `live_ty` must be live at
@@ -230,75 +130,6 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
             });
     }
 
-    /// Some variable with type `live_ty` is "drop live" at `location`
-    /// -- i.e., it may be dropped later. This means that *some* of
-    /// the regions in its type must be live at `location`. The
-    /// precise set will depend on the dropck constraints, and in
-    /// particular this takes `#[may_dangle]` into account.
-    fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) {
-        debug!(
-            "add_drop_live_constraint(dropped_ty={:?}, location={:?})",
-            dropped_ty,
-            location
-        );
-
-        let tcx = self.infcx.tcx;
-        let mut types = vec![(dropped_ty, 0)];
-        let mut known = FxHashSet();
-        while let Some((ty, depth)) = types.pop() {
-            let span = DUMMY_SP; // FIXME
-            let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
-                Ok(result) => result,
-                Err(ErrorReported) => {
-                    continue;
-                }
-            };
-
-            let ty::DtorckConstraint {
-                outlives,
-                dtorck_types,
-            } = result;
-
-            // All things in the `outlives` array may be touched by
-            // the destructor and must be live at this point.
-            for outlive in outlives {
-                self.add_regular_live_constraint(outlive, location);
-            }
-
-            // However, there may also be some types that
-            // `dtorck_constraint_for_ty` could not resolve (e.g.,
-            // associated types and parameters). We need to normalize
-            // associated types here and possibly recursively process.
-            for ty in dtorck_types {
-                let cause = ObligationCause::dummy();
-                // We know that our original `dropped_ty` is well-formed,
-                // so region obligations resulting from this normalization
-                // should always hold.
-                //
-                // Therefore we ignore them instead of trying to match
-                // them up with a location.
-                let fulfillcx = traits::FulfillmentContext::new_ignoring_regions();
-                match traits::fully_normalize_with_fulfillcx(
-                    self.infcx, fulfillcx, cause, self.param_env, &ty
-                ) {
-                    Ok(ty) => match ty.sty {
-                        ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
-                            self.add_regular_live_constraint(ty, location);
-                        }
-
-                        _ => if known.insert(ty) {
-                            types.push((ty, depth + 1));
-                        },
-                    },
-
-                    Err(errors) => {
-                        self.infcx.report_fulfillment_errors(&errors, None);
-                    }
-                }
-            }
-        }
-    }
-
     fn add_reborrow_constraint(
         &mut self,
         location: Location,
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index 218142f7e2d..6977d91d25a 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -9,16 +9,16 @@
 // except according to those terms.
 
 use rustc::hir::def_id::DefId;
-use rustc::mir::{ClosureRegionRequirements, Mir};
+use rustc::mir::{ClosureRegionRequirements, ClosureOutlivesSubject, Mir};
 use rustc::infer::InferCtxt;
 use rustc::ty::{self, RegionKind, RegionVid};
 use rustc::util::nodemap::FxHashMap;
 use std::collections::BTreeSet;
+use std::fmt::Debug;
 use std::io;
 use transform::MirSource;
-use transform::type_check;
-use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
-use borrow_check::FlowAtLocation;
+use util::liveness::{LivenessResults, LocalSet};
+use dataflow::FlowAtLocation;
 use dataflow::MaybeInitializedLvals;
 use dataflow::move_paths::MoveData;
 
@@ -27,14 +27,15 @@ use util::pretty::{self, ALIGN};
 use self::mir_util::PassWhere;
 
 mod constraint_generation;
+pub(crate) mod region_infer;
+mod renumber;
 mod subtype_constraint_generation;
+pub(crate) mod type_check;
 mod universal_regions;
-use self::universal_regions::UniversalRegions;
 
-pub(crate) mod region_infer;
 use self::region_infer::RegionInferenceContext;
+use self::universal_regions::UniversalRegions;
 
-mod renumber;
 
 /// Rewrites the regions in the MIR to use NLL variables, also
 /// scraping out the set of universal regions (e.g., region parameters)
@@ -52,7 +53,7 @@ pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
     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);
+    renumber::renumber_mir(infcx, mir);
 
     let source = MirSource::item(def_id);
     mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, mir, |_, _| Ok(()));
@@ -73,11 +74,24 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     move_data: &MoveData<'tcx>,
 ) -> (
     RegionInferenceContext<'tcx>,
-    Option<ClosureRegionRequirements>,
+    Option<ClosureRegionRequirements<'gcx>>,
 ) {
     // 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);
+    let liveness = &LivenessResults::compute(mir);
+    let fr_fn_body = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
+    let constraint_sets = &type_check::type_check(
+        infcx,
+        mir_node_id,
+        param_env,
+        mir,
+        fr_fn_body,
+        universal_regions.input_tys,
+        universal_regions.output_ty,
+        &liveness,
+        flow_inits,
+        move_data,
+    );
 
     // Create the region inference context, taking ownership of the region inference
     // data that was contained in `infcx`.
@@ -85,35 +99,9 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     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 {
-        regular: liveness::liveness_of_locals(
-            &mir,
-            LivenessMode {
-                include_regular_use: true,
-                include_drops: false,
-            },
-        ),
-
-        drop: liveness::liveness_of_locals(
-            &mir,
-            LivenessMode {
-                include_regular_use: false,
-                include_drops: true,
-            },
-        ),
-    };
 
     // Generate non-subtyping constraints.
-    constraint_generation::generate_constraints(
-        infcx,
-        &mut regioncx,
-        &mir,
-        param_env,
-        liveness,
-        flow_inits,
-        move_data,
-    );
+    constraint_generation::generate_constraints(infcx, &mut regioncx, &mir);
 
     // Solve the region constraints.
     let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
@@ -136,11 +124,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     (regioncx, closure_region_requirements)
 }
 
-struct LivenessResults {
-    regular: LivenessResult,
-    drop: LivenessResult,
-}
-
 fn dump_mir_results<'a, 'gcx, 'tcx>(
     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     liveness: &LivenessResults,
@@ -283,9 +266,13 @@ fn for_each_region_constraint(
     with_msg: &mut FnMut(&str) -> io::Result<()>,
 ) -> io::Result<()> {
     for req in &closure_region_requirements.outlives_requirements {
+        let subject: &Debug = match &req.subject {
+            ClosureOutlivesSubject::Region(subject) => subject,
+            ClosureOutlivesSubject::Ty(ty) => ty,
+        };
         with_msg(&format!(
             "where {:?}: {:?}",
-            req.free_region,
+            subject,
             req.outlived_free_region,
         ))?;
     }
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs
new file mode 100644
index 00000000000..59860d61ab9
--- /dev/null
+++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs
@@ -0,0 +1,234 @@
+// 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.
+
+//! Module defining the `dfs` method on `RegionInferenceContext`, along with
+//! its associated helper traits.
+
+use borrow_check::nll::universal_regions::UniversalRegions;
+use borrow_check::nll::region_infer::RegionInferenceContext;
+use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements,
+                                              RegionValues};
+use rustc::mir::{Location, Mir};
+use rustc::ty::RegionVid;
+use rustc_data_structures::fx::FxHashSet;
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+    /// Function used to satisfy or test a `R1: R2 @ P`
+    /// constraint. The core idea is that it performs a DFS starting
+    /// from `P`. The precise actions *during* that DFS depend on the
+    /// `op` supplied, so see (e.g.) `CopyFromSourceToTarget` for more
+    /// details.
+    ///
+    /// Returns:
+    ///
+    /// - `Ok(true)` if the walk was completed and something changed
+    ///   along the way;
+    /// - `Ok(false)` if the walk was completed with no changes;
+    /// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the
+    ///   value that `op` returned.
+    pub(super) fn dfs<C>(&self, mir: &Mir<'tcx>, mut op: C) -> Result<bool, C::Early>
+    where
+        C: DfsOp,
+    {
+        let mut changed = false;
+
+        let mut stack = vec![];
+        let mut visited = FxHashSet();
+
+        stack.push(op.start_point());
+        while let Some(p) = stack.pop() {
+            let point_index = self.elements.index(p);
+
+            if !op.source_region_contains(point_index) {
+                debug!("            not in from-region");
+                continue;
+            }
+
+            if !visited.insert(p) {
+                debug!("            already visited");
+                continue;
+            }
+
+            let new = op.add_to_target_region(point_index)?;
+            changed |= new;
+
+            let block_data = &mir[p.block];
+
+            let start_stack_len = stack.len();
+
+            if p.statement_index < block_data.statements.len() {
+                stack.push(Location {
+                    statement_index: p.statement_index + 1,
+                    ..p
+                });
+            } else {
+                stack.extend(block_data.terminator().successors().iter().map(
+                    |&basic_block| {
+                        Location {
+                            statement_index: 0,
+                            block: basic_block,
+                        }
+                    },
+                ));
+            }
+
+            if stack.len() == start_stack_len {
+                // If we reach the END point in the graph, then copy
+                // over any skolemized end points in the `from_region`
+                // and make sure they are included in the `to_region`.
+                changed |= op.add_universal_regions_outlived_by_source_to_target()?;
+            }
+        }
+
+        Ok(changed)
+    }
+}
+
+/// Customizes the operation of the `dfs` function. This function is
+/// used during inference to satisfy a `R1: R2 @ P` constraint.
+pub(super) trait DfsOp {
+    /// If this op stops the walk early, what type does it propagate?
+    type Early;
+
+    /// Returns the point from which to start the DFS.
+    fn start_point(&self) -> Location;
+
+    /// Returns true if the source region contains the given point.
+    fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool;
+
+    /// Adds the given point to the target region, returning true if
+    /// something has changed. Returns `Err` if we should abort the
+    /// walk early.
+    fn add_to_target_region(
+        &mut self,
+        point_index: RegionElementIndex,
+    ) -> Result<bool, Self::Early>;
+
+    /// Adds all universal regions in the source region to the target region, returning
+    /// true if something has changed.
+    fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result<bool, Self::Early>;
+}
+
+/// Used during inference to enforce a `R1: R2 @ P` constraint.  For
+/// each point Q we reach along the DFS, we check if Q is in R2 (the
+/// "source region"). If not, we stop the walk. Otherwise, we add Q to
+/// R1 (the "target region") and continue to Q's successors. If we
+/// reach the end of the graph, then we add any universal regions from
+/// R2 into R1.
+pub(super) struct CopyFromSourceToTarget<'v> {
+    pub source_region: RegionVid,
+    pub target_region: RegionVid,
+    pub inferred_values: &'v mut RegionValues,
+    pub constraint_point: Location,
+}
+
+impl<'v> DfsOp for CopyFromSourceToTarget<'v> {
+    /// We never stop the walk early.
+    type Early = !;
+
+    fn start_point(&self) -> Location {
+        self.constraint_point
+    }
+
+    fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool {
+        self.inferred_values
+            .contains(self.source_region, point_index)
+    }
+
+    fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result<bool, !> {
+        Ok(self.inferred_values.add(self.target_region, point_index))
+    }
+
+    fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result<bool, !> {
+        Ok(
+            self.inferred_values
+                .add_universal_regions_outlived_by(self.source_region, self.target_region),
+        )
+    }
+}
+
+/// Used after inference to *test* a `R1: R2 @ P` constraint.  For
+/// each point Q we reach along the DFS, we check if Q in R2 is also
+/// contained in R1. If not, we abort the walk early with an `Err`
+/// condition. Similarly, if we reach the end of the graph and find
+/// that R1 contains some universal region that R2 does not contain,
+/// we abort the walk early.
+pub(super) struct TestTargetOutlivesSource<'v, 'tcx: 'v> {
+    pub source_region: RegionVid,
+    pub target_region: RegionVid,
+    pub elements: &'v RegionValueElements,
+    pub universal_regions: &'v UniversalRegions<'tcx>,
+    pub inferred_values: &'v RegionValues,
+    pub constraint_point: Location,
+}
+
+impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> {
+    /// The element that was not found within R2.
+    type Early = RegionElementIndex;
+
+    fn start_point(&self) -> Location {
+        self.constraint_point
+    }
+
+    fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool {
+        self.inferred_values
+            .contains(self.source_region, point_index)
+    }
+
+    fn add_to_target_region(
+        &mut self,
+        point_index: RegionElementIndex,
+    ) -> Result<bool, RegionElementIndex> {
+        if !self.inferred_values
+            .contains(self.target_region, point_index)
+        {
+            return Err(point_index);
+        }
+
+        Ok(false)
+    }
+
+    fn add_universal_regions_outlived_by_source_to_target(
+        &mut self,
+    ) -> Result<bool, RegionElementIndex> {
+        // For all `ur_in_source` in `source_region`.
+        for ur_in_source in self.inferred_values
+            .universal_regions_outlived_by(self.source_region)
+        {
+            // Check that `target_region` outlives `ur_in_source`.
+
+            // If `ur_in_source` is a member of `target_region`, OK.
+            //
+            // (This is implied by the loop below, actually, just an
+            // irresistible micro-opt. Mm. Premature optimization. So
+            // tasty.)
+            if self.inferred_values
+                .contains(self.target_region, ur_in_source)
+            {
+                continue;
+            }
+
+            // If there is some other element X such that `target_region: X` and
+            // `X: ur_in_source`, OK.
+            if self.inferred_values
+                .universal_regions_outlived_by(self.target_region)
+                .any(|ur_in_target| {
+                    self.universal_regions.outlives(ur_in_target, ur_in_source)
+                }) {
+                continue;
+            }
+
+            // Otherwise, not known to be true.
+            return Err(self.elements.index(ur_in_source));
+        }
+
+        Ok(false)
+    }
+}
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 c926c7432bb..58e16e7673a 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -12,18 +12,23 @@ use super::universal_regions::UniversalRegions;
 use rustc::hir::def_id::DefId;
 use rustc::infer::InferCtxt;
 use rustc::infer::NLLRegionVariableOrigin;
+use rustc::infer::RegionObligation;
 use rustc::infer::RegionVariableOrigin;
 use rustc::infer::SubregionOrigin;
-use rustc::infer::region_constraints::VarOrigins;
-use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir};
-use rustc::ty::{self, RegionVid};
+use rustc::infer::region_constraints::{GenericKind, VarOrigins};
+use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
+                 Location, Mir};
+use rustc::traits::ObligationCause;
+use rustc::ty::{self, RegionVid, Ty, TypeFoldable};
 use rustc_data_structures::indexed_vec::IndexVec;
-use rustc_data_structures::fx::FxHashSet;
 use std::fmt;
 use std::rc::Rc;
+use syntax::ast;
 use syntax_pos::Span;
 
 mod annotation;
+mod dfs;
+use self::dfs::{CopyFromSourceToTarget, TestTargetOutlivesSource};
 mod dump_mir;
 mod graphviz;
 mod values;
@@ -52,6 +57,9 @@ pub struct RegionInferenceContext<'tcx> {
     /// The constraints we have accumulated and used during solving.
     constraints: Vec<Constraint>,
 
+    /// Type constraints that we check after solving.
+    type_tests: Vec<TypeTest<'tcx>>,
+
     /// Information about the universally quantified regions in scope
     /// on this function and their (known) relations to one another.
     universal_regions: UniversalRegions<'tcx>,
@@ -94,6 +102,92 @@ pub struct Constraint {
     span: Span,
 }
 
+/// A "type test" corresponds to an outlives constraint between a type
+/// and a lifetime, like `T: 'x` or `<T as Foo>::Bar: 'x`.  They are
+/// translated from the `Verify` region constraints in the ordinary
+/// inference context.
+///
+/// These sorts of constraints are handled differently than ordinary
+/// constraints, at least at present. During type checking, the
+/// `InferCtxt::process_registered_region_obligations` method will
+/// attempt to convert a type test like `T: 'x` into an ordinary
+/// outlives constraint when possible (for example, `&'a T: 'b` will
+/// be converted into `'a: 'b` and registered as a `Constraint`).
+///
+/// In some cases, however, there are outlives relationships that are
+/// not converted into a region constraint, but rather into one of
+/// these "type tests".  The distinction is that a type test does not
+/// influence the inference result, but instead just examines the
+/// values that we ultimately inferred for each region variable and
+/// checks that they meet certain extra criteria.  If not, an error
+/// can be issued.
+///
+/// One reason for this is that these type tests typically boil down
+/// to a check like `'a: 'x` where `'a` is a universally quantified
+/// region -- and therefore not one whose value is really meant to be
+/// *inferred*, precisely (this is not always the case: one can have a
+/// type test like `<Foo as Trait<'?0>>::Bar: 'x`, where `'?0` is an
+/// inference variable). Another reason is that these type tests can
+/// involve *disjunction* -- that is, they can be satisfied in more
+/// than one way.
+///
+/// For more information about this translation, see
+/// `InferCtxt::process_registered_region_obligations` and
+/// `InferCtxt::type_must_outlive` in `rustc::infer::outlives`.
+#[derive(Clone, Debug)]
+pub struct TypeTest<'tcx> {
+    /// The type `T` that must outlive the region.
+    pub generic_kind: GenericKind<'tcx>,
+
+    /// The region `'x` that the type must outlive.
+    pub lower_bound: RegionVid,
+
+    /// The point where the outlives relation must hold.
+    pub point: Location,
+
+    /// Where did this constraint arise?
+    pub span: Span,
+
+    /// A test which, if met by the region `'x`, proves that this type
+    /// constraint is satisfied.
+    pub test: RegionTest,
+}
+
+/// A "test" that can be applied to some "subject region" `'x`. These are used to
+/// describe type constraints. Tests do not presently affect the
+/// region values that get inferred for each variable; they only
+/// examine the results *after* inference.  This means they can
+/// conveniently include disjuction ("a or b must be true").
+#[derive(Clone, Debug)]
+pub enum RegionTest {
+    /// The subject region `'x` must by outlived by *some* region in
+    /// the given set of regions.
+    ///
+    /// This test comes from e.g. a where clause like `T: 'a + 'b`,
+    /// which implies that we know that `T: 'a` and that `T:
+    /// 'b`. Therefore, if we are trying to prove that `T: 'x`, we can
+    /// do so by showing that `'a: 'x` *or* `'b: 'x`.
+    IsOutlivedByAnyRegionIn(Vec<RegionVid>),
+
+    /// The subject region `'x` must by outlived by *all* regions in
+    /// the given set of regions.
+    ///
+    /// This test comes from e.g. a projection type like `T = <u32 as
+    /// Trait<'a, 'b>>::Foo`, which must outlive `'a` or `'b`, and
+    /// maybe both. Therefore we can prove that `T: 'x` if we know
+    /// that `'a: 'x` *and* `'b: 'x`.
+    IsOutlivedByAllRegionsIn(Vec<RegionVid>),
+
+    /// Any of the given tests are true.
+    ///
+    /// This arises from projections, for which there are multiple
+    /// ways to prove an outlives relationship.
+    Any(Vec<RegionTest>),
+
+    /// All of the given tests are true.
+    All(Vec<RegionTest>),
+}
+
 impl<'tcx> RegionInferenceContext<'tcx> {
     /// Creates a new region inference context with a total of
     /// `num_region_variables` valid inference variables; the first N
@@ -121,6 +215,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             liveness_constraints: RegionValues::new(elements, num_region_variables),
             inferred_values: None,
             constraints: Vec::new(),
+            type_tests: Vec::new(),
             universal_regions,
         };
 
@@ -242,32 +337,414 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         });
     }
 
-    /// Perform region inference.
-    pub(super) fn solve(
+    /// Add a "type test" that must be satisfied.
+    pub(super) fn add_type_test(&mut self, type_test: TypeTest<'tcx>) {
+        self.type_tests.push(type_test);
+    }
+
+    /// Perform region inference and report errors if we see any
+    /// unsatisfiable constraints. If this is a closure, returns the
+    /// region requirements to propagate to our creator, if any.
+    pub(super) fn solve<'gcx>(
         &mut self,
-        infcx: &InferCtxt<'_, '_, 'tcx>,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
         mir: &Mir<'tcx>,
         mir_def_id: DefId,
-    ) -> Option<ClosureRegionRequirements> {
+    ) -> Option<ClosureRegionRequirements<'gcx>> {
         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);
 
-        // Now, see whether any of the constraints were too strong. In
-        // particular, we want to check for a case where a universally
-        // quantified region exceeded its bounds.  Consider:
-        //
-        //     fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
-        //
-        // In this case, returning `x` requires `&'a u32 <: &'b u32`
-        // and hence we establish (transitively) a constraint that
-        // `'a: 'b`. The `propagate_constraints` code above will
-        // therefore add `end('a)` into the region for `'b` -- but we
-        // have no evidence that `'a` outlives `'b`, so we want to report
-        // an error.
+        // If this is a closure, we can propagate unsatisfied
+        // `outlives_requirements` to our creator, so create a vector
+        // to store those. Otherwise, we'll pass in `None` to the
+        // functions below, which will trigger them to report errors
+        // eagerly.
+        let mut outlives_requirements = if infcx.tcx.is_closure(mir_def_id) {
+            Some(vec![])
+        } else {
+            None
+        };
+
+        self.check_type_tests(infcx, mir, outlives_requirements.as_mut());
+
+        self.check_universal_regions(infcx, outlives_requirements.as_mut());
+
+        let outlives_requirements = outlives_requirements.unwrap_or(vec![]);
+
+        if outlives_requirements.is_empty() {
+            None
+        } else {
+            let num_external_vids = self.universal_regions.num_global_and_external_regions();
+            Some(ClosureRegionRequirements {
+                num_external_vids,
+                outlives_requirements,
+            })
+        }
+    }
+
+    /// 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
+    /// feasible, but we check this later.
+    fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
+        let mut changed = true;
+
+        debug!("propagate_constraints()");
+        debug!("propagate_constraints: constraints={:#?}", {
+            let mut constraints: Vec<_> = self.constraints.iter().collect();
+            constraints.sort();
+            constraints
+        });
+
+        // The initial values for each region are derived from the liveness
+        // constraints we have accumulated.
+        let mut inferred_values = self.liveness_constraints.clone();
+
+        while changed {
+            changed = false;
+            debug!("propagate_constraints: --------------------");
+            for constraint in &self.constraints {
+                debug!("propagate_constraints: constraint={:?}", constraint);
+
+                // Grow the value as needed to accommodate the
+                // outlives constraint.
+                let Ok(made_changes) = self.dfs(
+                    mir,
+                    CopyFromSourceToTarget {
+                        source_region: constraint.sub,
+                        target_region: constraint.sup,
+                        inferred_values: &mut inferred_values,
+                        constraint_point: constraint.point,
+                    },
+                );
+
+                if made_changes {
+                    debug!("propagate_constraints:   sub={:?}", constraint.sub);
+                    debug!("propagate_constraints:   sup={:?}", constraint.sup);
+                    changed = true;
+                }
+            }
+            debug!("\n");
+        }
+
+        self.inferred_values = Some(inferred_values);
+    }
+
+    /// Once regions have been propagated, this method is used to see
+    /// whether the "type tests" produced by typeck were satisfied;
+    /// type tests encode type-outlives relationships like `T:
+    /// 'a`. See `TypeTest` for more details.
+    fn check_type_tests<'gcx>(
+        &self,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+        mir: &Mir<'tcx>,
+        mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
+    ) {
+        let tcx = infcx.tcx;
+
+        for type_test in &self.type_tests {
+            debug!("check_type_test: {:?}", type_test);
+
+            if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) {
+                continue;
+            }
+
+            if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements {
+                if self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) {
+                    continue;
+                }
+            }
+
+            // Oh the humanity. Obviously we will do better than this error eventually.
+            tcx.sess.span_err(
+                type_test.span,
+                &format!(
+                    "`{}` does not outlive `{:?}`",
+                    type_test.generic_kind,
+                    type_test.lower_bound,
+                ),
+            );
+        }
+    }
+
+    fn try_promote_type_test<'gcx>(
+        &self,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+        type_test: &TypeTest<'tcx>,
+        propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'gcx>>,
+    ) -> bool {
+        let tcx = infcx.tcx;
+
+        let TypeTest {
+            generic_kind,
+            lower_bound,
+            point: _,
+            span,
+            test: _,
+        } = type_test;
+
+        let generic_ty = generic_kind.to_ty(tcx);
+        let subject = match self.try_promote_type_test_subject(infcx, generic_ty) {
+            Some(s) => s,
+            None => return false,
+        };
+
+        // Find some bounding subject-region R+ that is a super-region
+        // of the existing subject-region R. This should be a non-local, universal
+        // region, which ensures it can be encoded in a `ClosureOutlivesRequirement`.
+        let lower_bound_plus = self.non_local_universal_upper_bound(*lower_bound);
+        assert!(self.universal_regions.is_universal_region(lower_bound_plus));
+        assert!(!self.universal_regions
+            .is_local_free_region(lower_bound_plus));
+
+        propagated_outlives_requirements.push(ClosureOutlivesRequirement {
+            subject,
+            outlived_free_region: lower_bound_plus,
+            blame_span: *span,
+        });
+        true
+    }
+
+    /// When we promote a type test `T: 'r`, we have to convert the
+    /// type `T` into something we can store in a query result (so
+    /// something allocated for `'gcx`). This is problematic if `ty`
+    /// contains regions. During the course of NLL region checking, we
+    /// will have replaced all of those regions with fresh inference
+    /// variables. To create a test subject, we want to replace those
+    /// inference variables with some region from the closure
+    /// signature -- this is not always possible, so this is a
+    /// fallible process. Presuming we do find a suitable region, we
+    /// will represent it with a `ReClosureBound`, which is a
+    /// `RegionKind` variant that can be allocated in the gcx.
+    fn try_promote_type_test_subject<'gcx>(
+        &self,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Option<ClosureOutlivesSubject<'gcx>> {
+        let tcx = infcx.tcx;
+        let gcx = tcx.global_tcx();
+        let inferred_values = self.inferred_values
+            .as_ref()
+            .expect("region values not yet inferred");
+
+        debug!("try_promote_type_test_subject(ty = {:?})", ty);
+
+        let ty = tcx.fold_regions(&ty, &mut false, |r, _depth| {
+            let region_vid = self.to_region_vid(r);
+
+            // The challenge if this. We have some region variable `r`
+            // whose value is a set of CFG points and universal
+            // regions. We want to find if that set is *equivalent* to
+            // any of the named regions found in the closure.
+            //
+            // To do so, we compute the
+            // `non_local_universal_upper_bound`. This will be a
+            // non-local, universal region that is greater than `r`.
+            // However, it might not be *contained* within `r`, so
+            // then we further check whether this bound is contained
+            // in `r`. If so, we can say that `r` is equivalent to the
+            // bound.
+            //
+            // Let's work through a few examples. For these, imagine
+            // that we have 3 non-local regions (I'll denote them as
+            // `'static`, `'a`, and `'b`, though of course in the code
+            // they would be represented with indices) where:
+            //
+            // - `'static: 'a`
+            // - `'static: 'b`
+            //
+            // First, let's assume that `r` is some existential
+            // variable with an inferred value `{'a, 'static}` (plus
+            // some CFG nodes). In this case, the non-local upper
+            // bound is `'static`, since that outlives `'a`. `'static`
+            // is also a member of `r` and hence we consider `r`
+            // equivalent to `'static` (and replace it with
+            // `'static`).
+            //
+            // Now let's consider the inferred value `{'a, 'b}`. This
+            // means `r` is effectively `'a | 'b`. I'm not sure if
+            // this can come about, actually, but assuming it did, we
+            // would get a non-local upper bound of `'static`. Since
+            // `'static` is not contained in `r`, we would fail to
+            // find an equivalent.
+            let upper_bound = self.non_local_universal_upper_bound(region_vid);
+            if inferred_values.contains(region_vid, upper_bound) {
+                tcx.mk_region(ty::ReClosureBound(upper_bound))
+            } else {
+                // In the case of a failure, use a `ReVar`
+                // result. This will cause the `lift` later on to
+                // fail.
+                r
+            }
+        });
+        debug!("try_promote_type_test_subject: folded ty = {:?}", ty);
+
+        // `lift` will only fail if we failed to promote some region.
+        let ty = gcx.lift(&ty)?;
+
+        Some(ClosureOutlivesSubject::Ty(ty))
+    }
+
+    /// Given some universal or existential region `r`, finds a
+    /// non-local, universal region `r+` that outlives `r` at entry to (and
+    /// exit from) the closure. In the worst case, this will be
+    /// `'static`.
+    ///
+    /// This is used for two purposes. First, if we are propagated
+    /// some requirement `T: r`, we can use this method to enlarge `r`
+    /// to something we can encode for our creator (which only knows
+    /// about non-local, universal regions). It is also used when
+    /// encoding `T` as part of `try_promote_type_test_subject` (see
+    /// that fn for details).
+    ///
+    /// Since `r` is (potentially) an existential region, it has some
+    /// value which may include (a) any number of points in the CFG
+    /// and (b) any number of `end('x)` elements of universally
+    /// quantified regions. To convert this into a single universal
+    /// region we do as follows:
+    ///
+    /// - Ignore the CFG points in `'r`. All universally quantified regions
+    ///   include the CFG anyhow.
+    /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding
+    ///   a result `'y`.
+    /// - Finally, we take the non-local upper bound of `'y`.
+    ///   - This uses `UniversalRegions::non_local_upper_bound`, which
+    ///     is similar to this method but only works on universal
+    ///     regions).
+    fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
+        let inferred_values = self.inferred_values.as_ref().unwrap();
+
+        debug!(
+            "non_local_universal_upper_bound(r={:?}={})",
+            r,
+            inferred_values.region_value_str(r)
+        );
+
+        // Find the smallest universal region that contains all other
+        // universal regions within `region`.
+        let mut lub = self.universal_regions.fr_fn_body;
+        for ur in inferred_values.universal_regions_outlived_by(r) {
+            lub = self.universal_regions.postdom_upper_bound(lub, ur);
+        }
+
+        debug!("non_local_universal_upper_bound: lub={:?}", lub);
+
+        // Grow further to get smallest universal region known to
+        // creator.
+        let non_local_lub = self.universal_regions.non_local_upper_bound(lub);
+
+        debug!(
+            "non_local_universal_upper_bound: non_local_lub={:?}",
+            non_local_lub
+        );
+
+        non_local_lub
+    }
+
+    /// Test if `test` is true when applied to `lower_bound` at
+    /// `point`, and returns true or false.
+    fn eval_region_test(
+        &self,
+        mir: &Mir<'tcx>,
+        point: Location,
+        lower_bound: RegionVid,
+        test: &RegionTest,
+    ) -> bool {
+        debug!(
+            "eval_region_test(point={:?}, lower_bound={:?}, test={:?})",
+            point,
+            lower_bound,
+            test
+        );
 
+        match test {
+            RegionTest::IsOutlivedByAllRegionsIn(regions) => regions
+                .iter()
+                .all(|&r| self.eval_outlives(mir, r, lower_bound, point)),
+
+            RegionTest::IsOutlivedByAnyRegionIn(regions) => regions
+                .iter()
+                .any(|&r| self.eval_outlives(mir, r, lower_bound, point)),
+
+            RegionTest::Any(tests) => tests
+                .iter()
+                .any(|test| self.eval_region_test(mir, point, lower_bound, test)),
+
+            RegionTest::All(tests) => tests
+                .iter()
+                .all(|test| self.eval_region_test(mir, point, lower_bound, test)),
+        }
+    }
+
+    // Evaluate whether `sup_region: sub_region @ point`.
+    fn eval_outlives(
+        &self,
+        mir: &Mir<'tcx>,
+        sup_region: RegionVid,
+        sub_region: RegionVid,
+        point: Location,
+    ) -> bool {
+        debug!(
+            "eval_outlives({:?}: {:?} @ {:?})",
+            sup_region,
+            sub_region,
+            point
+        );
+
+        // Roughly speaking, do a DFS of all region elements reachable
+        // from `point` contained in `sub_region`. If any of those are
+        // *not* present in `sup_region`, the DFS will abort early and
+        // yield an `Err` result.
+        match self.dfs(
+            mir,
+            TestTargetOutlivesSource {
+                source_region: sub_region,
+                target_region: sup_region,
+                constraint_point: point,
+                elements: &self.elements,
+                universal_regions: &self.universal_regions,
+                inferred_values: self.inferred_values.as_ref().unwrap(),
+            },
+        ) {
+            Ok(_) => {
+                debug!("eval_outlives: true");
+                true
+            }
+
+            Err(elem) => {
+                debug!(
+                    "eval_outlives: false because `{:?}` is not present in `{:?}`",
+                    self.elements.to_element(elem),
+                    sup_region
+                );
+                false
+            }
+        }
+    }
+
+    /// Once regions have been propagated, this method is used to see
+    /// whether any of the constraints were too strong. In particular,
+    /// we want to check for a case where a universally quantified
+    /// region exceeded its bounds.  Consider:
+    ///
+    ///     fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
+    ///
+    /// In this case, returning `x` requires `&'a u32 <: &'b u32`
+    /// and hence we establish (transitively) a constraint that
+    /// `'a: 'b`. The `propagate_constraints` code above will
+    /// therefore add `end('a)` into the region for `'b` -- but we
+    /// have no evidence that `'b` outlives `'a`, so we want to report
+    /// an error.
+    ///
+    /// If `propagated_outlives_requirements` is `Some`, then we will
+    /// push unsatisfied obligations into there. Otherwise, we'll
+    /// report them as errors.
+    fn check_universal_regions<'gcx>(
+        &self,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+        mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
+    ) {
         // The universal regions are always found in a prefix of the
         // full list.
         let universal_definitions = self.definitions
@@ -277,32 +754,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // 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);
+            self.check_universal_region(infcx, fr, &mut propagated_outlives_requirements);
         }
-
-        // 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,
-        })
     }
 
     /// Check the final value for the free region `fr` to see if it
@@ -313,11 +767,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ///
     /// Things that are to be propagated are accumulated into the
     /// `outlives_requirements` vector.
-    fn check_universal_region(
+    fn check_universal_region<'gcx>(
         &self,
-        infcx: &InferCtxt<'_, '_, 'tcx>,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
         longer_fr: RegionVid,
-        outlives_requirements: &mut Vec<ClosureOutlivesRequirement>,
+        propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
     ) {
         let inferred_values = self.inferred_values.as_ref().unwrap();
 
@@ -339,33 +793,39 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
             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 let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
+                // 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+`
+                    propagated_outlives_requirements.push(ClosureOutlivesRequirement {
+                        subject: ClosureOutlivesSubject::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.
+            // If we are not in a context where we can propagate
+            // errors, or we could not shrink `fr` to something
+            // smaller, then just report an error.
+            //
+            // Note: in this case, we use the unapproximated regions
+            // to report the error. This gives better error messages
+            // in some cases.
             self.report_error(infcx, longer_fr, shorter_fr, blame_span);
         }
     }
@@ -395,117 +855,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         );
     }
 
-    /// 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
-    /// feasible, but we check this later.
-    fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
-        let mut changed = true;
-
-        debug!("propagate_constraints()");
-        debug!("propagate_constraints: constraints={:#?}", {
-            let mut constraints: Vec<_> = self.constraints.iter().collect();
-            constraints.sort();
-            constraints
-        });
-
-        // The initial values for each region are derived from the liveness
-        // constraints we have accumulated.
-        let mut inferred_values = self.liveness_constraints.clone();
-
-        while changed {
-            changed = false;
-            debug!("propagate_constraints: --------------------");
-            for constraint in &self.constraints {
-                debug!("propagate_constraints: constraint={:?}", constraint);
-
-                // Grow the value as needed to accommodate the
-                // outlives constraint.
-
-                if self.copy(
-                    &mut inferred_values,
-                    mir,
-                    constraint.sub,
-                    constraint.sup,
-                    constraint.point,
-                ) {
-                    debug!("propagate_constraints:   sub={:?}", constraint.sub);
-                    debug!("propagate_constraints:   sup={:?}", constraint.sup);
-                    changed = true;
-                }
-            }
-            debug!("\n");
-        }
-
-        self.inferred_values = Some(inferred_values);
-    }
-
-    fn copy(
-        &self,
-        inferred_values: &mut RegionValues,
-        mir: &Mir<'tcx>,
-        from_region: RegionVid,
-        to_region: RegionVid,
-        constraint_point: Location,
-    ) -> bool {
-        let mut changed = false;
-
-        let mut stack = vec![];
-        let mut visited = FxHashSet();
-
-        stack.push(constraint_point);
-        while let Some(p) = stack.pop() {
-            let point_index = self.elements.index(p);
-
-            if !inferred_values.contains(from_region, point_index) {
-                debug!("            not in from-region");
-                continue;
-            }
-
-            if !visited.insert(p) {
-                debug!("            already visited");
-                continue;
-            }
-
-            let new = inferred_values.add(to_region, point_index);
-            changed |= new;
-
-            let block_data = &mir[p.block];
-            let successor_points = if p.statement_index < block_data.statements.len() {
-                vec![
-                    Location {
-                        statement_index: p.statement_index + 1,
-                        ..p
-                    },
-                ]
-            } else {
-                block_data
-                    .terminator()
-                    .successors()
-                    .iter()
-                    .map(|&basic_block| {
-                        Location {
-                            statement_index: 0,
-                            block: basic_block,
-                        }
-                    })
-                    .collect::<Vec<_>>()
-            };
-
-            if successor_points.is_empty() {
-                // If we reach the END point in the graph, then copy
-                // over any skolemized end points in the `from_region`
-                // and make sure they are included in the `to_region`.
-                changed |=
-                    inferred_values.add_universal_regions_outlived_by(from_region, to_region);
-            } else {
-                stack.extend(successor_points);
-            }
-        }
-
-        changed
-    }
-
     /// Tries to finds a good span to blame for the fact that `fr1`
     /// contains `fr2`.
     fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
@@ -520,35 +869,50 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // be obvious to the user -- not to mention the naive notion
         // of dependencies, which doesn't account for the locations of
         // contraints at all. But it will do for now.
-        for constraint in &self.constraints {
-            if constraint.sub == fr2 && influenced_fr1[constraint.sup] {
-                return constraint.span;
-            }
-        }
-
-        bug!(
-            "could not find any constraint to blame for {:?}: {:?}",
-            fr1,
-            fr2
-        );
+        let relevant_constraint = self.constraints
+                .iter()
+                .filter_map(|constraint| {
+                    if constraint.sub != fr2 {
+                        None
+                    } else {
+                        influenced_fr1[constraint.sup]
+                            .map(|distance| (distance, constraint.span))
+                    }
+                })
+                .min() // constraining fr1 with fewer hops *ought* to be more obvious
+                .map(|(_dist, span)| span);
+
+        relevant_constraint.unwrap_or_else(|| {
+            bug!(
+                "could not find any constraint to blame for {:?}: {:?}",
+                fr1,
+                fr2
+            );
+        })
     }
 
     /// Finds all regions whose values `'a` may depend on in some way.
-    /// Basically if there exists a constraint `'a: 'b @ P`, then `'b`
-    /// and `dependencies('b)` will be in the final set.
+    /// For each region, returns either `None` (does not influence
+    /// `'a`) or `Some(d)` which indicates that it influences `'a`
+    /// with distinct `d` (minimum number of edges that must be
+    /// traversed).
     ///
     /// Used during error reporting, extremely naive and inefficient.
-    fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, bool> {
-        let mut result_set = IndexVec::from_elem(false, &self.definitions);
+    fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, Option<usize>> {
+        let mut result_set = IndexVec::from_elem(None, &self.definitions);
         let mut changed = true;
-        result_set[r0] = true;
+        result_set[r0] = Some(0); // distance 0 from `r0`
 
         while changed {
             changed = false;
             for constraint in &self.constraints {
-                if result_set[constraint.sup] {
-                    if !result_set[constraint.sub] {
-                        result_set[constraint.sub] = true;
+                if let Some(n) = result_set[constraint.sup] {
+                    let m = n + 1;
+                    if result_set[constraint.sub]
+                        .map(|distance| m < distance)
+                        .unwrap_or(true)
+                    {
+                        result_set[constraint.sub] = Some(m);
                         changed = true;
                     }
                 }
@@ -585,17 +949,27 @@ impl fmt::Debug for Constraint {
     }
 }
 
-pub trait ClosureRegionRequirementsExt {
-    fn apply_requirements<'tcx>(
+pub trait ClosureRegionRequirementsExt<'gcx, 'tcx> {
+    fn apply_requirements(
         &self,
-        infcx: &InferCtxt<'_, '_, 'tcx>,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+        body_id: ast::NodeId,
         location: Location,
         closure_def_id: DefId,
         closure_substs: ty::ClosureSubsts<'tcx>,
     );
+
+    fn subst_closure_mapping<T>(
+        &self,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+        closure_mapping: &IndexVec<RegionVid, ty::Region<'tcx>>,
+        value: &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>;
 }
 
-impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
+impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequirements<'gcx> {
     /// 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
@@ -608,9 +982,10 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
     /// 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>(
+    fn apply_requirements(
         &self,
-        infcx: &InferCtxt<'_, '_, 'tcx>,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+        body_id: ast::NodeId,
         location: Location,
         closure_def_id: DefId,
         closure_substs: ty::ClosureSubsts<'tcx>,
@@ -632,22 +1007,71 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
         // 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);
+            &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);
+
+            match outlives_requirement.subject {
+                ClosureOutlivesSubject::Region(region) => {
+                    let region = closure_mapping[region];
+                    debug!(
+                        "apply_requirements: region={:?} \
+                         outlived_region={:?} \
+                         outlives_requirement={:?}",
+                        region,
+                        outlived_region,
+                        outlives_requirement,
+                    );
+                    infcx.sub_regions(origin, outlived_region, region);
+                }
+
+                ClosureOutlivesSubject::Ty(ty) => {
+                    let ty = self.subst_closure_mapping(infcx, closure_mapping, &ty);
+                    debug!(
+                        "apply_requirements: ty={:?} \
+                         outlived_region={:?} \
+                         outlives_requirement={:?}",
+                        ty,
+                        outlived_region,
+                        outlives_requirement,
+                    );
+                    infcx.register_region_obligation(
+                        body_id,
+                        RegionObligation {
+                            sup_type: ty,
+                            sub_region: outlived_region,
+                            cause: ObligationCause::misc(outlives_requirement.blame_span, body_id),
+                        },
+                    );
+                }
+            }
         }
     }
+
+    fn subst_closure_mapping<T>(
+        &self,
+        infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+        closure_mapping: &IndexVec<RegionVid, ty::Region<'tcx>>,
+        value: &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        infcx.tcx.fold_regions(value, &mut false, |r, _depth| {
+            if let ty::ReClosureBound(vid) = r {
+                closure_mapping[*vid]
+            } else {
+                bug!(
+                    "subst_closure_mapping: encountered non-closure bound free region {:?}",
+                    r
+                )
+            }
+        })
+    }
 }
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
index 849ccd3259a..5f23a0e5790 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
@@ -215,8 +215,11 @@ impl RegionValues {
         // FIXME. We could optimize this by improving
         // `BitMatrix::merge` so it does not always merge an entire
         // row.
-        debug!("add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
-               from_region, to_region);
+        debug!(
+            "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
+            from_region,
+            to_region
+        );
         let mut changed = false;
         for elem in self.elements.all_universal_region_indices() {
             if self.contains(from_region, elem) {
@@ -269,24 +272,70 @@ impl RegionValues {
         let mut result = String::new();
         result.push_str("{");
 
-        for (index, element) in self.elements_contained_in(r).enumerate() {
-            if index > 0 {
-                result.push_str(", ");
-            }
+        // Set to Some(l1, l2) when we have observed all the locations
+        // from l1..=l2 (inclusive) but not yet printed them. This
+        // gets extended if we then see l3 where l3 is the successor
+        // to l2.
+        let mut open_location: Option<(Location, Location)> = None;
+
+        let mut sep = "";
+        let mut push_sep = |s: &mut String| {
+            s.push_str(sep);
+            sep = ", ";
+        };
 
+        for element in self.elements_contained_in(r) {
             match element {
                 RegionElement::Location(l) => {
-                    result.push_str(&format!("{:?}", l));
+                    if let Some((location1, location2)) = open_location {
+                        if location2.block == l.block
+                            && location2.statement_index == l.statement_index - 1
+                        {
+                            open_location = Some((location1, l));
+                            continue;
+                        }
+
+                        push_sep(&mut result);
+                        Self::push_location_range(&mut result, location1, location2);
+                    }
+
+                    open_location = Some((l, l));
                 }
 
                 RegionElement::UniversalRegion(fr) => {
+                    if let Some((location1, location2)) = open_location {
+                        push_sep(&mut result);
+                        Self::push_location_range(&mut result, location1, location2);
+                        open_location = None;
+                    }
+
+                    push_sep(&mut result);
                     result.push_str(&format!("{:?}", fr));
                 }
             }
         }
 
+        if let Some((location1, location2)) = open_location {
+            push_sep(&mut result);
+            Self::push_location_range(&mut result, location1, location2);
+        }
+
         result.push_str("}");
 
         result
     }
+
+    fn push_location_range(str: &mut String, location1: Location, location2: Location) {
+        if location1 == location2 {
+            str.push_str(&format!("{:?}", location1));
+        } else {
+            assert_eq!(location1.block, location2.block);
+            str.push_str(&format!(
+                "{:?}[{}..={}]",
+                location1.block,
+                location1.statement_index,
+                location2.statement_index
+            ));
+        }
+    }
 }
diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs
index 1262c238a13..79505405692 100644
--- a/src/librustc_mir/borrow_check/nll/renumber.rs
+++ b/src/librustc_mir/borrow_check/nll/renumber.rs
@@ -8,50 +8,24 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use rustc_data_structures::indexed_vec::Idx;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable};
-use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
-use rustc::mir::RETURN_PLACE;
+use rustc::mir::{BasicBlock, Location, Mir, Statement, StatementKind};
 use rustc::mir::visit::{MutVisitor, TyContext};
 use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
 
-use super::ToRegionVid;
-use super::universal_regions::UniversalRegions;
-
 /// Replaces all free regions appearing in the MIR with fresh
 /// inference variables, returning the number of variables created.
-pub fn renumber_mir<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    universal_regions: &UniversalRegions<'tcx>,
-    mir: &mut Mir<'tcx>,
-) {
+pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut Mir<'tcx>) {
     debug!("renumber_mir()");
     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,
-        arg_count: mir.arg_count,
-    };
+    let mut visitor = NLLVisitor { infcx };
     visitor.visit_mir(mir);
 }
 
 struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
-    arg_count: usize,
 }
 
 impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
@@ -71,45 +45,13 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
                 self.infcx.next_nll_region_var(origin)
             })
     }
-
-    /// 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!("assert_free_regions_are_renumbered(value={:?})", value);
-
-        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 {
-        // The first argument is return slot, next N are arguments.
-        local.index() <= self.arg_count
-    }
 }
 
 impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
     fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
-        let is_arg = match ty_context {
-            TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local),
-            TyContext::ReturnTy(..) => true,
-            TyContext::Location(..) => false,
-        };
-        debug!(
-            "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})",
-            ty,
-            is_arg,
-            ty_context
-        );
+        debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context);
 
-        if is_arg {
-            self.assert_free_regions_are_renumbered(ty);
-        } else {
-            *ty = self.renumber_regions(ty_context, ty);
-        }
+        *ty = self.renumber_regions(ty_context, ty);
 
         debug!("visit_ty: ty={:?}", ty);
     }
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 c98a94fa8bc..e42302761bf 100644
--- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
@@ -11,11 +11,14 @@
 use rustc::mir::Mir;
 use rustc::infer::region_constraints::Constraint;
 use rustc::infer::region_constraints::RegionConstraintData;
+use rustc::infer::region_constraints::{Verify, VerifyBound};
 use rustc::ty;
-use transform::type_check::MirTypeckRegionConstraints;
-use transform::type_check::OutlivesSet;
+use syntax::codemap::Span;
 
-use super::region_infer::RegionInferenceContext;
+use super::region_infer::{TypeTest, RegionInferenceContext, RegionTest};
+use super::type_check::Locations;
+use super::type_check::MirTypeckRegionConstraints;
+use super::type_check::OutlivesSet;
 
 /// When the MIR type-checker executes, it validates all the types in
 /// the MIR, and in the process generates a set of constraints that
@@ -27,10 +30,7 @@ pub(super) fn generate<'tcx>(
     mir: &Mir<'tcx>,
     constraints: &MirTypeckRegionConstraints<'tcx>,
 ) {
-    SubtypeConstraintGenerator {
-        regioncx,
-        mir,
-    }.generate(constraints);
+    SubtypeConstraintGenerator { regioncx, mir }.generate(constraints);
 }
 
 struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
@@ -65,6 +65,8 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
                 givens,
             } = data;
 
+            let span = self.mir.source_info(locations.from_location).span;
+
             for constraint in constraints.keys() {
                 debug!("generate: constraint: {:?}", constraint);
                 let (a_vid, b_vid) = match constraint {
@@ -81,12 +83,15 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
                 // reverse direction, because `regioncx` talks about
                 // "outlives" (`>=`) whereas the region constraints
                 // talk about `<=`.
-                let span = self.mir.source_info(locations.from_location).span;
                 self.regioncx
                     .add_outlives(span, b_vid, a_vid, locations.at_location);
             }
 
-            assert!(verifys.is_empty(), "verifys not yet implemented");
+            for verify in verifys {
+                let type_test = self.verify_to_type_test(verify, span, locations);
+                self.regioncx.add_type_test(type_test);
+            }
+
             assert!(
                 givens.is_empty(),
                 "MIR type-checker does not use givens (thank goodness)"
@@ -94,6 +99,55 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
         }
     }
 
+    fn verify_to_type_test(
+        &self,
+        verify: &Verify<'tcx>,
+        span: Span,
+        locations: &Locations,
+    ) -> TypeTest<'tcx> {
+        let generic_kind = verify.kind;
+
+        let lower_bound = self.to_region_vid(verify.region);
+
+        let point = locations.at_location;
+
+        let test = self.verify_bound_to_region_test(&verify.bound);
+
+        TypeTest {
+            generic_kind,
+            lower_bound,
+            point,
+            span,
+            test,
+        }
+    }
+
+    fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest {
+        match verify_bound {
+            VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn(
+                regions.iter().map(|r| self.to_region_vid(r)).collect(),
+            ),
+
+            VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn(
+                regions.iter().map(|r| self.to_region_vid(r)).collect(),
+            ),
+
+            VerifyBound::AnyBound(bounds) => RegionTest::Any(
+                bounds
+                    .iter()
+                    .map(|b| self.verify_bound_to_region_test(b))
+                    .collect(),
+            ),
+
+            VerifyBound::AllBounds(bounds) => RegionTest::All(
+                bounds
+                    .iter()
+                    .map(|b| self.verify_bound_to_region_test(b))
+                    .collect(),
+            ),
+        }
+    }
+
     fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid {
         // Every region that we see in the constraints came from the
         // MIR or from the parameter environment. If the former, it
diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs
new file mode 100644
index 00000000000..e41bf7cda8e
--- /dev/null
+++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs
@@ -0,0 +1,220 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use dataflow::{FlowAtLocation, FlowsAtLocation};
+use dataflow::MaybeInitializedLvals;
+use dataflow::move_paths::{HasMoveData, MoveData};
+use rustc::mir::{BasicBlock, Location, Mir};
+use rustc::mir::Local;
+use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
+use rustc::util::common::ErrorReported;
+use rustc_data_structures::fx::FxHashSet;
+use syntax::codemap::DUMMY_SP;
+use util::liveness::LivenessResults;
+
+use super::TypeChecker;
+
+/// Combines liveness analysis with initialization analysis to
+/// determine which variables are live at which points, both due to
+/// ordinary uses and drops. Returns a set of (ty, location) pairs
+/// that indicate which types must be live at which point in the CFG.
+/// This vector is consumed by `constraint_generation`.
+///
+/// NB. This computation requires normalization; therefore, it must be
+/// performed before
+pub(super) fn generate<'gcx, 'tcx>(
+    cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
+    mir: &Mir<'tcx>,
+    liveness: &LivenessResults,
+    flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
+    move_data: &MoveData<'tcx>,
+) {
+    let tcx = cx.tcx();
+    let mut generator = TypeLivenessGenerator {
+        cx,
+        tcx,
+        mir,
+        liveness,
+        flow_inits,
+        move_data,
+    };
+
+    for bb in mir.basic_blocks().indices() {
+        generator.add_liveness_constraints(bb);
+    }
+}
+
+struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
+where
+    'typeck: 'gen,
+    'flow: 'gen,
+    'tcx: 'typeck + 'flow,
+    'gcx: 'tcx,
+{
+    cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
+    tcx: TyCtxt<'typeck, 'gcx, 'tcx>,
+    mir: &'gen Mir<'tcx>,
+    liveness: &'gen LivenessResults,
+    flow_inits: &'gen mut FlowAtLocation<MaybeInitializedLvals<'flow, 'gcx, 'tcx>>,
+    move_data: &'gen MoveData<'tcx>,
+}
+
+impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> {
+    /// Liveness constraints:
+    ///
+    /// > If a variable V is live at point P, then all regions R in the type of V
+    /// > must include the point P.
+    fn add_liveness_constraints(&mut self, bb: BasicBlock) {
+        debug!("add_liveness_constraints(bb={:?})", bb);
+
+        self.liveness
+            .regular
+            .simulate_block(self.mir, bb, |location, live_locals| {
+                for live_local in live_locals.iter() {
+                    let live_local_ty = self.mir.local_decls[live_local].ty;
+                    self.push_type_live_constraint(live_local_ty, location);
+                }
+            });
+
+        let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
+        self.liveness
+            .drop
+            .simulate_block(self.mir, bb, |location, live_locals| {
+                all_live_locals.push((location, live_locals.iter().collect()));
+            });
+        debug!(
+            "add_liveness_constraints: all_live_locals={:#?}",
+            all_live_locals
+        );
+
+        let terminator_index = self.mir.basic_blocks()[bb].statements.len();
+        self.flow_inits.reset_to_entry_of(bb);
+        while let Some((location, live_locals)) = all_live_locals.pop() {
+            for live_local in live_locals {
+                debug!(
+                    "add_liveness_constraints: location={:?} live_local={:?}",
+                    location,
+                    live_local
+                );
+
+                self.flow_inits.each_state_bit(|mpi_init| {
+                    debug!(
+                        "add_liveness_constraints: location={:?} initialized={:?}",
+                        location,
+                        &self.flow_inits.operator().move_data().move_paths[mpi_init]
+                    );
+                });
+
+                let mpi = self.move_data.rev_lookup.find_local(live_local);
+                if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
+                    debug!(
+                        "add_liveness_constraints: mpi={:?} has initialized child {:?}",
+                        self.move_data.move_paths[mpi],
+                        self.move_data.move_paths[initialized_child]
+                    );
+
+                    let live_local_ty = self.mir.local_decls[live_local].ty;
+                    self.add_drop_live_constraint(live_local_ty, location);
+                }
+            }
+
+            if location.statement_index == terminator_index {
+                debug!(
+                    "add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
+                    location
+                );
+                self.flow_inits.reconstruct_terminator_effect(location);
+            } else {
+                debug!(
+                    "add_liveness_constraints: reconstruct_statement_effect from {:#?}",
+                    location
+                );
+                self.flow_inits.reconstruct_statement_effect(location);
+            }
+            self.flow_inits.apply_local_effect(location);
+        }
+    }
+
+    /// Some variable with type `live_ty` is "regular live" at
+    /// `location` -- i.e., it may be used later. This means that all
+    /// regions appearing in the type `live_ty` must be live at
+    /// `location`.
+    fn push_type_live_constraint<T>(&mut self, value: T, location: Location)
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        debug!(
+            "push_type_live_constraint(live_ty={:?}, location={:?})",
+            value,
+            location
+        );
+
+        self.tcx.for_each_free_region(&value, |live_region| {
+            self.cx
+                .constraints
+                .liveness_set
+                .push((live_region, location));
+        });
+    }
+
+    /// Some variable with type `live_ty` is "drop live" at `location`
+    /// -- i.e., it may be dropped later. This means that *some* of
+    /// the regions in its type must be live at `location`. The
+    /// precise set will depend on the dropck constraints, and in
+    /// particular this takes `#[may_dangle]` into account.
+    fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) {
+        debug!(
+            "add_drop_live_constraint(dropped_ty={:?}, location={:?})",
+            dropped_ty,
+            location
+        );
+
+        let tcx = self.cx.infcx.tcx;
+        let mut types = vec![(dropped_ty, 0)];
+        let mut known = FxHashSet();
+        while let Some((ty, depth)) = types.pop() {
+            let span = DUMMY_SP; // FIXME
+            let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
+                Ok(result) => result,
+                Err(ErrorReported) => {
+                    continue;
+                }
+            };
+
+            let ty::DtorckConstraint {
+                outlives,
+                dtorck_types,
+            } = result;
+
+            // All things in the `outlives` array may be touched by
+            // the destructor and must be live at this point.
+            for outlive in outlives {
+                self.push_type_live_constraint(outlive, location);
+            }
+
+            // However, there may also be some types that
+            // `dtorck_constraint_for_ty` could not resolve (e.g.,
+            // associated types and parameters). We need to normalize
+            // associated types here and possibly recursively process.
+            for ty in dtorck_types {
+                let ty = self.cx.normalize(&ty, location);
+                match ty.sty {
+                    ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
+                        self.push_type_live_constraint(ty, location);
+                    }
+
+                    _ => if known.insert(ty) {
+                        types.push((ty, depth + 1));
+                    },
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index fae911780a1..6cdd77048c9 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -12,6 +12,9 @@
 #![allow(unreachable_code)]
 
 use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
+use dataflow::FlowAtLocation;
+use dataflow::MaybeInitializedLvals;
+use dataflow::move_paths::MoveData;
 use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
 use rustc::infer::region_constraints::RegionConstraintData;
 use rustc::traits::{self, FulfillmentContext};
@@ -26,24 +29,80 @@ use std::fmt;
 use syntax::ast;
 use syntax_pos::{Span, DUMMY_SP};
 use transform::{MirPass, MirSource};
+use util::liveness::LivenessResults;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::indexed_vec::Idx;
 
+mod liveness;
+
 /// Type checks the given `mir` in the context of the inference
 /// context `infcx`. Returns any region constraints that have yet to
-/// be proven.
+/// be proven. This result is includes liveness constraints that
+/// ensure that regions appearing in the types of all local variables
+/// are live at all points where that local variable may later be
+/// used.
 ///
 /// This phase of type-check ought to be infallible -- this is because
 /// the original, HIR-based type-check succeeded. So if any errors
 /// occur here, we will get a `bug!` reported.
-pub fn type_check<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+///
+/// # Parameters
+///
+/// - `infcx` -- inference context to use
+/// - `body_id` -- body-id of the MIR being checked
+/// - `param_env` -- parameter environment to use for trait solving
+/// - `mir` -- MIR to type-check
+/// - `implicit_region_bound` -- a region which all generic parameters are assumed
+///   to outlive; should represent the fn body
+/// - `input_tys` -- fully liberated, but **not** normalized, expected types of the arguments;
+///   the types of the input parameters found in the MIR itself will be equated with these
+/// - `output_ty` -- fully liberaetd, but **not** normalized, expected return type;
+///   the type for the RETURN_PLACE will be equated with this
+/// - `liveness` -- results of a liveness computation on the MIR; used to create liveness
+///   constraints for the regions in the types of variables
+/// - `flow_inits` -- results of a maybe-init dataflow analysis
+/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
+pub(crate) fn type_check<'gcx, 'tcx>(
+    infcx: &InferCtxt<'_, 'gcx, 'tcx>,
     body_id: ast::NodeId,
     param_env: ty::ParamEnv<'gcx>,
     mir: &Mir<'tcx>,
+    implicit_region_bound: ty::Region<'tcx>,
+    input_tys: &[Ty<'tcx>],
+    output_ty: Ty<'tcx>,
+    liveness: &LivenessResults,
+    flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
+    move_data: &MoveData<'tcx>,
 ) -> MirTypeckRegionConstraints<'tcx> {
-    let mut checker = TypeChecker::new(infcx, body_id, param_env);
+    type_check_internal(
+        infcx,
+        body_id,
+        param_env,
+        mir,
+        Some(implicit_region_bound),
+        &mut |cx| {
+            liveness::generate(cx, mir, liveness, flow_inits, move_data);
+
+            // Equate the input and output tys given by the user with
+            // the ones found in the MIR.
+            cx.equate_input_or_output(output_ty, mir.local_decls[RETURN_PLACE].ty);
+            for (&input_ty, local) in input_tys.iter().zip((1..).map(Local::new)) {
+                cx.equate_input_or_output(input_ty, mir.local_decls[local].ty);
+            }
+        },
+    )
+}
+
+fn type_check_internal<'gcx, 'tcx>(
+    infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+    body_id: ast::NodeId,
+    param_env: ty::ParamEnv<'gcx>,
+    mir: &Mir<'tcx>,
+    implicit_region_bound: Option<ty::Region<'tcx>>,
+    extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>),
+) -> MirTypeckRegionConstraints<'tcx> {
+    let mut checker = TypeChecker::new(infcx, body_id, param_env, implicit_region_bound);
     let errors_reported = {
         let mut verifier = TypeVerifier::new(&mut checker, mir);
         verifier.visit_mir(mir);
@@ -55,9 +114,12 @@ pub fn type_check<'a, 'gcx, 'tcx>(
         checker.typeck_mir(mir);
     }
 
+    extra(&mut checker);
+
     checker.constraints
 }
 
+
 fn mirbug(tcx: TyCtxt, span: Span, msg: &str) {
     // We sometimes see MIR failures (notably predicate failures) due to
     // the fact that we check rvalue sized predicates here. So use `delay_span_bug`
@@ -503,11 +565,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
 /// constraints needed for it to be valid and well-typed. Along the
 /// way, it accrues region constraints -- these can later be used by
 /// NLL region checking.
-pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
+struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
     param_env: ty::ParamEnv<'gcx>,
     last_span: Span,
     body_id: ast::NodeId,
+    implicit_region_bound: Option<ty::Region<'tcx>>,
     reported_errors: FxHashSet<(Ty<'tcx>, Span)>,
     constraints: MirTypeckRegionConstraints<'tcx>,
 }
@@ -561,12 +624,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
         body_id: ast::NodeId,
         param_env: ty::ParamEnv<'gcx>,
+        implicit_region_bound: Option<ty::Region<'tcx>>,
     ) -> Self {
         TypeChecker {
             infcx,
             last_span: DUMMY_SP,
             body_id,
             param_env,
+            implicit_region_bound,
             reported_errors: FxHashSet(),
             constraints: MirTypeckRegionConstraints::default(),
         }
@@ -591,8 +656,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             span_mirbug!(self, "", "errors selecting obligation: {:?}", e);
         }
 
-        self.infcx
-            .process_registered_region_obligations(&[], None, self.param_env, self.body_id);
+        self.infcx.process_registered_region_obligations(
+            &[],
+            self.implicit_region_bound,
+            self.param_env,
+            self.body_id,
+        );
 
         let data = self.infcx.take_and_reset_region_constraints();
         if !data.is_empty() {
@@ -625,6 +694,25 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         })
     }
 
+    fn equate_input_or_output(&mut self, unnormalized_a: Ty<'tcx>, b: Ty<'tcx>) {
+        let start_position = Location {
+            block: START_BLOCK,
+            statement_index: 0,
+        };
+        let a = self.normalize(&unnormalized_a, start_position);
+        if let Err(terr) = self.eq_types(a, b, start_position.at_self()) {
+            span_mirbug!(
+                self,
+                start_position,
+                "bad input or output {:?} normalized to {:?} should equal {:?} but got error {:?}",
+                unnormalized_a,
+                a,
+                b,
+                terr
+            );
+        }
+    }
+
     fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
         self.infcx.tcx
     }
@@ -1368,6 +1456,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) {
                     closure_region_requirements.apply_requirements(
                         self.infcx,
+                        self.body_id,
                         location,
                         *def_id,
                         *substs,
@@ -1474,7 +1563,7 @@ impl MirPass for TypeckMir {
         }
         let param_env = tcx.param_env(def_id);
         tcx.infer_ctxt().enter(|infcx| {
-            let _region_constraint_sets = type_check(&infcx, id, param_env, mir);
+            let _ = type_check_internal(&infcx, id, param_env, mir, None, &mut |_| ());
 
             // For verification purposes, we just ignore the resulting
             // region constraint sets. Not our problem. =)
diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs
index 5336bd271f5..a1e6ea135c6 100644
--- a/src/librustc_mir/borrow_check/nll/universal_regions.rs
+++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs
@@ -45,6 +45,11 @@ pub struct UniversalRegions<'tcx> {
     /// The vid assigned to `'static`
     pub fr_static: RegionVid,
 
+    /// A special region vid created to represent the current MIR fn
+    /// body.  It will outlive the entire CFG but it will not outlive
+    /// any other universal regions.
+    pub fr_fn_body: 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:
@@ -64,12 +69,16 @@ pub struct UniversalRegions<'tcx> {
     /// 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.
+    /// The return type of this function, with all regions replaced by
+    /// their universal `RegionVid` equivalents. This type is **NOT
+    /// NORMALIZED** (i.e., it contains unnormalized associated type
+    /// projections).
     pub output_ty: Ty<'tcx>,
 
     /// The fully liberated input types of this function, with all
     /// regions replaced by their universal `RegionVid` equivalents.
+    /// This type is **NOT NORMALIZED** (i.e., it contains
+    /// unnormalized associated type projections).
     pub input_tys: &'tcx [Ty<'tcx>],
 
     /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to
@@ -242,17 +251,7 @@ impl<'tcx> UniversalRegions<'tcx> {
         (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.
+    /// True if `r` is classified as an local region.
     pub fn is_local_free_region(&self, r: RegionVid) -> bool {
         self.region_classification(r) == Some(RegionClassification::Local)
     }
@@ -262,6 +261,20 @@ impl<'tcx> UniversalRegions<'tcx> {
         self.num_universals
     }
 
+    /// Given two universal regions, returns the postdominating
+    /// upper-bound (effectively the least upper bound).
+    ///
+    /// (See `TransitiveRelation::postdom_upper_bound` for details on
+    /// the postdominating upper bound in general.)
+    pub fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid {
+        assert!(self.is_universal_region(fr1));
+        assert!(self.is_universal_region(fr2));
+        *self.relations
+            .inverse_outlives
+            .postdom_upper_bound(&fr1, &fr2)
+            .unwrap_or(&self.fr_static)
+    }
+
     /// 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
@@ -305,6 +318,10 @@ impl<'tcx> UniversalRegions<'tcx> {
         relation: &TransitiveRelation<RegionVid>,
         fr0: RegionVid,
     ) -> Option<RegionVid> {
+        // This method assumes that `fr0` is one of the universally
+        // quantified region variables.
+        assert!(self.is_universal_region(fr0));
+
         let mut external_parents = vec![];
         let mut queue = vec![&fr0];
 
@@ -408,6 +425,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
         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 fr_fn_body = self.infcx.next_nll_region_var(FR).to_region_vid();
         let num_universals = self.infcx.num_region_vars();
 
         // Insert the facts we know from the predicates. Why? Why not.
@@ -419,12 +437,19 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
             self.add_implied_bounds(&indices, ty);
         }
 
-        // Finally, outlives is reflexive, and static outlives every
-        // other free region.
+        // Finally:
+        // - outlives is reflexive, so `'r: 'r` for every region `'r`
+        // - `'static: 'r` for every region `'r`
+        // - `'r: 'fn_body` for every (other) universally quantified
+        //   region `'r`, all of which are provided by our caller
         for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) {
-            debug!("build: relating free region {:?} to itself and to 'static", fr);
+            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);
+            self.relations.relate_universal_regions(fr, fr_fn_body);
         }
 
         let (output_ty, input_tys) = inputs_and_output.split_last().unwrap();
@@ -432,19 +457,26 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
         // 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);
+        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,
+            fr_fn_body,
             first_extern_index,
             first_local_index,
             num_universals,
diff --git a/src/librustc_mir/dataflow/at_location.rs b/src/librustc_mir/dataflow/at_location.rs
index a6c3398489a..7f243ad6e26 100644
--- a/src/librustc_mir/dataflow/at_location.rs
+++ b/src/librustc_mir/dataflow/at_location.rs
@@ -25,26 +25,43 @@ use std::iter;
 /// There's probably a way to auto-impl this, but I think
 /// it is cleaner to have manual visitor impls.
 pub trait FlowsAtLocation {
-    // reset the state bitvector to represent the entry to block `bb`.
+    /// Reset the state bitvector to represent the entry to block `bb`.
     fn reset_to_entry_of(&mut self, bb: BasicBlock);
 
-    // build gen + kill sets for statement at `loc`.
+    /// Build gen + kill sets for statement at `loc`.
+    ///
+    /// Note that invoking this method alone does not change the
+    /// `curr_state` -- you must invoke `apply_local_effect`
+    /// afterwards.
     fn reconstruct_statement_effect(&mut self, loc: Location);
 
-    // build gen + kill sets for terminator for `loc`.
+    /// Build gen + kill sets for terminator for `loc`.
+    ///
+    /// Note that invoking this method alone does not change the
+    /// `curr_state` -- you must invoke `apply_local_effect`
+    /// afterwards.
     fn reconstruct_terminator_effect(&mut self, loc: Location);
 
-    // apply current gen + kill sets to `flow_state`.
-    //
-    // (`bb` and `stmt_idx` parameters can be ignored if desired by
-    // client. For the terminator, the `stmt_idx` will be the number
-    // of statements in the block.)
+    /// Apply current gen + kill sets to `flow_state`.
+    ///
+    /// (`loc` parameters can be ignored if desired by
+    /// client. For the terminator, the `stmt_idx` will be the number
+    /// of statements in the block.)
     fn apply_local_effect(&mut self, loc: Location);
 }
 
 /// Represents the state of dataflow at a particular
 /// CFG location, both before and after it is
 /// executed.
+///
+/// Data flow results are typically computed only as basic block
+/// boundaries. A `FlowInProgress` allows you to reconstruct the
+/// effects at any point in the control-flow graph by starting with
+/// the state at the start of the basic block (`reset_to_entry_of`)
+/// and then replaying the effects of statements and terminators
+/// (e.g. via `reconstruct_statement_effect` and
+/// `reconstruct_terminator_effect`; don't forget to call
+/// `apply_local_effect`).
 pub struct FlowAtLocation<BD>
 where
     BD: BitDenotation,
@@ -59,6 +76,7 @@ impl<BD> FlowAtLocation<BD>
 where
     BD: BitDenotation,
 {
+    /// Iterate over each bit set in the current state.
     pub fn each_state_bit<F>(&self, f: F)
     where
         F: FnMut(BD::Idx),
@@ -67,6 +85,9 @@ where
             .each_bit(self.base_results.operator().bits_per_block(), f)
     }
 
+    /// Iterate over each `gen` bit in the current effect (invoke
+    /// `reconstruct_statement_effect` or
+    /// `reconstruct_terminator_effect` first).
     pub fn each_gen_bit<F>(&self, f: F)
     where
         F: FnMut(BD::Idx),
@@ -88,6 +109,7 @@ where
         }
     }
 
+    /// Access the underlying operator.
     pub fn operator(&self) -> &BD {
         self.base_results.operator()
     }
@@ -96,11 +118,15 @@ where
         self.curr_state.contains(x)
     }
 
+    /// Returns an iterator over the elements present in the current state.
     pub fn elems_incoming(&self) -> iter::Peekable<indexed_set::Elems<BD::Idx>> {
         let univ = self.base_results.sets().bits_per_block();
         self.curr_state.elems(univ).peekable()
     }
 
+    /// Creates a clone of the current state and applies the local
+    /// effects to the clone (leaving the state of self intact).
+    /// Invokes `f` with an iterator over the resulting state.
     pub fn with_elems_outgoing<F>(&self, f: F)
     where
         F: FnOnce(indexed_set::Elems<BD::Idx>),
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index fb9daf07c71..563405fccc9 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use borrow_check::nll::type_check;
 use build;
 use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use rustc::mir::{Mir, Promoted};
@@ -30,7 +31,6 @@ pub mod simplify_branches;
 pub mod simplify;
 pub mod erase_regions;
 pub mod no_landing_pads;
-pub mod type_check;
 pub mod rustc_peek;
 pub mod elaborate_drops;
 pub mod add_call_guards;
diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs
index 45c3fcd8a61..5163f74dd25 100644
--- a/src/librustc_mir/util/liveness.rs
+++ b/src/librustc_mir/util/liveness.rs
@@ -80,6 +80,39 @@ pub struct LivenessMode {
     pub include_drops: bool,
 }
 
+/// A combination of liveness results, used in NLL.
+pub struct LivenessResults {
+    /// Liveness results where a regular use makes a variable X live,
+    /// but not a drop.
+    pub regular: LivenessResult,
+
+    /// Liveness results where a drop makes a variable X live,
+    /// but not a regular use.
+    pub drop: LivenessResult,
+}
+
+impl LivenessResults {
+    pub fn compute<'tcx>(mir: &Mir<'tcx>) -> LivenessResults {
+        LivenessResults {
+            regular: liveness_of_locals(
+                &mir,
+                LivenessMode {
+                    include_regular_use: true,
+                    include_drops: false,
+                },
+            ),
+
+            drop: liveness_of_locals(
+                &mir,
+                LivenessMode {
+                    include_regular_use: false,
+                    include_drops: true,
+                },
+            ),
+        }
+    }
+}
+
 /// Compute which local variables are live within the given function
 /// `mir`. The liveness mode `mode` determines what sorts of uses are
 /// considered to make a variable live (e.g., do drops count?).
diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs
index ef6552c8e33..df42d5eaa0a 100644
--- a/src/librustc_typeck/variance/constraints.rs
+++ b/src/librustc_typeck/variance/constraints.rs
@@ -465,6 +465,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
             }
 
             ty::ReFree(..) |
+            ty::ReClosureBound(..) |
             ty::ReScope(..) |
             ty::ReVar(..) |
             ty::ReSkolemized(..) |
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 8d02fd26866..2bf701d1f4e 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1055,6 +1055,7 @@ impl Clean<Option<Lifetime>> for ty::RegionKind {
             ty::ReVar(..) |
             ty::ReSkolemized(..) |
             ty::ReEmpty |
+            ty::ReClosureBound(_) |
             ty::ReErased => None
         }
     }
diff --git a/src/test/compile-fail/mir_check_cast_closure.rs b/src/test/compile-fail/mir_check_cast_closure.rs
index be0d4b13741..6562efeb6d8 100644
--- a/src/test/compile-fail/mir_check_cast_closure.rs
+++ b/src/test/compile-fail/mir_check_cast_closure.rs
@@ -14,9 +14,9 @@
 
 fn bar<'a, 'b>() -> fn(&'a u32, &'b u32) -> &'a u32 {
     let g: fn(_, _) -> _ = |_x, y| y;
+    //~^ ERROR free region `'b` does not outlive free region `'a`
     g
     //~^ WARNING not reporting region error due to -Znll
-    //~| ERROR free region `'b` does not outlive free region `'a`
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/mir_check_cast_reify.rs b/src/test/compile-fail/mir_check_cast_reify.rs
index 091e0b71b2d..1736aea2d6d 100644
--- a/src/test/compile-fail/mir_check_cast_reify.rs
+++ b/src/test/compile-fail/mir_check_cast_reify.rs
@@ -45,8 +45,8 @@ fn bar<'a>(x: &'a u32) -> &'static u32 {
     // as part of checking the `ReifyFnPointer`.
     let f: fn(_) -> _ = foo;
     //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR free region `'_#1r` does not outlive free region `'static`
     f(x)
-    //~^ ERROR free region `'_#1r` does not outlive free region `'static`
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs
index 701a7c6b056..39eafa10040 100644
--- a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs
+++ b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs
@@ -17,8 +17,8 @@ fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 {
     // in `g`. These are related via the `UnsafeFnPointer` cast.
     let g: unsafe fn(_) -> _ = f;
     //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR free region `'_#1r` does not outlive free region `'static`
     unsafe { g(input) }
-    //~^ ERROR free region `'_#1r` does not outlive free region `'static`
 }
 
 fn main() {}
diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs
index f3a57c08840..8feac15d69a 100644
--- a/src/test/mir-opt/nll/named-lifetimes-basic.rs
+++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs
@@ -27,17 +27,23 @@ fn main() {
 // END RUST SOURCE
 // START rustc.use_x.nll.0.mir
 // | Free Region Mapping
-// | '_#0r    | Global   | ['_#2r, '_#1r, '_#0r, '_#3r]
-// | '_#1r    | External | ['_#1r]
-// | '_#2r    | External | ['_#2r, '_#1r]
-// | '_#3r    | Local    | ['_#3r]
+// | '_#0r    | Global   | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r]
+// | '_#1r    | External | ['_#1r, '_#4r]
+// | '_#2r    | External | ['_#2r, '_#1r, '_#4r]
+// | '_#3r    | Local    | ['_#4r, '_#3r]
+// | '_#4r    | Local    | ['_#4r]
 // |
 // | Inferred Region Values
-// | '_#0r    | {'_#0r, bb0[0], bb0[1]}
-// | '_#1r    | {'_#1r, bb0[0], bb0[1]}
-// | '_#2r    | {'_#2r, bb0[0], bb0[1]}
-// | '_#3r    | {'_#3r, bb0[0], bb0[1]}
+// | '_#0r    | {'_#0r, bb0[0..=1]}
+// | '_#1r    | {'_#1r, bb0[0..=1]}
+// | '_#2r    | {'_#2r, bb0[0..=1]}
+// | '_#3r    | {'_#3r, bb0[0..=1]}
+// | '_#4r    | {'_#4r, bb0[0..=1]}
+// | '_#5r    | {'_#1r, bb0[0..=1]}
+// | '_#6r    | {'_#2r, bb0[0..=1]}
+// | '_#7r    | {'_#1r, bb0[0..=1]}
+// | '_#8r    | {'_#3r, bb0[0..=1]}
 // |
 // ...
-// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool {
+// fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r 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 d203472f20c..f69c51c3562 100644
--- a/src/test/mir-opt/nll/reborrow-basic.rs
+++ b/src/test/mir-opt/nll/reborrow-basic.rs
@@ -28,11 +28,11 @@ 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]}
+// | '_#7r    | {bb0[6..=14]}
 // ...
-// | '_#8r    | {bb0[11], bb0[12], bb0[13], bb0[14]}
+// | '_#9r    | {bb0[11..=14]}
 // ...
-// let _2: &'_#6r mut i32;
+// let _2: &'_#7r mut i32;
 // ...
-// let _4: &'_#8r mut i32;
+// let _4: &'_#9r mut i32;
 // END rustc.main.nll.0.mir
diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs
index c04cedbc04b..e9834305550 100644
--- a/src/test/mir-opt/nll/region-liveness-basic.rs
+++ b/src/test/mir-opt/nll/region-liveness-basic.rs
@@ -31,15 +31,15 @@ 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]}
+// | '_#2r    | {bb2[0..=1], bb3[0..=1]}
+// | '_#3r    | {bb2[1], bb3[0..=1]}
 // ...
-//             let _2: &'_#2r usize;
+//             let _2: &'_#3r usize;
 // END rustc.main.nll.0.mir
 // START rustc.main.nll.0.mir
 //    bb2: {
 //            | Live variables on entry to bb2[0]: [_1, _3]
-//        _2 = &'_#1r _1[_3];
+//        _2 = &'_#2r _1[_3];
 //            | Live variables on entry to bb2[1]: [_2]
 //        switchInt(const true) -> [0u8: bb4, otherwise: bb3];
 //    }
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 e2ad49a4436..c14ce6bb581 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,7 @@ 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]}
+// | '_#6r    | {bb2[3..=5], bb3[0..=1]}
+// ...
+// let _2: Wrap<&'_#6r usize>;
 // 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 e0272a51d03..058a57fe612 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,7 @@ 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]}
+// | '_#6r    | {bb2[3..=5], bb3[0..=2], bb4[0], bb5[0..=2], bb6[0], bb7[0..=1], bb8[0]}
+// ...
+// let _2: Wrap<&'_#6r usize>;
 // 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 8aa0eb1a3a9..821cd736671 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,14 +36,14 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-// | '_#1r    | {bb2[0], bb2[1], bb3[0], bb3[1]}
+// | '_#2r    | {bb2[0..=1], bb3[0..=1]}
 // ...
-// | '_#3r    | {bb8[1], bb8[2], bb8[3], bb8[4]}
-// | '_#4r    | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]}
+// | '_#4r    | {bb8[1..=4]}
+// | '_#5r    | {bb2[1], bb3[0..=1], bb8[2..=4]}
 // ...
-// let mut _2: &'_#4r usize;
+// let mut _2: &'_#5r usize;
 // ...
-// _2 = &'_#1r _1[_3];
+// _2 = &'_#2r _1[_3];
 // ...
-// _2 = &'_#3r (*_10);
+// _2 = &'_#4r (*_10);
 // END rustc.main.nll.0.mir
diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs
index 2bc165bd3c4..a3f68ed5ebf 100644
--- a/src/test/mir-opt/nll/region-subtyping-basic.rs
+++ b/src/test/mir-opt/nll/region-subtyping-basic.rs
@@ -32,16 +32,16 @@ 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]}
+// | '_#2r    | {bb2[0..=6], bb3[0..=1]}
+// | '_#3r    | {bb2[1..=6], bb3[0..=1]}
+// | '_#4r    | {bb2[5..=6], bb3[0..=1]}
 // END rustc.main.nll.0.mir
 // START rustc.main.nll.0.mir
-// let _2: &'_#2r usize;
+// let _2: &'_#3r usize;
 // ...
-// let _6: &'_#3r usize;
+// let _6: &'_#4r usize;
 // ...
-// _2 = &'_#1r _1[_3];
+// _2 = &'_#2r _1[_3];
 // ...
 // _7 = _2;
 // ...
diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr
index ed57d89802c..3812ff40407 100644
--- a/src/test/ui/nll/capture-ref-in-struct.stderr
+++ b/src/test/ui/nll/capture-ref-in-struct.stderr
@@ -7,7 +7,7 @@ error[E0597]: `y` does not live long enough
 37 |     }
    |      - borrowed value only lives until here
    |
-   = note: borrowed value must be valid for lifetime '_#4r...
+   = note: borrowed value must be valid for lifetime '_#5r...
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr
index c842d51a2ad..2dfafd8f172 100644
--- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr
+++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr
@@ -10,7 +10,7 @@ error: free region `'_#4r` does not outlive free region `'_#3r`
 36 |         let mut closure = expect_sig(|p, y| *p = y);
    |                                             ^^^^^^
 
-note: External requirements
+note: No external requirements
   --> $DIR/escape-argument-callee.rs:36:38
    |
 36 |         let mut closure = expect_sig(|p, y| *p = y);
@@ -20,7 +20,6 @@ note: External requirements
                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
diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr
index 4e9a25e2892..94019798600 100644
--- a/src/test/ui/nll/closure-requirements/escape-argument.stderr
+++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr
@@ -1,4 +1,4 @@
-note: External requirements
+note: No external requirements
   --> $DIR/escape-argument.rs:36:38
    |
 36 |         let mut closure = expect_sig(|p, y| *p = y);
@@ -8,7 +8,6 @@ note: External requirements
                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
@@ -33,7 +32,7 @@ error[E0597]: `y` does not live long enough
 39 |     }
    |      - borrowed value only lives until here
    |
-   = note: borrowed value must be valid for lifetime '_#5r...
+   = note: borrowed value must be valid for lifetime '_#6r...
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr
index 60bd7a569a0..277e5201416 100644
--- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr
+++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr
@@ -59,7 +59,7 @@ error[E0597]: `y` does not live long enough
 36 |       }
    |        - borrowed value only lives until here
    |
-   = note: borrowed value must be valid for lifetime '_#3r...
+   = note: borrowed value must be valid for lifetime '_#4r...
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
index dc86f4cff08..709e6bc2de1 100644
--- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
+++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
@@ -36,7 +36,7 @@ error[E0597]: `y` does not live long enough
 36 |     }
    |      - borrowed value only lives until here
    |
-   = note: borrowed value must be valid for lifetime '_#3r...
+   = note: borrowed value must be valid for lifetime '_#4r...
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs
index c2f071cc029..50d7877de50 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs
@@ -54,8 +54,8 @@ fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell
             // Only works if 'x: 'y:
             let p = x.get();
             //~^ WARN not reporting region error due to -Znll
+            //~| ERROR free region `'_#5r` does not outlive free region `'_#6r`
             demand_y(x, y, p)
-            //~^ ERROR free region `'_#5r` does not outlive free region `'_#6r`
         },
     );
 }
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
index d581622c4c6..f90bc7c175a 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
@@ -5,20 +5,20 @@ warning: not reporting region error due to -Znll
    |                     ^^^^^^^
 
 error: free region `'_#5r` does not outlive free region `'_#6r`
-  --> $DIR/propagate-approximated-fail-no-postdom.rs:57:25
+  --> $DIR/propagate-approximated-fail-no-postdom.rs:55:17
    |
-57 |             demand_y(x, y, p)
-   |                         ^
+55 |             let p = x.get();
+   |                 ^
 
-note: External requirements
+note: No 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`
+57 | |             //~| ERROR free region `'_#5r` does not outlive free region `'_#6r`
+58 | |             demand_y(x, y, p)
 59 | |         },
    | |_________^
    |
@@ -26,7 +26,6 @@ note: External requirements
                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
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
index 717cf481a01..4bae29ad326 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
@@ -24,10 +24,10 @@ note: External requirements
    = note: where '_#1r: '_#2r
 
 error: free region `'_#1r` does not outlive free region `'_#2r`
-  --> $DIR/propagate-approximated-ref.rs:53:38
+  --> $DIR/propagate-approximated-ref.rs:53:29
    |
 53 |     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
-   |                                      ^^^^^^^
+   |                             ^^^^^^^
 
 note: No external requirements
   --> $DIR/propagate-approximated-ref.rs:52:1
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
index 9b23c48e249..45dc5d913ee 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
@@ -10,7 +10,7 @@ error: free region `'_#2r` does not outlive free region `'_#1r`
 33 |         cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
    |         ^^^^^^
 
-note: External requirements
+note: No external requirements
   --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15
    |
 31 |       foo(cell, |cell_a, cell_x| {
@@ -25,7 +25,6 @@ note: External requirements
                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
@@ -80,7 +79,7 @@ error[E0597]: `a` does not live long enough
 49 | }
    | - borrowed value only lives until here
    |
-   = note: borrowed value must be valid for lifetime '_#1r...
+   = note: borrowed value must be valid for lifetime '_#2r...
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr
new file mode 100644
index 00000000000..502b344c89e
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr
@@ -0,0 +1,45 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-approximated-to-empty.rs:41:9
+   |
+41 |         demand_y(x, y, x.get())
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: free region `'_#6r` does not outlive free region `'_#4r`
+  --> $DIR/propagate-approximated-to-empty.rs:41:18
+   |
+41 |         demand_y(x, y, x.get())
+   |                  ^
+
+note: No external requirements
+  --> $DIR/propagate-approximated-to-empty.rs:39:47
+   |
+39 |       establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
+   |  _______________________________________________^
+40 | |         // Only works if 'x: 'y:
+41 | |         demand_y(x, y, x.get())
+42 | |         //~^ WARN not reporting region error due to -Znll
+43 | |         //~| ERROR free region `'_#6r` does not outlive free region `'_#4r`
+44 | |     });
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:18 ~ propagate_approximated_to_empty[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: No external requirements
+  --> $DIR/propagate-approximated-to-empty.rs:38:1
+   |
+38 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+39 | |     establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
+40 | |         // Only works if 'x: 'y:
+41 | |         demand_y(x, y, x.get())
+...  |
+44 | |     });
+45 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_approximated_to_empty[317d]::supply[0]) with substs []
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
index 43464bfb2b9..43d61fdf1b5 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
@@ -24,10 +24,10 @@ note: External requirements
    = note: where '_#1r: '_#2r
 
 error: free region `'_#1r` does not outlive free region `'_#2r`
-  --> $DIR/propagate-approximated-val.rs:46:37
+  --> $DIR/propagate-approximated-val.rs:46:29
    |
 46 |     establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| {
-   |                                     ^^^^^^
+   |                             ^^^^^^
 
 note: No external requirements
   --> $DIR/propagate-approximated-val.rs:45:1
diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
index 6094f9aad81..f3c40c838fb 100644
--- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
@@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll
    |         ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: free region `'_#6r` does not outlive free region `'_#4r`
-  --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:21
+  --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:18
    |
 47 |         demand_y(x, y, x.get())
-   |                     ^
+   |                  ^
 
-note: External requirements
+note: No external requirements
   --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:45:47
    |
 45 |       establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
@@ -26,7 +26,6 @@ note: External requirements
                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
diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
index 6658ee63abd..a66c2a78970 100644
--- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
@@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll
    |         ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: free region `'_#5r` does not outlive free region `'_#7r`
-  --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:21
+  --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:18
    |
 51 |         demand_y(x, y, x.get())
-   |                     ^
+   |                  ^
 
-note: External requirements
+note: No external requirements
   --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:49:47
    |
 49 |       establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
@@ -26,7 +26,6 @@ note: External requirements
                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
diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs
new file mode 100644
index 00000000000..604c81da49d
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs
@@ -0,0 +1,60 @@
+// 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 that regions which appear only in the closure's generics (in
+// this case, `'a`) are properly mapped to the creator's generics. In
+// this case, the closure constrains its type parameter `T` to outlive
+// the same `'a` for which it implements `Trait`, which can only be the `'a`
+// from the function definition.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+trait Trait<'a> {}
+
+fn establish_relationships<T, F>(value: T, closure: F)
+where
+    F: FnOnce(T),
+{
+    closure(value)
+}
+
+fn require<'a, T>(t: T)
+where
+    T: Trait<'a> + 'a,
+{
+}
+
+#[rustc_regions]
+fn supply<'a, T>(value: T)
+where
+    T: Trait<'a>,
+{
+    establish_relationships(value, |value| {
+        //~^ ERROR `T` does not outlive
+
+        // This function call requires that
+        //
+        // (a) T: Trait<'a>
+        //
+        // and
+        //
+        // (b) T: 'a
+        //
+        // The latter does not hold.
+
+        require(value);
+        //~^ WARNING not reporting region error due to -Znll
+    });
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr
new file mode 100644
index 00000000000..efac55f2bee
--- /dev/null
+++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr
@@ -0,0 +1,60 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/propagate-from-trait-match.rs:55:9
+   |
+55 |         require(value);
+   |         ^^^^^^^
+
+note: External requirements
+  --> $DIR/propagate-from-trait-match.rs:42:36
+   |
+42 |       establish_relationships(value, |value| {
+   |  ____________________________________^
+43 | |         //~^ ERROR `T` does not outlive
+44 | |
+45 | |         // This function call requires that
+...  |
+56 | |         //~^ WARNING not reporting region error due to -Znll
+57 | |     });
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:16 ~ propagate_from_trait_match[317d]::supply[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((T,))
+           ]
+   = note: number of external vids: 2
+   = note: where T: '_#1r
+
+error: `T` does not outlive `'_#3r`
+  --> $DIR/propagate-from-trait-match.rs:42:36
+   |
+42 |       establish_relationships(value, |value| {
+   |  ____________________________________^
+43 | |         //~^ ERROR `T` does not outlive
+44 | |
+45 | |         // This function call requires that
+...  |
+56 | |         //~^ WARNING not reporting region error due to -Znll
+57 | |     });
+   | |_____^
+
+note: No external requirements
+  --> $DIR/propagate-from-trait-match.rs:38:1
+   |
+38 | / fn supply<'a, T>(value: T)
+39 | | where
+40 | |     T: Trait<'a>,
+41 | | {
+...  |
+57 | |     });
+58 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ propagate_from_trait_match[317d]::supply[0]) with substs [
+               '_#1r,
+               T
+           ]
+
+error: aborting due to previous error
+
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
index 8999f69e8de..cb2b2e2f118 100644
--- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr
+++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr
@@ -10,7 +10,7 @@ error: free region `'_#3r` does not outlive free region `'_#2r`
 21 |     expect_sig(|a, b| b); // ought to return `a`
    |                       ^
 
-note: External requirements
+note: No external requirements
   --> $DIR/return-wrong-bound-region.rs:21:16
    |
 21 |     expect_sig(|a, b| b); // ought to return `a`
@@ -20,7 +20,6 @@ note: External requirements
                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
diff --git a/src/test/ui/nll/projection-return.rs b/src/test/ui/nll/projection-return.rs
new file mode 100644
index 00000000000..31388cf50c5
--- /dev/null
+++ b/src/test/ui/nll/projection-return.rs
@@ -0,0 +1,29 @@
+// 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
+// must-compile-successfully
+
+#![feature(rustc_attrs)]
+
+trait Foo {
+    type Bar;
+}
+
+impl Foo for () {
+    type Bar = u32;
+}
+
+fn foo() -> <() as Foo>::Bar {
+    22
+}
+
+fn main() { }
+
diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs
new file mode 100644
index 00000000000..9451163ace9
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs
@@ -0,0 +1,68 @@
+// 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 -Zverbose
+
+// Tests closures that propagate an outlives relationship to their
+// creator where the subject is a projection with no regions (`<T as
+// Iterator>::Item`, to be exact).
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+#![feature(rustc_attrs)]
+
+trait Anything { }
+
+impl<T> Anything for T { }
+
+fn with_signature<'a, T, F>(x: Box<T>, op: F) -> Box<dyn Anything + 'a>
+    where F: FnOnce(Box<T>) -> Box<dyn Anything + 'a>
+{
+    op(x)
+}
+
+#[rustc_regions]
+fn no_region<'a, T>(x: Box<T>) -> Box<dyn Anything + 'a>
+where
+    T: Iterator,
+{
+    with_signature(x, |mut y| Box::new(y.next()))
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `<T as std::iter::Iterator>::Item` does not outlive
+}
+
+#[rustc_regions]
+fn correct_region<'a, T>(x: Box<T>) -> Box<dyn Anything + 'a>
+where
+    T: 'a + Iterator,
+{
+    with_signature(x, |mut y| Box::new(y.next()))
+}
+
+#[rustc_regions]
+fn wrong_region<'a, 'b, T>(x: Box<T>) -> Box<dyn Anything + 'a>
+where
+    T: 'b + Iterator,
+{
+    with_signature(x, |mut y| Box::new(y.next()))
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `<T as std::iter::Iterator>::Item` does not outlive
+}
+
+#[rustc_regions]
+fn outlives_region<'a, 'b, T>(x: Box<T>) -> Box<dyn Anything + 'a>
+where
+    T: 'b + Iterator,
+    'b: 'a,
+{
+    with_signature(x, |mut y| Box::new(y.next()))
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr
new file mode 100644
index 00000000000..9afd5d41182
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr
@@ -0,0 +1,157 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-no-regions-closure.rs:36:31
+   |
+36 |     with_signature(x, |mut y| Box::new(y.next()))
+   |                               ^^^^^^^^^^^^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-no-regions-closure.rs:54:31
+   |
+54 |     with_signature(x, |mut y| Box::new(y.next()))
+   |                               ^^^^^^^^^^^^^^^^^^
+
+note: External requirements
+  --> $DIR/projection-no-regions-closure.rs:36:23
+   |
+36 |     with_signature(x, |mut y| Box::new(y.next()))
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:15 ~ projection_no_regions_closure[317d]::no_region[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<Anything + '_#2r>
+           ]
+   = note: number of external vids: 3
+   = note: where <T as std::iter::Iterator>::Item: '_#2r
+
+note: External requirements
+  --> $DIR/projection-no-regions-closure.rs:46:23
+   |
+46 |     with_signature(x, |mut y| Box::new(y.next()))
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:18 ~ projection_no_regions_closure[317d]::correct_region[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<Anything + '_#2r>
+           ]
+   = note: number of external vids: 3
+   = note: where <T as std::iter::Iterator>::Item: '_#2r
+
+note: External requirements
+  --> $DIR/projection-no-regions-closure.rs:54:23
+   |
+54 |     with_signature(x, |mut y| Box::new(y.next()))
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:22 ~ projection_no_regions_closure[317d]::wrong_region[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<Anything + '_#3r>
+           ]
+   = note: number of external vids: 4
+   = note: where <T as std::iter::Iterator>::Item: '_#3r
+
+note: External requirements
+  --> $DIR/projection-no-regions-closure.rs:65:23
+   |
+65 |     with_signature(x, |mut y| Box::new(y.next()))
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:26 ~ projection_no_regions_closure[317d]::outlives_region[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<Anything + '_#3r>
+           ]
+   = note: number of external vids: 4
+   = note: where <T as std::iter::Iterator>::Item: '_#3r
+
+error: `<T as std::iter::Iterator>::Item` does not outlive `'_#4r`
+  --> $DIR/projection-no-regions-closure.rs:36:23
+   |
+36 |     with_signature(x, |mut y| Box::new(y.next()))
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: No external requirements
+  --> $DIR/projection-no-regions-closure.rs:32:1
+   |
+32 | / fn no_region<'a, T>(x: Box<T>) -> Box<dyn Anything + 'a>
+33 | | where
+34 | |     T: Iterator,
+35 | | {
+...  |
+38 | |     //~| ERROR `<T as std::iter::Iterator>::Item` does not outlive
+39 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ projection_no_regions_closure[317d]::no_region[0]) with substs [
+               '_#1r,
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/projection-no-regions-closure.rs:42:1
+   |
+42 | / fn correct_region<'a, T>(x: Box<T>) -> Box<dyn Anything + 'a>
+43 | | where
+44 | |     T: 'a + Iterator,
+45 | | {
+46 | |     with_signature(x, |mut y| Box::new(y.next()))
+47 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:7 ~ projection_no_regions_closure[317d]::correct_region[0]) with substs [
+               '_#1r,
+               T
+           ]
+
+error: `<T as std::iter::Iterator>::Item` does not outlive `'_#6r`
+  --> $DIR/projection-no-regions-closure.rs:54:23
+   |
+54 |     with_signature(x, |mut y| Box::new(y.next()))
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: No external requirements
+  --> $DIR/projection-no-regions-closure.rs:50:1
+   |
+50 | / fn wrong_region<'a, 'b, T>(x: Box<T>) -> Box<dyn Anything + 'a>
+51 | | where
+52 | |     T: 'b + Iterator,
+53 | | {
+...  |
+56 | |     //~| ERROR `<T as std::iter::Iterator>::Item` does not outlive
+57 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:8 ~ projection_no_regions_closure[317d]::wrong_region[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/projection-no-regions-closure.rs:60:1
+   |
+60 | / fn outlives_region<'a, 'b, T>(x: Box<T>) -> Box<dyn Anything + 'a>
+61 | | where
+62 | |     T: 'b + Iterator,
+63 | |     'b: 'a,
+64 | | {
+65 | |     with_signature(x, |mut y| Box::new(y.next()))
+66 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:9 ~ projection_no_regions_closure[317d]::outlives_region[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs
new file mode 100644
index 00000000000..c815fdc1a0c
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.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.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+
+trait Anything { }
+
+impl<T> Anything for T { }
+
+fn no_region<'a, T>(mut x: T) -> Box<dyn Anything + 'a>
+where
+    T: Iterator,
+{
+    Box::new(x.next())
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `<T as std::iter::Iterator>::Item` does not outlive
+}
+
+fn correct_region<'a, T>(mut x: T) -> Box<dyn Anything + 'a>
+where
+    T: 'a + Iterator,
+{
+    Box::new(x.next())
+}
+
+fn wrong_region<'a, 'b, T>(mut x: T) -> Box<dyn Anything + 'a>
+where
+    T: 'b + Iterator,
+{
+    Box::new(x.next())
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `<T as std::iter::Iterator>::Item` does not outlive
+}
+
+fn outlives_region<'a, 'b, T>(mut x: T) -> Box<dyn Anything + 'a>
+where
+    T: 'b + Iterator,
+    'b: 'a,
+{
+    Box::new(x.next())
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr
new file mode 100644
index 00000000000..4d13972641c
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr
@@ -0,0 +1,26 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-no-regions-fn.rs:24:5
+   |
+24 |     Box::new(x.next())
+   |     ^^^^^^^^^^^^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-no-regions-fn.rs:40:5
+   |
+40 |     Box::new(x.next())
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: `<T as std::iter::Iterator>::Item` does not outlive `'_#4r`
+  --> $DIR/projection-no-regions-fn.rs:24:5
+   |
+24 |     Box::new(x.next())
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: `<T as std::iter::Iterator>::Item` does not outlive `'_#5r`
+  --> $DIR/projection-no-regions-fn.rs:40:5
+   |
+40 |     Box::new(x.next())
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs
new file mode 100644
index 00000000000..e2a2d20d77d
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs
@@ -0,0 +1,106 @@
+// 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 cases where we constrain `<T as Anything<'b>>::AssocType` to
+// outlive `'a` and there are no bounds in the trait definition of
+// `Anything`. This means that the constraint can only be satisfied in two
+// ways:
+//
+// - by ensuring that `T: 'a` and `'b: 'a`, or
+// - by something in the where clauses.
+//
+// As of this writing, the where clause option does not work because
+// of limitations in our region inferencing system (this is true both
+// with and without NLL). See `projection_outlives`.
+//
+// Ensuring that both `T: 'a` and `'b: 'a` holds does work (`elements_outlive`).
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+trait Anything<'a> {
+    type AssocType;
+}
+
+fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F)
+where
+    F: FnOnce(Cell<&'a ()>, T),
+{
+    op(cell, t)
+}
+
+fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T)
+where
+    T: Anything<'b>,
+    T::AssocType: 'a,
+{
+}
+
+#[rustc_regions]
+fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `T` does not outlive
+    //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r`
+}
+
+#[rustc_regions]
+fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+    'a: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `T` does not outlive
+    //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+}
+
+#[rustc_regions]
+fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+    T::AssocType: 'a,
+{
+    // This error is unfortunate. This code ought to type-check: we
+    // are projecting `<T as Anything<'b>>::AssocType`, and we know
+    // that this outlives `'a` because of the where-clause. However,
+    // the way the region checker works, we don't register this
+    // outlives obligation, and hence we get an error: this is because
+    // what we see is a projection like `<T as
+    // Anything<'?0>>::AssocType`, and we don't yet know if `?0` will
+    // equal `'b` or not, so we ignore the where-clause. Obviously we
+    // can do better here with a more involved verification step.
+
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `T` does not outlive
+    //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+}
+
+#[rustc_regions]
+fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+    T: 'a,
+    'b: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr
new file mode 100644
index 00000000000..cbd80d70bf9
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr
@@ -0,0 +1,194 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-one-region-closure.rs:56:39
+   |
+56 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                                       ^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-one-region-closure.rs:68:39
+   |
+68 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                                       ^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-one-region-closure.rs:90:39
+   |
+90 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                                       ^^^^^^^
+
+note: External requirements
+  --> $DIR/projection-one-region-closure.rs:56:29
+   |
+56 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:19 ~ projection_one_region_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
+           ]
+   = note: number of external vids: 3
+   = note: where T: '_#2r
+   = note: where '_#1r: '_#2r
+
+note: External requirements
+  --> $DIR/projection-one-region-closure.rs:68:29
+   |
+68 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:23 ~ projection_one_region_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+   = note: number of external vids: 4
+   = note: where T: '_#3r
+   = note: where '_#2r: '_#3r
+
+note: External requirements
+  --> $DIR/projection-one-region-closure.rs:90:29
+   |
+90 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:27 ~ projection_one_region_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+   = note: number of external vids: 4
+   = note: where T: '_#3r
+   = note: where '_#2r: '_#3r
+
+note: External requirements
+   --> $DIR/projection-one-region-closure.rs:103:29
+    |
+103 |     with_signature(cell, t, |cell, t| require(cell, t));
+    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+    |
+    = note: defining type: DefId(0/1:31 ~ projection_one_region_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [
+                '_#1r,
+                '_#2r,
+                T,
+                i32,
+                extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+            ]
+    = note: number of external vids: 4
+    = note: where T: '_#3r
+    = note: where '_#2r: '_#3r
+
+error: `T` does not outlive `'_#5r`
+  --> $DIR/projection-one-region-closure.rs:56:29
+   |
+56 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r`
+  --> $DIR/projection-one-region-closure.rs:56:20
+   |
+56 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                    ^^^^
+
+note: No external requirements
+  --> $DIR/projection-one-region-closure.rs:52:1
+   |
+52 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+53 | | where
+54 | |     T: Anything<'b>,
+55 | | {
+...  |
+59 | |     //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r`
+60 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:8 ~ projection_one_region_closure[317d]::no_relationships_late[0]) with substs [
+               '_#1r,
+               T
+           ]
+
+error: `T` does not outlive `'_#6r`
+  --> $DIR/projection-one-region-closure.rs:68:29
+   |
+68 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+  --> $DIR/projection-one-region-closure.rs:68:20
+   |
+68 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                    ^^^^
+
+note: No external requirements
+  --> $DIR/projection-one-region-closure.rs:63:1
+   |
+63 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+64 | | where
+65 | |     T: Anything<'b>,
+66 | |     'a: 'a,
+...  |
+71 | |     //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+72 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:9 ~ projection_one_region_closure[317d]::no_relationships_early[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+error: `T` does not outlive `'_#6r`
+  --> $DIR/projection-one-region-closure.rs:90:29
+   |
+90 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+  --> $DIR/projection-one-region-closure.rs:90:20
+   |
+90 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                    ^^^^
+
+note: No external requirements
+  --> $DIR/projection-one-region-closure.rs:75:1
+   |
+75 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+76 | | where
+77 | |     T: Anything<'b>,
+78 | |     T::AssocType: 'a,
+...  |
+93 | |     //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+94 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:10 ~ projection_one_region_closure[317d]::projection_outlives[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+note: No external requirements
+   --> $DIR/projection-one-region-closure.rs:97:1
+    |
+97  | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+98  | | where
+99  | |     T: Anything<'b>,
+100 | |     T: 'a,
+...   |
+103 | |     with_signature(cell, t, |cell, t| require(cell, t));
+104 | | }
+    | |_^
+    |
+    = note: defining type: DefId(0/0:11 ~ projection_one_region_closure[317d]::elements_outlive[0]) with substs [
+                '_#1r,
+                '_#2r,
+                T
+            ]
+
+error: aborting due to 6 previous errors
+
diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs
new file mode 100644
index 00000000000..e179927dfb0
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs
@@ -0,0 +1,106 @@
+// 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 cases where we constrain `<T as Anything<'b>>::AssocType` to
+// outlive `'a` and there is a unique bound in the trait definition of
+// `Anything` -- i.e., we know that `AssocType` outlives `'b`. In this
+// case, the best way to satisfy the trait bound is to show that `'b:
+// 'a`, which can be done in various ways.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+trait Anything<'a> {
+    type AssocType: 'a;
+}
+
+fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F)
+where
+    F: FnOnce(Cell<&'a ()>, T),
+{
+    op(cell, t)
+}
+
+fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T)
+where
+    T: Anything<'b>,
+    T::AssocType: 'a,
+{
+}
+
+#[rustc_regions]
+fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r`
+}
+
+#[rustc_regions]
+fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+    'a: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+}
+
+#[rustc_regions]
+fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+    T::AssocType: 'a,
+{
+    // This error is unfortunate. This code ought to type-check: we
+    // are projecting `<T as Anything<'b>>::AssocType`, and we know
+    // that this outlives `'a` because of the where-clause. However,
+    // the way the region checker works, we don't register this
+    // outlives obligation, and hence we get an error: this is because
+    // what we see is a projection like `<T as
+    // Anything<'?0>>::AssocType`, and we don't yet know if `?0` will
+    // equal `'b` or not, so we ignore the where-clause. Obviously we
+    // can do better here with a more involved verification step.
+
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+}
+
+#[rustc_regions]
+fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+    'b: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+#[rustc_regions]
+fn one_region<'a, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'a>,
+{
+    // Note that in this case the closure still propagates an external
+    // requirement between two variables in its signature, but the
+    // creator maps both those two region variables to `'a` on its
+    // side.
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr
new file mode 100644
index 00000000000..1088ae846fe
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr
@@ -0,0 +1,204 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-one-region-trait-bound-closure.rs:48:39
+   |
+48 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                                       ^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-one-region-trait-bound-closure.rs:59:39
+   |
+59 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                                       ^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-one-region-trait-bound-closure.rs:80:39
+   |
+80 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                                       ^^^^^^^
+
+note: External requirements
+  --> $DIR/projection-one-region-trait-bound-closure.rs:48:29
+   |
+48 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:19 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
+           ]
+   = note: number of external vids: 3
+   = note: where '_#1r: '_#2r
+
+note: External requirements
+  --> $DIR/projection-one-region-trait-bound-closure.rs:59:29
+   |
+59 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:23 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+   = note: number of external vids: 4
+   = note: where '_#2r: '_#3r
+
+note: External requirements
+  --> $DIR/projection-one-region-trait-bound-closure.rs:80:29
+   |
+80 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:27 ~ projection_one_region_trait_bound_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+   = note: number of external vids: 4
+   = note: where '_#2r: '_#3r
+
+note: External requirements
+  --> $DIR/projection-one-region-trait-bound-closure.rs:91:29
+   |
+91 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:31 ~ projection_one_region_trait_bound_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+   = note: number of external vids: 4
+   = note: where '_#2r: '_#3r
+
+note: External requirements
+   --> $DIR/projection-one-region-trait-bound-closure.rs:103:29
+    |
+103 |     with_signature(cell, t, |cell, t| require(cell, t));
+    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+    |
+    = note: defining type: DefId(0/1:34 ~ projection_one_region_trait_bound_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [
+                '_#1r,
+                T,
+                i32,
+                extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
+            ]
+    = note: number of external vids: 3
+    = note: where '_#1r: '_#2r
+
+error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r`
+  --> $DIR/projection-one-region-trait-bound-closure.rs:48:20
+   |
+48 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                    ^^^^
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-closure.rs:44:1
+   |
+44 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+45 | | where
+46 | |     T: Anything<'b>,
+47 | | {
+...  |
+50 | |     //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r`
+51 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [
+               '_#1r,
+               T
+           ]
+
+error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+  --> $DIR/projection-one-region-trait-bound-closure.rs:59:20
+   |
+59 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                    ^^^^
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-closure.rs:54:1
+   |
+54 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+55 | | where
+56 | |     T: Anything<'b>,
+57 | |     'a: 'a,
+...  |
+61 | |     //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+62 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_early[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+  --> $DIR/projection-one-region-trait-bound-closure.rs:80:20
+   |
+80 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                    ^^^^
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-closure.rs:65:1
+   |
+65 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+66 | | where
+67 | |     T: Anything<'b>,
+68 | |     T::AssocType: 'a,
+...  |
+82 | |     //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)`
+83 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_closure[317d]::projection_outlives[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-closure.rs:86:1
+   |
+86 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+87 | | where
+88 | |     T: Anything<'b>,
+89 | |     'b: 'a,
+90 | | {
+91 | |     with_signature(cell, t, |cell, t| require(cell, t));
+92 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_closure[317d]::elements_outlive[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+note: No external requirements
+   --> $DIR/projection-one-region-trait-bound-closure.rs:95:1
+    |
+95  | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T)
+96  | | where
+97  | |     T: Anything<'a>,
+98  | | {
+...   |
+103 | |     with_signature(cell, t, |cell, t| require(cell, t));
+104 | | }
+    | |_^
+    |
+    = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_closure[317d]::one_region[0]) with substs [
+                '_#1r,
+                T
+            ]
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs
new file mode 100644
index 00000000000..67e28af1146
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs
@@ -0,0 +1,99 @@
+// 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 cases where we constrain `<T as Anything<'b>>::AssocType` to
+// outlive `'static`. In this case, we don't get any errors, and in fact
+// we don't even propagate constraints from the closures to the callers.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+// must-compile-successfully
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+trait Anything<'a> {
+    type AssocType: 'static;
+}
+
+fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F)
+where
+    F: FnOnce(Cell<&'a ()>, T),
+{
+    op(cell, t)
+}
+
+fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T)
+where
+    T: Anything<'b>,
+    T::AssocType: 'a,
+{
+}
+
+#[rustc_regions]
+fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+#[rustc_regions]
+fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+    'a: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+#[rustc_regions]
+fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+    T::AssocType: 'a,
+{
+    // This error is unfortunate. This code ought to type-check: we
+    // are projecting `<T as Anything<'b>>::AssocType`, and we know
+    // that this outlives `'a` because of the where-clause. However,
+    // the way the region checker works, we don't register this
+    // outlives obligation, and hence we get an error: this is because
+    // what we see is a projection like `<T as
+    // Anything<'?0>>::AssocType`, and we don't yet know if `?0` will
+    // equal `'b` or not, so we ignore the where-clause. Obviously we
+    // can do better here with a more involved verification step.
+
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+#[rustc_regions]
+fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b>,
+    'b: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+#[rustc_regions]
+fn one_region<'a, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'a>,
+{
+    // Note that in this case the closure still propagates an external
+    // requirement between two variables in its signature, but the
+    // creator maps both those two region variables to `'a` on its
+    // side.
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr
new file mode 100644
index 00000000000..986676d28d9
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr
@@ -0,0 +1,155 @@
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:47:29
+   |
+47 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:19 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:56:29
+   |
+56 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:23 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:75:29
+   |
+75 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:27 ~ projection_one_region_trait_bound_static_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:84:29
+   |
+84 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:31 ~ projection_one_region_trait_bound_static_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:96:29
+   |
+96 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:34 ~ projection_one_region_trait_bound_static_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:43:1
+   |
+43 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+44 | | where
+45 | |     T: Anything<'b>,
+46 | | {
+47 | |     with_signature(cell, t, |cell, t| require(cell, t));
+48 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_late[0]) with substs [
+               '_#1r,
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:51:1
+   |
+51 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+52 | | where
+53 | |     T: Anything<'b>,
+54 | |     'a: 'a,
+55 | | {
+56 | |     with_signature(cell, t, |cell, t| require(cell, t));
+57 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_early[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:60:1
+   |
+60 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+61 | | where
+62 | |     T: Anything<'b>,
+63 | |     T::AssocType: 'a,
+...  |
+75 | |     with_signature(cell, t, |cell, t| require(cell, t));
+76 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_static_closure[317d]::projection_outlives[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:79:1
+   |
+79 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+80 | | where
+81 | |     T: Anything<'b>,
+82 | |     'b: 'a,
+83 | | {
+84 | |     with_signature(cell, t, |cell, t| require(cell, t));
+85 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_static_closure[317d]::elements_outlive[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/projection-one-region-trait-bound-static-closure.rs:88:1
+   |
+88 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T)
+89 | | where
+90 | |     T: Anything<'a>,
+91 | | {
+...  |
+96 | |     with_signature(cell, t, |cell, t| require(cell, t));
+97 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_static_closure[317d]::one_region[0]) with substs [
+               '_#1r,
+               T
+           ]
+
diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs
new file mode 100644
index 00000000000..42bfdfcf9f9
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs
@@ -0,0 +1,135 @@
+// 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 cases where we constrain `<T as Anything<'a, 'b>>::AssocType`
+// to outlive `'a` and there are two bounds in the trait definition of
+// `Anything` -- i.e., we know that `AssocType` outlives `'a` and
+// `'b`. In this case, it's not clear what is the best way to satisfy
+// the trait bound, and hence we propagate it to the caller as a type
+// test.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+trait Anything<'a, 'b> {
+    type AssocType: 'a + 'b;
+}
+
+fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F)
+where
+    F: FnOnce(Cell<&'a ()>, T),
+{
+    op(cell, t)
+}
+
+fn require<'a, 'b, 'c, T>(_cell: Cell<&'a ()>, _t: T)
+where
+    T: Anything<'b, 'c>,
+    T::AssocType: 'a,
+{
+}
+
+#[rustc_regions]
+fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b, 'c>,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `<T as Anything<'_#5r, '_#6r>>::AssocType` does not outlive `'_#7r`
+}
+
+#[rustc_regions]
+fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b, 'c>,
+    'a: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `<T as Anything<'_#6r, '_#7r>>::AssocType` does not outlive `'_#8r`
+}
+
+#[rustc_regions]
+fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b, 'c>,
+    T::AssocType: 'a,
+{
+    // This error is unfortunate. This code ought to type-check: we
+    // are projecting `<T as Anything<'b>>::AssocType`, and we know
+    // that this outlives `'a` because of the where-clause. However,
+    // the way the region checker works, we don't register this
+    // outlives obligation, and hence we get an error: this is because
+    // what we see is a projection like `<T as
+    // Anything<'?0>>::AssocType`, and we don't yet know if `?0` will
+    // equal `'b` or not, so we ignore the where-clause. Obviously we
+    // can do better here with a more involved verification step.
+
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `<T as Anything<'_#6r, '_#7r>>::AssocType` does not outlive `'_#8r`
+}
+
+#[rustc_regions]
+fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b, 'c>,
+    'b: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+#[rustc_regions]
+fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b, 'c>,
+    'c: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+#[rustc_regions]
+fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b, 'b>,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r`
+}
+
+#[rustc_regions]
+fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'b, 'b>,
+    'b: 'a,
+{
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+#[rustc_regions]
+fn one_region<'a, T>(cell: Cell<&'a ()>, t: T)
+where
+    T: Anything<'a, 'a>,
+{
+    // Note that in this case the closure still propagates an external
+    // requirement between two variables in its signature, but the
+    // creator maps both those two region variables to `'a` on its
+    // side.
+    with_signature(cell, t, |cell, t| require(cell, t));
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr
new file mode 100644
index 00000000000..5b708a0d7e6
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr
@@ -0,0 +1,326 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-two-region-trait-bound-closure.rs:49:39
+   |
+49 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                                       ^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-two-region-trait-bound-closure.rs:60:39
+   |
+60 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                                       ^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-two-region-trait-bound-closure.rs:81:39
+   |
+81 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                                       ^^^^^^^
+
+warning: not reporting region error due to -Znll
+   --> $DIR/projection-two-region-trait-bound-closure.rs:109:39
+    |
+109 |     with_signature(cell, t, |cell, t| require(cell, t));
+    |                                       ^^^^^^^
+
+note: External requirements
+  --> $DIR/projection-two-region-trait-bound-closure.rs:49:29
+   |
+49 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:22 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+   = note: number of external vids: 4
+   = note: where <T as Anything<ReClosureBound('_#1r), ReClosureBound('_#2r)>>::AssocType: '_#3r
+
+note: External requirements
+  --> $DIR/projection-two-region-trait-bound-closure.rs:60:29
+   |
+60 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:27 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               '_#3r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T))
+           ]
+   = note: number of external vids: 5
+   = note: where <T as Anything<ReClosureBound('_#2r), ReClosureBound('_#3r)>>::AssocType: '_#4r
+
+note: External requirements
+  --> $DIR/projection-two-region-trait-bound-closure.rs:81:29
+   |
+81 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:32 ~ projection_two_region_trait_bound_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               '_#3r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T))
+           ]
+   = note: number of external vids: 5
+   = note: where <T as Anything<ReClosureBound('_#2r), ReClosureBound('_#3r)>>::AssocType: '_#4r
+
+note: External requirements
+  --> $DIR/projection-two-region-trait-bound-closure.rs:92:29
+   |
+92 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: DefId(0/1:37 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive1[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               '_#3r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T))
+           ]
+   = note: number of external vids: 5
+   = note: where <T as Anything<ReClosureBound('_#2r), ReClosureBound('_#3r)>>::AssocType: '_#4r
+
+note: External requirements
+   --> $DIR/projection-two-region-trait-bound-closure.rs:101:29
+    |
+101 |     with_signature(cell, t, |cell, t| require(cell, t));
+    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+    |
+    = note: defining type: DefId(0/1:42 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive2[0]::{{closure}}[0]) with closure substs [
+                '_#1r,
+                '_#2r,
+                '_#3r,
+                T,
+                i32,
+                extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T))
+            ]
+    = note: number of external vids: 5
+    = note: where <T as Anything<ReClosureBound('_#2r), ReClosureBound('_#3r)>>::AssocType: '_#4r
+
+note: External requirements
+   --> $DIR/projection-two-region-trait-bound-closure.rs:109:29
+    |
+109 |     with_signature(cell, t, |cell, t| require(cell, t));
+    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+    |
+    = note: defining type: DefId(0/1:46 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]::{{closure}}[0]) with closure substs [
+                '_#1r,
+                T,
+                i32,
+                extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
+            ]
+    = note: number of external vids: 3
+    = note: where <T as Anything<ReClosureBound('_#1r), ReClosureBound('_#1r)>>::AssocType: '_#2r
+
+note: External requirements
+   --> $DIR/projection-two-region-trait-bound-closure.rs:120:29
+    |
+120 |     with_signature(cell, t, |cell, t| require(cell, t));
+    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+    |
+    = note: defining type: DefId(0/1:50 ~ projection_two_region_trait_bound_closure[317d]::two_regions_outlive[0]::{{closure}}[0]) with closure substs [
+                '_#1r,
+                '_#2r,
+                T,
+                i32,
+                extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+            ]
+    = note: number of external vids: 4
+    = note: where <T as Anything<ReClosureBound('_#2r), ReClosureBound('_#2r)>>::AssocType: '_#3r
+
+note: External requirements
+   --> $DIR/projection-two-region-trait-bound-closure.rs:132:29
+    |
+132 |     with_signature(cell, t, |cell, t| require(cell, t));
+    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+    |
+    = note: defining type: DefId(0/1:53 ~ projection_two_region_trait_bound_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [
+                '_#1r,
+                T,
+                i32,
+                extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
+            ]
+    = note: number of external vids: 3
+    = note: where <T as Anything<ReClosureBound('_#1r), ReClosureBound('_#1r)>>::AssocType: '_#2r
+
+error: `<T as Anything<'_#5r, '_#6r>>::AssocType` does not outlive `'_#7r`
+  --> $DIR/projection-two-region-trait-bound-closure.rs:49:29
+   |
+49 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: No external requirements
+  --> $DIR/projection-two-region-trait-bound-closure.rs:45:1
+   |
+45 | / fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+46 | | where
+47 | |     T: Anything<'b, 'c>,
+48 | | {
+...  |
+51 | |     //~| ERROR `<T as Anything<'_#5r, '_#6r>>::AssocType` does not outlive `'_#7r`
+52 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:8 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+error: `<T as Anything<'_#6r, '_#7r>>::AssocType` does not outlive `'_#8r`
+  --> $DIR/projection-two-region-trait-bound-closure.rs:60:29
+   |
+60 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: No external requirements
+  --> $DIR/projection-two-region-trait-bound-closure.rs:55:1
+   |
+55 | / fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+56 | | where
+57 | |     T: Anything<'b, 'c>,
+58 | |     'a: 'a,
+...  |
+62 | |     //~| ERROR `<T as Anything<'_#6r, '_#7r>>::AssocType` does not outlive `'_#8r`
+63 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:9 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_early[0]) with substs [
+               '_#1r,
+               '_#2r,
+               '_#3r,
+               T
+           ]
+
+error: `<T as Anything<'_#6r, '_#7r>>::AssocType` does not outlive `'_#8r`
+  --> $DIR/projection-two-region-trait-bound-closure.rs:81:29
+   |
+81 |     with_signature(cell, t, |cell, t| require(cell, t));
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: No external requirements
+  --> $DIR/projection-two-region-trait-bound-closure.rs:66:1
+   |
+66 | / fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+67 | | where
+68 | |     T: Anything<'b, 'c>,
+69 | |     T::AssocType: 'a,
+...  |
+83 | |     //~| ERROR `<T as Anything<'_#6r, '_#7r>>::AssocType` does not outlive `'_#8r`
+84 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:10 ~ projection_two_region_trait_bound_closure[317d]::projection_outlives[0]) with substs [
+               '_#1r,
+               '_#2r,
+               '_#3r,
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/projection-two-region-trait-bound-closure.rs:87:1
+   |
+87 | / fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+88 | | where
+89 | |     T: Anything<'b, 'c>,
+90 | |     'b: 'a,
+91 | | {
+92 | |     with_signature(cell, t, |cell, t| require(cell, t));
+93 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:11 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive1[0]) with substs [
+               '_#1r,
+               '_#2r,
+               '_#3r,
+               T
+           ]
+
+note: No external requirements
+   --> $DIR/projection-two-region-trait-bound-closure.rs:96:1
+    |
+96  | / fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
+97  | | where
+98  | |     T: Anything<'b, 'c>,
+99  | |     'c: 'a,
+100 | | {
+101 | |     with_signature(cell, t, |cell, t| require(cell, t));
+102 | | }
+    | |_^
+    |
+    = note: defining type: DefId(0/0:12 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive2[0]) with substs [
+                '_#1r,
+                '_#2r,
+                '_#3r,
+                T
+            ]
+
+error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r`
+   --> $DIR/projection-two-region-trait-bound-closure.rs:109:20
+    |
+109 |     with_signature(cell, t, |cell, t| require(cell, t));
+    |                    ^^^^
+
+note: No external requirements
+   --> $DIR/projection-two-region-trait-bound-closure.rs:105:1
+    |
+105 | / fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+106 | | where
+107 | |     T: Anything<'b, 'b>,
+108 | | {
+...   |
+111 | |     //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r`
+112 | | }
+    | |_^
+    |
+    = note: defining type: DefId(0/0:13 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]) with substs [
+                '_#1r,
+                T
+            ]
+
+note: No external requirements
+   --> $DIR/projection-two-region-trait-bound-closure.rs:115:1
+    |
+115 | / fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
+116 | | where
+117 | |     T: Anything<'b, 'b>,
+118 | |     'b: 'a,
+119 | | {
+120 | |     with_signature(cell, t, |cell, t| require(cell, t));
+121 | | }
+    | |_^
+    |
+    = note: defining type: DefId(0/0:14 ~ projection_two_region_trait_bound_closure[317d]::two_regions_outlive[0]) with substs [
+                '_#1r,
+                '_#2r,
+                T
+            ]
+
+note: No external requirements
+   --> $DIR/projection-two-region-trait-bound-closure.rs:124:1
+    |
+124 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T)
+125 | | where
+126 | |     T: Anything<'a, 'a>,
+127 | | {
+...   |
+132 | |     with_signature(cell, t, |cell, t| require(cell, t));
+133 | | }
+    | |_^
+    |
+    = note: defining type: DefId(0/0:15 ~ projection_two_region_trait_bound_closure[317d]::one_region[0]) with substs [
+                '_#1r,
+                T
+            ]
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs
new file mode 100644
index 00000000000..14e2eb26976
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs
@@ -0,0 +1,66 @@
+// 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 -Zverbose
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+#![feature(rustc_attrs)]
+
+use std::fmt::Debug;
+
+fn with_signature<'a, T, F>(x: Box<T>, op: F) -> Box<dyn Debug + 'a>
+    where F: FnOnce(Box<T>) -> Box<dyn Debug + 'a>
+{
+    op(x)
+}
+
+#[rustc_regions]
+fn no_region<'a, T>(x: Box<T>) -> Box<dyn Debug + 'a>
+where
+    T: Debug,
+{
+    // Here, the closure winds up being required to prove that `T:
+    // 'a`.  In principle, it could know that, except that it is
+    // type-checked in a fully generic way, and hence it winds up with
+    // a propagated requirement that `T: '_#2`, where `'_#2` appears
+    // in the return type. The caller makes the mapping from `'_#2` to
+    // `'a` (and subsequently reports an error).
+
+    with_signature(x, |y| y)
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `T` does not outlive
+}
+
+fn correct_region<'a, T>(x: Box<T>) -> Box<Debug + 'a>
+where
+    T: 'a + Debug,
+{
+    x
+}
+
+fn wrong_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a>
+where
+    T: 'b + Debug,
+{
+    x
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `T` does not outlive
+}
+
+fn outlives_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a>
+where
+    T: 'b + Debug,
+    'b: 'a,
+{
+    x
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr
new file mode 100644
index 00000000000..37ebc38da4d
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr
@@ -0,0 +1,58 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:27
+   |
+37 |     with_signature(x, |y| y)
+   |                           ^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5
+   |
+53 |     x
+   |     ^
+
+note: External requirements
+  --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23
+   |
+37 |     with_signature(x, |y| y)
+   |                       ^^^^^
+   |
+   = note: defining type: DefId(0/1:14 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<std::fmt::Debug + '_#2r>
+           ]
+   = note: number of external vids: 3
+   = note: where T: '_#2r
+
+error: `T` does not outlive `'_#4r`
+  --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23
+   |
+37 |     with_signature(x, |y| y)
+   |                       ^^^^^
+
+note: No external requirements
+  --> $DIR/ty-param-closure-outlives-from-return-type.rs:26:1
+   |
+26 | / fn no_region<'a, T>(x: Box<T>) -> Box<dyn Debug + 'a>
+27 | | where
+28 | |     T: Debug,
+29 | | {
+...  |
+39 | |     //~| ERROR `T` does not outlive
+40 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:5 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]) with substs [
+               '_#1r,
+               T
+           ]
+
+error: `T` does not outlive `'_#4r`
+  --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5
+   |
+53 |     x
+   |     ^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs
new file mode 100644
index 00000000000..beed1a740ea
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs
@@ -0,0 +1,96 @@
+// 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 that we can propagate `T: 'a` obligations to our caller.  See
+// `correct_region` for an explanation of how this test is setup; it's
+// somewhat intricate.
+
+// compile-flags:-Znll -Zborrowck=mir -Zverbose
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+#![feature(rustc_attrs)]
+
+use std::cell::Cell;
+
+fn with_signature<'a, T, F>(a: Cell<&'a ()>, b: T, op: F)
+where
+    F: FnOnce(Cell<&'a ()>, T),
+{
+    op(a, b)
+}
+
+fn require<'a, T>(_a: &Cell<&'a ()>, _b: &T)
+where
+    T: 'a,
+{
+}
+
+#[rustc_regions]
+fn no_region<'a, T>(a: Cell<&'a ()>, b: T) {
+    with_signature(a, b, |x, y| {
+        //~^ ERROR `T` does not outlive
+        //
+        // See `correct_region`, which explains the point of this
+        // test.  The only difference is that, in the case of this
+        // function, there is no where clause *anywhere*, and hence we
+        // get an error (but reported by the closure creator).
+        require(&x, &y)
+        //~^ WARNING not reporting region error due to -Znll
+    })
+}
+
+#[rustc_regions]
+fn correct_region<'a, T>(a: Cell<&'a ()>, b: T)
+where
+    T: 'a,
+{
+    with_signature(a, b, |x, y| {
+        // Key point of this test:
+        //
+        // The *closure* is being type-checked with all of its free
+        // regions "universalized". In particular, it does not know
+        // that `x` has the type `Cell<&'a ()>`, but rather treats it
+        // as if the type of `x` is `Cell<&'A ()>`, where `'A` is some
+        // fresh, independent region distinct from the `'a` which
+        // appears in the environment. The call to `require` here
+        // forces us then to prove that `T: 'A`, but the closure
+        // cannot do it on its own. It has to surface this requirement
+        // to its creator (which knows that `'a == 'A`).
+        require(&x, &y)
+    })
+}
+
+#[rustc_regions]
+fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T)
+where
+    T: 'b,
+{
+    with_signature(a, b, |x, y| {
+        //~^ ERROR `T` does not outlive
+        // See `correct_region`
+        require(&x, &y)
+        //~^ WARNING not reporting region error due to -Znll
+    })
+}
+
+#[rustc_regions]
+fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T)
+where
+    T: 'b,
+    'b: 'a,
+{
+    with_signature(a, b, |x, y| {
+        // See `correct_region`
+        require(&x, &y)
+    })
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr
new file mode 100644
index 00000000000..78445eb47c3
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr
@@ -0,0 +1,191 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:45:9
+   |
+45 |         require(&x, &y)
+   |         ^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:79:9
+   |
+79 |         require(&x, &y)
+   |         ^^^^^^^
+
+note: External requirements
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26
+   |
+38 |       with_signature(a, b, |x, y| {
+   |  __________________________^
+39 | |         //~^ ERROR `T` does not outlive
+40 | |         //
+41 | |         // See `correct_region`, which explains the point of this
+...  |
+46 | |         //~^ WARNING not reporting region error due to -Znll
+47 | |     })
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:16 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]::{{closure}}[0]) with closure substs [
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#1r ()>, T))
+           ]
+   = note: number of external vids: 2
+   = note: where T: '_#1r
+
+note: External requirements
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:55:26
+   |
+55 |       with_signature(a, b, |x, y| {
+   |  __________________________^
+56 | |         // Key point of this test:
+57 | |         //
+58 | |         // The *closure* is being type-checked with all of its free
+...  |
+67 | |         require(&x, &y)
+68 | |     })
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:19 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
+           ]
+   = note: number of external vids: 3
+   = note: where T: '_#2r
+
+note: External requirements
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26
+   |
+76 |       with_signature(a, b, |x, y| {
+   |  __________________________^
+77 | |         //~^ ERROR `T` does not outlive
+78 | |         // See `correct_region`
+79 | |         require(&x, &y)
+80 | |         //~^ WARNING not reporting region error due to -Znll
+81 | |     })
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:23 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T))
+           ]
+   = note: number of external vids: 3
+   = note: where T: '_#2r
+
+note: External requirements
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:90:26
+   |
+90 |       with_signature(a, b, |x, y| {
+   |  __________________________^
+91 | |         // See `correct_region`
+92 | |         require(&x, &y)
+93 | |     })
+   | |_____^
+   |
+   = note: defining type: DefId(0/1:27 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]::{{closure}}[0]) with closure substs [
+               '_#1r,
+               '_#2r,
+               T,
+               i32,
+               extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
+           ]
+   = note: number of external vids: 4
+   = note: where T: '_#3r
+
+error: `T` does not outlive `'_#3r`
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26
+   |
+38 |       with_signature(a, b, |x, y| {
+   |  __________________________^
+39 | |         //~^ ERROR `T` does not outlive
+40 | |         //
+41 | |         // See `correct_region`, which explains the point of this
+...  |
+46 | |         //~^ WARNING not reporting region error due to -Znll
+47 | |     })
+   | |_____^
+
+note: No external requirements
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:37:1
+   |
+37 | / fn no_region<'a, T>(a: Cell<&'a ()>, b: T) {
+38 | |     with_signature(a, b, |x, y| {
+39 | |         //~^ ERROR `T` does not outlive
+40 | |         //
+...  |
+47 | |     })
+48 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:6 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]) with substs [
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:51:1
+   |
+51 | / fn correct_region<'a, T>(a: Cell<&'a ()>, b: T)
+52 | | where
+53 | |     T: 'a,
+54 | | {
+...  |
+68 | |     })
+69 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:7 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]) with substs [
+               '_#1r,
+               T
+           ]
+
+error: `T` does not outlive `'_#5r`
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26
+   |
+76 |       with_signature(a, b, |x, y| {
+   |  __________________________^
+77 | |         //~^ ERROR `T` does not outlive
+78 | |         // See `correct_region`
+79 | |         require(&x, &y)
+80 | |         //~^ WARNING not reporting region error due to -Znll
+81 | |     })
+   | |_____^
+
+note: No external requirements
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:72:1
+   |
+72 | / fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T)
+73 | | where
+74 | |     T: 'b,
+75 | | {
+...  |
+81 | |     })
+82 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:8 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]) with substs [
+               '_#1r,
+               T
+           ]
+
+note: No external requirements
+  --> $DIR/ty-param-closure-outlives-from-where-clause.rs:85:1
+   |
+85 | / fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T)
+86 | | where
+87 | |     T: 'b,
+88 | |     'b: 'a,
+...  |
+93 | |     })
+94 | | }
+   | |_^
+   |
+   = note: defining type: DefId(0/0:9 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]) with substs [
+               '_#1r,
+               '_#2r,
+               T
+           ]
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs
new file mode 100644
index 00000000000..a1e636cbc44
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs
@@ -0,0 +1,41 @@
+// 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 we assume that universal types like `T` outlive the
+// function body.
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+
+use std::cell::Cell;
+
+// No errors here, because `'a` is local to the body.
+fn region_within_body<T>(t: T) {
+    let some_int = 22;
+    let cell = Cell::new(&some_int);
+    outlives(cell, t)
+}
+
+// Error here, because T: 'a is not satisfied.
+fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) {
+    outlives(cell, t)
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `T` does not outlive
+}
+
+fn outlives<'a, T>(x: Cell<&'a usize>, y: T)
+where
+    T: 'a,
+{
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr
new file mode 100644
index 00000000000..bbe55c52b6e
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr
@@ -0,0 +1,14 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/ty-param-fn-body.rs:30:5
+   |
+30 |     outlives(cell, t)
+   |     ^^^^^^^^
+
+error: `T` does not outlive `'_#4r`
+  --> $DIR/ty-param-fn-body.rs:30:5
+   |
+30 |     outlives(cell, t)
+   |     ^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.rs b/src/test/ui/nll/ty-outlives/ty-param-fn.rs
new file mode 100644
index 00000000000..76783af4ceb
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/ty-param-fn.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.
+
+// compile-flags:-Znll -Zborrowck=mir
+
+#![allow(warnings)]
+#![feature(dyn_trait)]
+
+use std::fmt::Debug;
+
+fn no_region<'a, T>(x: Box<T>) -> Box<Debug + 'a>
+where
+    T: Debug,
+{
+    x
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `T` does not outlive
+}
+
+fn correct_region<'a, T>(x: Box<T>) -> Box<Debug + 'a>
+where
+    T: 'a + Debug,
+{
+    x
+}
+
+fn wrong_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a>
+where
+    T: 'b + Debug,
+{
+    x
+    //~^ WARNING not reporting region error due to -Znll
+    //~| ERROR `T` does not outlive
+}
+
+fn outlives_region<'a, 'b, T>(x: Box<T>) -> Box<Debug + 'a>
+where
+    T: 'b + Debug,
+    'b: 'a,
+{
+    x
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr
new file mode 100644
index 00000000000..02c4ebbd5ac
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr
@@ -0,0 +1,26 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/ty-param-fn.rs:22:5
+   |
+22 |     x
+   |     ^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/ty-param-fn.rs:38:5
+   |
+38 |     x
+   |     ^
+
+error: `T` does not outlive `'_#3r`
+  --> $DIR/ty-param-fn.rs:22:5
+   |
+22 |     x
+   |     ^
+
+error: `T` does not outlive `'_#4r`
+  --> $DIR/ty-param-fn.rs:38:5
+   |
+38 |     x
+   |     ^
+
+error: aborting due to 2 previous errors
+