about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/middle/region.rs2
-rw-r--r--src/librustc/mir/mod.rs18
-rw-r--r--src/librustc/mir/transform.rs6
-rw-r--r--src/librustc/ty/fold.rs37
-rw-r--r--src/librustc_borrowck/borrowck/check_loans.rs3
-rw-r--r--src/librustc_data_structures/indexed_set.rs12
-rw-r--r--src/librustc_data_structures/indexed_vec.rs35
-rw-r--r--src/librustc_driver/driver.rs1
-rw-r--r--src/librustc_mir/borrow_check.rs250
-rw-r--r--src/librustc_mir/build/mod.rs8
-rw-r--r--src/librustc_mir/dataflow/drop_flag_effects.rs48
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs59
-rw-r--r--src/librustc_mir/dataflow/impls/mod.rs66
-rw-r--r--src/librustc_mir/dataflow/mod.rs24
-rw-r--r--src/librustc_mir/dataflow/move_paths/builder.rs38
-rw-r--r--src/librustc_mir/dataflow/move_paths/mod.rs6
-rw-r--r--src/librustc_mir/lib.rs3
-rw-r--r--src/librustc_mir/transform/elaborate_drops.rs10
-rw-r--r--src/librustc_mir/transform/generator.rs7
-rw-r--r--src/librustc_mir/transform/nll/constraint_generation.rs269
-rw-r--r--src/librustc_mir/transform/nll/mod.rs319
-rw-r--r--src/librustc_mir/transform/nll/region_infer.rs (renamed from src/librustc_mir/transform/nll/infer.rs)112
-rw-r--r--src/librustc_mir/transform/nll/renumber.rs132
-rw-r--r--src/librustc_mir/transform/nll/subtype.rs99
-rw-r--r--src/librustc_mir/util/borrowck_errors.rs40
-rw-r--r--src/librustc_mir/util/graphviz.rs26
-rw-r--r--src/librustc_mir/util/liveness.rs386
-rw-r--r--src/librustc_mir/util/pretty.rs68
-rw-r--r--src/test/compile-fail/nll/loan_ends_mid_block_pair.rs50
-rw-r--r--src/test/compile-fail/nll/loan_ends_mid_block_vec.rs49
-rw-r--r--src/test/compile-fail/nll/region-ends-after-if-condition.rs46
-rw-r--r--src/test/compile-fail/nll/return_from_loop.rs49
-rw-r--r--src/test/mir-opt/end_region_destruction_extents_1.rs4
-rw-r--r--src/test/mir-opt/nll/liveness-call-subtlety.rs51
-rw-r--r--src/test/mir-opt/nll/liveness-drop-intra-block.rs41
-rw-r--r--src/test/mir-opt/nll/liveness-interblock.rs50
-rw-r--r--src/test/mir-opt/nll/reborrow-basic.rs39
-rw-r--r--src/test/mir-opt/nll/region-liveness-basic.rs56
-rw-r--r--src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs48
-rw-r--r--src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs50
-rw-r--r--src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs49
-rw-r--r--src/test/mir-opt/nll/region-subtyping-basic.rs49
-rw-r--r--src/tools/compiletest/src/runtest.rs9
43 files changed, 2102 insertions, 622 deletions
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index fa4ee7c0092..e725592ff99 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -158,7 +158,7 @@ pub struct BlockRemainder {
 
 newtype_index!(FirstStatementIndex
     {
-        DEBUG_NAME = "",
+        DEBUG_FORMAT = "{}",
         MAX = SCOPE_DATA_REMAINDER_MAX,
     });
 
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index f5a3c1989cf..c4a33bb07cd 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -417,7 +417,7 @@ pub enum BorrowKind {
 
 newtype_index!(Local
     {
-        DEBUG_NAME = "_",
+        DEBUG_FORMAT = "_{}",
         const RETURN_POINTER = 0,
     });
 
@@ -553,7 +553,7 @@ pub struct UpvarDecl {
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlock
 
-newtype_index!(BasicBlock { DEBUG_NAME = "bb" });
+newtype_index!(BasicBlock { DEBUG_FORMAT = "bb{}" });
 
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlockData and Terminator
@@ -1135,7 +1135,7 @@ pub type LvalueProjection<'tcx> = Projection<'tcx, Lvalue<'tcx>, Local, Ty<'tcx>
 /// and the index is a local.
 pub type LvalueElem<'tcx> = ProjectionElem<'tcx, Local, Ty<'tcx>>;
 
-newtype_index!(Field { DEBUG_NAME = "field" });
+newtype_index!(Field { DEBUG_FORMAT = "field[{}]" });
 
 impl<'tcx> Lvalue<'tcx> {
     pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> {
@@ -1202,7 +1202,7 @@ impl<'tcx> Debug for Lvalue<'tcx> {
 
 newtype_index!(VisibilityScope
     {
-        DEBUG_NAME = "scope",
+        DEBUG_FORMAT = "scope[{}]",
         const ARGUMENT_VISIBILITY_SCOPE = 0,
     });
 
@@ -1529,7 +1529,7 @@ pub struct Constant<'tcx> {
     pub literal: Literal<'tcx>,
 }
 
-newtype_index!(Promoted { DEBUG_NAME = "promoted" });
+newtype_index!(Promoted { DEBUG_FORMAT = "promoted[{}]" });
 
 #[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub enum Literal<'tcx> {
@@ -1637,6 +1637,14 @@ impl fmt::Debug for Location {
 }
 
 impl Location {
+    /// Returns the location immediately after this one within the enclosing block.
+    ///
+    /// Note that if this location represents a terminator, then the
+    /// resulting location would be out of bounds and invalid.
+    pub fn successor_within_block(&self) -> Location {
+        Location { block: self.block, statement_index: self.statement_index + 1 }
+    }
+
     pub fn dominates(&self, other: &Location, dominators: &Dominators<BasicBlock>) -> bool {
         if self.block == other.block {
             self.statement_index <= other.statement_index
diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs
index f29405e6650..6c90a5f38d0 100644
--- a/src/librustc/mir/transform.rs
+++ b/src/librustc/mir/transform.rs
@@ -39,13 +39,13 @@ pub enum MirSource {
     GeneratorDrop(NodeId),
 }
 
-impl<'a, 'tcx> MirSource {
-    pub fn from_local_def_id(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> MirSource {
+impl<'a, 'gcx, 'tcx> MirSource {
+    pub fn from_local_def_id(tcx: TyCtxt<'a, 'gcx, 'tcx>, def_id: DefId) -> MirSource {
         let id = tcx.hir.as_local_node_id(def_id).expect("mir source requires local def-id");
         Self::from_node(tcx, id)
     }
 
-    pub fn from_node(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: NodeId) -> MirSource {
+    pub fn from_node(tcx: TyCtxt<'a, 'gcx, 'tcx>, id: NodeId) -> MirSource {
         use hir::*;
 
         // Handle constants in enum discriminants, types, and repeat expressions.
diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs
index edd4329fa41..149999e0eee 100644
--- a/src/librustc/ty/fold.rs
+++ b/src/librustc/ty/fold.rs
@@ -218,6 +218,43 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     {
         value.fold_with(&mut RegionFolder::new(self, skipped_regions, &mut f))
     }
+
+    pub fn for_each_free_region<T,F>(self,
+                                     value: &T,
+                                     callback: F)
+        where F: FnMut(ty::Region<'tcx>),
+              T: TypeFoldable<'tcx>,
+    {
+        value.visit_with(&mut RegionVisitor { current_depth: 0, callback });
+
+        struct RegionVisitor<F> {
+            current_depth: u32,
+            callback: F,
+        }
+
+        impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor<F>
+            where F : FnMut(ty::Region<'tcx>)
+        {
+            fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<T>) -> bool {
+                self.current_depth += 1;
+                t.skip_binder().visit_with(self);
+                self.current_depth -= 1;
+
+                false // keep visiting
+            }
+
+            fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
+                match *r {
+                    ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => {
+                        /* ignore bound regions */
+                    }
+                    _ => (self.callback)(r),
+                }
+
+                false // keep visiting
+            }
+        }
+    }
 }
 
 /// Folds over the substructure of a type, visiting its component
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index c1d0d849dfb..908737669c5 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -484,7 +484,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
             // 3. Where does old loan expire.
 
             let previous_end_span =
-                old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree).end_point();
+                Some(old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree)
+                     .end_point());
 
             let mut err = match (new_loan.kind, old_loan.kind) {
                 (ty::MutBorrow, ty::MutBorrow) =>
diff --git a/src/librustc_data_structures/indexed_set.rs b/src/librustc_data_structures/indexed_set.rs
index c790463e47a..c5ffb003399 100644
--- a/src/librustc_data_structures/indexed_set.rs
+++ b/src/librustc_data_structures/indexed_set.rs
@@ -53,11 +53,19 @@ pub struct IdxSet<T: Idx> {
 }
 
 impl<T: Idx> fmt::Debug for IdxSetBuf<T> {
-    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) }
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
+        w.debug_list()
+         .entries(self.iter())
+         .finish()
+    }
 }
 
 impl<T: Idx> fmt::Debug for IdxSet<T> {
-    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) }
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
+        w.debug_list()
+         .entries(self.iter())
+         .finish()
+    }
 }
 
 impl<T: Idx> IdxSetBuf<T> {
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
index 1d1b367de20..0660cd96a4a 100644
--- a/src/librustc_data_structures/indexed_vec.rs
+++ b/src/librustc_data_structures/indexed_vec.rs
@@ -47,7 +47,7 @@ macro_rules! newtype_index {
         newtype_index!(
             @type[$name]
             @max[::std::u32::MAX]
-            @debug_name[unsafe {::std::intrinsics::type_name::<$name>() }]);
+            @debug_format["{}"]);
     );
 
     // Define any constants
@@ -55,14 +55,14 @@ macro_rules! newtype_index {
         newtype_index!(
             @type[$name]
             @max[::std::u32::MAX]
-            @debug_name[unsafe {::std::intrinsics::type_name::<$name>() }]
+            @debug_format["{}"]
             $($tokens)+);
     );
 
     // ---- private rules ----
 
     // Base case, user-defined constants (if any) have already been defined
-    (@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]) => (
+    (@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]) => (
         #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
             RustcEncodable, RustcDecodable)]
         pub struct $type(pub u32);
@@ -79,40 +79,43 @@ macro_rules! newtype_index {
 
         impl ::std::fmt::Debug for $type {
             fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-                write!(fmt, "{}{}", $debug_name, self.0)
+                write!(fmt, $debug_format, self.0)
             }
         }
     );
 
     // Rewrite final without comma to one that includes comma
-    (@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]
+    (@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]
             $name:ident = $constant:expr) => (
-        newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $name = $constant,);
+        newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $name = $constant,);
     );
 
     // Rewrite final const without comma to one that includes comma
-    (@type[$type:ident] @max[$_max:expr] @debug_name[$debug_name:expr]
+    (@type[$type:ident] @max[$_max:expr] @debug_format[$debug_format:expr]
             const $name:ident = $constant:expr) => (
-        newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] const $name = $constant,);
+        newtype_index!(@type[$type]
+                       @max[$max]
+                       @debug_format[$debug_format]
+                       const $name = $constant,);
     );
 
     // Replace existing default for max
-    (@type[$type:ident] @max[$_max:expr] @debug_name[$debug_name:expr]
+    (@type[$type:ident] @max[$_max:expr] @debug_format[$debug_format:expr]
             MAX = $max:expr, $($tokens:tt)*) => (
-        newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*);
+        newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*);
     );
 
-    // Replace existing default for debug_name
-    (@type[$type:ident] @max[$max:expr] @debug_name[$_debug_name:expr]
-            DEBUG_NAME = $debug_name:expr, $($tokens:tt)*) => (
-        newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*);
+    // Replace existing default for debug_format
+    (@type[$type:ident] @max[$max:expr] @debug_format[$_debug_format:expr]
+            DEBUG_FORMAT = $debug_format:expr, $($tokens:tt)*) => (
+        newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*);
     );
 
     // Assign a user-defined constant (as final param)
-    (@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]
+    (@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]
             const $name:ident = $constant:expr, $($tokens:tt)*) => (
         pub const $name: $type = $type($constant);
-        newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*);
+        newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*);
     );
 }
 
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 1cbc1aa7234..855362cf645 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -1007,7 +1007,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
     passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants);
     passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts"));
-    passes.push_pass(MIR_VALIDATED, mir::transform::nll::NLL);
 
     // borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED.
 
diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs
index ee2ef00be57..819f67a39e9 100644
--- a/src/librustc_mir/borrow_check.rs
+++ b/src/librustc_mir/borrow_check.rs
@@ -17,7 +17,8 @@ use rustc::ty::maps::Providers;
 use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue, Local};
 use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
 use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind};
-use rustc::mir::transform::{MirSource};
+use rustc::mir::transform::MirSource;
+use transform::nll;
 
 use rustc_data_structures::indexed_set::{self, IdxSetBuf};
 use rustc_data_structures::indexed_vec::{Idx};
@@ -46,93 +47,120 @@ pub fn provide(providers: &mut Providers) {
 }
 
 fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
-    let mir = tcx.mir_validated(def_id);
+    let input_mir = tcx.mir_validated(def_id);
     let src = MirSource::from_local_def_id(tcx, def_id);
     debug!("run query mir_borrowck: {}", tcx.node_path_str(src.item_id()));
 
-    let mir: &Mir<'tcx> = &mir.borrow();
-    if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.debugging_opts.borrowck_mir {
+    if {
+        !tcx.has_attr(def_id, "rustc_mir_borrowck") &&
+            !tcx.sess.opts.debugging_opts.borrowck_mir &&
+            !tcx.sess.opts.debugging_opts.nll
+    } {
         return;
     }
 
-    let id = src.item_id();
+    tcx.infer_ctxt().enter(|infcx| {
+        let input_mir: &Mir = &input_mir.borrow();
+        do_mir_borrowck(&infcx, input_mir, def_id, src);
+    });
+    debug!("mir_borrowck done");
+}
+
+fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                   input_mir: &Mir<'gcx>,
+                                   def_id: DefId,
+                                   src: MirSource)
+{
+    let tcx = infcx.tcx;
     let attributes = tcx.get_attrs(def_id);
     let param_env = tcx.param_env(def_id);
-    tcx.infer_ctxt().enter(|_infcx| {
-
-        let move_data = match MoveData::gather_moves(mir, tcx, param_env) {
-            Ok(move_data) => move_data,
-            Err((move_data, move_errors)) => {
-                for move_error in move_errors {
-                    let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
-                        MoveError::UnionMove { .. } =>
-                            unimplemented!("dont know how to report union move errors yet."),
-                        MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind),
-                    };
-                    let origin = Origin::Mir;
-                    let mut err = match kind {
-                        IllegalMoveOriginKind::Static =>
-                            tcx.cannot_move_out_of(span, "static item", origin),
-                        IllegalMoveOriginKind::BorrowedContent =>
-                            tcx.cannot_move_out_of(span, "borrowed_content", origin),
-                        IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } =>
-                            tcx.cannot_move_out_of_interior_of_drop(span, ty, origin),
-                        IllegalMoveOriginKind::InteriorOfSlice { elem_ty: ty, is_index } =>
-                            tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
-                        IllegalMoveOriginKind::InteriorOfArray { elem_ty: ty, is_index } =>
-                            tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
-                    };
-                    err.emit();
-                }
-                move_data
+
+    let id = src.item_id();
+
+    let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx, param_env) {
+        Ok(move_data) => move_data,
+        Err((move_data, move_errors)) => {
+            for move_error in move_errors {
+                let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
+                    MoveError::UnionMove { .. } =>
+                        unimplemented!("dont know how to report union move errors yet."),
+                    MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind),
+                };
+                let origin = Origin::Mir;
+                let mut err = match kind {
+                    IllegalMoveOriginKind::Static =>
+                        tcx.cannot_move_out_of(span, "static item", origin),
+                    IllegalMoveOriginKind::BorrowedContent =>
+                        tcx.cannot_move_out_of(span, "borrowed_content", origin),
+                    IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } =>
+                        tcx.cannot_move_out_of_interior_of_drop(span, ty, origin),
+                    IllegalMoveOriginKind::InteriorOfSlice { elem_ty: ty, is_index } =>
+                        tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
+                    IllegalMoveOriginKind::InteriorOfArray { elem_ty: ty, is_index } =>
+                        tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
+                };
+                err.emit();
             }
