about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-07-21 03:25:05 +0000
committerbors <bors@rust-lang.org>2019-07-21 03:25:05 +0000
commit1301422a6c2e8916560b8cc2f0564f38d8858a75 (patch)
tree90b9983673a3961a15ad90e87674f50e9c80b5b9
parent95b1fe560d2bd8472f250fb8cfd2168520a58405 (diff)
parenta8ceeeb5a0f4a5d274ef7a0f434c5d16e09f864b (diff)
downloadrust-1301422a6c2e8916560b8cc2f0564f38d8858a75.tar.gz
rust-1301422a6c2e8916560b8cc2f0564f38d8858a75.zip
Auto merge of #60913 - spastorino:place2_4, r=oli-obk
Place 2.0 change from enum to struct

r? @oli-obk
-rw-r--r--src/librustc/mir/mod.rs141
-rw-r--r--src/librustc/mir/tcx.rs19
-rw-r--r--src/librustc/mir/visit.rs37
-rw-r--r--src/librustc_codegen_ssa/mir/analyze.rs131
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs47
-rw-r--r--src/librustc_codegen_ssa/mir/operand.rs20
-rw-r--r--src/librustc_codegen_ssa/mir/place.rs77
-rw-r--r--src/librustc_codegen_ssa/mir/rvalue.rs13
-rw-r--r--src/librustc_codegen_ssa/mir/statement.rs11
-rw-r--r--src/librustc_mir/borrow_check/borrow_set.rs7
-rw-r--r--src/librustc_mir/borrow_check/conflict_errors.rs199
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs207
-rw-r--r--src/librustc_mir/borrow_check/mod.rs431
-rw-r--r--src/librustc_mir/borrow_check/move_errors.rs66
-rw-r--r--src/librustc_mir/borrow_check/mutability_errors.rs279
-rw-r--r--src/librustc_mir/borrow_check/nll/constraint_generation.rs5
-rw-r--r--src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs69
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs50
-rw-r--r--src/librustc_mir/borrow_check/path_utils.rs2
-rw-r--r--src/librustc_mir/borrow_check/place_ext.rs2
-rw-r--r--src/librustc_mir/borrow_check/places_conflict.rs25
-rw-r--r--src/librustc_mir/borrow_check/prefixes.rs85
-rw-r--r--src/librustc_mir/borrow_check/used_muts.rs9
-rw-r--r--src/librustc_mir/build/expr/as_place.rs11
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs46
-rw-r--r--src/librustc_mir/build/expr/into.rs2
-rw-r--r--src/librustc_mir/build/matches/mod.rs45
-rw-r--r--src/librustc_mir/build/scope.rs10
-rw-r--r--src/librustc_mir/dataflow/drop_flag_effects.rs6
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs4
-rw-r--r--src/librustc_mir/dataflow/impls/mod.rs6
-rw-r--r--src/librustc_mir/dataflow/impls/storage_liveness.rs12
-rw-r--r--src/librustc_mir/dataflow/mod.rs4
-rw-r--r--src/librustc_mir/dataflow/move_paths/builder.rs52
-rw-r--r--src/librustc_mir/dataflow/move_paths/mod.rs9
-rw-r--r--src/librustc_mir/interpret/operand.rs5
-rw-r--r--src/librustc_mir/transform/add_retag.rs51
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs27
-rw-r--r--src/librustc_mir/transform/const_prop.rs11
-rw-r--r--src/librustc_mir/transform/copy_prop.rs40
-rw-r--r--src/librustc_mir/transform/elaborate_drops.rs12
-rw-r--r--src/librustc_mir/transform/generator.rs50
-rw-r--r--src/librustc_mir/transform/inline.rs25
-rw-r--r--src/librustc_mir/transform/instcombine.rs17
-rw-r--r--src/librustc_mir/transform/promote_consts.rs40
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs163
-rw-r--r--src/librustc_mir/transform/remove_noop_landing_pads.rs5
-rw-r--r--src/librustc_mir/transform/rustc_peek.rs14
-rw-r--r--src/librustc_mir/transform/uniform_array_move_out.rs101
-rw-r--r--src/librustc_mir/util/alignment.rs13
-rw-r--r--src/librustc_mir/util/elaborate_drops.rs11
51 files changed, 1735 insertions, 989 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 783964c701a..d8b641fbe31 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1718,11 +1718,11 @@ impl<'tcx> Debug for Statement<'tcx> {
 #[derive(
     Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, HashStable,
 )]
-pub enum Place<'tcx> {
-    Base(PlaceBase<'tcx>),
+pub struct Place<'tcx> {
+    pub base: PlaceBase<'tcx>,
 
     /// projection out of a place (access a field, deref a pointer, etc)
-    Projection(Box<Projection<'tcx>>),
+    pub projection: Option<Box<Projection<'tcx>>>,
 }
 
 #[derive(
@@ -1761,7 +1761,7 @@ impl_stable_hash_for!(struct Static<'tcx> {
     Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, HashStable,
 )]
 pub struct Projection<'tcx> {
-    pub base: Place<'tcx>,
+    pub base: Option<Box<Projection<'tcx>>>,
     pub elem: PlaceElem<'tcx>,
 }
 
