about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Daniel Faria <nashenas88@users.noreply.github.com>2017-11-17 04:34:02 -0500
committerNiko Matsakis <niko@alum.mit.edu>2017-12-04 08:51:11 -0500
commita9cb25b23aace3e8a7db3e64468dd84314a6d867 (patch)
treebbc932afb29cce1b628aab2700ca54c4181bccf5
parent08c8d7e91917dd7265042d6ccf204e241f17db32 (diff)
downloadrust-a9cb25b23aace3e8a7db3e64468dd84314a6d867.tar.gz
rust-a9cb25b23aace3e8a7db3e64468dd84314a6d867.zip
inform constraint generation using maybe-init
In particular, if we see a variable is DROP-LIVE, but it is not
MAYBE-INIT, then we can ignore the drop. This leavess attempt to use
more complex refinements of the idea (e.g., for subpaths or subfields)
to future work.
-rw-r--r--src/librustc_mir/borrow_check/mod.rs (renamed from src/librustc_mir/borrow_check.rs)112
-rw-r--r--src/librustc_mir/borrow_check/nll/constraint_generation.rs (renamed from src/librustc_mir/transform/nll/constraint_generation.rs)77
-rw-r--r--src/librustc_mir/borrow_check/nll/free_regions.rs (renamed from src/librustc_mir/transform/nll/free_regions.rs)0
-rw-r--r--src/librustc_mir/borrow_check/nll/mod.rs (renamed from src/librustc_mir/transform/nll/mod.rs)51
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer.rs (renamed from src/librustc_mir/transform/nll/region_infer.rs)0
-rw-r--r--src/librustc_mir/borrow_check/nll/renumber.rs (renamed from src/librustc_mir/transform/nll/renumber.rs)0
-rw-r--r--src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs (renamed from src/librustc_mir/transform/nll/subtype_constraint_generation.rs)0
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs10
-rw-r--r--src/librustc_mir/dataflow/move_paths/mod.rs4
-rw-r--r--src/librustc_mir/transform/mod.rs1
-rw-r--r--src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs33
-rw-r--r--src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr11
-rw-r--r--src/test/ui/nll/maybe-initialized-drop-uninitialized.rs28
-rw-r--r--src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr0
-rw-r--r--src/test/ui/nll/maybe-initialized-drop-with-fragment.rs32
-rw-r--r--src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr11
-rw-r--r--src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs34
-rw-r--r--src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr11
-rw-r--r--src/test/ui/nll/maybe-initialized-drop.rs27
-rw-r--r--src/test/ui/nll/maybe-initialized-drop.stderr10
20 files changed, 374 insertions, 78 deletions
diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check/mod.rs
index ff38760cce6..446aba3d3d7 100644
--- a/src/librustc_mir/borrow_check.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -18,7 +18,6 @@ use rustc::ty::maps::Providers;
 use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place};
 use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
 use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
-use transform::nll;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::indexed_set::{self, IdxSetBuf};
@@ -39,6 +38,7 @@ use util::borrowck_errors::{BorrowckErrors, Origin};
 
 use self::MutateMode::{JustWrite, WriteAndRead};
 