-        };
-        let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
-        let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
-        let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
-                                       Borrows::new(tcx, mir),
-                                       |bd, i| bd.location(i));
-        let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
-                                     MaybeInitializedLvals::new(tcx, mir, &mdpe),
-                                     |bd, i| &bd.move_data().move_paths[i]);
-        let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
-                                       MaybeUninitializedLvals::new(tcx, mir, &mdpe),
-                                       |bd, i| &bd.move_data().move_paths[i]);
-
-        let mut mbcx = MirBorrowckCtxt {
-            tcx: tcx,
-            mir: mir,
-            node_id: id,
-            move_data: &mdpe.move_data,
-            param_env: param_env,
-            fake_infer_ctxt: &_infcx,
-        };
+            move_data
+        }
+    };
 
-        let mut state = InProgress::new(flow_borrows,
-                                        flow_inits,
-                                        flow_uninits);
+    // Make our own copy of the MIR. This copy will be modified (in place) to
+    // contain non-lexical lifetimes. It will have a lifetime tied
+    // to the inference context.
+    let mut mir: Mir<'tcx> = input_mir.clone();
+    let mir = &mut mir;
+
+    // If we are in non-lexical mode, compute the non-lexical lifetimes.
+    let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll {
+        None
+    } else {
+        Some(nll::compute_regions(infcx, src, mir))
+    };
 
-        mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
-    });
+    let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
+    let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
+    let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
+                                   Borrows::new(tcx, mir, opt_regioncx.as_ref()),
+                                   |bd, i| bd.location(i));
+    let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
+                                 MaybeInitializedLvals::new(tcx, mir, &mdpe),
+                                 |bd, i| &bd.move_data().move_paths[i]);
+    let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
+                                   MaybeUninitializedLvals::new(tcx, mir, &mdpe),
+                                   |bd, i| &bd.move_data().move_paths[i]);
+
+    let mut mbcx = MirBorrowckCtxt {
+        tcx: tcx,
+        mir: mir,
+        node_id: id,
+        move_data: &mdpe.move_data,
+        param_env: param_env,
+        fake_infer_ctxt: &infcx,
+    };
 
-    debug!("mir_borrowck done");
+    let mut state = InProgress::new(flow_borrows,
+                                    flow_inits,
+                                    flow_uninits);
+
+    mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
 }
 
 #[allow(dead_code)]
 pub struct MirBorrowckCtxt<'c, 'b, 'a: 'b+'c, 'gcx: 'a+'tcx, 'tcx: 'a> {
-    tcx: TyCtxt<'a, 'gcx, 'gcx>,
-    mir: &'b Mir<'gcx>,
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    mir: &'b Mir<'tcx>,
     node_id: ast::NodeId,
-    move_data: &'b MoveData<'gcx>,
+    move_data: &'b MoveData<'tcx>,
     param_env: ParamEnv<'tcx>,
     fake_infer_ctxt: &'c InferCtxt<'c, 'gcx, 'tcx>,
 }
 
 // (forced to be `pub` due to its use as an associated type below.)
