about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2017-08-21 12:48:33 +0200
committerFelix S. Klock II <pnkfelix@pnkfx.org>2017-09-25 14:02:34 +0200
commitd9d10c1628faea4b2b55a1d31b7ba7075edba018 (patch)
treebfe1f317408f3bccd1f01fdb21537dfa5f9a41d2
parentc4c32b24bd757975b693fbbb46320503d6415f55 (diff)
downloadrust-d9d10c1628faea4b2b55a1d31b7ba7075edba018.tar.gz
rust-d9d10c1628faea4b2b55a1d31b7ba7075edba018.zip
Make mir-borrowck more closely match (draft) NLL RFC.
In particular:

 * introduce the shallow/deep distinction for read/write accesses

 * use the notions of prefixes, shallow prefixes, and supporting prefixes
   rather than trying to recreate the restricted sets from ast-borrowck.

 * Add shallow reads of Discriminant and ArrayLength, and treat them
   as artificial fields when doing prefix traversals.
-rw-r--r--src/librustc_mir/borrow_check.rs420
1 files changed, 339 insertions, 81 deletions
diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs
index 8cc74ef0447..3ea6cb9ce6e 100644
--- a/src/librustc_mir/borrow_check.rs
+++ b/src/librustc_mir/borrow_check.rs
@@ -173,14 +173,23 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
         let span = stmt.source_info.span;
         match stmt.kind {
             StatementKind::Assign(ref lhs, ref rhs) => {
+                // NOTE: NLL RFC calls for *shallow* write; using Deep
+                // for short-term compat w/ AST-borrowck. Also, switch
+                // to shallow requires to dataflow: "if this is an
+                // assignment `lv = <rvalue>`, then any loan for some
+                // path P of which `lv` is a prefix is killed."
                 self.mutate_lvalue(ContextKind::AssignLhs.new(location),
-                                   (lhs, span), JustWrite, flow_state);
+                                   (lhs, span), Deep, JustWrite, flow_state);
+
                 self.consume_rvalue(ContextKind::AssignRhs.new(location),
                                     (rhs, span), location, flow_state);
             }
             StatementKind::SetDiscriminant { ref lvalue, variant_index: _ } => {
                 self.mutate_lvalue(ContextKind::SetDiscrim.new(location),
-                                   (lvalue, span), JustWrite, flow_state);
+                                   (lvalue, span),
+                                   Shallow(Some(ArtificialField::Discriminant)),
+                                   JustWrite,
+                                   flow_state);
             }
             StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
                 for (o, output) in asm.outputs.iter().zip(outputs) {
@@ -192,6 +201,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
                     } else {
                         self.mutate_lvalue(ContextKind::InlineAsm.new(location),
                                            (output, span),
+                                           Deep,
                                            if o.is_rw { WriteAndRead } else { JustWrite },
                                            flow_state);
                     }
@@ -209,15 +219,15 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
             StatementKind::Nop |
             StatementKind::Validate(..) |
             StatementKind::StorageLive(..) => {
-                // ignored by borrowck
+                // `Nop`, `Validate`, and `StorageLive` are irrelevant
+                // to borrow check.
             }
 
             StatementKind::StorageDead(local) => {
-                // causes non-drop values to be dropped.
-                self.consume_lvalue(ContextKind::StorageDead.new(location),
-                                    ConsumeKind::Consume,
-                                    (&Lvalue::Local(local), span),
-                                    flow_state)
+                self.access_lvalue(ContextKind::StorageDead.new(location),
+                                   (&Lvalue::Local(local), span),
+                                   (Shallow(None), Write(WriteKind::StorageDead)),
+                                   flow_state);
             }
         }
     }
@@ -246,7 +256,10 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
                                              target: _,
                                              unwind: _ } => {
                 self.mutate_lvalue(ContextKind::DropAndReplace.new(loc),
-                                   (drop_lvalue, span), JustWrite, flow_state);
+                                   (drop_lvalue, span),
+                                   Deep,
+                                   JustWrite,
+                                   flow_state);
                 self.consume_operand(ContextKind::DropAndReplace.new(loc),
                                      ConsumeKind::Drop,
                                      (new_value, span), flow_state);
