about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSantiago Pastorino <spastorino@gmail.com>2019-04-30 18:58:24 +0200
committerSantiago Pastorino <spastorino@gmail.com>2019-07-20 05:08:38 +0200
commitd0accade3e094d3196dfdda94dee7d4a359fc130 (patch)
tree8b8a7183f3c6a4df716a5173f4407f0a351874c9 /src
parent5c26b523686497a46d10104c7295f366099a298c (diff)
downloadrust-d0accade3e094d3196dfdda94dee7d4a359fc130.tar.gz
rust-d0accade3e094d3196dfdda94dee7d4a359fc130.zip
Migrate from Place enum to Place struct
Diffstat (limited to 'src')
-rw-r--r--src/librustc/mir/mod.rs100
-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.rs25
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs34
-rw-r--r--src/librustc_codegen_ssa/mir/place.rs64
-rw-r--r--src/librustc_codegen_ssa/mir/rvalue.rs5
-rw-r--r--src/librustc_codegen_ssa/mir/statement.rs5
-rw-r--r--src/librustc_mir/borrow_check/borrow_set.rs7
-rw-r--r--src/librustc_mir/borrow_check/conflict_errors.rs147
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs172
-rw-r--r--src/librustc_mir/borrow_check/mod.rs413
-rw-r--r--src/librustc_mir/borrow_check/move_errors.rs49
-rw-r--r--src/librustc_mir/borrow_check/mutability_errors.rs248
-rw-r--r--src/librustc_mir/borrow_check/nll/constraint_generation.rs5
-rw-r--r--src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs64
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs50
-rw-r--r--src/librustc_mir/borrow_check/place_ext.rs2
-rw-r--r--src/librustc_mir/borrow_check/places_conflict.rs17
-rw-r--r--src/librustc_mir/borrow_check/prefixes.rs54
-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.rs32
-rw-r--r--src/librustc_mir/build/scope.rs10
-rw-r--r--src/librustc_mir/dataflow/drop_flag_effects.rs4
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs4
-rw-r--r--src/librustc_mir/dataflow/impls/storage_liveness.rs12
-rw-r--r--src/librustc_mir/dataflow/move_paths/builder.rs43
-rw-r--r--src/librustc_mir/dataflow/move_paths/mod.rs5
-rw-r--r--src/librustc_mir/interpret/operand.rs5
-rw-r--r--src/librustc_mir/transform/add_retag.rs46
-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/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.rs148
-rw-r--r--src/librustc_mir/transform/remove_noop_landing_pads.rs5
-rw-r--r--src/librustc_mir/transform/rustc_peek.rs10
-rw-r--r--src/librustc_mir/transform/uniform_array_move_out.rs108
-rw-r--r--src/librustc_mir/util/alignment.rs13
-rw-r--r--src/librustc_mir/util/elaborate_drops.rs11
46 files changed, 1461 insertions, 790 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 783964c701a..eff0b33fed5 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>,
 }
 
@@ -1827,7 +1827,10 @@ newtype_index! {
 }
 
 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 +1856,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 +1868,70 @@ 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())
+                }
 
-            Place::Base(base) => op(base, next.iter()),
+                Some(interior) => {
+                    iterate_over2(
+                        place_base,
+                        &interior.base,
+                        &Projections::List {
+                            projection: interior,
+                            next,
+                        },
+                        op,
+                    )
+                }
+            }
         }
+
+        iterate_over2(place_base, place_projection, &Projections::Empty, op)
     }
 }
 
 impl From<Local> for Place<'_> {
     fn from(local: Local) -> Self {
-        Place::Base(local.into())
+        Place {
+            base: local.into(),
+            projection: None,
+        }
     }
 }
 
@@ -3155,18 +3177,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..be02a2ac179 100644
--- a/src/librustc_codegen_ssa/mir/analyze.rs
+++ b/src/librustc_codegen_ssa/mir/analyze.rs
@@ -103,7 +103,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);
@@ -157,7 +160,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
         debug!("visit_place(place={:?}, context={:?})", place, context);
         let cx = self.fx.cx;
 
