]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/borrow_check/mod.rs
Place::as_place_ref is now Place::as_ref
[rust.git] / src / librustc_mir / borrow_check / mod.rs
index 25ac93cc2422c1559b4dcc551cf2beda2e321e42..5ec1e514100de1c60d9192b5ede6781a7f4fbdd4 100644 (file)
@@ -10,9 +10,8 @@
 use rustc::middle::borrowck::SignalledError;
 use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
 use rustc::mir::{
-    ClearCrossCrate, Local, Location, Body, Mutability, Operand, Place, PlaceBase, Static,
-
-    StaticKind
+    ClearCrossCrate, Local, Location, Body, Mutability, Operand, Place, PlaceBase, PlaceRef,
+    Static, StaticKind
 };
 use rustc::mir::{Field, Projection, ProjectionElem, Rvalue, Statement, StatementKind};
 use rustc::mir::{Terminator, TerminatorKind};
@@ -41,7 +40,6 @@
 use crate::dataflow::{do_dataflow, DebugFormatted};
 use crate::dataflow::EverInitializedPlaces;
 use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
-use crate::util::borrowck_errors::{BorrowckErrors, Origin};
 
 use self::borrow_set::{BorrowData, BorrowSet};
 use self::flows::Flows;
@@ -335,7 +333,7 @@ fn do_mir_borrowck<'a, 'tcx>(
             }
 
             let span = local_decl.source_info.span;
-            if span.compiler_desugaring_kind().is_some() {
+            if span.desugaring_kind().is_some() {
                 // If the `mut` arises as part of a desugaring, we should ignore it.
                 continue;
             }
@@ -422,8 +420,8 @@ fn downgrade_if_error(diag: &mut Diagnostic) {
     }
 }
 
