about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAriel Ben-Yehuda <ariel.byd@gmail.com>2016-06-11 23:47:28 +0300
committerAriel Ben-Yehuda <arielb1@mail.tau.ac.il>2016-09-16 15:08:32 +0300
commiteb19cd65756cd285c81410e627752a75a41e3f0e (patch)
tree03353acf24ab761d9228426180779fd2691db9a8
parent89500e934134d19b09e51a1f45430ded65e291b4 (diff)
downloadrust-eb19cd65756cd285c81410e627752a75a41e3f0e.tar.gz
rust-eb19cd65756cd285c81410e627752a75a41e3f0e.zip
groundwork refactoring of `gather_moves`
-rw-r--r--src/librustc/mir/repr.rs7
-rw-r--r--src/librustc/mir/visit.rs3
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/impls.rs47
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs24
-rw-r--r--src/librustc_borrowck/borrowck/mir/elaborate_drops.rs161
-rw-r--r--src/librustc_borrowck/borrowck/mir/gather_moves.rs875
-rw-r--r--src/librustc_borrowck/borrowck/mir/mod.rs70
-rw-r--r--src/librustc_trans/mir/analyze.rs1
8 files changed, 480 insertions, 708 deletions
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index a2abaa5e12f..53b6ccdbd53 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -1243,7 +1243,7 @@ impl<'a, 'b>  GraphSuccessors<'b> for Mir<'a> {
     type Iter = IntoIter<BasicBlock>;
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
 pub struct Location {
     /// the location is within this block
     pub block: BasicBlock,
@@ -1253,3 +1253,8 @@ pub struct Location {
     pub statement_index: usize,
 }
 
+impl fmt::Debug for Location {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "{:?}[{}]", self.block, self.statement_index)
+    }
+}
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index c2d0b2c686e..16e0b376f4b 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -774,9 +774,6 @@ pub enum LvalueContext<'tcx> {
     // Being borrowed
     Borrow { region: &'tcx Region, kind: BorrowKind },
 
-    // Being sliced -- this should be same as being borrowed, probably
-    Slice { from_start: usize, from_end: usize },
-
     // Used as base for another lvalue, e.g. `x` in `x.y`
     Projection,
 
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
index c46daf9c225..8ac59c60396 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
@@ -17,7 +17,7 @@ use super::super::MoveDataParamEnv;
 use super::super::DropFlagState;
 use super::super::drop_flag_effects_for_function_entry;
 use super::super::drop_flag_effects_for_location;
-use super::super::on_all_children_bits;
+use super::super::on_lookup_result_bits;
 
 use super::{BitDenotation, BlockSets, DataflowOperator};
 
@@ -277,10 +277,9 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
                              dest_lval: &repr::Lvalue) {
         // when a call returns successfully, that means we need to set
         // the bits for that dest_lval to 1 (initialized).
-        let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval);
-        on_all_children_bits(self.tcx, self.mir, &ctxt.move_data,
-                             move_path_index,
-                             |mpi| { in_out.add(&mpi); });
+        on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data,
+                              ctxt.move_data.rev_lookup.find(dest_lval),
+                              |mpi| { in_out.add(&mpi); });
     }
 }
 
@@ -338,11 +337,10 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
                              _dest_bb: repr::BasicBlock,
                              dest_lval: &repr::Lvalue) {
         // when a call returns successfully, that means we need to set
-        // the bits for that dest_lval to 1 (initialized).
-        let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval);
-        on_all_children_bits(self.tcx, self.mir, &ctxt.move_data,
-                             move_path_index,
-                             |mpi| { in_out.remove(&mpi); });
+        // the bits for that dest_lval to 0 (initialized).
+        on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data,
+                              ctxt.move_data.rev_lookup.find(dest_lval),
+                              |mpi| { in_out.remove(&mpi); });
     }
 }
 
@@ -400,10 +398,9 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
                              dest_lval: &repr::Lvalue) {
         // when a call returns successfully, that means we need to set
         // the bits for that dest_lval to 1 (initialized).
-        let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval);
-        on_all_children_bits(self.tcx, self.mir, &ctxt.move_data,
-                             move_path_index,
-                             |mpi| { in_out.add(&mpi); });
+        on_lookup_result_bits(self.tcx, self.mir, &ctxt.move_data,
+                              ctxt.move_data.rev_lookup.find(dest_lval),
+                              |mpi| { in_out.add(&mpi); });
     }
 }
 
@@ -448,11 +445,10 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
                 // assigning into this `lvalue` kills all
                 // MoveOuts from it, and *also* all MoveOuts
                 // for children and associated fragment sets.
-                let move_path_index = rev_lookup.find(lvalue);
-                on_all_children_bits(tcx,
+                on_lookup_result_bits(tcx,
                                      mir,
                                      move_data,
-                                     move_path_index,
+                                     rev_lookup.find(lvalue),
                                      |mpi| for moi in &path_map[mpi] {
                                          assert!(moi.index() < bits_per_block);
                                          sets.kill_set.add(&moi);
@@ -489,18 +485,17 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
                              _dest_bb: repr::BasicBlock,
                              dest_lval: &repr::Lvalue) {
         let move_data = &ctxt.move_data;
-        let move_path_index = move_data.rev_lookup.find(dest_lval);
         let bits_per_block = self.bits_per_block(ctxt);
 
         let path_map = &move_data.path_map;
-        on_all_children_bits(self.tcx,
-                             self.mir,
-                             move_data,
-                             move_path_index,
-                             |mpi| for moi in &path_map[mpi] {
-                                 assert!(moi.index() < bits_per_block);
-                                 in_out.remove(&moi);
-                             });
+        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);
+                              });
     }
 }
 
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
index 9a4865755e7..88f6d5fef56 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
@@ -16,7 +16,7 @@ use rustc::ty::{self, TyCtxt};
 use rustc::mir::repr::{self, Mir};
 use rustc_data_structures::indexed_vec::Idx;
 
-use super::super::gather_moves::{MovePathIndex};
+use super::super::gather_moves::{MovePathIndex, LookupResult};
 use super::super::MoveDataParamEnv;
 use super::BitDenotation;
 use super::DataflowResults;
@@ -116,20 +116,26 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                      repr::BorrowKind::Shared,
                                      ref peeking_at_lval) = *rvalue {
                 // Okay, our search is over.
-                let peek_mpi = move_data.rev_lookup.find(peeking_at_lval);
-                let bit_state = sets.on_entry.contains(&peek_mpi);
-                debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
-                       lvalue, peeking_at_lval, bit_state);
-                if !bit_state {
-                    tcx.sess.span_err(span, &format!("rustc_peek: bit not set"));
+                match move_data.rev_lookup.find(peeking_at_lval) {
+                    LookupResult::Exact(peek_mpi) => {
+                        let bit_state = sets.on_entry.contains(&peek_mpi);
+                        debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
+                               lvalue, peeking_at_lval, bit_state);
+                        if !bit_state {
+                            tcx.sess.span_err(span, "rustc_peek: bit not set");
+                        }
+                    }
+                    LookupResult::Parent(..) => {
+                        tcx.sess.span_err(span, "rustc_peek: argument untracked");
+                    }
                 }
                 return;
             } else {
                 // Our search should have been over, but the input
                 // does not match expectations of `rustc_peek` for
                 // this sanity_check.
-                let msg = &format!("rustc_peek: argument expression \
-                                    must be immediate borrow of form `&expr`");
+                let msg = "rustc_peek: argument expression \
+                           must be immediate borrow of form `&expr`";
                 tcx.sess.span_err(span, msg);
             }
         }
diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
index 71274b7e021..96702b209a1 100644
--- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
+++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
@@ -9,10 +9,11 @@
 // except according to those terms.
 
 use indexed_set::IdxSetBuf;
-use super::gather_moves::{MoveData, MovePathIndex, MovePathContent};
+use super::gather_moves::{MoveData, MovePathIndex, LookupResult};
 use super::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
 use super::dataflow::{DataflowResults};
 use super::{drop_flag_effects_for_location, on_all_children_bits};