-        if let mir::Place::Projection(ref proj) = *place {
+        if let Some(proj) = &place.projection {
             // Allow uses of projections that are ZSTs or from scalar fields.
             let is_consume = match context {
                 PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
@@ -165,7 +168,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
                 _ => false
             };
             if is_consume {
-                let base_ty = proj.base.ty(self.fx.mir, cx.tcx());
+                let base_ty = mir::Place::ty_from(&place.base, &proj.base, self.fx.mir, cx.tcx());
                 let base_ty = self.fx.monomorphize(&base_ty);
 
                 // ZSTs don't require any actual memory access.
@@ -183,7 +186,15 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
                         // 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);
+                        self.visit_place(
+                            // FIXME do not clone
+                            &mir::Place {
+                                base: place.base.clone(),
+                                projection: proj.base.clone(),
+                            },
+                            context,
+                            location,
+                        );
                         return;
                     }
                 }
@@ -192,7 +203,11 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
             // A deref projection only reads the pointer, never needs the place.
             if let mir::ProjectionElem::Deref = proj.elem {
                 return self.visit_place(
-                    &proj.base,
+                    // FIXME do not clone
+                    &mir::Place {
+                        base: place.base.clone(),
+                        projection: proj.base.clone(),
+                    },
                     PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                     location
                 );
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index 941166ccfab..26f439ac38d 100644
--- a/src/librustc_codegen_ssa/mir/block.rs
+++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -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"),
@@ -1153,7 +1160,10 @@ 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"),
diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs
index 588badfa11a..da6c09d1d53 100644
--- a/src/librustc_codegen_ssa/mir/place.rs
+++ b/src/librustc_codegen_ssa/mir/place.rs
@@ -435,9 +435,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         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 {
+            mir::Place {
+                base: mir::PlaceBase::Local(index),
+                projection: None,
+            } => {
+                match self.locals[*index] {
                     LocalRef::Place(place) => {
                         return place;
                     }
@@ -449,15 +452,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     }
                 }
             }
-            mir::Place::Base(
-                mir::PlaceBase::Static(
-                    box mir::Static { ty, kind: mir::StaticKind::Promoted(promoted) }
-                )
-            ) => {
+            mir::Place {
+                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 +485,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::Place {
+                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::Place {
+                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::Place {
+                    base: base.clone(),
+                    projection: proj_base.clone(),
+                }).deref(bx.cx())
             }
-            mir::Place::Projection(ref projection) => {
-                let cg_base = self.codegen_place(bx, &projection.base);
+            mir::Place {
+                base,
+                projection: Some(projection),
+            } => {
+                // FIXME turn this recursion into iteration
+                let cg_base = self.codegen_place(bx, &mir::Place {
+                    base: base.clone(),
+                    projection: projection.base.clone(),
+                });
 
                 match projection.elem {
                     mir::ProjectionElem::Deref => bug!(),
diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs
index 4a1971e3e2e..6d26c35373b 100644
--- a/src/librustc_codegen_ssa/mir/rvalue.rs
+++ b/src/librustc_codegen_ssa/mir/rvalue.rs
@@ -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());
diff --git a/src/librustc_codegen_ssa/mir/statement.rs b/src/librustc_codegen_ssa/mir/statement.rs
index 750b2f5b1a5..0fbf6905c7a 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)
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..7bd3a4729ac 100644
--- a/src/librustc_mir/borrow_check/conflict_errors.rs
+++ b/src/librustc_mir/borrow_check/conflict_errors.rs
@@ -72,9 +72,15 @@ 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.base, &used_place.projection, PrefixSet::All)
+                .last()
+                .unwrap();
 
-            if self.uninitialized_error_reported.contains(root_place) {
+            if self.uninitialized_error_reported.contains(&Place {
+                base: root_place.0.clone(),
+                projection: root_place.1.clone(),
+            }) {
                 debug!(
                     "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
                     root_place
@@ -82,7 +88,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 return;
             }
 
-            self.uninitialized_error_reported.insert(root_place.clone());
+            self.uninitialized_error_reported.insert(Place {
+                base: root_place.0.clone(),
+                projection: root_place.1.clone(),
+            });
 
             let item_msg = match self.describe_place_with_options(used_place,
                                                                   IncludingDowncast(true)) {
@@ -105,8 +114,8 @@ 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)
-                    .any(|p| p == used_place)
+                if self.prefixes(&reported_place.base, &reported_place.projection, PrefixSet::All)
+                    .any(|p| *p.0 == used_place.base && *p.1 == used_place.projection)
                 {
                     debug!(
                         "report_use_of_moved_or_uninitialized place: error suppressed \
@@ -232,7 +241,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 {
@@ -575,8 +587,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 +607,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((Place {
+                                base: base.clone(),
+                                projection: base_proj.clone(),
+                            }, field));
                         },
-                        _ => current = base,
+                        _ => current = base_proj,
                     }
                 }
                 None
@@ -609,13 +630,27 @@ 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(&Place {
+                                    base: base.clone(),
+                                    projection: proj_base.clone(),
+                                }).unwrap_or_else(|| "_".to_owned());
+
                                 return Some((
-                                    describe_place(base),
+                                    describe_base_place,
                                     describe_place(first_borrowed_place),
                                     describe_place(second_borrowed_place),
                                     union_ty.to_string(),
@@ -624,7 +659,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         }
                     }
 
-                    current = base;
+                    current = proj_base;
                 }
                 None
             })
@@ -663,20 +698,26 @@ 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.base,
+                                       &borrow.borrowed_place.projection,
+                                       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.1.is_none());
+        let proper_span = match root_place.0 {
+            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.0.clone(),
+                projection: root_place.1.clone(),
+            }, borrow_span))
         {
             debug!(
                 "suppressing access_place error when borrow doesn't live long enough for {:?}",
@@ -686,7 +727,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         }
 
         self.access_place_error_reported
-            .insert((root_place.clone(), borrow_span));
+            .insert((Place {
+                base: root_place.0.clone(),
+                projection: root_place.1.clone(),
+            }, borrow_span));
 
         if let StorageDeadOrDrop::Destructor(dropped_ty) =
             self.classify_drop_access_kind(&borrow.borrowed_place)
@@ -709,7 +753,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
 
         let err = match (place_desc, explanation) {
-            (Some(_), _) if self.is_place_thread_local(root_place) => {
+            (Some(_), _) if self.is_place_thread_local(&Place {
+                base: root_place.0.clone(),
+                projection: root_place.1.clone(),
+            }) => {
                 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span)
             }
             // If the outlives constraint comes from inside the closure,
@@ -1061,7 +1108,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 +1133,17 @@ 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.base,
+                                           &borrow.borrowed_place.projection,
+                                           PrefixSet::All)
                 .last()
                 .unwrap();
-            let local = if let Place::Base(PlaceBase::Local(local)) = *root_place {
+            let local = if let (PlaceBase::Local(local), 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(),
@@ -1385,7 +1437,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 {
@@ -1456,19 +1511,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn classify_drop_access_kind(&self, place: &Place<'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(&Place {
+                    base: place.base.clone(),
+                    projection: base.clone(),
+                });
                 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 +1533,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 +1600,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 +1616,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 +1744,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..54f041865a1 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -150,16 +150,36 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         including_downcast: &IncludingDowncast,
     ) -> Result<(), ()> {
         match *place {
-            Place::Base(PlaceBase::Local(local)) => {
+            Place {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } => {
                 self.append_local_to_string(local, buf)?;
             }
-            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) => {
+            Place {
+                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), .. })) => {
+            Place {
+                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) => {
+            Place {
+                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,
+                                    &Place {
+                                        base: base.clone(),
+                                        projection: proj.base.clone(),
+                                    },
                                     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(
+                                                &Place {
+                                                    base: base.clone(),
+                                                    projection: proj.base.clone(),
+                                                },
+                                                buf,
+                                                autoderef,
+                                                &including_downcast,
+                                            )?;
+                                        } else {
+                                            // FIXME deduplicate this and the _ => body below
+                                            buf.push_str(&"*");
+                                            self.append_place_to_string(
+                                                &Place {
+                                                    base: base.clone(),
+                                                    projection: proj.base.clone(),
+                                                },
+                                                buf,
+                                                autoderef,
+                                                &including_downcast,
+                                            )?;
+                                        }
+                                    }
+
+                                    _ => {
+                                        buf.push_str(&"*");
+                                        self.append_place_to_string(
+                                            &Place {
+                                                base: base.clone(),
+                                                projection: proj.base.clone(),
+                                            },
+                                            buf,
+                                            autoderef,
+                                            &including_downcast,
+                                        )?;
+                                    }
+                                }
                             }
                         }
                     }
                     ProjectionElem::Downcast(..) => {
                         self.append_place_to_string(
-                            &proj.base,
+                            &Place {
+                                base: base.clone(),
+                                projection: proj.base.clone(),
+                            },
                             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(&Place {
+                                base: base.clone(),
+                                projection: proj.base.clone(),
+                            }, field);
                             self.append_place_to_string(
-                                &proj.base,
+                                &Place {
+                                    base: base.clone(),
+                                    projection: proj.base.clone(),
+                                },
                                 buf,
                                 autoderef,
                                 &including_downcast,
@@ -243,7 +292,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         autoderef = true;
 
                         self.append_place_to_string(
-                            &proj.base,
+                            &Place {
+                                base: base.clone(),
+                                projection: proj.base.clone(),
+                            },
                             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,
+                            &Place {
+                                base: base.clone(),
+                                projection: proj.base.clone(),
+                            },
                             buf,
                             autoderef,
                             &including_downcast,
@@ -288,18 +343,31 @@ 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: &Place<'tcx>, field: Field) -> String {
+        // FIXME Place2 Make this work iteratively
+        match place {
+            Place {
+                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_)) =>
+            Place {
+                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),
+            Place {
+                base,
+                projection: Some(proj),
+            } => match proj.elem {
+                ProjectionElem::Deref => self.describe_field(&Place {
+                    base: base.clone(),
+                    projection: proj.base.clone(),
+                }, field),
                 ProjectionElem::Downcast(_, variant_index) => {
-                    let base_ty = base.ty(self.body, self.infcx.tcx).ty;
+                    let base_ty = place.ty(self.body, self.infcx.tcx).ty;
                     self.describe_field_from_ty(&base_ty, field, Some(variant_index))
                 }
                 ProjectionElem::Field(_, field_type) => {
@@ -308,7 +376,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 ProjectionElem::Index(..)
                 | ProjectionElem::ConstantIndex { .. }
                 | ProjectionElem::Subslice { .. } => {
-                    self.describe_field(&proj.base, field)
+                    self.describe_field(&Place {
+                        base: base.clone(),
+                        projection: proj.base.clone(),
+                    }, field)
                 }
             },
         }
@@ -366,9 +437,13 @@ 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 {
+        if let Place {
+            base: PlaceBase::Static(box Static {
+                kind: StaticKind::Static(def_id),
+                ..
+            }),
+            projection: None,
+        } = place {
             let attrs = self.infcx.tcx.get_attrs(*def_id);
             let is_thread_local = attrs.iter().any(|attr| attr.check_name(sym::thread_local));
 
@@ -750,7 +825,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),
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 5851cd81788..cc2cd94b1d8 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -890,7 +890,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,
 }
 
@@ -1166,12 +1167,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,
                 );
@@ -1305,16 +1309,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) {
+                    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 +1322,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;
@@ -1434,30 +1438,39 @@ 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.base, &place.projection, 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.1.is_none());
+        let (might_be_alive, will_be_dropped) = match root_place.0 {
+            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(&Place {
+                    base: root_place.0.clone(),
+                    projection: root_place.1.clone(),
+                }))
             }
-            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 {
@@ -1475,7 +1488,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             self.body,
             place,
             borrow.kind,
-            root_place,
+            &Place {
+                base: root_place.0.clone(),
+                projection: root_place.1.clone(),
+            },
             sd,
             places_conflict::PlaceConflictBias::Overlap,
         ) {
@@ -1610,7 +1626,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     self.report_use_of_moved_or_uninitialized(
                         location,
                         desired_action,
-                        (prefix, place_span.0, place_span.1),
+                        (&prefix, place_span.0, place_span.1),
                         mpi,
                     );
                     return; // don't bother finding other problems.
@@ -1689,18 +1705,26 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn move_path_closest_to<'a>(
         &mut self,
         place: &'a Place<'tcx>,
-    ) -> Result<(&'a Place<'tcx>, MovePathIndex), NoMovePathFound> where 'cx: 'a {
-        let mut last_prefix = place;
-        for prefix in self.prefixes(place, PrefixSet::All) {
-            if let Some(mpi) = self.move_path_for_place(prefix) {
-                return Ok((prefix, mpi));
+    ) -> Result<(Place<'tcx>, MovePathIndex), NoMovePathFound> where 'cx: 'a {
+        let mut last_prefix = &place.base;
+
+        for prefix in self.prefixes(&place.base, &place.projection, PrefixSet::All) {
+            if let Some(mpi) = self.move_path_for_place(&Place {
+                base: prefix.0.clone(),
+                projection: prefix.1.clone(),
+            }) {
+                return Ok((Place {
+                    base: prefix.0.clone(),
+                    projection: prefix.1.clone(),
+                }, mpi));
             }
-            last_prefix = prefix;
+
+            last_prefix = prefix.0;
         }
-        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),
         }
     }
 
@@ -1723,83 +1747,86 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'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,
+                        (&Place {
+                            base: place.base.clone(),
+                            projection: base.clone(),
+                        }, 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,
+                                (&Place {
+                                    base: place.base.clone(),
+                                    projection: base.clone(),
+                                }, 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, &Place {
+                                base: place.base.clone(),
+                                projection: base.clone(),
+                            }, 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>(
@@ -1845,8 +1872,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             // Find the shortest uninitialized prefix you can reach
             // without going over a Deref.
             let mut shortest_uninit_seen = None;
-            for prefix in this.prefixes(base, PrefixSet::Shallow) {
-                let mpi = match this.move_path_for_place(prefix) {
+            for prefix in this.prefixes(&base.base, &base.projection, PrefixSet::Shallow) {
+                let mpi = match this.move_path_for_place(&Place {
+                    base: prefix.0.clone(),
+                    projection: prefix.1.clone(),
+                }) {
                     Some(mpi) => mpi, None => continue,
                 };
 
@@ -1881,7 +1911,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 this.report_use_of_moved_or_uninitialized(
                     location,
                     InitializationRequiringAction::PartialAssignment,
-                    (prefix, base, span),
+                    (&Place {
+                        base: prefix.0.clone(),
+                        projection: prefix.1.clone(),
+                    }, base, span),
                     mpi,
                 );
             }
@@ -1911,7 +1944,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 +1960,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 +1972,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 +1993,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 +2014,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         self.report_mutability_error(
                             place,
                             span,
-                            _place_err,
+                            &Place {
+                                base: _place_err.0.clone(),
+                                projection: _place_err.1.clone(),
+                            },
                             error_access,
                             location,
                         );
@@ -2015,7 +2051,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             self.report_mutability_error(
                 place,
                 span,
-                the_place_err,
+                &Place {
+                    base: the_place_err.0.clone(),
+                    projection: the_place_err.1.clone(),
+                },
                 error_access,
                 location,
             );
@@ -2044,7 +2083,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 +2097,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: Some(proj),
                 is_local_mutation_allowed: _,
             } => {
-                if let Some(field) = self.is_upvar_field_projection(place) {
+                if let Some(field) = self.is_upvar_field_projection(&Place {
+                    base: place_base.clone(),
+                    projection: Some(proj.clone()),
+                }) {
                     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 +2125,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(&Place {
+                                            base: place_base.clone(),
+                                            projection: place_projection.clone(),
+                                        }) {
                                             Some(field)
                                                 if self.upvars[field.index()].by_ref =>
                                             {
@@ -2143,19 +2205,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 +2226,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 +2239,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(&Place {
+                            base: place_base.clone(),
+                            projection: place_projection.clone(),
+                        });
                         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 +2283,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)
                         }
                     }
                 }
@@ -2237,32 +2307,33 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// 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;
+        let mut place_projection = place.projection.clone();
+        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.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..b58d0e62a99 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
@@ -273,21 +276,22 @@ 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 let Place::Base(_) = place {
+        let description = if place.projection.is_none() {
             format!("static item `{}`", self.describe_place(place).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 = Place {
+                base: place.base.clone(),
+                projection: base_static.clone(),
+            };
+
             format!(
                 "`{:?}` as `{:?}` is a static item",
                 self.describe_place(place).unwrap(),
-                self.describe_place(base_static).unwrap(),
+                self.describe_place(&base_static).unwrap(),
             )
         };
 
@@ -304,15 +308,24 @@ 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)
-            .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 upvar_field = self.prefixes(&move_place.base, &move_place.projection, PrefixSet::All)
+            .find_map(|p| self.is_upvar_field_projection(&Place {
+                base: p.0.clone(),
+                projection: p.1.clone(),
+            }));
+
+        let deref_base = match deref_target_place.projection {
+            Some(box Projection { ref base, elem: ProjectionElem::Deref }) => Place {
+                base: deref_target_place.base.clone(),
+                projection: base.clone(),
+            },
             _ => bug!("deref_target_place is not a deref projection"),
         };
 
-        if let Place::Base(PlaceBase::Local(local)) = *deref_base {
+        if let Place {
+            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(
@@ -378,7 +391,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 diag
             }
             _ => {
-                let source = self.borrowed_content_source(deref_base);
+                let source = self.borrowed_content_source(&deref_base);
                 match (self.describe_place(move_place), source.describe_for_named_place()) {
                     (Some(place_desc), Some(source_desc)) => {
                         self.cannot_move_out_of(
diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs
index 59a3354f9c5..bc6c1c93a30 100644
--- a/src/librustc_mir/borrow_check/mutability_errors.rs
+++ b/src/librustc_mir/borrow_check/mutability_errors.rs
@@ -44,9 +44,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
 
         match the_place_err {
-            Place::Base(PlaceBase::Local(local)) => {
+            Place {
+                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,12 +62,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Projection(box Projection {
-                base,
-                elem: ProjectionElem::Field(upvar_index, _),
-            }) => {
+            Place {
+                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());
@@ -73,11 +83,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Projection(box Projection {
-                base,
-                elem: ProjectionElem::Deref,
-            }) => {
-                if *base == Place::Base(PlaceBase::Local(Local::new(1))) &&
+            Place {
+                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());
@@ -91,8 +106,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         ", 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 +115,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(&Place {
+                        base: the_place_err.base.clone(),
+                        projection: base.clone(),
+                    });
                     let pointer_type = source.describe_for_immutable_place();
                     opt_source = Some(source);
                     if let Some(desc) = access_place_desc {
@@ -119,11 +137,27 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Promoted(_), .. })) =>
-                unreachable!(),
+            Place {
+                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 {
+            Place {
+                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 +167,36 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Projection(box Projection {
+            Place {
                 base: _,
-                elem: ProjectionElem::Index(_),
-            })
-            | Place::Projection(box Projection {
+                projection:
+                    Some(box Projection {
+                        base: _,
+                        elem: ProjectionElem::Index(_),
+                    }),
+            }
+            | Place {
                 base: _,
-                elem: ProjectionElem::ConstantIndex { .. },
-            })
-            | Place::Projection(box Projection {
+                projection:
+                    Some(box Projection {
+                        base: _,
+                        elem: ProjectionElem::ConstantIndex { .. },
+                    }),
+            }
+            | Place {
                 base: _,
-                elem: ProjectionElem::Subslice { .. },
-            })
-            | Place::Projection(box Projection {
+                projection: Some(box Projection {
+                    base: _,
+                    elem: ProjectionElem::Subslice { .. },
+                }),
+            }
+            | Place {
                 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);
@@ -203,21 +251,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,
+            Place {
+                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 +281,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
-                            }
+            Place {
+                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() => {
+            Place {
+                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 +338,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
 
             // Also suggest adding mut for upvars
-            Place::Projection(box Projection {
+            Place {
                 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 +374,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 {
+            Place {
+                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 +389,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 {
+            Place {
+                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 +416,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() =>
+            Place {
+                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 +499,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            Place::Projection(box Projection {
+            Place {
                 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 +516,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 );
             }
 
-            Place::Projection(box Projection {
+            Place {
                 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..d4ae15467be 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};
@@ -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
                             {
@@ -489,8 +492,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 +540,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 +567,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 +580,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 +599,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 +636,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 +650,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/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..8f7695b40e8 100644
--- a/src/librustc_mir/borrow_check/places_conflict.rs
+++ b/src/librustc_mir/borrow_check/places_conflict.rs
@@ -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 Place {
+            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..38be4ae4644 100644
--- a/src/librustc_mir/borrow_check/prefixes.rs
+++ b/src/librustc_mir/borrow_check/prefixes.rs
@@ -11,7 +11,7 @@ use super::MirBorrowckCtxt;
 
 use rustc::hir;
 use rustc::ty::{self, TyCtxt};
-use rustc::mir::{Body, Place, PlaceBase, ProjectionElem};
+use rustc::mir::{Body, Place, PlaceBase, Projection, ProjectionElem};
 
 pub trait IsPrefixOf<'tcx> {
     fn is_prefix_of(&self, other: &Place<'tcx>) -> bool;
@@ -19,18 +19,15 @@ pub trait IsPrefixOf<'tcx> {
 
 impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> {
     fn is_prefix_of(&self, other: &Place<'tcx>) -> bool {
-        let mut cursor = other;
+        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<(&'cx PlaceBase<'tcx>, &'cx Option<Box<Projection<'tcx>>>)>,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -59,9 +56,14 @@ 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_base: &'cx PlaceBase<'tcx>,
+        place_projection: &'cx Option<Box<Projection<'tcx>>>,
+        kind: PrefixSet,
+    ) -> Prefixes<'cx, 'tcx> {
         Prefixes {
-            next: Some(place),
+            next: Some((place_base, place_projection)),
             kind,
             body: self.body,
             tcx: self.infcx.tcx,
@@ -70,7 +72,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 }
 
 impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
-    type Item = &'cx Place<'tcx>;
+    type Item = (&'cx PlaceBase<'tcx>, &'cx Option<Box<Projection<'tcx>>>);
     fn next(&mut self) -> Option<Self::Item> {
         let mut cursor = self.next?;
 
@@ -80,27 +82,27 @@ 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 {
+                (&PlaceBase::Local(_), &None)
+                | // search yielded this leaf
+                (&PlaceBase::Static(_), &None) => {
                     self.next = None;
                     return Some(cursor);
                 }
-
-                Place::Projection(ref proj) => proj,
+                (_, &Some(ref 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((cursor.0, &proj.base));
                     return Some(cursor);
                 }
                 ProjectionElem::Downcast(..) |
                 ProjectionElem::Subslice { .. } |
                 ProjectionElem::ConstantIndex { .. } |
                 ProjectionElem::Index(_) => {
-                    cursor = &proj.base;
+                    cursor = (cursor.0, &proj.base);
                     continue 'cursor;
                 }
                 ProjectionElem::Deref => {
@@ -121,7 +123,7 @@ 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((cursor.0, &proj.base));
                     return Some(cursor);
                 }
                 PrefixSet::Supporting => {
@@ -134,7 +136,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.0, &proj.base, self.body, self.tcx).ty;
             match ty.sty {
                 ty::RawPtr(_) |
                 ty::Ref(
@@ -152,12 +154,12 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> {
                     _, /*ty*/
                     hir::MutMutable,
                     ) => {
-                    self.next = Some(&proj.base);
+                    self.next = Some((cursor.0, &proj.base));
                     return Some(cursor);
                 }
 
                 ty::Adt(..) if ty.is_box() => {
-                    self.next = Some(&proj.base);
+                    self.next = Some((cursor.0, &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..9b842c19999 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 = Place {
+                    base: base.clone(),
+                    projection: base_proj.clone(),
+                };
+
                 // 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..5e3acd9fa8e 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<(Place<'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(Place {
+                        base: place.base.clone(),
+                        projection: base.clone(),
+                    });
                 }
                 prefix_cursor = base;
             }
 
-            all_fake_borrows.push(place);
+            all_fake_borrows.push(place.clone());
         }
 
         // Deduplicate and ensure a deterministic order.
@@ -1339,7 +1345,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<(Place<'tcx>, Local)>,
         scrutinee_span: Span,
         region_scope: region::Scope,
     ) -> BasicBlock {
@@ -1470,7 +1476,7 @@ 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,
@@ -1479,7 +1485,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 self.cfg.push_assign(
                     block,
                     scrutinee_source_info,
-                    &Place::from(temp),
+                    &Place::from(*temp),
                     borrow,
                 );
             }
@@ -1549,7 +1555,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..51a0a10b33d 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)
                 }
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/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/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
index f282c276e09..8eff8b74e4f 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
@@ -423,21 +429,32 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
     fn gather_init(&mut self, place: &Place<'tcx>, kind: InitKind) {
         debug!("gather_init({:?}, {:?})", self.loc, place);
 
-        let place = match place {
+        let place = match place.projection {
             // 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,
+            Some(box Projection {
+                base: ref proj_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,
+            }) => {
+                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 { base: place.base.clone(), projection: proj_base.clone() }
+                    } else {
+                        place.clone()
+                    }
+                } else {
+                    place.clone()
+                }
+            }
+
+            _ => place.clone()
         };
 
-        if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) {
+        if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(&place) {
             let init = self.builder.data.inits.push(Init {
                 location: InitLocation::Statement(self.loc),
                 path,
diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs
index 938450c63ae..b30750850b5 100644
--- a/src/librustc_mir/dataflow/move_paths/mod.rs
+++ b/src/librustc_mir/dataflow/move_paths/mod.rs
@@ -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..0e534d9b757 100644
--- a/src/librustc_mir/transform/add_retag.rs
+++ b/src/librustc_mir/transform/add_retag.rs
@@ -17,32 +17,28 @@ pub struct AddRetag;
 fn is_stable(
     place: &Place<'_>,
 ) -> 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(&Place {
+                    base: place.base.clone(),
+                    projection: proj.base.clone(),
+                }),
         }
+    } else {
+        true
     }
 }
 
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/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..5223d721cca 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, &Place {
+            base: base.clone(),
+            projection: proj.base.clone(),
+        });
         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,19 +206,37 @@ 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_)) => {
+            Place {
+                base: PlaceBase::Local(local),
+                projection: None,
+            } => Self::in_local(cx, local),
+            Place {
+                base: PlaceBase::Static(box Static {
+                    kind: StaticKind::Promoted(_),
+                    ..
+                }),
+                projection: None,
+            } => bug!("qualifying already promoted MIR"),
+            Place {
+                base: PlaceBase::Static(ref static_),
+                projection: None,
+            } => {
                 Self::in_static(cx, static_)
             },
-            Place::Projection(ref proj) => Self::in_projection(cx, proj),
+            Place {
+                ref base,
+                projection: Some(ref proj),
+            } => Self::in_projection(cx, &base, proj),
         }
     }
 
@@ -264,11 +286,14 @@ 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, &Place {
+                                base: place.base.clone(),
+                                projection: proj.base.clone(),
+                            });
                         }
                     }
                 }
@@ -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,31 +854,39 @@ 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,
+                        &Place {
+                            base: base.clone(),
+                            projection: dest_projection.clone(),
+                        },
                         PlaceContext::MutatingUse(MutatingUseContext::Store),
                         location
                     );
-                    dest = &proj.base;
+                    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,
+                        &Place {
+                            base: dest.base.clone(),
+                            projection: dest_projection.clone(),
+                        },
                         PlaceContext::MutatingUse(MutatingUseContext::Store),
                         location
                     );
@@ -950,7 +991,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 +1003,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 +1090,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 +1099,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 +1130,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 +1167,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 +1186,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 +1210,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
                         MutatingUseContext::Borrow,
                     ),
                 };
-                self.visit_place(place, ctx, location);
+                self.visit_place(&Place {
+                    base: place.base.clone(),
+                    projection: proj.clone(),
+                }, ctx, location);
             } else {
                 self.super_rvalue(rvalue, location);
             }
@@ -1428,7 +1482,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 +1715,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..6def9dc2b5e 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,
     };
 
diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs
index 6878eceb2a5..887601338ff 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);
@@ -187,7 +214,7 @@ impl MirPass for RestoreSubsliceArrayMoveOut {
                             None
                         }).collect();
 
-                        let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
+                        let opt_src_place = items.first().and_then(|x| x.clone()).map(|x| x.2);
                         let opt_size = opt_src_place.and_then(|src_place| {
                             let src_ty = src_place.ty(body, tcx).ty;
                             if let ty::Array(_, ref size_o) = src_ty.sty {
@@ -210,16 +237,17 @@ 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, Place<'tcx>)>],
                              opt_size: Option<u64>,
                              patch: &mut MirPatch<'tcx>,
                              dst_place: &Place<'tcx>) {
-        let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
+        let opt_src_place = items.first().and_then(|x| x.clone()).map(|x| x.2);
 
         if opt_size.is_some() && items.iter().all(
-            |l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) {
+            |l| l.is_some() && l.clone().unwrap().2 == opt_src_place.clone().unwrap()) {
+            let src_place = opt_src_place.clone().unwrap();
 
-            let indices: Vec<_> = items.iter().map(|x| x.unwrap().1).collect();
+            let indices: Vec<_> = items.iter().map(|x| x.clone().unwrap().1).collect();
             for i in 1..indices.len() {
                 if indices[i - 1] + 1 != indices[i] {
                     return;
@@ -230,7 +258,7 @@ impl RestoreSubsliceArrayMoveOut {
             let max = *indices.last().unwrap();
 
             for item in items {
-                let locals_use = item.unwrap().0;
+                let locals_use = item.clone().unwrap().0;
                 patch.make_nop(locals_use.alive.unwrap());
                 patch.make_nop(locals_use.dead.unwrap());
                 patch.make_nop(locals_use.first_use.unwrap());
@@ -241,25 +269,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, Place<'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, Place {
+                        base: base.clone(),
+                        projection: proj_base.clone(),
+                    }))
                 }
             }
         }
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 {