-pub struct InProgress<'b, 'tcx: 'b> {
-    borrows: FlowInProgress<Borrows<'b, 'tcx>>,
-    inits: FlowInProgress<MaybeInitializedLvals<'b, 'tcx>>,
-    uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>>,
+pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> {
+    borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
+    inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
+    uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
 }
 
 struct FlowInProgress<BD> where BD: BitDenotation {
@@ -147,12 +175,12 @@ struct FlowInProgress<BD> where BD: BitDenotation {
 // 2. loans made in overlapping scopes do not conflict
 // 3. assignments do not affect things loaned out as immutable
 // 4. moves do not affect things loaned out in any way
-impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
+impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx>
     for MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 {
-    type FlowState = InProgress<'b, 'gcx>;
+    type FlowState = InProgress<'b, 'gcx, 'tcx>;
 
-    fn mir(&self) -> &'b Mir<'gcx> { self.mir }
+    fn mir(&self) -> &'b Mir<'tcx> { self.mir }
 
     fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
         flow_state.each_flow(|b| b.reset_to_entry_of(bb),
@@ -193,7 +221,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
 
     fn visit_statement_entry(&mut self,
                              location: Location,
-                             stmt: &Statement<'gcx>,
+                             stmt: &Statement<'tcx>,
                              flow_state: &Self::FlowState) {
         let summary = flow_state.summary();
         debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location, stmt, summary);
@@ -261,7 +289,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
 
     fn visit_terminator_entry(&mut self,
                               location: Location,
-                              term: &Terminator<'gcx>,
+                              term: &Terminator<'tcx>,
                               flow_state: &Self::FlowState) {
         let loc = location;
         let summary = flow_state.summary();
@@ -405,9 +433,9 @@ enum WriteKind {
 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
     fn access_lvalue(&mut self,
                      context: Context,
-                     lvalue_span: (&Lvalue<'gcx>, Span),
+                     lvalue_span: (&Lvalue<'tcx>, Span),
                      kind: (ShallowOrDeep, ReadOrWrite),
-                     flow_state: &InProgress<'b, 'gcx>) {
+                     flow_state: &InProgress<'b, 'gcx, 'tcx>) {
         // FIXME: also need to check permissions (e.g. reject mut
         // borrow of immutable ref, moves through non-`Box`-ref)
         let (sd, rw) = kind;
@@ -425,8 +453,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
                                     context, lvalue_span, borrow),
                             ReadKind::Borrow(bk) => {
                                 let end_issued_loan_span =
-                                    flow_state.borrows.base_results.operator().region_span(
-                                        &borrow.region).end_point();
+                                    flow_state.borrows.base_results.operator().opt_region_end_span(
+                                        &borrow.region);
                                 this.report_conflicting_borrow(
                                     context, common_prefix, lvalue_span, bk,
                                     &borrow, end_issued_loan_span)
@@ -438,8 +466,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
                         match kind {
                             WriteKind::MutableBorrow(bk) => {
                                 let end_issued_loan_span =
-                                    flow_state.borrows.base_results.operator().region_span(
-                                        &borrow.region).end_point();
+                                    flow_state.borrows.base_results.operator().opt_region_end_span(
+                                        &borrow.region);
                                 this.report_conflicting_borrow(
                                     context, common_prefix, lvalue_span, bk,
                                     &borrow, end_issued_loan_span)
@@ -460,10 +488,10 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
     fn mutate_lvalue(&mut self,
                      context: Context,
-                     lvalue_span: (&Lvalue<'gcx>, Span),
+                     lvalue_span: (&Lvalue<'tcx>, Span),
                      kind: ShallowOrDeep,
                      mode: MutateMode,
-                     flow_state: &InProgress<'b, 'gcx>) {
+                     flow_state: &InProgress<'b, 'gcx, 'tcx>) {
         // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
         match mode {
             MutateMode::WriteAndRead => {
@@ -482,9 +510,9 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
     fn consume_rvalue(&mut self,
                       context: Context,
-                      (rvalue, span): (&Rvalue<'gcx>, Span),
+                      (rvalue, span): (&Rvalue<'tcx>, Span),
                       _location: Location,
-                      flow_state: &InProgress<'b, 'gcx>) {
+                      flow_state: &InProgress<'b, 'gcx, 'tcx>) {
         match *rvalue {
             Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => {
                 let access_kind = match bk {
@@ -540,8 +568,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
     fn consume_operand(&mut self,
                        context: Context,
                        consume_via_drop: ConsumeKind,
-                       (operand, span): (&Operand<'gcx>, Span),
-                       flow_state: &InProgress<'b, 'gcx>) {
+                       (operand, span): (&Operand<'tcx>, Span),
+                       flow_state: &InProgress<'b, 'gcx, 'tcx>) {
         match *operand {
             Operand::Consume(ref lvalue) => {
                 self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state)
@@ -553,8 +581,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
     fn consume_lvalue(&mut self,
                       context: Context,
                       consume_via_drop: ConsumeKind,
-                      lvalue_span: (&Lvalue<'gcx>, Span),
-                      flow_state: &InProgress<'b, 'gcx>) {
+                      lvalue_span: (&Lvalue<'tcx>, Span),
+                      flow_state: &InProgress<'b, 'gcx, 'tcx>) {
         let lvalue = lvalue_span.0;
         let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
         let moves_by_default =
@@ -584,8 +612,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
     fn check_if_reassignment_to_immutable_state(&mut self,
                                                 context: Context,
-                                                (lvalue, span): (&Lvalue<'gcx>, Span),
-                                                flow_state: &InProgress<'b, 'gcx>) {
+                                                (lvalue, span): (&Lvalue<'tcx>, Span),
+                                                flow_state: &InProgress<'b, 'gcx, 'tcx>) {
         let move_data = self.move_data;
 
         // determine if this path has a non-mut owner (and thus needs checking).
@@ -635,8 +663,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
     fn check_if_path_is_moved(&mut self,
                               context: Context,
                               desired_action: &str,
-                              lvalue_span: (&Lvalue<'gcx>, Span),
-                              flow_state: &InProgress<'b, 'gcx>) {
+                              lvalue_span: (&Lvalue<'tcx>, Span),
+                              flow_state: &InProgress<'b, 'gcx, 'tcx>) {
         // FIXME: analogous code in check_loans first maps `lvalue` to
         // its base_path ... but is that what we want here?
         let lvalue = self.base_path(lvalue_span.0);
@@ -725,7 +753,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
     /// An Err result includes a tag indicated why the search failed.
     /// Currenly this can only occur if the lvalue is built off of a
     /// static variable, as we do not track those in the MoveData.
-    fn move_path_closest_to(&mut self, lvalue: &Lvalue<'gcx>)
+    fn move_path_closest_to(&mut self, lvalue: &Lvalue<'tcx>)
                             -> Result<MovePathIndex, NoMovePathFound>
     {
         let mut last_prefix = lvalue;
@@ -743,7 +771,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
     }
 
     fn move_path_for_lvalue(&mut self,
-                            lvalue: &Lvalue<'gcx>)
+                            lvalue: &Lvalue<'tcx>)
                             -> Option<MovePathIndex>
     {
         // If returns None, then there is no move path corresponding
@@ -758,8 +786,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
     fn check_if_assigned_path_is_moved(&mut self,
                                        context: Context,
-                                       (lvalue, span): (&Lvalue<'gcx>, Span),
-                                       flow_state: &InProgress<'b, 'gcx>) {
+                                       (lvalue, span): (&Lvalue<'tcx>, Span),
+                                       flow_state: &InProgress<'b, 'gcx, 'tcx>) {
         // recur down lvalue; dispatch to check_if_path_is_moved when necessary
         let mut lvalue = lvalue;
         loop {
@@ -827,10 +855,10 @@ enum NoMovePathFound {
 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
     fn each_borrow_involving_path<F>(&mut self,
                                      _context: Context,
-                                     access_lvalue: (ShallowOrDeep, &Lvalue<'gcx>),
-                                     flow_state: &InProgress<'b, 'gcx>,
+                                     access_lvalue: (ShallowOrDeep, &Lvalue<'tcx>),
+                                     flow_state: &InProgress<'b, 'gcx, 'tcx>,
                                      mut op: F)
-        where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'gcx>, &Lvalue) -> Control
+        where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Lvalue) -> Control
     {
         let (access, lvalue) = access_lvalue;
 
@@ -928,9 +956,9 @@ mod prefixes {
     }
 
 
-    pub(super) struct Prefixes<'c, 'tcx: 'c> {
+    pub(super) struct Prefixes<'c, 'gcx: 'tcx, 'tcx: 'c> {
         mir: &'c Mir<'tcx>,
-        tcx: TyCtxt<'c, 'tcx, 'tcx>,
+        tcx: TyCtxt<'c, 'gcx, 'tcx>,
         kind: PrefixSet,
         next: Option<&'c Lvalue<'tcx>>,
     }
@@ -951,15 +979,15 @@ mod prefixes {
         /// (inclusive) from longest to smallest, potentially
         /// terminating the iteration early based on `kind`.
         pub(super) fn prefixes<'d>(&self,
-                                   lvalue: &'d Lvalue<'gcx>,
+                                   lvalue: &'d Lvalue<'tcx>,
                                    kind: PrefixSet)
-                                   -> Prefixes<'d, 'gcx> where 'b: 'd
+                                   -> Prefixes<'d, 'gcx, 'tcx> where 'b: 'd
         {
             Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx }
         }
     }
 
-    impl<'c, 'tcx> Iterator for Prefixes<'c, 'tcx> {
+    impl<'c, 'gcx, 'tcx> Iterator for Prefixes<'c, 'gcx, 'tcx> {
         type Item = &'c Lvalue<'tcx>;
         fn next(&mut self) -> Option<Self::Item> {
             let mut cursor = match self.next {
@@ -1101,7 +1129,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
                                  (lvalue, span): (&Lvalue, Span),
                                  gen_borrow_kind: BorrowKind,
                                  issued_borrow: &BorrowData,
-                                 end_issued_loan_span: Span) {
+                                 end_issued_loan_span: Option<Span>) {
         use self::prefixes::IsPrefixOf;
 
         assert!(common_prefix.is_prefix_of(lvalue));
@@ -1315,7 +1343,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
     // moves out of a Box. They should be removed when/if we stop
     // treating Box specially (e.g. when/if DerefMove is added...)
 
-    fn base_path<'d>(&self, lvalue: &'d Lvalue<'gcx>) -> &'d Lvalue<'gcx> {
+    fn base_path<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> &'d Lvalue<'tcx> {
         //! Returns the base of the leftmost (deepest) dereference of an
         //! Box in `lvalue`. If there is no dereference of an Box
         //! in `lvalue`, then it just returns `lvalue` itself.
@@ -1364,10 +1392,10 @@ impl ContextKind {
     fn new(self, loc: Location) -> Context { Context { kind: self, loc: loc } }
 }
 
-impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
-    pub(super) fn new(borrows: DataflowResults<Borrows<'b, 'tcx>>,
-                      inits: DataflowResults<MaybeInitializedLvals<'b, 'tcx>>,
-                      uninits: DataflowResults<MaybeUninitializedLvals<'b, 'tcx>>)
+impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
+    pub(super) fn new(borrows: DataflowResults<Borrows<'b, 'gcx, 'tcx>>,
+                      inits: DataflowResults<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
+                      uninits: DataflowResults<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>)
                       -> Self {
         InProgress {
             borrows: FlowInProgress::new(borrows),
@@ -1380,9 +1408,9 @@ impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
                              mut xform_borrows: XB,
                              mut xform_inits: XI,
                              mut xform_uninits: XU) where
-        XB: FnMut(&mut FlowInProgress<Borrows<'b, 'tcx>>),
-        XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'tcx>>),
-        XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>>),
+        XB: FnMut(&mut FlowInProgress<Borrows<'b, 'gcx, 'tcx>>),
+        XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>),
+        XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>),
     {
         xform_borrows(&mut self.borrows);
         xform_inits(&mut self.inits);
@@ -1438,7 +1466,7 @@ impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
     }
 }
 
-impl<'b, 'tcx> FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>> {
+impl<'b, 'gcx, 'tcx> FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>> {
     fn has_any_child_of(&self, mpi: MovePathIndex) -> Option<MovePathIndex> {
         let move_data = self.base_results.operator().move_data();
 
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index b2f0ff57b62..77496c7b8f2 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -240,10 +240,10 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 ///////////////////////////////////////////////////////////////////////////
 // BuildMir -- walks a crate, looking for fn items and methods to build MIR from
 
-pub fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                             closure_expr_id: ast::NodeId,
-                             body_id: hir::BodyId)
-                             -> Ty<'tcx> {
+pub fn closure_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                       closure_expr_id: ast::NodeId,
+                                       body_id: hir::BodyId)
+                                       -> Ty<'tcx> {
     let closure_expr_hir_id = tcx.hir.node_to_hir_id(closure_expr_id);
     let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_hir_id);
 
diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs
index bd41bce67da..e35bd34c40b 100644
--- a/src/librustc_mir/dataflow/drop_flag_effects.rs
+++ b/src/librustc_mir/dataflow/drop_flag_effects.rs
@@ -58,9 +58,9 @@ pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
 /// is no need to maintain separate drop flags to track such state.
 ///
 /// FIXME: we have to do something for moving slice patterns.
-fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                                      mir: &Mir<'tcx>,
-                                                      lv: &mir::Lvalue<'tcx>) -> bool {
+fn lvalue_contents_drop_state_cannot_differ<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                                            mir: &Mir<'tcx>,
+                                                            lv: &mir::Lvalue<'tcx>) -> bool {
     let ty = lv.ty(mir, tcx).to_ty(tcx);
     match ty.sty {
         ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => {
@@ -79,8 +79,8 @@ fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
     }
 }
 
-pub(crate) fn on_lookup_result_bits<'a, 'tcx, F>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub(crate) fn on_lookup_result_bits<'a, 'gcx, 'tcx, F>(
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &Mir<'tcx>,
     move_data: &MoveData<'tcx>,
     lookup_result: LookupResult,
@@ -97,16 +97,16 @@ pub(crate) fn on_lookup_result_bits<'a, 'tcx, F>(
     }
 }
 
-pub(crate) fn on_all_children_bits<'a, 'tcx, F>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub(crate) fn on_all_children_bits<'a, 'gcx, 'tcx, F>(
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &Mir<'tcx>,
     move_data: &MoveData<'tcx>,
     move_path_index: MovePathIndex,
     mut each_child: F)
     where F: FnMut(MovePathIndex)
 {
-    fn is_terminal_path<'a, 'tcx>(
-        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    fn is_terminal_path<'a, 'gcx, 'tcx>(
+        tcx: TyCtxt<'a, 'gcx, 'tcx>,
         mir: &Mir<'tcx>,
         move_data: &MoveData<'tcx>,
         path: MovePathIndex) -> bool
@@ -115,8 +115,8 @@ pub(crate) fn on_all_children_bits<'a, 'tcx, F>(
             tcx, mir, &move_data.move_paths[path].lvalue)
     }
 
-    fn on_all_children_bits<'a, 'tcx, F>(
-        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    fn on_all_children_bits<'a, 'gcx, 'tcx, F>(
+        tcx: TyCtxt<'a, 'gcx, 'tcx>,
         mir: &Mir<'tcx>,
         move_data: &MoveData<'tcx>,
         move_path_index: MovePathIndex,
@@ -138,10 +138,10 @@ pub(crate) fn on_all_children_bits<'a, 'tcx, F>(
     on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child);
 }
 
-pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub(crate) fn on_all_drop_children_bits<'a, 'gcx, 'tcx, F>(
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &Mir<'tcx>,
-    ctxt: &MoveDataParamEnv<'tcx>,
+    ctxt: &MoveDataParamEnv<'gcx, 'tcx>,
     path: MovePathIndex,
     mut each_child: F)
     where F: FnMut(MovePathIndex)
@@ -151,7 +151,9 @@ pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>(
         let ty = lvalue.ty(mir, tcx).to_ty(tcx);
         debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, lvalue, ty);
 
-        if ty.needs_drop(tcx, ctxt.param_env) {
+        let gcx = tcx.global_tcx();
+        let erased_ty = gcx.lift(&tcx.erase_regions(&ty)).unwrap();
+        if erased_ty.needs_drop(gcx, ctxt.param_env) {
             each_child(child);
         } else {
             debug!("on_all_drop_children_bits - skipping")
@@ -159,10 +161,10 @@ pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>(
     })
 }
 
-pub(crate) fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub(crate) fn drop_flag_effects_for_function_entry<'a, 'gcx, 'tcx, F>(
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &Mir<'tcx>,
-    ctxt: &MoveDataParamEnv<'tcx>,
+    ctxt: &MoveDataParamEnv<'gcx, 'tcx>,
     mut callback: F)
     where F: FnMut(MovePathIndex, DropFlagState)
 {
@@ -176,10 +178,10 @@ pub(crate) fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
     }
 }
 
-pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub(crate) fn drop_flag_effects_for_location<'a, 'gcx, 'tcx, F>(
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &Mir<'tcx>,
-    ctxt: &MoveDataParamEnv<'tcx>,
+    ctxt: &MoveDataParamEnv<'gcx, 'tcx>,
     loc: Location,
     mut callback: F)
     where F: FnMut(MovePathIndex, DropFlagState)
@@ -196,7 +198,9 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>(
         // don't move out of non-Copy things
         let lvalue = &move_data.move_paths[path].lvalue;
         let ty = lvalue.ty(mir, tcx).to_ty(tcx);
-        if !ty.moves_by_default(tcx, param_env, DUMMY_SP) {
+        let gcx = tcx.global_tcx();
+        let erased_ty = gcx.lift(&tcx.erase_regions(&ty)).unwrap();
+        if !erased_ty.moves_by_default(gcx, param_env, DUMMY_SP) {
             continue;
         }
 
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 9321121fe15..17aa8c05418 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -21,6 +21,8 @@ use rustc_data_structures::indexed_vec::{IndexVec};
 
 use dataflow::{BitDenotation, BlockSets, DataflowOperator};
 pub use dataflow::indexes::BorrowIndex;
+use transform::nll::region_infer::RegionInferenceContext;
+use transform::nll::ToRegionIndex;
 
 use syntax_pos::Span;
 
@@ -29,13 +31,14 @@ use std::fmt;
 // `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be
 // uniquely identified in the MIR by the `Location` of the assigment
 // statement in which it appears on the right hand side.
-pub struct Borrows<'a, 'tcx: 'a> {
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &'a Mir<'tcx>,
     borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
     location_map: FxHashMap<Location, BorrowIndex>,
     region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
     region_span_map: FxHashMap<RegionKind, Span>,
+    nonlexical_regioncx: Option<&'a RegionInferenceContext>,
 }
 
 // temporarily allow some dead fields: `kind` and `region` will be
@@ -63,8 +66,11 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
     }
 }
 
-impl<'a, 'tcx> Borrows<'a, 'tcx> {
-    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
+impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+               mir: &'a Mir<'tcx>,
+               nonlexical_regioncx: Option<&'a RegionInferenceContext>)
+               -> Self {
         let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
                                           location_map: FxHashMap(),
                                           region_map: FxHashMap(),
@@ -75,7 +81,8 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
                          borrows: visitor.idx_vec,
                          location_map: visitor.location_map,
                          region_map: visitor.region_map,
-                         region_span_map: visitor.region_span_map};
+                         region_span_map: visitor.region_span_map,
+                         nonlexical_regioncx };
 
         struct GatherBorrows<'tcx> {
             idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
@@ -116,14 +123,34 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
         &self.borrows[idx].location
     }
 
-    pub fn region_span(&self, region: &Region) -> Span {
+    /// Returns the span for the "end point" given region. This will
+    /// return `None` if NLL is enabled, since that concept has no
+    /// meaning there.  Otherwise, it should return some.
+    pub fn opt_region_end_span(&self, region: &Region) -> Option<Span> {
         let opt_span = self.region_span_map.get(region);
-        assert!(opt_span.is_some(), "end region not found for {:?}", region);
-        *opt_span.unwrap()
+        assert!(self.nonlexical_regioncx.is_some() ||
+                opt_span.is_some(), "end region not found for {:?}", region);
+        opt_span.map(|s| s.end_point())
+    }
+
+    /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
+    fn kill_loans_out_of_scope_at_location(&self,
+                                           sets: &mut BlockSets<BorrowIndex>,
+                                           location: Location) {
+        if let Some(regioncx) = self.nonlexical_regioncx {
+            for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
+                let borrow_region = regioncx.region_value(borrow_data.region.to_region_index());
+                if !borrow_region.may_contain(location) && location != borrow_data.location {
+                    debug!("kill_loans_out_of_scope_at_location: kill{:?} \
+                           location={:?} borrow_data={:?}", borrow_index, location, borrow_data);
+                    sets.kill(&borrow_index);
+                }
+            }
+        }
     }
 }
 
-impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
     type Idx = BorrowIndex;
     fn name() -> &'static str { "borrows" }
     fn bits_per_block(&self) -> usize {
@@ -146,6 +173,7 @@ impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
         match stmt.kind {
             mir::StatementKind::EndRegion(region_scope) => {
                 if let Some(borrow_indexes) = self.region_map.get(&ReScope(region_scope)) {
+                    assert!(self.nonlexical_regioncx.is_none());
                     for idx in borrow_indexes { sets.kill(&idx); }
                 } else {
                     // (if there is no entry, then there are no borrows to be tracked)
@@ -172,11 +200,14 @@ impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
             mir::StatementKind::Nop => {}
 
         }
+
+        self.kill_loans_out_of_scope_at_location(sets, location);
     }
+
     fn terminator_effect(&self,
-                         _sets: &mut BlockSets<BorrowIndex>,
-                         _location: Location) {
-        // no terminators start nor end region scopes.
+                         sets: &mut BlockSets<BorrowIndex>,
+                         location: Location) {
+        self.kill_loans_out_of_scope_at_location(sets, location);
     }
 
     fn propagate_call_return(&self,
@@ -188,14 +219,14 @@ impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> BitwiseOperator for Borrows<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> {
     #[inline]
     fn join(&self, pred1: usize, pred2: usize) -> usize {
         pred1 | pred2 // union effects of preds when computing borrows
     }
 }
 
-impl<'a, 'tcx> DataflowOperator for Borrows<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> DataflowOperator for Borrows<'a, 'gcx, 'tcx> {
     #[inline]
     fn bottom_value() -> bool {
         false // bottom = no Rvalue::Refs are active by default
diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs
index 19a595622b9..af99706be81 100644
--- a/src/librustc_mir/dataflow/impls/mod.rs
+++ b/src/librustc_mir/dataflow/impls/mod.rs
@@ -69,23 +69,23 @@ pub(super) mod borrows;
 /// Similarly, at a given `drop` statement, the set-intersection
 /// between this data and `MaybeUninitializedLvals` yields the set of
 /// l-values that would require a dynamic drop-flag at that statement.
-pub struct MaybeInitializedLvals<'a, 'tcx: 'a> {
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub struct MaybeInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &'a Mir<'tcx>,
-    mdpe: &'a MoveDataParamEnv<'tcx>,
+    mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
 }
 
-impl<'a, 'tcx: 'a> MaybeInitializedLvals<'a, 'tcx> {
-    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                mir: &'a Mir<'tcx>,
-               mdpe: &'a MoveDataParamEnv<'tcx>)
+               mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
                -> Self
     {
         MaybeInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
     }
 }
 
-impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
 }
 
@@ -124,23 +124,23 @@ impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'tcx> {
 /// Similarly, at a given `drop` statement, the set-intersection
 /// between this data and `MaybeInitializedLvals` yields the set of
 /// l-values that would require a dynamic drop-flag at that statement.
-pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> {
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub struct MaybeUninitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &'a Mir<'tcx>,
-    mdpe: &'a MoveDataParamEnv<'tcx>,
+    mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
 }
 
-impl<'a, 'tcx: 'a> MaybeUninitializedLvals<'a, 'tcx> {
-    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                mir: &'a Mir<'tcx>,
-               mdpe: &'a MoveDataParamEnv<'tcx>)
+               mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
                -> Self
     {
         MaybeUninitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
     }
 }
 
-impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
 }
 
@@ -185,27 +185,27 @@ impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'tcx> {
 /// Similarly, at a given `drop` statement, the set-difference between
 /// this data and `MaybeInitializedLvals` yields the set of l-values
 /// that would require a dynamic drop-flag at that statement.
-pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> {
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub struct DefinitelyInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &'a Mir<'tcx>,
-    mdpe: &'a MoveDataParamEnv<'tcx>,
+    mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
 }
 
-impl<'a, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'tcx> {
-    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                mir: &'a Mir<'tcx>,
-               mdpe: &'a MoveDataParamEnv<'tcx>)
+               mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
                -> Self
     {
         DefinitelyInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
     }
 }
 
-impl<'a, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
 }
 
-impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
                    state: DropFlagState)
     {
@@ -216,7 +216,7 @@ impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
                    state: DropFlagState)
     {
@@ -227,7 +227,7 @@ impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
                    state: DropFlagState)
     {
@@ -238,7 +238,7 @@ impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
     type Idx = MovePathIndex;
     fn name() -> &'static str { "maybe_init" }
     fn bits_per_block(&self) -> usize {
@@ -290,7 +290,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
     type Idx = MovePathIndex;
     fn name() -> &'static str { "maybe_uninit" }
     fn bits_per_block(&self) -> usize {
@@ -345,7 +345,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
     type Idx = MovePathIndex;
     fn name() -> &'static str { "definite_init" }
     fn bits_per_block(&self) -> usize {
@@ -399,21 +399,21 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
     #[inline]
     fn join(&self, pred1: usize, pred2: usize) -> usize {
         pred1 | pred2 // "maybe" means we union effects of both preds
     }
 }
 
-impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
     #[inline]
     fn join(&self, pred1: usize, pred2: usize) -> usize {
         pred1 | pred2 // "maybe" means we union effects of both preds
     }
 }
 
-impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
     #[inline]
     fn join(&self, pred1: usize, pred2: usize) -> usize {
         pred1 & pred2 // "definitely" means we intersect effects of both preds
@@ -430,21 +430,21 @@ impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
 // propagating, or you start at all-ones and then use Intersect as
 // your merge when propagating.
 
-impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
     #[inline]
     fn bottom_value() -> bool {
         false // bottom = uninitialized
     }
 }
 
-impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
     #[inline]
     fn bottom_value() -> bool {
         false // bottom = initialized (start_block_effect counters this at outset)
     }
 }
 
-impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
     #[inline]
     fn bottom_value() -> bool {
         true // bottom = initialized (start_block_effect counters this at outset)
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index 9fa5691d647..d27a4e7e9d9 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -91,19 +91,19 @@ pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option
     return None;
 }
 
-pub struct MoveDataParamEnv<'tcx> {
+pub struct MoveDataParamEnv<'gcx, 'tcx> {
     pub(crate) move_data: MoveData<'tcx>,
-    pub(crate) param_env: ty::ParamEnv<'tcx>,
+    pub(crate) param_env: ty::ParamEnv<'gcx>,
 }
 
-pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                mir: &Mir<'tcx>,
-                                node_id: ast::NodeId,
-                                attributes: &[ast::Attribute],
-                                dead_unwinds: &IdxSet<BasicBlock>,
-                                bd: BD,
-                                p: P)
-                                -> DataflowResults<BD>
+pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                                 mir: &Mir<'tcx>,
+                                                 node_id: ast::NodeId,
+                                                 attributes: &[ast::Attribute],
+                                                 dead_unwinds: &IdxSet<BasicBlock>,
+                                                 bd: BD,
+                                                 p: P)
+                                                 -> DataflowResults<BD>
     where BD: BitDenotation,
           P: Fn(&BD, BD::Idx) -> &fmt::Debug
 {
@@ -612,9 +612,9 @@ pub trait BitDenotation: DataflowOperator {
                              dest_lval: &mir::Lvalue);
 }
 
-impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
+impl<'a, 'gcx, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
 {
-    pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    pub fn new(_tcx: TyCtxt<'a, 'gcx, 'tcx>,
                mir: &'a Mir<'tcx>,
                dead_unwinds: &'a IdxSet<mir::BasicBlock>,
                denotation: D) -> Self {
diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
index 0790d937ceb..8f473d035ee 100644
--- a/src/librustc_mir/dataflow/move_paths/builder.rs
+++ b/src/librustc_mir/dataflow/move_paths/builder.rs
@@ -25,18 +25,18 @@ use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, Move
 use super::{MoveError};
 use super::IllegalMoveOriginKind::*;
 
-struct MoveDataBuilder<'a, 'tcx: 'a> {
+struct MoveDataBuilder<'a, 'gcx: 'tcx, 'tcx: 'a> {
     mir: &'a Mir<'tcx>,
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    param_env: ty::ParamEnv<'gcx>,
     data: MoveData<'tcx>,
     errors: Vec<MoveError<'tcx>>,
 }
 
-impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> {
     fn new(mir: &'a Mir<'tcx>,
-           tcx: TyCtxt<'a, 'tcx, 'tcx>,
-           param_env: ty::ParamEnv<'tcx>)
+           tcx: TyCtxt<'a, 'gcx, 'tcx>,
+           param_env: ty::ParamEnv<'gcx>)
            -> Self {
         let mut move_paths = IndexVec::new();
         let mut path_map = IndexVec::new();
@@ -86,7 +86,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
     }
 }
 
-impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
+impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
     /// This creates a MovePath for a given lvalue, returning an `MovePathError`
     /// if that lvalue can't be moved from.
     ///
@@ -175,7 +175,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> {
     fn finalize(self) -> Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<MoveError<'tcx>>)> {
         debug!("{}", {
             debug!("moves for {:?}:", self.mir.span);
@@ -197,11 +197,11 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
     }
 }
 
-pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
-                                     tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                     param_env: ty::ParamEnv<'tcx>)
-                                     -> Result<MoveData<'tcx>,
-                                               (MoveData<'tcx>, Vec<MoveError<'tcx>>)> {
+pub(super) fn gather_moves<'a, 'gcx, 'tcx>(mir: &Mir<'tcx>,
+                                           tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                           param_env: ty::ParamEnv<'gcx>)
+                                           -> Result<MoveData<'tcx>,
+                                                     (MoveData<'tcx>, Vec<MoveError<'tcx>>)> {
     let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
 
     for (bb, block) in mir.basic_blocks().iter_enumerated() {
@@ -220,7 +220,7 @@ pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
     builder.finalize()
 }
 
-impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
+impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> {
     fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
         debug!("gather_statement({:?}, {:?})", loc, stmt);
         (Gatherer { builder: self, loc }).gather_statement(stmt);
@@ -232,12 +232,12 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
     }
 }
 
-struct Gatherer<'b, 'a: 'b, 'tcx: 'a> {
-    builder: &'b mut MoveDataBuilder<'a, 'tcx>,
+struct Gatherer<'b, 'a: 'b, 'gcx: 'tcx, 'tcx: 'a> {
+    builder: &'b mut MoveDataBuilder<'a, 'gcx, 'tcx>,
     loc: Location,
 }
 
-impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
+impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
     fn gather_statement(&mut self, stmt: &Statement<'tcx>) {
         match stmt.kind {
             StatementKind::Assign(ref lval, ref rval) => {
@@ -352,8 +352,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
         debug!("gather_move({:?}, {:?})", self.loc, lval);
 
         let tcx = self.builder.tcx;
+        let gcx = tcx.global_tcx();
         let lv_ty = lval.ty(self.builder.mir, tcx).to_ty(tcx);
-        if !lv_ty.moves_by_default(tcx, self.builder.param_env, DUMMY_SP) {
+        let erased_ty = gcx.lift(&tcx.erase_regions(&lv_ty)).unwrap();
+        if !erased_ty.moves_by_default(gcx, self.builder.param_env, DUMMY_SP) {
             debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", self.loc, lval, lv_ty);
             return
         }
diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs
index 9369156a223..5bfecd01aaa 100644
--- a/src/librustc_mir/dataflow/move_paths/mod.rs
+++ b/src/librustc_mir/dataflow/move_paths/mod.rs
@@ -256,10 +256,10 @@ impl<'tcx> MoveError<'tcx> {
     }
 }
 
-impl<'a, 'tcx> MoveData<'tcx> {
+impl<'a, 'gcx, 'tcx> MoveData<'tcx> {
     pub fn gather_moves(mir: &Mir<'tcx>,
-                        tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                        param_env: ty::ParamEnv<'tcx>)
+                        tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                        param_env: ty::ParamEnv<'gcx>)
                         -> Result<Self, (Self, Vec<MoveError<'tcx>>)> {
         builder::gather_moves(mir, tcx, param_env)
     }
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index 7e4206e14c5..b72823dff2b 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -18,13 +18,14 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
 
 #![feature(box_patterns)]
 #![feature(box_syntax)]
+#![feature(conservative_impl_trait)]
 #![feature(const_fn)]
-#![feature(core_intrinsics)]
 #![feature(i128_type)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(placement_in_syntax)]
 #![feature(collection_placement)]
 #![feature(nonzero)]
+#![feature(underscore_lifetimes)]
 
 #[macro_use]
 extern crate bitflags;
diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs
index be1b794ecdf..94da1f31a96 100644
--- a/src/librustc_mir/transform/elaborate_drops.rs
+++ b/src/librustc_mir/transform/elaborate_drops.rs
@@ -83,7 +83,7 @@ fn find_dead_unwinds<'a, 'tcx>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     mir: &Mir<'tcx>,
     id: ast::NodeId,
-    env: &MoveDataParamEnv<'tcx>)
+    env: &MoveDataParamEnv<'tcx, 'tcx>)
     -> IdxSetBuf<BasicBlock>
 {
     debug!("find_dead_unwinds({:?})", mir.span);
@@ -146,7 +146,7 @@ impl InitializationData {
     fn apply_location<'a,'tcx>(&mut self,
                                tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                mir: &Mir<'tcx>,
-                               env: &MoveDataParamEnv<'tcx>,
+                               env: &MoveDataParamEnv<'tcx, 'tcx>,
                                loc: Location)
     {
         drop_flag_effects_for_location(tcx, mir, env, loc, |path, df| {
@@ -280,9 +280,9 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> {
 struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     mir: &'a Mir<'tcx>,
-    env: &'a MoveDataParamEnv<'tcx>,
-    flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx>>,
-    flow_uninits:  DataflowResults<MaybeUninitializedLvals<'a, 'tcx>>,
+    env: &'a MoveDataParamEnv<'tcx, 'tcx>,
+    flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx, 'tcx>>,
+    flow_uninits:  DataflowResults<MaybeUninitializedLvals<'a, 'tcx, 'tcx>>,
     drop_flags: FxHashMap<MovePathIndex, Local>,
     patch: MirPatch<'tcx>,
 }
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 7d0814b67fb..52a50333f45 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -68,7 +68,7 @@ use rustc::mir::visit::{LvalueContext, Visitor, MutVisitor};
 use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior};
 use rustc::ty::subst::{Kind, Substs};
 use util::dump_mir;
-use util::liveness;
+use util::liveness::{self, LivenessMode};
 use rustc_const_math::ConstInt;
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::indexed_set::IdxSetBuf;
@@ -348,7 +348,10 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     ignored.visit_mir(mir);
 
     let mut set = liveness::LocalSet::new_empty(mir.local_decls.len());
-    let liveness = liveness::liveness_of_locals(mir);
+    let liveness = liveness::liveness_of_locals(mir, LivenessMode {
+        include_regular_use: true,
+        include_drops: true,
+    });
     liveness::dump_mir(tcx, "generator_liveness", source, mir, &liveness);
 
     let mut storage_liveness_map = HashMap::new();
diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs
new file mode 100644
index 00000000000..a7570c610d8
--- /dev/null
+++ b/src/librustc_mir/transform/nll/constraint_generation.rs
@@ -0,0 +1,269 @@
+// 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 rustc::hir;
+use rustc::mir::{BasicBlock, BorrowKind, Location, Lvalue, Mir, Rvalue, Statement, StatementKind};
+use rustc::mir::transform::MirSource;
+use rustc::mir::visit::Visitor;
+use rustc::mir::Lvalue::Projection;
+use rustc::mir::{LvalueProjection, ProjectionElem};
+use rustc::infer::InferCtxt;
+use rustc::traits::{self, ObligationCause};
+use rustc::ty::{self, Ty};
+use rustc::ty::fold::TypeFoldable;
+use rustc::util::common::ErrorReported;
+use rustc_data_structures::fx::FxHashSet;
+use syntax::codemap::DUMMY_SP;
+
+use super::subtype;
+use super::LivenessResults;
+use super::ToRegionIndex;
+use super::region_infer::RegionInferenceContext;
+
+pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    regioncx: &mut RegionInferenceContext,
+    mir: &Mir<'tcx>,
+    mir_source: MirSource,
+    liveness: &LivenessResults,
+) {
+    ConstraintGeneration {
+        infcx,
+        regioncx,
+        mir,
+        liveness,
+        mir_source,
+    }.add_constraints();
+}
+
+struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    regioncx: &'cx mut RegionInferenceContext,
+    mir: &'cx Mir<'tcx>,
+    liveness: &'cx LivenessResults,
+    mir_source: MirSource,
+}
+
+impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
+    fn add_constraints(&mut self) {
+        self.add_liveness_constraints();
+        self.add_borrow_constraints();
+    }
+
+    /// 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) {
+        debug!("add_liveness_constraints()");
+        for bb in self.mir.basic_blocks().indices() {
+            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);
+                    }
+                });
+
+            self.liveness
+                .drop
+                .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_drop_live_constraint(live_local_ty, 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 add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        debug!(
+            "add_regular_live_constraint(live_ty={:?}, location={:?})",
+            live_ty,
+            location
+        );
+
+        self.infcx
+            .tcx
+            .for_each_free_region(&live_ty, |live_region| {
+                let vid = live_region.to_region_index();
+                self.regioncx.add_live_point(vid, 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.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 {
+                if let Some(ty) = outlive.as_type() {
+                    self.add_regular_live_constraint(ty, location);
+                } else if let Some(r) = outlive.as_region() {
+                    self.add_regular_live_constraint(r, location);
+                } else {
+                    bug!()
+                }
+            }
+
+            // 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.
+            let def_id = tcx.hir.local_def_id(self.mir_source.item_id());
+            let param_env = self.infcx.tcx.param_env(def_id);
+            for ty in dtorck_types {
+                // FIXME -- I think that this may disregard some region obligations
+                // or something. Do we care? -nmatsakis
+                let cause = ObligationCause::dummy();
+                match traits::fully_normalize(self.infcx, cause, 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_borrow_constraints(&mut self) {
+        self.visit_mir(self.mir);
+    }
+
+    fn add_borrow_constraint(
+        &mut self,
+        location: Location,
+        destination_lv: &Lvalue<'tcx>,
+        borrow_region: ty::Region<'tcx>,
+        _borrow_kind: BorrowKind,
+        _borrowed_lv: &Lvalue<'tcx>,
+    ) {
+        let tcx = self.infcx.tcx;
+        let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
+
+        let destination_region = match destination_ty.sty {
+            ty::TyRef(r, _) => r,
+            _ => bug!()
+        };
+
+        self.regioncx.add_outlives(borrow_region.to_region_index(),
+                                   destination_region.to_region_index(),
+                                   location.successor_within_block());
+    }
+
+    fn add_reborrow_constraint(
+        &mut self,
+        location: Location,
+        borrow_region: ty::Region<'tcx>,
+        borrowed_lv: &Lvalue<'tcx>,
+    ) {
+        if let Projection(ref proj) = *borrowed_lv {
+            let LvalueProjection { ref base, ref elem } = **proj;
+
+            if let ProjectionElem::Deref = *elem {
+                let tcx = self.infcx.tcx;
+                let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
+                let base_sty = &base_ty.sty;
+
+                if let ty::TyRef(base_region, ty::TypeAndMut{ ty: _, mutbl }) = *base_sty {
+                    match mutbl {
+                        hir::Mutability::MutImmutable => { },
+
+                        hir::Mutability::MutMutable => {
+                            self.add_reborrow_constraint(location, borrow_region, base);
+                        },
+                    }
+
+                    self.regioncx.add_outlives(base_region.to_region_index(),
+                                               borrow_region.to_region_index(),
+                                               location.successor_within_block());
+                }
+            }
+        }
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
+    fn visit_statement(&mut self,
+                       block: BasicBlock,
+                       statement: &Statement<'tcx>,
+                       location: Location) {
+
+        debug!("visit_statement(statement={:?}, location={:?})", statement, location);
+
+        // Look for a statement like:
+        //
+        //     D = & L
+        //
+        // where D is the path to which we are assigning, and
+        // L is the path that is borrowed.
+        if let StatementKind::Assign(ref destination_lv, ref rv) = statement.kind {
+            if let Rvalue::Ref(region, bk, ref borrowed_lv) = *rv {
+                self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv);
+                self.add_reborrow_constraint(location, region, borrowed_lv);
+            }
+
+            let tcx = self.infcx.tcx;
+            let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
+            let rv_ty = rv.ty(self.mir, tcx);
+
+            for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) {
+                self.regioncx.add_outlives(a, b, location.successor_within_block());
+            }
+        }
+
+        self.super_statement(block, statement, location);
+    }
+}
diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs
index 805e9c976e4..d4938dc40bf 100644
--- a/src/librustc_mir/transform/nll/mod.rs
+++ b/src/librustc_mir/transform/nll/mod.rs
@@ -8,169 +8,150 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use self::infer::InferenceContext;
-use rustc::ty::TypeFoldable;
-use rustc::ty::subst::{Kind, Substs};
-use rustc::ty::{Ty, TyCtxt, ClosureSubsts, RegionVid, RegionKind};
-use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
-use rustc::mir::visit::{MutVisitor, Lookup};
-use rustc::mir::transform::{MirPass, MirSource};
-use rustc::infer::{self as rustc_infer, InferCtxt};
-use rustc::util::nodemap::FxHashSet;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
-use syntax_pos::DUMMY_SP;
-use std::collections::HashMap;
+use rustc::ty::{self, RegionKind};
+use rustc::mir::{Location, Mir};
+use rustc::mir::transform::MirSource;
+use rustc::infer::InferCtxt;
+use rustc::util::nodemap::FxHashMap;
+use rustc_data_structures::indexed_vec::Idx;
+use std::collections::BTreeSet;
 use std::fmt;
+use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
 
 use util as mir_util;
 use self::mir_util::PassWhere;
 
-mod infer;
-
-#[allow(dead_code)]
-struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
-    lookup_map: HashMap<RegionVid, Lookup>,
-    regions: IndexVec<RegionIndex, Region>,
-    #[allow(dead_code)]
-    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+mod constraint_generation;
+mod subtype;
+
+pub(crate) mod region_infer;
+use self::region_infer::RegionInferenceContext;
+
+mod renumber;
+
+/// Computes the (non-lexical) regions from the input MIR.
+///
+/// This may result in errors being reported.
+pub fn compute_regions<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    source: MirSource,
+    mir: &mut Mir<'tcx>,
+) -> RegionInferenceContext {
+    // Replace all regions with fresh inference variables.
+    let num_region_variables = renumber::renumber_mir(infcx, mir);
+
+    // 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,
+            },
+        ),
+    };
+
+    // Create the region inference context, generate the constraints,
+    // and then solve them.
+    let mut regioncx = RegionInferenceContext::new(num_region_variables);
+    constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness);
+    let errors = regioncx.solve(infcx, &mir);
+
+    assert!(errors.is_empty(), "FIXME: report region inference failures");
+
+    // Dump MIR results into a file, if that is enabled. This let us
+    // write unit-tests.
+    dump_mir_results(infcx, liveness, source, &mir, &regioncx);
+
+    regioncx
 }
 
-impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
-    pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
-        NLLVisitor {
-            infcx,
-            lookup_map: HashMap::new(),
-            regions: IndexVec::new(),
-        }
-    }
-
-    pub fn into_results(self) -> (HashMap<RegionVid, Lookup>, IndexVec<RegionIndex, Region>) {
-        (self.lookup_map, self.regions)
-    }
-
-    fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> {
-        self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| {
-            self.regions.push(Region::default());
-            self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
-        })
-    }
-
-    fn store_region(&mut self, region: &RegionKind, lookup: Lookup) {
-        if let RegionKind::ReVar(rid) = *region {
-            self.lookup_map.entry(rid).or_insert(lookup);
-        }
-    }
-
-    fn store_ty_regions(&mut self, ty: &Ty<'tcx>, lookup: Lookup) {
-        for region in ty.regions() {
-            self.store_region(region, lookup);
-        }
-    }
-
-    fn store_kind_regions(&mut self, kind: &'tcx Kind, lookup: Lookup) {
-        if let Some(ty) = kind.as_type() {
-            self.store_ty_regions(&ty, lookup);
-        } else if let Some(region) = kind.as_region() {
-            self.store_region(region, lookup);
-        }
-    }
+struct LivenessResults {
+    regular: LivenessResult,
+    drop: LivenessResult,
 }
 
-impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
-    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, lookup: Lookup) {
-        let old_ty = *ty;
-        *ty = self.renumber_regions(&old_ty);
-        self.store_ty_regions(ty, lookup);
-    }
-
-    fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
-        *substs = self.renumber_regions(&{*substs});
-        let lookup = Lookup::Loc(location);
-        for kind in *substs {
-            self.store_kind_regions(kind, lookup);
-        }
+fn dump_mir_results<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    liveness: &LivenessResults,
+    source: MirSource,
+    mir: &Mir<'tcx>,
+    regioncx: &RegionInferenceContext,
+) {
+    if !mir_util::dump_enabled(infcx.tcx, "nll", source) {
+        return;
     }
 
-    fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
-        match *rvalue {
-            Rvalue::Ref(ref mut r, _, _) => {
-                let old_r = *r;
-                *r = self.renumber_regions(&old_r);
-                let lookup = Lookup::Loc(location);
-                self.store_region(r, lookup);
-            }
-            Rvalue::Use(..) |
-            Rvalue::Repeat(..) |
-            Rvalue::Len(..) |
-            Rvalue::Cast(..) |
-            Rvalue::BinaryOp(..) |
-            Rvalue::CheckedBinaryOp(..) |
-            Rvalue::UnaryOp(..) |
-            Rvalue::Discriminant(..) |
-            Rvalue::NullaryOp(..) |
-            Rvalue::Aggregate(..) => {
-                // These variants don't contain regions.
+    let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
+        .indices()
+        .flat_map(|bb| {
+            let mut results = vec![];
+            liveness
+                .regular
+                .simulate_block(&mir, bb, |location, local_set| {
+                    results.push((location, local_set.clone()));
+                });
+            results
+        })
+        .collect();
+
+    let drop_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
+        .indices()
+        .flat_map(|bb| {
+            let mut results = vec![];
+            liveness
+                .drop
+                .simulate_block(&mir, bb, |location, local_set| {
+                    results.push((location, local_set.clone()));
+                });
+            results
+        })
+        .collect();
+
+    mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| {
+        match pass_where {
+            // Before the CFG, dump out the values for each region variable.
+            PassWhere::BeforeCFG => for region in regioncx.regions() {
+                writeln!(
+                    out,
+                    "| {:?}: {:?}",
+                    region,
+                    regioncx.region_value(region)
+                )?;
+            },
+
+            // Before each basic block, dump out the values
+            // that are live on entry to the basic block.
+            PassWhere::BeforeBlock(bb) => {
+                let s = live_variable_set(&liveness.regular.ins[bb], &liveness.drop.ins[bb]);
+                writeln!(out, "    | Live variables on entry to {:?}: {}", bb, s)?;
             }
-        }
-        self.super_rvalue(rvalue, location);
-    }
 
-    fn visit_closure_substs(&mut self,
-                            substs: &mut ClosureSubsts<'tcx>,
-                            location: Location) {
-        *substs = self.renumber_regions(substs);
-        let lookup = Lookup::Loc(location);
-        for kind in substs.substs {
-            self.store_kind_regions(kind, lookup);
-        }
-    }
-
-    fn visit_statement(&mut self,
-                       block: BasicBlock,
-                       statement: &mut Statement<'tcx>,
-                       location: Location) {
-        if let StatementKind::EndRegion(_) = statement.kind {
-            statement.kind = StatementKind::Nop;
-        }
-        self.super_statement(block, statement, location);
-    }
-}
-
-// MIR Pass for non-lexical lifetimes
-pub struct NLL;
+            PassWhere::InCFG(location) => {
+                let s = live_variable_set(
+                    &regular_liveness_per_location[&location],
+                    &drop_liveness_per_location[&location],
+                );
+                writeln!(out, "            | Live variables at {:?}: {}", location, s)?;
+            }
 
-impl MirPass for NLL {
-    fn run_pass<'a, 'tcx>(&self,
-                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          source: MirSource,
-                          mir: &mut Mir<'tcx>) {
-        if !tcx.sess.opts.debugging_opts.nll {
-            return;
+            PassWhere::AfterCFG => {}
         }
-
-        tcx.infer_ctxt().enter(|infcx| {
-            // Clone mir so we can mutate it without disturbing the rest of the compiler
-            let mut renumbered_mir = mir.clone();
-            let mut visitor = NLLVisitor::new(&infcx);
-            visitor.visit_mir(&mut renumbered_mir);
-            mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| {
-                if let PassWhere::BeforeCFG = pass_where {
-                    for (index, value) in visitor.regions.iter_enumerated() {
-                        writeln!(out, "// R{:03}: {:?}", index.0, value)?;
-                    }
-                }
-                Ok(())
-            });
-            let (_lookup_map, regions) = visitor.into_results();
-            let mut inference_context = InferenceContext::new(regions);
-            inference_context.solve(&infcx, &renumbered_mir);
-        })
-    }
+        Ok(())
+    });
 }
 
 #[derive(Clone, Default, PartialEq, Eq)]
 pub struct Region {
-    points: FxHashSet<Location>,
+    points: BTreeSet<Location>,
 }
 
 impl fmt::Debug for Region {
@@ -189,4 +170,52 @@ impl Region {
     }
 }
 
-newtype_index!(RegionIndex);
+newtype_index!(RegionIndex {
+    DEBUG_FORMAT = "'_#{}r",
+});
+
+/// Right now, we piggy back on the `ReVar` to store our NLL inference
+/// regions. These are indexed with `RegionIndex`. This method will
+/// assert that the region is a `ReVar` and convert the internal index
+/// into a `RegionIndex`. This is reasonable because in our MIR we
+/// replace all free regions with inference variables.
+pub trait ToRegionIndex {
+    fn to_region_index(&self) -> RegionIndex;
+}
+
+impl ToRegionIndex for RegionKind {
+    fn to_region_index(&self) -> RegionIndex {
+        if let &ty::ReVar(vid) = self {
+            RegionIndex::new(vid.index as usize)
+        } else {
+            bug!("region is not an ReVar: {:?}", self)
+        }
+    }
+}
+
+fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String {
+    // sort and deduplicate:
+    let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect();
+
+    // construct a string with each local, including `(drop)` if it is
+    // only dropped, versus a regular use.
+    let mut string = String::new();
+    for local in all_locals {
+        string.push_str(&format!("{:?}", local));
+
+        if !regular.contains(&local) {
+            assert!(drops.contains(&local));
+            string.push_str(" (drop)");
+        }
+
+        string.push_str(", ");
+    }
+
+    let len = if string.is_empty() {
+        0
+    } else {
+        string.len() - 2
+    };
+
+    format!("[{}]", &string[..len])
+}
diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/region_infer.rs
index e6e00f295ca..c23d73e784a 100644
--- a/src/librustc_mir/transform/nll/infer.rs
+++ b/src/librustc_mir/transform/nll/region_infer.rs
@@ -12,13 +12,22 @@ use super::{Region, RegionIndex};
 use std::mem;
 use rustc::infer::InferCtxt;
 use rustc::mir::{Location, Mir};
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_data_structures::fx::FxHashSet;
 
-pub struct InferenceContext {
-    definitions: IndexVec<RegionIndex, VarDefinition>,
-    constraints: IndexVec<ConstraintIndex, Constraint>,
-    errors: IndexVec<InferenceErrorIndex, InferenceError>,
+pub struct RegionInferenceContext {
+    /// Contains the definition for every region variable.  Region
+    /// variables are identified by their index (`RegionIndex`). The
+    /// definition contains information about where the region came
+    /// from as well as its final inferred value.
+    definitions: IndexVec<RegionIndex, RegionDefinition>,
+
+    /// The constraints we have accumulated and used during solving.
+    constraints: Vec<Constraint>,
+
+    /// List of errors we have accumulated as we add constraints.
+    /// After solving is done, this is replaced with an empty vector.
+    errors: Vec<InferenceError>,
 }
 
 pub struct InferenceError {
@@ -26,24 +35,13 @@ pub struct InferenceError {
     pub name: (), // FIXME(nashenas88) RegionName
 }
 
-newtype_index!(InferenceErrorIndex);
-
-struct VarDefinition {
+#[derive(Default)]
+struct RegionDefinition {
     name: (), // FIXME(nashenas88) RegionName
     value: Region,
     capped: bool,
 }
 
-impl VarDefinition {
-    pub fn new(value: Region) -> Self {
-        Self {
-            name: (),
-            value,
-            capped: false,
-        }
-    }
-}
-
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub struct Constraint {
     sub: RegionIndex,
@@ -51,24 +49,43 @@ pub struct Constraint {
     point: Location,
 }
 
-newtype_index!(ConstraintIndex);
-
-impl InferenceContext {
-    pub fn new(values: IndexVec<RegionIndex, Region>) -> Self {
+impl RegionInferenceContext {
+    pub fn new(num_region_variables: usize) -> Self {
         Self {
-            definitions: values.into_iter().map(VarDefinition::new).collect(),
-            constraints: IndexVec::new(),
-            errors: IndexVec::new(),
+            definitions: (0..num_region_variables)
+                .map(|_| RegionDefinition::default())
+                .collect(),
+            constraints: Vec::new(),
+            errors: Vec::new(),
         }
     }
 
+
+    /// Returns an iterator over all the region indices.
+    pub fn regions(&self) -> impl Iterator<Item = RegionIndex> {
+        self.definitions.indices()
+    }
+
+    /// Returns the inferred value for the region `r`.
+    ///
+    /// Until `solve()` executes, this value is not particularly meaningful.
+    pub fn region_value(&self, r: RegionIndex) -> &Region {
+        &self.definitions[r].value
+    }
+
+    /// Flags a region as being "capped" -- this means that if its
+    /// value is required to grow as a result of some constraint
+    /// (e.g., `add_live_point` or `add_outlives`), that indicates an
+    /// error. This is used for the regions representing named
+    /// lifetime parameters on a function: they get initialized to
+    /// their complete value, and then "capped" so that they can no
+    /// longer grow.
     #[allow(dead_code)]
-    pub fn cap_var(&mut self, v: RegionIndex) {
+    pub(super) fn cap_var(&mut self, v: RegionIndex) {
         self.definitions[v].capped = true;
     }
 
-    #[allow(dead_code)]
-    pub fn add_live_point(&mut self, v: RegionIndex, point: Location) {
+    pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) {
         debug!("add_live_point({:?}, {:?})", v, point);
         let definition = &mut self.definitions[v];
         if definition.value.add_point(point) {
@@ -81,22 +98,17 @@ impl InferenceContext {
         }
     }
 
-    #[allow(dead_code)]
-    pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
+    pub(super) fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
         debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
         self.constraints.push(Constraint { sup, sub, point });
     }
 
-    #[allow(dead_code)]
-    pub fn region(&self, v: RegionIndex) -> &Region {
-        &self.definitions[v].value
-    }
-
-    pub fn solve<'a, 'gcx, 'tcx>(
+    /// Perform region inference.
+    pub(super) fn solve<'a, 'gcx, 'tcx>(
         &mut self,
         infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
         mir: &'a Mir<'tcx>,
-    ) -> IndexVec<InferenceErrorIndex, InferenceError>
+    ) -> Vec<InferenceError>
     where
         'gcx: 'tcx + 'a,
         'tcx: 'a,
@@ -139,13 +151,12 @@ impl InferenceContext {
             debug!("\n");
         }
 
-        mem::replace(&mut self.errors, IndexVec::new())
+        mem::replace(&mut self.errors, Vec::new())
     }
 }
 
 struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> {
-    #[allow(dead_code)]
-    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    #[allow(dead_code)] infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
     mir: &'a Mir<'tcx>,
 }
 
@@ -183,17 +194,22 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
 
             let block_data = &self.mir[p.block];
             let successor_points = if p.statement_index < block_data.statements.len() {
-                vec![Location {
-                    statement_index: p.statement_index + 1,
-                    ..p
-                }]
+                vec![
+                    Location {
+                        statement_index: p.statement_index + 1,
+                        ..p
+                    },
+                ]
             } else {
-                block_data.terminator()
+                block_data
+                    .terminator()
                     .successors()
                     .iter()
-                    .map(|&basic_block| Location {
-                        statement_index: 0,
-                        block: basic_block,
+                    .map(|&basic_block| {
+                        Location {
+                            statement_index: 0,
+                            block: basic_block,
+                        }
                     })
                     .collect::<Vec<_>>()
             };
diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs
new file mode 100644
index 00000000000..589179c2066
--- /dev/null
+++ b/src/librustc_mir/transform/nll/renumber.rs
@@ -0,0 +1,132 @@
+// 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 rustc::ty::TypeFoldable;
+use rustc::ty::subst::{Kind, Substs};
+use rustc::ty::{Ty, ClosureSubsts, RegionVid, RegionKind};
+use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
+use rustc::mir::visit::{MutVisitor, Lookup};
+use rustc::infer::{self as rustc_infer, InferCtxt};
+use syntax_pos::DUMMY_SP;
+use std::collections::HashMap;
+
+/// 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>,
+                                    mir: &mut Mir<'tcx>)
+                                    -> usize
+{
+    let mut visitor = NLLVisitor::new(infcx);
+    visitor.visit_mir(mir);
+    visitor.num_region_variables
+}
+
+struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
+    lookup_map: HashMap<RegionVid, Lookup>,
+    num_region_variables: usize,
+    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+}
+
+impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
+    pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
+        NLLVisitor {
+            infcx,
+            lookup_map: HashMap::new(),
+            num_region_variables: 0
+        }
+    }
+
+    fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> {
+        self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| {
+            self.num_region_variables += 1;
+            self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
+        })
+    }
+
+    fn store_region(&mut self, region: &RegionKind, lookup: Lookup) {
+        if let RegionKind::ReVar(rid) = *region {
+            self.lookup_map.entry(rid).or_insert(lookup);
+        }
+    }
+
+    fn store_ty_regions(&mut self, ty: &Ty<'tcx>, lookup: Lookup) {
+        for region in ty.regions() {
+            self.store_region(region, lookup);
+        }
+    }
+
+    fn store_kind_regions(&mut self, kind: &'tcx Kind, lookup: Lookup) {
+        if let Some(ty) = kind.as_type() {
+            self.store_ty_regions(&ty, lookup);
+        } else if let Some(region) = kind.as_region() {
+            self.store_region(region, lookup);
+        }
+    }
+}
+
+impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
+    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, lookup: Lookup) {
+        let old_ty = *ty;
+        *ty = self.renumber_regions(&old_ty);
+        self.store_ty_regions(ty, lookup);
+    }
+
+    fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
+        *substs = self.renumber_regions(&{*substs});
+        let lookup = Lookup::Loc(location);
+        for kind in *substs {
+            self.store_kind_regions(kind, lookup);
+        }
+    }
+
+    fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
+        match *rvalue {
+            Rvalue::Ref(ref mut r, _, _) => {
+                let old_r = *r;
+                *r = self.renumber_regions(&old_r);
+                let lookup = Lookup::Loc(location);
+                self.store_region(r, lookup);
+            }
+            Rvalue::Use(..) |
+            Rvalue::Repeat(..) |
+            Rvalue::Len(..) |
+            Rvalue::Cast(..) |
+            Rvalue::BinaryOp(..) |
+            Rvalue::CheckedBinaryOp(..) |
+            Rvalue::UnaryOp(..) |
+            Rvalue::Discriminant(..) |
+            Rvalue::NullaryOp(..) |
+            Rvalue::Aggregate(..) => {
+                // These variants don't contain regions.
+            }
+        }
+        self.super_rvalue(rvalue, location);
+    }
+
+    fn visit_closure_substs(&mut self,
+                            substs: &mut ClosureSubsts<'tcx>,
+                            location: Location) {
+        *substs = self.renumber_regions(substs);
+        let lookup = Lookup::Loc(location);
+        for kind in substs.substs {
+            self.store_kind_regions(kind, lookup);
+        }
+    }
+
+    fn visit_statement(&mut self,
+                       block: BasicBlock,
+                       statement: &mut Statement<'tcx>,
+                       location: Location) {
+        if let StatementKind::EndRegion(_) = statement.kind {
+            statement.kind = StatementKind::Nop;
+        }
+        self.super_statement(block, statement, location);
+    }
+}
diff --git a/src/librustc_mir/transform/nll/subtype.rs b/src/librustc_mir/transform/nll/subtype.rs
new file mode 100644
index 00000000000..953fc0eb733
--- /dev/null
+++ b/src/librustc_mir/transform/nll/subtype.rs
@@ -0,0 +1,99 @@
+// 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 super::RegionIndex;
+use transform::nll::ToRegionIndex;
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation};
+
+pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                      a: Ty<'tcx>,
+                      b: Ty<'tcx>)
+                      -> Vec<(RegionIndex, RegionIndex)>
+{
+    let mut subtype = Subtype::new(tcx);
+    match subtype.relate(&a, &b) {
+        Ok(_) => subtype.outlives_pairs,
+
+        Err(_) => bug!("Fail to relate a = {:?} and b = {:?}", a, b)
+    }
+}
+
+struct Subtype<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    outlives_pairs: Vec<(RegionIndex, RegionIndex)>,
+    ambient_variance: ty::Variance,
+}
+
+impl<'a, 'gcx, 'tcx> Subtype<'a, 'gcx, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Subtype<'a, 'gcx, 'tcx> {
+        Subtype {
+            tcx,
+            outlives_pairs: vec![],
+            ambient_variance: ty::Covariant,
+        }
+    }
+}
+
+impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Subtype<'a, 'gcx, 'tcx> {
+    fn tag(&self) -> &'static str { "Subtype" }
+    fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.tcx }
+    fn a_is_expected(&self) -> bool { true } // irrelevant
+
+    fn relate_with_variance<T: Relate<'tcx>>(&mut self,
+                                             variance: ty::Variance,
+                                             a: &T,
+                                             b: &T)
+                                             -> RelateResult<'tcx, T>
+    {
+        let old_ambient_variance = self.ambient_variance;
+        self.ambient_variance = self.ambient_variance.xform(variance);
+
+        let result = self.relate(a, b);
+        self.ambient_variance = old_ambient_variance;
+        result
+    }
+
+    fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        relate::super_relate_tys(self, t, t2)
+    }
+
+    fn regions(&mut self, r_a: ty::Region<'tcx>, r_b: ty::Region<'tcx>)
+               -> RelateResult<'tcx, ty::Region<'tcx>> {
+        let a = r_a.to_region_index();
+        let b = r_b.to_region_index();
+
+        match self.ambient_variance {
+            ty::Covariant => {
+                self.outlives_pairs.push((b, a));
+            },
+
+            ty::Invariant => {
+                self.outlives_pairs.push((a, b));
+                self.outlives_pairs.push((b, a));
+            },
+
+            ty::Contravariant => {
+                self.outlives_pairs.push((a, b));
+            },
+
+            ty::Bivariant => {},
+        }
+
+        Ok(r_a)
+    }
+
+    fn binders<T>(&mut self, _a: &ty::Binder<T>, _b: &ty::Binder<T>)
+                  -> RelateResult<'tcx, ty::Binder<T>>
+        where T: Relate<'tcx>
+    {
+        unimplemented!();
+    }
+}
diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs
index 5451da2148e..a4a7699abda 100644
--- a/src/librustc_mir/util/borrowck_errors.rs
+++ b/src/librustc_mir/util/borrowck_errors.rs
@@ -93,7 +93,7 @@ pub trait BorrowckErrors {
                                       opt_via: &str,
                                       old_loan_span: Span,
                                       old_opt_via: &str,
-                                      old_load_end_span:Span,
+                                      old_load_end_span: Option<Span>,
                                       o: Origin)
                                       -> DiagnosticBuilder
     {
@@ -106,13 +106,17 @@ pub trait BorrowckErrors {
             err.span_label(new_loan_span,
                            format!("mutable borrow starts here in previous \
                                     iteration of loop{}", opt_via));
-            err.span_label(old_load_end_span, "mutable borrow ends here");
+            if let Some(old_load_end_span) = old_load_end_span {
+                err.span_label(old_load_end_span, "mutable borrow ends here");
+            }
         } else {
             err.span_label(old_loan_span,
                            format!("first mutable borrow occurs here{}", old_opt_via));
             err.span_label(new_loan_span,
                            format!("second mutable borrow occurs here{}", opt_via));
-            err.span_label(old_load_end_span, "first borrow ends here");
+            if let Some(old_load_end_span) = old_load_end_span {
+                err.span_label(old_load_end_span, "first borrow ends here");
+            }
         }
         err
     }
@@ -121,7 +125,7 @@ pub trait BorrowckErrors {
                                               new_loan_span: Span,
                                               desc: &str,
                                               old_loan_span: Span,
-                                              old_load_end_span: Span,
+                                              old_load_end_span: Option<Span>,
                                               o: Origin)
                                               -> DiagnosticBuilder
     {
@@ -134,9 +138,11 @@ pub trait BorrowckErrors {
         err.span_label(
             new_loan_span,
             "second closure is constructed here");
-        err.span_label(
-            old_load_end_span,
-            "borrow from first closure ends here");
+        if let Some(old_load_end_span) = old_load_end_span {
+            err.span_label(
+                old_load_end_span,
+                "borrow from first closure ends here");
+        }
         err
     }
 
@@ -147,7 +153,7 @@ pub trait BorrowckErrors {
                                              old_loan_span: Span,
                                              noun_old: &str,
                                              old_opt_via: &str,
-                                             previous_end_span: Span,
+                                             previous_end_span: Option<Span>,
                                              o: Origin)
                                              -> DiagnosticBuilder
     {
@@ -158,7 +164,9 @@ pub trait BorrowckErrors {
                        format!("closure construction occurs here{}", opt_via));
         err.span_label(old_loan_span,
                        format!("borrow occurs here{}", old_opt_via));
-        err.span_label(previous_end_span, "borrow ends here");
+        if let Some(previous_end_span) = previous_end_span {
+            err.span_label(previous_end_span, "borrow ends here");
+        }
         err
     }
 
@@ -169,7 +177,7 @@ pub trait BorrowckErrors {
                                                  kind_new: &str,
                                                  old_loan_span: Span,
                                                  old_opt_via: &str,
-                                                 previous_end_span: Span,
+                                                 previous_end_span: Option<Span>,
                                                  o: Origin)
                                                  -> DiagnosticBuilder
     {
@@ -181,7 +189,9 @@ pub trait BorrowckErrors {
                        format!("borrow occurs here{}", opt_via));
         err.span_label(old_loan_span,
                        format!("closure construction occurs here{}", old_opt_via));
-        err.span_label(previous_end_span, "borrow from closure ends here");
+        if let Some(previous_end_span) = previous_end_span {
+            err.span_label(previous_end_span, "borrow from closure ends here");
+        }
         err
     }
 
@@ -194,7 +204,7 @@ pub trait BorrowckErrors {
                                         noun_old: &str,
                                         kind_old: &str,
                                         msg_old: &str,
-                                        old_load_end_span: Span,
+                                        old_load_end_span: Option<Span>,
                                         o: Origin)
                                         -> DiagnosticBuilder
     {
@@ -203,7 +213,9 @@ pub trait BorrowckErrors {
                          desc_new, msg_new, kind_new, noun_old, kind_old, msg_old, OGN=o);
         err.span_label(span, format!("{} borrow occurs here{}", kind_new, msg_new));
         err.span_label(old_span, format!("{} borrow occurs here{}", kind_old, msg_old));
-        err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old));
+        if let Some(old_load_end_span) = old_load_end_span {
+            err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old));
+        }
         err
     }
 
@@ -429,7 +441,7 @@ pub trait BorrowckErrors {
     }
 }
 
-impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> {
+impl<'b, 'gcx, 'tcx> BorrowckErrors for TyCtxt<'b, 'gcx, 'tcx> {
     fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
                                                          sp: S,
                                                          msg: &str,
diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs
index cec8067530b..a38d317b823 100644
--- a/src/librustc_mir/util/graphviz.rs
+++ b/src/librustc_mir/util/graphviz.rs
@@ -21,10 +21,10 @@ use rustc_data_structures::indexed_vec::Idx;
 use super::pretty::dump_mir_def_ids;
 
 /// Write a graphviz DOT graph of a list of MIRs.
-pub fn write_mir_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                       single: Option<DefId>,
-                                       w: &mut W)
-                                       -> io::Result<()>
+pub fn write_mir_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>,
+                                   single: Option<DefId>,
+                                   w: &mut W)
+                                   -> io::Result<()>
     where W: Write
 {
     for def_id in dump_mir_def_ids(tcx, single) {
@@ -36,10 +36,10 @@ pub fn write_mir_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 }
 
 /// Write a graphviz DOT graph of the MIR.
-pub fn write_mir_fn_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                        nodeid: NodeId,
-                                        mir: &Mir,
-                                        w: &mut W) -> io::Result<()>
+pub fn write_mir_fn_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>,
+                                      nodeid: NodeId,
+                                      mir: &Mir,
+                                      w: &mut W) -> io::Result<()>
     where W: Write
 {
     writeln!(w, "digraph Mir_{} {{", nodeid)?;
@@ -137,11 +137,11 @@ fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result
 /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
 /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
 /// all the variables and temporaries.
-fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                         nid: NodeId,
-                                         mir: &Mir,
-                                         w: &mut W)
-                                         -> io::Result<()> {
+fn write_graph_label<'a, 'gcx, 'tcx, W: Write>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                               nid: NodeId,
+                                               mir: &Mir,
+                                               w: &mut W)
+                                               -> io::Result<()> {
     write!(w, "    label=<fn {}(", dot::escape_html(&tcx.node_path_str(nid)))?;
 
     // fn argument types.
diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs
index 1424c063d73..7658e49ea5e 100644
--- a/src/librustc_mir/util/liveness.rs
+++ b/src/librustc_mir/util/liveness.rs
@@ -35,48 +35,235 @@
 
 use rustc::mir::*;
 use rustc::mir::visit::{LvalueContext, Visitor};
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc_data_structures::indexed_set::IdxSetBuf;
-use util::pretty::{write_basic_block, dump_enabled, write_mir_intro};
+use util::pretty::{dump_enabled, write_basic_block, write_mir_intro};
 use rustc::mir::transform::MirSource;
 use rustc::ty::item_path;
-use std::path::{PathBuf, Path};
+use std::path::{Path, PathBuf};
 use std::fs;
 use rustc::ty::TyCtxt;
 use std::io::{self, Write};
 
 pub type LocalSet = IdxSetBuf<Local>;
 
+/// This gives the result of the liveness analysis at the boundary of
+/// basic blocks. You can use `simulate_block` to obtain the
+/// intra-block results.
+pub struct LivenessResult {
+    /// Liveness mode in use when these results were computed.
+    pub mode: LivenessMode,
+
+    /// Live variables on entry to each basic block.
+    pub ins: IndexVec<BasicBlock, LocalSet>,
+
+    /// Live variables on exit to each basic block. This is equal to
+    /// the union of the `ins` for each successor.
+    pub outs: IndexVec<BasicBlock, LocalSet>,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct LivenessMode {
+    /// If true, then we will consider "regular uses" of a variable to be live.
+    /// For example, if the user writes `foo(x)`, then this is a regular use of
+    /// the variable `x`.
+    pub include_regular_use: bool,
+
+    /// If true, then we will consider (implicit) drops of a variable
+    /// to be live.  For example, if the user writes `{ let x =
+    /// vec![...]; .. }`, then the drop at the end of the block is an
+    /// implicit drop.
+    ///
+    /// NB. Despite its name, a call like `::std::mem::drop(x)` is
+    /// **not** considered a drop for this purposes, but rather a
+    /// regular use.
+    pub include_drops: bool,
+}
+
+/// 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?).
+pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> LivenessResult {
+    let locals = mir.local_decls.len();
+    let def_use: IndexVec<_, _> = mir.basic_blocks()
+        .iter()
+        .map(|b| block(mode, b, locals))
+        .collect();
+
+    let mut ins: IndexVec<_, _> = mir.basic_blocks()
+        .indices()
+        .map(|_| LocalSet::new_empty(locals))
+        .collect();
+    let mut outs = ins.clone();
+
+    let mut changed = true;
+    let mut bits = LocalSet::new_empty(locals);
+    while changed {
+        changed = false;
+
+        for b in mir.basic_blocks().indices().rev() {
+            // outs[b] = ∪ {ins of successors}
+            bits.clear();
+            for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() {
+                bits.union(&ins[successor]);
+            }
+            outs[b].clone_from(&bits);
+
+            // bits = use ∪ (bits - def)
+            def_use[b].apply(&mut bits);
+
+            // update bits on entry and flag if they have changed
+            if ins[b] != bits {
+                ins[b].clone_from(&bits);
+                changed = true;
+            }
+        }
+    }
+
+    LivenessResult { mode, ins, outs }
+}
+
+impl LivenessResult {
+    /// Walks backwards through the statements/terminator in the given
+    /// basic block `block`.  At each point within `block`, invokes
+    /// the callback `op` with the current location and the set of
+    /// variables that are live on entry to that location.
+    pub fn simulate_block<'tcx, OP>(&self, mir: &Mir<'tcx>, block: BasicBlock, mut callback: OP)
+    where
+        OP: FnMut(Location, &LocalSet),
+    {
+        let data = &mir[block];
+
+        // Get a copy of the bits on exit from the block.
+        let mut bits = self.outs[block].clone();
+
+        // Start with the maximal statement index -- i.e., right before
+        // the terminator executes.
+        let mut statement_index = data.statements.len();
+
+        // Compute liveness right before terminator and invoke callback.
+        let terminator_location = Location {
+            block,
+            statement_index,
+        };
+        let terminator_defs_uses = self.defs_uses(mir, terminator_location, &data.terminator);
+        terminator_defs_uses.apply(&mut bits);
+        callback(terminator_location, &bits);
+
+        // Compute liveness before each statement (in rev order) and invoke callback.
+        for statement in data.statements.iter().rev() {
+            statement_index -= 1;
+            let statement_location = Location {
+                block,
+                statement_index,
+            };
+            let statement_defs_uses = self.defs_uses(mir, statement_location, statement);
+            statement_defs_uses.apply(&mut bits);
+            callback(statement_location, &bits);
+        }
+
+        assert_eq!(bits, self.ins[block]);
+    }
+
+    fn defs_uses<'tcx, V>(&self, mir: &Mir<'tcx>, location: Location, thing: &V) -> DefsUses
+    where
+        V: MirVisitable<'tcx>,
+    {
+        let locals = mir.local_decls.len();
+        let mut visitor = DefsUsesVisitor {
+            mode: self.mode,
+            defs_uses: DefsUses {
+                defs: LocalSet::new_empty(locals),
+                uses: LocalSet::new_empty(locals),
+            },
+        };
+
+        // Visit the various parts of the basic block in reverse. If we go
+        // forward, the logic in `add_def` and `add_use` would be wrong.
+        thing.apply(location, &mut visitor);
+
+        visitor.defs_uses
+    }
+}
+
+struct DefsUsesVisitor {
+    mode: LivenessMode,
+    defs_uses: DefsUses,
+}
+
 #[derive(Eq, PartialEq, Clone)]
-struct BlockInfo {
+struct DefsUses {
     defs: LocalSet,
     uses: LocalSet,
 }
 
-struct BlockInfoVisitor {
-    pre_defs: LocalSet,
-    defs: LocalSet,
-    uses: LocalSet,
+impl DefsUses {
+    fn apply(&self, bits: &mut LocalSet) -> bool {
+        bits.subtract(&self.defs) | bits.union(&self.uses)
+    }
+
+    fn add_def(&mut self, index: Local) {
+        // If it was used already in the block, remove that use
+        // now that we found a definition.
+        //
+        // Example:
+        //
+        //     // Defs = {X}, Uses = {}
+        //     X = 5
+        //     // Defs = {}, Uses = {X}
+        //     use(X)
+        self.uses.remove(&index);
+        self.defs.add(&index);
+    }
+
+    fn add_use(&mut self, index: Local) {
+        // Inverse of above.
+        //
+        // Example:
+        //
+        //     // Defs = {}, Uses = {X}
+        //     use(X)
+        //     // Defs = {X}, Uses = {}
+        //     X = 5
+        //     // Defs = {}, Uses = {X}
+        //     use(X)
+        self.defs.remove(&index);
+        self.uses.add(&index);
+    }
 }
 
-impl<'tcx> Visitor<'tcx> for BlockInfoVisitor {
-    fn visit_local(&mut self,
-                   &local: &Local,
-                   context: LvalueContext<'tcx>,
-                   _: Location) {
+impl<'tcx> Visitor<'tcx> for DefsUsesVisitor {
+    fn visit_local(&mut self, &local: &Local, context: LvalueContext<'tcx>, _: Location) {
         match context {
+            ///////////////////////////////////////////////////////////////////////////
+            // DEFS
+
             LvalueContext::Store |
 
-            // We let Call defined the result in both the success and unwind cases.
-            // This may not be right.
+            // We let Call define the result in both the success and
+            // unwind cases. This is not really correct, however it
+            // does not seem to be observable due to the way that we
+            // generate MIR. See the test case
+            // `mir-opt/nll/liveness-call-subtlety.rs`. To do things
+            // properly, we would apply the def in call only to the
+            // input from the success path and not the unwind
+            // path. -nmatsakis
             LvalueContext::Call |
 
             // Storage live and storage dead aren't proper defines, but we can ignore
             // values that come before them.
             LvalueContext::StorageLive |
             LvalueContext::StorageDead => {
-                self.defs.add(&local);
+                self.defs_uses.add_def(local);
             }
+
+            ///////////////////////////////////////////////////////////////////////////
+            // REGULAR USES
+            //
+            // These are uses that occur *outside* of a drop. For the
+            // purposes of NLL, these are special in that **all** the
+            // lifetimes appearing in the variable must be live for each regular use.
+
             LvalueContext::Projection(..) |
 
             // Borrows only consider their local used at the point of the borrow.
@@ -87,124 +274,108 @@ impl<'tcx> Visitor<'tcx> for BlockInfoVisitor {
 
             LvalueContext::Inspect |
             LvalueContext::Consume |
-            LvalueContext::Validate |
+            LvalueContext::Validate => {
+                if self.mode.include_regular_use {
+                    self.defs_uses.add_use(local);
+                }
+            }
+
+            ///////////////////////////////////////////////////////////////////////////
+            // DROP USES
+            //
+            // These are uses that occur in a DROP (a MIR drop, not a
+            // call to `std::mem::drop()`). For the purposes of NLL,
+            // uses in drop are special because `#[may_dangle]`
+            // attributes can affect whether lifetimes must be live.
 
-            // We consider drops to always be uses of locals.
-            // Drop eloboration should be run before this analysis otherwise
-            // the results might be too pessimistic.
             LvalueContext::Drop => {
-                // Ignore uses which are already defined in this block
-                if !self.pre_defs.contains(&local) {
-                    self.uses.add(&local);
+                if self.mode.include_drops {
+                    self.defs_uses.add_use(local);
                 }
             }
         }
     }
 }
 
-fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> BlockInfo {
-    let mut visitor = BlockInfoVisitor {
-        pre_defs: LocalSet::new_empty(locals),
-        defs: LocalSet::new_empty(locals),
-        uses: LocalSet::new_empty(locals),
+fn block<'tcx>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses {
+    let mut visitor = DefsUsesVisitor {
+        mode,
+        defs_uses: DefsUses {
+            defs: LocalSet::new_empty(locals),
+            uses: LocalSet::new_empty(locals),
+        },
     };
 
-    let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 };
+    let dummy_location = Location {
+        block: BasicBlock::new(0),
+        statement_index: 0,
+    };
 
-    for statement in &b.statements {
+    // Visit the various parts of the basic block in reverse. If we go
+    // forward, the logic in `add_def` and `add_use` would be wrong.
+    visitor.visit_terminator(BasicBlock::new(0), b.terminator(), dummy_location);
+    for statement in b.statements.iter().rev() {
         visitor.visit_statement(BasicBlock::new(0), statement, dummy_location);
-        visitor.pre_defs.union(&visitor.defs);
     }
-    visitor.visit_terminator(BasicBlock::new(0), b.terminator(), dummy_location);
 
-    BlockInfo {
-        defs: visitor.defs,
-        uses: visitor.uses,
-    }
+    visitor.defs_uses
 }
 
-// This gives the result of the liveness analysis at the boundary of basic blocks
-pub struct LivenessResult {
-    pub ins: IndexVec<BasicBlock, LocalSet>,
-    pub outs: IndexVec<BasicBlock, LocalSet>,
+trait MirVisitable<'tcx> {
+    fn apply<V>(&self, location: Location, visitor: &mut V)
+    where
+        V: Visitor<'tcx>;
 }
 
-pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
-    let locals = mir.local_decls.len();
-    let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| {
-        block(b, locals)
-    }).collect();
-
-    let copy = |from: &IndexVec<BasicBlock, LocalSet>, to: &mut IndexVec<BasicBlock, LocalSet>| {
-        for (i, set) in to.iter_enumerated_mut() {
-            set.clone_from(&from[i]);
-        }
-    };
-
-    let mut ins: IndexVec<_, _> = mir.basic_blocks()
-        .indices()
-        .map(|_| LocalSet::new_empty(locals)).collect();
-    let mut outs = ins.clone();
-
-    let mut ins_ = ins.clone();
-    let mut outs_ = outs.clone();
-
-    loop {
-        copy(&ins, &mut ins_);
-        copy(&outs, &mut outs_);
-
-        for b in mir.basic_blocks().indices().rev() {
-            // out = ∪ {ins of successors}
-            outs[b].clear();
-            for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() {
-                outs[b].union(&ins[successor]);
-            }
-
-            // in = use ∪ (out - def)
-            ins[b].clone_from(&outs[b]);
-            ins[b].subtract(&def_use[b].defs);
-            ins[b].union(&def_use[b].uses);
-        }
-
-        if ins_ == ins && outs_ == outs {
-            break;
-        }
+impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> {
+    fn apply<V>(&self, location: Location, visitor: &mut V)
+    where
+        V: Visitor<'tcx>,
+    {
+        visitor.visit_statement(location.block, self, location)
     }
+}
 
-    LivenessResult {
-        ins,
-        outs,
+impl<'tcx> MirVisitable<'tcx> for Option<Terminator<'tcx>> {
+    fn apply<V>(&self, location: Location, visitor: &mut V)
+    where
+        V: Visitor<'tcx>,
+    {
+        visitor.visit_terminator(location.block, self.as_ref().unwrap(), location)
     }
 }
 
-pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          pass_name: &str,
-                          source: MirSource,
-                          mir: &Mir<'tcx>,
-                          result: &LivenessResult) {
+pub fn dump_mir<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    pass_name: &str,
+    source: MirSource,
+    mir: &Mir<'tcx>,
+    result: &LivenessResult,
+) {
     if !dump_enabled(tcx, pass_name, source) {
         return;
     }
-    let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
+    let node_path = item_path::with_forced_impl_filename_line(|| {
+        // see notes on #41697 below
         tcx.item_path_str(tcx.hir.local_def_id(source.item_id()))
     });
-    dump_matched_mir_node(tcx, pass_name, &node_path,
-                          source, mir, result);
+    dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, result);
 }
 
-fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                   pass_name: &str,
-                                   node_path: &str,
-                                   source: MirSource,
-                                   mir: &Mir<'tcx>,
-                                   result: &LivenessResult) {
+fn dump_matched_mir_node<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    pass_name: &str,
+    node_path: &str,
+    source: MirSource,
+    mir: &Mir<'tcx>,
+    result: &LivenessResult,
+) {
     let mut file_path = PathBuf::new();
     if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
         let p = Path::new(file_dir);
         file_path.push(p);
     };
-    let file_name = format!("rustc.node{}{}-liveness.mir",
-                            source.item_id(), pass_name);
+    let file_name = format!("rustc.node{}{}-liveness.mir", source.item_id(), pass_name);
     file_path.push(&file_name);
     let _ = fs::File::create(&file_path).and_then(|mut file| {
         writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
@@ -216,16 +387,18 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     });
 }
 
-pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                              src: MirSource,
-                              mir: &Mir<'tcx>,
-                              w: &mut Write,
-                              result: &LivenessResult)
-                              -> io::Result<()> {
+pub fn write_mir_fn<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    src: MirSource,
+    mir: &Mir<'tcx>,
+    w: &mut Write,
+    result: &LivenessResult,
+) -> io::Result<()> {
     write_mir_intro(tcx, src, mir, w)?;
     for block in mir.basic_blocks().indices() {
         let print = |w: &mut Write, prefix, result: &IndexVec<BasicBlock, LocalSet>| {
-            let live: Vec<String> = mir.local_decls.indices()
+            let live: Vec<String> = mir.local_decls
+                .indices()
                 .filter(|i| result[block].contains(i))
                 .map(|i| format!("{:?}", i))
                 .collect();
@@ -242,4 +415,3 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     writeln!(w, "}}")?;
     Ok(())
 }
-
diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs
index 9f61feabe41..1d924175b21 100644
--- a/src/librustc_mir/util/pretty.rs
+++ b/src/librustc_mir/util/pretty.rs
@@ -56,13 +56,13 @@ pub enum PassWhere {
 /// - `substring1&substring2,...` -- `&`-separated list of substrings
 ///   that can appear in the pass-name or the `item_path_str` for the given
 ///   node-id. If any one of the substrings match, the data is dumped out.
-pub fn dump_mir<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                             pass_num: Option<(MirSuite, MirPassIndex)>,
-                             pass_name: &str,
-                             disambiguator: &Display,
-                             source: MirSource,
-                             mir: &Mir<'tcx>,
-                             extra_data: F)
+pub fn dump_mir<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                   pass_num: Option<(MirSuite, MirPassIndex)>,
+                                   pass_name: &str,
+                                   disambiguator: &Display,
+                                   source: MirSource,
+                                   mir: &Mir<'tcx>,
+                                   extra_data: F)
 where
     F: FnMut(PassWhere, &mut Write) -> io::Result<()>
 {
@@ -77,10 +77,10 @@ where
                           disambiguator, source, mir, extra_data);
 }
 
-pub fn dump_enabled<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                              pass_name: &str,
-                              source: MirSource)
-                              -> bool {
+pub fn dump_enabled<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                    pass_name: &str,
+                                    source: MirSource)
+                                    -> bool {
     let filters = match tcx.sess.opts.debugging_opts.dump_mir {
         None => return false,
         Some(ref filters) => filters,
@@ -101,14 +101,14 @@ pub fn dump_enabled<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 // `item_path_str()` would otherwise trigger `type_of`, and this can
 // run while we are already attempting to evaluate `type_of`.
 
-fn dump_matched_mir_node<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                      pass_num: Option<(MirSuite, MirPassIndex)>,
-                                      pass_name: &str,
-                                      node_path: &str,
-                                      disambiguator: &Display,
-                                      source: MirSource,
-                                      mir: &Mir<'tcx>,
-                                      mut extra_data: F)
+fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                            pass_num: Option<(MirSuite, MirPassIndex)>,
+                                            pass_name: &str,
+                                            node_path: &str,
+                                            disambiguator: &Display,
+                                            source: MirSource,
+                                            mir: &Mir<'tcx>,
+                                            mut extra_data: F)
 where
     F: FnMut(PassWhere, &mut Write) -> io::Result<()>
 {
@@ -161,10 +161,10 @@ where
 }
 
 /// Write out a human-readable textual representation for the given MIR.
-pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                  single: Option<DefId>,
-                                  w: &mut Write)
-                                  -> io::Result<()>
+pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                        single: Option<DefId>,
+                                        w: &mut Write)
+                                        -> io::Result<()>
 {
     writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
     writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
@@ -192,12 +192,12 @@ pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     Ok(())
 }
 
-pub fn write_mir_fn<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                 src: MirSource,
-                                 mir: &Mir<'tcx>,
-                                 extra_data: &mut F,
-                                 w: &mut Write)
-                                 -> io::Result<()>
+pub fn write_mir_fn<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                       src: MirSource,
+                                       mir: &Mir<'tcx>,
+                                       extra_data: &mut F,
+                                       w: &mut Write)
+                                       -> io::Result<()>
 where
     F: FnMut(PassWhere, &mut Write) -> io::Result<()>
 {
@@ -321,11 +321,11 @@ fn write_scope_tree(tcx: TyCtxt,
 
 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
 /// local variables (both user-defined bindings and compiler temporaries).
-pub fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                             src: MirSource,
-                             mir: &Mir,
-                             w: &mut Write)
-                             -> io::Result<()> {
+pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                       src: MirSource,
+                                       mir: &Mir,
+                                       w: &mut Write)
+                                       -> io::Result<()> {
     write_mir_sig(tcx, src, mir, w)?;
     writeln!(w, " {{")?;
 
diff --git a/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs b/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs
new file mode 100644
index 00000000000..c02977f22ea
--- /dev/null
+++ b/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs
@@ -0,0 +1,50 @@
+// Copyright 2012 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:-Zborrowck-mir -Znll
+
+#![allow(warnings)]
+#![feature(rustc_attrs)]
+
+
+fn main() {
+}
+
+fn nll_fail() {
+    let mut data = ('a', 'b', 'c');
+    let c = &mut data.0;
+    capitalize(c);
+    data.0 = 'e';
+    //~^ ERROR (Ast) [E0506]
+    //~| ERROR (Mir) [E0506]
+    data.0 = 'f';
+    //~^ ERROR (Ast) [E0506]
+    //~| ERROR (Mir) [E0506]
+    data.0 = 'g';
+    //~^ ERROR (Ast) [E0506]
+    //~| ERROR (Mir) [E0506]
+    capitalize(c);
+}
+
+fn nll_ok() {
+    let mut data = ('a', 'b', 'c');
+    let c = &mut data.0;
+    capitalize(c);
+    data.0 = 'e';
+    //~^ ERROR (Ast) [E0506]
+    data.0 = 'f';
+    //~^ ERROR (Ast) [E0506]
+    data.0 = 'g';
+    //~^ ERROR (Ast) [E0506]
+}
+
+fn capitalize(_: &mut char) {
+}
diff --git a/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs b/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs
new file mode 100644
index 00000000000..5e3a003b54e
--- /dev/null
+++ b/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs
@@ -0,0 +1,49 @@
+// Copyright 2012 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:-Zborrowck-mir -Znll
+
+#![allow(warnings)]
+#![feature(rustc_attrs)]
+
+fn main() {
+}
+
+fn nll_fail() {
+    let mut data = vec!['a', 'b', 'c'];
+    let slice = &mut data;
+    capitalize(slice);
+    data.push('d');
+    //~^ ERROR (Ast) [E0499]
+    //~| ERROR (Mir) [E0499]
+    data.push('e');
+    //~^ ERROR (Ast) [E0499]
+    //~| ERROR (Mir) [E0499]
+    data.push('f');
+    //~^ ERROR (Ast) [E0499]
+    //~| ERROR (Mir) [E0499]
+    capitalize(slice);
+}
+
+fn nll_ok() {
+    let mut data = vec!['a', 'b', 'c'];
+    let slice = &mut data;
+    capitalize(slice);
+    data.push('d');
+    //~^ ERROR (Ast) [E0499]
+    data.push('e');
+    //~^ ERROR (Ast) [E0499]
+    data.push('f');
+    //~^ ERROR (Ast) [E0499]
+}
+
+fn capitalize(_: &mut [char]) {
+}
diff --git a/src/test/compile-fail/nll/region-ends-after-if-condition.rs b/src/test/compile-fail/nll/region-ends-after-if-condition.rs
new file mode 100644
index 00000000000..bec56982c57
--- /dev/null
+++ b/src/test/compile-fail/nll/region-ends-after-if-condition.rs
@@ -0,0 +1,46 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic test for liveness constraints: the region (`R1`) that appears
+// in the type of `p` includes the points after `&v[0]` up to (but not
+// including) the call to `use_x`. The `else` branch is not included.
+
+// compile-flags:-Zborrowck-mir -Znll
+
+#![allow(warnings)]
+#![feature(rustc_attrs)]
+
+struct MyStruct {
+    field: String
+}
+
+fn foo1() {
+    let mut my_struct = MyStruct { field: format!("Hello") };
+
+    let value = &my_struct.field;
+    if value.is_empty() {
+        my_struct.field.push_str("Hello, world!");
+        //~^ ERROR (Ast) [E0502]
+    }
+}
+
+fn foo2() {
+    let mut my_struct = MyStruct { field: format!("Hello") };
+
+    let value = &my_struct.field;
+    if value.is_empty() {
+        my_struct.field.push_str("Hello, world!");
+        //~^ ERROR (Ast) [E0502]
+        //~| ERROR (Mir) [E0502]
+    }
+    drop(value);
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/nll/return_from_loop.rs b/src/test/compile-fail/nll/return_from_loop.rs
new file mode 100644
index 00000000000..6b287fd2272
--- /dev/null
+++ b/src/test/compile-fail/nll/return_from_loop.rs
@@ -0,0 +1,49 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic test for liveness constraints: the region (`R1`) that appears
+// in the type of `p` includes the points after `&v[0]` up to (but not
+// including) the call to `use_x`. The `else` branch is not included.
+
+// compile-flags:-Zborrowck-mir -Znll
+
+#![allow(warnings)]
+#![feature(rustc_attrs)]
+
+struct MyStruct {
+    field: String
+}
+
+fn main() {
+}
+
+fn nll_fail() {
+    let mut my_struct = MyStruct { field: format!("Hello") };
+
+    let value = &mut my_struct.field;
+    loop {
+        my_struct.field.push_str("Hello, world!");
+        //~^ ERROR (Ast) [E0499]
+        //~| ERROR (Mir) [E0499]
+        value.len();
+        return;
+    }
+}
+
+fn nll_ok() {
+    let mut my_struct = MyStruct { field: format!("Hello") };
+
+    let value = &mut my_struct.field;
+    loop {
+        my_struct.field.push_str("Hello, world!");
+        //~^ ERROR (Ast) [E0499]
+        return;
+    }
+}
diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs
index 1f9ad988acc..61dc1d20659 100644
--- a/src/test/mir-opt/end_region_destruction_extents_1.rs
+++ b/src/test/mir-opt/end_region_destruction_extents_1.rs
@@ -133,11 +133,11 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> {
 //         StorageLive(_3);
 //         StorageLive(_4);
 //         StorageLive(_5);
-//         _5 = promoted1;
+//         _5 = promoted[1];
 //         _4 = &'12ds (*_5);
 //         StorageLive(_7);
 //         StorageLive(_8);
-//         _8 = promoted0;
+//         _8 = promoted[0];
 //         _7 = &'10s (*_8);
 //         _3 = D1<'12ds, '10s>::{{constructor}}(_4, _7);
 //         EndRegion('10s);
diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs
new file mode 100644
index 00000000000..873431505f5
--- /dev/null
+++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs
@@ -0,0 +1,51 @@
+// Copyright 2012-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
+
+fn can_panic() -> Box<usize> {
+    Box::new(44)
+}
+
+fn main() {
+    let mut x = Box::new(22);
+    x = can_panic();
+}
+
+// Check that:
+// - `_1` is the variable corresponding to `x`
+// and
+// - `_1` is live when `can_panic` is called (because it may be dropped)
+//
+// END RUST SOURCE
+// START rustc.node12.nll.0.mir
+//    | Live variables on entry to bb0: []
+//    bb0: {
+//            | Live variables at bb0[0]: []
+//        StorageLive(_1);
+//            | Live variables at bb0[1]: []
+//        StorageLive(_2);
+//            | Live variables at bb0[2]: []
+//        _2 = const 22usize;
+//            | Live variables at bb0[3]: [_2]
+//        _1 = const <std::boxed::Box<T>>::new(_2) -> bb1;
+//    }
+// END rustc.node12.nll.0.mir
+// START rustc.node12.nll.0.mir
+//    | Live variables on entry to bb1: [_1 (drop)]
+//    bb1: {
+//            | Live variables at bb1[0]: [_1 (drop)]
+//        StorageDead(_2);
+//            | Live variables at bb1[1]: [_1 (drop)]
+//        StorageLive(_3);
+//            | Live variables at bb1[2]: [_1 (drop)]
+//        _3 = const can_panic() -> [return: bb2, unwind: bb4];
+//    }
+// END rustc.node12.nll.0.mir
diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs
new file mode 100644
index 00000000000..96fd29dfe2f
--- /dev/null
+++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs
@@ -0,0 +1,41 @@
+// Copyright 2012-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
+
+#![allow(warnings)]
+
+fn use_x(_: usize) -> bool { true }
+
+fn main() {
+    let mut x = 22;
+    loop {
+        // Key point: `x` not live on entry to this basic block.
+        x = 55;
+        if use_x(x) { break; }
+    }
+}
+
+// END RUST SOURCE
+// START rustc.node12.nll.0.mir
+//    | Live variables on entry to bb1: []
+//    bb1: {
+//            | Live variables at bb1[0]: []
+//        _1 = const 55usize;
+//            | Live variables at bb1[1]: [_1]
+//        StorageLive(_3);
+//            | Live variables at bb1[2]: [_1]
+//        StorageLive(_4);
+//            | Live variables at bb1[3]: [_1]
+//        _4 = _1;
+//            | Live variables at bb1[4]: [_4]
+//        _3 = const use_x(_4) -> bb2;
+//    }
+// END rustc.node12.nll.0.mir
diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs
new file mode 100644
index 00000000000..c557763c004
--- /dev/null
+++ b/src/test/mir-opt/nll/liveness-interblock.rs
@@ -0,0 +1,50 @@
+// Copyright 2012-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
+
+fn cond() -> bool { false }
+
+fn make_live(_: usize) { }
+
+fn make_dead() { }
+
+fn main() {
+    let x = 5;
+
+    if cond() {
+        make_live(x);
+    } else {
+        // x should be dead on entry to this block
+        make_dead();
+    }
+}
+
+// END RUST SOURCE
+// START rustc.node18.nll.0.mir
+//     | Live variables on entry to bb2: [_1]
+//     bb2: {
+//             | Live variables at bb2[0]: [_1]
+//         StorageLive(_4);
+//             | Live variables at bb2[1]: [_1]
+//         _4 = _1;
+//             | Live variables at bb2[2]: [_4]
+//         _3 = const make_live(_4) -> bb4;
+//     }
+// END rustc.node18.nll.0.mir
+// START rustc.node18.nll.0.mir
+//     | Live variables on entry to bb3: []
+//     bb3: {
+//             | Live variables at bb3[0]: []
+//         _5 = const make_dead() -> bb5;
+//     }
+// END rustc.node18.nll.0.mir
+
+
diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs
new file mode 100644
index 00000000000..60a4da430b9
--- /dev/null
+++ b/src/test/mir-opt/nll/reborrow-basic.rs
@@ -0,0 +1,39 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic test for reborrow constraints: the region (`R5`) that appears
+// in the type of `r_a` must outlive the region (`R7`) that appears in
+// the type of `r_b`
+
+// compile-flags:-Znll -Zverbose
+//                     ^^^^^^^^^ force compiler to dump more region information
+
+#![allow(warnings)]
+
+fn use_x(_: &mut i32) -> bool { true }
+
+fn main() {
+    let mut foo: i32     = 22;
+    let r_a: &mut i32 = &mut foo;
+    let r_b: &mut i32 = &mut *r_a;
+    use_x(r_b);
+}
+
+// END RUST SOURCE
+// START rustc.node13.nll.0.mir
+// | '_#5r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]}
+// ...
+// | '_#7r: {bb0[11], bb0[12], bb0[13], bb0[14]}
+// END rustc.node13.nll.0.mir
+// START rustc.node13.nll.0.mir
+// let _2: &'_#5r mut i32;
+// ...
+// let _4: &'_#7r mut i32;
+// END rustc.node13.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
new file mode 100644
index 00000000000..7792f0a36f3
--- /dev/null
+++ b/src/test/mir-opt/nll/region-liveness-basic.rs
@@ -0,0 +1,56 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic test for liveness constraints: the region (`R1`) that appears
+// in the type of `p` includes the points after `&v[0]` up to (but not
+// including) the call to `use_x`. The `else` branch is not included.
+
+// compile-flags:-Znll -Zverbose
+//                     ^^^^^^^^^ force compiler to dump more region information
+
+#![allow(warnings)]
+
+fn use_x(_: usize) -> bool { true }
+
+fn main() {
+    let mut v = [1, 2, 3];
+    let p = &v[0];
+    if true {
+        use_x(*p);
+    } else {
+        use_x(22);
+    }
+}
+
+// END RUST SOURCE
+// START rustc.node12.nll.0.mir
+// | '_#0r: {bb1[1], bb2[0], bb2[1]}
+// | '_#1r: {bb1[1], bb2[0], bb2[1]}
+// ...
+//             let _2: &'_#1r usize;
+// END rustc.node12.nll.0.mir
+// START rustc.node12.nll.0.mir
+//    bb1: {
+//            | Live variables at bb1[0]: [_1, _3]
+//        _2 = &'_#0r _1[_3];
+//            | Live variables at bb1[1]: [_2]
+//        switchInt(const true) -> [0u8: bb3, otherwise: bb2];
+//    }
+// END rustc.node12.nll.0.mir
+// START rustc.node12.nll.0.mir
+//    bb2: {
+//            | Live variables at bb2[0]: [_2]
+//        StorageLive(_7);
+//            | Live variables at bb2[1]: [_2]
+//        _7 = (*_2);
+//            | Live variables at bb2[2]: [_7]
+//        _6 = const use_x(_7) -> bb4;
+//    }
+// END rustc.node12.nll.0.mir
diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
new file mode 100644
index 00000000000..4f4bb596e5f
--- /dev/null
+++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
@@ -0,0 +1,48 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic test for liveness constraints: the region (`R1`) that appears
+// in the type of `p` includes the points after `&v[0]` up to (but not
+// including) the call to `use_x`. The `else` branch is not included.
+
+// compile-flags:-Znll -Zverbose
+//                     ^^^^^^^^^ force compiler to dump more region information
+
+#![allow(warnings)]
+#![feature(dropck_eyepatch)]
+#![feature(generic_param_attrs)]
+
+fn use_x(_: usize) -> bool { true }
+
+fn main() {
+    let mut v = [1, 2, 3];
+    let p: Wrap<& /* R4 */ usize> = Wrap { value: &v[0] };
+    if true {
+        use_x(*p.value);
+    } else {
+        use_x(22);
+    }
+
+    // `p` will get dropped here. However, because of the
+    // `#[may_dangle]` attribute, we do not need to consider R4 live.
+}
+
+struct Wrap<T> {
+    value: T
+}
+
+unsafe impl<#[may_dangle] T> Drop for Wrap<T> {
+    fn drop(&mut self) { }
+}
+
+// END RUST SOURCE
+// START rustc.node12.nll.0.mir
+// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]}
+// END rustc.node12.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
new file mode 100644
index 00000000000..0ddb745b61f
--- /dev/null
+++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs
@@ -0,0 +1,50 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic test for liveness constraints: the region (`R1`) that appears
+// in the type of `p` includes the points after `&v[0]` up to (but not
+// including) the call to `use_x`. The `else` branch is not included.
+
+// ignore-tidy-linelength
+// compile-flags:-Znll -Zverbose
+//                     ^^^^^^^^^ force compiler to dump more region information
+
+#![allow(warnings)]
+
+fn use_x(_: usize) -> bool { true }
+
+fn main() {
+    let mut v = [1, 2, 3];
+    let p: Wrap<& /* R1 */ usize> = Wrap { value: &v[0] };
+    if true {
+        use_x(*p.value);
+    } else {
+        use_x(22);
+    }
+
+    // `p` will get dropped here. Because the `#[may_dangle]`
+    // attribute is not present on `Wrap`, we must conservatively
+    // assume that the dtor may access the `value` field, and hence we
+    // must consider R1 to be live.
+}
+
+struct Wrap<T> {
+    value: T
+}
+
+// Look ma, no `#[may_dangle]` attribute here.
+impl<T> Drop for Wrap<T> {
+    fn drop(&mut self) { }
+}
+
+// END RUST SOURCE
+// START rustc.node12.nll.0.mir
+// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]}
+// END rustc.node12.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
new file mode 100644
index 00000000000..664298b9374
--- /dev/null
+++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs
@@ -0,0 +1,49 @@
+// Copyright 2012-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 for the subregion constraints. In this case, the region R3 on
+// `p` includes two disjoint regions of the control-flow graph. The
+// borrows in `&v[0]` and `&v[1]` each (in theory) have to outlive R3,
+// but only at a particular point, and hence they wind up including
+// distinct regions.
+
+// compile-flags:-Znll -Zverbose
+//                     ^^^^^^^^^ force compiler to dump more region information
+
+#![allow(warnings)]
+
+fn use_x(_: usize) -> bool { true }
+
+fn main() {
+    let mut v = [1, 2, 3];
+    let mut p = &v[0];
+    if true {
+        use_x(*p);
+    } else {
+        use_x(22);
+    }
+
+    p = &v[1];
+    use_x(*p);
+}
+
+// END RUST SOURCE
+// START rustc.node12.nll.0.mir
+// | '_#0r: {bb1[1], bb2[0], bb2[1]}
+// ...
+// | '_#2r: {bb7[2], bb7[3], bb7[4]}
+// | '_#3r: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]}
+// ...
+// let mut _2: &'_#3r usize;
+// ...
+// _2 = &'_#0r _1[_3];
+// ...
+// _2 = &'_#2r (*_11);
+// END rustc.node12.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
new file mode 100644
index 00000000000..4ae891f5b70
--- /dev/null
+++ b/src/test/mir-opt/nll/region-subtyping-basic.rs
@@ -0,0 +1,49 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic test for liveness constraints: the region (`R1`) that appears
+// in the type of `p` includes the points after `&v[0]` up to (but not
+// including) the call to `use_x`. The `else` branch is not included.
+
+// compile-flags:-Znll -Zverbose
+//                     ^^^^^^^^^ force compiler to dump more region information
+
+#![allow(warnings)]
+
+fn use_x(_: usize) -> bool { true }
+
+fn main() {
+    let mut v = [1, 2, 3];
+    let p = &v[0];
+    let q = p;
+    if true {
+        use_x(*q);
+    } else {
+        use_x(22);
+    }
+}
+
+// END RUST SOURCE
+// START rustc.node12.nll.0.mir
+// | '_#0r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]}
+// | '_#1r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]}
+// | '_#2r: {bb1[5], bb1[6], bb2[0], bb2[1]}
+// END rustc.node12.nll.0.mir
+// START rustc.node12.nll.0.mir
+// let _2: &'_#1r usize;
+// ...
+// let _6: &'_#2r usize;
+// ...
+// _2 = &'_#0r _1[_3];
+// ...
+// _7 = _2;
+// ...
+// _6 = _7;
+// END rustc.node12.nll.0.mir
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 0473c2a5405..f8628158aff 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1404,6 +1404,7 @@ actual:\n\
                     "-Zdump-mir-exclude-pass-number"]);
 
                 let mir_dump_dir = self.get_mir_dump_dir();
+                let _ = fs::remove_dir_all(&mir_dump_dir);
                 create_dir_all(mir_dump_dir.as_path()).unwrap();
                 let mut dir_opt = "-Zdump-mir-dir=".to_string();
                 dir_opt.push_str(mir_dump_dir.to_str().unwrap());
@@ -2367,12 +2368,10 @@ actual:\n\
     }
 
     fn get_mir_dump_dir(&self) -> PathBuf {
-        let mut mir_dump_dir = PathBuf::from(self.config.build_base
-                                                    .as_path()
-                                                    .to_str()
-                                                    .unwrap());
+        let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
         debug!("input_file: {:?}", self.testpaths.file);
-        mir_dump_dir.push(self.testpaths.file.file_stem().unwrap().to_str().unwrap());
+        mir_dump_dir.push(&self.testpaths.relative_dir);
+        mir_dump_dir.push(self.testpaths.file.file_stem().unwrap());
         mir_dump_dir
     }