]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_mir/borrow_check.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / librustc_mir / borrow_check.rs
index db6a0ee4ba5d368cc4614162c9b3affed39078dc..cdac72b6dffb0ffa5d1820831721db9df3a5c0c9 100644 (file)
 
 //! This query borrow-checks the MIR to (further) ensure it is not broken.
 
+use rustc::hir;
 use rustc::hir::def_id::{DefId};
 use rustc::infer::{InferCtxt};
 use rustc::ty::{self, TyCtxt, ParamEnv};
 use rustc::ty::maps::Providers;
-use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue};
+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 transform::nll;
 
+use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::indexed_set::{self, IdxSetBuf};
 use rustc_data_structures::indexed_vec::{Idx};
 
@@ -29,9 +31,10 @@ use dataflow::{do_dataflow};
 use dataflow::{MoveDataParamEnv};
 use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer};
 use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
+use dataflow::{MovingOutStatements};
 use dataflow::{Borrows, BorrowData, BorrowIndex};
 use dataflow::move_paths::{MoveError, IllegalMoveOriginKind};
-use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult};
+use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult, MoveOutIndex};
 use util::borrowck_errors::{BorrowckErrors, Origin};
 
 use self::MutateMode::{JustWrite, WriteAndRead};
@@ -46,93 +49,124 @@ 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 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 {
+    let input_mir = tcx.mir_validated(def_id);
+    debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
+
+    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);
+    });
+    debug!("mir_borrowck done");
+}
+
+fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+                                   input_mir: &Mir<'gcx>,
+                                   def_id: DefId)
+{
+    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 = tcx.hir.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, 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::InteriorOfSliceOrArray { 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, def_id, param_env, 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 flow_move_outs = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
+                                     MovingOutStatements::new(tcx, mir, &mdpe),
+                                     |bd, i| &bd.move_data().moves[i]);
+
+    let mut mbcx = MirBorrowckCtxt {
+        tcx: tcx,
+        mir: mir,
+        node_id: id,
+        move_data: &mdpe.move_data,
+        param_env: param_env,
+        storage_drop_or_dead_error_reported: FxHashSet(),
+    };
 
-    debug!("mir_borrowck done");
+    let mut state = InProgress::new(flow_borrows,
+                                    flow_inits,
+                                    flow_uninits,
+                                    flow_move_outs);
+
+    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>,
+pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+    mir: &'cx Mir<'tcx>,
     node_id: ast::NodeId,
-    move_data: &'b MoveData<'gcx>,
-    param_env: ParamEnv<'tcx>,
-    fake_infer_ctxt: &'c InferCtxt<'c, 'gcx, 'tcx>,
+    move_data: &'cx MoveData<'tcx>,
+    param_env: ParamEnv<'gcx>,
+    /// This field keeps track of when storage drop or dead errors are reported
+    /// in order to stop duplicate error reporting and identify the conditions required
+    /// for a "temporary value dropped here while still borrowed" error. See #45360.
+    storage_drop_or_dead_error_reported: FxHashSet<Local>,
 }
 
 // (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>>,
+    move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
 }
 
 struct FlowInProgress<BD> where BD: BitDenotation {
@@ -147,17 +181,16 @@ 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>
-    for MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
-{
-    type FlowState = InProgress<'b, 'gcx>;
+impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    type FlowState = InProgress<'cx, 'gcx, 'tcx>;
 
-    fn mir(&self) -> &'b Mir<'gcx> { self.mir }
+    fn mir(&self) -> &'cx 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),
                              |i| i.reset_to_entry_of(bb),
-                             |u| u.reset_to_entry_of(bb));
+                             |u| u.reset_to_entry_of(bb),
+                             |m| m.reset_to_entry_of(bb));
     }
 
     fn reconstruct_statement_effect(&mut self,
@@ -165,7 +198,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
                                     flow_state: &mut Self::FlowState) {
         flow_state.each_flow(|b| b.reconstruct_statement_effect(location),
                              |i| i.reconstruct_statement_effect(location),
-                             |u| u.reconstruct_statement_effect(location));
+                             |u| u.reconstruct_statement_effect(location),
+                             |m| m.reconstruct_statement_effect(location));
     }
 
     fn apply_local_effect(&mut self,
@@ -173,7 +207,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
                           flow_state: &mut Self::FlowState) {
         flow_state.each_flow(|b| b.apply_local_effect(),
                              |i| i.apply_local_effect(),
-                             |u| u.apply_local_effect());
+                             |u| u.apply_local_effect(),
+                             |m| m.apply_local_effect());
     }
 
     fn reconstruct_terminator_effect(&mut self,
@@ -181,7 +216,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
                                      flow_state: &mut Self::FlowState) {
         flow_state.each_flow(|b| b.reconstruct_terminator_effect(location),
                              |i| i.reconstruct_terminator_effect(location),
-                             |u| u.reconstruct_terminator_effect(location));
+                             |u| u.reconstruct_terminator_effect(location),
+                             |m| m.reconstruct_terminator_effect(location));
     }
 
     fn visit_block_entry(&mut self,
@@ -193,7 +229,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);
@@ -251,17 +287,22 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
             }
 
             StatementKind::StorageDead(local) => {
-                self.access_lvalue(ContextKind::StorageDead.new(location),
-                                   (&Lvalue::Local(local), span),
-                                   (Shallow(None), Write(WriteKind::StorageDead)),
-                                   flow_state);
+                if !self.storage_drop_or_dead_error_reported.contains(&local) {
+                    let error_reported = self.access_lvalue(ContextKind::StorageDead.new(location),
+                        (&Lvalue::Local(local), span),
+                        (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), flow_state);
+
+                    if error_reported {
+                        self.storage_drop_or_dead_error_reported.insert(local);
+                    }
+                }
             }
         }
     }
 
     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();
@@ -336,7 +377,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
             TerminatorKind::Resume |
             TerminatorKind::Return |
             TerminatorKind::GeneratorDrop |