+pub(crate) mod nll;
 
 pub fn provide(providers: &mut Providers) {
     *providers = Providers {
@@ -77,7 +77,21 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         .as_local_node_id(def_id)
         .expect("do_mir_borrowck: non-local DefId");
 
-    let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx) {
+    // 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 free_regions = if !tcx.sess.opts.debugging_opts.nll {
+        None
+    } else {
+        let mir = &mut mir;
+
+        // Replace all regions with fresh inference variables.
+        Some(nll::replace_regions_in_mir(infcx, def_id, mir))
+    };
+    let mir = &mir;
+
+    let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) {
         Ok(move_data) => move_data,
         Err((move_data, move_errors)) => {
             for move_error in move_errors {
@@ -110,34 +124,12 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         }
     };
 
-    // 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, def_id, param_env, mir))
-    };
-
     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(
+    let mut flow_inits = FlowInProgress::new(do_dataflow(
         tcx,
         mir,
         id,
@@ -145,8 +137,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         &dead_unwinds,
         MaybeInitializedLvals::new(tcx, mir, &mdpe),
         |bd, i| &bd.move_data().move_paths[i],
-    );
-    let flow_uninits = do_dataflow(
+    ));
+    let flow_uninits = FlowInProgress::new(do_dataflow(
         tcx,
         mir,
         id,
@@ -154,8 +146,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         &dead_unwinds,
         MaybeUninitializedLvals::new(tcx, mir, &mdpe),
         |bd, i| &bd.move_data().move_paths[i],
-    );
-    let flow_move_outs = do_dataflow(
+    ));
+    let flow_move_outs = FlowInProgress::new(do_dataflow(
         tcx,
         mir,
         id,
@@ -163,8 +155,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         &dead_unwinds,
         MovingOutStatements::new(tcx, mir, &mdpe),
         |bd, i| &bd.move_data().moves[i],
-    );
-    let flow_ever_inits = do_dataflow(
+    ));
+    let flow_ever_inits = FlowInProgress::new(do_dataflow(
         tcx,
         mir,
         id,
@@ -172,7 +164,24 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         &dead_unwinds,
         EverInitializedLvals::new(tcx, mir, &mdpe),
         |bd, i| &bd.move_data().inits[i],
-    );
+    ));
+
+    // If we are in non-lexical mode, compute the non-lexical lifetimes.
+    let opt_regioncx = if let Some(free_regions) = free_regions {
+        Some(nll::compute_regions(
+            infcx,
+            def_id,
+            free_regions,
+            mir,
+            param_env,
+            &mut flow_inits,
+            &mdpe.move_data,
+        ))
+    } else {
+        assert!(!tcx.sess.opts.debugging_opts.nll);
+        None
+    };
+    let flow_inits = flow_inits; // remove mut
 
     let mut mbcx = MirBorrowckCtxt {
         tcx: tcx,
@@ -183,6 +192,16 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         storage_dead_or_drop_error_reported: FxHashSet(),
     };
 
+    let flow_borrows = FlowInProgress::new(do_dataflow(
+        tcx,
+        mir,
+        id,
+        &attributes,
+        &dead_unwinds,
+        Borrows::new(tcx, mir, opt_regioncx),
+        |bd, i| bd.location(i),
+    ));
+
     let mut state = InProgress::new(
         flow_borrows,
         flow_inits,
@@ -2318,19 +2337,19 @@ impl ContextKind {
 }
 
 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>>,
-        move_out: DataflowResults<MovingOutStatements<'b, 'gcx, 'tcx>>,
-        ever_inits: DataflowResults<EverInitializedLvals<'b, 'gcx, 'tcx>>,
+    fn new(
+        borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
+        inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
+        uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
+        move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
+        ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
     ) -> Self {
         InProgress {
-            borrows: FlowInProgress::new(borrows),
-            inits: FlowInProgress::new(inits),
-            uninits: FlowInProgress::new(uninits),
-            move_outs: FlowInProgress::new(move_out),
-            ever_inits: FlowInProgress::new(ever_inits),
+            borrows,
+            inits,
+            uninits,
+            move_outs,
+            ever_inits,
         }
     }
 
@@ -2436,8 +2455,11 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
     }
 }
 