@@ -262,7 +275,10 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
                 }
                 if let Some((ref dest, _/*bb*/)) = *destination {
                     self.mutate_lvalue(ContextKind::CallDest.new(loc),
-                                       (dest, span), JustWrite, flow_state);
+                                       (dest, span),
+                                       Deep,
+                                       JustWrite,
+                                       flow_state);
                 }
             }
             TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
@@ -309,29 +325,121 @@ enum ConsumeKind { Drop, Consume }
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 enum Control { Continue, Break }
 
+use self::ShallowOrDeep::{Shallow, Deep};
+use self::ReadOrWrite::{Read, Write};
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ArtificialField {
+    Discriminant,
+    ArrayLength,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ShallowOrDeep {
+    /// From the RFC: "A *shallow* access means that the immediate
+    /// fields reached at LV are accessed, but references or pointers
+    /// found within are not dereferenced. Right now, the only access
+    /// that is shallow is an assignment like `x = ...;`, which would
+    /// be a *shallow write* of `x`."
+    Shallow(Option<ArtificialField>),
+
+    /// From the RFC: "A *deep* access means that all data reachable
+    /// through the given lvalue may be invalidated or accesses by
+    /// this action."
+    Deep,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ReadOrWrite {
+    /// From the RFC: "A *read* means that the existing data may be
+    /// read, but will not be changed."
+    Read(ReadKind),
+
+    /// From the RFC: "A *write* means that the data may be mutated to
+    /// new values or otherwise invalidated (for example, it could be
+    /// de-initialized, as in a move operation).
+    Write(WriteKind),
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ReadKind {
+    Borrow(BorrowKind),
+    Copy,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum WriteKind {
+    StorageDead,
+    MutableBorrow(BorrowKind),
+    Mutate,
+    Move,
+}
+
 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+    fn access_lvalue(&mut self,
+                     context: Context,
+                     lvalue_span: (&Lvalue<'gcx>, Span),
+                     kind: (ShallowOrDeep, ReadOrWrite),
+                     flow_state: &InProgress<'b, 'gcx>) {
+        // FIXME: also need to check permissions (e.g. reject mut
+        // borrow of immutable ref, moves through non-`Box`-ref)
+        let (sd, rw) = kind;
+        self.each_borrow_involving_path(
+            context, (sd, lvalue_span.0), flow_state, |this, _index, borrow| {
+                match (rw, borrow.kind) {
+                    (Read(_), BorrowKind::Shared) => {
+                        Control::Continue
+                    }
+                    (Read(kind), BorrowKind::Unique) |
+                    (Read(kind), BorrowKind::Mut) => {
+                        match kind {
+                            ReadKind::Copy =>
+                                this.report_use_while_mutably_borrowed(
+                                    context, lvalue_span, borrow),
+                            ReadKind::Borrow(bk) =>
+                                this.report_conflicting_borrow(
+                                    context, lvalue_span,
+                                    (lvalue_span.0, bk), (&borrow.lvalue, borrow.kind)),
+                        }
+                        Control::Break
+                    }
+                    (Write(kind), _) => {
+                        match kind {
+                            WriteKind::MutableBorrow(bk) =>
+                                this.report_conflicting_borrow(
+                                    context, lvalue_span,
+                                    (lvalue_span.0, bk), (&borrow.lvalue, borrow.kind)),
+                            WriteKind::StorageDead |
+                            WriteKind::Mutate =>
+                                this.report_illegal_mutation_of_borrowed(
+                                    context, lvalue_span),
+                            WriteKind::Move =>
+                                this.report_move_out_while_borrowed(
+                                    context, lvalue_span, borrow),
+                        }
+                        Control::Break
+                    }
+                }
+            });
+    }
+
     fn mutate_lvalue(&mut self,
                      context: Context,
                      lvalue_span: (&Lvalue<'gcx>, Span),
+                     kind: ShallowOrDeep,
                      mode: MutateMode,
                      flow_state: &InProgress<'b, 'gcx>) {
         // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
         match mode {
             MutateMode::WriteAndRead => {
-                self.check_if_path_is_moved(context, lvalue_span, flow_state);
+                self.check_if_path_is_moved(context, "update", lvalue_span, flow_state);
             }
             MutateMode::JustWrite => {
                 self.check_if_assigned_path_is_moved(context, lvalue_span, flow_state);
             }
         }
 
-        // check we don't invalidate any outstanding loans
-        self.each_borrow_involving_path(context,
-                                        lvalue_span.0, flow_state, |this, _index, _data| {
-                                            this.report_illegal_mutation_of_borrowed(context,
-                                                                                     lvalue_span);
-                                            Control::Break
-                                        });
+        self.access_lvalue(context, lvalue_span, (kind, Write(WriteKind::Mutate)), flow_state);
 
         // check for reassignments to immutable local variables
         self.check_if_reassignment_to_immutable_state(context, lvalue_span, flow_state);
@@ -340,11 +448,17 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
     fn consume_rvalue(&mut self,
                       context: Context,
                       (rvalue, span): (&Rvalue<'gcx>, Span),
-                      location: Location,
+                      _location: Location,
                       flow_state: &InProgress<'b, 'gcx>) {
         match *rvalue {
             Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => {
-                self.borrow(context, location, bk, (lvalue, span), flow_state)
+                let access_kind = match bk {
+                    BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
+                    BorrowKind::Unique |
+                    BorrowKind::Mut => (Deep, Write(WriteKind::MutableBorrow(bk))),
+                };
+                self.access_lvalue(context, (lvalue, span), access_kind, flow_state);
+                self.check_if_path_is_moved(context, "borrow", (lvalue, span), flow_state);
             }
 
             Rvalue::Use(ref operand) |
@@ -356,8 +470,14 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
             Rvalue::Len(ref lvalue) |
             Rvalue::Discriminant(ref lvalue) => {
-                // len(_)/discriminant(_) merely read, not consume.
-                self.check_if_path_is_moved(context, (lvalue, span), flow_state);
+                let af = match *rvalue {
+                    Rvalue::Len(..) => ArtificialField::ArrayLength,
+                    Rvalue::Discriminant(..) => ArtificialField::Discriminant,
+                    _ => unreachable!(),
+                };
+                self.access_lvalue(
+                    context, (lvalue, span), (Shallow(Some(af)), Read(ReadKind::Copy)), flow_state);
+                self.check_if_path_is_moved(context, "use", (lvalue, span), flow_state);
             }
 
             Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) |
@@ -388,8 +508,9 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
                        (operand, span): (&Operand<'gcx>, Span),
                        flow_state: &InProgress<'b, 'gcx>) {
         match *operand {
-            Operand::Consume(ref lvalue) =>
-                self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state),
+            Operand::Consume(ref lvalue) => {
+                self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state)
+            }
             Operand::Constant(_) => {}
         }
     }
@@ -405,26 +526,10 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
             self.fake_infer_ctxt.type_moves_by_default(self.param_env, ty, DUMMY_SP);
         if moves_by_default {
             // move of lvalue: check if this is move of already borrowed path
-            self.each_borrow_involving_path(
-                context, lvalue_span.0, flow_state, |this, _idx, borrow| {
-                    if !borrow.compatible_with(BorrowKind::Mut) {
-                        this.report_move_out_while_borrowed(context, lvalue_span, borrow);
-                        Control::Break
-                    } else {
-                        Control::Continue
-                    }
-                });
+            self.access_lvalue(context, lvalue_span, (Deep, Write(WriteKind::Move)), flow_state);
         } else {
             // copy of lvalue: check if this is "copy of frozen path" (FIXME: see check_loans.rs)
-            self.each_borrow_involving_path(
-                context, lvalue_span.0, flow_state, |this, _idx, borrow| {
-                    if !borrow.compatible_with(BorrowKind::Shared) {
-                        this.report_use_while_mutably_borrowed(context, lvalue_span, borrow);
-                        Control::Break
-                    } else {
-                        Control::Continue
-                    }
-                });
+            self.access_lvalue(context, lvalue_span, (Deep, Read(ReadKind::Copy)), flow_state);
         }
 
         // Finally, check if path was already moved.
@@ -435,11 +540,12 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
                 // skip this check in that case).
             }
             ConsumeKind::Consume => {
-                self.check_if_path_is_moved(context, lvalue_span, flow_state);
+                self.check_if_path_is_moved(context, "use", lvalue_span, flow_state);
             }
         }
     }
 
+    #[cfg(not_anymore)]
     fn borrow(&mut self,
               context: Context,
               location: Location,
@@ -494,6 +600,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
     fn check_if_path_is_moved(&mut self,
                               context: Context,
+                              desired_action: &str,
                               lvalue_span: (&Lvalue<'gcx>, Span),
                               flow_state: &InProgress<'b, 'gcx>) {
         // FIXME: analogous code in check_loans first maps `lvalue` to
@@ -505,7 +612,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
         if let Some(mpi) = self.move_path_for_lvalue(context, move_data, lvalue) {
             if maybe_uninits.curr_state.contains(&mpi) {
                 // find and report move(s) that could cause this to be uninitialized
-                self.report_use_of_moved(context, lvalue_span);
+                self.report_use_of_moved(context, desired_action, lvalue_span);
             } else {
                 // sanity check: initialized on *some* path, right?
                 assert!(flow_state.inits.curr_state.contains(&mpi));
@@ -572,8 +679,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
                                     // check_loans.rs first maps
                                     // `base` to its base_path.
 
-                                    self.check_if_path_is_moved(context,
-                                                                (base, span), flow_state);
+                                    self.check_if_path_is_moved(
+                                        context, "assignment", (base, span), flow_state);
 
                                     // (base initialized; no need to
                                     // recur further)
@@ -591,6 +698,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
         }
     }
 
+    #[cfg(not_anymore)]
     fn check_for_conflicting_loans(&mut self,
                                    context: Context,
                                    _location: Location,
@@ -651,11 +759,13 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
     fn each_borrow_involving_path<F>(&mut self,
                                      _context: Context,
-                                     lvalue: &Lvalue<'gcx>,
+                                     access_lvalue: (ShallowOrDeep, &Lvalue<'gcx>),
                                      flow_state: &InProgress<'b, 'gcx>,
                                      mut op: F)
         where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'gcx>) -> Control
     {
+        let (access, lvalue) = access_lvalue;
+
         // FIXME: analogous code in check_loans first maps `lvalue` to
         // its base_path.
 
@@ -664,47 +774,189 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
         // check for loan restricting path P being used. Accounts for
         // borrows of P, P.a.b, etc.
-        for i in flow_state.borrows.elems_incoming() {
-            // FIXME: check_loans.rs filtered this to "in scope"
-            // loans; i.e. it took a scope S and checked that each
-            // restriction's kill_scope was a superscope of S.
+        'next_borrow: for i in flow_state.borrows.elems_incoming() {
             let borrowed = &data[i];
-            for restricted in self.restrictions(&borrowed.lvalue) {
-                if restricted == lvalue {
+
+            // Is `lvalue` (or a prefix of it) already borrowed? If
+            // so, that's relevant.
+            //
+            // FIXME: Differs from AST-borrowck; includes drive-by fix
+            // to #38899. Will probably need back-compat mode flag.
+            for accessed_prefix in self.prefixes(lvalue, PrefixSet::All) {
+                if *accessed_prefix == borrowed.lvalue {
+                    // FIXME: pass in prefix here too? And/or enum
+                    // describing case we are in?
                     let ctrl = op(self, i, borrowed);
                     if ctrl == Control::Break { return; }
                 }
             }
-        }
 
-        // check for loans (not restrictions) on any base path.
-        // e.g. Rejects `{ let x = &mut a.b; let y = a.b.c; }`,
-        // since that moves out of borrowed path `a.b`.
-        //
-        // Limiting to loans (not restrictions) keeps this one
-        // working: `{ let x = &mut a.b; let y = a.c; }`
-        let mut cursor = lvalue;
-        loop {
-            // FIXME: check_loans.rs invoked `op` *before* cursor
-            // shift here.  Might just work (and even avoid redundant
-            // errors?) given code above?  But for now, I want to try
-            // doing what I think is more "natural" check.
-            for i in flow_state.borrows.elems_incoming() {
-                let borrowed = &data[i];
-                if borrowed.lvalue == *cursor {
+            // Is `lvalue` a prefix (modulo access type) of the
+            // `borrowed.lvalue`? If so, that's relevant.
+
+            let prefix_kind = match access {
+                Shallow(Some(ArtificialField::Discriminant)) |
+                Shallow(Some(ArtificialField::ArrayLength)) => {
+                    // The discriminant and array length are like
+                    // additional fields on the type; they do not
+                    // overlap any existing data there. Furthermore,
+                    // they cannot actually be a prefix of any
+                    // borrowed lvalue (at least in MIR as it is
+                    // currently.)
+                    continue 'next_borrow;
+                }
+                Shallow(None) => PrefixSet::Shallow,
+                Deep => PrefixSet::Supporting,
+            };
+
+            for borrowed_prefix in self.prefixes(&borrowed.lvalue, prefix_kind) {
+                if borrowed_prefix == lvalue {
+                    // FIXME: pass in prefix here too? And/or enum
+                    // describing case we are in?
                     let ctrl = op(self, i, borrowed);
                     if ctrl == Control::Break { return; }
                 }
             }
+        }
+    }
+}
+
+use self::prefixes::PrefixSet;
 
-            match *cursor {
-                Lvalue::Local(_) | Lvalue::Static(_) => break,
-                Lvalue::Projection(ref proj) => cursor = &proj.base,
+/// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an
+/// lvalue are formed by stripping away fields and derefs, except that
+/// we stop when we reach the deref of a shared reference. [...] "
+///
+/// "Shallow prefixes are found by stripping away fields, but stop at
+/// any dereference. So: writing a path like `a` is illegal if `a.b`
+/// is borrowed. But: writing `a` is legal if `*a` is borrowed,
+/// whether or not `a` is a shared or mutable reference. [...] "
+mod prefixes {
+    use super::{MirBorrowckCtxt};
+
+    use rustc::hir;
+    use rustc::ty::{self, TyCtxt};
+    use rustc::mir::{Lvalue, Mir, ProjectionElem};
+
+    pub(super) struct Prefixes<'c, 'tcx: 'c> {
+        mir: &'c Mir<'tcx>,
+        tcx: TyCtxt<'c, 'tcx, 'tcx>,
+        kind: PrefixSet,
+        next: Option<&'c Lvalue<'tcx>>,
+    }
+
+    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
+    pub(super) enum PrefixSet {
+        All,
+        Shallow,
+        Supporting,
+    }
+
+    impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
+        pub(super) fn prefixes<'d>(&self,
+                                   lvalue: &'d Lvalue<'gcx>,
+                                   kind: PrefixSet)
+                                   -> Prefixes<'d, 'gcx> where 'b: 'd
+        {
+            Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx }
+        }
+    }
+
+    impl<'c, 'tcx> Iterator for Prefixes<'c, 'tcx> {
+        type Item = &'c Lvalue<'tcx>;
+        fn next(&mut self) -> Option<Self::Item> {
+            let mut cursor = match self.next {
+                None => return None,
+                Some(lvalue) => lvalue,
+            };
+
+            // Post-processing `lvalue`: Enqueue any remaining
+            // work. Also, `lvalue` may not be a prefix itself, but
+            // may hold one further down (e.g. we never return
+            // downcasts here, but may return a base of a downcast).
+
+            'cursor: loop {
+                let proj = match *cursor {
+                    Lvalue::Local(_) | // search yielded this leaf
+                    Lvalue::Static(_) => {
+                        self.next = None;
+                        return Some(cursor);
+                    }
+
+                    Lvalue::Projection(ref proj) => proj,
+                };
+
+                match proj.elem {
+                    ProjectionElem::Field(_/*field*/, _/*ty*/) => {
+                        // FIXME: add union handling
+                        self.next = Some(&proj.base);
+                        return Some(cursor);
+                    }
+                    ProjectionElem::Downcast(..) |
+                    ProjectionElem::Subslice { .. } |
+                    ProjectionElem::ConstantIndex { .. } |
+                    ProjectionElem::Index(_) => {
+                        cursor = &proj.base;
+                        continue 'cursor;
+                    }
+                    ProjectionElem::Deref => {
+                        // (handled below)
+                    }
+                }
+
+                assert_eq!(proj.elem, ProjectionElem::Deref);
+
+                match self.kind {
+                    PrefixSet::Shallow => {
+                        // shallow prefixes are found by stripping away
+                        // fields, but stop at *any* dereference.
+                        // So we can just stop the traversal now.
+                        self.next = None;
+                        return Some(cursor);
+                    }
+                    PrefixSet::All => {
+                        // all prefixes: just blindly enqueue the base
+                        // of the projection
+                        self.next = Some(&proj.base);
+                        return Some(cursor);
+                    }
+                    PrefixSet::Supporting => {
+                        // fall through!
+                    }
+                }
+
+                assert_eq!(self.kind, PrefixSet::Supporting);
+                // supporting prefixes: strip away fields and
+                // derefs, except we stop at the deref of a shared
+                // reference.
+
+                let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+                match ty.sty {
+                    ty::TyRawPtr(_) |
+                    ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
+                        // don't continue traversing over derefs of raw pointers or shared borrows.
+                        self.next = None;
+                        return Some(cursor);
+                    }
+
+                    ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
+                        self.next = Some(&proj.base);
+                        return Some(cursor);
+                    }
+
+                    ty::TyAdt(..) if ty.is_box() => {
+                        self.next = Some(&proj.base);
+                        return Some(cursor);
+                    }
+
+                    _ => panic!("unknown type fed to Projection Deref."),
+                }
             }
         }
     }
-}
+    }
 
+#[cfg(not_anymore)]
 mod restrictions {
     use super::MirBorrowckCtxt;
 
@@ -724,7 +976,7 @@ mod restrictions {
                                        -> Restrictions<'d, 'gcx> where 'b: 'd
         {
             let lvalue_stack = if self.has_restrictions(lvalue) { vec![lvalue] } else { vec![] };
-            Restrictions { lvalue_stack: lvalue_stack, mir: self.mir, tcx: self.tcx }
+            Restrictions { lvalue_stack, mir: self.mir, tcx: self.tcx }
         }
 
         fn has_restrictions(&self, lvalue: &Lvalue<'gcx>) -> bool {
@@ -895,9 +1147,10 @@ mod restrictions {
 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
     fn report_use_of_moved(&mut self,
                            _context: Context,
+                           desired_action: &str,
                            (lvalue, span): (&Lvalue, Span)) {
         self.tcx.cannot_act_on_uninitialized_variable(span,
-                                                      "use",
+                                                      desired_action,
                                                       &self.describe_lvalue(lvalue),
                                                       Origin::Mir)
                 .span_label(span, format!("use of possibly uninitialized `{}`",
@@ -939,14 +1192,16 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
     fn report_conflicting_borrow(&mut self,
                                  _context: Context,
                                  (lvalue, span): (&Lvalue, Span),
-                                 loan1: &BorrowData,
-                                 loan2: &BorrowData) {
+                                 loan1: (&Lvalue, BorrowKind),
+                                 loan2: (&Lvalue, BorrowKind)) {
+        let (loan1_lvalue, loan1_kind) = loan1;
+        let (loan2_lvalue, loan2_kind) = loan2;
         // FIXME: obviously falsifiable. Generalize for non-eq lvalues later.
-        assert_eq!(loan1.lvalue, loan2.lvalue);
+        assert_eq!(loan1_lvalue, loan2_lvalue);
 
         // FIXME: supply non-"" `opt_via` when appropriate
-        let mut err = match (loan1.kind, "immutable", "mutable",
-                             loan2.kind, "immutable", "mutable") {
+        let mut err = match (loan1_kind, "immutable", "mutable",
+                             loan2_kind, "immutable", "mutable") {
             (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
             (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) =>
                 self.tcx.cannot_reborrow_already_borrowed(
@@ -1065,6 +1320,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
     // FIXME: needs to be able to express errors analogous to check_loans.rs
+    #[cfg(not_anymore)]
     fn conflicts_with(&self, loan1: &BorrowData<'gcx>, loan2: &BorrowData<'gcx>) -> bool {
         if loan1.compatible_with(loan2.kind) { return false; }
 
@@ -1129,8 +1385,8 @@ enum ContextKind {
     CallOperand,
     CallDest,
     Assert,
-    StorageDead,
     Yield,
+    StorageDead,
 }
 
 impl ContextKind {
@@ -1262,6 +1518,7 @@ impl<BD> FlowInProgress<BD> where BD: BitDenotation {
         self.curr_state.subtract(&self.stmt_kill);
     }
 
+    #[allow(dead_code)]
     fn elems_generated(&self) -> indexed_set::Elems<BD::Idx> {
         let univ = self.base_results.sets().bits_per_block();
         self.stmt_gen.elems(univ)
@@ -1274,6 +1531,7 @@ impl<BD> FlowInProgress<BD> where BD: BitDenotation {
 }
 
 impl<'tcx> BorrowData<'tcx> {
+    #[allow(dead_code)]
     fn compatible_with(&self, bk: BorrowKind) -> bool {
         match (self.kind, bk) {
             (BorrowKind::Shared, BorrowKind::Shared) => true,