diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
index 5564a0d98af..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 dataflow::{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 c616f027331..3329448e2b7 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -77,7 +77,16 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
 ) {
     // 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 constraint_sets = &type_check::type_check(
+        infcx,
+        mir_node_id,
+        param_env,
+        mir,
+        &liveness,
+        flow_inits,
+        move_data,
+    );
 
     // Create the region inference context, taking ownership of the region inference
     // data that was contained in `infcx`.
@@ -85,19 +94,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::compute(mir);
 
     // 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);
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 86ba7524bf0..f36392c9fcf 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -425,11 +425,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// 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.
-    fn check_type_tests(
-        &self,
-        infcx: &InferCtxt<'_, '_, 'tcx>,
-        mir: &Mir<'tcx>,
-    ) {
+    fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
         for type_test in &self.type_tests {
             debug!("check_type_test: {:?}", type_test);
 
@@ -473,13 +469,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 .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::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)),
+            RegionTest::All(tests) => tests.iter().all(|test| {
+                self.eval_region_test(mir, point, lower_bound, test)
+            }),
         }
     }
 
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/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index fae911780a1..8f18105600a 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.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,22 +29,43 @@ 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>,
+pub(crate) fn type_check<'gcx, 'tcx>(
+    infcx: &InferCtxt<'_, 'gcx, 'tcx>,
     body_id: ast::NodeId,
     param_env: ty::ParamEnv<'gcx>,
     mir: &Mir<'tcx>,
+    liveness: &LivenessResults,
+    flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
+    move_data: &MoveData<'tcx>,
+) -> MirTypeckRegionConstraints<'tcx> {
+    type_check_internal(infcx, body_id, param_env, mir, &mut |cx| {
+        liveness::generate(cx, mir, liveness, flow_inits, move_data)
+    })
+}
+
+fn type_check_internal<'gcx, 'tcx>(
+    infcx: &InferCtxt<'_, 'gcx, 'tcx>,
+    body_id: ast::NodeId,
+    param_env: ty::ParamEnv<'gcx>,
+    mir: &Mir<'tcx>,
+    extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>),
 ) -> MirTypeckRegionConstraints<'tcx> {
     let mut checker = TypeChecker::new(infcx, body_id, param_env);
     let errors_reported = {
@@ -55,9 +79,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,7 +530,7 @@ 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,
@@ -1474,7 +1501,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, &mut |_| ());
 
             // For verification purposes, we just ignore the resulting
             // region constraint sets. Not our problem. =)
diff --git a/src/test/ui/nll/ty-outlives/projection-fn.rs b/src/test/ui/nll/ty-outlives/projection-fn.rs
new file mode 100644
index 00000000000..677d1352788
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-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
+
+#![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 failed type test
+}
+
+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 failed type test
+}
+
+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-fn.stderr b/src/test/ui/nll/ty-outlives/projection-fn.stderr
new file mode 100644
index 00000000000..a478348edc1
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/projection-fn.stderr
@@ -0,0 +1,26 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-fn.rs:24:5
+   |
+24 |     Box::new(x.next())
+   |     ^^^^^^^^^^^^^^^^^^
+
+warning: not reporting region error due to -Znll
+  --> $DIR/projection-fn.rs:40:5
+   |
+40 |     Box::new(x.next())
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#3r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn([]) }
+  --> $DIR/projection-fn.rs:24:5
+   |
+24 |     Box::new(x.next())
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r]) }
+  --> $DIR/projection-fn.rs:40:5
+   |
+40 |     Box::new(x.next())
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+