-impl<'b, 'gcx, 'tcx> FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>> {
-    fn has_any_child_of(&self, mpi: MovePathIndex) -> Option<MovePathIndex> {
+impl<'tcx, T> FlowInProgress<T>
+where
+    T: HasMoveData<'tcx> + BitDenotation<Idx = MovePathIndex>,
+{
+    fn has_any_child_of(&self, mpi: T::Idx) -> Option<T::Idx> {
         let move_data = self.base_results.operator().move_data();
 
         let mut todo = vec![mpi];
diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
index 73d5a610dbd..460d49af20e 100644
--- a/src/librustc_mir/transform/nll/constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use rustc::hir;
-use rustc::mir::{Location, Place, Mir, Rvalue};
+use rustc::mir::{Local, Location, Place, Mir, Rvalue};
 use rustc::mir::visit::Visitor;
 use rustc::mir::Place::Projection;
 use rustc::mir::{PlaceProjection, ProjectionElem};
@@ -20,17 +20,22 @@ use rustc::ty::fold::TypeFoldable;
 use rustc::util::common::ErrorReported;
 use rustc_data_structures::fx::FxHashSet;
 use syntax::codemap::DUMMY_SP;
+use borrow_check::FlowInProgress;
+use dataflow::MaybeInitializedLvals;
+use dataflow::move_paths::{MoveData, HasMoveData};
 
 use super::LivenessResults;
 use super::ToRegionVid;
 use super::region_infer::RegionInferenceContext;
 
-pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
     regioncx: &mut RegionInferenceContext<'tcx>,
     mir: &Mir<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     liveness: &LivenessResults,
+    flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
+    move_data: &MoveData<'tcx>,
 ) {
     ConstraintGeneration {
         infcx,
@@ -38,18 +43,23 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
         mir,
         liveness,
         param_env,
+        flow_inits,
+        move_data,
     }.add_constraints();
 }
 
-struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
-    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
-    regioncx: &'cx mut RegionInferenceContext<'tcx>,
-    mir: &'cx Mir<'tcx>,
-    liveness: &'cx LivenessResults,
+/// 'cg = the duration of the constraint generation process itself.
+struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
+    regioncx: &'cg mut RegionInferenceContext<'tcx>,
+    mir: &'cg Mir<'tcx>,
+    liveness: &'cg LivenessResults,
     param_env: ty::ParamEnv<'tcx>,
+    flow_inits: &'cg mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
+    move_data: &'cg MoveData<'tcx>,
 }
 
-impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
+impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
     fn add_constraints(&mut self) {
         self.add_liveness_constraints();
         self.add_borrow_constraints();
@@ -73,14 +83,51 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
                     }
                 });
 
-            self.liveness
-                .drop
-                .simulate_block(self.mir, bb, |location, live_locals| {
-                    for live_local in live_locals.iter() {
+            let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
+            self.liveness.drop.simulate_block(self.mir, bb, |location, live_locals| {
+                all_live_locals.push((location, live_locals.iter().collect()));
+            });
+            debug!("add_liveness_constraints: all_live_locals={:#?}", all_live_locals);
+
+            let terminator_index = self.mir.basic_blocks()[bb].statements.len();
+            self.flow_inits.reset_to_entry_of(bb);
+            while let Some((location, live_locals)) = all_live_locals.pop() {
+                for live_local in live_locals {
+                    debug!("add_liveness_constraints: location={:?} live_local={:?}", location,
+                           live_local);
+
+                    self.flow_inits.each_state_bit(|mpi_init| {
+                        debug!("add_liveness_constraints: location={:?} initialized={:?}",
+                               location,
+                               &self.flow_inits
+                                   .base_results
+                                   .operator()
+                                   .move_data()
+                                   .move_paths[mpi_init]);
+                    });
+
+                    let mpi = self.move_data.rev_lookup.find_local(live_local);
+                    if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
+                        debug!("add_liveness_constraints: mpi={:?} has initialized child {:?}",
+                               self.move_data.move_paths[mpi],
+                               self.move_data.move_paths[initialized_child]);
+
                         let live_local_ty = self.mir.local_decls[live_local].ty;
                         self.add_drop_live_constraint(live_local_ty, location);
                     }
-                });
+                }
+
+                if location.statement_index == terminator_index {
+                    debug!("add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
+                           location);
+                    self.flow_inits.reconstruct_terminator_effect(location);
+                } else {
+                    debug!("add_liveness_constraints: reconstruct_statement_effect from {:#?}",
+                           location);
+                    self.flow_inits.reconstruct_statement_effect(location);
+                }
+                self.flow_inits.apply_local_effect();
+            }
         }
     }
 
@@ -219,7 +266,7 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
     }
 }
 
-impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
+impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> {
     fn visit_rvalue(&mut self,
                     rvalue: &Rvalue<'tcx>,
                     location: Location) {
diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/borrow_check/nll/free_regions.rs
index 92a8a714d52..92a8a714d52 100644
--- a/src/librustc_mir/transform/nll/free_regions.rs
+++ b/src/librustc_mir/borrow_check/nll/free_regions.rs
diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index 147f061ad11..213cf52a8eb 100644
--- a/src/librustc_mir/transform/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -17,6 +17,9 @@ use std::collections::BTreeSet;
 use transform::MirSource;
 use transform::type_check;
 use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
+use borrow_check::FlowInProgress;
+use dataflow::MaybeInitializedLvals;
+use dataflow::move_paths::MoveData;
 
 use util as mir_util;
 use self::mir_util::PassWhere;
@@ -24,27 +27,43 @@ use self::mir_util::PassWhere;
 mod constraint_generation;
 mod subtype_constraint_generation;
 mod free_regions;
+use self::free_regions::FreeRegions;
 
 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>,
+/// Rewrites the regions in the MIR to use NLL variables, also
+/// scraping out the set of free regions (e.g., region parameters)
+/// declared on the function. That set will need to be given to
+/// `compute_regions`.
+pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
     def_id: DefId,
-    param_env: ty::ParamEnv<'gcx>,
     mir: &mut Mir<'tcx>,
-) -> RegionInferenceContext<'tcx> {
+) -> FreeRegions<'tcx> {
     // Compute named region information.
-    let free_regions = &free_regions::free_regions(infcx, def_id);
+    let free_regions = free_regions::free_regions(infcx, def_id);
 
     // Replace all regions with fresh inference variables.
-    renumber::renumber_mir(infcx, free_regions, mir);
+    renumber::renumber_mir(infcx, &free_regions, mir);
+
+    free_regions
+}
 
+/// Computes the (non-lexical) regions from the input MIR.
+///
+/// This may result in errors being reported.
+pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
+    def_id: DefId,
+    free_regions: FreeRegions<'tcx>,
+    mir: &Mir<'tcx>,
+    param_env: ty::ParamEnv<'gcx>,
+    flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
+    move_data: &MoveData<'tcx>,
+) -> RegionInferenceContext<'tcx> {
     // Run the MIR type-checker.
     let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
     let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
@@ -52,8 +71,8 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
     // Create the region inference context, taking ownership of the region inference
     // data that was contained in `infcx`.
     let var_origins = infcx.take_region_var_origins();
-    let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir);
-    subtype_constraint_generation::generate(&mut regioncx, free_regions, mir, constraint_sets);
+    let mut regioncx = RegionInferenceContext::new(var_origins, &free_regions, mir);
+    subtype_constraint_generation::generate(&mut regioncx, &free_regions, mir, constraint_sets);
 
     // Compute what is live where.
     let liveness = &LivenessResults {
@@ -75,7 +94,15 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
     };
 
     // Generate non-subtyping constraints.
-    constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness);
+    constraint_generation::generate_constraints(
+        infcx,
+        &mut regioncx,
+        &mir,
+        param_env,
+        liveness,
+        flow_inits,
+        move_data,
+    );
 
     // Solve the region constraints.
     regioncx.solve(infcx, &mir);
diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/borrow_check/nll/region_infer.rs
index f60bd3c6ece..f60bd3c6ece 100644
--- a/src/librustc_mir/transform/nll/region_infer.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer.rs
diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs
index 1076b774de6..1076b774de6 100644
--- a/src/librustc_mir/transform/nll/renumber.rs
+++ b/src/librustc_mir/borrow_check/nll/renumber.rs
diff --git a/src/librustc_mir/transform/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
index c1850c76541..c1850c76541 100644
--- a/src/librustc_mir/transform/nll/subtype_constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 19ab45dda95..286ca768b16 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -21,8 +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::ToRegionVid;
+use borrow_check::nll::region_infer::RegionInferenceContext;
+use borrow_check::nll::ToRegionVid;
 
 use syntax_pos::Span;
 
@@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
     location_map: FxHashMap<Location, BorrowIndex>,
     region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
     region_span_map: FxHashMap<RegionKind, Span>,
-    nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>,
+    nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>,
 }
 
 // temporarily allow some dead fields: `kind` and `region` will be
@@ -69,7 +69,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
 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<'tcx>>)