-pub struct MirBorrowckCtxt<'cx, 'tcx> {
-    infcx: &'cx InferCtxt<'cx, 'tcx>,
+crate struct MirBorrowckCtxt<'cx, 'tcx> {
+    crate infcx: &'cx InferCtxt<'cx, 'tcx>,
     body: &'cx Body<'tcx>,
     mir_def_id: DefId,
     move_data: &'cx MoveData<'tcx>,
@@ -475,10 +473,10 @@ pub struct MirBorrowckCtxt<'cx, 'tcx> {
     /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
     /// when errors in the map are being re-added to the error buffer so that errors with the
     /// same primary span come out in a consistent order.
-    move_error_reported: BTreeMap<Vec<MoveOutIndex>, (Place<'tcx>, DiagnosticBuilder<'cx>)>,
+    move_error_reported: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'cx, 'tcx>, DiagnosticBuilder<'cx>)>,
     /// This field keeps track of errors reported in the checking of uninitialized variables,
     /// so that we don't report seemingly duplicate errors.
-    uninitialized_error_reported: FxHashSet<Place<'tcx>>,
+    uninitialized_error_reported: FxHashSet<PlaceRef<'cx, 'tcx>>,
     /// Errors to be reported buffer
     errors_buffer: Vec<Diagnostic>,
     /// This field keeps track of all the local variables that are declared mut and are mutated.
@@ -521,7 +519,7 @@ fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) {
     fn visit_statement_entry(
         &mut self,
         location: Location,
-        stmt: &Statement<'tcx>,
+        stmt: &'cx Statement<'tcx>,
         flow_state: &Self::FlowState,
     ) {
         debug!(
@@ -562,7 +560,7 @@ fn visit_statement_entry(
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Use,
-                    (place, span),
+                    (place.as_ref(), span),
                     flow_state,
                 );
             }
@@ -593,7 +591,7 @@ fn visit_statement_entry(
                         self.check_if_path_or_subpath_is_moved(
                             location,
                             InitializationRequiringAction::Use,
-                            (output, o.span),
+                            (output.as_ref(), o.span),
                             flow_state,
                         );
                     } else {
@@ -632,7 +630,7 @@ fn visit_statement_entry(
     fn visit_terminator_entry(
         &mut self,
         location: Location,
-        term: &Terminator<'tcx>,
+        term: &'cx Terminator<'tcx>,
         flow_state: &Self::FlowState,
     ) {
         let loc = location;
@@ -891,7 +889,8 @@ enum InitializationRequiringAction {
 }
 
 struct RootPlace<'d, 'tcx> {
-    place: &'d Place<'tcx>,
+    place_base: &'d PlaceBase<'tcx>,
+    place_projection: &'d Option<Box<Projection<'tcx>>>,
     is_local_mutation_allowed: LocalMutationIsAllowed,
 }
 
@@ -1144,7 +1143,7 @@ fn check_access_for_conflict(
     fn mutate_place(
         &mut self,
         location: Location,
-        place_span: (&Place<'tcx>, Span),
+        place_span: (&'cx Place<'tcx>, Span),
         kind: AccessDepth,
         mode: MutateMode,
         flow_state: &Flows<'cx, 'tcx>,
@@ -1155,7 +1154,7 @@ fn mutate_place(
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Update,
-                    place_span,
+                    (place_span.0.as_ref(), place_span.1),
                     flow_state,
                 );
             }
@@ -1167,12 +1166,15 @@ fn mutate_place(
         // 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,
                 );
@@ -1193,7 +1195,7 @@ fn mutate_place(
     fn consume_rvalue(
         &mut self,
         location: Location,
-        (rvalue, span): (&Rvalue<'tcx>, Span),
+        (rvalue, span): (&'cx Rvalue<'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         match *rvalue {
@@ -1230,7 +1232,7 @@ fn consume_rvalue(
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     action,
-                    (place, span),
+                    (place.as_ref(), span),
                     flow_state,
                 );
             }
@@ -1258,7 +1260,7 @@ fn consume_rvalue(
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Use,
-                    (place, span),
+                    (place.as_ref(), span),
                     flow_state,
                 );
             }
@@ -1306,16 +1308,12 @@ fn consume_rvalue(
 
     fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) {
         let propagate_closure_used_mut_place = |this: &mut Self, place: &Place<'tcx>| {
-            match *place {
-                Place::Projection { .. } => {
-                    if let Some(field) = this.is_upvar_field_projection(place) {
-                        this.used_mut_upvars.push(field);
-                    }
-                }
-                Place::Base(PlaceBase::Local(local)) => {
-                    this.used_mut.insert(local);
+            if place.projection.is_some() {
+                if let Some(field) = this.is_upvar_field_projection(place.as_ref()) {
+                    this.used_mut_upvars.push(field);
                 }
-                Place::Base(PlaceBase::Static(_)) => {}
+            } else if let PlaceBase::Local(local) = place.base {
+                this.used_mut.insert(local);
             }
         };
 
@@ -1323,10 +1321,14 @@ fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'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;
@@ -1380,7 +1382,7 @@ fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) {
     fn consume_operand(
         &mut self,
         location: Location,
-        (operand, span): (&Operand<'tcx>, Span),
+        (operand, span): (&'cx Operand<'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         match *operand {
@@ -1399,7 +1401,7 @@ fn consume_operand(
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Use,
-                    (place, span),
+                    (place.as_ref(), span),
                     flow_state,
                 );
             }
@@ -1417,7 +1419,7 @@ fn consume_operand(
                 self.check_if_path_or_subpath_is_moved(
                     location,
                     InitializationRequiringAction::Use,
-                    (place, span),
+                    (place.as_ref(), span),
                     flow_state,
                 );
             }
@@ -1435,30 +1437,35 @@ fn check_for_invalidation_at_exit(
     ) {
         debug!("check_for_invalidation_at_exit({:?})", borrow);
         let place = &borrow.borrowed_place;
-        let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
+        let root_place = self.prefixes(place.as_ref(), PrefixSet::All).last().unwrap();
 
         // FIXME(nll-rfc#40): do more precise destructor tracking here. For now
         // we just know that all locals are dropped at function exit (otherwise
         // we'll have a memory leak) and assume that all statics have a destructor.
         //
         // FIXME: allow thread-locals to borrow other thread locals?
-        let (might_be_alive, will_be_dropped) = match root_place {
-            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) => {
+
+        assert!(root_place.projection.is_none());
+        let (might_be_alive, will_be_dropped) = match root_place.base {
+            PlaceBase::Static(box Static {
+                kind: StaticKind::Promoted(_),
+                ..
+            }) => {
                 (true, false)
             }
-            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(_), .. })) => {
+            PlaceBase::Static(box Static {
+                kind: StaticKind::Static(_),
+                ..
+            }) => {
                 // Thread-locals might be dropped after the function exits, but
                 // "true" statics will never be.
-                (true, self.is_place_thread_local(&root_place))
+                (true, self.is_place_thread_local(root_place))
             }
-            Place::Base(PlaceBase::Local(_)) => {
+            PlaceBase::Local(_) => {
                 // Locals are always dropped at function exit, and if they
                 // have a destructor it would've been called already.
                 (false, self.locals_are_invalidated_at_exit)
             }
-            Place::Projection(..) => {
-                bug!("root of {:?} is a projection ({:?})?", place, root_place)
-            }
         };
 
         if !will_be_dropped {
@@ -1499,11 +1506,9 @@ fn check_for_local_borrow(&mut self, borrow: &BorrowData<'tcx>, yield_span: Span
         debug!("check_for_local_borrow({:?})", borrow);
 
         if borrow_of_local_data(&borrow.borrowed_place) {
-            let err = self.infcx.tcx
-                .cannot_borrow_across_generator_yield(
+            let err = self.cannot_borrow_across_generator_yield(
                     self.retrieve_borrow_spans(borrow).var_or_use(),
                     yield_span,
-                    Origin::Mir,
                 );
 
             err.buffer(&mut self.errors_buffer);
@@ -1566,7 +1571,7 @@ fn check_if_full_path_is_moved(
         &mut self,
         location: Location,
         desired_action: InitializationRequiringAction,
-        place_span: (&Place<'tcx>, Span),
+        place_span: (PlaceRef<'cx, 'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         let maybe_uninits = &flow_state.uninits;
@@ -1634,7 +1639,7 @@ fn check_if_path_or_subpath_is_moved(
         &mut self,
         location: Location,
         desired_action: InitializationRequiringAction,
-        place_span: (&Place<'tcx>, Span),
+        place_span: (PlaceRef<'cx, 'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         let maybe_uninits = &flow_state.uninits;
@@ -1689,25 +1694,27 @@ fn check_if_path_or_subpath_is_moved(
     /// An Err result includes a tag indicated why the search failed.
     /// Currently this can only occur if the place is built off of a
     /// static variable, as we do not track those in the MoveData.
-    fn move_path_closest_to<'a>(
+    fn move_path_closest_to(
         &mut self,
-        place: &'a Place<'tcx>,
-    ) -> Result<(&'a Place<'tcx>, MovePathIndex), NoMovePathFound> where 'cx: 'a {
-        let mut last_prefix = place;
+        place: PlaceRef<'cx, 'tcx>,
+    ) -> Result<(PlaceRef<'cx, 'tcx>, MovePathIndex), NoMovePathFound> {
+        let mut last_prefix = place.base;
+
         for prefix in self.prefixes(place, PrefixSet::All) {
             if let Some(mpi) = self.move_path_for_place(prefix) {
                 return Ok((prefix, mpi));
             }
-            last_prefix = prefix;
+
+            last_prefix = prefix.base;
         }
-        match *last_prefix {
-            Place::Base(PlaceBase::Local(_)) => panic!("should have move path for every Local"),
-            Place::Projection(_) => panic!("PrefixSet::All meant don't stop for Projection"),
-            Place::Base(PlaceBase::Static(_)) => Err(NoMovePathFound::ReachedStatic),
+
+        match last_prefix {
+            PlaceBase::Local(_) => panic!("should have move path for every Local"),
+            PlaceBase::Static(_) => Err(NoMovePathFound::ReachedStatic),
         }
     }
 
-    fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option<MovePathIndex> {
+    fn move_path_for_place(&mut self, place: PlaceRef<'cx, 'tcx>) -> Option<MovePathIndex> {
         // If returns None, then there is no move path corresponding
         // to a direct owner of `place` (which means there is nothing
         // that borrowck tracks for its analysis).
@@ -1721,94 +1728,97 @@ fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option<MovePathIndex>
     fn check_if_assigned_path_is_moved(
         &mut self,
         location: Location,
-        (place, span): (&Place<'tcx>, Span),
+        (place, span): (&'cx Place<'tcx>, Span),
         flow_state: &Flows<'cx, 'tcx>,
     ) {
         debug!("check_if_assigned_path_is_moved place: {:?}", place);
         // recur down place; dispatch to external checks when necessary
-        let mut place = place;
-        loop {
-            match *place {
-                Place::Base(PlaceBase::Local(_)) | Place::Base(PlaceBase::Static(_)) => {
-                    // assigning to `x` does not require `x` be initialized.
+        let mut place_projection = &place.projection;
+
+        // None case => assigning to `x` does not require `x` be initialized.
+        while let Some(proj) = place_projection {
+            let Projection { ref base, ref elem } = **proj;
+            match *elem {
+                ProjectionElem::Index(_/*operand*/) |
+                ProjectionElem::ConstantIndex { .. } |
+                // assigning to P[i] requires P to be valid.
+                ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
+                // assigning to (P->variant) is okay if assigning to `P` is okay
+                //
+                // FIXME: is this true even if P is a adt with a dtor?
+                { }
+
+                // assigning to (*P) requires P to be initialized
+                ProjectionElem::Deref => {
+                    self.check_if_full_path_is_moved(
+                        location, InitializationRequiringAction::Use,
+                        (PlaceRef {
+                            base: &place.base,
+                            projection: base,
+                        }, span), flow_state);
+                    // (base initialized; no need to
+                    // recur further)
                     break;
                 }
-                Place::Projection(ref proj) => {
-                    let Projection { ref base, ref elem } = **proj;
-                    match *elem {
-                        ProjectionElem::Index(_/*operand*/) |
-                        ProjectionElem::ConstantIndex { .. } |
-                        // assigning to P[i] requires P to be valid.
-                        ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
-                        // assigning to (P->variant) is okay if assigning to `P` is okay
-                        //
-                        // FIXME: is this true even if P is a adt with a dtor?
-                        { }
-
-                        // assigning to (*P) requires P to be initialized
-                        ProjectionElem::Deref => {
-                            self.check_if_full_path_is_moved(
-                                location, InitializationRequiringAction::Use,
-                                (base, span), flow_state);
+
+                ProjectionElem::Subslice { .. } => {
+                    panic!("we don't allow assignments to subslices, location: {:?}",
+                           location);
+                }
+
+                ProjectionElem::Field(..) => {
+                    // if type of `P` has a dtor, then
+                    // assigning to `P.f` requires `P` itself
+                    // be already initialized
+                    let tcx = self.infcx.tcx;
+                    let base_ty = Place::ty_from(&place.base, base, self.body, tcx).ty;
+                    match base_ty.sty {
+                        ty::Adt(def, _) if def.has_dtor(tcx) => {
+                            self.check_if_path_or_subpath_is_moved(
+                                location, InitializationRequiringAction::Assignment,
+                                (PlaceRef {
+                                    base: &place.base,
+                                    projection: base,
+                                }, span), flow_state);
+
                             // (base initialized; no need to
                             // recur further)
                             break;
                         }
 
-                        ProjectionElem::Subslice { .. } => {
-                            panic!("we don't allow assignments to subslices, location: {:?}",
-                                   location);
-                        }
-
-                        ProjectionElem::Field(..) => {
-                            // if type of `P` has a dtor, then
-                            // assigning to `P.f` requires `P` itself
-                            // be already initialized
-                            let tcx = self.infcx.tcx;
-                            match base.ty(self.body, tcx).ty.sty {
-                                ty::Adt(def, _) if def.has_dtor(tcx) => {
-                                    self.check_if_path_or_subpath_is_moved(
-                                        location, InitializationRequiringAction::Assignment,
-                                        (base, span), flow_state);
-
-                                    // (base initialized; no need to
-                                    // recur further)
-                                    break;
-                                }
-
-
-                                // Once `let s; s.x = V; read(s.x);`,
-                                // is allowed, remove this match arm.
-                                ty::Adt(..) | ty::Tuple(..) => {
-                                    check_parent_of_field(self, location, base, span, flow_state);
-
-                                    if let Some(local) = place.base_local() {
-                                        // rust-lang/rust#21232,
-                                        // #54499, #54986: during
-                                        // period where we reject
-                                        // partial initialization, do
-                                        // not complain about
-                                        // unnecessary `mut` on an
-                                        // attempt to do a partial
-                                        // initialization.
-                                        self.used_mut.insert(local);
-                                    }
-                                }
-
-                                _ => {}
+                        // Once `let s; s.x = V; read(s.x);`,
+                        // is allowed, remove this match arm.
+                        ty::Adt(..) | ty::Tuple(..) => {
+                            check_parent_of_field(self, location, PlaceRef {
+                                base: &place.base,
+                                projection: base,
+                            }, span, flow_state);
+
+                            if let PlaceBase::Local(local) = place.base {
+                                // rust-lang/rust#21232,
+                                // #54499, #54986: during
+                                // period where we reject
+                                // partial initialization, do
+                                // not complain about
+                                // unnecessary `mut` on an
+                                // attempt to do a partial
+                                // initialization.
+                                self.used_mut.insert(local);
                             }
                         }
-                    }
 
-                    place = base;
+                        _ => {}
+                    }
                 }
             }
+
+            place_projection = base;
         }
 
         fn check_parent_of_field<'cx, 'tcx>(
             this: &mut MirBorrowckCtxt<'cx, 'tcx>,
             location: Location,
-            base: &Place<'tcx>,
+            base: PlaceRef<'cx, 'tcx>,
             span: Span,
             flow_state: &Flows<'cx, 'tcx>,
         ) {
@@ -1869,7 +1879,9 @@ fn check_parent_of_field<'cx, 'tcx>(
                 // no move out from an earlier location) then this is an attempt at initialization
                 // of the union - we should error in that case.
                 let tcx = this.infcx.tcx;
-                if let ty::Adt(def, _) = base.ty(this.body, tcx).ty.sty {
+                if let ty::Adt(def, _) =
+                    Place::ty_from(base.base, base.projection, this.body, tcx).ty.sty
+                {
                     if def.is_union() {
                         if this.move_data.path_map[mpi].iter().any(|moi| {
                             this.move_data.moves[*moi].source.is_predecessor_of(
@@ -1914,7 +1926,7 @@ fn check_access_permissions(
         // 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
@@ -1930,7 +1942,7 @@ fn check_access_permissions(
                     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;
@@ -1942,7 +1954,7 @@ fn check_access_permissions(
                 }
             }
             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;
@@ -1963,7 +1975,7 @@ fn check_access_permissions(
             | 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() {
@@ -1984,7 +1996,10 @@ fn check_access_permissions(
                         self.report_mutability_error(
                             place,
                             span,
-                            _place_err,
+                            PlaceRef {
+                                base: _place_err.0,
+                                projection: _place_err.1,
+                            },
                             error_access,
                             location,
                         );
@@ -2018,7 +2033,10 @@ fn check_access_permissions(
             self.report_mutability_error(
                 place,
                 span,
-                the_place_err,
+                PlaceRef {
+                    base: the_place_err.0,
+                    projection: the_place_err.1,
+                },
                 error_access,
                 location,
             );
@@ -2047,7 +2065,8 @@ fn is_local_ever_initialized(
     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
@@ -2060,19 +2079,25 @@ fn add_used_mut<'d>(&mut self, root_place: RootPlace<'d, 'tcx>, flow_state: &Flo
                 }
             }
             RootPlace {
-                place: _,
+                place_base: _,
+                place_projection: _,
                 is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
             } => {}
             RootPlace {
-                place: place @ Place::Projection(_),
+                place_base,
+                place_projection: place_projection @ Some(_),
                 is_local_mutation_allowed: _,
             } => {
-                if let Some(field) = self.is_upvar_field_projection(place) {
+                if let Some(field) = self.is_upvar_field_projection(PlaceRef {
+                    base: &place_base,
+                    projection: &place_projection,
+                }) {
                     self.used_mut_upvars.push(field);
                 }
             }
             RootPlace {
-                place: Place::Base(PlaceBase::Static(..)),
+                place_base: PlaceBase::Static(..),
+                place_projection: None,
                 is_local_mutation_allowed: _,
             } => {}
         }
@@ -2082,62 +2107,78 @@ fn add_used_mut<'d>(&mut self, root_place: RootPlace<'d, 'tcx>, flow_state: &Flo
     /// Returns the root place if the place passed in is a projection.
     fn is_mutable<'d>(
         &self,
-        place: &'d Place<'tcx>,
+        place_base: &'d PlaceBase<'tcx>,
+        place_projection: &'d Option<Box<Projection<'tcx>>>,
         is_local_mutation_allowed: LocalMutationIsAllowed,
-    ) -> Result<RootPlace<'d, 'tcx>, &'d Place<'tcx>> {
-        match *place {
-            Place::Base(PlaceBase::Local(local)) => {
-                let local = &self.body.local_decls[local];
+    ) -> Result<RootPlace<'d, 'tcx>, (&'d PlaceBase<'tcx>, &'d Option<Box<Projection<'tcx>>>)> {
+        match (place_base, place_projection) {
+            (PlaceBase::Local(local), None) => {
+                let local = &self.body.local_decls[*local];
                 match local.mutability {
                     Mutability::Not => match is_local_mutation_allowed {
                         LocalMutationIsAllowed::Yes => Ok(RootPlace {
-                            place,
+                            place_base,
+                            place_projection,
                             is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
                         }),
                         LocalMutationIsAllowed::ExceptUpvars => Ok(RootPlace {
-                            place,
+                            place_base,
+                            place_projection,
                             is_local_mutation_allowed: LocalMutationIsAllowed::ExceptUpvars,
                         }),
-                        LocalMutationIsAllowed::No => Err(place),
+                        LocalMutationIsAllowed::No => Err((place_base, place_projection)),
                     },
                     Mutability::Mut => Ok(RootPlace {
-                        place,
+                        place_base,
+                        place_projection,
                         is_local_mutation_allowed,
                     }),
                 }
             }
             // The rules for promotion are made by `qualify_consts`, there wouldn't even be a
             // `Place::Promoted` if the promotion weren't 100% legal. So we just forward this
-            Place::Base(PlaceBase::Static(box Static{kind: StaticKind::Promoted(_), ..})) =>
+            (PlaceBase::Static(box Static {
+                kind: StaticKind::Promoted(_),
+                ..
+            }), None) =>
                 Ok(RootPlace {
-                    place,
+                    place_base,
+                    place_projection,
                     is_local_mutation_allowed,
                 }),
-            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })) => {
-                if !self.infcx.tcx.is_mutable_static(def_id) {
-                    Err(place)
+            (PlaceBase::Static(box Static {
+                kind: StaticKind::Static(def_id),
+                ..
+            }), None) => {
+                if !self.infcx.tcx.is_mutable_static(*def_id) {
+                    Err((place_base, place_projection))
                 } else {
                     Ok(RootPlace {
-                        place,
+                        place_base,
+                        place_projection,
                         is_local_mutation_allowed,
                     })
                 }
             }
-            Place::Projection(ref proj) => {
+            (_, Some(ref proj)) => {
                 match proj.elem {
                     ProjectionElem::Deref => {
-                        let base_ty = proj.base.ty(self.body, self.infcx.tcx).ty;
+                        let base_ty =
+                            Place::ty_from(place_base, &proj.base, self.body, self.infcx.tcx).ty;
 
                         // Check the kind of deref to decide
                         match base_ty.sty {
                             ty::Ref(_, _, mutbl) => {
                                 match mutbl {
                                     // Shared borrowed data is never mutable
-                                    hir::MutImmutable => Err(place),
+                                    hir::MutImmutable => Err((place_base, place_projection)),
                                     // Mutably borrowed data is mutable, but only if we have a
                                     // unique path to the `&mut`
                                     hir::MutMutable => {
-                                        let mode = match self.is_upvar_field_projection(place) {
+                                        let mode = match self.is_upvar_field_projection(PlaceRef {
+                                            base: &place_base,
+                                            projection: &place_projection,
+                                        }) {
                                             Some(field)
                                                 if self.upvars[field.index()].by_ref =>
                                             {
@@ -2146,19 +2187,20 @@ fn is_mutable<'d>(
                                             _ => 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,
                                         })
                                     }
@@ -2166,7 +2208,7 @@ fn is_mutable<'d>(
                             }
                             // `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),
@@ -2179,17 +2221,21 @@ fn is_mutable<'d>(
                     | ProjectionElem::ConstantIndex { .. }
                     | ProjectionElem::Subslice { .. }
                     | ProjectionElem::Downcast(..) => {
-                        let upvar_field_projection = self.is_upvar_field_projection(place);
+                        let upvar_field_projection = self.is_upvar_field_projection(PlaceRef {
+                            base: &place_base,
+                            projection: &place_projection,
+                        });
                         if let Some(field) = upvar_field_projection {
                             let upvar = &self.upvars[field.index()];
                             debug!(
-                                "upvar.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
-                                upvar, is_local_mutation_allowed, place
+                                "upvar.mutability={:?} local_mutation_is_allowed={:?} \
+                                place={:?} {:?}",
+                                upvar, is_local_mutation_allowed, place_base, place_projection
                             );
                             match (upvar.mutability, is_local_mutation_allowed) {
                                 (Mutability::Not, LocalMutationIsAllowed::No)
                                 | (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) => {
-                                    Err(place)
+                                    Err((place_base, place_projection))
                                 }
                                 (Mutability::Not, LocalMutationIsAllowed::Yes)
                                 | (Mutability::Mut, _) => {
@@ -2219,15 +2265,18 @@ fn is_mutable<'d>(
                                     //     });
                                     // }
                                     // ```
-                                    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)
                         }
                     }
                 }
@@ -2239,33 +2288,34 @@ fn is_mutable<'d>(
     /// then returns the index of the field being projected. Note that this closure will always
     /// be `self` in the current MIR, because that is the only time we directly access the fields
     /// of a closure type.
-    pub fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
-        let (place, by_ref) = if let Place::Projection(ref proj) = place {
-            if let ProjectionElem::Deref = proj.elem {
-                (&proj.base, true)
-            } else {
-                (place, false)
-            }
-        } else {
-            (place, false)
-        };
-
-        match place {
-            Place::Projection(ref proj) => match proj.elem {
-                ProjectionElem::Field(field, _ty) => {
-                    let tcx = self.infcx.tcx;
-                    let base_ty = proj.base.ty(self.body, tcx).ty;
+    pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'cx, 'tcx>) -> Option<Field> {
+        let mut place_projection = place_ref.projection;
+        let mut by_ref = false;
+
+        if let Some(box Projection {
+            base,
+            elem: ProjectionElem::Deref,
+        }) = place_projection {
+            place_projection = &base;
+            by_ref = true;
+        }
 
-                    if (base_ty.is_closure() || base_ty.is_generator()) &&
-                        (!by_ref || self.upvars[field.index()].by_ref)
-                    {
-                        Some(field)
-                    } else {
-                        None
-                    }
-                },
-                _ => None,
+        match place_projection {
+            Some(box Projection {
+                base,
+                elem: ProjectionElem::Field(field, _ty),
+            }) => {
+                let tcx = self.infcx.tcx;
+                let base_ty = Place::ty_from(place_ref.base, &base, self.body, tcx).ty;
+
+                if (base_ty.is_closure() || base_ty.is_generator()) &&
+                    (!by_ref || self.upvars[field.index()].by_ref) {
+                    Some(*field)
+                } else {
+                    None
+                }
             }
+
             _ => None,
         }
     }