about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2017-11-11 13:38:07 +0100
committerGitHub <noreply@github.com>2017-11-11 13:38:07 +0100
commit5d07d73ffbf0d2f4bc316ab0c7c2b1ea6988fc5d (patch)
tree379c4369d4888545278b78b5295abcfe20b80549 /src
parentfec24adea8a16525b7e091e2abc7b7a67b1c84d0 (diff)
parent3acb4e9e4a30cd5c413b094a0821426951d0745c (diff)
downloadrust-5d07d73ffbf0d2f4bc316ab0c7c2b1ea6988fc5d.tar.gz
rust-5d07d73ffbf0d2f4bc316ab0c7c2b1ea6988fc5d.zip
Rollup merge of #45877 - mikhail-m1:mir-borrowck-act-on-moved, r=arielb1
restore move out dataflow, add report of move out errors

fix https://github.com/rust-lang/rust/issues/45363
r? @arielb1
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/borrow_check.rs107
-rw-r--r--src/librustc_mir/dataflow/impls/mod.rs175
-rw-r--r--src/librustc_mir/dataflow/mod.rs2
-rw-r--r--src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs2
-rw-r--r--src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs4
-rw-r--r--src/test/ui/borrowck/borrowck-reinit.rs19
-rw-r--r--src/test/ui/borrowck/borrowck-reinit.stderr20
7 files changed, 301 insertions, 28 deletions
diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs
index 2cb1a23ef5a..d2524b306cf 100644
--- a/src/librustc_mir/borrow_check.rs
+++ b/src/librustc_mir/borrow_check.rs
@@ -30,9 +30,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};
@@ -129,6 +130,9 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     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,
@@ -141,7 +145,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
 
     let mut state = InProgress::new(flow_borrows,
                                     flow_inits,
-                                    flow_uninits);
+                                    flow_uninits,
+                                    flow_move_outs);
 
     mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
 }
@@ -161,6 +166,7 @@ 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 {
@@ -185,7 +191,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx>
     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,
@@ -193,7 +200,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx>
                                     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,
@@ -201,7 +209,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx>
                           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,
@@ -209,7 +218,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx>
                                      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,
@@ -671,6 +681,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
         let lvalue = self.base_path(lvalue_span.0);
 
         let maybe_uninits = &flow_state.uninits;
+        let curr_move_outs = &flow_state.move_outs.curr_state;
 
         // Bad scenarios:
         //
@@ -712,7 +723,9 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
         match self.move_path_closest_to(lvalue) {
             Ok(mpi) => {
                 if maybe_uninits.curr_state.contains(&mpi) {
-                    self.report_use_of_moved(context, desired_action, lvalue_span);
+                    self.report_use_of_moved_or_uninitialized(context, desired_action,
+                                                              lvalue_span, mpi,
+                                                              curr_move_outs);
                     return; // don't bother finding other problems.
                 }
             }
@@ -737,8 +750,10 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
         debug!("check_if_path_is_moved part2 lvalue: {:?}", lvalue);
         if let Some(mpi) = self.move_path_for_lvalue(lvalue) {
-            if let Some(_) = maybe_uninits.has_any_child_of(mpi) {
-                self.report_use_of_moved(context, desired_action, lvalue_span);
+            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.
             }
         }
@@ -1083,17 +1098,47 @@ mod prefixes {
 }
 
 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
-    fn report_use_of_moved(&mut self,
+    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, 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,
@@ -1396,26 +1441,31 @@ 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>>)
+                      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
+    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 {
@@ -1461,6 +1511,17 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, '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;
diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs
index af99706be81..dad96dc3a6f 100644
--- a/src/librustc_mir/dataflow/impls/mod.rs
+++ b/src/librustc_mir/dataflow/impls/mod.rs
@@ -14,13 +14,16 @@
 
 use rustc::ty::TyCtxt;
 use rustc::mir::{self, Mir, Location};
+use rustc_data_structures::bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
 use rustc_data_structures::bitslice::{BitwiseOperator};
 use rustc_data_structures::indexed_set::{IdxSet};
+use rustc_data_structures::indexed_vec::Idx;
 
 use super::MoveDataParamEnv;
 use util::elaborate_drops::DropFlagState;
 
-use super::move_paths::{HasMoveData, MoveData, MovePathIndex};
+use super::move_paths::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex};
+use super::move_paths::LookupResult;
 use super::{BitDenotation, BlockSets, DataflowOperator};
 
 use super::drop_flag_effects_for_function_entry;
@@ -205,6 +208,40 @@ impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'g
     fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
 }
 
+/// `MovingOutStatements` tracks the statements that perform moves out
+/// of particular l-values. More precisely, it tracks whether the
+/// *effect* of such moves (namely, the uninitialization of the
+/// l-value in question) can reach some point in the control-flow of
+/// the function, or if that effect is "killed" by some intervening
+/// operation reinitializing that l-value.
+///
+/// The resulting dataflow is a more enriched version of
+/// `MaybeUninitializedLvals`. Both structures on their own only tell
+/// you if an l-value *might* be uninitialized at a given point in the
+/// control flow. But `MovingOutStatements` also includes the added
+/// data of *which* particular statement causing the deinitialization
+/// that the borrow checker's error message may need to report.
+#[allow(dead_code)]
+pub struct MovingOutStatements<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    mir: &'a Mir<'tcx>,
+    mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
+}
+
+impl<'a, 'gcx: 'tcx, 'tcx: 'a> MovingOutStatements<'a, 'gcx, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+               mir: &'a Mir<'tcx>,
+               mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
+               -> Self
+    {
+        MovingOutStatements { tcx: tcx, mir: mir, mdpe: mdpe }
+    }
+}
+
+impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'gcx, 'tcx> {
+    fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
+}
+
 impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
     fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
                    state: DropFlagState)