+               nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>)
                -> Self {
         let mut visitor = GatherBorrows {
             tcx,
@@ -156,7 +156,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
     fn kill_loans_out_of_scope_at_location(&self,
                                            sets: &mut BlockSets<BorrowIndex>,
                                            location: Location) {
-        if let Some(regioncx) = self.nonlexical_regioncx {
+        if let Some(ref regioncx) = self.nonlexical_regioncx {
             for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
                 let borrow_region = borrow_data.region.to_region_vid();
                 if !regioncx.region_contains_point(borrow_region, location) {
diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs
index 294f48178a8..9d91e1344dc 100644
--- a/src/librustc_mir/dataflow/move_paths/mod.rs
+++ b/src/librustc_mir/dataflow/move_paths/mod.rs
@@ -263,6 +263,10 @@ impl<'tcx> MovePathLookup<'tcx> {
             }
         }
     }
+
+    pub fn find_local(&self, local: Local) -> MovePathIndex {
+        self.locals[local]
+    }
 }
 
 #[derive(Debug)]
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 830838c6037..fb9daf07c71 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -43,7 +43,6 @@ pub mod instcombine;
 pub mod copy_prop;
 pub mod generator;
 pub mod inline;
-pub mod nll;
 pub mod lower_128bit;
 
 pub(crate) fn provide(providers: &mut Providers) {
diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs
new file mode 100644
index 00000000000..0047f6d5923
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs
@@ -0,0 +1,33 @@
+// 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.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+struct Foo<'p> { a: String, b: Wrap<'p> }
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    let s = String::from("str");
+    let foo = Foo { a: s, b: wrap };
+    std::mem::drop(foo.b);
+    x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+    // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr
new file mode 100644
index 00000000000..389334f9c1d
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr
@@ -0,0 +1,11 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:31:5
+   |
+27 |     let wrap = Wrap { p: &mut x };
+   |                          ------ borrow of `x` occurs here
+...
+31 |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs
new file mode 100644
index 00000000000..64a4d391000
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs
@@ -0,0 +1,28 @@
+// 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.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    std::mem::drop(wrap);
+    x = 1; // OK, drop is inert
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr b/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr
diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs
new file mode 100644
index 00000000000..3242136f005
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs
@@ -0,0 +1,32 @@
+// 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.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+struct Foo<'p> { a: String, b: Wrap<'p> }
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    let s = String::from("str");
+    let foo = Foo { a: s, b: wrap };
+    std::mem::drop(foo.a);
+    x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr
new file mode 100644
index 00000000000..9edeca2d188
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr
@@ -0,0 +1,11 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/maybe-initialized-drop-with-fragment.rs:31:5
+   |
+27 |     let wrap = Wrap { p: &mut x };
+   |                          ------ borrow of `x` occurs here
+...
+31 |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs
new file mode 100644
index 00000000000..3e32818b8dc
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs
@@ -0,0 +1,34 @@
+// 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.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+struct Foo<'p> { a: String, b: Wrap<'p> }
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    let s = String::from("str");
+    let foo = Foo { a: s, b: wrap };
+    std::mem::drop(foo.a);
+    std::mem::drop(foo.b);
+    x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+    // FIXME ^ This currently errors and it should not.
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr
new file mode 100644
index 00000000000..24d0d6d04c8
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr
@@ -0,0 +1,11 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/maybe-initialized-drop-with-uninitialized-fragments.rs:32:5
+   |
+27 |     let wrap = Wrap { p: &mut x };
+   |                          ------ borrow of `x` occurs here
+...
+32 |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs
new file mode 100644
index 00000000000..291fcbd73f3
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop.rs
@@ -0,0 +1,27 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop.stderr b/src/test/ui/nll/maybe-initialized-drop.stderr
new file mode 100644
index 00000000000..7b1b55d133a
--- /dev/null
+++ b/src/test/ui/nll/maybe-initialized-drop.stderr
@@ -0,0 +1,10 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/maybe-initialized-drop.rs:26:5
+   |
+25 |     let wrap = Wrap { p: &mut x };
+   |                          ------ borrow of `x` occurs here
+26 |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+
+error: aborting due to previous error
+