@@ -1826,8 +1826,17 @@ newtype_index! {
     }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct PlaceRef<'a, 'tcx> {
+    pub base: &'a PlaceBase<'tcx>,
+    pub projection: &'a Option<Box<Projection<'tcx>>>,
+}
+
 impl<'tcx> Place<'tcx> {
-    pub const RETURN_PLACE: Place<'tcx> = Place::Base(PlaceBase::Local(RETURN_PLACE));
+    pub const RETURN_PLACE: Place<'tcx> = Place {
+        base: PlaceBase::Local(RETURN_PLACE),
+        projection: None,
+    };
 
     pub fn field(self, f: Field, ty: Ty<'tcx>) -> Place<'tcx> {
         self.elem(ProjectionElem::Field(f, ty))
@@ -1853,7 +1862,10 @@ impl<'tcx> Place<'tcx> {
     }
 
     pub fn elem(self, elem: PlaceElem<'tcx>) -> Place<'tcx> {
-        Place::Projection(Box::new(Projection { base: self, elem }))
+        Place {
+            base: self.base,
+            projection: Some(Box::new(Projection { base: self.projection, elem })),
+        }
     }
 
     /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
@@ -1862,54 +1874,77 @@ impl<'tcx> Place<'tcx> {
     // FIXME: can we safely swap the semantics of `fn base_local` below in here instead?
     pub fn local_or_deref_local(&self) -> Option<Local> {
         match self {
-            Place::Base(PlaceBase::Local(local))
-            | Place::Projection(box Projection {
-                base: Place::Base(PlaceBase::Local(local)),
-                elem: ProjectionElem::Deref,
-            }) => Some(*local),
+            Place {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } |
+            Place {
+                base: PlaceBase::Local(local),
+                projection: Some(box Projection {
+                    base: None,
+                    elem: ProjectionElem::Deref,
+                }),
+            } => Some(*local),
             _ => None,
         }
     }
 
-    /// Finds the innermost `Local` from this `Place`.
-    pub fn base_local(&self) -> Option<Local> {
-        let mut place = self;
-        loop {
-            match place {
-                Place::Projection(proj) => place = &proj.base,
-                Place::Base(PlaceBase::Static(_)) => return None,
-                Place::Base(PlaceBase::Local(local)) => return Some(*local),
-            }
-        }
-    }
-
     /// Recursively "iterates" over place components, generating a `PlaceBase` and
     /// `Projections` list and invoking `op` with a `ProjectionsIter`.
     pub fn iterate<R>(
         &self,
         op: impl FnOnce(&PlaceBase<'tcx>, ProjectionsIter<'_, 'tcx>) -> R,
     ) -> R {
-        self.iterate2(&Projections::Empty, op)
+        Place::iterate_over(&self.base, &self.projection, op)
     }
 
-    fn iterate2<R>(
-        &self,
-        next: &Projections<'_, 'tcx>,
+    pub fn iterate_over<R>(
+        place_base: &PlaceBase<'tcx>,
+        place_projection: &Option<Box<Projection<'tcx>>>,
         op: impl FnOnce(&PlaceBase<'tcx>, ProjectionsIter<'_, 'tcx>) -> R,
     ) -> R {
-        match self {
-            Place::Projection(interior) => {
-                interior.base.iterate2(&Projections::List { projection: interior, next }, op)
+        fn iterate_over2<'tcx, R>(
+            place_base: &PlaceBase<'tcx>,
+            place_projection: &Option<Box<Projection<'tcx>>>,
+            next: &Projections<'_, 'tcx>,
+            op: impl FnOnce(&PlaceBase<'tcx>, ProjectionsIter<'_, 'tcx>) -> R,
+        ) -> R {
+            match place_projection {
+                None => {
+                    op(place_base, next.iter())
+                }
+
+                Some(interior) => {
+                    iterate_over2(
+                        place_base,
+                        &interior.base,
+                        &Projections::List {
+                            projection: interior,
+                            next,
+                        },
+                        op,
+                    )
+                }
             }
+        }
 
-            Place::Base(base) => op(base, next.iter()),
+        iterate_over2(place_base, place_projection, &Projections::Empty, op)
+    }
+
+    pub fn as_place_ref(&self) -> PlaceRef<'_, 'tcx> {
+        PlaceRef {
+            base: &self.base,
+            projection: &self.projection,
         }
     }
 }
 
 impl From<Local> for Place<'_> {
     fn from(local: Local) -> Self {
-        Place::Base(local.into())
+        Place {
+            base: local.into(),
+            projection: None,
+        }
     }
 }
 
@@ -1919,6 +1954,36 @@ impl From<Local> for PlaceBase<'_> {
     }
 }
 
+impl<'a, 'tcx> PlaceRef<'a, 'tcx> {
+    pub fn iterate<R>(
+        &self,
+        op: impl FnOnce(&PlaceBase<'tcx>, ProjectionsIter<'_, 'tcx>) -> R,
+    ) -> R {
+        Place::iterate_over(self.base, self.projection, op)
+    }
+
+    /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
+    /// a single deref of a local.
+    //
+    // FIXME: can we safely swap the semantics of `fn base_local` below in here instead?
+    pub fn local_or_deref_local(&self) -> Option<Local> {
+        match self {
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } |
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: Some(box Projection {
+                    base: None,
+                    elem: ProjectionElem::Deref,
+                }),
+            } => Some(*local),
+            _ => None,
+        }
+    }
+}
+
 /// A linked list of projections running up the stack; begins with the
 /// innermost projection and extends to the outermost (e.g., `a.b.c`
 /// would have the place `b` with a "next" pointer to `b.c`).
@@ -3155,18 +3220,14 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
 
 impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> {
     fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
-        match self {
-            &Place::Projection(ref p) => Place::Projection(p.fold_with(folder)),
-            _ => self.clone(),
+        Place {
+            base: self.base.clone(),
+            projection: self.projection.fold_with(folder),
         }
     }
 
     fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
-        if let &Place::Projection(ref p) = self {
-            p.visit_with(visitor)
-        } else {
-            false
-        }
+        self.projection.visit_with(visitor)
     }
 }
 
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 2079a2a34e7..e2f5d192281 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -118,11 +118,15 @@ BraceStructTypeFoldableImpl! {
 }
 
 impl<'tcx> Place<'tcx> {
-    pub fn ty<D>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
-    where
-        D: HasLocalDecls<'tcx>,
+    pub fn ty_from<D>(
+        base: &PlaceBase<'tcx>,
+        projection: &Option<Box<Projection<'tcx>>>,
+        local_decls: &D,
+        tcx: TyCtxt<'tcx>
+    ) -> PlaceTy<'tcx>
+        where D: HasLocalDecls<'tcx>
     {
-        self.iterate(|place_base, place_projections| {
+        Place::iterate_over(base, projection, |place_base, place_projections| {
             let mut place_ty = place_base.ty(local_decls);
 
             for proj in place_projections {
@@ -132,6 +136,13 @@ impl<'tcx> Place<'tcx> {
             place_ty
         })
     }
+
+    pub fn ty<D>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
+    where
+        D: HasLocalDecls<'tcx>,
+    {
+        Place::ty_from(&self.base, &self.projection, local_decls, tcx)
+    }
 }
 
 impl<'tcx> PlaceBase<'tcx> {
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index babce812d4a..c3a4566b5ae 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -159,10 +159,11 @@ macro_rules! make_mir_visitor {
             }
 
             fn visit_projection(&mut self,
+                                place_base: & $($mutability)? PlaceBase<'tcx>,
                                 place: & $($mutability)? Projection<'tcx>,
                                 context: PlaceContext,
                                 location: Location) {
-                self.super_projection(place, context, location);
+                self.super_projection(place_base, place, context, location);
             }
 
             fn visit_constant(&mut self,
@@ -676,19 +677,20 @@ macro_rules! make_mir_visitor {
                             place: & $($mutability)? Place<'tcx>,
                             context: PlaceContext,
                             location: Location) {
-                match place {
-                    Place::Base(place_base) => {
-                        self.visit_place_base(place_base, context, location);
-                    }
-                    Place::Projection(proj) => {
-                        let context = if context.is_mutating_use() {
-                            PlaceContext::MutatingUse(MutatingUseContext::Projection)
-                        } else {
-                            PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
-                        };
+                let mut context = context;
+
+                if place.projection.is_some() {
+                    context = if context.is_mutating_use() {
+                        PlaceContext::MutatingUse(MutatingUseContext::Projection)
+                    } else {
+                        PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
+                    };
+                }
 
-                        self.visit_projection(proj, context, location);
-                    }
+                self.visit_place_base(& $($mutability)? place.base, context, location);
+
+                if let Some(box proj) = & $($mutability)? place.projection {
+                    self.visit_projection(& $($mutability)? place.base, proj, context, location);
                 }
             }
 
@@ -707,13 +709,14 @@ macro_rules! make_mir_visitor {
             }
 
             fn super_projection(&mut self,
+                                place_base: & $($mutability)? PlaceBase<'tcx>,
                                 proj: & $($mutability)? Projection<'tcx>,
                                 context: PlaceContext,
                                 location: Location) {
-                // this is calling `super_place` in preparation for changing `Place` to be
-                // a struct with a base and a slice of projections. `visit_place` should only ever
-                // be called for the outermost place now.
-                self.super_place(& $($mutability)? proj.base, context, location);
+                if let Some(box proj_base) = & $($mutability)? proj.base {
+                    self.visit_projection(place_base, proj_base, context, location);
+                }
+
                 match & $($mutability)? proj.elem {
                     ProjectionElem::Deref => {
                     }
diff --git a/src/librustc_codegen_ssa/mir/analyze.rs b/src/librustc_codegen_ssa/mir/analyze.rs
index 2af9b448ef1..09c346117f9 100644
--- a/src/librustc_codegen_ssa/mir/analyze.rs
+++ b/src/librustc_codegen_ssa/mir/analyze.rs
@@ -92,6 +92,86 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
             self.first_assignment[local] = location;
         }
     }
+
+    fn process_place(&mut self,
+                     place_ref: &mir::PlaceRef<'_, 'tcx>,
+                     context: PlaceContext,
+                     location: Location) {
+        let cx = self.fx.cx;
+
+        if let Some(proj) = place_ref.projection {
+            // Allow uses of projections that are ZSTs or from scalar fields.
+            let is_consume = match context {
+                PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
+                PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true,
+                _ => false
+            };
+            if is_consume {
+                let base_ty =
+                    mir::Place::ty_from(place_ref.base, &proj.base, self.fx.mir, cx.tcx());
+                let base_ty = self.fx.monomorphize(&base_ty);
+
+                // ZSTs don't require any actual memory access.
+                let elem_ty = base_ty
+                    .projection_ty(cx.tcx(), &proj.elem)
+                    .ty;
+                let elem_ty = self.fx.monomorphize(&elem_ty);
+                if cx.layout_of(elem_ty).is_zst() {
+                    return;
+                }
+
+                if let mir::ProjectionElem::Field(..) = proj.elem {
+                    let layout = cx.layout_of(base_ty.ty);
+                    if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
+                        // Recurse with the same context, instead of `Projection`,
+                        // potentially stopping at non-operand projections,
+                        // which would trigger `not_ssa` on locals.
+                        self.process_place(
+                            &mir::PlaceRef {
+                                base: place_ref.base,
+                                projection: &proj.base,
+                            },
+                            context,
+                            location,
+                        );
+                        return;
+                    }
+                }
+            }
+
+            // A deref projection only reads the pointer, never needs the place.
+            if let mir::ProjectionElem::Deref = proj.elem {
+                self.process_place(
+                    &mir::PlaceRef {
+                        base: place_ref.base,
+                        projection: &proj.base,
+                    },
+                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
+                    location
+                );
+                return;
+            }
+        }
+
+        // FIXME this is super_place code, is repeated here to avoid cloning place or changing
+        // visit_place API
+        let mut context = context;
+
+        if place_ref.projection.is_some() {
+            context = if context.is_mutating_use() {
+                PlaceContext::MutatingUse(MutatingUseContext::Projection)
+            } else {
+                PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
+            };
+        }
+
+        self.visit_place_base(place_ref.base, context, location);
+
+        if let Some(box proj) = place_ref.projection {
+            self.visit_projection(place_ref.base, proj, context, location);
+        }
+    }
+
 }
 
 impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
@@ -103,7 +183,10 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
                     location: Location) {
         debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);
 
-        if let mir::Place::Base(mir::PlaceBase::Local(index)) = *place {
+        if let mir::Place {
+            base: mir::PlaceBase::Local(index),
+            projection: None,
+        } = *place {
             self.assign(index, location);
             if !self.fx.rvalue_creates_operand(rvalue) {
                 self.not_ssa(index);
@@ -155,51 +238,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
                    context: PlaceContext,
                    location: Location) {
         debug!("visit_place(place={:?}, context={:?})", place, context);
-        let cx = self.fx.cx;
-
-        if let mir::Place::Projection(ref proj) = *place {
-            // Allow uses of projections that are ZSTs or from scalar fields.
-            let is_consume = match context {
-                PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
-                PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => true,
-                _ => false
-            };
-            if is_consume {
-                let base_ty = proj.base.ty(self.fx.mir, cx.tcx());
-                let base_ty = self.fx.monomorphize(&base_ty);
-
-                // ZSTs don't require any actual memory access.
-                let elem_ty = base_ty
-                    .projection_ty(cx.tcx(), &proj.elem)
-                    .ty;
-                let elem_ty = self.fx.monomorphize(&elem_ty);
-                if cx.layout_of(elem_ty).is_zst() {
-                    return;
-                }
-
-                if let mir::ProjectionElem::Field(..) = proj.elem {
-                    let layout = cx.layout_of(base_ty.ty);
-                    if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
-                        // Recurse with the same context, instead of `Projection`,
-                        // potentially stopping at non-operand projections,
-                        // which would trigger `not_ssa` on locals.
-                        self.visit_place(&proj.base, context, location);
-                        return;
-                    }
-                }
-            }
-
-            // A deref projection only reads the pointer, never needs the place.
-            if let mir::ProjectionElem::Deref = proj.elem {
-                return self.visit_place(
-                    &proj.base,
-                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
-                    location
-                );
-            }
-        }
-
-        self.super_place(place, context, location);
+        self.process_place(&place.as_place_ref(), context, location);
     }
 
     fn visit_local(&mut self,
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index 941166ccfab..d4b434ffe80 100644
--- a/src/librustc_codegen_ssa/mir/block.rs
+++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -253,7 +253,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
             PassMode::Direct(_) | PassMode::Pair(..) => {
                 let op =
-                    self.codegen_consume(&mut bx, &mir::Place::RETURN_PLACE);
+                    self.codegen_consume(&mut bx, &mir::Place::RETURN_PLACE.as_place_ref());
                 if let Ref(llval, _, align) = op.val {
                     bx.load(llval, align)
                 } else {
@@ -314,7 +314,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             return
         }
 
-        let place = self.codegen_place(&mut bx, location);
+        let place = self.codegen_place(&mut bx, &location.as_place_ref());
         let (args1, args2);
         let mut args = if let Some(llextra) = place.llextra {
             args2 = [place.llval, llextra];
@@ -607,18 +607,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         // but specified directly in the code. This means it gets promoted
                         // and we can then extract the value by evaluating the promoted.
                         mir::Operand::Copy(
-                            Place::Base(
-                                PlaceBase::Static(
-                                    box Static { kind: StaticKind::Promoted(promoted), ty }
-                                )
-                            )
+                            Place {
+                                base: PlaceBase::Static(box Static {
+                                    kind: StaticKind::Promoted(promoted),
+                                    ty,
+                                }),
+                                projection: None,
+                            }
                         ) |
                         mir::Operand::Move(
-                            Place::Base(
-                                PlaceBase::Static(
-                                    box Static { kind: StaticKind::Promoted(promoted), ty }
-                                )
-                            )
+                            Place {
+                                base: PlaceBase::Static(box Static {
+                                    kind: StaticKind::Promoted(promoted),
+                                    ty,
+                                }),
+                                projection: None,
+                            }
                         ) => {
                             let param_env = ty::ParamEnv::reveal_all();
                             let cid = mir::interpret::GlobalId {
@@ -1098,7 +1102,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         if fn_ret.is_ignore() {
             return ReturnDest::Nothing;
         }
-        let dest = if let mir::Place::Base(mir::PlaceBase::Local(index)) = *dest {
+        let dest = if let mir::Place {
+            base: mir::PlaceBase::Local(index),
+            projection: None,
+        } = *dest {
             match self.locals[index] {
                 LocalRef::Place(dest) => dest,
                 LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
@@ -1128,7 +1135,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
             }
         } else {
-            self.codegen_place(bx, dest)
+            self.codegen_place(bx, &mir::PlaceRef {
+                base: &dest.base,
+                projection: &dest.projection,
+            })
         };
         if fn_ret.is_indirect() {
             if dest.align < dest.layout.align.abi {
@@ -1153,12 +1163,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         src: &mir::Operand<'tcx>,
         dst: &mir::Place<'tcx>
     ) {
-        if let mir::Place::Base(mir::PlaceBase::Local(index)) = *dst {
+        if let mir::Place {
+            base: mir::PlaceBase::Local(index),
+            projection: None,
+        } = *dst {
             match self.locals[index] {
                 LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place),
                 LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"),
                 LocalRef::Operand(None) => {
-                    let dst_layout = bx.layout_of(self.monomorphized_place_ty(dst));
+                    let dst_layout = bx.layout_of(self.monomorphized_place_ty(&dst.as_place_ref()));
                     assert!(!dst_layout.ty.has_erasable_regions());
                     let place = PlaceRef::alloca(bx, dst_layout, "transmute_temp");
                     place.storage_live(bx);
@@ -1173,7 +1186,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
             }
         } else {
-            let dst = self.codegen_place(bx, dst);
+            let dst = self.codegen_place(bx, &dst.as_place_ref());
             self.codegen_transmute_into(bx, src, dst);
         }
     }
diff --git a/src/librustc_codegen_ssa/mir/operand.rs b/src/librustc_codegen_ssa/mir/operand.rs
index 4a6752fec35..0f6a95c1968 100644
--- a/src/librustc_codegen_ssa/mir/operand.rs
+++ b/src/librustc_codegen_ssa/mir/operand.rs
@@ -380,11 +380,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     fn maybe_codegen_consume_direct(
         &mut self,
         bx: &mut Bx,
-        place: &mir::Place<'tcx>
+        place_ref: &mir::PlaceRef<'_, 'tcx>
     ) -> Option<OperandRef<'tcx, Bx::Value>> {
-        debug!("maybe_codegen_consume_direct(place={:?})", place);
+        debug!("maybe_codegen_consume_direct(place_ref={:?})", place_ref);
 
-        place.iterate(|place_base, place_projection| {
+        place_ref.iterate(|place_base, place_projection| {
             if let mir::PlaceBase::Local(index) = place_base {
                 match self.locals[*index] {
                     LocalRef::Operand(Some(mut o)) => {
@@ -413,7 +413,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         Some(o)
                     }
                     LocalRef::Operand(None) => {
-                        bug!("use of {:?} before def", place);
+                        bug!("use of {:?} before def", place_ref);
                     }
                     LocalRef::Place(..) | LocalRef::UnsizedPlace(..) => {
                         // watch out for locals that do not have an
@@ -430,11 +430,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn codegen_consume(
         &mut self,
         bx: &mut Bx,
-        place: &mir::Place<'tcx>
+        place_ref: &mir::PlaceRef<'_, 'tcx>
     ) -> OperandRef<'tcx, Bx::Value> {
-        debug!("codegen_consume(place={:?})", place);
+        debug!("codegen_consume(place_ref={:?})", place_ref);
 
-        let ty = self.monomorphized_place_ty(place);
+        let ty = self.monomorphized_place_ty(place_ref);
         let layout = bx.cx().layout_of(ty);
 
         // ZSTs don't require any actual memory access.
@@ -442,13 +442,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             return OperandRef::new_zst(bx, layout);
         }
 
-        if let Some(o) = self.maybe_codegen_consume_direct(bx, place) {
+        if let Some(o) = self.maybe_codegen_consume_direct(bx, place_ref) {
             return o;
         }
 
         // for most places, to consume them we just load them
         // out from their home
-        let place = self.codegen_place(bx, place);
+        let place = self.codegen_place(bx, place_ref);
         bx.load_operand(place)
     }
 
@@ -462,7 +462,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         match *operand {
             mir::Operand::Copy(ref place) |
             mir::Operand::Move(ref place) => {
-                self.codegen_consume(bx, place)
+                self.codegen_consume(bx, &place.as_place_ref())
             }
 
             mir::Operand::Constant(ref constant) => {
diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs
index 588badfa11a..b38e58baaf6 100644
--- a/src/librustc_codegen_ssa/mir/place.rs
+++ b/src/librustc_codegen_ssa/mir/place.rs
@@ -428,16 +428,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn codegen_place(
         &mut self,
         bx: &mut Bx,
-        place: &mir::Place<'tcx>
+        place_ref: &mir::PlaceRef<'_, 'tcx>
     ) -> PlaceRef<'tcx, Bx::Value> {
-        debug!("codegen_place(place={:?})", place);
-
+        debug!("codegen_place(place_ref={:?})", place_ref);
         let cx = self.cx;
         let tcx = self.cx.tcx();
 
-        let result = match *place {
-            mir::Place::Base(mir::PlaceBase::Local(index)) => {
-                match self.locals[index] {
+        let result = match &place_ref {
+            mir::PlaceRef {
+                base: mir::PlaceBase::Local(index),
+                projection: None,
+            } => {
+                match self.locals[*index] {
                     LocalRef::Place(place) => {
                         return place;
                     }
@@ -445,19 +447,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         return bx.load_operand(place).deref(cx);
                     }
                     LocalRef::Operand(..) => {
-                        bug!("using operand local {:?} as place", place);
+                        bug!("using operand local {:?} as place", place_ref);
                     }
                 }
             }
-            mir::Place::Base(
-                mir::PlaceBase::Static(
-                    box mir::Static { ty, kind: mir::StaticKind::Promoted(promoted) }
-                )
-            ) => {
+            mir::PlaceRef {
+                base: mir::PlaceBase::Static(box mir::Static {
+                    ty,
+                    kind: mir::StaticKind::Promoted(promoted),
+                }),
+                projection: None,
+            } => {
                 let param_env = ty::ParamEnv::reveal_all();
                 let cid = mir::interpret::GlobalId {
                     instance: self.instance,
-                    promoted: Some(promoted),
+                    promoted: Some(*promoted),
                 };
                 let layout = cx.layout_of(self.monomorphize(&ty));
                 match bx.tcx().const_eval(param_env.and(cid)) {
@@ -480,26 +484,41 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     }
                 }
             }
-            mir::Place::Base(
-                mir::PlaceBase::Static(
-                    box mir::Static { ty, kind: mir::StaticKind::Static(def_id) }
-                )
-            ) => {
+            mir::PlaceRef {
+                base: mir::PlaceBase::Static(box mir::Static {
+                    ty,
+                    kind: mir::StaticKind::Static(def_id),
+                }),
+                projection: None,
+            } => {
                 // NB: The layout of a static may be unsized as is the case when working
                 // with a static that is an extern_type.
                 let layout = cx.layout_of(self.monomorphize(&ty));
-                let static_ = bx.get_static(def_id);
+                let static_ = bx.get_static(*def_id);
                 PlaceRef::new_thin_place(bx, static_, layout, layout.align.abi)
             },
-            mir::Place::Projection(box mir::Projection {
-                ref base,
-                elem: mir::ProjectionElem::Deref
-            }) => {
+            mir::PlaceRef {
+                base,
+                projection: Some(box mir::Projection {
+                    base: proj_base,
+                    elem: mir::ProjectionElem::Deref,
+                }),
+            } => {
                 // Load the pointer from its location.
-                self.codegen_consume(bx, base).deref(bx.cx())
+                self.codegen_consume(bx, &mir::PlaceRef {
+                    base,
+                    projection: proj_base,
+                }).deref(bx.cx())
             }
-            mir::Place::Projection(ref projection) => {
-                let cg_base = self.codegen_place(bx, &projection.base);
+            mir::PlaceRef {
+                base,
+                projection: Some(projection),
+            } => {
+                // FIXME turn this recursion into iteration
+                let cg_base = self.codegen_place(bx, &mir::PlaceRef {
+                    base,
+                    projection: &projection.base,
+                });
 
                 match projection.elem {
                     mir::ProjectionElem::Deref => bug!(),
@@ -553,13 +572,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
             }
         };
-        debug!("codegen_place(place={:?}) => {:?}", place, result);
+        debug!("codegen_place(place={:?}) => {:?}", place_ref, result);
         result
     }
 
-    pub fn monomorphized_place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> {
+    pub fn monomorphized_place_ty(&self, place_ref: &mir::PlaceRef<'_, 'tcx>) -> Ty<'tcx> {
         let tcx = self.cx.tcx();
-        let place_ty = place.ty(self.mir, tcx);
+        let place_ty = mir::Place::ty_from(place_ref.base, place_ref.projection, self.mir, tcx);
         self.monomorphize(&place_ty.ty)
     }
 }
diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs
index 4a1971e3e2e..cfb7db73658 100644
--- a/src/librustc_codegen_ssa/mir/rvalue.rs
+++ b/src/librustc_codegen_ssa/mir/rvalue.rs
@@ -355,7 +355,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
 
             mir::Rvalue::Ref(_, bk, ref place) => {
-                let cg_place = self.codegen_place(&mut bx, place);
+                let cg_place = self.codegen_place(&mut bx, &place.as_place_ref());
 
                 let ty = cg_place.layout.ty;
 
@@ -446,7 +446,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
             mir::Rvalue::Discriminant(ref place) => {
                 let discr_ty = rvalue.ty(&*self.mir, bx.tcx());
-                let discr =  self.codegen_place(&mut bx, place)
+                let discr =  self.codegen_place(&mut bx, &place.as_place_ref())
                     .codegen_get_discr(&mut bx, discr_ty);
                 (bx, OperandRef {
                     val: OperandValue::Immediate(discr),
@@ -515,7 +515,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     ) -> Bx::Value {
         // ZST are passed as operands and require special handling
         // because codegen_place() panics if Local is operand.
-        if let mir::Place::Base(mir::PlaceBase::Local(index)) = *place {
+        if let mir::Place {
+            base: mir::PlaceBase::Local(index),
+            projection: None,
+        } = *place {
             if let LocalRef::Operand(Some(op)) = self.locals[index] {
                 if let ty::Array(_, n) = op.layout.ty.sty {
                     let n = n.unwrap_usize(bx.cx().tcx());
@@ -524,8 +527,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
         }
         // use common size calculation for non zero-sized types
-        let cg_value = self.codegen_place(bx, place);
-        return cg_value.len(bx.cx());
+        let cg_value = self.codegen_place(bx, &place.as_place_ref());
+        cg_value.len(bx.cx())
     }
 
     pub fn codegen_scalar_binop(
diff --git a/src/librustc_codegen_ssa/mir/statement.rs b/src/librustc_codegen_ssa/mir/statement.rs
index 750b2f5b1a5..499cda1cf84 100644
--- a/src/librustc_codegen_ssa/mir/statement.rs
+++ b/src/librustc_codegen_ssa/mir/statement.rs
@@ -17,7 +17,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         self.set_debug_loc(&mut bx, statement.source_info);
         match statement.kind {
             mir::StatementKind::Assign(ref place, ref rvalue) => {
-                if let mir::Place::Base(mir::PlaceBase::Local(index)) = *place {
+                if let mir::Place {
+                    base: mir::PlaceBase::Local(index),
+                    projection: None,
+                } = *place {
                     match self.locals[index] {
                         LocalRef::Place(cg_dest) => {
                             self.codegen_rvalue(bx, cg_dest, rvalue)
@@ -43,12 +46,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         }
                     }
                 } else {
-                    let cg_dest = self.codegen_place(&mut bx, place);
+                    let cg_dest = self.codegen_place(&mut bx, &place.as_place_ref());
                     self.codegen_rvalue(bx, cg_dest, rvalue)
                 }
             }
             mir::StatementKind::SetDiscriminant{ref place, variant_index} => {
-                self.codegen_place(&mut bx, place)
+                self.codegen_place(&mut bx, &place.as_place_ref())
                     .codegen_set_discr(&mut bx, variant_index);
                 bx
             }
@@ -70,7 +73,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
             mir::StatementKind::InlineAsm(ref asm) => {
                 let outputs = asm.outputs.iter().map(|output| {
-                    self.codegen_place(&mut bx, output)
+                    self.codegen_place(&mut bx, &output.as_place_ref())
                 }).collect();
 
                 let input_vals = asm.inputs.iter()
diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs
index 40d8173ce40..c9e6e7f70a2 100644
--- a/src/librustc_mir/borrow_check/borrow_set.rs
+++ b/src/librustc_mir/borrow_check/borrow_set.rs
@@ -209,7 +209,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
 
             self.insert_as_pending_if_two_phase(location, &assigned_place, kind, idx);
 
-            if let Some(local) = borrowed_place.base_local() {
+            if let mir::PlaceBase::Local(local) = borrowed_place.base {
                 self.local_map.entry(local).or_default().insert(idx);
             }
         }
@@ -315,7 +315,10 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> {
         //    TEMP = &foo
         //
         // so extract `temp`.
-        let temp = if let &mir::Place::Base(mir::PlaceBase::Local(temp)) = assigned_place {
+        let temp = if let &mir::Place {
+            base: mir::PlaceBase::Local(temp),
+            projection: None,
+        } = assigned_place {
             temp
         } else {
             span_bug!(
diff --git a/src/librustc_mir/borrow_check/conflict_errors.rs b/src/librustc_mir/borrow_check/conflict_errors.rs
index 8986e87627e..95fc22dc5eb 100644
--- a/src/librustc_mir/borrow_check/conflict_errors.rs
+++ b/src/librustc_mir/borrow_check/conflict_errors.rs
@@ -2,7 +2,7 @@ use rustc::hir;
 use rustc::hir::def_id::DefId;
 use rustc::mir::{
     self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, Local,
-    LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, Projection,
+    LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, Projection, PlaceRef,
     ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
 };
 use rustc::ty::{self, Ty};
@@ -48,7 +48,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &mut self,
         location: Location,
         desired_action: InitializationRequiringAction,
-        (moved_place, used_place, span): (&Place<'tcx>, &Place<'tcx>, Span),
+        (moved_place, used_place, span): (PlaceRef<'cx, 'tcx>, PlaceRef<'cx, 'tcx>, Span),
         mpi: MovePathIndex,
     ) {
         debug!(
@@ -72,9 +72,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             .collect();
 
         if move_out_indices.is_empty() {
-            let root_place = self.prefixes(&used_place, PrefixSet::All).last().unwrap();
+            let root_place = self
+                .prefixes(used_place, PrefixSet::All)
+                .last()
+                .unwrap();
 
-            if self.uninitialized_error_reported.contains(root_place) {
+            if self.uninitialized_error_reported.contains(&root_place) {
                 debug!(
                     "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
                     root_place
@@ -82,7 +85,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 return;
             }
 
-            self.uninitialized_error_reported.insert(root_place.clone());
+            self.uninitialized_error_reported.insert(root_place);
 
             let item_msg = match self.describe_place_with_options(used_place,
                                                                   IncludingDowncast(true)) {
@@ -105,7 +108,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             err.buffer(&mut self.errors_buffer);
         } else {
             if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) {
-                if self.prefixes(&reported_place, PrefixSet::All)
+                if self.prefixes(*reported_place, PrefixSet::All)
                     .any(|p| p == used_place)
                 {
                     debug!(
@@ -123,7 +126,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 span,
                 desired_action.as_noun(),
                 msg,
-                self.describe_place_with_options(&moved_place, IncludingDowncast(true)),
+                self.describe_place_with_options(moved_place, IncludingDowncast(true)),
             );
 
             self.add_moved_or_invoked_closure_note(
@@ -136,13 +139,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let is_partial_move = move_site_vec.iter().any(|move_site| {
                 let move_out = self.move_data.moves[(*move_site).moi];
                 let moved_place = &self.move_data.move_paths[move_out.path].place;
-                used_place != moved_place && used_place.is_prefix_of(moved_place)
+                used_place != moved_place.as_place_ref()
+                    && used_place.is_prefix_of(moved_place.as_place_ref())
             });
             for move_site in &move_site_vec {
                 let move_out = self.move_data.moves[(*move_site).moi];
                 let moved_place = &self.move_data.move_paths[move_out.path].place;
 
-                let move_spans = self.move_spans(moved_place, move_out.source);
+                let move_spans = self.move_spans(moved_place.as_place_ref(), move_out.source);
                 let move_span = move_spans.args_or_use();
 
                 let move_msg = if move_spans.for_closure() {
@@ -200,7 +204,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 );
             }
 
-            let ty = used_place.ty(self.body, self.infcx.tcx).ty;
+            let ty =
+                Place::ty_from(used_place.base, used_place.projection, self.body, self.infcx.tcx)
+                    .ty;
             let needs_note = match ty.sty {
                 ty::Closure(id, _) => {
                     let tables = self.infcx.tcx.typeck_tables_of(id);
@@ -216,7 +222,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 let place = &self.move_data.move_paths[mpi].place;
 
                 let ty = place.ty(self.body, self.infcx.tcx).ty;
-                let opt_name = self.describe_place_with_options(place, IncludingDowncast(true));
+                let opt_name =
+                    self.describe_place_with_options(place.as_place_ref(), IncludingDowncast(true));
                 let note_msg = match opt_name {
                     Some(ref name) => format!("`{}`", name),
                     None => "value".to_owned(),
@@ -232,7 +239,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         );
                     }
                 }
-                let span = if let Place::Base(PlaceBase::Local(local)) = place {
+                let span = if let Place {
+                    base: PlaceBase::Local(local),
+                    projection: None,
+                } = place {
                     let decl = &self.body.local_decls[*local];
                     Some(decl.source_info.span)
                 } else {
@@ -247,7 +257,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
 
             if let Some((_, mut old_err)) = self.move_error_reported
-                .insert(move_out_indices, (used_place.clone(), err))
+                .insert(move_out_indices, (used_place, err))
             {
                 // Cancel the old error so it doesn't ICE.
                 old_err.cancel();
@@ -265,11 +275,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
             location, place, span, borrow
         );
-        let value_msg = match self.describe_place(place) {
+        let value_msg = match self.describe_place(place.as_place_ref()) {
             Some(name) => format!("`{}`", name),
             None => "value".to_owned(),
         };
-        let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
+        let borrow_msg = match self.describe_place(borrow.borrowed_place.as_place_ref()) {
             Some(name) => format!("`{}`", name),
             None => "value".to_owned(),
         };
@@ -277,12 +287,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let borrow_spans = self.retrieve_borrow_spans(borrow);
         let borrow_span = borrow_spans.args_or_use();
 
-        let move_spans = self.move_spans(place, location);
+        let move_spans = self.move_spans(place.as_place_ref(), location);
         let span = move_spans.args_or_use();
 
         let mut err = self.cannot_move_when_borrowed(
             span,
-            &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
+            &self.describe_place(place.as_place_ref()).unwrap_or_else(|| "_".to_owned()),
         );
         err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
         err.span_label(span, format!("move out of {} occurs here", value_msg));
@@ -316,20 +326,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         // Conflicting borrows are reported separately, so only check for move
         // captures.
-        let use_spans = self.move_spans(place, location);
+        let use_spans = self.move_spans(place.as_place_ref(), location);
         let span = use_spans.var_or_use();
 
         let mut err = self.cannot_use_when_mutably_borrowed(
             span,
-            &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
+            &self.describe_place(place.as_place_ref()).unwrap_or_else(|| "_".to_owned()),
             borrow_span,
-            &self.describe_place(&borrow.borrowed_place)
+            &self.describe_place(borrow.borrowed_place.as_place_ref())
                 .unwrap_or_else(|| "_".to_owned()),
         );
 
         borrow_spans.var_span_label(&mut err, {
             let place = &borrow.borrowed_place;
-            let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned());
+            let desc_place =
+                self.describe_place(place.as_place_ref()).unwrap_or_else(|| "_".to_owned());
 
             format!("borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe())
         });
@@ -506,7 +517,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             );
         } else {
             let borrow_place = &issued_borrow.borrowed_place;
-            let borrow_place_desc = self.describe_place(borrow_place)
+            let borrow_place_desc = self.describe_place(borrow_place.as_place_ref())
                                         .unwrap_or_else(|| "_".to_owned());
             issued_spans.var_span_label(
                 &mut err,
@@ -575,8 +586,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     ) -> (String, String, String, String) {
         // Define a small closure that we can use to check if the type of a place
         // is a union.
-        let union_ty = |place: &Place<'tcx>| -> Option<Ty<'tcx>> {
-            let ty = place.ty(self.body, self.infcx.tcx).ty;
+        let union_ty = |place_base, place_projection| {
+            let ty = Place::ty_from(place_base, place_projection, self.body, self.infcx.tcx).ty;
             ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
         };
         let describe_place = |place| self.describe_place(place).unwrap_or_else(|| "_".to_owned());
@@ -595,13 +606,22 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 // field access to a union. If we find that, then we will keep the place of the
                 // union being accessed and the field that was being accessed so we can check the
                 // second borrowed place for the same union and a access to a different field.
-                let mut current = first_borrowed_place;
-                while let Place::Projection(box Projection { base, elem }) = current {
+                let Place {
+                    base,
+                    projection,
+                } = first_borrowed_place;
+
+                let mut current = projection;
+
+                while let Some(box Projection { base: base_proj, elem }) = current {
                     match elem {
-                        ProjectionElem::Field(field, _) if union_ty(base).is_some() => {
-                            return Some((base, field));
+                        ProjectionElem::Field(field, _) if union_ty(base, base_proj).is_some() => {
+                            return Some((PlaceRef {
+                                base: base,
+                                projection: base_proj,
+                            }, field));
                         },
-                        _ => current = base,
+                        _ => current = base_proj,
                     }
                 }
                 None
@@ -609,22 +629,36 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             .and_then(|(target_base, target_field)| {
                 // With the place of a union and a field access into it, we traverse the second
                 // borrowed place and look for a access to a different field of the same union.
-                let mut current = second_borrowed_place;
-                while let Place::Projection(box Projection { base, elem }) = current {
+                let Place {
+                    base,
+                    projection,
+                } = second_borrowed_place;
+
+                let mut current = projection;
+
+                while let Some(box Projection { base: proj_base, elem }) = current {
                     if let ProjectionElem::Field(field, _) = elem {
-                        if let Some(union_ty) = union_ty(base) {
-                            if field != target_field && base == target_base {
+                        if let Some(union_ty) = union_ty(base, proj_base) {
+                            if field != target_field
+                                && base == target_base.base
+                                && proj_base == target_base.projection {
+                                // FIXME when we avoid clone reuse describe_place closure
+                                let describe_base_place =  self.describe_place(PlaceRef {
+                                    base: base,
+                                    projection: proj_base,
+                                }).unwrap_or_else(|| "_".to_owned());
+
                                 return Some((
-                                    describe_place(base),
-                                    describe_place(first_borrowed_place),
-                                    describe_place(second_borrowed_place),
+                                    describe_base_place,
+                                    describe_place(first_borrowed_place.as_place_ref()),
+                                    describe_place(second_borrowed_place.as_place_ref()),
                                     union_ty.to_string(),
                                 ));
                             }
                         }
                     }
 
-                    current = base;
+                    current = proj_base;
                 }
                 None
             })
@@ -632,7 +666,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 // If we didn't find a field access into a union, or both places match, then
                 // only return the description of the first place.
                 (
-                    describe_place(first_borrowed_place),
+                    describe_place(first_borrowed_place.as_place_ref()),
                     "".to_string(),
                     "".to_string(),
                     "".to_string(),
@@ -663,20 +697,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         );
 
         let drop_span = place_span.1;
-        let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All)
+        let root_place = self.prefixes(borrow.borrowed_place.as_place_ref(), PrefixSet::All)
             .last()
             .unwrap();
 
         let borrow_spans = self.retrieve_borrow_spans(borrow);
         let borrow_span = borrow_spans.var_or_use();
 
-        let proper_span = match *root_place {
-            Place::Base(PlaceBase::Local(local)) => self.body.local_decls[local].source_info.span,
+        assert!(root_place.projection.is_none());
+        let proper_span = match root_place.base {
+            PlaceBase::Local(local) => self.body.local_decls[*local].source_info.span,
             _ => drop_span,
         };
 
         if self.access_place_error_reported
-            .contains(&(root_place.clone(), borrow_span))
+            .contains(&(Place {
+                base: root_place.base.clone(),
+                projection: root_place.projection.clone(),
+            }, borrow_span))
         {
             debug!(
                 "suppressing access_place error when borrow doesn't live long enough for {:?}",
@@ -686,16 +724,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         }
 
         self.access_place_error_reported
-            .insert((root_place.clone(), borrow_span));
+            .insert((Place {
+                base: root_place.base.clone(),
+                projection: root_place.projection.clone(),
+            }, borrow_span));
 
         if let StorageDeadOrDrop::Destructor(dropped_ty) =
-            self.classify_drop_access_kind(&borrow.borrowed_place)
+            self.classify_drop_access_kind(borrow.borrowed_place.as_place_ref())
         {
             // If a borrow of path `B` conflicts with drop of `D` (and
             // we're not in the uninteresting case where `B` is a
             // prefix of `D`), then report this as a more interesting
             // destructor conflict.
-            if !borrow.borrowed_place.is_prefix_of(place_span.0) {
+            if !borrow.borrowed_place.as_place_ref().is_prefix_of(place_span.0.as_place_ref()) {
                 self.report_borrow_conflicts_with_destructor(
                     location, borrow, place_span, kind, dropped_ty,
                 );
@@ -703,7 +744,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         }
 
-        let place_desc = self.describe_place(&borrow.borrowed_place);
+        let place_desc = self.describe_place(borrow.borrowed_place.as_place_ref());
 
         let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
         let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
@@ -910,12 +951,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         let mut err = self.cannot_borrow_across_destructor(borrow_span);
 
-        let what_was_dropped = match self.describe_place(place) {
+        let what_was_dropped = match self.describe_place(place.as_place_ref()) {
             Some(name) => format!("`{}`", name.as_str()),
             None => String::from("temporary value"),
         };
 
-        let label = match self.describe_place(&borrow.borrowed_place) {
+        let label = match self.describe_place(borrow.borrowed_place.as_place_ref()) {
             Some(borrowed) => format!(
                 "here, drop of {D} needs exclusive access to `{B}`, \
                  because the type `{T}` implements the `Drop` trait",
@@ -1061,7 +1102,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
             let local_kind = match borrow.borrowed_place {
-                Place::Base(PlaceBase::Local(local)) => {
+                Place {
+                    base: PlaceBase::Local(local),
+                    projection: None,
+                } => {
                     match self.body.local_kind(local) {
                         LocalKind::ReturnPointer
                         | LocalKind::Temp => bug!("temporary or return pointer with a name"),
@@ -1083,15 +1127,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 format!("`{}` is borrowed here", place_desc),
             )
         } else {
-            let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All)
+            let root_place = self.prefixes(borrow.borrowed_place.as_place_ref(),
+                                           PrefixSet::All)
                 .last()
                 .unwrap();
-            let local = if let Place::Base(PlaceBase::Local(local)) = *root_place {
+            let local = if let PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } = root_place {
                 local
             } else {
                 bug!("try_report_cannot_return_reference_to_local: not a local")
             };
-            match self.body.local_kind(local) {
+            match self.body.local_kind(*local) {
                 LocalKind::ReturnPointer | LocalKind::Temp => {
                     (
                         "temporary value".to_string(),
@@ -1342,7 +1390,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let mut err = self.cannot_mutate_in_match_guard(
                 span,
                 loan_span,
-                &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
+                &self.describe_place(place.as_place_ref()).unwrap_or_else(|| "_".to_owned()),
                 "assign",
             );
             loan_spans.var_span_label(
@@ -1358,7 +1406,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let mut err = self.cannot_assign_to_borrowed(
             span,
             loan_span,
-            &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
+            &self.describe_place(place.as_place_ref()).unwrap_or_else(|| "_".to_owned()),
         );
 
         loan_spans.var_span_label(
@@ -1385,7 +1433,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         assigned_span: Span,
         err_place: &Place<'tcx>,
     ) {
-        let (from_arg, local_decl) = if let Place::Base(PlaceBase::Local(local)) = *err_place {
+        let (from_arg, local_decl) = if let Place {
+            base: PlaceBase::Local(local),
+            projection: None,
+        } = *err_place {
             if let LocalKind::Arg = self.body.local_kind(local) {
                 (true, Some(&self.body.local_decls[local]))
             } else {
@@ -1415,8 +1466,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 is_user_variable: None,
                 ..
             })
-            | None => (self.describe_place(place), assigned_span),
-            Some(decl) => (self.describe_place(err_place), decl.source_info.span),
+            | None => (self.describe_place(place.as_place_ref()), assigned_span),
+            Some(decl) => (self.describe_place(err_place.as_place_ref()), decl.source_info.span),
         };
 
         let mut err = self.cannot_reassign_immutable(
@@ -1454,21 +1505,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         err.buffer(&mut self.errors_buffer);
     }
 
-    fn classify_drop_access_kind(&self, place: &Place<'tcx>) -> StorageDeadOrDrop<'tcx> {
+    fn classify_drop_access_kind(&self, place: PlaceRef<'cx, 'tcx>) -> StorageDeadOrDrop<'tcx> {
         let tcx = self.infcx.tcx;
-        match place {
-            Place::Base(PlaceBase::Local(_)) |
-            Place::Base(PlaceBase::Static(_)) => {
+        match place.projection {
+            None => {
                 StorageDeadOrDrop::LocalStorageDead
             }
-            Place::Projection(box Projection { base, elem }) => {
-                let base_access = self.classify_drop_access_kind(base);
+            Some(box Projection { ref base, ref elem }) => {
+                let base_access = self.classify_drop_access_kind(PlaceRef {
+                    base: place.base,
+                    projection: base,
+                });
                 match elem {
                     ProjectionElem::Deref => match base_access {
                         StorageDeadOrDrop::LocalStorageDead
                         | StorageDeadOrDrop::BoxedStorageDead => {
                             assert!(
-                                base.ty(self.body, tcx).ty.is_box(),
+                                Place::ty_from(&place.base, base, self.body, tcx).ty.is_box(),
                                 "Drop of value behind a reference or raw pointer"
                             );
                             StorageDeadOrDrop::BoxedStorageDead
@@ -1476,7 +1529,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         StorageDeadOrDrop::Destructor(_) => base_access,
                     },
                     ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
-                        let base_ty = base.ty(self.body, tcx).ty;
+                        let base_ty = Place::ty_from(&place.base, base, self.body, tcx).ty;
                         match base_ty.sty {
                             ty::Adt(def, _) if def.has_dtor(tcx) => {
                                 // Report the outermost adt with a destructor
@@ -1543,8 +1596,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             );
             // Check that the initial assignment of the reserve location is into a temporary.
             let mut target = *match reservation {
-                Place::Base(PlaceBase::Local(local))
-                    if self.body.local_kind(*local) == LocalKind::Temp => local,
+                Place {
+                    base: PlaceBase::Local(local),
+                    projection: None,
+                } if self.body.local_kind(*local) == LocalKind::Temp => local,
                 _ => return None,
             };
 
@@ -1557,7 +1612,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     target, stmt
                 );
                 if let StatementKind::Assign(
-                    Place::Base(PlaceBase::Local(assigned_to)),
+                    Place {
+                        base: PlaceBase::Local(assigned_to),
+                        projection: None,
+                    },
                     box rvalue
                 ) = &stmt.kind {
                     debug!(
@@ -1682,7 +1740,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 target, terminator
             );
             if let TerminatorKind::Call {
-                destination: Some((Place::Base(PlaceBase::Local(assigned_to)), _)),
+                destination: Some((Place {
+                    base: PlaceBase::Local(assigned_to),
+                    projection: None,
+                }, _)),
                 args,
                 ..
             } = &terminator.kind
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 5f61ed151c0..156897aedb7 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -3,8 +3,8 @@ use rustc::hir::def::Namespace;
 use rustc::hir::def_id::DefId;
 use rustc::mir::{
     AggregateKind, Constant, Field, Local, LocalKind, Location, Operand,
-    Place, PlaceBase, ProjectionElem, Rvalue, Statement, StatementKind, Static,
-    StaticKind, Terminator, TerminatorKind,
+    Place, PlaceBase, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind,
+    Static, StaticKind, Terminator, TerminatorKind,
 };
 use rustc::ty::{self, DefIdTree, Ty, TyCtxt};
 use rustc::ty::layout::VariantIdx;
@@ -34,7 +34,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     pub(super) fn add_moved_or_invoked_closure_note(
         &self,
         location: Location,
-        place: &Place<'tcx>,
+        place: PlaceRef<'cx, 'tcx>,
         diag: &mut DiagnosticBuilder<'_>,
     ) {
         debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
@@ -121,8 +121,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     /// End-user visible description of `place` if one can be found. If the
     /// place is a temporary for instance, None will be returned.
-    pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
-        self.describe_place_with_options(place, IncludingDowncast(false))
+    pub(super) fn describe_place(&self, place_ref: PlaceRef<'cx, 'tcx>) -> Option<String> {
+        self.describe_place_with_options(place_ref, IncludingDowncast(false))
     }
 
     /// End-user visible description of `place` if one can be found. If the
@@ -131,7 +131,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// `Downcast` and `IncludingDowncast` is true
     pub(super) fn describe_place_with_options(
         &self,
-        place: &Place<'tcx>,
+        place: PlaceRef<'cx, 'tcx>,
         including_downcast: IncludingDowncast,
     ) -> Option<String> {
         let mut buf = String::new();
@@ -144,22 +144,42 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// Appends end-user visible description of `place` to `buf`.
     fn append_place_to_string(
         &self,
-        place: &Place<'tcx>,
+        place: PlaceRef<'cx, 'tcx>,
         buf: &mut String,
         mut autoderef: bool,
         including_downcast: &IncludingDowncast,
     ) -> Result<(), ()> {
-        match *place {
-            Place::Base(PlaceBase::Local(local)) => {
-                self.append_local_to_string(local, buf)?;
+        match place {
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } => {
+                self.append_local_to_string(*local, buf)?;
             }
-            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) => {
+            PlaceRef {
+                base:
+                    PlaceBase::Static(box Static {
+                        kind: StaticKind::Promoted(_),
+                        ..
+                    }),
+                projection: None,
+            } => {
                 buf.push_str("promoted");
             }
-            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })) => {
-                buf.push_str(&self.infcx.tcx.item_name(def_id).to_string());
+            PlaceRef {
+                base:
+                    PlaceBase::Static(box Static {
+                        kind: StaticKind::Static(def_id),
+                        ..
+                    }),
+                projection: None,
+            } => {
+                buf.push_str(&self.infcx.tcx.item_name(*def_id).to_string());
             }
-            Place::Projection(ref proj) => {
+            PlaceRef {
+                ref base,
+                projection: Some(ref proj),
+            } => {
                 match proj.elem {
                     ProjectionElem::Deref => {
                         let upvar_field_projection =
@@ -174,43 +194,66 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             }
                         } else {
                             if autoderef {
+                                // FIXME turn this recursion into iteration
                                 self.append_place_to_string(
-                                    &proj.base,
+                                    PlaceRef {
+                                        base: &base,
+                                        projection: &proj.base,
+                                    },
                                     buf,
                                     autoderef,
                                     &including_downcast,
                                 )?;
-                            } else if let Place::Base(PlaceBase::Local(local)) = proj.base {
-                                if self.body.local_decls[local].is_ref_for_guard() {
-                                    self.append_place_to_string(
-                                        &proj.base,
-                                        buf,
-                                        autoderef,
-                                        &including_downcast,
-                                    )?;
-                                } else {
-                                    buf.push_str(&"*");
-                                    self.append_place_to_string(
-                                        &proj.base,
-                                        buf,
-                                        autoderef,
-                                        &including_downcast,
-                                    )?;
-                                }
                             } else {
-                                buf.push_str(&"*");
-                                self.append_place_to_string(
-                                    &proj.base,
-                                    buf,
-                                    autoderef,
-                                    &including_downcast,
-                                )?;
+                                match (&proj.base, base) {
+                                    (None, PlaceBase::Local(local)) => {
+                                        if self.body.local_decls[*local].is_ref_for_guard() {
+                                            self.append_place_to_string(
+                                                PlaceRef {
+                                                    base: &base,
+                                                    projection: &proj.base,
+                                                },
+                                                buf,
+                                                autoderef,
+                                                &including_downcast,
+                                            )?;
+                                        } else {
+                                            // FIXME deduplicate this and the _ => body below
+                                            buf.push_str(&"*");
+                                            self.append_place_to_string(
+                                                PlaceRef {
+                                                    base: &base,
+                                                    projection: &proj.base,
+                                                },
+                                                buf,
+                                                autoderef,
+                                                &including_downcast,
+                                            )?;
+                                        }
+                                    }
+
+                                    _ => {
+                                        buf.push_str(&"*");
+                                        self.append_place_to_string(
+                                            PlaceRef {
+                                                base: &base,
+                                                projection: &proj.base,
+                                            },
+                                            buf,
+                                            autoderef,
+                                            &including_downcast,
+                                        )?;
+                                    }
+                                }
                             }
                         }
                     }
                     ProjectionElem::Downcast(..) => {
                         self.append_place_to_string(
-                            &proj.base,
+                            PlaceRef {
+                                base: &base,
+                                projection: &proj.base,
+                            },
                             buf,
                             autoderef,
                             &including_downcast,
@@ -229,9 +272,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             let name = self.upvars[var_index].name.to_string();
                             buf.push_str(&name);
                         } else {
-                            let field_name = self.describe_field(&proj.base, field);
+                            let field_name = self.describe_field(PlaceRef {
+                                base: base,
+                                projection: &proj.base,
+                            }, field);
                             self.append_place_to_string(
-                                &proj.base,
+                                PlaceRef {
+                                    base: &base,
+                                    projection: &proj.base,
+                                },
                                 buf,
                                 autoderef,
                                 &including_downcast,
@@ -243,7 +292,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         autoderef = true;
 
                         self.append_place_to_string(
-                            &proj.base,
+                            PlaceRef {
+                                base: &base,
+                                projection: &proj.base,
+                            },
                             buf,
                             autoderef,
                             &including_downcast,
@@ -260,7 +312,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         // then use another while the borrow is held, don't output indices details
                         // to avoid confusing the end-user
                         self.append_place_to_string(
-                            &proj.base,
+                            PlaceRef {
+                                base: &base,
+                                projection: &proj.base,
+                            },
                             buf,
                             autoderef,
                             &including_downcast,
@@ -288,18 +343,32 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     }
 
     /// End-user visible description of the `field`nth field of `base`
-    fn describe_field(&self, base: &Place<'tcx>, field: Field) -> String {
-        match *base {
-            Place::Base(PlaceBase::Local(local)) => {
-                let local = &self.body.local_decls[local];
+    fn describe_field(&self, place: PlaceRef<'cx, 'tcx>, field: Field) -> String {
+        // FIXME Place2 Make this work iteratively
+        match place {
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } => {
+                let local = &self.body.local_decls[*local];
                 self.describe_field_from_ty(&local.ty, field, None)
             }
-            Place::Base(PlaceBase::Static(ref static_)) =>
+            PlaceRef {
+                base: PlaceBase::Static(static_),
+                projection: None,
+            } =>
                 self.describe_field_from_ty(&static_.ty, field, None),
-            Place::Projection(ref proj) => match proj.elem {
-                ProjectionElem::Deref => self.describe_field(&proj.base, field),
+            PlaceRef {
+                base,
+                projection: Some(proj),
+            } => match proj.elem {
+                ProjectionElem::Deref => self.describe_field(PlaceRef {
+                    base,
+                    projection: &proj.base,
+                }, field),
                 ProjectionElem::Downcast(_, variant_index) => {
-                    let base_ty = base.ty(self.body, self.infcx.tcx).ty;
+                    let base_ty =
+                        Place::ty_from(place.base, place.projection, self.body, self.infcx.tcx).ty;
                     self.describe_field_from_ty(&base_ty, field, Some(variant_index))
                 }
                 ProjectionElem::Field(_, field_type) => {
@@ -308,7 +377,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 ProjectionElem::Index(..)
                 | ProjectionElem::ConstantIndex { .. }
                 | ProjectionElem::Subslice { .. } => {
-                    self.describe_field(&proj.base, field)
+                    self.describe_field(PlaceRef {
+                        base,
+                        projection: &proj.base,
+                    }, field)
                 }
             },
         }
@@ -365,10 +437,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     }
 
     /// Checks if a place is a thread-local static.
-    pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool {
-        if let Place::Base(
-            PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })
-        ) = place {
+    pub fn is_place_thread_local(&self, place_ref: PlaceRef<'cx, 'tcx>) -> bool {
+        if let PlaceRef {
+            base: PlaceBase::Static(box Static {
+                kind: StaticKind::Static(def_id),
+                ..
+            }),
+            projection: None,
+        } = place_ref {
             let attrs = self.infcx.tcx.get_attrs(*def_id);
             let is_thread_local = attrs.iter().any(|attr| attr.check_name(sym::thread_local));
 
@@ -405,7 +481,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     pub(super) fn borrowed_content_source(
         &self,
-        deref_base: &Place<'tcx>,
+        deref_base: PlaceRef<'cx, 'tcx>,
     ) -> BorrowedContentSource<'tcx> {
         let tcx = self.infcx.tcx;
 
@@ -457,7 +533,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         // If we didn't find an overloaded deref or index, then assume it's a
         // built in deref and check the type of the base.
-        let base_ty = deref_base.ty(self.body, tcx).ty;
+        let base_ty = Place::ty_from(deref_base.base, deref_base.projection, self.body, tcx).ty;
         if base_ty.is_unsafe_ptr() {
             BorrowedContentSource::DerefRawPointer
         } else if base_ty.is_mutable_pointer() {
@@ -700,7 +776,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// Finds the spans associated to a move or copy of move_place at location.
     pub(super) fn move_spans(
         &self,
-        moved_place: &Place<'tcx>, // Could also be an upvar.
+        moved_place: PlaceRef<'cx, 'tcx>, // Could also be an upvar.
         location: Location,
     ) -> UseSpans {
         use self::UseSpans::*;
@@ -750,7 +826,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             .get(location.statement_index)
         {
             Some(&Statement {
-                kind: StatementKind::Assign(Place::Base(PlaceBase::Local(local)), _),
+                kind: StatementKind::Assign(Place {
+                    base: PlaceBase::Local(local),
+                    projection: None,
+                }, _),
                 ..
             }) => local,
             _ => return OtherUse(use_span),
@@ -776,7 +855,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     def_id, is_generator, places
                 );
                 if let Some((args_span, var_span)) = self.closure_span(
-                    *def_id, &Place::from(target), places
+                    *def_id, Place::from(target).as_place_ref(), places
                 ) {
                     return ClosureUse {
                         is_generator,
@@ -800,7 +879,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn closure_span(
         &self,
         def_id: DefId,
-        target_place: &Place<'tcx>,
+        target_place: PlaceRef<'cx, 'tcx>,
         places: &Vec<Operand<'tcx>>,
     ) -> Option<(Span, Span)> {
         debug!(
@@ -816,7 +895,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             for (upvar, place) in self.infcx.tcx.upvars(def_id)?.values().zip(places) {
                 match place {
                     Operand::Copy(place) |
-                    Operand::Move(place) if target_place == place => {
+                    Operand::Move(place) if target_place == place.as_place_ref() => {
                         debug!("closure_span: found captured local {:?}", place);
                         return Some((*args_span, upvar.span));
                     },
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 5851cd81788..cfc7e77f4e5 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -10,9 +10,8 @@ use rustc::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT};
 use rustc::middle::borrowck::SignalledError;
 use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
 use rustc::mir::{
-    ClearCrossCrate, Local, Location, Body, Mutability, Operand, Place, PlaceBase, Static,
-
-    StaticKind
+    ClearCrossCrate, Local, Location, Body, Mutability, Operand, Place, PlaceBase, PlaceRef,
+    Static, StaticKind
 };
 use rustc::mir::{Field, Projection, ProjectionElem, Rvalue, Statement, StatementKind};
 use rustc::mir::{Terminator, TerminatorKind};
@@ -474,10 +473,10 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
     /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
     /// when errors in the map are being re-added to the error buffer so that errors with the
     /// same primary span come out in a consistent order.
-    move_error_reported: BTreeMap<Vec<MoveOutIndex>, (Place<'tcx>, DiagnosticBuilder<'cx>)>,
+    move_error_reported: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'cx, 'tcx>, DiagnosticBuilder<'cx>)>,
     /// This field keeps track of errors reported in the checking of uninitialized variables,
     /// so that we don't report seemingly duplicate errors.
-    uninitialized_error_reported: FxHashSet<Place<'tcx>>,
+    uninitialized_error_reported: FxHashSet<PlaceRef<'cx, 'tcx>>,
     /// Errors to be reported buffer
     errors_buffer: Vec<Diagnostic>,
     /// This field keeps track of all the local variables that are declared mut and are mutated.
@@ -520,7 +519,7 @@ impl<'cx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx
     fn visit_statement_entry(
         &mut self,
         location: Location,
-        stmt: &Statement<'tcx>,
+        stmt: &'cx Statement<'tcx>,
         flow_state: &Self::FlowState,
     ) {
         debug!(
@@ -561,7 +560,7 @@ impl<'cx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Use,
-                    (place, span),
+                    (place.as_place_ref(), span),
                     flow_state,
                 );
             }
@@ -592,7 +591,7 @@ impl<'cx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx
                         self.check_if_path_or_subpath_is_moved(
                             location,
                             InitializationRequiringAction::Use,
-                            (output, o.span),
+                            (output.as_place_ref(), o.span),
                             flow_state,
                         );
                     } else {
@@ -631,7 +630,7 @@ impl<'cx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx
     fn visit_terminator_entry(
         &mut self,
         location: Location,
-        term: &Terminator<'tcx>,
+        term: &'cx Terminator<'tcx>,
         flow_state: &Self::FlowState,
     ) {
         let loc = location;
@@ -890,7 +889,8 @@ enum InitializationRequiringAction {
 }
 
 struct RootPlace<'d, 'tcx> {
-    place: &'d Place<'tcx>,
+    place_base: &'d PlaceBase<'tcx>,
+    place_projection: &'d Option<Box<Projection<'tcx>>>,
     is_local_mutation_allowed: LocalMutationIsAllowed,
 }
 
@@ -1143,7 +1143,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn mutate_place(
         &mut self,
         location: Location,
-        place_span: (&Place<'tcx>, Span),
+        place_span: (&'cx Place<'tcx>, Span),
         kind: AccessDepth,
         mode: MutateMode,
         flow_state: &Flows<'cx, 'tcx>,
@@ -1154,7 +1154,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Update,
-                    place_span,
+                    (place_span.0.as_place_ref(), place_span.1),
                     flow_state,
                 );
             }
@@ -1166,12 +1166,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         // Special case: you can assign a immutable local variable
         // (e.g., `x = ...`) so long as it has never been initialized
         // before (at this point in the flow).
-        if let &Place::Base(PlaceBase::Local(local)) = place_span.0 {
-            if let Mutability::Not = self.body.local_decls[local].mutability {
+        if let Place {
+            base: PlaceBase::Local(local),
+            projection: None,
+        } = place_span.0 {
+            if let Mutability::Not = self.body.local_decls[*local].mutability {
                 // check for reassignments to immutable local variables
                 self.check_if_reassignment_to_immutable_state(
                     location,
-                    local,
+                    *local,
                     place_span,
                     flow_state,
                 );
@@ -1192,7 +1195,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn consume_rvalue(
         &mut self,
         location: Location,
-        (rvalue, span): (&Rvalue<'tcx>, Span),
+        (rvalue, span): (&'cx Rvalue<'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         match *rvalue {
@@ -1229,7 +1232,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     action,
-                    (place, span),
+                    (place.as_place_ref(), span),
                     flow_state,
                 );
             }
@@ -1257,7 +1260,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Use,
-                    (place, span),
+                    (place.as_place_ref(), span),
                     flow_state,
                 );
             }
@@ -1305,16 +1308,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) {
         let propagate_closure_used_mut_place = |this: &mut Self, place: &Place<'tcx>| {
-            match *place {
-                Place::Projection { .. } => {
-                    if let Some(field) = this.is_upvar_field_projection(place) {
-                        this.used_mut_upvars.push(field);
-                    }
-                }
-                Place::Base(PlaceBase::Local(local)) => {
-                    this.used_mut.insert(local);
+            if place.projection.is_some() {
+                if let Some(field) = this.is_upvar_field_projection(place.as_place_ref()) {
+                    this.used_mut_upvars.push(field);
                 }
-                Place::Base(PlaceBase::Static(_)) => {}
+            } else if let PlaceBase::Local(local) = place.base {
+                this.used_mut.insert(local);
             }
         };
 
@@ -1322,10 +1321,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         // captures of a closure are copied/moved directly
         // when generating MIR.
         match *operand {
-            Operand::Move(Place::Base(PlaceBase::Local(local)))
-            | Operand::Copy(Place::Base(PlaceBase::Local(local)))
-                if self.body.local_decls[local].is_user_variable.is_none() =>
-            {
+            Operand::Move(Place {
+                base: PlaceBase::Local(local),
+                projection: None,
+            }) |
+            Operand::Copy(Place {
+                base: PlaceBase::Local(local),
+                projection: None,
+            }) if self.body.local_decls[local].is_user_variable.is_none() => {
                 if self.body.local_decls[local].ty.is_mutable_pointer() {
                     // The variable will be marked as mutable by the borrow.
                     return;
@@ -1379,7 +1382,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn consume_operand(
         &mut self,
         location: Location,
-        (operand, span): (&Operand<'tcx>, Span),
+        (operand, span): (&'cx Operand<'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         match *operand {
@@ -1398,7 +1401,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Use,
-                    (place, span),
+                    (place.as_place_ref(), span),
                     flow_state,
                 );
             }
@@ -1416,7 +1419,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Use,
-                    (place, span),
+                    (place.as_place_ref(), span),
                     flow_state,
                 );
             }
@@ -1434,30 +1437,35 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     ) {
         debug!("check_for_invalidation_at_exit({:?})", borrow);
         let place = &borrow.borrowed_place;
-        let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
+        let root_place = self.prefixes(place.as_place_ref(), PrefixSet::All).last().unwrap();
 
         // FIXME(nll-rfc#40): do more precise destructor tracking here. For now
         // we just know that all locals are dropped at function exit (otherwise
         // we'll have a memory leak) and assume that all statics have a destructor.
         //
         // FIXME: allow thread-locals to borrow other thread locals?
-        let (might_be_alive, will_be_dropped) = match root_place {
-            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) => {
+
+        assert!(root_place.projection.is_none());
+        let (might_be_alive, will_be_dropped) = match root_place.base {
+            PlaceBase::Static(box Static {
+                kind: StaticKind::Promoted(_),
+                ..
+            }) => {
                 (true, false)
             }
-            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(_), .. })) => {
+            PlaceBase::Static(box Static {
+                kind: StaticKind::Static(_),
+                ..
+            }) => {
                 // Thread-locals might be dropped after the function exits, but
                 // "true" statics will never be.
-                (true, self.is_place_thread_local(&root_place))
+                (true, self.is_place_thread_local(root_place))
             }
-            Place::Base(PlaceBase::Local(_)) => {
+            PlaceBase::Local(_) => {
                 // Locals are always dropped at function exit, and if they
                 // have a destructor it would've been called already.
                 (false, self.locals_are_invalidated_at_exit)
             }
-            Place::Projection(..) => {
-                bug!("root of {:?} is a projection ({:?})?", place, root_place)
-            }
         };
 
         if !will_be_dropped {
@@ -1563,7 +1571,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &mut self,
         location: Location,
         desired_action: InitializationRequiringAction,
-        place_span: (&Place<'tcx>, Span),
+        place_span: (PlaceRef<'cx, 'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         let maybe_uninits = &flow_state.uninits;
@@ -1631,7 +1639,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &mut self,
         location: Location,
         desired_action: InitializationRequiringAction,
-        place_span: (&Place<'tcx>, Span),
+        place_span: (PlaceRef<'cx, 'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         let maybe_uninits = &flow_state.uninits;
@@ -1686,25 +1694,27 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// An Err result includes a tag indicated why the search failed.
     /// Currently this can only occur if the place is built off of a
     /// static variable, as we do not track those in the MoveData.
-    fn move_path_closest_to<'a>(
+    fn move_path_closest_to(
         &mut self,
-        place: &'a Place<'tcx>,
-    ) -> Result<(&'a Place<'tcx>, MovePathIndex), NoMovePathFound> where 'cx: 'a {
-        let mut last_prefix = place;
+        place: PlaceRef<'cx, 'tcx>,
+    ) -> Result<(PlaceRef<'cx, 'tcx>, MovePathIndex), NoMovePathFound> {
+        let mut last_prefix = place.base;
+
         for prefix in self.prefixes(place, PrefixSet::All) {
             if let Some(mpi) = self.move_path_for_place(prefix) {
                 return Ok((prefix, mpi));
             }
-            last_prefix = prefix;
+
+            last_prefix = prefix.base;
         }
-        match *last_prefix {
-            Place::Base(PlaceBase::Local(_)) => panic!("should have move path for every Local"),
-            Place::Projection(_) => panic!("PrefixSet::All meant don't stop for Projection"),
-            Place::Base(PlaceBase::Static(_)) => Err(NoMovePathFound::ReachedStatic),
+
+        match last_prefix {
+            PlaceBase::Local(_) => panic!("should have move path for every Local"),
+            PlaceBase::Static(_) => Err(NoMovePathFound::ReachedStatic),
         }
     }
 
-    fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option<MovePathIndex> {
+    fn move_path_for_place(&mut self, place: PlaceRef<'cx, 'tcx>) -> Option<MovePathIndex> {
         // If returns None, then there is no move path corresponding
         // to a direct owner of `place` (which means there is nothing
         // that borrowck tracks for its analysis).
@@ -1718,94 +1728,97 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn check_if_assigned_path_is_moved(
         &mut self,
         location: Location,
-        (place, span): (&Place<'tcx>, Span),
+        (place, span): (&'cx Place<'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         debug!("check_if_assigned_path_is_moved place: {:?}", place);
         // recur down place; dispatch to external checks when necessary
-        let mut place = place;
-        loop {
-            match *place {
-                Place::Base(PlaceBase::Local(_)) | Place::Base(PlaceBase::Static(_)) => {
-                    // assigning to `x` does not require `x` be initialized.
+        let mut place_projection = &place.projection;
+
+        // None case => assigning to `x` does not require `x` be initialized.
+        while let Some(proj) = place_projection {
+            let Projection { ref base, ref elem } = **proj;
+            match *elem {
+                ProjectionElem::Index(_/*operand*/) |
+                ProjectionElem::ConstantIndex { .. } |
+                // assigning to P[i] requires P to be valid.
+                ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
+                // assigning to (P->variant) is okay if assigning to `P` is okay
+                //
+                // FIXME: is this true even if P is a adt with a dtor?
+                { }
+
+                // assigning to (*P) requires P to be initialized
+                ProjectionElem::Deref => {
+                    self.check_if_full_path_is_moved(
+                        location, InitializationRequiringAction::Use,
+                        (PlaceRef {
+                            base: &place.base,
+                            projection: base,
+                        }, span), flow_state);
+                    // (base initialized; no need to
+                    // recur further)
                     break;
                 }
-                Place::Projection(ref proj) => {
-                    let Projection { ref base, ref elem } = **proj;
-                    match *elem {
-                        ProjectionElem::Index(_/*operand*/) |
-                        ProjectionElem::ConstantIndex { .. } |
-                        // assigning to P[i] requires P to be valid.
-                        ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
-                        // assigning to (P->variant) is okay if assigning to `P` is okay
-                        //
-                        // FIXME: is this true even if P is a adt with a dtor?
-                        { }
-
-                        // assigning to (*P) requires P to be initialized
-                        ProjectionElem::Deref => {
-                            self.check_if_full_path_is_moved(
-                                location, InitializationRequiringAction::Use,
-                                (base, span), flow_state);
+
+                ProjectionElem::Subslice { .. } => {
+                    panic!("we don't allow assignments to subslices, location: {:?}",
+                           location);
+                }
+
+                ProjectionElem::Field(..) => {
+                    // if type of `P` has a dtor, then
+                    // assigning to `P.f` requires `P` itself
+                    // be already initialized
+                    let tcx = self.infcx.tcx;
+                    let base_ty = Place::ty_from(&place.base, base, self.body, tcx).ty;
+                    match base_ty.sty {
+                        ty::Adt(def, _) if def.has_dtor(tcx) => {
+                            self.check_if_path_or_subpath_is_moved(
+                                location, InitializationRequiringAction::Assignment,
+                                (PlaceRef {
+                                    base: &place.base,
+                                    projection: base,
+                                }, span), flow_state);
+
                             // (base initialized; no need to
                             // recur further)
                             break;
                         }
 
-                        ProjectionElem::Subslice { .. } => {
-                            panic!("we don't allow assignments to subslices, location: {:?}",
-                                   location);
-                        }
-
-                        ProjectionElem::Field(..) => {
-                            // if type of `P` has a dtor, then
-                            // assigning to `P.f` requires `P` itself
-                            // be already initialized
-                            let tcx = self.infcx.tcx;
-                            match base.ty(self.body, tcx).ty.sty {
-                                ty::Adt(def, _) if def.has_dtor(tcx) => {
-                                    self.check_if_path_or_subpath_is_moved(
-                                        location, InitializationRequiringAction::Assignment,
-                                        (base, span), flow_state);
-
-                                    // (base initialized; no need to
-                                    // recur further)
-                                    break;
-                                }
-
-
-                                // Once `let s; s.x = V; read(s.x);`,
-                                // is allowed, remove this match arm.
-                                ty::Adt(..) | ty::Tuple(..) => {
-                                    check_parent_of_field(self, location, base, span, flow_state);
-
-                                    if let Some(local) = place.base_local() {
-                                        // rust-lang/rust#21232,
-                                        // #54499, #54986: during
-                                        // period where we reject
-                                        // partial initialization, do
-                                        // not complain about
-                                        // unnecessary `mut` on an
-                                        // attempt to do a partial
-                                        // initialization.
-                                        self.used_mut.insert(local);
-                                    }
-                                }
-
-                                _ => {}
+                        // Once `let s; s.x = V; read(s.x);`,
+                        // is allowed, remove this match arm.
+                        ty::Adt(..) | ty::Tuple(..) => {
+                            check_parent_of_field(self, location, PlaceRef {
+                                base: &place.base,
+                                projection: base,
+                            }, span, flow_state);
+
+                            if let PlaceBase::Local(local) = place.base {
+                                // rust-lang/rust#21232,
+                                // #54499, #54986: during
+                                // period where we reject
+                                // partial initialization, do
+                                // not complain about
+                                // unnecessary `mut` on an
+                                // attempt to do a partial
+                                // initialization.
+                                self.used_mut.insert(local);
                             }
                         }
-                    }
 
-                    place = base;
+                        _ => {}
+                    }
                 }
             }
+
+            place_projection = base;
         }
 
         fn check_parent_of_field<'cx, 'tcx>(
             this: &mut MirBorrowckCtxt<'cx, 'tcx>,
             location: Location,
-            base: &Place<'tcx>,
+            base: PlaceRef<'cx, 'tcx>,
             span: Span,
             flow_state: &Flows<'cx, 'tcx>,
         ) {
@@ -1866,7 +1879,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 // no move out from an earlier location) then this is an attempt at initialization
                 // of the union - we should error in that case.
                 let tcx = this.infcx.tcx;
-                if let ty::Adt(def, _) = base.ty(this.body, tcx).ty.sty {
+                if let ty::Adt(def, _) =
+                    Place::ty_from(base.base, base.projection, this.body, tcx).ty.sty
+                {
                     if def.is_union() {
                         if this.move_data.path_map[mpi].iter().any(|moi| {
                             this.move_data.moves[*moi].source.is_predecessor_of(
@@ -1911,7 +1926,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         // partial initialization, do not complain about mutability
         // errors except for actual mutation (as opposed to an attempt
         // to do a partial initialization).
-        let previously_initialized = if let Some(local) = place.base_local() {
+        let previously_initialized = if let PlaceBase::Local(local) = place.base {
             self.is_local_ever_initialized(local, flow_state).is_some()
         } else {
             true
@@ -1927,7 +1942,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     BorrowKind::Mut { .. } => is_local_mutation_allowed,
                     BorrowKind::Shared | BorrowKind::Shallow => unreachable!(),
                 };
-                match self.is_mutable(place, is_local_mutation_allowed) {
+                match self.is_mutable(&place.base, &place.projection, is_local_mutation_allowed) {
                     Ok(root_place) => {
                         self.add_used_mut(root_place, flow_state);
                         return false;
@@ -1939,7 +1954,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 }
             }
             Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => {
-                match self.is_mutable(place, is_local_mutation_allowed) {
+                match self.is_mutable(&place.base, &place.projection, is_local_mutation_allowed) {
                     Ok(root_place) => {
                         self.add_used_mut(root_place, flow_state);
                         return false;
@@ -1960,7 +1975,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared))
             | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shallow)) => {
                 if let (Err(_place_err), true) = (
-                    self.is_mutable(place, is_local_mutation_allowed),
+                    self.is_mutable(&place.base, &place.projection, is_local_mutation_allowed),
                     self.errors_buffer.is_empty()
                 ) {
                     if self.infcx.tcx.migrate_borrowck() {
@@ -1981,7 +1996,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         self.report_mutability_error(
                             place,
                             span,
-                            _place_err,
+                            PlaceRef {
+                                base: _place_err.0,
+                                projection: _place_err.1,
+                            },
                             error_access,
                             location,
                         );
@@ -2015,7 +2033,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             self.report_mutability_error(
                 place,
                 span,
-                the_place_err,
+                PlaceRef {
+                    base: the_place_err.0,
+                    projection: the_place_err.1,
+                },
                 error_access,
                 location,
             );
@@ -2044,7 +2065,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn add_used_mut<'d>(&mut self, root_place: RootPlace<'d, 'tcx>, flow_state: &Flows<'cx, 'tcx>) {
         match root_place {
             RootPlace {
-                place: Place::Base(PlaceBase::Local(local)),
+                place_base: PlaceBase::Local(local),
+                place_projection: None,
                 is_local_mutation_allowed,
             } => {
                 // If the local may have been initialized, and it is now currently being
@@ -2057,19 +2079,25 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 }
             }
             RootPlace {
-                place: _,
+                place_base: _,
+                place_projection: _,
                 is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
             } => {}
             RootPlace {
-                place: place @ Place::Projection(_),
+                place_base,
+                place_projection: place_projection @ Some(_),
                 is_local_mutation_allowed: _,
             } => {
-                if let Some(field) = self.is_upvar_field_projection(place) {
+                if let Some(field) = self.is_upvar_field_projection(PlaceRef {
+                    base: &place_base,
+                    projection: &place_projection,
+                }) {
                     self.used_mut_upvars.push(field);
                 }
             }
             RootPlace {
-                place: Place::Base(PlaceBase::Static(..)),
+                place_base: PlaceBase::Static(..),
+                place_projection: None,
                 is_local_mutation_allowed: _,
             } => {}
         }
@@ -2079,62 +2107,78 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// Returns the root place if the place passed in is a projection.
     fn is_mutable<'d>(
         &self,
-        place: &'d Place<'tcx>,
+        place_base: &'d PlaceBase<'tcx>,
+        place_projection: &'d Option<Box<Projection<'tcx>>>,
         is_local_mutation_allowed: LocalMutationIsAllowed,
-    ) -> Result<RootPlace<'d, 'tcx>, &'d Place<'tcx>> {
-        match *place {
-            Place::Base(PlaceBase::Local(local)) => {
-                let local = &self.body.local_decls[local];
+    ) -> Result<RootPlace<'d, 'tcx>, (&'d PlaceBase<'tcx>, &'d Option<Box<Projection<'tcx>>>)> {
+        match (place_base, place_projection) {
+            (PlaceBase::Local(local), None) => {
+                let local = &self.body.local_decls[*local];
                 match local.mutability {
                     Mutability::Not => match is_local_mutation_allowed {
                         LocalMutationIsAllowed::Yes => Ok(RootPlace {
-                            place,
+                            place_base,
+                            place_projection,
                             is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
                         }),
                         LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace {
-                            place,
+                            place_base,
+                            place_projection,
                             is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars,
                         }),
-                        LocalMutationIsAllowed::No => Err(place),
+                        LocalMutationIsAllowed::No => Err((place_base, place_projection)),
                     },
                     Mutability::Mut => Ok(RootPlace {
-                        place,
+                        place_base,
+                        place_projection,
                         is_local_mutation_allowed,
                     }),
                 }
             }
             // The rules for promotion are made by `qualify_consts`, there wouldn't even be a
             // `Place::Promoted` if the promotion weren't 100% legal. So we just forward this
-            Place::Base(PlaceBase::Static(box Static{kind: StaticKind::Promoted(_), ..})) =>
+            (PlaceBase::Static(box Static {
+                kind: StaticKind::Promoted(_),
+                ..
+            }), None) =>
                 Ok(RootPlace {
-                    place,
+                    place_base,
+                    place_projection,
                     is_local_mutation_allowed,
                 }),
-            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })) => {
-                if !self.infcx.tcx.is_mutable_static(def_id) {
-                    Err(place)
+            (PlaceBase::Static(box Static {
+                kind: StaticKind::Static(def_id),
+                ..
+            }), None) => {
+                if !self.infcx.tcx.is_mutable_static(*def_id) {
+                    Err((place_base, place_projection))
                 } else {
                     Ok(RootPlace {
-                        place,
+                        place_base,
+                        place_projection,
                         is_local_mutation_allowed,
                     })
                 }
             }
-            Place::Projection(ref proj) => {
+            (_, Some(ref proj)) => {
                 match proj.elem {
                     ProjectionElem::Deref => {
-                        let base_ty = proj.base.ty(self.body, self.infcx.tcx).ty;
+                        let base_ty =
+                            Place::ty_from(place_base, &proj.base, self.body, self.infcx.tcx).ty;
 
                         // Check the kind of deref to decide
                         match base_ty.sty {
                             ty::Ref(_, _, mutbl) => {
                                 match mutbl {
                                     // Shared borrowed data is never mutable
-                                    hir::MutImmutable => Err(place),
+                                    hir::MutImmutable => Err((place_base, place_projection)),
                                     // Mutably borrowed data is mutable, but only if we have a
                                     // unique path to the `&mut`
                                     hir::MutMutable => {
-                                        let mode = match self.is_upvar_field_projection(place) {
+                                        let mode = match self.is_upvar_field_projection(PlaceRef {
+                                            base: &place_base,
+                                            projection: &place_projection,
+                                        }) {
                                             Some(field)
                                                 if self.upvars[field.index()].by_ref =>
                                             {
@@ -2143,19 +2187,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                             _ => LocalMutationIsAllowed::Yes,
                                         };
 
-                                        self.is_mutable(&proj.base, mode)
+                                        self.is_mutable(place_base, &proj.base, mode)
                                     }
                                 }
                             }
                             ty::RawPtr(tnm) => {
                                 match tnm.mutbl {
                                     // `*const` raw pointers are not mutable
-                                    hir::MutImmutable => Err(place),
+                                    hir::MutImmutable => Err((place_base, place_projection)),
                                     // `*mut` raw pointers are always mutable, regardless of
                                     // context. The users have to check by themselves.
                                     hir::MutMutable => {
                                         Ok(RootPlace {
-                                            place,
+                                            place_base,
+                                            place_projection,
                                             is_local_mutation_allowed,
                                         })
                                     }
@@ -2163,7 +2208,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             }
                             // `Box<T>` owns its content, so mutable if its location is mutable
                             _ if base_ty.is_box() => {
-                                self.is_mutable(&proj.base, is_local_mutation_allowed)
+                                self.is_mutable(place_base, &proj.base, is_local_mutation_allowed)
                             }
                             // Deref should only be for reference, pointers or boxes
                             _ => bug!("Deref of unexpected type: {:?}", base_ty),
@@ -2176,17 +2221,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     | ProjectionElem::ConstantIndex { .. }
                     | ProjectionElem::Subslice { .. }
                     | ProjectionElem::Downcast(..) => {
-                        let upvar_field_projection = self.is_upvar_field_projection(place);
+                        let upvar_field_projection = self.is_upvar_field_projection(PlaceRef {
+                            base: &place_base,
+                            projection: &place_projection,
+                        });
                         if let Some(field) = upvar_field_projection {
                             let upvar = &self.upvars[field.index()];
                             debug!(
-                                "upvar.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
-                                upvar, is_local_mutation_allowed, place
+                                "upvar.mutability={:?} local_mutation_is_allowed={:?} \
+                                place={:?} {:?}",
+                                upvar, is_local_mutation_allowed, place_base, place_projection
                             );
                             match (upvar.mutability, is_local_mutation_allowed) {
                                 (Mutability::Not, LocalMutationIsAllowed::No)
                                 | (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) => {
-                                    Err(place)
+                                    Err((place_base, place_projection))
                                 }
                                 (Mutability::Not, LocalMutationIsAllowed::Yes)
                                 | (Mutability::Mut, _) => {
@@ -2216,15 +2265,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                     //     });
                                     // }
                                     // ```
-                                    let _ = self.is_mutable(&proj.base, is_local_mutation_allowed)?;
+                                    let _ = self.is_mutable(place_base,
+                                                            &proj.base,
+                                                            is_local_mutation_allowed)?;
                                     Ok(RootPlace {
-                                        place,
+                                        place_base,
+                                        place_projection,
                                         is_local_mutation_allowed,
                                     })
                                 }
                             }
                         } else {
-                            self.is_mutable(&proj.base, is_local_mutation_allowed)
+                            self.is_mutable(place_base, &proj.base, is_local_mutation_allowed)
                         }
                     }
                 }
@@ -2236,33 +2288,34 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// then returns the index of the field being projected. Note that this closure will always
     /// be `self` in the current MIR, because that is the only time we directly access the fields
     /// of a closure type.
-    pub fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
-        let (place, by_ref) = if let Place::Projection(ref proj) = place {
-            if let ProjectionElem::Deref = proj.elem {
-                (&proj.base, true)
-            } else {
-                (place, false)
-            }
-        } else {
-            (place, false)
-        };
-
-        match place {
-            Place::Projection(ref proj) => match proj.elem {
-                ProjectionElem::Field(field, _ty) => {
-                    let tcx = self.infcx.tcx;
-                    let base_ty = proj.base.ty(self.body, tcx).ty;
+    pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'cx, 'tcx>) -> Option<Field> {
+        let mut place_projection = place_ref.projection;
+        let mut by_ref = false;
+
+        if let Some(box Projection {
+            base,
+            elem: ProjectionElem::Deref,
+        }) = place_projection {
+            place_projection = &base;
+            by_ref = true;
+        }
 
-                    if (base_ty.is_closure() || base_ty.is_generator()) &&
-                        (!by_ref || self.upvars[field.index()].by_ref)
-                    {
-                        Some(field)
-                    } else {
-                        None
-                    }
-                },
-                _ => None,
+        match place_projection {
+            Some(box Projection {
+                base,
+                elem: ProjectionElem::Field(field, _ty),
+            }) => {
+                let tcx = self.infcx.tcx;
+                let base_ty = Place::ty_from(place_ref.base, &base, self.body, tcx).ty;
+
+                if (base_ty.is_closure() || base_ty.is_generator()) &&
+                    (!by_ref || self.upvars[field.index()].by_ref) {
+                    Some(*field)
+                } else {
+                    None
+                }
             }
+
             _ => None,
         }
     }
diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs
index 5939adc5528..38653dc0e5e 100644
--- a/src/librustc_mir/borrow_check/move_errors.rs
+++ b/src/librustc_mir/borrow_check/move_errors.rs
@@ -91,7 +91,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 // If that ever stops being the case, then the ever initialized
                 // flow could be used.
                 if let Some(StatementKind::Assign(
-                    Place::Base(PlaceBase::Local(local)),
+                    Place {
+                        base: PlaceBase::Local(local),
+                        projection: None,
+                    },
                     box Rvalue::Use(Operand::Move(move_from)),
                 )) = self.body.basic_blocks()[location.block]
                     .statements
@@ -128,7 +131,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     }
                 }
 
-                let move_spans = self.move_spans(&original_path, location);
+                let move_spans = self.move_spans(original_path.as_place_ref(), location);
                 grouped_errors.push(GroupedMoveError::OtherIllegalMove {
                     use_spans: move_spans,
                     original_path,
@@ -157,7 +160,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         let from_simple_let = match_place.is_none();
         let match_place = match_place.as_ref().unwrap_or(move_from);
 
-        match self.move_data.rev_lookup.find(match_place) {
+        match self.move_data.rev_lookup.find(match_place.as_place_ref()) {
             // Error with the match place
             LookupResult::Parent(_) => {
                 for ge in &mut *grouped_errors {
@@ -189,7 +192,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
             // Error with the pattern
             LookupResult::Exact(_) => {
-                let mpi = match self.move_data.rev_lookup.find(move_from) {
+                let mpi = match self.move_data.rev_lookup.find(move_from.as_place_ref()) {
                     LookupResult::Parent(Some(mpi)) => mpi,
                     // move_from should be a projection from match_place.
                     _ => unreachable!("Probably not unreachable..."),
@@ -239,7 +242,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 };
             debug!("report: original_path={:?} span={:?}, kind={:?} \
                    original_path.is_upvar_field_projection={:?}", original_path, span, kind,
-                   self.is_upvar_field_projection(original_path));
+                   self.is_upvar_field_projection(original_path.as_place_ref()));
             (
                 match kind {
                     IllegalMoveOriginKind::Static => {
@@ -273,20 +276,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         place: &Place<'tcx>,
         span: Span
     ) -> DiagnosticBuilder<'a> {
-        let mut base_static = place;
-        loop {
-            match base_static {
-                Place::Base(_) => break,
-                Place::Projection(box Projection { base, .. }) => base_static = base,
+        let description = if place.projection.is_none() {
+            format!("static item `{}`", self.describe_place(place.as_place_ref()).unwrap())
+        } else {
+            let mut base_static = &place.projection;
+            while let Some(box Projection { base: Some(ref proj), .. }) = base_static {
+                base_static = &proj.base;
             }
-        }
+            let base_static = PlaceRef {
+                base: &place.base,
+                projection: base_static,
+            };
 
-        let description = if let Place::Base(_) = place {
-            format!("static item `{}`", self.describe_place(place).unwrap())
-        } else {
             format!(
                 "`{:?}` as `{:?}` is a static item",
-                self.describe_place(place).unwrap(),
+                self.describe_place(place.as_place_ref()).unwrap(),
                 self.describe_place(base_static).unwrap(),
             )
         };
@@ -304,16 +308,22 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         // borrow to provide feedback about why this
         // was a move rather than a copy.
         let ty = deref_target_place.ty(self.body, self.infcx.tcx).ty;
-        let upvar_field = self.prefixes(&move_place, PrefixSet::All)
+        let upvar_field = self.prefixes(move_place.as_place_ref(), PrefixSet::All)
             .find_map(|p| self.is_upvar_field_projection(p));
 
-        let deref_base = match deref_target_place {
-            Place::Projection(box Projection { base, elem: ProjectionElem::Deref }) => base,
+        let deref_base = match deref_target_place.projection {
+            Some(box Projection { ref base, elem: ProjectionElem::Deref }) => PlaceRef {
+                base: &deref_target_place.base,
+                projection: base,
+            },
             _ => bug!("deref_target_place is not a deref projection"),
         };
 
-        if let Place::Base(PlaceBase::Local(local)) = *deref_base {
-            let decl = &self.body.local_decls[local];
+        if let PlaceRef {
+            base: PlaceBase::Local(local),
+            projection: None,
+        } = deref_base {
+            let decl = &self.body.local_decls[*local];
             if decl.is_ref_for_guard() {
                 let mut err = self.cannot_move_out_of(
                     span,
@@ -353,9 +363,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 let upvar_name = upvar.name;
                 let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id);
 
-                let place_name = self.describe_place(move_place).unwrap();
+                let place_name = self.describe_place(move_place.as_place_ref()).unwrap();
 
-                let place_description = if self.is_upvar_field_projection(move_place).is_some() {
+                let place_description = if self
+                    .is_upvar_field_projection(move_place.as_place_ref())
+                    .is_some()
+                {
                     format!("`{}`, a {}", place_name, capture_description)
                 } else {
                     format!(
@@ -379,7 +392,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
             _ => {
                 let source = self.borrowed_content_source(deref_base);
-                match (self.describe_place(move_place), source.describe_for_named_place()) {
+                match (
+                    self.describe_place(move_place.as_place_ref()),
+                    source.describe_for_named_place(),
+                ) {
                     (Some(place_desc), Some(source_desc)) => {
                         self.cannot_move_out_of(
                             span,
@@ -439,7 +455,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
                 if binds_to.is_empty() {
                     let place_ty = move_from.ty(self.body, self.infcx.tcx).ty;
-                    let place_desc = match self.describe_place(&move_from) {
+                    let place_desc = match self.describe_place(move_from.as_place_ref()) {
                         Some(desc) => format!("`{}`", desc),
                         None => format!("value"),
                     };
@@ -467,7 +483,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
                 let span = use_spans.var_or_use();
                 let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
-                let place_desc = match self.describe_place(original_path) {
+                let place_desc = match self.describe_place(original_path.as_place_ref()) {
                     Some(desc) => format!("`{}`", desc),
                     None => format!("value"),
                 };
diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs
index 59a3354f9c5..c424c06c41a 100644
--- a/src/librustc_mir/borrow_check/mutability_errors.rs
+++ b/src/librustc_mir/borrow_check/mutability_errors.rs
@@ -2,7 +2,9 @@ use core::unicode::property::Pattern_White_Space;
 use rustc::hir;
 use rustc::hir::Node;
 use rustc::mir::{self, BindingForm, ClearCrossCrate, Local, Location, Body};
-use rustc::mir::{Mutability, Place, PlaceBase, Projection, ProjectionElem, Static, StaticKind};
+use rustc::mir::{
+    Mutability, Place, PlaceRef, PlaceBase, Projection, ProjectionElem, Static, StaticKind
+};
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc_data_structures::indexed_vec::Idx;
 use syntax_pos::Span;
@@ -25,7 +27,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         &mut self,
         access_place: &Place<'tcx>,
         span: Span,
-        the_place_err: &Place<'tcx>,
+        the_place_err: PlaceRef<'cx, 'tcx>,
         error_access: AccessKind,
         location: Location,
     ) {
@@ -40,13 +42,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         let item_msg;
         let reason;
         let mut opt_source = None;
-        let access_place_desc = self.describe_place(access_place);
+        let access_place_desc = self.describe_place(access_place.as_place_ref());
         debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
 
         match the_place_err {
-            Place::Base(PlaceBase::Local(local)) => {
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } => {
                 item_msg = format!("`{}`", access_place_desc.unwrap());
-                if let Place::Base(PlaceBase::Local(_)) = access_place {
+                if let Place {
+                    base: PlaceBase::Local(_),
+                    projection: None,
+                } = access_place {
                     reason = ", as it is not declared as mutable".to_string();
                 } else {
                     let name = self.body.local_decls[*local]
@@ -56,16 +64,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Projection(box Projection {
-                base,
-                elem: ProjectionElem::Field(upvar_index, _),
-            }) => {
+            PlaceRef {
+                base: _,
+                projection:
+                    Some(box Projection {
+                        base,
+                        elem: ProjectionElem::Field(upvar_index, _),
+                    }),
+            } => {
                 debug_assert!(is_closure_or_generator(
-                    base.ty(self.body, self.infcx.tcx).ty
+                    Place::ty_from(&the_place_err.base, &base, self.body, self.infcx.tcx).ty
                 ));
 
                 item_msg = format!("`{}`", access_place_desc.unwrap());
-                if self.is_upvar_field_projection(access_place).is_some() {
+                if self.is_upvar_field_projection(access_place.as_place_ref()).is_some() {
                     reason = ", as it is not declared as mutable".to_string();
                 } else {
                     let name = self.upvars[upvar_index.index()].name;
@@ -73,26 +85,38 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Projection(box Projection {
-                base,
-                elem: ProjectionElem::Deref,
-            }) => {
-                if *base == Place::Base(PlaceBase::Local(Local::new(1))) &&
+            PlaceRef {
+                base: _,
+                projection:
+                    Some(box Projection {
+                        base,
+                        elem: ProjectionElem::Deref,
+                    }),
+            } => {
+                if the_place_err.base == &PlaceBase::Local(Local::new(1)) &&
+                    base.is_none() &&
                     !self.upvars.is_empty() {
                     item_msg = format!("`{}`", access_place_desc.unwrap());
                     debug_assert!(self.body.local_decls[Local::new(1)].ty.is_region_ptr());
                     debug_assert!(is_closure_or_generator(
-                        the_place_err.ty(self.body, self.infcx.tcx).ty
+                        Place::ty_from(
+                            the_place_err.base,
+                            the_place_err.projection,
+                            self.body,
+                            self.infcx.tcx
+                        )
+                        .ty
                     ));
 
-                    reason = if self.is_upvar_field_projection(access_place).is_some() {
-                        ", as it is a captured variable in a `Fn` closure".to_string()
-                    } else {
-                        ", as `Fn` closures cannot mutate their captured variables".to_string()
-                    }
+                    reason =
+                        if self.is_upvar_field_projection(access_place.as_place_ref()).is_some() {
+                            ", as it is a captured variable in a `Fn` closure".to_string()
+                        } else {
+                            ", as `Fn` closures cannot mutate their captured variables".to_string()
+                        }
                 } else if {
-                    if let Place::Base(PlaceBase::Local(local)) = *base {
-                        self.body.local_decls[local].is_ref_for_guard()
+                    if let (PlaceBase::Local(local), None) = (&the_place_err.base, base) {
+                        self.body.local_decls[*local].is_ref_for_guard()
                     } else {
                         false
                     }
@@ -100,7 +124,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     item_msg = format!("`{}`", access_place_desc.unwrap());
                     reason = ", as it is immutable for the pattern guard".to_string();
                 } else {
-                    let source = self.borrowed_content_source(base);
+                    let source = self.borrowed_content_source(PlaceRef {
+                        base: the_place_err.base,
+                        projection: base,
+                    });
                     let pointer_type = source.describe_for_immutable_place();
                     opt_source = Some(source);
                     if let Some(desc) = access_place_desc {
@@ -119,11 +146,27 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Promoted(_), .. })) =>
-                unreachable!(),
+            PlaceRef {
+                base:
+                    PlaceBase::Static(box Static {
+                        kind: StaticKind::Promoted(_),
+                        ..
+                    }),
+                projection: None,
+            } => unreachable!(),
 
-            Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Static(def_id), .. })) => {
-                if let Place::Base(PlaceBase::Static(_)) = access_place {
+            PlaceRef {
+                base:
+                    PlaceBase::Static(box Static {
+                        kind: StaticKind::Static(def_id),
+                        ..
+                    }),
+                projection: None,
+            } => {
+                if let Place {
+                    base: PlaceBase::Static(_),
+                    projection: None,
+                } = access_place {
                     item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
                     reason = String::new();
                 } else {
@@ -133,22 +176,36 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Projection(box Projection {
+            PlaceRef {
                 base: _,
-                elem: ProjectionElem::Index(_),
-            })
-            | Place::Projection(box Projection {
+                projection:
+                    Some(box Projection {
+                        base: _,
+                        elem: ProjectionElem::Index(_),
+                    }),
+            }
+            | PlaceRef {
                 base: _,
-                elem: ProjectionElem::ConstantIndex { .. },
-            })
-            | Place::Projection(box Projection {
+                projection:
+                    Some(box Projection {
+                        base: _,
+                        elem: ProjectionElem::ConstantIndex { .. },
+                    }),
+            }
+            | PlaceRef {
                 base: _,
-                elem: ProjectionElem::Subslice { .. },
-            })
-            | Place::Projection(box Projection {
+                projection: Some(box Projection {
+                    base: _,
+                    elem: ProjectionElem::Subslice { .. },
+                }),
+            }
+            | PlaceRef {
                 base: _,
-                elem: ProjectionElem::Downcast(..),
-            }) => bug!("Unexpected immutable place."),
+                projection: Some(box Projection {
+                    base: _,
+                    elem: ProjectionElem::Downcast(..),
+                }),
+            } => bug!("Unexpected immutable place."),
         }
 
         debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
@@ -187,7 +244,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     format!(
                         "mutable borrow occurs due to use of `{}` in closure",
                         // always Some() if the message is printed.
-                        self.describe_place(access_place).unwrap_or_default(),
+                        self.describe_place(access_place.as_place_ref()).unwrap_or_default(),
                     )
                 );
                 borrow_span
@@ -203,21 +260,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             // something like `*((*_1).0`. The local that we get will be a reference to the
             // struct we've got a field access of (it must be a reference since there's a deref
             // after the field access).
-            Place::Projection(box Projection {
-                base: Place::Projection(box Projection {
-                    base: Place::Projection(box Projection {
-                        base,
-                        elem: ProjectionElem::Deref,
+            PlaceRef {
+                base,
+                projection: Some(box Projection {
+                    base: Some(box Projection {
+                        base: Some(box Projection {
+                            base: base_proj,
+                            elem: ProjectionElem::Deref,
+                        }),
+                        elem: ProjectionElem::Field(field, _),
                     }),
-                    elem: ProjectionElem::Field(field, _),
+                    elem: ProjectionElem::Deref,
                 }),
-                elem: ProjectionElem::Deref,
-            }) => {
+            } => {
                 err.span_label(span, format!("cannot {ACT}", ACT = act));
 
                 if let Some((span, message)) = annotate_struct_field(
                     self.infcx.tcx,
-                    base.ty(self.body, self.infcx.tcx).ty,
+                    Place::ty_from(&base, &base_proj, self.body, self.infcx.tcx).ty,
                     field,
                 ) {
                     err.span_suggestion(
@@ -230,43 +290,46 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             },
 
             // Suggest removing a `&mut` from the use of a mutable reference.
-            Place::Base(PlaceBase::Local(local))
-                if {
-                    self.body.local_decls.get(*local).map(|local_decl| {
-                        if let ClearCrossCrate::Set(
-                            mir::BindingForm::ImplicitSelf(kind)
-                        ) = local_decl.is_user_variable.as_ref().unwrap() {
-                            // Check if the user variable is a `&mut self` and we can therefore
-                            // suggest removing the `&mut`.
-                            //
-                            // Deliberately fall into this case for all implicit self types,
-                            // so that we don't fall in to the next case with them.
-                            *kind == mir::ImplicitSelfKind::MutRef
-                        } else if Some(kw::SelfLower) == local_decl.name {
-                            // Otherwise, check if the name is the self kewyord - in which case
-                            // we have an explicit self. Do the same thing in this case and check
-                            // for a `self: &mut Self` to suggest removing the `&mut`.
-                            if let ty::Ref(
-                                _, _, hir::Mutability::MutMutable
-                            ) = local_decl.ty.sty {
-                                true
-                            } else {
-                                false
-                            }
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } if {
+                self.body.local_decls.get(*local).map(|local_decl| {
+                    if let ClearCrossCrate::Set(
+                        mir::BindingForm::ImplicitSelf(kind)
+                    ) = local_decl.is_user_variable.as_ref().unwrap() {
+                        // Check if the user variable is a `&mut self` and we can therefore
+                        // suggest removing the `&mut`.
+                        //
+                        // Deliberately fall into this case for all implicit self types,
+                        // so that we don't fall in to the next case with them.
+                        *kind == mir::ImplicitSelfKind::MutRef
+                    } else if Some(kw::SelfLower) == local_decl.name {
+                        // Otherwise, check if the name is the self kewyord - in which case
+                        // we have an explicit self. Do the same thing in this case and check
+                        // for a `self: &mut Self` to suggest removing the `&mut`.
+                        if let ty::Ref(
+                            _, _, hir::Mutability::MutMutable
+                        ) = local_decl.ty.sty {
+                            true
                         } else {
                             false
                         }
-                    }).unwrap_or(false)
-                } =>
-            {
+                    } else {
+                        false
+                    }
+                }).unwrap_or(false)
+            } => {
                 err.span_label(span, format!("cannot {ACT}", ACT = act));
                 err.span_label(span, "try removing `&mut` here");
             },
 
             // We want to suggest users use `let mut` for local (user
             // variable) mutations...
-            Place::Base(PlaceBase::Local(local))
-                if self.body.local_decls[*local].can_be_made_mutable() => {
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } if self.body.local_decls[*local].can_be_made_mutable() => {
                 // ... but it doesn't make sense to suggest it on
                 // variables that are `ref x`, `ref mut x`, `&self`,
                 // or `&mut self` (such variables are simply not
@@ -284,12 +347,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
 
             // Also suggest adding mut for upvars
-            Place::Projection(box Projection {
+            PlaceRef {
                 base,
-                elem: ProjectionElem::Field(upvar_index, _),
-            }) => {
+                projection: Some(box Projection {
+                    base: proj_base,
+                    elem: ProjectionElem::Field(upvar_index, _),
+                }),
+            } => {
                 debug_assert!(is_closure_or_generator(
-                    base.ty(self.body, self.infcx.tcx).ty
+                    Place::ty_from(&base, &proj_base, self.body, self.infcx.tcx).ty
                 ));
 
                 err.span_label(span, format!("cannot {ACT}", ACT = act));
@@ -317,8 +383,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             // complete hack to approximate old AST-borrowck
             // diagnostic: if the span starts with a mutable borrow of
             // a local variable, then just suggest the user remove it.
-            Place::Base(PlaceBase::Local(_))
-                if {
+            PlaceRef {
+                base: PlaceBase::Local(_),
+                projection: None,
+            } if {
                     if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
                         snippet.starts_with("&mut ")
                     } else {
@@ -330,10 +398,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 err.span_label(span, "try removing `&mut` here");
             }
 
-            Place::Projection(box Projection {
-                base: Place::Base(PlaceBase::Local(local)),
-                elem: ProjectionElem::Deref,
-            }) if {
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: Some(box Projection {
+                    base: None,
+                    elem: ProjectionElem::Deref,
+                }),
+            } if {
                 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
                     self.body.local_decls[*local].is_user_variable
                 {
@@ -354,10 +425,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             //
             // FIXME: can this case be generalized to work for an
             // arbitrary base for the projection?
-            Place::Projection(box Projection {
-                base: Place::Base(PlaceBase::Local(local)),
-                elem: ProjectionElem::Deref,
-            }) if self.body.local_decls[*local].is_user_variable.is_some() =>
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: Some(box Projection {
+                    base: None,
+                    elem: ProjectionElem::Deref,
+                }),
+            } if self.body.local_decls[*local].is_user_variable.is_some() =>
             {
                 let local_decl = &self.body.local_decls[*local];
                 let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
@@ -434,10 +508,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Projection(box Projection {
+            PlaceRef {
                 base,
-                elem: ProjectionElem::Deref,
-            }) if *base == Place::Base(PlaceBase::Local(Local::new(1))) &&
+                projection: Some(box Projection {
+                    base: None,
+                    elem: ProjectionElem::Deref,
+                }),
+            // FIXME document what is this 1 magic number about
+            } if *base == PlaceBase::Local(Local::new(1)) &&
                   !self.upvars.is_empty() =>
             {
                 err.span_label(span, format!("cannot {ACT}", ACT = act));
@@ -447,10 +525,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 );
             }
 
-            Place::Projection(box Projection {
+            PlaceRef {
                 base: _,
-                elem: ProjectionElem::Deref,
-            }) => {
+                projection: Some(box Projection {
+                    base: _,
+                    elem: ProjectionElem::Deref,
+                }),
+            } => {
                 err.span_label(span, format!("cannot {ACT}", ACT = act));
 
                 match opt_source {
diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
index 058cdec5cea..055568f0a27 100644
--- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
@@ -128,7 +128,10 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> {
         // When we see `X = ...`, then kill borrows of
         // `(*X).foo` and so forth.
         if let Some(all_facts) = self.all_facts {
-            if let Place::Base(PlaceBase::Local(temp)) = place {
+            if let Place {
+                base: PlaceBase::Local(temp),
+                projection: None,
+            } = place {
                 if let Some(borrow_indices) = self.borrow_set.local_map.get(temp) {
                     all_facts.killed.reserve(borrow_indices.len());
                     for &borrow_index in borrow_indices {
diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
index ed88b162535..abb84c59d9b 100644
--- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
@@ -7,7 +7,7 @@ use crate::borrow_check::nll::ConstraintDescription;
 use crate::borrow_check::{MirBorrowckCtxt, WriteKind};
 use rustc::mir::{
     CastKind, ConstraintCategory, FakeReadCause, Local, Location, Body, Operand, Place, PlaceBase,
-    Projection, ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind,
+    Rvalue, Statement, StatementKind, TerminatorKind,
 };
 use rustc::ty::{self, TyCtxt};
 use rustc::ty::adjustment::{PointerCast};
@@ -252,7 +252,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             Some(Cause::LiveVar(local, location)) => {
                 let span = body.source_info(location).span;
                 let spans = self
-                    .move_spans(&Place::from(local), location)
+                    .move_spans(Place::from(local).as_place_ref(), location)
                     .or_else(|| self.borrow_spans(span, location));
 
                 let borrow_location = location;
@@ -272,7 +272,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 let mut should_note_order = false;
                 if body.local_decls[local].name.is_some() {
                     if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
-                        if let Place::Base(PlaceBase::Local(borrowed_local)) = place {
+                        if let Place {
+                            base: PlaceBase::Local(borrowed_local),
+                            projection: None,
+                        } = place {
                              if body.local_decls[*borrowed_local].name.is_some()
                                 && local != *borrowed_local
                             {
@@ -301,7 +304,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             region,
                         );
                     if let Some(region_name) = region_name {
-                        let opt_place_desc = self.describe_place(&borrow.borrowed_place);
+                        let opt_place_desc =
+                            self.describe_place(borrow.borrowed_place.as_place_ref());
                         BorrowExplanation::MustBeValidFor {
                             category,
                             from_closure,
@@ -489,8 +493,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         // Just point to the function, to reduce the chance of overlapping spans.
                         let function_span = match func {
                             Operand::Constant(c) => c.span,
-                            Operand::Copy(Place::Base(PlaceBase::Local(l))) |
-                            Operand::Move(Place::Base(PlaceBase::Local(l))) => {
+                            Operand::Copy(Place {
+                                base: PlaceBase::Local(l),
+                                projection: None,
+                            }) |
+                            Operand::Move(Place {
+                                base: PlaceBase::Local(l),
+                                projection: None,
+                            }) => {
                                 let local_decl = &self.body.local_decls[*l];
                                 if local_decl.name.is_none() {
                                     local_decl.source_info.span
@@ -531,7 +541,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         // it which simplifies the termination logic.
         let mut queue = vec![location];
         let mut target = if let Some(&Statement {
-            kind: StatementKind::Assign(Place::Base(PlaceBase::Local(local)), _),
+            kind: StatementKind::Assign(Place {
+                base: PlaceBase::Local(local),
+                projection: None,
+            }, _),
             ..
         }) = stmt
         {
@@ -555,13 +568,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
                 // The only kind of statement that we care about is assignments...
                 if let StatementKind::Assign(place, box rvalue) = &stmt.kind {
-                    let into = match place {
-                        Place::Base(PlaceBase::Local(into)) => into,
-                        Place::Projection(box Projection {
-                            base: Place::Base(PlaceBase::Local(into)),
-                            elem: ProjectionElem::Deref,
-                        }) => into,
-                        _ => {
+                    let into = match place.local_or_deref_local() {
+                        Some(into) => into,
+                        None => {
                             // Continue at the next location.
                             queue.push(current_location.successor_within_block());
                             continue;
@@ -572,11 +581,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         // If we see a use, we should check whether it is our data, and if so
                         // update the place that we're looking for to that new place.
                         Rvalue::Use(operand) => match operand {
-                            Operand::Copy(Place::Base(PlaceBase::Local(from)))
-                            | Operand::Move(Place::Base(PlaceBase::Local(from)))
+                            Operand::Copy(Place {
+                                base: PlaceBase::Local(from),
+                                projection: None,
+                            })
+                            | Operand::Move(Place {
+                                base: PlaceBase::Local(from),
+                                projection: None,
+                            })
                                 if *from == target =>
                             {
-                                target = *into;
+                                target = into;
                             }
                             _ => {}
                         },
@@ -585,8 +600,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         Rvalue::Cast(
                             CastKind::Pointer(PointerCast::Unsize), operand, ty
                         ) => match operand {
-                            Operand::Copy(Place::Base(PlaceBase::Local(from)))
-                            | Operand::Move(Place::Base(PlaceBase::Local(from)))
+                            Operand::Copy(Place {
+                                base: PlaceBase::Local(from),
+                                projection: None,
+                            })
+                            | Operand::Move(Place {
+                                base: PlaceBase::Local(from),
+                                projection: None,
+                            })
                                 if *from == target =>
                             {
                                 debug!("was_captured_by_trait_object: ty={:?}", ty);
@@ -616,7 +637,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 debug!("was_captured_by_trait_object: terminator={:?}", terminator);
 
                 if let TerminatorKind::Call {
-                    destination: Some((Place::Base(PlaceBase::Local(dest)), block)),
+                    destination: Some((Place {
+                        base: PlaceBase::Local(dest),
+                        projection: None,
+                    }, block)),
                     args,
                     ..
                 } = &terminator.kind
@@ -627,7 +651,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     );
                     // Check if one of the arguments to this function is the target place.
                     let found_target = args.iter().any(|arg| {
-                        if let Operand::Move(Place::Base(PlaceBase::Local(potential))) = arg {
+                        if let Operand::Move(Place {
+                            base: PlaceBase::Local(potential),
+                            projection: None,
+                        }) = arg {
                             *potential == target
                         } else {
                             false
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index dbb5a52e0aa..b0e364fa2dd 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -499,13 +499,16 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
             };
 
             // FIXME use place_projection.is_empty() when is available
-            if let Place::Base(_) = place {
+            if place.projection.is_none() {
                 if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
                     let is_promoted = match place {
-                        Place::Base(PlaceBase::Static(box Static {
-                            kind: StaticKind::Promoted(_),
-                            ..
-                        })) => true,
+                        Place {
+                            base: PlaceBase::Static(box Static {
+                                kind: StaticKind::Promoted(_),
+                                ..
+                            }),
+                            projection: None,
+                        } => true,
                         _ => false,
                     };
 
@@ -1345,15 +1348,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 // of lowering. Assignments to other sorts of places *are* interesting
                 // though.
                 let category = match *place {
-                    Place::Base(PlaceBase::Local(RETURN_PLACE)) => if let BorrowCheckContext {
+                    Place {
+                        base: PlaceBase::Local(RETURN_PLACE),
+                        projection: None,
+                    } => if let BorrowCheckContext {
                         universal_regions:
                             UniversalRegions {
                                 defining_ty: DefiningTy::Const(def_id, _),
                                 ..
                             },
                         ..
-                    } = self.borrowck_context
-                    {
+                    } = self.borrowck_context {
                         if tcx.is_static(*def_id) {
                             ConstraintCategory::UseAsStatic
                         } else {
@@ -1362,8 +1367,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     } else {
                         ConstraintCategory::Return
                     },
-                    Place::Base(PlaceBase::Local(l))
-                        if !body.local_decls[l].is_user_variable.is_some() => {
+                    Place {
+                        base: PlaceBase::Local(l),
+                        projection: None,
+                    } if !body.local_decls[l].is_user_variable.is_some() => {
                         ConstraintCategory::Boring
                     }
                     _ => ConstraintCategory::Assignment,
@@ -1647,7 +1654,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             Some((ref dest, _target_block)) => {
                 let dest_ty = dest.ty(body, tcx).ty;
                 let category = match *dest {
-                    Place::Base(PlaceBase::Local(RETURN_PLACE)) => {
+                    Place {
+                        base: PlaceBase::Local(RETURN_PLACE),
+                        projection: None,
+                    } => {
                         if let BorrowCheckContext {
                             universal_regions:
                                 UniversalRegions {
@@ -1666,8 +1676,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             ConstraintCategory::Return
                         }
                     }
-                    Place::Base(PlaceBase::Local(l))
-                        if !body.local_decls[l].is_user_variable.is_some() => {
+                    Place {
+                        base: PlaceBase::Local(l),
+                        projection: None,
+                    } if !body.local_decls[l].is_user_variable.is_some() => {
                         ConstraintCategory::Boring
                     }
                     _ => ConstraintCategory::Assignment,
@@ -2400,19 +2412,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         // *p`, where the `p` has type `&'b mut Foo`, for example, we
         // need to ensure that `'b: 'a`.
 
-        let mut borrowed_place = borrowed_place;
+        let mut borrowed_projection = &borrowed_place.projection;
 
         debug!(
             "add_reborrow_constraint({:?}, {:?}, {:?})",
             location, borrow_region, borrowed_place
         );
-        while let Place::Projection(box Projection { base, elem }) = borrowed_place {
-            debug!("add_reborrow_constraint - iteration {:?}", borrowed_place);
+        while let Some(box proj) = borrowed_projection {
+            debug!("add_reborrow_constraint - iteration {:?}", borrowed_projection);
 
-            match *elem {
+            match proj.elem {
                 ProjectionElem::Deref => {
                     let tcx = self.infcx.tcx;
-                    let base_ty = base.ty(body, tcx).ty;
+                    let base_ty = Place::ty_from(&borrowed_place.base, &proj.base, body, tcx).ty;
 
                     debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
                     match base_ty.sty {
@@ -2477,7 +2489,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
             // The "propagate" case. We need to check that our base is valid
             // for the borrow's lifetime.
-            borrowed_place = base;
+            borrowed_projection = &proj.base;
         }
     }
 
diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs
index 538ac6881d9..75065816df0 100644
--- a/src/librustc_mir/borrow_check/path_utils.rs
+++ b/src/librustc_mir/borrow_check/path_utils.rs
@@ -50,7 +50,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
             body,
             &borrowed.borrowed_place,
             borrowed.kind,
-            place,
+            place.as_place_ref(),
             access,
             places_conflict::PlaceConflictBias::Overlap,
         ) {
diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs
index a8f28b64b49..72d5588c341 100644
--- a/src/librustc_mir/borrow_check/place_ext.rs
+++ b/src/librustc_mir/borrow_check/place_ext.rs
@@ -55,7 +55,7 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
 
             for proj in place_projection {
                 if proj.elem == ProjectionElem::Deref {
-                    let ty = proj.base.ty(body, tcx).ty;
+                    let ty = Place::ty_from(place_base, &proj.base, body, tcx).ty;
                     match ty.sty {
                         // For both derefs of raw pointers and `&T`
                         // references, the original path is `Copy` and
diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs
index 64ca00defc9..348214f97f2 100644
--- a/src/librustc_mir/borrow_check/places_conflict.rs
+++ b/src/librustc_mir/borrow_check/places_conflict.rs
@@ -3,8 +3,8 @@ use crate::borrow_check::Overlap;
 use crate::borrow_check::{Deep, Shallow, AccessDepth};
 use rustc::hir;
 use rustc::mir::{
-    BorrowKind, Body, Place, PlaceBase, Projection, ProjectionElem, ProjectionsIter,
-    StaticKind
+    Body, BorrowKind, Place, PlaceBase, PlaceRef, Projection, ProjectionElem, ProjectionsIter,
+    StaticKind,
 };
 use rustc::ty::{self, TyCtxt};
 use std::cmp::max;
@@ -36,7 +36,7 @@ crate fn places_conflict<'tcx>(
         body,
         borrow_place,
         BorrowKind::Mut { allow_two_phase_borrow: true },
-        access_place,
+        access_place.as_place_ref(),
         AccessDepth::Deep,
         bias,
     )
@@ -51,7 +51,7 @@ pub(super) fn borrow_conflicts_with_place<'tcx>(
     body: &Body<'tcx>,
     borrow_place: &Place<'tcx>,
     borrow_kind: BorrowKind,
-    access_place: &Place<'tcx>,
+    access_place: PlaceRef<'_, 'tcx>,
     access: AccessDepth,
     bias: PlaceConflictBias,
 ) -> bool {
@@ -62,8 +62,14 @@ pub(super) fn borrow_conflicts_with_place<'tcx>(
 
     // This Local/Local case is handled by the more general code below, but
     // it's so common that it's a speed win to check for it first.
-    if let Place::Base(PlaceBase::Local(l1)) = borrow_place {
-        if let Place::Base(PlaceBase::Local(l2)) = access_place {
+    if let Place {
+        base: PlaceBase::Local(l1),
+        projection: None,
+    } = borrow_place {
+        if let PlaceRef {
+            base: PlaceBase::Local(l2),
+            projection: None,
+        } = access_place {
             return l1 == l2;
         }
     }
@@ -175,7 +181,7 @@ fn place_components_conflict<'tcx>(
                 // check whether the components being borrowed vs
                 // accessed are disjoint (as in the second example,
                 // but not the first).
-                match place_projection_conflict(tcx, body, borrow_c, access_c, bias) {
+                match place_projection_conflict(tcx, body, borrow_base, borrow_c, access_c, bias) {
                     Overlap::Arbitrary => {
                         // We have encountered different fields of potentially
                         // the same union - the borrow now partially overlaps.
@@ -214,7 +220,7 @@ fn place_components_conflict<'tcx>(
 
                 let base = &borrow_c.base;
                 let elem = &borrow_c.elem;
-                let base_ty = base.ty(body, tcx).ty;
+                let base_ty = Place::ty_from(borrow_base, base, body, tcx).ty;
 
                 match (elem, &base_ty.sty, access) {
                     (_, _, Shallow(Some(ArtificialField::ArrayLength)))
@@ -368,6 +374,7 @@ fn place_base_conflict<'tcx>(
 fn place_projection_conflict<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
+    pi1_base: &PlaceBase<'tcx>,
     pi1: &Projection<'tcx>,
     pi2: &Projection<'tcx>,
     bias: PlaceConflictBias,
@@ -384,7 +391,7 @@ fn place_projection_conflict<'tcx>(
                 debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
                 Overlap::EqualOrDisjoint
             } else {
-                let ty = pi1.base.ty(body, tcx).ty;
+                let ty = Place::ty_from(pi1_base, &pi1.base, body, tcx).ty;
                 match ty.sty {
                     ty::Adt(def, _) if def.is_union() => {
                         // Different fields of a union, we are basically stuck.
diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/src/librustc_mir/borrow_check/prefixes.rs
index 0cc1dfd4def..ecafd4eb115 100644
--- a/src/librustc_mir/borrow_check/prefixes.rs
+++ b/src/librustc_mir/borrow_check/prefixes.rs
@@ -11,26 +11,23 @@ use super::MirBorrowckCtxt;
 
 use rustc::hir;
 use rustc::ty::{self, TyCtxt};
-use rustc::mir::{Body, Place, PlaceBase, ProjectionElem};
+use rustc::mir::{Body, Place, PlaceBase, PlaceRef, ProjectionElem};
 
-pub trait IsPrefixOf<'tcx> {
-    fn is_prefix_of(&self, other: &Place<'tcx>) -> bool;
+pub trait IsPrefixOf<'cx, 'tcx> {
+    fn is_prefix_of(&self, other: PlaceRef<'cx, 'tcx>) -> bool;
 }
 
-impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> {
-    fn is_prefix_of(&self, other: &Place<'tcx>) -> bool {
-        let mut cursor = other;
+impl<'cx, 'tcx> IsPrefixOf<'cx, 'tcx> for PlaceRef<'cx, 'tcx> {
+    fn is_prefix_of(&self, other: PlaceRef<'cx, 'tcx>) -> bool {
+        let mut cursor = other.projection;
         loop {
-            if self == cursor {
-                return true;
+            if self.projection == cursor {
+                return self.base == other.base;
             }
 
-            match *cursor {
-                Place::Base(PlaceBase::Local(_)) |
-                Place::Base(PlaceBase::Static(_)) => return false,
-                Place::Projection(ref proj) => {
-                    cursor = &proj.base;
-                }
+            match cursor {
+                None => return false,
+                Some(proj) => cursor = &proj.base,
             }
         }
     }
@@ -40,7 +37,7 @@ pub(super) struct Prefixes<'cx, 'tcx> {
     body: &'cx Body<'tcx>,
     tcx: TyCtxt<'tcx>,
     kind: PrefixSet,
-    next: Option<&'cx Place<'tcx>>,
+    next: Option<(PlaceRef<'cx, 'tcx>)>,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -59,9 +56,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// Returns an iterator over the prefixes of `place`
     /// (inclusive) from longest to smallest, potentially
     /// terminating the iteration early based on `kind`.
-    pub(super) fn prefixes(&self, place: &'cx Place<'tcx>, kind: PrefixSet) -> Prefixes<'cx, 'tcx> {
+    pub(super) fn prefixes(
+        &self,
+        place_ref: PlaceRef<'cx, 'tcx>,
+        kind: PrefixSet,
+    ) -> Prefixes<'cx, 'tcx> {
         Prefixes {
-            next: Some(place),
+            next: Some(place_ref),
             kind,
             body: self.body,
             tcx: self.infcx.tcx,
@@ -70,7 +71,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 }
 
 impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
-    type Item = &'cx Place<'tcx>;
+    type Item = PlaceRef<'cx, 'tcx>;
     fn next(&mut self) -> Option<Self::Item> {
         let mut cursor = self.next?;
 
@@ -80,27 +81,42 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
         // downcasts here, but may return a base of a downcast).
 
         'cursor: loop {
-            let proj = match *cursor {
-                Place::Base(PlaceBase::Local(_)) | // search yielded this leaf
-                Place::Base(PlaceBase::Static(_)) => {
+            let proj = match &cursor {
+                PlaceRef {
+                    base: PlaceBase::Local(_),
+                    projection: None,
+                }
+                | // search yielded this leaf
+                PlaceRef {
+                    base: PlaceBase::Static(_),
+                    projection: None,
+                } => {
                     self.next = None;
                     return Some(cursor);
                 }
-
-                Place::Projection(ref proj) => proj,
+                PlaceRef {
+                    base: _,
+                    projection: Some(proj),
+                } => proj,
             };
 
             match proj.elem {
                 ProjectionElem::Field(_ /*field*/, _ /*ty*/) => {
-                        // FIXME: add union handling
-                    self.next = Some(&proj.base);
+                    // FIXME: add union handling
+                    self.next = Some(PlaceRef {
+                        base: cursor.base,
+                        projection: &proj.base,
+                    });
                     return Some(cursor);
                 }
                 ProjectionElem::Downcast(..) |
                 ProjectionElem::Subslice { .. } |
                 ProjectionElem::ConstantIndex { .. } |
                 ProjectionElem::Index(_) => {
-                    cursor = &proj.base;
+                    cursor = PlaceRef {
+                        base: cursor.base,
+                        projection: &proj.base,
+                    };
                     continue 'cursor;
                 }
                 ProjectionElem::Deref => {
@@ -121,7 +137,10 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
                 PrefixSet::All => {
                     // all prefixes: just blindly enqueue the base
                     // of the projection
-                    self.next = Some(&proj.base);
+                    self.next = Some(PlaceRef {
+                        base: cursor.base,
+                        projection: &proj.base,
+                    });
                     return Some(cursor);
                 }
                 PrefixSet::Supporting => {
@@ -134,7 +153,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
             // derefs, except we stop at the deref of a shared
             // reference.
 
-            let ty = proj.base.ty(self.body, self.tcx).ty;
+            let ty = Place::ty_from(cursor.base, &proj.base, self.body, self.tcx).ty;
             match ty.sty {
                 ty::RawPtr(_) |
                 ty::Ref(
@@ -152,12 +171,18 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
                     _, /*ty*/
                     hir::MutMutable,
                     ) => {
-                    self.next = Some(&proj.base);
+                    self.next = Some(PlaceRef {
+                        base: cursor.base,
+                        projection: &proj.base,
+                    });
                     return Some(cursor);
                 }
 
                 ty::Adt(..) if ty.is_box() => {
-                    self.next = Some(&proj.base);
+                    self.next = Some(PlaceRef {
+                        base: cursor.base,
+                        projection: &proj.base,
+                    });
                     return Some(cursor);
                 }
 
diff --git a/src/librustc_mir/borrow_check/used_muts.rs b/src/librustc_mir/borrow_check/used_muts.rs
index 9c5569011df..2587d14a73a 100644
--- a/src/librustc_mir/borrow_check/used_muts.rs
+++ b/src/librustc_mir/borrow_check/used_muts.rs
@@ -59,7 +59,7 @@ impl GatherUsedMutsVisitor<'_, '_, '_> {
         // be those that were never initialized - we will consider those as being used as
         // they will either have been removed by unreachable code optimizations; or linted
         // as unused variables.
-        if let Some(local) = into.base_local() {
+        if let PlaceBase::Local(local) = into.base {
             let _ = self.never_initialized_mut_locals.remove(&local);
         }
     }
@@ -90,7 +90,7 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc
     ) {
         match &statement.kind {
             StatementKind::Assign(into, _) => {
-                if let Some(local) = into.base_local() {
+                if let PlaceBase::Local(local) = into.base {
                     debug!(
                         "visit_statement: statement={:?} local={:?} \
                          never_initialized_mut_locals={:?}",
@@ -118,7 +118,10 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc
                     "assignment of {:?} to {:?}, adding {:?} to used mutable set",
                     path.place, local, path.place
                 );
-                if let Place::Base(PlaceBase::Local(user_local)) = path.place {
+                if let Place {
+                    base: PlaceBase::Local(user_local),
+                    projection: None,
+                } = path.place {
                     self.mbcx.used_mut.insert(user_local);
                 }
             }
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs
index 82accb47437..2d9e7ac75c7 100644
--- a/src/librustc_mir/build/expr/as_place.rs
+++ b/src/librustc_mir/build/expr/as_place.rs
@@ -123,10 +123,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 };
                 block.and(place)
             }
-            ExprKind::StaticRef { id } => block.and(Place::Base(PlaceBase::Static(Box::new(Static {
-                ty: expr.ty,
-                kind: StaticKind::Static(id),
-            })))),
+            ExprKind::StaticRef { id } => block.and(Place {
+                base: PlaceBase::Static(Box::new(Static {
+                    ty: expr.ty,
+                    kind: StaticKind::Static(id),
+                })),
+                projection: None,
+            }),
 
             ExprKind::PlaceTypeAscription { source, user_ty } => {
                 let place = unpack!(block = this.as_place(block, source));
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 56c518a6d57..851a6b0b07c 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -497,32 +497,48 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let arg_place = unpack!(block = this.as_place(block, arg));
 
         let mutability = match arg_place {
-            Place::Base(PlaceBase::Local(local)) => this.local_decls[local].mutability,
-            Place::Projection(box Projection {
-                base: Place::Base(PlaceBase::Local(local)),
-                elem: ProjectionElem::Deref,
-            }) => {
+            Place {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } => this.local_decls[local].mutability,
+            Place {
+                base: PlaceBase::Local(local),
+                projection: Some(box Projection {
+                    base: None,
+                    elem: ProjectionElem::Deref,
+                })
+            } => {
                 debug_assert!(
                     this.local_decls[local].is_ref_for_guard(),
                     "Unexpected capture place",
                 );
                 this.local_decls[local].mutability
             }
-            Place::Projection(box Projection {
+            Place {
+                ref base,
+                projection: Some(box Projection {
+                    base: ref base_proj,
+                    elem: ProjectionElem::Field(upvar_index, _),
+                }),
+            }
+            | Place {
                 ref base,
-                elem: ProjectionElem::Field(upvar_index, _),
-            })
-            | Place::Projection(box Projection {
-                base:
-                    Place::Projection(box Projection {
-                        ref base,
+                projection: Some(box Projection {
+                    base: Some(box Projection {
+                        base: ref base_proj,
                         elem: ProjectionElem::Field(upvar_index, _),
                     }),
-                elem: ProjectionElem::Deref,
-            }) => {
+                    elem: ProjectionElem::Deref,
+                }),
+            } => {
+                let place = PlaceRef {
+                    base,
+                    projection: base_proj,
+                };
+
                 // Not projected from the implicit `self` in a closure.
                 debug_assert!(
-                    match base.local_or_deref_local() {
+                    match place.local_or_deref_local() {
                         Some(local) => local == Local::new(1),
                         None => false,
                     },
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index e433da904a6..2815361a647 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -296,7 +296,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // Create a "fake" temporary variable so that we check that the
                 // value is Sized. Usually, this is caught in type checking, but
                 // in the case of box expr there is no such check.
-                if let Place::Projection(..) = destination {
+                if destination.projection.is_some() {
                     this.local_decls
                         .push(LocalDecl::new_temp(expr.ty, expr.span));
                 }
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index dafb8c5f8f7..b58cef9cce1 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -536,7 +536,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let var_ty = self.local_decls[local_id].ty;
         let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
         self.schedule_drop(span, region_scope, local_id, var_ty, DropKind::Storage);
-        Place::Base(PlaceBase::Local(local_id))
+        Place::from(local_id)
     }
 
     pub fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) {
@@ -937,11 +937,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             for Binding { source, .. }
                 in matched_candidates.iter().flat_map(|candidate| &candidate.bindings)
             {
-                let mut cursor = source;
-                while let Place::Projection(box Projection { base, elem }) = cursor {
+                let mut cursor = &source.projection;
+                while let Some(box Projection { base, elem }) = cursor {
                     cursor = base;
                     if let ProjectionElem::Deref = elem {
-                        fake_borrows.insert(cursor.clone());
+                        fake_borrows.insert(Place {
+                            base: source.base.clone(),
+                            projection: cursor.clone(),
+                        });
                         break;
                     }
                 }
@@ -1277,7 +1280,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         &mut self,
         fake_borrows: &'b FxHashSet<Place<'tcx>>,
         temp_span: Span,
-    ) -> Vec<(&'b Place<'tcx>, Local)> {
+    ) -> Vec<(PlaceRef<'b, 'tcx>, Local)> {
         let tcx = self.hir.tcx();
 
         debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
@@ -1287,18 +1290,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // Insert a Shallow borrow of the prefixes of any fake borrows.
         for place in fake_borrows
         {
-            let mut prefix_cursor = place;
-            while let Place::Projection(box Projection { base, elem }) = prefix_cursor {
+            let mut prefix_cursor = &place.projection;
+            while let Some(box Projection { base, elem }) = prefix_cursor {
                 if let ProjectionElem::Deref = elem {
                     // Insert a shallow borrow after a deref. For other
                     // projections the borrow of prefix_cursor will
                     // conflict with any mutation of base.
-                    all_fake_borrows.push(base);
+                    all_fake_borrows.push(PlaceRef {
+                        base: &place.base,
+                        projection: base,
+                    });
                 }
                 prefix_cursor = base;
             }
 
-            all_fake_borrows.push(place);
+            all_fake_borrows.push(place.as_place_ref());
         }
 
         // Deduplicate and ensure a deterministic order.
@@ -1308,7 +1314,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
 
         all_fake_borrows.into_iter().map(|matched_place| {
-            let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty;
+            let fake_borrow_deref_ty = Place::ty_from(
+                matched_place.base,
+                matched_place.projection,
+                &self.local_decls,
+                tcx,
+            )
+            .ty;
             let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
             let fake_borrow_temp = self.local_decls.push(
                 LocalDecl::new_temp(fake_borrow_ty, temp_span)
@@ -1339,7 +1351,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         &mut self,
         candidate: Candidate<'pat, 'tcx>,
         guard: Option<Guard<'tcx>>,
-        fake_borrows: &Vec<(&Place<'tcx>, Local)>,
+        fake_borrows: &Vec<(PlaceRef<'_, 'tcx>, Local)>,
         scrutinee_span: Span,
         region_scope: region::Scope,
     ) -> BasicBlock {
@@ -1470,16 +1482,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
             let re_erased = tcx.lifetimes.re_erased;
             let scrutinee_source_info = self.source_info(scrutinee_span);
-            for &(place, temp) in fake_borrows {
+            for (place, temp) in fake_borrows {
                 let borrow = Rvalue::Ref(
                     re_erased,
                     BorrowKind::Shallow,
-                    place.clone(),
+                    Place {
+                        base: place.base.clone(),
+                        projection: place.projection.clone(),
+                    },
                 );
                 self.cfg.push_assign(
                     block,
                     scrutinee_source_info,
-                    &Place::from(temp),
+                    &Place::from(*temp),
                     borrow,
                 );
             }
@@ -1549,7 +1564,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             // place they refer to can't be modified by the guard.
             for binding in by_value_bindings.clone() {
                 let local_id = self.var_local_id(binding.var_id, RefWithinGuard);
-                    let place = Place::from(local_id);
+                let place = Place::from(local_id);
                 self.cfg.push(
                     post_guard_block,
                     Statement {
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index a74d5d7ab2d..a04c041ca9d 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -851,8 +851,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             // If constants and statics, we don't generate StorageLive for this
             // temporary, so don't try to generate StorageDead for it either.
             _ if self.local_scope().is_none() => (),
-            Operand::Copy(Place::Base(PlaceBase::Local(cond_temp)))
-            | Operand::Move(Place::Base(PlaceBase::Local(cond_temp))) => {
+            Operand::Copy(Place {
+                base: PlaceBase::Local(cond_temp),
+                projection: None,
+            })
+            | Operand::Move(Place {
+                base: PlaceBase::Local(cond_temp),
+                projection: None,
+            }) => {
                 // Manually drop the condition on both branches.
                 let top_scope = self.scopes.scopes.last_mut().unwrap();
                 let top_drop_data = top_scope.drops.pop().unwrap();
diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs
index a73ec2ed8e0..b6dd544d395 100644
--- a/src/librustc_mir/dataflow/drop_flag_effects.rs
+++ b/src/librustc_mir/dataflow/drop_flag_effects.rs
@@ -14,8 +14,8 @@ pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
 {
     let mut next_child = move_data.move_paths[path].first_child;
     while let Some(child_index) = next_child {
-        match move_data.move_paths[child_index].place {
-            mir::Place::Projection(ref proj) => {
+        match move_data.move_paths[child_index].place.projection {
+            Some(ref proj) => {
                 if cond(proj) {
                     return Some(child_index)
                 }
@@ -171,7 +171,7 @@ pub(crate) fn drop_flag_effects_for_function_entry<'tcx, F>(
     let move_data = &ctxt.move_data;
     for arg in body.args_iter() {
         let place = mir::Place::from(arg);
-        let lookup_result = move_data.rev_lookup.find(&place);
+        let lookup_result = move_data.rev_lookup.find(place.as_place_ref());
         on_lookup_result_bits(tcx, body, move_data,
                               lookup_result,
                               |mpi| callback(mpi, DropFlagState::Present));
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index dcc6ba5ca05..10c3e52b525 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -194,7 +194,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
     ) {
         debug!("kill_borrows_on_place: place={:?}", place);
 
-        if let Some(local) = place.base_local() {
+        if let PlaceBase::Local(local) = place.base {
             let other_borrows_of_local = self
                 .borrow_set
                 .local_map
@@ -205,7 +205,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
             // If the borrowed place is a local with no projections, all other borrows of this
             // local must conflict. This is purely an optimization so we don't have to call
             // `places_conflict` for every borrow.
-            if let Place::Base(PlaceBase::Local(_)) = place {
+            if place.projection.is_none() {
                 trans.kill_all(other_borrows_of_local);
                 return;
             }
diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs
index 065cfe8a4e8..ade732bbb75 100644
--- a/src/librustc_mir/dataflow/impls/mod.rs
+++ b/src/librustc_mir/dataflow/impls/mod.rs
@@ -309,7 +309,7 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
         // when a call returns successfully, that means we need to set
         // the bits for that dest_place to 1 (initialized).
         on_lookup_result_bits(self.tcx, self.body, self.move_data(),
-                              self.move_data().rev_lookup.find(dest_place),
+                              self.move_data().rev_lookup.find(dest_place.as_place_ref()),
                               |mpi| { in_out.insert(mpi); });
     }
 }
@@ -367,7 +367,7 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
         // when a call returns successfully, that means we need to set
         // the bits for that dest_place to 0 (initialized).
         on_lookup_result_bits(self.tcx, self.body, self.move_data(),
-                              self.move_data().rev_lookup.find(dest_place),
+                              self.move_data().rev_lookup.find(dest_place.as_place_ref()),
                               |mpi| { in_out.remove(mpi); });
     }
 }
@@ -423,7 +423,7 @@ impl<'a, 'tcx> BitDenotation<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
         // when a call returns successfully, that means we need to set
         // the bits for that dest_place to 1 (initialized).
         on_lookup_result_bits(self.tcx, self.body, self.move_data(),
-                              self.move_data().rev_lookup.find(dest_place),
+                              self.move_data().rev_lookup.find(dest_place.as_place_ref()),
                               |mpi| { in_out.insert(mpi); });
     }
 }
diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs
index 7fa950cb98d..0e01701ea9e 100644
--- a/src/librustc_mir/dataflow/impls/storage_liveness.rs
+++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs
@@ -121,11 +121,15 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
             StatementKind::StorageDead(l) => sets.kill(l),
             StatementKind::Assign(ref place, _)
             | StatementKind::SetDiscriminant { ref place, .. } => {
-                place.base_local().map(|l| sets.gen(l));
+                if let PlaceBase::Local(local) = place.base {
+                    sets.gen(local);
+                }
             }
             StatementKind::InlineAsm(box InlineAsm { ref outputs, .. }) => {
                 for p in &**outputs {
-                    p.base_local().map(|l| sets.gen(l));
+                    if let PlaceBase::Local(local) = p.base {
+                        sets.gen(local);
+                    }
                 }
             }
             _ => (),
@@ -146,7 +150,9 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
         _dest_bb: mir::BasicBlock,
         dest_place: &mir::Place<'tcx>,
     ) {
-        dest_place.base_local().map(|l| in_out.insert(l));
+        if let PlaceBase::Local(local) = dest_place.base {
+            in_out.insert(local);
+        }
     }
 }
 
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index f05dfad3257..3bdd3e3da04 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -314,12 +314,12 @@ pub(crate) trait DataflowResultsConsumer<'a, 'tcx: 'a> {
 
     fn visit_statement_entry(&mut self,
                              _loc: Location,
-                             _stmt: &Statement<'tcx>,
+                             _stmt: &'a Statement<'tcx>,
                              _flow_state: &Self::FlowState) {}
 
     fn visit_terminator_entry(&mut self,
                               _loc: Location,
-                              _term: &Terminator<'tcx>,
+                              _term: &'a Terminator<'tcx>,
                               _flow_state: &Self::FlowState) {}
 
     // Main entry point: this drives the processing of results.
diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
index f282c276e09..436ac30ffb4 100644
--- a/src/librustc_mir/dataflow/move_paths/builder.rs
+++ b/src/librustc_mir/dataflow/move_paths/builder.rs
@@ -106,13 +106,16 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
             for proj in place_projection {
                 let body = self.builder.body;
                 let tcx = self.builder.tcx;
-                let place_ty = proj.base.ty(body, tcx).ty;
+                let place_ty = Place::ty_from(place_base, &proj.base, body, tcx).ty;
                 match place_ty.sty {
                     ty::Ref(..) | ty::RawPtr(..) =>
                         return Err(MoveError::cannot_move_out_of(
                             self.loc,
                             BorrowedContent {
-                                target_place: Place::Projection(Box::new(proj.clone())),
+                                target_place: Place {
+                                    base: place_base.clone(),
+                                    projection: Some(Box::new(proj.clone())),
+                                }
                             })),
                     ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() =>
                         return Err(MoveError::cannot_move_out_of(self.loc,
@@ -159,7 +162,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
                             &mut self.builder.data.path_map,
                             &mut self.builder.data.init_path_map,
                             Some(base),
-                            Place::Projection(Box::new(proj.clone())),
+                            Place {
+                                base: place_base.clone(),
+                                projection: Some(Box::new(proj.clone())),
+                            },
                         );
                         ent.insert(path);
                         path
@@ -268,9 +274,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
                     // move-path for the interior so it will be separate from
                     // the exterior.
                     self.create_move_path(&place.clone().deref());
-                    self.gather_init(place, InitKind::Shallow);
+                    self.gather_init(place.as_place_ref(), InitKind::Shallow);
                 } else {
-                    self.gather_init(place, InitKind::Deep);
+                    self.gather_init(place.as_place_ref(), InitKind::Deep);
                 }
                 self.gather_rvalue(rval);
             }
@@ -280,7 +286,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
             StatementKind::InlineAsm(ref asm) => {
                 for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) {
                     if !kind.is_indirect {
-                        self.gather_init(output, InitKind::Deep);
+                        self.gather_init(output.as_place_ref(), InitKind::Deep);
                     }
                 }
                 for (_, input) in asm.inputs.iter() {
@@ -370,7 +376,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
             TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
                 self.create_move_path(location);
                 self.gather_operand(value);
-                self.gather_init(location, InitKind::Deep);
+                self.gather_init(location.as_place_ref(), InitKind::Deep);
             }
             TerminatorKind::Call {
                 ref func,
@@ -385,7 +391,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
                 }
                 if let Some((ref destination, _bb)) = *destination {
                     self.create_move_path(destination);
-                    self.gather_init(destination, InitKind::NonPanicPathOnly);
+                    self.gather_init(destination.as_place_ref(), InitKind::NonPanicPathOnly);
                 }
             }
         }
@@ -420,22 +426,24 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
         self.builder.data.loc_map[self.loc].push(move_out);
     }
 
-    fn gather_init(&mut self, place: &Place<'tcx>, kind: InitKind) {
+    fn gather_init(&mut self, place: PlaceRef<'cx, 'tcx>, kind: InitKind) {
         debug!("gather_init({:?}, {:?})", self.loc, place);
 
-        let place = match place {
-            // Check if we are assigning into a field of a union, if so, lookup the place
-            // of the union so it is marked as initialized again.
-            Place::Projection(box Projection {
-                base,
-                elem: ProjectionElem::Field(_, _),
-            }) if match base.ty(self.builder.body, self.builder.tcx).ty.sty {
-                    ty::Adt(def, _) if def.is_union() => true,
-                    _ => false,
-            } => base,
-            // Otherwise, lookup the place.
-            _ => place,
-        };
+        let mut place = place;
+
+        // Check if we are assigning into a field of a union, if so, lookup the place
+        // of the union so it is marked as initialized again.
+        if let Some(box Projection { base: proj_base, elem: ProjectionElem::Field(_, _) }) =
+            place.projection
+        {
+            if let ty::Adt(def, _) =
+                Place::ty_from(place.base, proj_base, self.builder.body, self.builder.tcx).ty.sty
+            {
+                if def.is_union() {
+                    place = PlaceRef { base: place.base, projection: proj_base }
+                }
+            }
+        }
 
         if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) {
             let init = self.builder.data.inits.push(Init {
diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs
index 938450c63ae..5c2255882b2 100644
--- a/src/librustc_mir/dataflow/move_paths/mod.rs
+++ b/src/librustc_mir/dataflow/move_paths/mod.rs
@@ -240,8 +240,8 @@ impl MovePathLookup {
     // alternative will *not* create a MovePath on the fly for an
     // unknown place, but will rather return the nearest available
     // parent.
-    pub fn find(&self, place: &Place<'tcx>) -> LookupResult {
-        place.iterate(|place_base, place_projection| {
+    pub fn find(&self, place_ref: PlaceRef<'cx, 'tcx>) -> LookupResult {
+        place_ref.iterate(|place_base, place_projection| {
             let mut result = match place_base {
                 PlaceBase::Local(local) => self.locals[*local],
                 PlaceBase::Static(..) => return LookupResult::Parent(None),
@@ -318,7 +318,10 @@ impl<'tcx> MoveData<'tcx> {
     pub fn base_local(&self, mut mpi: MovePathIndex) -> Option<Local> {
         loop {
             let path = &self.move_paths[mpi];
-            if let Place::Base(PlaceBase::Local(l)) = path.place { return Some(l); }
+            if let Place {
+                base: PlaceBase::Local(l),
+                projection: None,
+            } = path.place { return Some(l); }
             if let Some(parent) = path.parent { mpi = parent; continue } else { return None }
         }
     }
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 3d97132e539..1816171d7b1 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -455,17 +455,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         mir_place: &mir::Place<'tcx>,
         layout: Option<TyLayout<'tcx>>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        use rustc::mir::Place;
         use rustc::mir::PlaceBase;
 
         mir_place.iterate(|place_base, place_projection| {
             let mut op = match place_base {
                 PlaceBase::Local(mir::RETURN_PLACE) => return err!(ReadFromReturnPointer),
                 PlaceBase::Local(local) => {
-                    // FIXME use place_projection.is_empty() when is available
                     // Do not use the layout passed in as argument if the base we are looking at
                     // here is not the entire place.
-                    let layout = if let Place::Base(_) = mir_place {
+                    // FIXME use place_projection.is_empty() when is available
+                    let layout = if mir_place.projection.is_none() {
                         layout
                     } else {
                         None
diff --git a/src/librustc_mir/transform/add_retag.rs b/src/librustc_mir/transform/add_retag.rs
index de5af0a46b5..887f93c6478 100644
--- a/src/librustc_mir/transform/add_retag.rs
+++ b/src/librustc_mir/transform/add_retag.rs
@@ -15,34 +15,30 @@ pub struct AddRetag;
 /// (Concurrent accesses by other threads are no problem as these are anyway non-atomic
 /// copies.  Data races are UB.)
 fn is_stable(
-    place: &Place<'_>,
+    place: PlaceRef<'_, '_>,
 ) -> bool {
-    use rustc::mir::Place::*;
-
-    match *place {
-        // Locals and statics have stable addresses, for sure
-        Base(PlaceBase::Local { .. }) |
-        Base(PlaceBase::Static { .. }) =>
-            true,
-        // Recurse for projections
-        Projection(ref proj) => {
-            match proj.elem {
-                // Which place this evaluates to can change with any memory write,
-                // so cannot assume this to be stable.
-                ProjectionElem::Deref =>
-                    false,
-                // Array indices are intersting, but MIR building generates a *fresh*
-                // temporary for every array access, so the index cannot be changed as
-                // a side-effect.
-                ProjectionElem::Index { .. } |
-                // The rest is completely boring, they just offset by a constant.
-                ProjectionElem::Field { .. } |
-                ProjectionElem::ConstantIndex { .. } |
-                ProjectionElem::Subslice { .. } |
-                ProjectionElem::Downcast { .. } =>
-                    is_stable(&proj.base),
-            }
+    if let Some(proj) = &place.projection {
+        match proj.elem {
+            // Which place this evaluates to can change with any memory write,
+            // so cannot assume this to be stable.
+            ProjectionElem::Deref =>
+                false,
+            // Array indices are intersting, but MIR building generates a *fresh*
+            // temporary for every array access, so the index cannot be changed as
+            // a side-effect.
+            ProjectionElem::Index { .. } |
+            // The rest is completely boring, they just offset by a constant.
+            ProjectionElem::Field { .. } |
+            ProjectionElem::ConstantIndex { .. } |
+            ProjectionElem::Subslice { .. } |
+            ProjectionElem::Downcast { .. } =>
+                is_stable(PlaceRef {
+                    base: place.base,
+                    projection: &proj.base,
+                }),
         }
+    } else {
+        true
     }
 }
 
@@ -83,7 +79,8 @@ impl MirPass for AddRetag {
         let needs_retag = |place: &Place<'tcx>| {
             // FIXME: Instead of giving up for unstable places, we should introduce
             // a temporary and retag on that.
-            is_stable(place) && may_have_reference(place.ty(&*local_decls, tcx).ty, tcx)
+            is_stable(place.as_place_ref())
+                && may_have_reference(place.ty(&*local_decls, tcx).ty, tcx)
         };
 
         // PART 1
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index 0c48531e345..d5c5267a119 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -248,8 +248,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                         }], &[]);
                     }
                 }
-                let is_borrow_of_interior_mut = context.is_borrow() && !proj.base
-                    .ty(self.body, self.tcx)
+                let is_borrow_of_interior_mut = context.is_borrow() &&
+                    !Place::ty_from(&place.base, &proj.base, self.body, self.tcx)
                     .ty
                     .is_freeze(self.tcx, self.param_env, self.source_info.span);
                 // prevent
@@ -264,15 +264,15 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
                     );
                 }
                 let old_source_info = self.source_info;
-                if let Place::Base(PlaceBase::Local(local)) = proj.base {
-                    if self.body.local_decls[local].internal {
+                if let (PlaceBase::Local(local), None) = (&place.base, &proj.base) {
+                    if self.body.local_decls[*local].internal {
                         // Internal locals are used in the `move_val_init` desugaring.
                         // We want to check unsafety against the source info of the
                         // desugaring, rather than the source info of the RHS.
-                        self.source_info = self.body.local_decls[local].source_info;
+                        self.source_info = self.body.local_decls[*local].source_info;
                     }
                 }
-                let base_ty = proj.base.ty(self.body, self.tcx).ty;
+                let base_ty = Place::ty_from(&place.base, &proj.base, self.body, self.tcx).ty;
                 match base_ty.sty {
                     ty::RawPtr(..) => {
                         self.require_unsafe("dereference of raw pointer",
@@ -404,15 +404,16 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
     }
     fn check_mut_borrowing_layout_constrained_field(
         &mut self,
-        mut place: &Place<'tcx>,
+        place: &Place<'tcx>,
         is_mut_use: bool,
     ) {
-        while let &Place::Projection(box Projection {
-            ref base, ref elem
-        }) = place {
-            match *elem {
+        let mut projection = &place.projection;
+        while let Some(proj) = projection {
+            match proj.elem {
                 ProjectionElem::Field(..) => {
-                    let ty = base.ty(&self.body.local_decls, self.tcx).ty;
+                    let ty =
+                        Place::ty_from(&place.base, &proj.base, &self.body.local_decls, self.tcx)
+                            .ty;
                     match ty.sty {
                         ty::Adt(def, _) => match self.tcx.layout_scalar_valid_range(def.did) {
                             (Bound::Unbounded, Bound::Unbounded) => {},
@@ -446,7 +447,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
                 }
                 _ => {}
             }
-            place = base;
+            projection = &proj.base;
         }
     }
 }
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 29480f88fce..72390228aa8 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -781,7 +781,10 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
                 .ty;
             if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
                 if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) {
-                    if let Place::Base(PlaceBase::Local(local)) = *place {
+                    if let Place {
+                        base: PlaceBase::Local(local),
+                        projection: None,
+                    } = *place {
                         trace!("checking whether {:?} can be stored to {:?}", value, local);
                         if self.can_const_prop[local] {
                             trace!("storing {:?} to {:?}", value, local);
@@ -821,11 +824,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
                         // doesn't use the invalid value
                         match cond {
                             Operand::Move(ref place) | Operand::Copy(ref place) => {
-                                let mut place = place;
-                                while let Place::Projection(ref proj) = *place {
-                                    place = &proj.base;
-                                }
-                                if let Place::Base(PlaceBase::Local(local)) = *place {
+                                if let PlaceBase::Local(local) = place.base {
                                     self.remove_const(local);
                                 }
                             },
diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs
index 88a46b1012b..7c9eeb5a577 100644
--- a/src/librustc_mir/transform/copy_prop.rs
+++ b/src/librustc_mir/transform/copy_prop.rs
@@ -94,7 +94,10 @@ impl MirPass for CopyPropagation {
                     // That use of the source must be an assignment.
                     match statement.kind {
                         StatementKind::Assign(
-                            Place::Base(PlaceBase::Local(local)),
+                            Place {
+                                base: PlaceBase::Local(local),
+                                projection: None,
+                            },
                             box Rvalue::Use(ref operand)
                         ) if local == dest_local => {
                             let maybe_action = match *operand {
@@ -145,12 +148,24 @@ fn eliminate_self_assignments(
             if let Some(stmt) = body[location.block].statements.get(location.statement_index) {
                 match stmt.kind {
                     StatementKind::Assign(
-                        Place::Base(PlaceBase::Local(local)),
-                        box Rvalue::Use(Operand::Copy(Place::Base(PlaceBase::Local(src_local)))),
+                        Place {
+                            base: PlaceBase::Local(local),
+                            projection: None,
+                        },
+                        box Rvalue::Use(Operand::Copy(Place {
+                            base: PlaceBase::Local(src_local),
+                            projection: None,
+                        })),
                     ) |
                     StatementKind::Assign(
-                        Place::Base(PlaceBase::Local(local)),
-                        box Rvalue::Use(Operand::Move(Place::Base(PlaceBase::Local(src_local)))),
+                        Place {
+                            base: PlaceBase::Local(local),
+                            projection: None,
+                        },
+                        box Rvalue::Use(Operand::Move(Place {
+                            base: PlaceBase::Local(src_local),
+                            projection: None,
+                        })),
                     ) if local == dest_local && dest_local == src_local => {}
                     _ => {
                         continue;
@@ -177,7 +192,10 @@ impl<'tcx> Action<'tcx> {
     fn local_copy(body: &Body<'tcx>, def_use_analysis: &DefUseAnalysis, src_place: &Place<'tcx>)
                   -> Option<Action<'tcx>> {
         // The source must be a local.
-        let src_local = if let Place::Base(PlaceBase::Local(local)) = *src_place {
+        let src_local = if let Place {
+            base: PlaceBase::Local(local),
+            projection: None,
+        } = *src_place {
             local
         } else {
             debug!("  Can't copy-propagate local: source is not a local");
@@ -331,8 +349,14 @@ impl<'tcx> MutVisitor<'tcx> for ConstantPropagationVisitor<'tcx> {
         self.super_operand(operand, location);
 
         match *operand {
-            Operand::Copy(Place::Base(PlaceBase::Local(local))) |
-            Operand::Move(Place::Base(PlaceBase::Local(local))) if local == self.dest_local => {}
+            Operand::Copy(Place {
+                base: PlaceBase::Local(local),
+                projection: None,
+            }) |
+            Operand::Move(Place {
+                base: PlaceBase::Local(local),
+                projection: None,
+            }) if local == self.dest_local => {}
             _ => return,
         }
 
diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs
index ad19b974d7d..0748321f605 100644
--- a/src/librustc_mir/transform/elaborate_drops.rs
+++ b/src/librustc_mir/transform/elaborate_drops.rs
@@ -105,7 +105,7 @@ fn find_dead_unwinds<'tcx>(
             init_data.apply_location(tcx, body, env, loc);
         }
 
-        let path = match env.move_data.rev_lookup.find(location) {
+        let path = match env.move_data.rev_lookup.find(location.as_place_ref()) {
             LookupResult::Exact(e) => e,
             LookupResult::Parent(..) => {
                 debug!("find_dead_unwinds: has parent; skipping");
@@ -360,7 +360,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                 statement_index: data.statements.len()
             });
 
-            let path = self.move_data().rev_lookup.find(location);
+            let path = self.move_data().rev_lookup.find(location.as_place_ref());
             debug!("collect_drop_flags: {:?}, place {:?} ({:?})",
                    bb, location, path);
 
@@ -399,7 +399,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
             match terminator.kind {
                 TerminatorKind::Drop { ref location, target, unwind } => {
                     let init_data = self.initialization_data_at(loc);
-                    match self.move_data().rev_lookup.find(location) {
+                    match self.move_data().rev_lookup.find(location.as_place_ref()) {
                         LookupResult::Exact(path) => {
                             elaborate_drop(
                                 &mut Elaborator {
@@ -488,7 +488,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
             is_cleanup: false,
         });
 
-        match self.move_data().rev_lookup.find(location) {
+        match self.move_data().rev_lookup.find(location.as_place_ref()) {
             LookupResult::Exact(path) => {
                 debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path);
                 let init_data = self.initialization_data_at(loc);
@@ -558,7 +558,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                 assert!(!self.patch.is_patched(bb));
 
                 let loc = Location { block: tgt, statement_index: 0 };
-                let path = self.move_data().rev_lookup.find(place);
+                let path = self.move_data().rev_lookup.find(place.as_place_ref());
                 on_lookup_result_bits(
                     self.tcx, self.body, self.move_data(), path,
                     |child| self.set_drop_flag(loc, child, DropFlagState::Present)
@@ -632,7 +632,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                 assert!(!self.patch.is_patched(bb));
 
                 let loc = Location { block: bb, statement_index: data.statements.len() };
-                let path = self.move_data().rev_lookup.find(place);
+                let path = self.move_data().rev_lookup.find(place.as_place_ref());
                 on_lookup_result_bits(
                     self.tcx, self.body, self.move_data(), path,
                     |child| self.set_drop_flag(loc, child, DropFlagState::Present)
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 2ed3f7d5c26..af412edbdc2 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -104,11 +104,14 @@ impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor {
                     place: &mut Place<'tcx>,
                     context: PlaceContext,
                     location: Location) {
-        if place.base_local() == Some(self_arg()) {
-            replace_base(place, Place::Projection(Box::new(Projection {
-                base: Place::Base(PlaceBase::Local(self_arg())),
-                elem: ProjectionElem::Deref,
-            })));
+        if place.base == PlaceBase::Local(self_arg()) {
+            replace_base(place, Place {
+                base: PlaceBase::Local(self_arg()),
+                projection: Some(Box::new(Projection {
+                    base: None,
+                    elem: ProjectionElem::Deref,
+                })),
+            });
         } else {
             self.super_place(place, context, location);
         }
@@ -131,11 +134,14 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> {
                     place: &mut Place<'tcx>,
                     context: PlaceContext,
                     location: Location) {
-        if place.base_local() == Some(self_arg()) {
-            replace_base(place, Place::Projection(Box::new(Projection {
-                base: Place::Base(PlaceBase::Local(self_arg())),
-                elem: ProjectionElem::Field(Field::new(0), self.ref_gen_ty),
-            })));
+        if place.base == PlaceBase::Local(self_arg()) {
+            replace_base(place, Place {
+                base: PlaceBase::Local(self_arg()),
+                projection: Some(Box::new(Projection {
+                    base: None,
+                    elem: ProjectionElem::Field(Field::new(0), self.ref_gen_ty),
+                })),
+            });
         } else {
             self.super_place(place, context, location);
         }
@@ -143,11 +149,13 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> {
 }
 
 fn replace_base(place: &mut Place<'tcx>, new_base: Place<'tcx>) {
-    if let Place::Projection(proj) = place {
-        replace_base(&mut proj.base, new_base);
-    } else {
-        *place = new_base;
+    let mut projection = &mut place.projection;
+    while let Some(box proj) = projection {
+        projection = &mut proj.base;
     }
+
+    place.base = new_base.base;
+    *projection = new_base.projection;
 }
 
 fn self_arg() -> Local {
@@ -203,10 +211,13 @@ impl TransformVisitor<'tcx> {
         let self_place = Place::from(self_arg());
         let base = self_place.downcast_unnamed(variant_index);
         let field = Projection {
-            base: base,
+            base: base.projection,
             elem: ProjectionElem::Field(Field::new(idx), ty),
         };
-        Place::Projection(Box::new(field))
+        Place {
+            base: base.base,
+            projection: Some(Box::new(field)),
+        }
     }
 
     // Create a statement which changes the discriminant
@@ -245,7 +256,7 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> {
                     place: &mut Place<'tcx>,
                     context: PlaceContext,
                     location: Location) {
-        if let Some(l) = place.base_local() {
+        if let PlaceBase::Local(l) = place.base {
             // Replace an Local in the remap with a generator struct access
             if let Some(&(ty, variant_index, idx)) = self.remap.get(&l) {
                 replace_base(place, self.make_field(variant_index, idx, ty));
@@ -835,7 +846,10 @@ fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut
             &Terminator {
                 source_info,
                 kind: TerminatorKind::Drop {
-                    location: Place::Base(PlaceBase::Local(local)),
+                    location: Place {
+                        base: PlaceBase::Local(local),
+                        projection: None,
+                    },
                     target,
                     unwind
                 }
diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs
index 04ee14f5f59..343832fe4a7 100644
--- a/src/librustc_mir/transform/inline.rs
+++ b/src/librustc_mir/transform/inline.rs
@@ -603,7 +603,10 @@ impl Inliner<'tcx> {
         // FIXME: Analysis of the usage of the arguments to avoid
         // unnecessary temporaries.
 
-        if let Operand::Move(Place::Base(PlaceBase::Local(local))) = arg {
+        if let Operand::Move(Place {
+            base: PlaceBase::Local(local),
+            projection: None,
+        }) = arg {
             if caller_body.local_kind(local) == LocalKind::Temp {
                 // Reuse the operand if it's a temporary already
                 return local;
@@ -671,7 +674,10 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
                    _location: Location) {
         if *local == RETURN_PLACE {
             match self.destination {
-                Place::Base(PlaceBase::Local(l)) => {
+                Place {
+                    base: PlaceBase::Local(l),
+                    projection: None,
+                } => {
                     *local = l;
                     return;
                 },
@@ -692,13 +698,20 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
                     _location: Location) {
 
         match place {
-            Place::Base(PlaceBase::Local(RETURN_PLACE)) => {
+            Place {
+                base: PlaceBase::Local(RETURN_PLACE),
+                projection: None,
+            } => {
                 // Return pointer; update the place itself
                 *place = self.destination.clone();
             },
-            Place::Base(
-                PlaceBase::Static(box Static { kind: StaticKind::Promoted(promoted), .. })
-            ) => {
+            Place {
+                base: PlaceBase::Static(box Static {
+                    kind: StaticKind::Promoted(promoted),
+                    ..
+                }),
+                projection: None,
+            } => {
                 if let Some(p) = self.promoted_map.get(*promoted).cloned() {
                     *promoted = p;
                 }
diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs
index 40563ad4167..55429265036 100644
--- a/src/librustc_mir/transform/instcombine.rs
+++ b/src/librustc_mir/transform/instcombine.rs
@@ -41,10 +41,14 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
         if self.optimizations.and_stars.remove(&location) {
             debug!("replacing `&*`: {:?}", rvalue);
             let new_place = match *rvalue {
-                Rvalue::Ref(_, _, Place::Projection(ref mut projection)) => {
+                Rvalue::Ref(_, _, Place {
+                    ref mut base,
+                    projection: Some(ref mut projection),
+                }) => Place {
                     // Replace with dummy
-                    mem::replace(&mut projection.base, Place::Base(PlaceBase::Local(Local::new(0))))
-                }
+                    base: mem::replace(base, PlaceBase::Local(Local::new(0))),
+                    projection: projection.base.take(),
+                },
                 _ => bug!("Detected `&*` but didn't find `&*`!"),
             };
             *rvalue = Rvalue::Use(Operand::Copy(new_place))
@@ -78,9 +82,12 @@ impl OptimizationFinder<'b, 'tcx> {
 
 impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
-        if let Rvalue::Ref(_, _, Place::Projection(ref projection)) = *rvalue {
+        if let Rvalue::Ref(_, _, Place {
+            ref base,
+            projection: Some(ref projection),
+        }) = *rvalue {
             if let ProjectionElem::Deref = projection.elem {
-                if projection.base.ty(self.body, self.tcx).ty.is_region_ptr() {
+                if Place::ty_from(&base, &projection.base, self.body, self.tcx).ty.is_region_ptr() {
                     self.optimizations.and_stars.insert(location);
                 }
             }
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index 33eb4106d07..3090b63a7e9 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -300,9 +300,13 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             let mut promoted_place = |ty, span| {
                 promoted.span = span;
                 promoted.local_decls[RETURN_PLACE] = LocalDecl::new_return_place(ty, span);
-                Place::Base(
-                    PlaceBase::Static(box Static{ kind: StaticKind::Promoted(promoted_id), ty })
-                )
+                Place {
+                    base: PlaceBase::Static(box Static {
+                        kind: StaticKind::Promoted(promoted_id),
+                        ty
+                    }),
+                    projection: None,
+                }
             };
             let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
             match candidate {
@@ -310,17 +314,14 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                     let ref mut statement = blocks[loc.block].statements[loc.statement_index];
                     match statement.kind {
                         StatementKind::Assign(_, box Rvalue::Ref(_, _, ref mut place)) => {
-                            // Find the underlying local for this (necessarily interior) borrow.
-                            let mut place = place;
-                            while let Place::Projection(ref mut proj) = *place {
-                                assert_ne!(proj.elem, ProjectionElem::Deref);
-                                place = &mut proj.base;
-                            };
-
-                            let ty = place.ty(local_decls, self.tcx).ty;
+                            // Use the underlying local for this (necessarily interior) borrow.
+                            let ty = place.base.ty(local_decls).ty;
                             let span = statement.source_info.span;
 
-                            Operand::Move(mem::replace(place, promoted_place(ty, span)))
+                            Operand::Move(Place {
+                                base: mem::replace(&mut place.base, promoted_place(ty, span).base),
+                                projection: None,
+                            })
                         }
                         _ => bug!()
                     }
@@ -397,7 +398,10 @@ pub fn promote_candidates<'tcx>(
             Candidate::Repeat(Location { block, statement_index }) |
             Candidate::Ref(Location { block, statement_index }) => {
                 match body[block].statements[statement_index].kind {
-                    StatementKind::Assign(Place::Base(PlaceBase::Local(local)), _) => {
+                    StatementKind::Assign(Place {
+                        base: PlaceBase::Local(local),
+                        projection: None,
+                    }, _) => {
                         if temps[local] == TempState::PromotedOut {
                             // Already promoted.
                             continue;
@@ -444,7 +448,10 @@ pub fn promote_candidates<'tcx>(
     for block in body.basic_blocks_mut() {
         block.statements.retain(|statement| {
             match statement.kind {
-                StatementKind::Assign(Place::Base(PlaceBase::Local(index)), _) |
+                StatementKind::Assign(Place {
+                    base: PlaceBase::Local(index),
+                    projection: None,
+                }, _) |
                 StatementKind::StorageLive(index) |
                 StatementKind::StorageDead(index) => {
                     !promoted(index)
@@ -454,7 +461,10 @@ pub fn promote_candidates<'tcx>(
         });
         let terminator = block.terminator_mut();
         match terminator.kind {
-            TerminatorKind::Drop { location: Place::Base(PlaceBase::Local(index)), target, .. } => {
+            TerminatorKind::Drop { location: Place {
+                base: PlaceBase::Local(index),
+                projection: None,
+            }, target, .. } => {
                 if promoted(index) {
                     terminator.kind = TerminatorKind::Goto {
                         target,
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 4308af7c5ad..739e2172b03 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -182,12 +182,16 @@ trait Qualif {
 
     fn in_projection_structurally(
         cx: &ConstCx<'_, 'tcx>,
+        base: &PlaceBase<'tcx>,
         proj: &Projection<'tcx>,
     ) -> bool {
-        let base_qualif = Self::in_place(cx, &proj.base);
+        let base_qualif = Self::in_place(cx, PlaceRef {
+            base,
+            projection: &proj.base,
+        });
         let qualif = base_qualif && Self::mask_for_ty(
             cx,
-            proj.base.ty(cx.body, cx.tcx)
+            Place::ty_from(&base, &proj.base, cx.body, cx.tcx)
                 .projection_ty(cx.tcx, &proj.elem)
                 .ty,
         );
@@ -202,26 +206,44 @@ trait Qualif {
         }
     }
 
-    fn in_projection(cx: &ConstCx<'_, 'tcx>, proj: &Projection<'tcx>) -> bool {
-        Self::in_projection_structurally(cx, proj)
+    fn in_projection(
+        cx: &ConstCx<'_, 'tcx>,
+        base: &PlaceBase<'tcx>,
+        proj: &Projection<'tcx>,
+    ) -> bool {
+        Self::in_projection_structurally(cx, base, proj)
     }
 
-    fn in_place(cx: &ConstCx<'_, 'tcx>, place: &Place<'tcx>) -> bool {
-        match *place {
-            Place::Base(PlaceBase::Local(local)) => Self::in_local(cx, local),
-            Place::Base(PlaceBase::Static(box Static {kind: StaticKind::Promoted(_), .. })) =>
-                bug!("qualifying already promoted MIR"),
-            Place::Base(PlaceBase::Static(ref static_)) => {
+    fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool {
+        match place {
+            PlaceRef {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } => Self::in_local(cx, *local),
+            PlaceRef {
+                base: PlaceBase::Static(box Static {
+                    kind: StaticKind::Promoted(_),
+                    ..
+                }),
+                projection: None,
+            } => bug!("qualifying already promoted MIR"),
+            PlaceRef {
+                base: PlaceBase::Static(static_),
+                projection: None,
+            } => {
                 Self::in_static(cx, static_)
             },
-            Place::Projection(ref proj) => Self::in_projection(cx, proj),
+            PlaceRef {
+                base,
+                projection: Some(proj),
+            } => Self::in_projection(cx, base, proj),
         }
     }
 
     fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
         match *operand {
             Operand::Copy(ref place) |
-            Operand::Move(ref place) => Self::in_place(cx, place),
+            Operand::Move(ref place) => Self::in_place(cx, place.as_place_ref()),
 
             Operand::Constant(ref constant) => {
                 if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
@@ -250,7 +272,7 @@ trait Qualif {
             Rvalue::NullaryOp(..) => false,
 
             Rvalue::Discriminant(ref place) |
-            Rvalue::Len(ref place) => Self::in_place(cx, place),
+            Rvalue::Len(ref place) => Self::in_place(cx, place.as_place_ref()),
 
             Rvalue::Use(ref operand) |
             Rvalue::Repeat(ref operand, _) |
@@ -264,16 +286,19 @@ trait Qualif {
 
             Rvalue::Ref(_, _, ref place) => {
                 // Special-case reborrows to be more like a copy of the reference.
-                if let Place::Projection(ref proj) = *place {
+                if let Some(ref proj) = place.projection {
                     if let ProjectionElem::Deref = proj.elem {
-                        let base_ty = proj.base.ty(cx.body, cx.tcx).ty;
+                        let base_ty = Place::ty_from(&place.base, &proj.base, cx.body, cx.tcx).ty;
                         if let ty::Ref(..) = base_ty.sty {
-                            return Self::in_place(cx, &proj.base);
+                            return Self::in_place(cx, PlaceRef {
+                                base: &place.base,
+                                projection: &proj.base,
+                            });
                         }
                     }
                 }
 
-                Self::in_place(cx, place)
+                Self::in_place(cx, place.as_place_ref())
             }
 
             Rvalue::Aggregate(_, ref operands) => {
@@ -421,7 +446,11 @@ impl Qualif for IsNotPromotable {
         }
     }
 
-    fn in_projection(cx: &ConstCx<'_, 'tcx>, proj: &Projection<'tcx>) -> bool {
+    fn in_projection(
+        cx: &ConstCx<'_, 'tcx>,
+        base: &PlaceBase<'tcx>,
+        proj: &Projection<'tcx>,
+    ) -> bool {
         match proj.elem {
             ProjectionElem::Deref |
             ProjectionElem::Downcast(..) => return true,
@@ -432,7 +461,7 @@ impl Qualif for IsNotPromotable {
 
             ProjectionElem::Field(..) => {
                 if cx.mode == Mode::NonConstFn {
-                    let base_ty = proj.base.ty(cx.body, cx.tcx).ty;
+                    let base_ty = Place::ty_from(base, &proj.base, cx.body, cx.tcx).ty;
                     if let Some(def) = base_ty.ty_adt_def() {
                         // No promotion of union field accesses.
                         if def.is_union() {
@@ -443,7 +472,7 @@ impl Qualif for IsNotPromotable {
             }
         }
 
-        Self::in_projection_structurally(cx, proj)
+        Self::in_projection_structurally(cx, base, proj)
     }
 
     fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
@@ -773,20 +802,24 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                     // We might have a candidate for promotion.
                     let candidate = Candidate::Ref(location);
                     // Start by traversing to the "base", with non-deref projections removed.
-                    let mut place = place;
-                    while let Place::Projection(ref proj) = *place {
+                    let mut place_projection = &place.projection;
+                    while let Some(proj) = place_projection {
                         if proj.elem == ProjectionElem::Deref {
                             break;
                         }
-                        place = &proj.base;
+                        place_projection = &proj.base;
                     }
-                    debug!("qualify_consts: promotion candidate: place={:?}", place);
+
+                    debug!(
+                        "qualify_consts: promotion candidate: place={:?} {:?}",
+                        place.base, place_projection
+                    );
                     // We can only promote interior borrows of promotable temps (non-temps
                     // don't get promoted anyway).
                     // (If we bailed out of the loop due to a `Deref` above, we will definitely
                     // not enter the conditional here.)
-                    if let Place::Base(PlaceBase::Local(local)) = *place {
-                        if self.body.local_kind(local) == LocalKind::Temp {
+                    if let (PlaceBase::Local(local), None) = (&place.base, place_projection) {
+                        if self.body.local_kind(*local) == LocalKind::Temp {
                             debug!("qualify_consts: promotion candidate: local={:?}", local);
                             // The borrowed place doesn't have `HasMutInterior`
                             // (from `in_rvalue`), so we can safely ignore
@@ -794,7 +827,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                             // This allows borrowing fields which don't have
                             // `HasMutInterior`, from a type that does, e.g.:
                             // `let _: &'static _ = &(Cell::new(1), 2).1;`
-                            let mut local_qualifs = self.qualifs_in_local(local);
+                            let mut local_qualifs = self.qualifs_in_local(*local);
                             // Any qualifications, except HasMutInterior (see above), disqualify
                             // from promotion.
                             // This is, in particular, the "implicit promotion" version of
@@ -821,34 +854,31 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
             _ => {},
         }
 
-        let mut dest = dest;
+        let mut dest_projection = &dest.projection;
         let index = loop {
-            match dest {
+            match (&dest.base, dest_projection) {
                 // We treat all locals equal in constants
-                Place::Base(PlaceBase::Local(index)) => break *index,
+                (&PlaceBase::Local(index), None) => break index,
                 // projections are transparent for assignments
                 // we qualify the entire destination at once, even if just a field would have
                 // stricter qualification
-                Place::Projection(proj) => {
+                (base, Some(proj)) => {
                     // Catch more errors in the destination. `visit_place` also checks various
                     // projection rules like union field access and raw pointer deref
-                    self.visit_place(
-                        dest,
-                        PlaceContext::MutatingUse(MutatingUseContext::Store),
-                        location
-                    );
-                    dest = &proj.base;
+                    let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
+                    self.visit_place_base(base, context, location);
+                    self.visit_projection(base, proj, context, location);
+                    dest_projection = &proj.base;
                 },
-                Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) =>
-                    bug!("promoteds don't exist yet during promotion"),
-                Place::Base(PlaceBase::Static(box Static{ kind: _, .. })) => {
+                (&PlaceBase::Static(box Static {
+                    kind: StaticKind::Promoted(_),
+                    ..
+                }), None) => bug!("promoteds don't exist yet during promotion"),
+                (&PlaceBase::Static(box Static{ kind: _, .. }), None) => {
                     // Catch more errors in the destination. `visit_place` also checks that we
                     // do not try to access statics from constants or try to mutate statics
-                    self.visit_place(
-                        dest,
-                        PlaceContext::MutatingUse(MutatingUseContext::Store),
-                        location
-                    );
+                    let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
+                    self.visit_place_base(&dest.base, context, location);
                     return;
                 }
             }
@@ -950,7 +980,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
             match *candidate {
                 Candidate::Repeat(Location { block: bb, statement_index: stmt_idx }) => {
                     if let StatementKind::Assign(_, box Rvalue::Repeat(
-                        Operand::Move(Place::Base(PlaceBase::Local(index))),
+                        Operand::Move(Place {
+                            base: PlaceBase::Local(index),
+                            projection: None,
+                        }),
                         _
                     )) = self.body[bb].statements[stmt_idx].kind {
                         promoted_temps.insert(index);
@@ -959,7 +992,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
                     if let StatementKind::Assign(
                         _,
-                        box Rvalue::Ref(_, _, Place::Base(PlaceBase::Local(index)))
+                        box Rvalue::Ref(_, _, Place {
+                            base: PlaceBase::Local(index),
+                            projection: None,
+                        })
                     ) = self.body[bb].statements[stmt_idx].kind {
                         promoted_temps.insert(index);
                     }
@@ -1043,6 +1079,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
 
     fn visit_projection(
         &mut self,
+        place_base: &PlaceBase<'tcx>,
         proj: &Projection<'tcx>,
         context: PlaceContext,
         location: Location,
@@ -1051,14 +1088,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
             "visit_place_projection: proj={:?} context={:?} location={:?}",
             proj, context, location,
         );
-        self.super_projection(proj, context, location);
+        self.super_projection(place_base, proj, context, location);
         match proj.elem {
             ProjectionElem::Deref => {
                 if context.is_mutating_use() {
                     // `not_const` errors out in const contexts
                     self.not_const()
                 }
-                let base_ty = proj.base.ty(self.body, self.tcx).ty;
+                let base_ty = Place::ty_from(place_base, &proj.base, self.body, self.tcx).ty;
                 match self.mode {
                     Mode::NonConstFn => {},
                     _ => {
@@ -1082,7 +1119,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
             ProjectionElem::Subslice {..} |
             ProjectionElem::Field(..) |
             ProjectionElem::Index(_) => {
-                let base_ty = proj.base.ty(self.body, self.tcx).ty;
+                let base_ty = Place::ty_from(place_base, &proj.base, self.body, self.tcx).ty;
                 if let Some(def) = base_ty.ty_adt_def() {
                     if def.is_union() {
                         match self.mode {
@@ -1119,7 +1156,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
         match *operand {
             Operand::Move(ref place) => {
                 // Mark the consumed locals to indicate later drops are noops.
-                if let Place::Base(PlaceBase::Local(local)) = *place {
+                if let Place {
+                    base: PlaceBase::Local(local),
+                    projection: None,
+                } = *place {
                     self.cx.per_local[NeedsDrop].remove(local);
                 }
             }
@@ -1135,16 +1175,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
         if let Rvalue::Ref(_, kind, ref place) = *rvalue {
             // Special-case reborrows.
             let mut reborrow_place = None;
-            if let Place::Projection(ref proj) = *place {
+            if let Some(ref proj) = place.projection {
                 if let ProjectionElem::Deref = proj.elem {
-                    let base_ty = proj.base.ty(self.body, self.tcx).ty;
+                    let base_ty = Place::ty_from(&place.base, &proj.base, self.body, self.tcx).ty;
                     if let ty::Ref(..) = base_ty.sty {
                         reborrow_place = Some(&proj.base);
                     }
                 }
             }
 
-            if let Some(place) = reborrow_place {
+            if let Some(proj) = reborrow_place {
                 let ctx = match kind {
                     BorrowKind::Shared => PlaceContext::NonMutatingUse(
                         NonMutatingUseContext::SharedBorrow,
@@ -1159,7 +1199,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
                         MutatingUseContext::Borrow,
                     ),
                 };
-                self.visit_place(place, ctx, location);
+                self.visit_place_base(&place.base, ctx, location);
+                if let Some(proj) = proj {
+                    self.visit_projection(&place.base, proj, ctx, location);
+                }
             } else {
                 self.super_rvalue(rvalue, location);
             }
@@ -1428,7 +1471,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
                 unleash_miri!(self);
                 // HACK(eddyb): emulate a bit of dataflow analysis,
                 // conservatively, that drop elaboration will do.
-                let needs_drop = if let Place::Base(PlaceBase::Local(local)) = *place {
+                let needs_drop = if let Place {
+                    base: PlaceBase::Local(local),
+                    projection: None,
+                } = *place {
                     if NeedsDrop::in_local(self, local) {
                         Some(self.body.local_decls[local].source_info.span)
                     } else {
@@ -1658,7 +1704,10 @@ impl MirPass for QualifyAndPromoteConstants {
                 let terminator = block.terminator_mut();
                 match terminator.kind {
                     TerminatorKind::Drop {
-                        location: Place::Base(PlaceBase::Local(index)),
+                        location: Place {
+                            base: PlaceBase::Local(index),
+                            projection: None,
+                        },
                         target,
                         ..
                     } => {
diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs
index 7b3cdc835eb..adba9097d12 100644
--- a/src/librustc_mir/transform/remove_noop_landing_pads.rs
+++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs
@@ -41,7 +41,10 @@ impl RemoveNoopLandingPads {
                     // These are all nops in a landing pad
                 }
 
-                StatementKind::Assign(Place::Base(PlaceBase::Local(_)), box Rvalue::Use(_)) => {
+                StatementKind::Assign(Place {
+                    base: PlaceBase::Local(_),
+                    projection: None,
+                }, box Rvalue::Use(_)) => {
                     // Writing to a local (e.g., a drop flag) does not
                     // turn a landing pad to a non-nop
                 }
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index f12309c1d0a..1fd865c42fc 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -118,8 +118,14 @@ fn each_block<'tcx, O>(
     };
     assert!(args.len() == 1);
     let peek_arg_place = match args[0] {
-        mir::Operand::Copy(ref place @ mir::Place::Base(mir::PlaceBase::Local(_))) |
-        mir::Operand::Move(ref place @ mir::Place::Base(mir::PlaceBase::Local(_))) => Some(place),
+        mir::Operand::Copy(ref place @ mir::Place {
+            base: mir::PlaceBase::Local(_),
+            projection: None,
+        }) |
+        mir::Operand::Move(ref place @ mir::Place {
+            base: mir::PlaceBase::Local(_),
+            projection: None,
+        }) => Some(place),
         _ => None,
     };
 
@@ -162,7 +168,7 @@ fn each_block<'tcx, O>(
         if place == peek_arg_place {
             if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = **rvalue {
                 // Okay, our search is over.
-                match move_data.rev_lookup.find(peeking_at_place) {
+                match move_data.rev_lookup.find(peeking_at_place.as_place_ref()) {
                     LookupResult::Exact(peek_mpi) => {
                         let bit_state = on_entry.contains(peek_mpi);
                         debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
@@ -186,7 +192,7 @@ fn each_block<'tcx, O>(
             }
         }
 
-        let lhs_mpi = move_data.rev_lookup.find(place);
+        let lhs_mpi = move_data.rev_lookup.find(place.as_place_ref());
 
         debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
                place, lhs_mpi, stmt);
diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs
index 6878eceb2a5..6aceeebaea1 100644
--- a/src/librustc_mir/transform/uniform_array_move_out.rs
+++ b/src/librustc_mir/transform/uniform_array_move_out.rs
@@ -59,19 +59,27 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
                     rvalue: &Rvalue<'tcx>,
                     location: Location) {
         if let Rvalue::Use(Operand::Move(ref src_place)) = rvalue {
-            if let Place::Projection(ref proj) = *src_place {
+            if let Some(ref proj) = src_place.projection {
                 if let ProjectionElem::ConstantIndex{offset: _,
                                                      min_length: _,
                                                      from_end: false} = proj.elem {
                     // no need to transformation
                 } else {
-                    let place_ty = proj.base.ty(self.body, self.tcx).ty;
+                    let place_ty =
+                        Place::ty_from(&src_place.base, &proj.base, self.body, self.tcx).ty;
                     if let ty::Array(item_ty, const_size) = place_ty.sty {
                         if let Some(size) = const_size.assert_usize(self.tcx) {
                             assert!(size <= u32::max_value() as u64,
                                     "uniform array move out doesn't supported
                                      for array bigger then u32");
-                            self.uniform(location, dst_place, proj, item_ty, size as u32);
+                            self.uniform(
+                                location,
+                                dst_place,
+                                &src_place.base,
+                                proj,
+                                item_ty,
+                                size as u32,
+                            );
                         }
                     }
 
@@ -86,6 +94,7 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
     fn uniform(&mut self,
                location: Location,
                dst_place: &Place<'tcx>,
+               base: &PlaceBase<'tcx>,
                proj: &Projection<'tcx>,
                item_ty: &'tcx ty::TyS<'tcx>,
                size: u32) {
@@ -100,13 +109,20 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
                                           Place::from(temp),
                                           Rvalue::Use(
                                               Operand::Move(
-                                                  Place::Projection(box Projection{
-                                                      base: proj.base.clone(),
-                                                      elem: ProjectionElem::ConstantIndex{
-                                                          offset: i,
-                                                          min_length: size,
-                                                          from_end: false}
-                                                  }))));
+                                                  Place {
+                                                      base: base.clone(),
+                                                      projection: Some(box Projection {
+                                                          base: proj.base.clone(),
+                                                          elem: ProjectionElem::ConstantIndex {
+                                                              offset: i,
+                                                              min_length: size,
+                                                              from_end: false,
+                                                          }
+                                                      }),
+                                                  }
+                                              )
+                                          )
+                    );
                     temp
                 }).collect();
                 self.patch.add_assign(
@@ -130,12 +146,20 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
                                       dst_place.clone(),
                                       Rvalue::Use(
                                           Operand::Move(
-                                              Place::Projection(box Projection{
-                                                  base: proj.base.clone(),
-                                                  elem: ProjectionElem::ConstantIndex{
-                                                      offset: size - offset,
-                                                      min_length: size,
-                                                      from_end: false }}))));
+                                              Place {
+                                                  base: base.clone(),
+                                                  projection: Some(box Projection {
+                                                      base: proj.base.clone(),
+                                                      elem: ProjectionElem::ConstantIndex {
+                                                          offset: size - offset,
+                                                          min_length: size,
+                                                          from_end: false,
+                                                      },
+                                                  }),
+                                              }
+                                          )
+                                      )
+                );
             }
             _ => {}
         }
@@ -173,7 +197,10 @@ impl MirPass for RestoreSubsliceArrayMoveOut {
                 if let StatementKind::Assign(ref dst_place, ref rval) = statement.kind {
                     if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = **rval {
                         let items : Vec<_> = items.iter().map(|item| {
-                            if let Operand::Move(Place::Base(PlaceBase::Local(local))) = item {
+                            if let Operand::Move(Place {
+                                base: PlaceBase::Local(local),
+                                projection: None,
+                            }) = item {
                                 let local_use = &visitor.locals_use[*local];
                                 let opt_index_and_place =
                                     Self::try_get_item_source(local_use, body);
@@ -189,7 +216,8 @@ impl MirPass for RestoreSubsliceArrayMoveOut {
 
                         let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
                         let opt_size = opt_src_place.and_then(|src_place| {
-                            let src_ty = src_place.ty(body, tcx).ty;
+                            let src_ty =
+                                Place::ty_from(src_place.base, src_place.projection, body, tcx).ty;
                             if let ty::Array(_, ref size_o) = src_ty.sty {
                                 size_o.assert_usize(tcx)
                             } else {
@@ -210,7 +238,7 @@ impl RestoreSubsliceArrayMoveOut {
     // indices is an integer interval. If all checks pass do the replacent.
     // items are Vec<Option<LocalUse, index in source array, source place for init local>>
     fn check_and_patch<'tcx>(candidate: Location,
-                             items: &[Option<(&LocalUse, u32, &Place<'tcx>)>],
+                             items: &[Option<(&LocalUse, u32, PlaceRef<'_, 'tcx>)>],
                              opt_size: Option<u64>,
                              patch: &mut MirPatch<'tcx>,
                              dst_place: &Place<'tcx>) {
@@ -218,6 +246,7 @@ impl RestoreSubsliceArrayMoveOut {
 
         if opt_size.is_some() && items.iter().all(
             |l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) {
+            let src_place = opt_src_place.unwrap();
 
             let indices: Vec<_> = items.iter().map(|x| x.unwrap().1).collect();
             for i in 1..indices.len() {
@@ -241,25 +270,39 @@ impl RestoreSubsliceArrayMoveOut {
                              dst_place.clone(),
                              Rvalue::Use(
                                  Operand::Move(
-                                     Place::Projection(box Projection{
-                                         base: opt_src_place.unwrap().clone(),
-                                         elem: ProjectionElem::Subslice{
-                                             from: min, to: size - max - 1}}))));
+                                     Place {
+                                         base: src_place.base.clone(),
+                                         projection: Some(box Projection {
+                                             base: src_place.projection.clone(),
+                                             elem: ProjectionElem::Subslice{
+                                                 from: min, to: size - max - 1}})})));
         }
     }
 
     fn try_get_item_source<'a, 'tcx>(local_use: &LocalUse,
-                                     body: &'a Body<'tcx>) -> Option<(u32, &'a Place<'tcx>)> {
+                                     body: &'a Body<'tcx>) -> Option<(u32, PlaceRef<'a, 'tcx>)> {
         if let Some(location) = local_use.first_use {
             let block = &body[location.block];
             if block.statements.len() > location.statement_index {
                 let statement = &block.statements[location.statement_index];
                 if let StatementKind::Assign(
-                    Place::Base(PlaceBase::Local(_)),
-                    box Rvalue::Use(Operand::Move(Place::Projection(box Projection{
-                        ref base, elem: ProjectionElem::ConstantIndex{
-                            offset, min_length: _, from_end: false}})))) = statement.kind {
-                    return Some((offset, base))
+                    Place {
+                        base: PlaceBase::Local(_),
+                        projection: None,
+                    },
+                    box Rvalue::Use(Operand::Move(Place {
+                        base,
+                        projection: Some(box Projection {
+                            base: proj_base,
+                            elem: ProjectionElem::ConstantIndex {
+                                offset, min_length: _, from_end: false
+                            }
+                        }),
+                    }))) = &statement.kind {
+                    return Some((*offset, PlaceRef {
+                        base,
+                        projection: proj_base,
+                    }))
                 }
             }
         }
diff --git a/src/librustc_mir/util/alignment.rs b/src/librustc_mir/util/alignment.rs
index 6245d9c208b..b8ef77da02e 100644
--- a/src/librustc_mir/util/alignment.rs
+++ b/src/librustc_mir/util/alignment.rs
@@ -38,15 +38,14 @@ fn is_within_packed<'tcx, L>(tcx: TyCtxt<'tcx>, local_decls: &L, place: &Place<'
 where
     L: HasLocalDecls<'tcx>,
 {
-    let mut place = place;
-    while let &Place::Projection(box Projection {
-        ref base, ref elem
-    }) = place {
-        match *elem {
+    let mut place_projection = &place.projection;
+
+    while let Some(proj) = place_projection {
+        match proj.elem {
             // encountered a Deref, which is ABI-aligned
             ProjectionElem::Deref => break,
             ProjectionElem::Field(..) => {
-                let ty = base.ty(local_decls, tcx).ty;
+                let ty = Place::ty_from(&place.base, &proj.base, local_decls, tcx).ty;
                 match ty.sty {
                     ty::Adt(def, _) if def.repr.packed() => {
                         return true
@@ -56,7 +55,7 @@ where
             }
             _ => {}
         }
-        place = base;
+        place_projection = &proj.base;
     }
 
     false
diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs
index dac90d37275..61ad2ba8f57 100644
--- a/src/librustc_mir/util/elaborate_drops.rs
+++ b/src/librustc_mir/util/elaborate_drops.rs
@@ -584,10 +584,13 @@ where
             (Rvalue::Ref(
                 tcx.lifetimes.re_erased,
                 BorrowKind::Mut { allow_two_phase_borrow: false },
-                Place::Projection(Box::new(Projection {
-                    base: Place::Base(PlaceBase::Local(cur)),
-                    elem: ProjectionElem::Deref,
-                }))
+                Place {
+                    base: PlaceBase::Local(cur),
+                    projection: Some(Box::new(Projection {
+                        base: None,
+                        elem: ProjectionElem::Deref,
+                    })),
+                }
              ),
              Rvalue::BinaryOp(BinOp::Offset, move_(&Place::from(cur)), one))
         } else {