use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
-use rustc_hir::{HirId, Node};
+use rustc_hir::Node;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
-use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::mir::{
traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
PlaceRef, VarDebugInfoContents,
use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt};
+use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
use rustc_span::{Span, Symbol, DUMMY_SP};
// FIXME(eddyb) perhaps move this somewhere more centrally.
#[derive(Debug)]
-crate struct Upvar {
+crate struct Upvar<'tcx> {
+ // FIXME(project-rfc_2229#36): print capture precisely here.
name: Symbol,
- // FIXME(project-rfc-2229#8): This should use Place or something similar
- var_hir_id: HirId,
+ place: CapturedPlace<'tcx>,
/// If true, the capture is behind a reference.
by_ref: bool,
-
- mutability: Mutability,
}
const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref];
let upvars: Vec<_> = tables
.closure_min_captures_flattened(def.did.to_def_id())
.map(|captured_place| {
- let var_hir_id = match captured_place.place.base {
- HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
- _ => bug!("Expected upvar"),
- };
+ let var_hir_id = captured_place.get_root_variable();
let capture = captured_place.info.capture_kind;
let by_ref = match capture {
ty::UpvarCapture::ByValue(_) => false,
ty::UpvarCapture::ByRef(..) => true,
};
- let mut upvar = Upvar {
- name: tcx.hir().name(var_hir_id),
- var_hir_id,
- by_ref,
- mutability: Mutability::Not,
- };
- let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode");
- if bm == ty::BindByValue(hir::Mutability::Mut) {
- upvar.mutability = Mutability::Mut;
- }
- upvar
+ Upvar { name: tcx.hir().name(var_hir_id), place: captured_place.clone(), by_ref }
})
.collect();
dominators: Dominators<BasicBlock>,
/// Information about upvars not necessarily preserved in types or MIR
- upvars: Vec<Upvar>,
+ upvars: Vec<Upvar<'tcx>>,
/// Names of local (user) variables (extracted from `var_debug_info`).
local_names: IndexVec<Local, Option<Symbol>>,
fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) {
let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| {
- if !place.projection.is_empty() {
- if let Some(field) = this.is_upvar_field_projection(place.as_ref()) {
+ // We have three possibilities here:
+ // a. We are modifying something through a mut-ref
+ // b. We are modifying something that is local to our parent
+ // c. Current body is a nested closure, and we are modifying path starting from
+ // a Place captured by our parent closure.
+
+ // Handle (c), the path being modified is exactly the path captured by our parent
+ if let Some(field) = this.is_upvar_field_projection(place.as_ref()) {
+ this.used_mut_upvars.push(field);
+ return;
+ }
+
+ for (place_ref, proj) in place.iter_projections().rev() {
+ // Handle (a)
+ if proj == ProjectionElem::Deref {
+ match place_ref.ty(this.body(), this.infcx.tcx).ty.kind() {
+ // We aren't modifying a variable directly
+ ty::Ref(_, _, hir::Mutability::Mut) => return,
+
+ _ => {}
+ }
+ }
+
+ // Handle (c)
+ if let Some(field) = this.is_upvar_field_projection(place_ref) {
this.used_mut_upvars.push(field);
+ return;
}
- } else {
- this.used_mut.insert(place.local);
}
+
+ // Handle(b)
+ this.used_mut.insert(place.local);
};
// This relies on the current way that by-value
self.check_if_full_path_is_moved(location, desired_action, place_span, flow_state);
- if let [base_proj @ .., ProjectionElem::Subslice { from, to, from_end: false }] =
- place_span.0.projection
+ if let Some((place_base, ProjectionElem::Subslice { from, to, from_end: false })) =
+ place_span.0.last_projection()
{
- let place_ty =
- Place::ty_from(place_span.0.local, base_proj, self.body(), self.infcx.tcx);
+ let place_ty = place_base.ty(self.body(), self.infcx.tcx);
if let ty::Array(..) = place_ty.ty.kind() {
- let array_place = PlaceRef { local: place_span.0.local, projection: base_proj };
self.check_if_subslice_element_is_moved(
location,
desired_action,
- (array_place, place_span.1),
+ (place_base, place_span.1),
maybe_uninits,
- *from,
- *to,
+ from,
+ to,
);
return;
}
debug!("check_if_assigned_path_is_moved place: {:?}", place);
// None case => assigning to `x` does not require `x` be initialized.
- let mut cursor = &*place.projection.as_ref();
- while let [proj_base @ .., elem] = cursor {
- cursor = proj_base;
-
+ for (place_base, elem) in place.iter_projections().rev() {
match elem {
ProjectionElem::Index(_/*operand*/) |
ProjectionElem::ConstantIndex { .. } |
ProjectionElem::Deref => {
self.check_if_full_path_is_moved(
location, InitializationRequiringAction::Use,
- (PlaceRef {
- local: place.local,
- projection: proj_base,
- }, span), flow_state);
+ (place_base, span), flow_state);
// (base initialized; no need to
// recur further)
break;
// assigning to `P.f` requires `P` itself
// be already initialized
let tcx = self.infcx.tcx;
- let base_ty = Place::ty_from(place.local, proj_base, self.body(), tcx).ty;
+ let base_ty = place_base.ty(self.body(), tcx).ty;
match base_ty.kind() {
ty::Adt(def, _) if def.has_dtor(tcx) => {
self.check_if_path_or_subpath_is_moved(
location, InitializationRequiringAction::Assignment,
- (PlaceRef {
- local: place.local,
- projection: proj_base,
- }, span), flow_state);
+ (place_base, span), flow_state);
// (base initialized; no need to
// recur further)
// 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 {
- local: place.local,
- projection: proj_base,
- }, span, flow_state);
+ check_parent_of_field(self, location, place_base, span, flow_state);
// rust-lang/rust#21232, #54499, #54986: during period where we reject
// partial initialization, do not complain about unnecessary `mut` on
// 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, _) =
- Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind()
- {
+ if let ty::Adt(def, _) = base.ty(this.body(), tcx).ty.kind() {
if def.is_union() {
if this.move_data.path_map[mpi].iter().any(|moi| {
this.move_data.moves[*moi].source.is_predecessor_of(location, this.body)
place: PlaceRef<'tcx>,
is_local_mutation_allowed: LocalMutationIsAllowed,
) -> Result<RootPlace<'tcx>, PlaceRef<'tcx>> {
- match place {
- PlaceRef { local, projection: [] } => {
- let local = &self.body.local_decls[local];
+ debug!("is_mutable: place={:?}, is_local...={:?}", place, is_local_mutation_allowed);
+ match place.last_projection() {
+ None => {
+ let local = &self.body.local_decls[place.local];
match local.mutability {
Mutability::Not => match is_local_mutation_allowed {
LocalMutationIsAllowed::Yes => Ok(RootPlace {
}),
}
}
- PlaceRef { local: _, projection: [proj_base @ .., elem] } => {
+ Some((place_base, elem)) => {
match elem {
ProjectionElem::Deref => {
- let base_ty =
- Place::ty_from(place.local, proj_base, self.body(), self.infcx.tcx).ty;
+ let base_ty = place_base.ty(self.body(), self.infcx.tcx).ty;
// Check the kind of deref to decide
match base_ty.kind() {
_ => LocalMutationIsAllowed::Yes,
};
- self.is_mutable(
- PlaceRef { local: place.local, projection: proj_base },
- mode,
- )
+ self.is_mutable(place_base, mode)
}
}
}
}
}
// `Box<T>` owns its content, so mutable if its location is mutable
- _ if base_ty.is_box() => self.is_mutable(
- PlaceRef { local: place.local, projection: proj_base },
- is_local_mutation_allowed,
- ),
+ _ if base_ty.is_box() => {
+ self.is_mutable(place_base, is_local_mutation_allowed)
+ }
// Deref should only be for reference, pointers or boxes
_ => bug!("Deref of unexpected type: {:?}", base_ty),
}
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
+ "is_mutable: upvar.mutability={:?} local_mutation_is_allowed={:?} \
+ place={:?}, place_base={:?}",
+ upvar, is_local_mutation_allowed, place, place_base
);
- match (upvar.mutability, is_local_mutation_allowed) {
+ match (upvar.place.mutability, is_local_mutation_allowed) {
(
Mutability::Not,
LocalMutationIsAllowed::No
// });
// }
// ```
- let _ = self.is_mutable(
- PlaceRef { local: place.local, projection: proj_base },
- is_local_mutation_allowed,
- )?;
+ let _ =
+ self.is_mutable(place_base, is_local_mutation_allowed)?;
Ok(RootPlace {
place_local: place.local,
place_projection: place.projection,
}
}
} else {
- self.is_mutable(
- PlaceRef { local: place.local, projection: proj_base },
- is_local_mutation_allowed,
- )
+ self.is_mutable(place_base, is_local_mutation_allowed)
}
}
}