@@ -399,6 +436,128 @@ impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'gcx, 'tcx
     }
 }
 
+impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
+    type Idx = MoveOutIndex;
+    fn name() -> &'static str { "moving_out" }
+    fn bits_per_block(&self) -> usize {
+        self.move_data().moves.len()
+    }
+
+    fn start_block_effect(&self, _sets: &mut BlockSets<MoveOutIndex>) {
+        // no move-statements have been executed prior to function
+        // execution, so this method has no effect on `_sets`.
+    }
+    fn statement_effect(&self,
+                        sets: &mut BlockSets<MoveOutIndex>,
+                        location: Location) {
+        let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data());
+        let stmt = &mir[location.block].statements[location.statement_index];
+        let loc_map = &move_data.loc_map;
+        let path_map = &move_data.path_map;
+        let rev_lookup = &move_data.rev_lookup;
+
+        debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
+               stmt, location, &loc_map[location]);
+        for move_index in &loc_map[location] {
+            // Every path deinitialized by a *particular move*
+            // has corresponding bit, "gen'ed" (i.e. set)
+            // here, in dataflow vector
+            zero_to_one(sets.gen_set.words_mut(), *move_index);
+        }
+        let bits_per_block = self.bits_per_block();
+        match stmt.kind {
+            mir::StatementKind::SetDiscriminant { .. } => {
+                span_bug!(stmt.source_info.span, "SetDiscriminant should not exist in borrowck");
+            }
+            mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
+                // assigning into this `lvalue` kills all
+                // MoveOuts from it, and *also* all MoveOuts
+                // for children and associated fragment sets.
+                match rvalue.initialization_state() {
+                    mir::tcx::RvalueInitializationState::Shallow => {
+                        if let LookupResult::Exact(mpi) = rev_lookup.find(lvalue) {
+                             for moi in &path_map[mpi] {
+                                 assert!(moi.index() < bits_per_block);
+                                 sets.kill_set.add(&moi);
+                             }
+                        }
+                    }
+                    mir::tcx::RvalueInitializationState::Deep => {
+                        on_lookup_result_bits(tcx,
+                                              mir,
+                                              move_data,
+                                              rev_lookup.find(lvalue),
+                                              |mpi| for moi in &path_map[mpi] {
+                                                  assert!(moi.index() < bits_per_block);
+                                                  sets.kill_set.add(&moi);
+                                              });
+                    }
+                }
+            }
+            mir::StatementKind::StorageLive(_) |
+            mir::StatementKind::StorageDead(_) |
+            mir::StatementKind::InlineAsm { .. } |
+            mir::StatementKind::EndRegion(_) |
+            mir::StatementKind::Validate(..) |
+            mir::StatementKind::Nop => {}
+        }
+    }
+
+    fn terminator_effect(&self,
+                         sets: &mut BlockSets<MoveOutIndex>,
+                         location: Location)
+    {
+        let (mir, move_data) = (self.mir, self.move_data());
+        let term = mir[location.block].terminator();
+        let loc_map = &move_data.loc_map;
+        debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
+               term, location, &loc_map[location]);
+        let bits_per_block = self.bits_per_block();
+        for move_index in &loc_map[location] {
+            assert!(move_index.index() < bits_per_block);
+            zero_to_one(sets.gen_set.words_mut(), *move_index);
+        }
+        match term.kind {
+            mir::TerminatorKind::DropAndReplace { ref location, .. } => {
+                on_lookup_result_bits(self.tcx,
+                                      mir,
+                                      move_data,
+                                      move_data.rev_lookup.find(location),
+                                      |mpi| for moi in &move_data.path_map[mpi] {
+                                          assert!(moi.index() < bits_per_block);
+                                          sets.kill_set.add(&moi);
+                                      });
+            }
+            _ => {}
+        }
+    }
+
+    fn propagate_call_return(&self,
+                             in_out: &mut IdxSet<MoveOutIndex>,
+                             _call_bb: mir::BasicBlock,
+                             _dest_bb: mir::BasicBlock,
+                             dest_lval: &mir::Lvalue) {
+        let move_data = self.move_data();
+        let bits_per_block = self.bits_per_block();
+
+        let path_map = &move_data.path_map;
+        on_lookup_result_bits(self.tcx,
+                              self.mir,
+                              move_data,
+                              move_data.rev_lookup.find(dest_lval),
+                              |mpi| for moi in &path_map[mpi] {
+                                  assert!(moi.index() < bits_per_block);
+                                  in_out.remove(&moi);
+                              });
+    }
+}
+
+fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
+    let retval = bitvec.set_bit(move_index.index());
+    assert!(retval);
+}
+
+
 impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
     #[inline]
     fn join(&self, pred1: usize, pred2: usize) -> usize {
@@ -420,6 +579,13 @@ impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'gcx, 't
     }
 }
 