+use super::on_lookup_result_bits;
 use super::{DropFlagState, MoveDataParamEnv};
 use super::patch::MirPatch;
 use rustc::ty::{self, Ty, TyCtxt};
@@ -42,7 +43,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
         }
         let id = src.item_id();
         let param_env = ty::ParameterEnvironment::for_item(tcx, id);
-        let move_data = MoveData::gather_moves(mir, tcx);
+        let move_data = MoveData::gather_moves(mir, tcx, &param_env);
         let elaborate_patch = {
             let mir = &*mir;
             let env = MoveDataParamEnv {
@@ -184,31 +185,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
 
     fn path_needs_drop(&self, path: MovePathIndex) -> bool
     {
-        match self.move_data().move_paths[path].content {
-            MovePathContent::Lvalue(ref lvalue) => {
-                let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
-                debug!("path_needs_drop({:?}, {:?} : {:?})", path, lvalue, ty);
+        let lvalue = &self.move_data().move_paths[path].lvalue;
+        let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
+        debug!("path_needs_drop({:?}, {:?} : {:?})", path, lvalue, ty);
 
-                self.tcx.type_needs_drop_given_env(ty, self.param_env())
-            }
-            _ => false
-        }
-    }
-
-    /// Returns whether this lvalue is tracked by drop elaboration. This
-    /// includes all lvalues, except these (1.) behind references or arrays,
-    ///  or (2.) behind ADT's with a Drop impl.
-    fn lvalue_is_tracked(&self, lv: &Lvalue<'tcx>) -> bool
-    {
-        // `lvalue_contents_drop_state_cannot_differ` only compares
-        // the `lv` to its immediate contents, while this recursively
-        // follows parent chain formed by `base` of each projection.
-        if let &Lvalue::Projection(ref data) = lv {
-            !super::lvalue_contents_drop_state_cannot_differ(self.tcx, self.mir, &data.base) &&
-                self.lvalue_is_tracked(&data.base)
-        } else {
-            true
-        }
+        self.tcx.type_needs_drop_given_env(ty, self.param_env())
     }
 
     fn collect_drop_flags(&mut self)
@@ -221,19 +202,29 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                 _ => continue
             };
 
-            if !self.lvalue_is_tracked(location) {
-                continue
-            }
-
             let init_data = self.initialization_data_at(Location {
                 block: bb,
                 statement_index: data.statements.len()
             });
 
             let path = self.move_data().rev_lookup.find(location);
-            debug!("collect_drop_flags: {:?}, lv {:?} (index {:?})",
+            debug!("collect_drop_flags: {:?}, lv {:?} ({:?})",
                    bb, location, path);
 
+            let path = match path {
+                LookupResult::Exact(e) => e,
+                LookupResult::Parent(None) => continue,
+                LookupResult::Parent(Some(parent)) => {
+                    let (_maybe_live, maybe_dead) = init_data.state(parent);
+                    if maybe_dead {
+                        span_bug!(terminator.source_info.span,
+                                  "drop of untracked, uninitialized value {:?}, lv {:?} ({:?})",
+                                  bb, location, path);
+                    }
+                    continue
+                }
+            };
+
             on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| {
                 if self.path_needs_drop(child) {
                     let (maybe_live, maybe_dead) = init_data.state(child);
@@ -257,20 +248,27 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
             match terminator.kind {
                 TerminatorKind::Drop { ref location, target, unwind } => {
                     let init_data = self.initialization_data_at(loc);
-                    let path = self.move_data().rev_lookup.find(location);
-                    self.elaborate_drop(&DropCtxt {
-                        source_info: terminator.source_info,
-                        is_cleanup: data.is_cleanup,
-                        init_data: &init_data,
-                        lvalue: location,
-                        path: path,
-                        succ: target,
-                        unwind: if data.is_cleanup {
-                            None
-                        } else {
-                            Some(Option::unwrap_or(unwind, resume_block))
+                    match self.move_data().rev_lookup.find(location) {
+                        LookupResult::Exact(path) => {
+                            self.elaborate_drop(&DropCtxt {
+                                source_info: terminator.source_info,
+                                is_cleanup: data.is_cleanup,
+                                init_data: &init_data,
+                                lvalue: location,
+                                path: path,
+                                succ: target,
+                                unwind: if data.is_cleanup {
+                                    None
+                                } else {
+                                    Some(Option::unwrap_or(unwind, resume_block))
+                                }
+                            }, bb);
                         }
-                    }, bb);
+                        LookupResult::Parent(..) => {
+                            span_bug!(terminator.source_info.span,
+                                      "drop of untracked value {:?}", bb);
+                        }
+                    }
                 }
                 TerminatorKind::DropAndReplace { ref location, ref value,
                                                  target, unwind } =>
@@ -336,35 +334,37 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
             is_cleanup: data.is_cleanup,
         });
 
-        if !self.lvalue_is_tracked(location) {
-            // drop and replace behind a pointer/array/whatever. The location
-            // must be initialized.
-            debug!("elaborate_drop_and_replace({:?}) - untracked", terminator);
-            self.patch.patch_terminator(bb, TerminatorKind::Drop {
-                location: location.clone(),
-                target: target,
-                unwind: Some(unwind)
-            });
-        } else {
-            debug!("elaborate_drop_and_replace({:?}) - tracked", terminator);
-            let init_data = self.initialization_data_at(loc);
-            let path = self.move_data().rev_lookup.find(location);
-
-            self.elaborate_drop(&DropCtxt {
-                source_info: terminator.source_info,
-                is_cleanup: data.is_cleanup,
-                init_data: &init_data,
-                lvalue: location,
-                path: path,
-                succ: target,
-                unwind: Some(unwind)
-            }, bb);
-            on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| {
-                self.set_drop_flag(Location { block: target, statement_index: 0 },
-                                   child, DropFlagState::Present);
-                self.set_drop_flag(Location { block: unwind, statement_index: 0 },
-                                   child, DropFlagState::Present);
-            });
+        match self.move_data().rev_lookup.find(location) {
+            LookupResult::Exact(path) => {
+                debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path);
+                let init_data = self.initialization_data_at(loc);
+
+                self.elaborate_drop(&DropCtxt {
+                    source_info: terminator.source_info,
+                    is_cleanup: data.is_cleanup,
+                    init_data: &init_data,
+                    lvalue: location,
+                    path: path,
+                    succ: target,
+                    unwind: Some(unwind)
+                }, bb);
+                on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| {
+                    self.set_drop_flag(Location { block: target, statement_index: 0 },
+                                       child, DropFlagState::Present);
+                    self.set_drop_flag(Location { block: unwind, statement_index: 0 },
+                                       child, DropFlagState::Present);
+                });
+            }
+            LookupResult::Parent(parent) => {
+                // drop and replace behind a pointer/array/whatever. The location
+                // must be initialized.
+                debug!("elaborate_drop_and_replace({:?}) - untracked {:?}", terminator, parent);
+                self.patch.patch_terminator(bb, TerminatorKind::Drop {
+                    location: location.clone(),
+                    target: target,
+                    unwind: Some(unwind)
+                });
+            }
         }
     }
 
@@ -446,10 +446,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                              substs: &'tcx Substs<'tcx>)
                              -> Vec<(Lvalue<'tcx>, Option<MovePathIndex>)>
     {
-        let move_paths = &self.move_data().move_paths;
         variant.fields.iter().enumerate().map(|(i, f)| {
             let subpath =
-                super::move_path_children_matching(move_paths, variant_path, |p| {
+                super::move_path_children_matching(self.move_data(), variant_path, |p| {
                     match p {
                         &Projection {
                             elem: ProjectionElem::Field(idx, _), ..
@@ -580,7 +579,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
         let fields = tys.iter().enumerate().map(|(i, &ty)| {
             (c.lvalue.clone().field(Field::new(i), ty),
              super::move_path_children_matching(
-                 &self.move_data().move_paths, c.path, |proj| match proj {
+                 self.move_data(), c.path, |proj| match proj {
                      &Projection {
                          elem: ProjectionElem::Field(f, _), ..
                      } => f.index() == i,
@@ -598,7 +597,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
         debug!("open_drop_for_box({:?}, {:?})", c, ty);
 
         let interior_path = super::move_path_children_matching(
-            &self.move_data().move_paths, c.path, |proj| match proj {
+            self.move_data(), c.path, |proj| match proj {
                 &Projection { elem: ProjectionElem::Deref, .. } => true,
                 _ => false
             }).unwrap();
@@ -625,10 +624,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                                  variant_index: usize)
                                  -> BasicBlock
     {
-        let move_paths = &self.move_data().move_paths;
-
         let subpath = super::move_path_children_matching(
-            move_paths, c.path, |proj| match proj {
+            self.move_data(), c.path, |proj| match proj {
                 &Projection {
                     elem: ProjectionElem::Downcast(_, idx), ..
                 } => idx == variant_index,
@@ -942,7 +939,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
 
                 let loc = Location { block: tgt, statement_index: 0 };
                 let path = self.move_data().rev_lookup.find(lv);
-                on_all_children_bits(
+                on_lookup_result_bits(
                     self.tcx, self.mir, self.move_data(), path,
                     |child| self.set_drop_flag(loc, child, DropFlagState::Present)
                 );
@@ -1011,7 +1008,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
 
                 let loc = Location { block: bb, statement_index: data.statements.len() };
                 let path = self.move_data().rev_lookup.find(lv);
-                on_all_children_bits(
+                on_lookup_result_bits(
                     self.tcx, self.mir, self.move_data(), path,
                     |child| self.set_drop_flag(loc, child, DropFlagState::Present)
                 );
diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
index 01bf8ed0e4b..2713a3c371d 100644
--- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
@@ -9,16 +9,18 @@
 // except according to those terms.
 
 
-use rustc::ty::TyCtxt;
+use rustc::ty::{self, TyCtxt, ParameterEnvironment};
 use rustc::mir::repr::*;
 use rustc::util::nodemap::FnvHashMap;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc::util::common::ErrorReported;
+use rustc_data_structures::indexed_vec::{IndexVec};
+
+use syntax::codemap::DUMMY_SP;
 
-use std::cell::{Cell};
 use std::collections::hash_map::Entry;
 use std::fmt;
-use std::iter;
-use std::ops::Index;
+use std::mem;
+use std::ops::{Index, IndexMut};
 
 use super::abs_domain::{AbstractElem, Lift};
 
@@ -28,17 +30,15 @@ use super::abs_domain::{AbstractElem, Lift};
 // ensure that other code does not accidentally access `index.0`
 // (which is likely to yield a subtle off-by-one error).
 mod indexes {
+    use std::fmt;
     use core::nonzero::NonZero;
     use rustc_data_structures::indexed_vec::Idx;
 
     macro_rules! new_index {
-        ($Index:ident) => {
-            #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+        ($Index:ident, $debug_name:expr) => {
+            #[derive(Copy, Clone, PartialEq, Eq, Hash)]
             pub struct $Index(NonZero<usize>);
 
-            impl $Index {
-            }
-
             impl Idx for $Index {
                 fn new(idx: usize) -> Self {
                     unsafe { $Index(NonZero::new(idx + 1)) }
@@ -47,14 +47,20 @@ mod indexes {
                     *self.0 - 1
                 }
             }
+
+            impl fmt::Debug for $Index {
+                fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+                    write!(fmt, "{}{}", $debug_name, self.index())
+                }
+            }
         }
     }
 
     /// Index into MovePathData.move_paths
-    new_index!(MovePathIndex);
+    new_index!(MovePathIndex, "mp");
 
     /// Index into MoveData.moves.
-    new_index!(MoveOutIndex);
+    new_index!(MoveOutIndex, "mo");
 }
 
 pub use self::indexes::MovePathIndex;
@@ -62,7 +68,7 @@ pub use self::indexes::MoveOutIndex;
 
 impl self::indexes::MoveOutIndex {
     pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex {
-        move_data.moves[self.index()].path
+        move_data.moves[*self].path
     }
 }
 
@@ -83,40 +89,7 @@ pub struct MovePath<'tcx> {
     pub next_sibling: Option<MovePathIndex>,
     pub first_child: Option<MovePathIndex>,
     pub parent: Option<MovePathIndex>,
-    pub content: MovePathContent<'tcx>,
-}
-
-/// MovePaths usually represent a single l-value. The exceptions are
-/// forms that arise due to erroneous input code: static data holds
-/// l-values that we cannot actually move out of. Therefore we map
-/// statics to a special marker value (`MovePathContent::Static`)
-/// representing an invalid origin.
-#[derive(Clone, Debug)]
-pub enum MovePathContent<'tcx> {
-    Lvalue(Lvalue<'tcx>),
-    Static,
-}
-
-/// During construction of the MovePath's, we use PreMovePath to
-/// represent accumulated state while we are gathering up all the
-/// children of each path.
-#[derive(Clone)]
-struct PreMovePath<'tcx> {
-    pub next_sibling: Option<MovePathIndex>,
-    pub first_child: Cell<Option<MovePathIndex>>,
-    pub parent: Option<MovePathIndex>,
-    pub content: MovePathContent<'tcx>,
-}
-
-impl<'tcx> PreMovePath<'tcx> {
-    fn into_move_path(self) -> MovePath<'tcx> {
-        MovePath {
-            next_sibling: self.next_sibling,
-            parent: self.parent,
-            content: self.content,
-            first_child: self.first_child.get(),
-        }
-    }
+    pub lvalue: Lvalue<'tcx>,
 }
 
 impl<'tcx> fmt::Debug for MovePath<'tcx> {
@@ -131,52 +104,50 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> {
         if let Some(next_sibling) = self.next_sibling {
             write!(w, " next_sibling: {:?}", next_sibling)?;
         }
-        write!(w, " content: {:?} }}", self.content)
+        write!(w, " lvalue: {:?} }}", self.lvalue)
     }
 }
 
 #[derive(Debug)]
 pub struct MoveData<'tcx> {
-    pub move_paths: MovePathData<'tcx>,
-    pub moves: Vec<MoveOut>,
-    pub loc_map: LocMap,
-    pub path_map: PathMap,
+    pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
+    pub moves: IndexVec<MoveOutIndex, MoveOut>,
+    /// Each Location `l` is mapped to the MoveOut's that are effects
+    /// of executing the code at `l`. (There can be multiple MoveOut's
+    /// for a given `l` because each MoveOut is associated with one
+    /// particular path being moved.)
+    pub loc_map: LocationMap<Vec<MoveOutIndex>>,
+    pub path_map: IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
     pub rev_lookup: MovePathLookup<'tcx>,
 }
 
 #[derive(Debug)]
-pub struct LocMap {
+pub struct LocationMap<T> {
     /// Location-indexed (BasicBlock for outer index, index within BB
-    /// for inner index) map to list of MoveOutIndex's.
-    ///
-    /// Each Location `l` is mapped to the MoveOut's that are effects
-    /// of executing the code at `l`. (There can be multiple MoveOut's
-    /// for a given `l` because each MoveOut is associated with one
-    /// particular path being moved.)
-    map: Vec<Vec<Vec<MoveOutIndex>>>,
+    /// for inner index) map.
+    map: IndexVec<BasicBlock, Vec<T>>,
 }
 
-impl Index<Location> for LocMap {
-    type Output = [MoveOutIndex];
+impl<T> Index<Location> for LocationMap<T> {
+    type Output = T;
     fn index(&self, index: Location) -> &Self::Output {
-        assert!(index.block.index() < self.map.len());
-        assert!(index.statement_index < self.map[index.block.index()].len());
-        &self.map[index.block.index()][index.statement_index]
+        &self.map[index.block][index.statement_index]
     }
 }
 
-#[derive(Debug)]
-pub struct PathMap {
-    /// Path-indexed map to list of MoveOutIndex's.
-    ///
-    /// Each Path `p` is mapped to the MoveOut's that move out of `p`.
-    map: Vec<Vec<MoveOutIndex>>,
+impl<T> IndexMut<Location> for LocationMap<T> {
+    fn index_mut(&mut self, index: Location) -> &mut Self::Output {
+        &mut self.map[index.block][index.statement_index]
+    }
 }
 
-impl Index<MovePathIndex> for PathMap {
-    type Output = [MoveOutIndex];
-    fn index(&self, index: MovePathIndex) -> &Self::Output {
-        &self.map[index.index()]
+impl<T> LocationMap<T> where T: Default + Clone {
+    fn new(mir: &Mir) -> Self {
+        LocationMap {
+            map: mir.basic_blocks().iter().map(|block| {
+                vec![T::default(); block.statements.len()+1]
+            }).collect()
+        }
     }
 }
 
@@ -196,583 +167,373 @@ pub struct MoveOut {
 
 impl fmt::Debug for MoveOut {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write!(fmt, "p{}@{:?}", self.path.index(), self.source)
-    }
-}
-
-#[derive(Debug)]
-pub struct MovePathData<'tcx> {
-    move_paths: Vec<MovePath<'tcx>>,
-}
-
-impl<'tcx> MovePathData<'tcx> {
-    pub fn len(&self) -> usize { self.move_paths.len() }
-}
-
-impl<'tcx> Index<MovePathIndex> for MovePathData<'tcx> {
-    type Output = MovePath<'tcx>;
-    fn index(&self, i: MovePathIndex) -> &MovePath<'tcx> {
-        &self.move_paths[i.index()]
+        write!(fmt, "{:?}@{:?}", self.path, self.source)
     }
 }
 
-struct MovePathDataBuilder<'tcx> {
-    pre_move_paths: Vec<PreMovePath<'tcx>>,
-    rev_lookup: MovePathLookup<'tcx>,
-}
-
 /// Tables mapping from an l-value to its MovePathIndex.
 #[derive(Debug)]
 pub struct MovePathLookup<'tcx> {
-    vars: IndexVec<Var, Option<MovePathIndex>>,
-    temps: IndexVec<Temp, Option<MovePathIndex>>,
-    args: IndexVec<Arg, Option<MovePathIndex>>,
+    vars: IndexVec<Var, MovePathIndex>,
+    temps: IndexVec<Temp, MovePathIndex>,
+    args: IndexVec<Arg, MovePathIndex>,
 
     /// The move path representing the return value is constructed
     /// lazily when we first encounter it in the input MIR.
     return_ptr: Option<MovePathIndex>,
 
-    /// A single move path (representing any static data referenced)
-    /// is constructed lazily when we first encounter statics in the
-    /// input MIR.
-    statics: Option<MovePathIndex>,
-
     /// projections are made from a base-lvalue and a projection
     /// elem. The base-lvalue will have a unique MovePathIndex; we use
     /// the latter as the index into the outer vector (narrowing
     /// subsequent search so that it is solely relative to that
     /// base-lvalue). For the remaining lookup, we map the projection
     /// elem to the associated MovePathIndex.
-    projections: Vec<FnvHashMap<AbstractElem<'tcx>, MovePathIndex>>,
-
-    /// Tracks the next index to allocate during construction of the
-    /// MovePathData. Unused after MovePathData is fully constructed.
-    next_index: MovePathIndex,
+    projections: FnvHashMap<(MovePathIndex, AbstractElem<'tcx>), MovePathIndex>
 }
 
-trait FillTo {
-    type T;
-    fn fill_to_with(&mut self, idx: usize, x: Self::T);
-    fn fill_to(&mut self, idx: usize) where Self::T: Default {
-        self.fill_to_with(idx, Default::default())
-    }
+struct MoveDataBuilder<'a, 'tcx: 'a> {
+    mir: &'a Mir<'tcx>,
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    param_env: &'a ParameterEnvironment<'tcx>,
+    data: MoveData<'tcx>,
 }
-impl<T:Clone> FillTo for Vec<T> {
-    type T = T;
-    fn fill_to_with(&mut self, idx: usize, x: T) {
-        if idx >= self.len() {
-            let delta = idx + 1 - self.len();
-            assert_eq!(idx + 1, self.len() + delta);
-            self.extend(iter::repeat(x).take(delta))
+
+impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
+    fn new(mir: &'a Mir<'tcx>,
+           tcx: TyCtxt<'a, 'tcx, 'tcx>,
+           param_env: &'a ParameterEnvironment<'tcx>)
+           -> Self {
+        let mut move_paths = IndexVec::new();
+        let mut path_map = IndexVec::new();
+
+        MoveDataBuilder {
+            mir: mir,
+            tcx: tcx,
+            param_env: param_env,
+            data: MoveData {
+                moves: IndexVec::new(),
+                loc_map: LocationMap::new(mir),
+                rev_lookup: MovePathLookup {
+                    vars: mir.var_decls.indices().map(Lvalue::Var).map(|v| {
+                        Self::new_move_path(&mut move_paths, &mut path_map, None, v)
+                    }).collect(),
+                    temps: mir.temp_decls.indices().map(Lvalue::Temp).map(|t| {
+                        Self::new_move_path(&mut move_paths, &mut path_map, None, t)
+                    }).collect(),
+                    args: mir.arg_decls.indices().map(Lvalue::Arg).map(|a| {
+                        Self::new_move_path(&mut move_paths, &mut path_map, None, a)
+                    }).collect(),
+                    return_ptr: None,
+                    projections: FnvHashMap(),
+                },
+                move_paths: move_paths,
+                path_map: path_map,
+            }
         }
-        debug_assert!(idx < self.len());
     }
-}
 
-#[derive(Clone, Debug)]
-enum LookupKind { Generate, Reuse }
-#[derive(Clone, Debug)]
-struct Lookup<T>(LookupKind, T);
-
-impl Lookup<MovePathIndex> {
-    fn index(&self) -> usize { (self.1).index() }
-}
-
-impl<'tcx> MovePathLookup<'tcx> {
-    fn new(mir: &Mir) -> Self {
-        MovePathLookup {
-            vars: IndexVec::from_elem(None, &mir.var_decls),
-            temps: IndexVec::from_elem(None, &mir.temp_decls),
-            args: IndexVec::from_elem(None, &mir.arg_decls),
-            statics: None,
-            return_ptr: None,
-            projections: vec![],
-            next_index: MovePathIndex::new(0),
+    fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
+                     path_map: &mut IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
+                     parent: Option<MovePathIndex>,
+                     lvalue: Lvalue<'tcx>)
+                     -> MovePathIndex
+    {
+        let move_path = move_paths.push(MovePath {
+            next_sibling: None,
+            first_child: None,
+            parent: parent,
+            lvalue: lvalue
+        });
+
+        if let Some(parent) = parent {
+            let next_sibling =
+                mem::replace(&mut move_paths[parent].first_child, Some(move_path));
+            move_paths[move_path].next_sibling = next_sibling;
         }
-    }
 
-    fn next_index(next: &mut MovePathIndex) -> MovePathIndex {
-        let i = *next;
-        *next = MovePathIndex::new(i.index() + 1);
-        i
+        let path_map_ent = path_map.push(vec![]);
+        assert_eq!(path_map_ent, move_path);
+        move_path
     }
 
-    fn lookup_or_generate<I: Idx>(vec: &mut IndexVec<I, Option<MovePathIndex>>,
-                                  idx: I,
-                                  next_index: &mut MovePathIndex)
-                                  -> Lookup<MovePathIndex> {
-        let entry = &mut vec[idx];
-        match *entry {
-            None => {
-                let i = Self::next_index(next_index);
-                *entry = Some(i);
-                Lookup(LookupKind::Generate, i)
-            }
-            Some(entry_idx) => {
-                Lookup(LookupKind::Reuse, entry_idx)
+    /// This creates a MovePath for a given lvalue, returning an `ErrorReported`
+    /// if that lvalue can't be moved from.
+    ///
+    /// NOTE: lvalues behind references *do not* get a move path, which is
+    /// problematic for borrowck.
+    ///
+    /// Maybe we should have seperate "borrowck" and "moveck" modes.
+    fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
+                     -> Result<MovePathIndex, ErrorReported>
+    {
+        debug!("lookup({:?})", lval);
+        match *lval {
+            Lvalue::Var(var) => Ok(self.data.rev_lookup.vars[var]),
+            Lvalue::Arg(arg) => Ok(self.data.rev_lookup.args[arg]),
+            Lvalue::Temp(temp) => Ok(self.data.rev_lookup.temps[temp]),
+            // error: can't move out of a static
+            Lvalue::Static(..) => Err(ErrorReported),
+            Lvalue::ReturnPointer => match self.data.rev_lookup.return_ptr {
+                Some(ptr) => Ok(ptr),
+                ref mut ptr @ None => {
+                    let path = Self::new_move_path(
+                        &mut self.data.move_paths,
+                        &mut self.data.path_map,
+                        None,
+                        lval.clone());
+                    *ptr = Some(path);
+                    Ok(path)
+                }
+            },
+            Lvalue::Projection(ref proj) => {
+                self.move_path_for_projection(lval, proj)
             }
         }
     }
 
-    fn lookup_var(&mut self, var_idx: Var) -> Lookup<MovePathIndex> {
-        Self::lookup_or_generate(&mut self.vars,
-                                 var_idx,
-                                 &mut self.next_index)
-    }
-
-    fn lookup_temp(&mut self, temp_idx: Temp) -> Lookup<MovePathIndex> {
-        Self::lookup_or_generate(&mut self.temps,
-                                 temp_idx,
-                                 &mut self.next_index)
-    }
-
-    fn lookup_arg(&mut self, arg_idx: Arg) -> Lookup<MovePathIndex> {
-        Self::lookup_or_generate(&mut self.args,
-                                 arg_idx,
-                                 &mut self.next_index)
+    fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
+        // This is an assignment, not a move, so this not being a valid
+        // move path is OK.
+        let _ = self.move_path_for(lval);
     }
 
-    fn lookup_static(&mut self) -> Lookup<MovePathIndex> {
-        match self.statics {
-            Some(mpi) => {
-                Lookup(LookupKind::Reuse, mpi)
-            }
-            ref mut ret @ None => {
-                let mpi = Self::next_index(&mut self.next_index);
-                *ret = Some(mpi);
-                Lookup(LookupKind::Generate, mpi)
+    fn move_path_for_projection(&mut self,
+                                lval: &Lvalue<'tcx>,
+                                proj: &LvalueProjection<'tcx>)
+                                -> Result<MovePathIndex, ErrorReported>
+    {
+        let base = try!(self.move_path_for(&proj.base));
+        let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+        match lv_ty.sty {
+            // error: can't move out of borrowed content
+            ty::TyRef(..) | ty::TyRawPtr(..) => return Err(ErrorReported),
+            // error: can't move out of struct with destructor
+            ty::TyStruct(adt, _) | ty::TyEnum(adt, _) if adt.has_dtor() =>
+                return Err(ErrorReported),
+
+            ty::TyArray(..) | ty::TySlice(..) => match proj.elem {
+                // error: can't move out of an array
+                ProjectionElem::Index(..) => return Err(ErrorReported),
+                _ => {}
+            },
+            _ => {}
+        };
+        match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) {
+            Entry::Occupied(ent) => Ok(*ent.get()),
+            Entry::Vacant(ent) => {
+                let path = Self::new_move_path(
+                    &mut self.data.move_paths,
+                    &mut self.data.path_map,
+                    Some(base),
+                    lval.clone()
+                );
+                ent.insert(path);
+                Ok(path)
             }
         }
     }
 
-    fn lookup_return_pointer(&mut self) -> Lookup<MovePathIndex> {
-        match self.return_ptr {
-            Some(mpi) => {
-                Lookup(LookupKind::Reuse, mpi)
+    fn finalize(self) -> MoveData<'tcx> {
+        debug!("{}", {
+            debug!("moves for {:?}:", self.mir.span);
+            for (j, mo) in self.data.moves.iter_enumerated() {
+                debug!("    {:?} = {:?}", j, mo);
             }
-            ref mut ret @ None => {
-                let mpi = Self::next_index(&mut self.next_index);
-                *ret = Some(mpi);
-                Lookup(LookupKind::Generate, mpi)
+            debug!("move paths for {:?}:", self.mir.span);
+            for (j, path) in self.data.move_paths.iter_enumerated() {
+                debug!("    {:?} = {:?}", j, path);
             }
-        }
+            "done dumping moves"
+        });
+        self.data
     }
+}
 
-    fn lookup_proj(&mut self,
-                   proj: &LvalueProjection<'tcx>,
-                   base: MovePathIndex) -> Lookup<MovePathIndex> {
-        let MovePathLookup { ref mut projections,
-                             ref mut next_index, .. } = *self;
-        projections.fill_to(base.index());
-        match projections[base.index()].entry(proj.elem.lift()) {
-            Entry::Occupied(ent) => {
-                Lookup(LookupKind::Reuse, *ent.get())
-            }
-            Entry::Vacant(ent) => {
-                let mpi = Self::next_index(next_index);
-                ent.insert(mpi);
-                Lookup(LookupKind::Generate, mpi)
-            }
-        }
-    }
+#[derive(Copy, Clone, Debug)]
+pub enum LookupResult {
+    Exact(MovePathIndex),
+    Parent(Option<MovePathIndex>)
 }
 
 impl<'tcx> MovePathLookup<'tcx> {
     // Unlike the builder `fn move_path_for` below, this lookup
     // alternative will *not* create a MovePath on the fly for an
-    // unknown l-value; it will simply panic.
-    pub fn find(&self, lval: &Lvalue<'tcx>) -> MovePathIndex {
+    // unknown l-value, but will rather return the nearest available
+    // parent.
+    pub fn find(&self, lval: &Lvalue<'tcx>) -> LookupResult {
         match *lval {
-            Lvalue::Var(var) => self.vars[var].unwrap(),
-            Lvalue::Temp(temp) => self.temps[temp].unwrap(),
-            Lvalue::Arg(arg) => self.args[arg].unwrap(),
-            Lvalue::Static(ref _def_id) => self.statics.unwrap(),
-            Lvalue::ReturnPointer => self.return_ptr.unwrap(),
+            Lvalue::Var(var) => LookupResult::Exact(self.vars[var]),
+            Lvalue::Temp(temp) => LookupResult::Exact(self.temps[temp]),
+            Lvalue::Arg(arg) => LookupResult::Exact(self.args[arg]),
+            Lvalue::Static(..) => LookupResult::Parent(None),
+            Lvalue::ReturnPointer => LookupResult::Exact(self.return_ptr.unwrap()),
             Lvalue::Projection(ref proj) => {
-                let base_index = self.find(&proj.base);
-                self.projections[base_index.index()][&proj.elem.lift()]
+                match self.find(&proj.base) {
+                    LookupResult::Exact(base_path) => {
+                        match self.projections.get(&(base_path, proj.elem.lift())) {
+                            Some(&subpath) => LookupResult::Exact(subpath),
+                            None => LookupResult::Parent(Some(base_path))
+                        }
+                    }
+                    inexact => inexact
+                }
             }
         }
     }
 }
 
-impl<'tcx> MovePathDataBuilder<'tcx> {
-    fn lookup(&mut self, lval: &Lvalue<'tcx>) -> Lookup<MovePathIndex> {
-        let proj = match *lval {
-            Lvalue::Var(var_idx) =>
-                return self.rev_lookup.lookup_var(var_idx),
-            Lvalue::Temp(temp_idx) =>
-                return self.rev_lookup.lookup_temp(temp_idx),
-            Lvalue::Arg(arg_idx) =>
-                return self.rev_lookup.lookup_arg(arg_idx),
-            Lvalue::Static(_def_id) =>
-                return self.rev_lookup.lookup_static(),
-            Lvalue::ReturnPointer =>
-                return self.rev_lookup.lookup_return_pointer(),
-            Lvalue::Projection(ref proj) => {
-                proj
-            }
-        };
-
-        let base_index = self.move_path_for(&proj.base);
-        self.rev_lookup.lookup_proj(proj, base_index)
-    }
-
-    fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
-        // Create MovePath for `lval`, discarding returned index.
-        self.move_path_for(lval);
+impl<'a, 'tcx> MoveData<'tcx> {
+    pub fn gather_moves(mir: &Mir<'tcx>,
+                        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                        param_env: &ParameterEnvironment<'tcx>)
+                        -> Self {
+        gather_moves(mir, tcx, param_env)
     }
+}
 
-    fn move_path_for(&mut self, lval: &Lvalue<'tcx>) -> MovePathIndex {
-        debug!("move_path_for({:?})", lval);
-
-        let lookup: Lookup<MovePathIndex> = self.lookup(lval);
-
-        // `lookup` is either the previously assigned index or a
-        // newly-allocated one.
-        debug_assert!(lookup.index() <= self.pre_move_paths.len());
-
-        if let Lookup(LookupKind::Generate, mpi) = lookup {
-            let parent;
-            let sibling;
-            // tracks whether content is Some non-static; statics map to None.
-            let content: Option<&Lvalue<'tcx>>;
-
-            match *lval {
-                Lvalue::Static(_) => {
-                    content = None;
-                    sibling = None;
-                    parent = None;
-                }
-
-                Lvalue::Var(_) | Lvalue::Temp(_) | Lvalue::Arg(_) |
-                Lvalue::ReturnPointer => {
-                    content = Some(lval);
-                    sibling = None;
-                    parent = None;
-                }
-                Lvalue::Projection(ref proj) => {
-                    content = Some(lval);
-
-                    // Here, install new MovePath as new first_child.
-
-                    // Note: `parent` previously allocated (Projection
-                    // case of match above established this).
-                    let idx = self.move_path_for(&proj.base);
-                    parent = Some(idx);
-
-                    let parent_move_path = &mut self.pre_move_paths[idx.index()];
-
-                    // At last: Swap in the new first_child.
-                    sibling = parent_move_path.first_child.get();
-                    parent_move_path.first_child.set(Some(mpi));
-                }
-            };
-
-            let content = match content {
-                Some(lval) => MovePathContent::Lvalue(lval.clone()),
-                None => MovePathContent::Static,
-            };
-
-            let move_path = PreMovePath {
-                next_sibling: sibling,
-                parent: parent,
-                content: content,
-                first_child: Cell::new(None),
-            };
+fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
+                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                          param_env: &ParameterEnvironment<'tcx>)
+                          -> MoveData<'tcx> {
+    let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
 
-            self.pre_move_paths.push(move_path);
+    for (bb, block) in mir.basic_blocks().iter_enumerated() {
+        for (i, stmt) in block.statements.iter().enumerate() {
+            let source = Location { block: bb, statement_index: i };
+            builder.gather_statement(source, stmt);
         }
 
-        return lookup.1;
+        let terminator_loc = Location {
+            block: bb,
+            statement_index: block.statements.len()
+        };
+        builder.gather_terminator(terminator_loc, block.terminator());
     }
-}
 
-impl<'a, 'tcx> MoveData<'tcx> {
-    pub fn gather_moves(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
-        gather_moves(mir, tcx)
-    }
+    builder.finalize()
 }
 
-#[derive(Debug)]
-enum StmtKind {
-    Use, Repeat, Cast, BinaryOp, UnaryOp, Box,
-    Aggregate, Drop, CallFn, CallArg, Return, If,
-}
-
-fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveData<'tcx> {
-    use self::StmtKind as SK;
-
-    let bb_count = mir.basic_blocks().len();
-    let mut moves = vec![];
-    let mut loc_map: Vec<_> = iter::repeat(Vec::new()).take(bb_count).collect();
-    let mut path_map = Vec::new();
-
-    // this is mutable only because we will move it to and fro' the
-    // BlockContexts constructed on each iteration. (Moving is more
-    // straight-forward than mutable borrows in this instance.)
-    let mut builder = MovePathDataBuilder {
-        pre_move_paths: Vec::new(),
-        rev_lookup: MovePathLookup::new(mir),
-    };
-
-    // Before we analyze the program text, we create the MovePath's
-    // for all of the vars, args, and temps. (This enforces a basic
-    // property that even if the MIR body doesn't contain any
-    // references to a var/arg/temp, it will still be a valid
-    // operation to lookup the MovePath associated with it.)
-    assert!(mir.var_decls.len() <= ::std::u32::MAX as usize);
-    assert!(mir.arg_decls.len() <= ::std::u32::MAX as usize);
-    assert!(mir.temp_decls.len() <= ::std::u32::MAX as usize);
-    for var in mir.var_decls.indices() {
-        let path_idx = builder.move_path_for(&Lvalue::Var(var));
-        path_map.fill_to(path_idx.index());
-    }
-    for arg in mir.arg_decls.indices() {
-        let path_idx = builder.move_path_for(&Lvalue::Arg(arg));
-        path_map.fill_to(path_idx.index());
-    }
-    for temp in mir.temp_decls.indices() {
-        let path_idx = builder.move_path_for(&Lvalue::Temp(temp));
-        path_map.fill_to(path_idx.index());
+impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
+    fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
+        debug!("gather_statement({:?}, {:?})", loc, stmt);
+        match stmt.kind {
+            StatementKind::Assign(ref lval, ref rval) => {
+                self.create_move_path(lval);
+                self.gather_rvalue(loc, rval);
+            }
+            StatementKind::StorageLive(_) |
+            StatementKind::StorageDead(_) => {}
+            StatementKind::SetDiscriminant{ .. } => {
+                span_bug!(stmt.source_info.span,
+                          "SetDiscriminant should not exist during borrowck");
+            }
+        }
     }
 
-    for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
-        let loc_map_bb = &mut loc_map[bb.index()];
-
-        debug_assert!(loc_map_bb.len() == 0);
-        let len = bb_data.statements.len();
-        loc_map_bb.fill_to(len);
-        debug_assert!(loc_map_bb.len() == len + 1);
-
-        let mut bb_ctxt = BlockContext {
-            _tcx: tcx,
-            moves: &mut moves,
-            builder: builder,
-            path_map: &mut path_map,
-            loc_map_bb: loc_map_bb,
-        };
-
-        for (i, stmt) in bb_data.statements.iter().enumerate() {
-            let source = Location { block: bb, statement_index: i };
-            match stmt.kind {
-                StatementKind::Assign(ref lval, ref rval) => {
-                    bb_ctxt.builder.create_move_path(lval);
-
-                    // Ensure that the path_map contains entries even
-                    // if the lvalue is assigned and never read.
-                    let assigned_path = bb_ctxt.builder.move_path_for(lval);
-                    bb_ctxt.path_map.fill_to(assigned_path.index());
-
-                    match *rval {
-                        Rvalue::Use(ref operand) => {
-                            bb_ctxt.on_operand(SK::Use, operand, source)
-                        }
-                        Rvalue::Repeat(ref operand, ref _const) =>
-                            bb_ctxt.on_operand(SK::Repeat, operand, source),
-                        Rvalue::Cast(ref _kind, ref operand, ref _ty) =>
-                            bb_ctxt.on_operand(SK::Cast, operand, source),
-                        Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) |
-                        Rvalue::CheckedBinaryOp(ref _binop, ref operand1, ref operand2) => {
-                            bb_ctxt.on_operand(SK::BinaryOp, operand1, source);
-                            bb_ctxt.on_operand(SK::BinaryOp, operand2, source);
-                        }
-                        Rvalue::UnaryOp(ref _unop, ref operand) => {
-                            bb_ctxt.on_operand(SK::UnaryOp, operand, source);
-                        }
-                        Rvalue::Box(ref _ty) => {
-                            // this is creating uninitialized
-                            // memory that needs to be initialized.
-                            let deref_lval = Lvalue::Projection(Box::new(Projection {
-                                base: lval.clone(),
-                                elem: ProjectionElem::Deref,
-                            }));
-                            bb_ctxt.on_move_out_lval(SK::Box, &deref_lval, source);
-                        }
-                        Rvalue::Aggregate(ref _kind, ref operands) => {
-                            for operand in operands {
-                                bb_ctxt.on_operand(SK::Aggregate, operand, source);
-                            }
-                        }
-                        Rvalue::Ref(..) |
-                        Rvalue::Len(..) |
-                        Rvalue::InlineAsm { .. } => {}
-                    }
-                }
-                StatementKind::StorageLive(_) |
-                StatementKind::StorageDead(_) => {}
-                StatementKind::SetDiscriminant{ .. } => {
-                    span_bug!(stmt.source_info.span,
-                              "SetDiscriminant should not exist during borrowck");
+    fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) {
+        match *rvalue {
+            Rvalue::Use(ref operand) |
+            Rvalue::Repeat(ref operand, _) |
+            Rvalue::Cast(_, ref operand, _) |
+            Rvalue::UnaryOp(_, ref operand) => {
+                self.gather_operand(loc, operand)
+            }
+            Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) |
+            Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
+                self.gather_operand(loc, lhs);
+                self.gather_operand(loc, rhs);
+            }
+            Rvalue::Aggregate(ref _kind, ref operands) => {
+                for operand in operands {
+                    self.gather_operand(loc, operand);
                 }
             }
+            Rvalue::Ref(..) |
+            Rvalue::Len(..) |
+            Rvalue::InlineAsm { .. } => {}
+            Rvalue::Box(..) => {
+                // This returns an rvalue with uninitialized contents. We can't
+                // move out of it here because it is an rvalue - assignments always
+                // completely initialize their lvalue.
+                //
+                // However, this does not matter - MIR building is careful to
+                // only emit a shallow free for the partially-initialized
+                // temporary.
+                //
+                // In any case, if we want to fix this, we have to register a
+                // special move and change the `statement_effect` functions.
+            }
         }
+    }
 
-        debug!("gather_moves({:?})", bb_data.terminator());
-        match bb_data.terminator().kind {
+    fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
+        debug!("gather_terminator({:?}, {:?})", loc, term);
+        match term.kind {
             TerminatorKind::Goto { target: _ } |
             TerminatorKind::Resume |
             TerminatorKind::Unreachable => { }
 
             TerminatorKind::Return => {
-                let source = Location { block: bb,
-                                        statement_index: bb_data.statements.len() };
-                debug!("gather_moves Return on_move_out_lval return {:?}", source);
-                bb_ctxt.on_move_out_lval(SK::Return, &Lvalue::ReturnPointer, source);
-            }
-
-            TerminatorKind::If { ref cond, targets: _ } => {
-                let source = Location { block: bb,
-                                        statement_index: bb_data.statements.len() };
-                bb_ctxt.on_operand(SK::If, cond, source);
-            }
-
-            TerminatorKind::Assert {
-                ref cond, expected: _,
-                ref msg, target: _, cleanup: _
-            } => {
-                // The `cond` is always of (copyable) type `bool`,
-                // so there will never be anything to move.
-                let _ = cond;
-                match *msg {
-                    AssertMessage:: BoundsCheck { ref len, ref index } => {
-                        // Same for the usize length and index in bounds-checking.
-                        let _ = (len, index);
-                    }
-                    AssertMessage::Math(_) => {}
-                }
+                self.gather_move(loc, &Lvalue::ReturnPointer);
             }
 
-            TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } |
-            TerminatorKind::Switch { adt_def: _, targets: _, ref discr } => {
-                // The `discr` is not consumed; that is instead
-                // encoded on specific match arms (and for
-                // SwitchInt`, it is always a copyable integer
-                // type anyway).
-                let _ = discr;
+            TerminatorKind::If { .. } |
+            TerminatorKind::Assert { .. } |
+            TerminatorKind::SwitchInt { .. } |
+            TerminatorKind::Switch { .. } => {
+                // branching terminators - these don't move anything
             }
 
             TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
-                let source = Location { block: bb,
-                                        statement_index: bb_data.statements.len() };
-                bb_ctxt.on_move_out_lval(SK::Drop, location, source);
+                self.gather_move(loc, location);
             }
             TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
-                let assigned_path = bb_ctxt.builder.move_path_for(location);
-                bb_ctxt.path_map.fill_to(assigned_path.index());
-
-                let source = Location { block: bb,
-                                        statement_index: bb_data.statements.len() };
-                bb_ctxt.on_operand(SK::Use, value, source);
+                self.create_move_path(location);
+                self.gather_operand(loc, value);
             }
             TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
-                let source = Location { block: bb,
-                                        statement_index: bb_data.statements.len() };
-                bb_ctxt.on_operand(SK::CallFn, func, source);
+                self.gather_operand(loc, func);
                 for arg in args {
-                    debug!("gather_moves Call on_operand {:?} {:?}", arg, source);
-                    bb_ctxt.on_operand(SK::CallArg, arg, source);
+                    self.gather_operand(loc, arg);
                 }
                 if let Some((ref destination, _bb)) = *destination {
-                    debug!("gather_moves Call create_move_path {:?} {:?}", destination, source);
-
-                    // Ensure that the path_map contains entries even
-                    // if the lvalue is assigned and never read.
-                    let assigned_path = bb_ctxt.builder.move_path_for(destination);
-                    bb_ctxt.path_map.fill_to(assigned_path.index());
-
-                    bb_ctxt.builder.create_move_path(destination);
+                    self.create_move_path(destination);
                 }
             }
         }
-
-        builder = bb_ctxt.builder;
     }
 
-    // At this point, we may have created some MovePaths that do not
-    // have corresponding entries in the path map.
-    //
-    // (For example, creating the path `a.b.c` may, as a side-effect,
-    // create a path for the parent path `a.b`.)
-    //
-    // All such paths were not referenced ...
-    //
-    // well you know, lets actually try just asserting that the path map *is* complete.
-    assert_eq!(path_map.len(), builder.pre_move_paths.len());
-
-    let pre_move_paths = builder.pre_move_paths;
-    let move_paths: Vec<_> = pre_move_paths.into_iter()
-        .map(|p| p.into_move_path())
-        .collect();
-
-    debug!("{}", {
-        let mut seen: Vec<_> = move_paths.iter().map(|_| false).collect();
-        for (j, &MoveOut { ref path, ref source }) in moves.iter().enumerate() {
-            debug!("MovePathData moves[{}]: MoveOut {{ path: {:?} = {:?}, source: {:?} }}",
-                   j, path, move_paths[path.index()], source);
-            seen[path.index()] = true;
-        }
-        for (j, path) in move_paths.iter().enumerate() {
-            if !seen[j] {
-                debug!("MovePathData move_paths[{}]: {:?}", j, path);
+    fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) {
+        match *operand {
+            Operand::Constant(..) => {} // not-a-move
+            Operand::Consume(ref lval) => { // a move
+                self.gather_move(loc, lval);
             }
         }
-        "done dumping MovePathData"
-    });
-
-    MoveData {
-        move_paths: MovePathData { move_paths: move_paths, },
-        moves: moves,
-        loc_map: LocMap { map: loc_map },
-        path_map: PathMap { map: path_map },
-        rev_lookup: builder.rev_lookup,
     }
-}
 
-struct BlockContext<'b, 'tcx: 'b> {
-    _tcx: TyCtxt<'b, 'tcx, 'tcx>,
-    moves: &'b mut Vec<MoveOut>,
-    builder: MovePathDataBuilder<'tcx>,
-    path_map: &'b mut Vec<Vec<MoveOutIndex>>,
-    loc_map_bb: &'b mut Vec<Vec<MoveOutIndex>>,
-}
+    fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) {
+        debug!("gather_move({:?}, {:?})", loc, lval);
 
-impl<'b, 'tcx: 'b> BlockContext<'b, 'tcx> {
-    fn on_move_out_lval(&mut self,
-                        stmt_kind: StmtKind,
-                        lval: &Lvalue<'tcx>,
-                        source: Location) {
-        let i = source.statement_index;
-        let index = MoveOutIndex::new(self.moves.len());
-
-        let path = self.builder.move_path_for(lval);
-        self.moves.push(MoveOut { path: path, source: source.clone() });
-        self.path_map.fill_to(path.index());
-
-        debug!("ctxt: {:?} add consume of lval: {:?} \
-                at index: {:?} \
-                to path_map for path: {:?} and \
-                to loc_map for loc: {:?}",
-               stmt_kind, lval, index, path, source);
-
-        debug_assert!(path.index() < self.path_map.len());
-        // this is actually a questionable assert; at the very
-        // least, incorrect input code can probably cause it to
-        // fire.
-        assert!(self.path_map[path.index()].iter().find(|idx| **idx == index).is_none());
-        self.path_map[path.index()].push(index);
-
-        debug_assert!(i < self.loc_map_bb.len());
-        debug_assert!(self.loc_map_bb[i].iter().find(|idx| **idx == index).is_none());
-        self.loc_map_bb[i].push(index);
-    }
-
-    fn on_operand(&mut self, stmt_kind: StmtKind, operand: &Operand<'tcx>, source: Location) {
-        match *operand {
-            Operand::Constant(..) => {} // not-a-move
-            Operand::Consume(ref lval) => { // a move
-                self.on_move_out_lval(stmt_kind, lval, source);
-            }
+        let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx);
+        if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) {
+            debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty);
+            return
         }
+
+        let path = self.move_path_for(lval).unwrap_or_else(|_| {
+            // Moving out of a bad path. Eventually, this should be a MIR
+            // borrowck error instead of a bug.
+            span_bug!(self.mir.span,
+                      "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
+                      lval, lv_ty, loc);
+        });
+        let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
+
+        debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
+               loc, lval, move_out, path);
+
+        self.data.path_map[path].push(move_out);
+        self.data.loc_map[loc].push(move_out);
     }
 }
diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs
index e035e268b1c..7c2410a2c14 100644
--- a/src/librustc_borrowck/borrowck/mir/mod.rs
+++ b/src/librustc_borrowck/borrowck/mir/mod.rs
@@ -34,8 +34,7 @@ use self::dataflow::{DataflowOperator};
 use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults};
 use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
 use self::dataflow::{DefinitelyInitializedLvals};
-use self::gather_moves::{MoveData, MovePathIndex};
-use self::gather_moves::{MovePathContent, MovePathData};
+use self::gather_moves::{MoveData, MovePathIndex, LookupResult};
 
 fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<P<MetaItem>> {
     for attr in attrs {
@@ -78,8 +77,8 @@ pub fn borrowck_mir<'a, 'tcx: 'a>(
 
     let tcx = bcx.tcx;
 
-    let move_data = MoveData::gather_moves(mir, tcx);
     let param_env = ty::ParameterEnvironment::for_item(tcx, id);
+    let move_data = MoveData::gather_moves(mir, tcx, &param_env);
     let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
     let flow_inits =
         do_dataflow(tcx, mir, id, attributes, &mdpe, MaybeInitializedLvals::new(tcx, mir));
@@ -211,23 +210,23 @@ impl DropFlagState {
     }
 }
 
-fn move_path_children_matching<'tcx, F>(move_paths: &MovePathData<'tcx>,
+fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
                                         path: MovePathIndex,
                                         mut cond: F)
                                         -> Option<MovePathIndex>
     where F: FnMut(&repr::LvalueProjection<'tcx>) -> bool
 {
-    let mut next_child = move_paths[path].first_child;
+    let mut next_child = move_data.move_paths[path].first_child;
     while let Some(child_index) = next_child {
-        match move_paths[child_index].content {
-            MovePathContent::Lvalue(repr::Lvalue::Projection(ref proj)) => {
+        match move_data.move_paths[child_index].lvalue {
+            repr::Lvalue::Projection(ref proj) => {
                 if cond(proj) {
                     return Some(child_index)
                 }
             }
             _ => {}
         }
-        next_child = move_paths[child_index].next_sibling;
+        next_child = move_data.move_paths[child_index].next_sibling;
     }
 
     None
@@ -272,6 +271,24 @@ fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
     }
 }
 
+fn on_lookup_result_bits<'a, 'tcx, F>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &Mir<'tcx>,
+    move_data: &MoveData<'tcx>,
+    lookup_result: LookupResult,
+    each_child: F)
+    where F: FnMut(MovePathIndex)
+{
+    match lookup_result {
+        LookupResult::Parent(..) => {
+            // access to untracked value - do not touch children
+        }
+        LookupResult::Exact(e) => {
+            on_all_children_bits(tcx, mir, move_data, e, each_child)
+        }
+    }
+}
+
 fn on_all_children_bits<'a, 'tcx, F>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     mir: &Mir<'tcx>,
@@ -286,12 +303,8 @@ fn on_all_children_bits<'a, 'tcx, F>(
         move_data: &MoveData<'tcx>,
         path: MovePathIndex) -> bool
     {
-        match move_data.move_paths[path].content {
-            MovePathContent::Lvalue(ref lvalue) => {
-                lvalue_contents_drop_state_cannot_differ(tcx, mir, lvalue)
-            }
-            _ => true
-        }
+        lvalue_contents_drop_state_cannot_differ(
+            tcx, mir, &move_data.move_paths[path].lvalue)
     }
 
     fn on_all_children_bits<'a, 'tcx, F>(
@@ -327,10 +340,10 @@ fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
     let move_data = &ctxt.move_data;
     for (arg, _) in mir.arg_decls.iter_enumerated() {
         let lvalue = repr::Lvalue::Arg(arg);
-        let move_path_index = move_data.rev_lookup.find(&lvalue);
-        on_all_children_bits(tcx, mir, move_data,
-                             move_path_index,
-                             |moi| callback(moi, DropFlagState::Present));
+        let lookup_result = move_data.rev_lookup.find(&lvalue);
+        on_lookup_result_bits(tcx, mir, move_data,
+                              lookup_result,
+                              |moi| callback(moi, DropFlagState::Present));
     }
 }
 
@@ -352,11 +365,10 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
         debug!("moving out of path {:?}", move_data.move_paths[path]);
 
         // don't move out of non-Copy things
-        if let MovePathContent::Lvalue(ref lvalue) = move_data.move_paths[path].content {
-            let ty = lvalue.ty(mir, tcx).to_ty(tcx);
-            if !ty.moves_by_default(tcx, param_env, DUMMY_SP) {
-                continue;
-            }
+        let lvalue = &move_data.move_paths[path].lvalue;
+        let ty = lvalue.ty(mir, tcx).to_ty(tcx);
+        if !ty.moves_by_default(tcx, param_env, DUMMY_SP) {
+            continue;
         }
 
         on_all_children_bits(tcx, mir, move_data,
@@ -372,9 +384,9 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
             }
             repr::StatementKind::Assign(ref lvalue, _) => {
                 debug!("drop_flag_effects: assignment {:?}", stmt);
-                 on_all_children_bits(tcx, mir, move_data,
-                                     move_data.rev_lookup.find(lvalue),
-                                     |moi| callback(moi, DropFlagState::Present))
+                 on_lookup_result_bits(tcx, mir, move_data,
+                                       move_data.rev_lookup.find(lvalue),
+                                       |moi| callback(moi, DropFlagState::Present))
             }
             repr::StatementKind::StorageLive(_) |
             repr::StatementKind::StorageDead(_) => {}
@@ -383,9 +395,9 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
             debug!("drop_flag_effects: replace {:?}", block.terminator());
             match block.terminator().kind {
                 repr::TerminatorKind::DropAndReplace { ref location, .. } => {
-                    on_all_children_bits(tcx, mir, move_data,
-                                         move_data.rev_lookup.find(location),
-                                         |moi| callback(moi, DropFlagState::Present))
+                    on_lookup_result_bits(tcx, mir, move_data,
+                                          move_data.rev_lookup.find(location),
+                                          |moi| callback(moi, DropFlagState::Present))
                 }
                 _ => {
                     // other terminators do not contain move-ins
diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs
index cfd1ec09968..e13da253102 100644
--- a/src/librustc_trans/mir/analyze.rs
+++ b/src/librustc_trans/mir/analyze.rs
@@ -180,7 +180,6 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
                 LvalueContext::Store |
                 LvalueContext::Inspect |
                 LvalueContext::Borrow { .. } |
-                LvalueContext::Slice { .. } |
                 LvalueContext::Projection => {
                     self.mark_as_lvalue(index);
                 }