diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index a5fb8a1cbe8b7..8f41bfae2fdf5 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -1616,20 +1616,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> { let tcx = self.infcx.tcx; - match place.projection { - [] => StorageDeadOrDrop::LocalStorageDead, - [proj_base @ .., elem] => { + match place.last_projection() { + None => StorageDeadOrDrop::LocalStorageDead, + Some((place_base, elem)) => { // FIXME(spastorino) make this iterate - let base_access = self.classify_drop_access_kind(PlaceRef { - local: place.local, - projection: proj_base, - }); + let base_access = self.classify_drop_access_kind(place_base); match elem { ProjectionElem::Deref => match base_access { StorageDeadOrDrop::LocalStorageDead | StorageDeadOrDrop::BoxedStorageDead => { assert!( - Place::ty_from(place.local, proj_base, self.body, tcx).ty.is_box(), + place_base.ty(self.body, tcx).ty.is_box(), "Drop of value behind a reference or raw pointer" ); StorageDeadOrDrop::BoxedStorageDead @@ -1637,7 +1634,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { StorageDeadOrDrop::Destructor(_) => base_access, }, ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { - 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) => { // Report the outermost adt with a destructor @@ -1652,7 +1649,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { _ => base_access, } } - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Index(_) => base_access, diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 9f97d5c9af7d8..6d98bf554f1cf 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -338,8 +338,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.describe_field(PlaceRef { local, projection: proj_base }, field) } ProjectionElem::Downcast(_, variant_index) => { - let base_ty = - Place::ty_from(place.local, place.projection, 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) => { @@ -473,7 +472,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // If we didn't find an overloaded deref or index, then assume it's a // built in deref and check the type of the base. - let base_ty = Place::ty_from(deref_base.local, deref_base.projection, self.body, tcx).ty; + let base_ty = deref_base.ty(self.body, tcx).ty; if base_ty.is_unsafe_ptr() { BorrowedContentSource::DerefRawPointer } else if base_ty.is_mutable_ptr() { diff --git a/compiler/rustc_mir/src/borrow_check/path_utils.rs b/compiler/rustc_mir/src/borrow_check/path_utils.rs index 934729553a73b..fa3ae2367e08e 100644 --- a/compiler/rustc_mir/src/borrow_check/path_utils.rs +++ b/compiler/rustc_mir/src/borrow_check/path_utils.rs @@ -147,27 +147,25 @@ pub(crate) fn is_upvar_field_projection( place_ref: PlaceRef<'tcx>, body: &Body<'tcx>, ) -> Option { - let mut place_projection = place_ref.projection; + let mut place_ref = place_ref; let mut by_ref = false; - if let [proj_base @ .., ProjectionElem::Deref] = place_projection { - place_projection = proj_base; + if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() { + place_ref = place_base; by_ref = true; } - match place_projection { - [base @ .., ProjectionElem::Field(field, _ty)] => { - let base_ty = Place::ty_from(place_ref.local, base, body, tcx).ty; - + match place_ref.last_projection() { + Some((place_base, ProjectionElem::Field(field, _ty))) => { + let base_ty = place_base.ty(body, tcx).ty; if (base_ty.is_closure() || base_ty.is_generator()) && (!by_ref || upvars[field.index()].by_ref) { - Some(*field) + Some(field) } else { None } } - _ => None, } } diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs index ab7fadac91e39..ee78ff00c9b2b 100644 --- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs +++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs @@ -518,14 +518,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { // Check if we are assigning into a field of a union, if so, lookup the place // of the union so it is marked as initialized again. - if let [proj_base @ .., ProjectionElem::Field(_, _)] = place.projection { - if let ty::Adt(def, _) = - Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx) - .ty - .kind() - { + if let Some((place_base, ProjectionElem::Field(_, _))) = place.last_projection() { + if let ty::Adt(def, _) = place_base.ty(self.builder.body, self.builder.tcx).ty.kind() { if def.is_union() { - place = PlaceRef { local: place.local, projection: proj_base } + place = place_base; } } } diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs index c66d3ed76df90..0ce1980f10a11 100644 --- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs @@ -174,14 +174,10 @@ where Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { // Special-case reborrows to be more like a copy of the reference. - if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx).ty; + if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() { + let base_ty = place_base.ty(cx.body, cx.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - return in_place::( - cx, - in_local, - PlaceRef { local: place.local, projection: proj_base }, - ); + return in_place::(cx, in_local, place_base); } } @@ -209,9 +205,9 @@ where Q: Qualif, F: FnMut(Local) -> bool, { - let mut projection = place.projection; - while let &[ref proj_base @ .., proj_elem] = projection { - match proj_elem { + let mut place = place; + while let Some((place_base, elem)) = place.last_projection() { + match elem { ProjectionElem::Index(index) if in_local(index) => return true, ProjectionElem::Deref @@ -222,16 +218,16 @@ where | ProjectionElem::Index(_) => {} } - let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx); - let proj_ty = base_ty.projection_ty(cx.tcx, proj_elem).ty; + let base_ty = place_base.ty(cx.body, cx.tcx); + let proj_ty = base_ty.projection_ty(cx.tcx, elem).ty; if !Q::in_any_value_of_ty(cx, proj_ty) { return false; } - projection = proj_base; + place = place_base; } - assert!(projection.is_empty()); + assert!(place.projection.is_empty()); in_local(place.local) } diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index 88b2378cd06e7..3d8192649a01f 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -1007,27 +1007,26 @@ fn place_as_reborrow( body: &Body<'tcx>, place: Place<'tcx>, ) -> Option<&'a [PlaceElem<'tcx>]> { - place.projection.split_last().and_then(|(outermost, inner)| { - if outermost != &ProjectionElem::Deref { - return None; - } - - // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const` - // that points to the allocation for the static. Don't treat these as reborrows. - if body.local_decls[place.local].is_ref_to_static() { - return None; - } - - // Ensure the type being derefed is a reference and not a raw pointer. - // - // This is sufficient to prevent an access to a `static mut` from being marked as a - // reborrow, even if the check above were to disappear. - let inner_ty = Place::ty_from(place.local, inner, body, tcx).ty; - match inner_ty.kind() { - ty::Ref(..) => Some(inner), - _ => None, + match place.as_ref().last_projection() { + Some((place_base, ProjectionElem::Deref)) => { + // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const` + // that points to the allocation for the static. Don't treat these as reborrows. + if body.local_decls[place_base.local].is_ref_to_static() { + None + } else { + // Ensure the type being derefed is a reference and not a raw pointer. + // + // This is sufficient to prevent an access to a `static mut` from being marked as a + // reborrow, even if the check above were to disappear. + let inner_ty = place_base.ty(body, tcx).ty; + match inner_ty.kind() { + ty::Ref(..) => Some(place_base.projection), + _ => None, + } + } } - }) + _ => None, + } } fn is_int_bool_or_char(ty: Ty<'_>) -> bool { diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs index 0732d2568ad1d..bac47d0b547e9 100644 --- a/compiler/rustc_mir/src/transform/check_unsafety.rs +++ b/compiler/rustc_mir/src/transform/check_unsafety.rs @@ -399,17 +399,13 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { place: Place<'tcx>, is_mut_use: bool, ) { - let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in place.iter_projections().rev() { match elem { // Modifications behind a dereference don't affect the value of // the pointer. ProjectionElem::Deref => return, ProjectionElem::Field(..) => { - let ty = - Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty; + let ty = place_base.ty(&self.body.local_decls, self.tcx).ty; if let ty::Adt(def, _) = ty.kind() { if self.tcx.layout_scalar_valid_range(def.did) != (Bound::Unbounded, Bound::Unbounded) diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs index 990ca313c5ddc..b0c70372b161a 100644 --- a/compiler/rustc_mir/src/transform/instcombine.rs +++ b/compiler/rustc_mir/src/transform/instcombine.rs @@ -277,11 +277,9 @@ 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) = rvalue { - if let PlaceRef { local, projection: &[ref proj_base @ .., ProjectionElem::Deref] } = - place.as_ref() - { + if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() { // The dereferenced place must have type `&_`. - let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty; + let ty = place_base.ty(self.body, self.tcx).ty; if let ty::Ref(_, _, Mutability::Not) = ty.kind() { self.optimizations.and_stars.insert(location); } diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index ea92e23e9bffb..cac5abb1059a8 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -445,43 +445,50 @@ impl<'tcx> Validator<'_, 'tcx> { } fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { - match place { - PlaceRef { local, projection: [] } => self.validate_local(local), - PlaceRef { local, projection: [proj_base @ .., elem] } => { + match place.last_projection() { + None => self.validate_local(place.local), + Some((place_base, elem)) => { // Validate topmost projection, then recurse. - match *elem { + match elem { ProjectionElem::Deref => { let mut promotable = false; - // This is a special treatment for cases like *&STATIC where STATIC is a - // global static variable. - // This pattern is generated only when global static variables are directly - // accessed and is qualified for promotion safely. - if let TempState::Defined { location, .. } = self.temps[local] { - let def_stmt = - self.body[location.block].statements.get(location.statement_index); - if let Some(Statement { - kind: - StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(c)))), - .. - }) = def_stmt + // The `is_empty` predicate is introduced to exclude the case + // where the projection operations are [ .field, * ]. + // The reason is because promotion will be illegal if field + // accesses precede the dereferencing. + // Discussion can be found at + // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247 + // There may be opportunity for generalization, but this needs to be + // accounted for. + if place_base.projection.is_empty() { + // This is a special treatment for cases like *&STATIC where STATIC is a + // global static variable. + // This pattern is generated only when global static variables are directly + // accessed and is qualified for promotion safely. + if let TempState::Defined { location, .. } = + self.temps[place_base.local] { - if let Some(did) = c.check_static_ptr(self.tcx) { - // Evaluating a promoted may not read statics except if it got - // promoted from a static (this is a CTFE check). So we - // can only promote static accesses inside statics. - if let Some(hir::ConstContext::Static(..)) = self.const_kind { - // The `is_empty` predicate is introduced to exclude the case - // where the projection operations are [ .field, * ]. - // The reason is because promotion will be illegal if field - // accesses precede the dereferencing. - // Discussion can be found at - // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247 - // There may be opportunity for generalization, but this needs to be - // accounted for. - if proj_base.is_empty() - && !self.tcx.is_thread_local_static(did) + let def_stmt = self.body[location.block] + .statements + .get(location.statement_index); + if let Some(Statement { + kind: + StatementKind::Assign(box ( + _, + Rvalue::Use(Operand::Constant(c)), + )), + .. + }) = def_stmt + { + if let Some(did) = c.check_static_ptr(self.tcx) { + // Evaluating a promoted may not read statics except if it got + // promoted from a static (this is a CTFE check). So we + // can only promote static accesses inside statics. + if let Some(hir::ConstContext::Static(..)) = self.const_kind { - promotable = true; + if !self.tcx.is_thread_local_static(did) { + promotable = true; + } } } } @@ -502,8 +509,7 @@ impl<'tcx> Validator<'_, 'tcx> { } ProjectionElem::Field(..) => { - let base_ty = - Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; + let base_ty = place_base.ty(self.body, self.tcx).ty; if let Some(def) = base_ty.ty_adt_def() { // No promotion of union field accesses. if def.is_union() { @@ -513,7 +519,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } - self.validate_place(PlaceRef { local: place.local, projection: proj_base }) + self.validate_place(place_base) } } } @@ -660,13 +666,11 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::AddressOf(_, place) => { // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is // no problem, only using it is. - if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; + if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() + { + let base_ty = place_base.ty(self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - return self.validate_place(PlaceRef { - local: place.local, - projection: proj_base, - }); + return self.validate_place(place_base); } } return Err(Unpromotable); @@ -675,12 +679,12 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::Ref(_, kind, place) => { // Special-case reborrows to be more like a copy of the reference. let mut place_simplified = place.as_ref(); - if let [proj_base @ .., ProjectionElem::Deref] = &place_simplified.projection { - let base_ty = - Place::ty_from(place_simplified.local, proj_base, self.body, self.tcx).ty; + if let Some((place_base, ProjectionElem::Deref)) = + place_simplified.last_projection() + { + let base_ty = place_base.ty(self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - place_simplified = - PlaceRef { local: place_simplified.local, projection: proj_base }; + place_simplified = place_base; } } diff --git a/compiler/rustc_mir/src/util/alignment.rs b/compiler/rustc_mir/src/util/alignment.rs index a0728a6a63015..f567c9cfaab86 100644 --- a/compiler/rustc_mir/src/util/alignment.rs +++ b/compiler/rustc_mir/src/util/alignment.rs @@ -38,15 +38,12 @@ fn is_within_packed<'tcx, L>(tcx: TyCtxt<'tcx>, local_decls: &L, place: Place<'t where L: HasLocalDecls<'tcx>, { - let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in place.iter_projections().rev() { match elem { // encountered a Deref, which is ABI-aligned ProjectionElem::Deref => break, ProjectionElem::Field(..) => { - let ty = Place::ty_from(place.local, proj_base, local_decls, tcx).ty; + let ty = place_base.ty(local_decls, tcx).ty; match ty.kind() { ty::Adt(def, _) if def.repr.packed() => return true, _ => {}