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};
/// `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.
fn visit_statement_entry(
&mut self,
location: Location,
- stmt: &Statement<'tcx>,
+ stmt: &'cx Statement<'tcx>,
flow_state: &Self::FlowState,
) {
debug!(
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
- (place, span),
+ (place.as_ref(), span),
flow_state,
);
}
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
- (output, o.span),
+ (output.as_ref(), o.span),
flow_state,
);
} else {
fn visit_terminator_entry(
&mut self,
location: Location,
- term: &Terminator<'tcx>,
+ term: &'cx Terminator<'tcx>,
flow_state: &Self::FlowState,
) {
let loc = location;
}
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,
}
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>,
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Update,
- place_span,
+ (place_span.0.as_ref(), place_span.1),
flow_state,
);
}
// 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,
);
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 {
self.check_if_path_or_subpath_is_moved(
location,
action,
- (place, span),
+ (place.as_ref(), span),
flow_state,
);
}
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
- (place, span),
+ (place.as_ref(), span),
flow_state,
);
}
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);
}
};
// 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;
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 {
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
- (place, span),
+ (place.as_ref(), span),
flow_state,
);
}
self.check_if_path_or_subpath_is_moved(
location,
InitializationRequiringAction::Use,
- (place, span),
+ (place.as_ref(), span),
flow_state,
);
}
) {
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 {
&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;
&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;
/// 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).
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>,
) {
// 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(
// 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
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;
}
}
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;
| 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() {
self.report_mutability_error(
place,
span,
- _place_err,
+ PlaceRef {
+ base: _place_err.0,
+ projection: _place_err.1,
+ },
error_access,
location,
);
self.report_mutability_error(
place,
span,
- the_place_err,
+ PlaceRef {
+ base: the_place_err.0,
+ projection: the_place_err.1,
+ },
error_access,
location,
);
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
}
}
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: _,
} => {}
}
/// 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 =>
{
_ => 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,
})
}
}
// `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),
| 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, _) => {
// });
// }
// ```
- 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)
}
}
}
/// 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,
}
}