-            TerminatorKind::Unreachable => {
+            TerminatorKind::Unreachable |
+            TerminatorKind::FalseEdges { .. } => {
                 // no data used, thus irrelevant to borrowck
             }
         }
@@ -396,21 +438,30 @@ enum ReadKind {
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 enum WriteKind {
-    StorageDead,
+    StorageDeadOrDrop,
     MutableBorrow(BorrowKind),
     Mutate,
     Move,
 }
 
-impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    /// Checks an access to the given lvalue to see if it is allowed. Examines the set of borrows
+    /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
+    /// lvalue is initialized and (b) it is not borrowed in some way that would prevent this
+    /// access.
+    ///
+    /// Returns true if an error is reported, false otherwise.
     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>) {
-        // FIXME: also need to check permissions (e.g. reject mut
-        // borrow of immutable ref, moves through non-`Box`-ref)
+                     flow_state: &InProgress<'cx, 'gcx, 'tcx>) -> bool {
         let (sd, rw) = kind;
+
+        // Check permissions
+        self.check_access_permissions(lvalue_span, rw);
+
+        let mut error_reported = false;
         self.each_borrow_involving_path(
             context, (sd, lvalue_span.0), flow_state, |this, _index, borrow, common_prefix| {
                 match (rw, borrow.kind) {
@@ -420,13 +471,16 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
                     (Read(kind), BorrowKind::Unique) |
                     (Read(kind), BorrowKind::Mut) => {
                         match kind {
-                            ReadKind::Copy =>
+                            ReadKind::Copy => {
+                                error_reported = true;
                                 this.report_use_while_mutably_borrowed(
-                                    context, lvalue_span, borrow),
+                                    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);
+                                error_reported = true;
                                 this.report_conflicting_borrow(
                                     context, common_prefix, lvalue_span, bk,
                                     &borrow, end_issued_loan_span)
@@ -438,32 +492,45 @@ 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);
+                                error_reported = true;
                                 this.report_conflicting_borrow(
                                     context, common_prefix, lvalue_span, bk,
                                     &borrow, end_issued_loan_span)
                             }
-                            WriteKind::StorageDead |
-                            WriteKind::Mutate =>
+                             WriteKind::StorageDeadOrDrop => {
+                                let end_span =
+                                    flow_state.borrows.base_results.operator().opt_region_end_span(
+                                        &borrow.region);
+                                error_reported = true;
+                                this.report_borrowed_value_does_not_live_long_enough(
+                                    context, lvalue_span, end_span)
+                            },
+                            WriteKind::Mutate => {
+                                error_reported = true;
                                 this.report_illegal_mutation_of_borrowed(
-                                    context, lvalue_span, borrow),
-                            WriteKind::Move =>
+                                    context, lvalue_span, borrow)
+                            },
+                            WriteKind::Move => {
+                                error_reported = true;
                                 this.report_move_out_while_borrowed(
-                                    context, lvalue_span, &borrow),
+                                    context, lvalue_span, &borrow)
+                            },
                         }
                         Control::Break
                     }
                 }
             });