+impl<'a, 'gcx, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'gcx, 'tcx> {
+    #[inline]
+    fn join(&self, pred1: usize, pred2: usize) -> usize {
+        pred1 | pred2 // moves from both preds are in scope
+    }
+}
+
 // The way that dataflow fixed point iteration works, you want to
 // start at bottom and work your way to a fixed point. Control-flow
 // merges will apply the `join` operator to each block entry's current
@@ -450,3 +616,10 @@ impl<'a, 'gcx, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'gcx, '
         true // bottom = initialized (start_block_effect counters this at outset)
     }
 }
+
+impl<'a, 'gcx, 'tcx> DataflowOperator for MovingOutStatements<'a, 'gcx, 'tcx> {
+    #[inline]
+    fn bottom_value() -> bool {
+        false // bottom = no loans in scope by default
+    }
+}
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index f5517096e3a..bca9324d5b0 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -26,7 +26,7 @@ use std::usize;
 
 pub use self::impls::{MaybeStorageLive};
 pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals};
-pub use self::impls::{DefinitelyInitializedLvals};
+pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements};
 pub use self::impls::borrows::{Borrows, BorrowData, BorrowIndex};
 pub(crate) use self::drop_flag_effects::*;
 
diff --git a/src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs b/src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs
index 8bab6e8dfce..087ced01d8c 100644
--- a/src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs
+++ b/src/test/compile-fail/borrowck/borrowck-drop-from-guard.rs
@@ -19,6 +19,6 @@ fn main()
     match Some(42) {
         Some(_) if { drop(my_str); false } => {}
         Some(_) => {}
-        None => { foo(my_str); } //~ ERROR (Mir) [E0381]
+        None => { foo(my_str); } //~ ERROR (Mir) [E0382]
     }
 }
diff --git a/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs b/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs
index 957086f6af1..8f39ae6c04e 100644
--- a/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs
+++ b/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs
@@ -39,11 +39,11 @@ fn main() {
     let _moved = line1.origin;
     let _ = line1.origin.x + 1; //[ast]~ ERROR use of collaterally moved value: `line1.origin.x`
                                 //[mir]~^       [E0382]
-                                //[mir]~| (Mir) [E0381]
+                                //[mir]~| (Mir) [E0382]
 
     let mut line2 = Line::default();
     let _moved = (line2.origin, line2.middle);
     line2.consume(); //[ast]~ ERROR use of partially moved value: `line2` [E0382]
                      //[mir]~^       [E0382]
-                     //[mir]~| (Mir) [E0381]
+                     //[mir]~| (Mir) [E0382]
 }
diff --git a/src/test/ui/borrowck/borrowck-reinit.rs b/src/test/ui/borrowck/borrowck-reinit.rs
new file mode 100644
index 00000000000..5547cb3ae52
--- /dev/null
+++ b/src/test/ui/borrowck/borrowck-reinit.rs
@@ -0,0 +1,19 @@
+// Copyright 2015 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 borrowck-mir -Z emit-end-regions
+
+fn main() {
+    let mut x = Box::new(0);
+    let _u = x; // error shouldn't note this move
+    x = Box::new(1);
+    drop(x);
+    let _ = (1,x);
+}
diff --git a/src/test/ui/borrowck/borrowck-reinit.stderr b/src/test/ui/borrowck/borrowck-reinit.stderr
new file mode 100644
index 00000000000..767d65c1e60
--- /dev/null
+++ b/src/test/ui/borrowck/borrowck-reinit.stderr
@@ -0,0 +1,20 @@
+error[E0382]: use of moved value: `x` (Ast)
+  --> $DIR/borrowck-reinit.rs:18:16
+   |
+17 |     drop(x);
+   |          - value moved here
+18 |     let _ = (1,x);
+   |                ^ value used here after move
+   |
+   = note: move occurs because `x` has type `std::boxed::Box<i32>`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `x` (Mir)
+  --> $DIR/borrowck-reinit.rs:18:16
+   |
+17 |     drop(x);
+   |          - value moved here
+18 |     let _ = (1,x);
+   |                ^ value use here after move
+
+error: aborting due to 2 previous errors
+