+        error_reported
     }
 
     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<'cx, 'gcx, 'tcx>) {
         // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
         match mode {
             MutateMode::WriteAndRead => {
@@ -482,9 +549,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<'cx, 'gcx, 'tcx>) {
         match *rvalue {
             Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => {
                 let access_kind = match bk {
@@ -540,8 +607,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<'cx, 'gcx, 'tcx>) {
         match *operand {
             Operand::Consume(ref lvalue) => {
                 self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state)
@@ -553,18 +620,56 @@ 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<'cx, 'gcx, 'tcx>) {
         let lvalue = lvalue_span.0;
+
         let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
-        let moves_by_default =
-            self.fake_infer_ctxt.type_moves_by_default(self.param_env, ty, DUMMY_SP);
-        if moves_by_default {
-            // move of lvalue: check if this is move of already borrowed path
-            self.access_lvalue(context, lvalue_span, (Deep, Write(WriteKind::Move)), flow_state);
-        } else {
-            // copy of lvalue: check if this is "copy of frozen path" (FIXME: see check_loans.rs)
-            self.access_lvalue(context, lvalue_span, (Deep, Read(ReadKind::Copy)), flow_state);
+
+        // Erase the regions in type before checking whether it moves by
+        // default. There are a few reasons to do this:
+        //
+        // - They should not affect the result.
+        // - It avoids adding new region constraints into the surrounding context,
+        //   which would trigger an ICE, since the infcx will have been "frozen" by
+        //   the NLL region context.
+        let gcx = self.tcx.global_tcx();
+        let erased_ty = gcx.lift(&self.tcx.erase_regions(&ty)).unwrap();
+        let moves_by_default = erased_ty.moves_by_default(gcx, self.param_env, DUMMY_SP);
+
+        // Check if error has already been reported to stop duplicate reporting.
+        let has_storage_drop_or_dead_error_reported = match *lvalue {
+            Lvalue::Local(local) => self.storage_drop_or_dead_error_reported.contains(&local),
+            _ => false,
+        };
+
+        // If the error has been reported already, then we don't need the access_lvalue call.
+        if !has_storage_drop_or_dead_error_reported || consume_via_drop != ConsumeKind::Drop {
+            let error_reported;
+
+            if moves_by_default {
+                let kind = match consume_via_drop {
+                    ConsumeKind::Drop => WriteKind::StorageDeadOrDrop,
+                    _ => WriteKind::Move,
+                };
+
+                // move of lvalue: check if this is move of already borrowed path
+                error_reported = self.access_lvalue(context, lvalue_span,
+                                                    (Deep, Write(kind)), flow_state);
+            } else {
+                // copy of lvalue: check if this is "copy of frozen path"
+                // (FIXME: see check_loans.rs)
+                error_reported = self.access_lvalue(context, lvalue_span,
+                                                    (Deep, Read(ReadKind::Copy)), flow_state);
+            }
+
+            // If there was an error, then we keep track of it so as to deduplicate it.
+            // We only do this on ConsumeKind::Drop.
+            if error_reported && consume_via_drop == ConsumeKind::Drop {
+                if let Lvalue::Local(local) = *lvalue {
+                    self.storage_drop_or_dead_error_reported.insert(local);
+                }
+            }
         }
 
         // Finally, check if path was already moved.
@@ -581,12 +686,12 @@ 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> {
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     fn check_if_reassignment_to_immutable_state(&mut self,
                                                 context: Context,
-                                                (lvalue, span): (&Lvalue<'gcx>, Span),
-                                                flow_state: &InProgress<'b, 'gcx>) {
-        let move_data = flow_state.inits.base_results.operator().move_data();
+                                                (lvalue, span): (&Lvalue<'tcx>, Span),
+                                                flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
+        let move_data = self.move_data;
 
         // determine if this path has a non-mut owner (and thus needs checking).
         let mut l = lvalue;
@@ -602,16 +707,18 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
                         Mutability::Mut => return,
                     }
                 }
-                Lvalue::Static(_) => {
+                Lvalue::Static(ref static_) => {
                     // mutation of non-mut static is always illegal,
                     // independent of dataflow.
-                    self.report_assignment_to_static(context, (lvalue, span));
+                    if !self.tcx.is_static_mut(static_.def_id) {
+                        self.report_assignment_to_static(context, (lvalue, span));
+                    }
                     return;
                 }
             }
         }
 
-        if let Some(mpi) = self.move_path_for_lvalue(context, move_data, lvalue) {
+        if let Some(mpi) = self.move_path_for_lvalue(lvalue) {
             if flow_state.inits.curr_state.contains(&mpi) {
                 // may already be assigned before reaching this statement;
                 // report error.
@@ -635,36 +742,127 @@ 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<'cx, '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);
 
         let maybe_uninits = &flow_state.uninits;
-        let move_data = maybe_uninits.base_results.operator().move_data();
-        if let Some(mpi) = self.move_path_for_lvalue(context, move_data, lvalue) {
-            if maybe_uninits.curr_state.contains(&mpi) {
-                // find and report move(s) that could cause this to be uninitialized
-                self.report_use_of_moved(context, desired_action, lvalue_span);
-            } else {
-                // sanity check: initialized on *some* path, right?
-                assert!(flow_state.inits.curr_state.contains(&mpi));
+        let curr_move_outs = &flow_state.move_outs.curr_state;
+
+        // Bad scenarios:
+        //
+        // 1. Move of `a.b.c`, use of `a.b.c`
+        // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
+        // 3. Move of `a.b.c`, use of `a` or `a.b`
+        // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
+        //    partial initialization support, one might have `a.x`
+        //    initialized but not `a.b`.
+        //
+        // OK scenarios:
+        //
+        // 5. Move of `a.b.c`, use of `a.b.d`
+        // 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
+        // 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
+        //    must have been initialized for the use to be sound.
+        // 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
+
+        // The dataflow tracks shallow prefixes distinctly (that is,
+        // field-accesses on P distinctly from P itself), in order to
+        // track substructure initialization separately from the whole
+        // structure.
+        //
+        // E.g., when looking at (*a.b.c).d, if the closest prefix for
+        // which we have a MovePath is `a.b`, then that means that the
+        // initialization state of `a.b` is all we need to inspect to
+        // know if `a.b.c` is valid (and from that we infer that the
+        // dereference and `.d` access is also valid, since we assume
+        // `a.b.c` is assigned a reference to a initialized and
+        // well-formed record structure.)
+
+        // Therefore, if we seek out the *closest* prefix for which we
+        // have a MovePath, that should capture the initialization
+        // state for the lvalue scenario.
+        //
+        // This code covers scenarios 1, 2, and 4.
+
+        debug!("check_if_path_is_moved part1 lvalue: {:?}", lvalue);
+        match self.move_path_closest_to(lvalue) {
+            Ok(mpi) => {
+                if maybe_uninits.curr_state.contains(&mpi) {
+                    self.report_use_of_moved_or_uninitialized(context, desired_action,
+                                                              lvalue_span, mpi,
+                                                              curr_move_outs);
+                    return; // don't bother finding other problems.
+                }
+            }
+            Err(NoMovePathFound::ReachedStatic) => {
+                // Okay: we do not build MoveData for static variables
+            }
+
+            // Only query longest prefix with a MovePath, not further
+            // ancestors; dataflow recurs on children when parents
+            // move (to support partial (re)inits).
+            //
+            // (I.e. querying parents breaks scenario 8; but may want
+            // to do such a query based on partial-init feature-gate.)
+        }
+
+        // A move of any shallow suffix of `lvalue` also interferes
+        // with an attempt to use `lvalue`. This is scenario 3 above.
+        //
+        // (Distinct from handling of scenarios 1+2+4 above because
+        // `lvalue` does not interfere with suffixes of its prefixes,
+        // e.g. `a.b.c` does not interfere with `a.b.d`)
+
+        debug!("check_if_path_is_moved part2 lvalue: {:?}", lvalue);
+        if let Some(mpi) = self.move_path_for_lvalue(lvalue) {
+            if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) {
+                self.report_use_of_moved_or_uninitialized(context, desired_action,
+                                                          lvalue_span, child_mpi,
+                                                          curr_move_outs);
+                return; // don't bother finding other problems.
             }
         }
     }
 
+    /// Currently MoveData does not store entries for all lvalues in
+    /// the input MIR. For example it will currently filter out
+    /// lvalues that are Copy; thus we do not track lvalues of shared
+    /// reference type. This routine will walk up an lvalue along its
+    /// prefixes, searching for a foundational lvalue that *is*
+    /// tracked in the MoveData.
+    ///
+    /// 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<'tcx>)
+                            -> Result<MovePathIndex, NoMovePathFound>
+    {
+        let mut last_prefix = lvalue;
+        for prefix in self.prefixes(lvalue, PrefixSet::All) {
+            if let Some(mpi) = self.move_path_for_lvalue(prefix) {
+                return Ok(mpi);
+            }
+            last_prefix = prefix;
+        }
+        match *last_prefix {
+            Lvalue::Local(_) => panic!("should have move path for every Local"),
+            Lvalue::Projection(_) => panic!("PrefixSet::All meant dont stop for Projection"),
+            Lvalue::Static(_) => return Err(NoMovePathFound::ReachedStatic),
+        }
+    }
+
     fn move_path_for_lvalue(&mut self,
-                            _context: Context,
-                            move_data: &MoveData<'gcx>,
-                            lvalue: &Lvalue<'gcx>)
+                            lvalue: &Lvalue<'tcx>)
                             -> Option<MovePathIndex>
     {
         // If returns None, then there is no move path corresponding
         // to a direct owner of `lvalue` (which means there is nothing
         // that borrowck tracks for its analysis).
 
-        match move_data.rev_lookup.find(lvalue) {
+        match self.move_data.rev_lookup.find(lvalue) {
             LookupResult::Parent(_) => None,
             LookupResult::Exact(mpi) => Some(mpi),
         }
@@ -672,8 +870,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<'cx, 'gcx, 'tcx>) {
         // recur down lvalue; dispatch to check_if_path_is_moved when necessary
         let mut lvalue = lvalue;
         loop {
@@ -731,15 +929,168 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
             }
         }
     }
+
+    /// Check the permissions for the given lvalue and read or write kind
+    fn check_access_permissions(&self, (lvalue, span): (&Lvalue<'tcx>, Span), kind: ReadOrWrite) {
+        match kind {
+            Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => {
+                if let Err(_lvalue_err) = self.is_unique(lvalue) {
+                    span_bug!(span, "&unique borrow for `{}` should not fail",
+                        self.describe_lvalue(lvalue));
+                }
+            },
+            Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => {
+                if let Err(lvalue_err) = self.is_mutable(lvalue) {
+                    let mut err = self.tcx.cannot_borrow_path_as_mutable(span,
+                        &format!("immutable item `{}`",
+                                  self.describe_lvalue(lvalue)),
+                        Origin::Mir);
+                    err.span_label(span, "cannot borrow as mutable");
+
+                    if lvalue != lvalue_err {
+                        err.note(&format!("Value not mutable causing this error: `{}`",
+                            self.describe_lvalue(lvalue_err)));
+                    }
+
+                    err.emit();
+                }
+            },
+            _ => {}// Access authorized
+        }
+    }
+
+    /// Can this value be written or borrowed mutably
+    fn is_mutable<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> Result<(), &'d Lvalue<'tcx>> {
+        match *lvalue {
+            Lvalue::Local(local) => {
+                let local = &self.mir.local_decls[local];
+                match local.mutability {
+                    Mutability::Not => Err(lvalue),
+                    Mutability::Mut => Ok(())
+                }
+            },
+            Lvalue::Static(ref static_) => {
+                if !self.tcx.is_static_mut(static_.def_id) {
+                    Err(lvalue)
+                } else {
+                    Ok(())
+                }
+            },
+            Lvalue::Projection(ref proj) => {
+                match proj.elem {
+                    ProjectionElem::Deref => {
+                        let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+
+                        // `Box<T>` owns its content, so mutable if its location is mutable
+                        if base_ty.is_box() {
+                            return self.is_mutable(&proj.base);
+                        }
+
+                        // Otherwise we check the kind of deref to decide
+                        match base_ty.sty {
+                            ty::TyRef(_, tnm) => {
+                                match tnm.mutbl {
+                                    // Shared borrowed data is never mutable
+                                    hir::MutImmutable => Err(lvalue),
+                                    // Mutably borrowed data is mutable, but only if we have a
+                                    // unique path to the `&mut`
+                                    hir::MutMutable => self.is_unique(&proj.base),
+                                }
+                            },
+                            ty::TyRawPtr(tnm) => {
+                                match tnm.mutbl {
+                                    // `*const` raw pointers are not mutable
+                                    hir::MutImmutable => Err(lvalue),
+                                    // `*mut` raw pointers are always mutable, regardless of context
+                                    // The users have to check by themselve.
+                                    hir::MutMutable => Ok(()),
+                                }
+                            },
+                            // Deref should only be for reference, pointers or boxes
+                            _ => bug!("Deref of unexpected type: {:?}", base_ty)
+                        }
+                    },
+                    // All other projections are owned by their base path, so mutable if
+                    // base path is mutable
+                    ProjectionElem::Field(..) |
+                    ProjectionElem::Index(..) |
+                    ProjectionElem::ConstantIndex{..} |
+                    ProjectionElem::Subslice{..} |
+                    ProjectionElem::Downcast(..) =>
+                        self.is_mutable(&proj.base)
+                }
+            }
+        }
+    }
+
+    /// Does this lvalue have a unique path
+    fn is_unique<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> Result<(), &'d Lvalue<'tcx>> {
+        match *lvalue {
+            Lvalue::Local(..) => {
+                // Local variables are unique
+                Ok(())
+            },
+            Lvalue::Static(..) => {
+                // Static variables are not
+                Err(lvalue)
+            },
+            Lvalue::Projection(ref proj) => {
+                match proj.elem {
+                    ProjectionElem::Deref => {
+                        let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+
+                        // `Box<T>` referent is unique if box is a unique spot
+                        if base_ty.is_box() {
+                            return self.is_unique(&proj.base);
+                        }
+
+                        // Otherwise we check the kind of deref to decide
+                        match base_ty.sty {
+                            ty::TyRef(_, tnm) => {
+                                match tnm.mutbl {
+                                    // lvalue represent an aliased location
+                                    hir::MutImmutable => Err(lvalue),
+                                    // `&mut T` is as unique as the context in which it is found
+                                    hir::MutMutable => self.is_unique(&proj.base),
+                                }
+                            },
+                            ty::TyRawPtr(tnm) => {
+                                match tnm.mutbl {
+                                    // `*mut` can be aliased, but we leave it to user
+                                    hir::MutMutable => Ok(()),
+                                    // `*const` is treated the same as `*mut`
+                                    hir::MutImmutable => Ok(()),
+                                }
+                            },
+                            // Deref should only be for reference, pointers or boxes
+                            _ => bug!("Deref of unexpected type: {:?}", base_ty)
+                        }
+                    },
+                    // Other projections are unique if the base is unique
+                    ProjectionElem::Field(..) |
+                    ProjectionElem::Index(..) |
+                    ProjectionElem::ConstantIndex{..} |
+                    ProjectionElem::Subslice{..} |
+                    ProjectionElem::Downcast(..) =>
+                        self.is_unique(&proj.base)
+                }
+            }
+        }
+    }
 }
 
-impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum NoMovePathFound {
+    ReachedStatic,
+}
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, '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<'cx, 'gcx, 'tcx>,
                                      mut op: F)
-        where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'gcx>, &Lvalue) -> Control
+        where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Lvalue<'tcx>) -> Control
     {
         let (access, lvalue) = access_lvalue;
 
@@ -837,32 +1188,39 @@ mod prefixes {
     }
 
 
-    pub(super) struct Prefixes<'c, 'tcx: 'c> {
-        mir: &'c Mir<'tcx>,
-        tcx: TyCtxt<'c, 'tcx, 'tcx>,
+    pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+        mir: &'cx Mir<'tcx>,
+        tcx: TyCtxt<'cx, 'gcx, 'tcx>,
         kind: PrefixSet,
-        next: Option<&'c Lvalue<'tcx>>,
+        next: Option<&'cx Lvalue<'tcx>>,
     }
 
     #[derive(Copy, Clone, PartialEq, Eq, Debug)]
     pub(super) enum PrefixSet {
+        /// Doesn't stop until it returns the base case (a Local or
+        /// Static prefix).
         All,
+        /// Stops at any dereference.
         Shallow,
+        /// Stops at the deref of a shared reference.
         Supporting,
     }
 
-    impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
-        pub(super) fn prefixes<'d>(&self,
-                                   lvalue: &'d Lvalue<'gcx>,
-                                   kind: PrefixSet)
-                                   -> Prefixes<'d, 'gcx> where 'b: 'd
+    impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+        /// Returns an iterator over the prefixes of `lvalue`
+        /// (inclusive) from longest to smallest, potentially
+        /// terminating the iteration early based on `kind`.
+        pub(super) fn prefixes(&self,
+                               lvalue: &'cx Lvalue<'tcx>,
+                               kind: PrefixSet)
+                               -> Prefixes<'cx, 'gcx, 'tcx>
         {
             Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx }
         }
     }
 
-    impl<'c, 'tcx> Iterator for Prefixes<'c, 'tcx> {
-        type Item = &'c Lvalue<'tcx>;
+    impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> {
+        type Item = &'cx Lvalue<'tcx>;
         fn next(&mut self) -> Option<Self::Item> {
             let mut cursor = match self.next {
                 None => return None,
@@ -955,24 +1313,54 @@ mod prefixes {
     }
 }
 
-impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
-    fn report_use_of_moved(&mut self,
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    fn report_use_of_moved_or_uninitialized(&mut self,
                            _context: Context,
                            desired_action: &str,
-                           (lvalue, span): (&Lvalue, Span)) {
-        self.tcx.cannot_act_on_uninitialized_variable(span,
-                                                      desired_action,
-                                                      &self.describe_lvalue(lvalue),
-                                                      Origin::Mir)
-                .span_label(span, format!("use of possibly uninitialized `{}`",
-                                          self.describe_lvalue(lvalue)))
-                .emit();
+                           (lvalue, span): (&Lvalue<'tcx>, Span),
+                           mpi: MovePathIndex,
+                           curr_move_out: &IdxSetBuf<MoveOutIndex>) {
+
+        let mois = self.move_data.path_map[mpi].iter().filter(
+            |moi| curr_move_out.contains(moi)).collect::<Vec<_>>();
+
+        if mois.is_empty() {
+            self.tcx.cannot_act_on_uninitialized_variable(span,
+                                                          desired_action,
+                                                          &self.describe_lvalue(lvalue),
+                                                          Origin::Mir)
+                    .span_label(span, format!("use of possibly uninitialized `{}`",
+                                              self.describe_lvalue(lvalue)))
+                    .emit();
+        } else {
+            let msg = ""; //FIXME: add "partially " or "collaterally "
+
+            let mut err = self.tcx.cannot_act_on_moved_value(span,
+                                                             desired_action,
+                                                             msg,
+                                                             &self.describe_lvalue(lvalue),
+                                                             Origin::Mir);
+            err.span_label(span, format!("value {} here after move", desired_action));
+            for moi in mois {
+                let move_msg = ""; //FIXME: add " (into closure)"
+                let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span;
+                if span == move_span {
+                    err.span_label(span,
+                                   format!("value moved{} here in previous iteration of loop",
+                                           move_msg));
+                } else {
+                    err.span_label(move_span, format!("value moved{} here", move_msg));
+                };
+            }
+            //FIXME: add note for closure
+            err.emit();
+        }
     }
 
     fn report_move_out_while_borrowed(&mut self,
                                       _context: Context,
-                                      (lvalue, span): (&Lvalue, Span),
-                                      borrow: &BorrowData) {
+                                      (lvalue, span): (&Lvalue<'tcx>, Span),
+                                      borrow: &BorrowData<'tcx>) {
         self.tcx.cannot_move_when_borrowed(span,
                                            &self.describe_lvalue(lvalue),
                                            Origin::Mir)
@@ -986,8 +1374,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
     fn report_use_while_mutably_borrowed(&mut self,
                                          _context: Context,
-                                         (lvalue, span): (&Lvalue, Span),
-                                         borrow : &BorrowData) {
+                                         (lvalue, span): (&Lvalue<'tcx>, Span),
+                                         borrow : &BorrowData<'tcx>) {
 
         let mut err = self.tcx.cannot_use_when_mutably_borrowed(
             span, &self.describe_lvalue(lvalue),
@@ -997,13 +1385,77 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
         err.emit();
     }
 
+    /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
+    /// the local assigned at `location`.
+    /// This is done by searching in statements succeeding `location`
+    /// and originating from `maybe_closure_span`.
+    fn find_closure_span(
+        &self,
+        maybe_closure_span: Span,
+        location: Location,
+    ) -> Option<(Span, Span)> {
+        use rustc::hir::ExprClosure;
+        use rustc::mir::AggregateKind;
+
+        let local = if let StatementKind::Assign(Lvalue::Local(local), _) =
+            self.mir[location.block].statements[location.statement_index].kind
+        {
+            local
+        } else {
+            return None;
+        };
+
+        for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
+            if maybe_closure_span != stmt.source_info.span {
+                break;
+            }
+
+            if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref lvs)) = stmt.kind {
+                if let AggregateKind::Closure(def_id, _) = **kind {
+                    debug!("find_closure_span: found closure {:?}", lvs);
+
+                    return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
+                        let args_span = if let ExprClosure(_, _, _, span, _) =
+                            self.tcx.hir.expect_expr(node_id).node
+                        {
+                            span
+                        } else {
+                            return None;
+                        };
+
+                        self.tcx
+                            .with_freevars(node_id, |freevars| {
+                                for (v, lv) in freevars.iter().zip(lvs) {
+                                    if let Operand::Consume(Lvalue::Local(l)) = *lv {
+                                        if local == l {
+                                            debug!(
+                                                "find_closure_span: found captured local {:?}",
+                                                l
+                                            );
+                                            return Some(v.span);
+                                        }
+                                    }
+                                }
+                                None
+                            })
+                            .map(|var_span| (args_span, var_span))
+                    } else {
+                        None
+                    };
+                }
+            }
+        }
+
+        None
+    }
+
     fn report_conflicting_borrow(&mut self,
-                                 _context: Context,
-                                 common_prefix: &Lvalue,
-                                 (lvalue, span): (&Lvalue, Span),
+                                 context: Context,
+                                 common_prefix: &Lvalue<'tcx>,
+                                 (lvalue, span): (&Lvalue<'tcx>, 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));
@@ -1011,44 +1463,87 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
         let issued_span = self.retrieve_borrow_span(issued_borrow);
 
+        let new_closure_span = self.find_closure_span(span, context.loc);
+        let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
+        let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location);
+        let issued_span = old_closure_span.map(|(args, _)| args).unwrap_or(issued_span);
+
+        let desc_lvalue = self.describe_lvalue(lvalue);
+
         // FIXME: supply non-"" `opt_via` when appropriate
         let mut err = match (gen_borrow_kind, "immutable", "mutable",
                              issued_borrow.kind, "immutable", "mutable") {
             (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
             (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) =>
                 self.tcx.cannot_reborrow_already_borrowed(
-                    span, &self.describe_lvalue(lvalue), "", lft, issued_span,
+                    span, &desc_lvalue, "", lft, issued_span,
                     "it", rgt, "", end_issued_loan_span, Origin::Mir),
 
             (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) =>
                 self.tcx.cannot_mutably_borrow_multiply(
-                    span, &self.describe_lvalue(lvalue), "", issued_span,
+                    span, &desc_lvalue, "", issued_span,
                     "", end_issued_loan_span, Origin::Mir),
 
             (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) =>
                 self.tcx.cannot_uniquely_borrow_by_two_closures(
-                    span, &self.describe_lvalue(lvalue), issued_span,
+                    span, &desc_lvalue, issued_span,
                     end_issued_loan_span, Origin::Mir),
 
             (BorrowKind::Unique, _, _, _, _, _) =>
                 self.tcx.cannot_uniquely_borrow_by_one_closure(
-                    span, &self.describe_lvalue(lvalue), "",
+                    span, &desc_lvalue, "",
                     issued_span, "it", "", end_issued_loan_span, Origin::Mir),
 
             (_, _, _, BorrowKind::Unique, _, _) =>
                 self.tcx.cannot_reborrow_already_uniquely_borrowed(
-                    span, &self.describe_lvalue(lvalue), "it", "",
+                    span, &desc_lvalue, "it", "",
                     issued_span, "", end_issued_loan_span, Origin::Mir),
 
             (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) =>
                 unreachable!(),
         };
+
+        if let Some((_, var_span)) = old_closure_span {
+            err.span_label(
+                var_span,
+                format!("previous borrow occurs due to use of `{}` in closure", desc_lvalue),
+            );
+        }
+
+        if let Some((_, var_span)) = new_closure_span {
+            err.span_label(
+                var_span,
+                format!("borrow occurs due to use of `{}` in closure", desc_lvalue),
+            );
+        }
+
+        err.emit();
+    }
+
+    fn report_borrowed_value_does_not_live_long_enough(&mut self,
+                                                       _: Context,
+                                                       (lvalue, span): (&Lvalue, Span),
+                                                       end_span: Option<Span>) {
+        let proper_span = match *lvalue {
+            Lvalue::Local(local) => self.mir.local_decls[local].source_info.span,
+            _ => span
+        };
+
+        let mut err = self.tcx.path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
+        err.span_label(proper_span, "temporary value created here");
+        err.span_label(span, "temporary value dropped here while still borrowed");
+        err.note("consider using a `let` binding to increase its lifetime");
+
+        if let Some(end) = end_span {
+            err.span_label(end, "temporary value needs to live until here");
+        }
+
         err.emit();
     }
 
     fn report_illegal_mutation_of_borrowed(&mut self,
                                            _: Context,
-                                           (lvalue, span): (&Lvalue, Span),
+                                           (lvalue, span): (&Lvalue<'tcx>, Span),
                                            loan: &BorrowData) {
         let mut err = self.tcx.cannot_assign_to_borrowed(
             span, self.retrieve_borrow_span(loan), &self.describe_lvalue(lvalue), Origin::Mir);
@@ -1058,74 +1553,173 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
     fn report_illegal_reassignment(&mut self,
                                    _context: Context,
-                                   (lvalue, span): (&Lvalue, Span),
+                                   (lvalue, span): (&Lvalue<'tcx>, Span),
                                    assigned_span: Span) {
         self.tcx.cannot_reassign_immutable(span,
                                            &self.describe_lvalue(lvalue),
                                            Origin::Mir)
-                .span_label(span, "re-assignment of immutable variable")
+                .span_label(span, "cannot assign twice to immutable variable")
                 .span_label(assigned_span, format!("first assignment to `{}`",
                                                    self.describe_lvalue(lvalue)))
                 .emit();
     }
 
-    fn report_assignment_to_static(&mut self, _context: Context, (lvalue, span): (&Lvalue, Span)) {
+    fn report_assignment_to_static(&mut self,
+                                   _context: Context,
+                                   (lvalue, span): (&Lvalue<'tcx>, Span)) {
         let mut err = self.tcx.cannot_assign_static(
             span, &self.describe_lvalue(lvalue), Origin::Mir);
         err.emit();
     }
 }
 
-impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     // End-user visible description of `lvalue`
-    fn describe_lvalue(&self, lvalue: &Lvalue) -> String {
+    fn describe_lvalue(&self, lvalue: &Lvalue<'tcx>) -> String {
         let mut buf = String::new();
-        self.append_lvalue_to_string(lvalue, &mut buf);
+        self.append_lvalue_to_string(lvalue, &mut buf, None);
         buf
     }
 
     // Appends end-user visible description of `lvalue` to `buf`.
-    fn append_lvalue_to_string(&self, lvalue: &Lvalue, buf: &mut String) {
+    fn append_lvalue_to_string(&self,
+                               lvalue: &Lvalue<'tcx>,
+                               buf: &mut String,
+                               autoderef: Option<bool>) {
         match *lvalue {
             Lvalue::Local(local) => {
-                let local = &self.mir.local_decls[local];
-                match local.name {
-                    Some(name) => buf.push_str(&format!("{}", name)),
-                    None => buf.push_str("_"),
-                }
+                self.append_local_to_string(local, buf, "_");
             }
             Lvalue::Static(ref static_) => {
                 buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
             }
             Lvalue::Projection(ref proj) => {
-                let (prefix, suffix, index_operand) = match proj.elem {
-                    ProjectionElem::Deref =>
-                        ("(*", format!(")"), None),
-                    ProjectionElem::Downcast(..) =>
-                        ("",   format!(""), None), // (dont emit downcast info)
-                    ProjectionElem::Field(field, _ty) =>
-                        ("",   format!(".{}", field.index()), None), // FIXME: report name of field
-                    ProjectionElem::Index(index) =>
-                        ("",   format!(""), Some(index)),
-                    ProjectionElem::ConstantIndex { offset, min_length, from_end: true } =>
-                        ("",   format!("[{} of {}]", offset, min_length), None),
-                    ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>
-                        ("",   format!("[-{} of {}]", offset, min_length), None),
-                    ProjectionElem::Subslice { from, to: 0 } =>
-                        ("",   format!("[{}:]", from), None),
-                    ProjectionElem::Subslice { from: 0, to } =>
-                        ("",   format!("[:-{}]", to), None),
-                    ProjectionElem::Subslice { from, to } =>
-                        ("",   format!("[{}:-{}]", from, to), None),
+                let mut autoderef = autoderef.unwrap_or(false);
+
+                match proj.elem {
+                    ProjectionElem::Deref => {
+                        if autoderef {
+                            self.append_lvalue_to_string(&proj.base, buf, Some(autoderef));
+                        } else {
+                            buf.push_str(&"(*");
+                            self.append_lvalue_to_string(&proj.base, buf, Some(autoderef));
+                            buf.push_str(&")");
+                        }
+                    },
+                    ProjectionElem::Downcast(..) => {
+                        self.append_lvalue_to_string(&proj.base, buf, Some(autoderef));
+                    },
+                    ProjectionElem::Field(field, _ty) => {
+                        autoderef = true;
+                        let is_projection_from_ty_closure = proj.base.ty(self.mir, self.tcx)
+                                .to_ty(self.tcx).is_closure();
+
+                        let field_name = self.describe_field(&proj.base, field.index());
+                        if is_projection_from_ty_closure {
+                            buf.push_str(&format!("{}", field_name));
+                        } else {
+                            self.append_lvalue_to_string(&proj.base, buf, Some(autoderef));
+                            buf.push_str(&format!(".{}", field_name));
+                        }
+                    },
+                    ProjectionElem::Index(index) => {
+                        autoderef = true;
+
+                        self.append_lvalue_to_string(&proj.base, buf, Some(autoderef));
+                        buf.push_str("[");
+                        self.append_local_to_string(index, buf, "..");
+                        buf.push_str("]");
+                    },
+                    ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
+                        autoderef = true;
+                        // Since it isn't possible to borrow an element on a particular index and
+                        // then use another while the borrow is held, don't output indices details
+                        // to avoid confusing the end-user
+                        self.append_lvalue_to_string(&proj.base, buf, Some(autoderef));
+                        buf.push_str(&"[..]");
+                    },
                 };
-                buf.push_str(prefix);
-                self.append_lvalue_to_string(&proj.base, buf);
-                if let Some(index) = index_operand {
-                    buf.push_str("[");
-                    self.append_lvalue_to_string(&Lvalue::Local(index), buf);
-                    buf.push_str("]");
-                } else {
-                    buf.push_str(&suffix);
+            }
+        }
+    }
+
+    // Appends end-user visible description of the `local` lvalue to `buf`. If `local` doesn't have
+    // a name, then `none_string` is appended instead
+    fn append_local_to_string(&self, local_index: Local, buf: &mut String, none_string: &str) {
+        let local = &self.mir.local_decls[local_index];
+        match local.name {
+            Some(name) => buf.push_str(&format!("{}", name)),
+            None => buf.push_str(none_string)
+        }
+    }
+
+    // FIXME Instead of passing usize, Field should be passed
+    // End-user visible description of the `field_index`nth field of `base`
+    fn describe_field(&self, base: &Lvalue, field_index: usize) -> String {
+        match *base {
+            Lvalue::Local(local) => {
+                let local = &self.mir.local_decls[local];
+                self.describe_field_from_ty(&local.ty, field_index)
+            },
+            Lvalue::Static(ref static_) => {
+                self.describe_field_from_ty(&static_.ty, field_index)
+            },
+            Lvalue::Projection(ref proj) => {
+                match proj.elem {
+                    ProjectionElem::Deref =>
+                        self.describe_field(&proj.base, field_index),
+                    ProjectionElem::Downcast(def, variant_index) =>
+                        format!("{}", def.variants[variant_index].fields[field_index].name),
+                    ProjectionElem::Field(_, field_type) =>
+                        self.describe_field_from_ty(&field_type, field_index),
+                    ProjectionElem::Index(..)
+                    | ProjectionElem::ConstantIndex { .. }
+                    | ProjectionElem::Subslice { .. } =>
+                        format!("{}", self.describe_field(&proj.base, field_index)),
+                }
+            }
+        }
+    }
+
+    // End-user visible description of the `field_index`nth field of `ty`
+    fn describe_field_from_ty(&self, ty: &ty::Ty, field_index: usize) -> String {
+        if ty.is_box() {
+            // If the type is a box, the field is described from the boxed type
+            self.describe_field_from_ty(&ty.boxed_ty(), field_index)
+        }
+        else {
+            match ty.sty {
+                ty::TyAdt(def, _) => {
+                    if def.is_enum() {
+                        format!("{}", field_index)
+                    }
+                    else {
+                        format!("{}", def.struct_variant().fields[field_index].name)
+                    }
+                },
+                ty::TyTuple(_, _) => {
+                    format!("{}", field_index)
+                },
+                ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => {
+                    self.describe_field_from_ty(&tnm.ty, field_index)
+                },
+                ty::TyArray(ty, _) | ty::TySlice(ty) => {
+                    self.describe_field_from_ty(&ty, field_index)
+                },
+                ty::TyClosure(closure_def_id, _) => {
+                    // Convert the def-id into a node-id. node-ids are only valid for
+                    // the local code in the current crate, so this returns an `Option` in case
+                    // the closure comes from another crate. But in that case we wouldn't
+                    // be borrowck'ing it, so we can just unwrap:
+                    let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
+                    let freevar = self.tcx.with_freevars(node_id, |fv| fv[field_index]);
+
+                    self.tcx.hir.name(freevar.var_id()).to_string()
+                 }
+                _ => {
+                    // Might need a revision when the fields in trait RFC is implemented
+                    // (https://github.com/rust-lang/rfcs/pull/1546)
+                    bug!("End-user description not implemented for field access on `{:?}`", ty.sty);
                 }
             }
         }
@@ -1137,13 +1731,13 @@ 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> {
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     // FIXME (#16118): function intended to allow the borrow checker
     // to be less precise in its handling of Box while still allowing
     // 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.
@@ -1192,29 +1786,34 @@ 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>>,
+                      move_out: DataflowResults<MovingOutStatements<'b, 'gcx, 'tcx>>)
                       -> Self {
         InProgress {
             borrows: FlowInProgress::new(borrows),
             inits: FlowInProgress::new(inits),
             uninits: FlowInProgress::new(uninits),
+            move_outs: FlowInProgress::new(move_out)
         }
     }
 
-    fn each_flow<XB, XI, XU>(&mut self,
-                             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>>),
+    fn each_flow<XB, XI, XU, XM>(&mut self,
+                                 mut xform_borrows: XB,
+                                 mut xform_inits: XI,
+                                 mut xform_uninits: XU,
+                                 mut xform_move_outs: XM) where
+        XB: FnMut(&mut FlowInProgress<Borrows<'b, 'gcx, 'tcx>>),
+        XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>),
+        XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>),
+        XM: FnMut(&mut FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>),
     {
         xform_borrows(&mut self.borrows);
         xform_inits(&mut self.inits);
         xform_uninits(&mut self.uninits);
+        xform_move_outs(&mut self.move_outs);
     }
 
     fn summary(&self) -> String {
@@ -1260,12 +1859,52 @@ impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
                 &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit];
             s.push_str(&format!("{}", move_path));
         });
+        s.push_str("] ");
+
+        s.push_str("move_out: [");
+        let mut saw_one = false;
+        self.move_outs.each_state_bit(|mpi_move_out| {
+            if saw_one { s.push_str(", "); };
+            saw_one = true;
+            let move_out =
+                &self.move_outs.base_results.operator().move_data().moves[mpi_move_out];
+            s.push_str(&format!("{:?}", move_out));
+        });
         s.push_str("]");
 
         return s;
     }
 }
 
+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();
+
+        let mut todo = vec![mpi];
+        let mut push_siblings = false; // don't look at siblings of original `mpi`.
+        while let Some(mpi) = todo.pop() {
+            if self.curr_state.contains(&mpi) {
+                return Some(mpi);
+            }
+            let move_path = &move_data.move_paths[mpi];
+            if let Some(child) = move_path.first_child {
+                todo.push(child);
+            }
+            if push_siblings {
+                if let Some(sibling) = move_path.next_sibling {
+                    todo.push(sibling);
+                }
+            } else {
+                // after we've processed the original `mpi`, we should
+                // always traverse the siblings of any of its
+                // children.
+                push_siblings = true;
+            }
+        }
+        return None;
+    }
+}
+
 impl<BD> FlowInProgress<BD> where BD: BitDenotation {
     fn each_state_bit<F>(&self, f: F) where F: FnMut(BD::Idx) {
         self.curr_state.each_bit(self.base_results.operator().bits_per_block(), f)