diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 57e49ba8d1a56..b1e372afc6501 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -104,7 +104,7 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { ) { let cx = self.fx.cx; - if let &[ref proj_base @ .., elem] = place_ref.projection { + if let Some((place_base, elem)) = place_ref.last_projection() { let mut base_context = if context.is_mutating_use() { PlaceContext::MutatingUse(MutatingUseContext::Projection) } else { @@ -119,8 +119,7 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { ) ); if is_consume { - let base_ty = - mir::Place::ty_from(place_ref.local, proj_base, self.fx.mir, cx.tcx()); + let base_ty = mir::PlaceRef::ty(&place_base, self.fx.mir, cx.tcx()); let base_ty = self.fx.monomorphize(base_ty); // ZSTs don't require any actual memory access. @@ -175,11 +174,7 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { base_context = context; } - self.process_place( - &mir::PlaceRef { local: place_ref.local, projection: proj_base }, - base_context, - location, - ); + self.process_place(&place_base, base_context, location); // HACK(eddyb) this emulates the old `visit_projection_elem`, this // entire `visit_place`-like `process_place` method should be rewritten, // now that we have moved to the "slice of projections" representation. diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index e4f4c884470c7..af05319ae157e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -514,7 +514,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn monomorphized_place_ty(&self, place_ref: mir::PlaceRef<'tcx>) -> Ty<'tcx> { let tcx = self.cx.tcx(); - let place_ty = mir::Place::ty_from(place_ref.local, place_ref.projection, self.mir, tcx); + let place_ty = mir::PlaceRef::ty(&place_ref, self.mir, tcx); self.monomorphize(place_ty.ty) } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a69555fd1a8ce..fab2f2c97e4ae 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1745,18 +1745,14 @@ impl<'tcx> Place<'tcx> { /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? + #[inline(always)] pub fn local_or_deref_local(&self) -> Option { - match self.as_ref() { - PlaceRef { local, projection: [] } - | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), - _ => None, - } + self.as_ref().local_or_deref_local() } /// If this place represents a local variable like `_X` with no /// projections, return `Some(_X)`. + #[inline(always)] pub fn as_local(&self) -> Option { self.as_ref().as_local() } @@ -1770,6 +1766,7 @@ impl<'tcx> Place<'tcx> { /// As a concrete example, given the place a.b.c, this would yield: /// - (a, .b) /// - (a.b, .c) + /// /// Given a place without projections, the iterator is empty. pub fn iter_projections( self, @@ -1790,8 +1787,6 @@ impl From for Place<'_> { impl<'tcx> PlaceRef<'tcx> { /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? pub fn local_or_deref_local(&self) -> Option { match *self { PlaceRef { local, projection: [] } @@ -1808,6 +1803,14 @@ impl<'tcx> PlaceRef<'tcx> { _ => None, } } + + pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> { + if let &[ref proj_base @ .., elem] = self.projection { + Some((PlaceRef { local: self.local, projection: proj_base }, elem)) + } else { + None + } + } } impl Debug for Place<'_> { 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 1474c7abfadaf..0bb09e26f03e8 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -293,9 +293,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - let ty = - Place::ty_from(used_place.local, used_place.projection, self.body, self.infcx.tcx) - .ty; + let ty = PlaceRef::ty(&used_place, self.body, self.infcx.tcx).ty; let needs_note = match ty.kind() { ty::Closure(id, _) => { let tables = self.infcx.tcx.typeck(id.expect_local()); @@ -732,8 +730,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_base, place_projection| { - let ty = Place::ty_from(place_base, place_projection, self.body, self.infcx.tcx).ty; + let union_ty = |place_base| { + let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty; ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty) }; @@ -751,15 +749,10 @@ 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 Place { local, projection } = first_borrowed_place; - - let mut cursor = projection.as_ref(); - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in first_borrowed_place.iter_projections().rev() { match elem { - ProjectionElem::Field(field, _) if union_ty(local, proj_base).is_some() => { - return Some((PlaceRef { local, projection: proj_base }, field)); + ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => { + return Some((place_base, field)); } _ => {} } @@ -769,23 +762,12 @@ 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 Place { local, ref projection } = second_borrowed_place; - - let mut cursor = &projection[..]; - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in second_borrowed_place.iter_projections().rev() { if let ProjectionElem::Field(field, _) = elem { - if let Some(union_ty) = union_ty(local, proj_base) { - if field != target_field - && local == target_base.local - && proj_base == target_base.projection - { + if let Some(union_ty) = union_ty(place_base) { + if field != target_field && place_base == target_base { return Some(( - self.describe_any_place(PlaceRef { - local, - projection: proj_base, - }), + self.describe_any_place(place_base), self.describe_any_place(first_borrowed_place.as_ref()), self.describe_any_place(second_borrowed_place.as_ref()), union_ty.to_string(), diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index 44044d55532de..006e05072a7a9 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -1740,20 +1740,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 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 = PlaceRef::ty(&place_base, 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; } @@ -1825,10 +1823,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 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 { .. } | @@ -1843,10 +1838,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 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; @@ -1862,15 +1854,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // 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 = PlaceRef::ty(&place_base, 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) @@ -1880,10 +1869,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // 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 @@ -1965,9 +1951,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'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, _) = - Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind() - { + if let ty::Adt(def, _) = PlaceRef::ty(&base, 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) @@ -2162,9 +2146,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place: PlaceRef<'tcx>, is_local_mutation_allowed: LocalMutationIsAllowed, ) -> Result, PlaceRef<'tcx>> { - match place { - PlaceRef { local, projection: [] } => { - let local = &self.body.local_decls[local]; + 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 { @@ -2186,11 +2170,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }), } } - 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 = PlaceRef::ty(&place_base, self.body(), self.infcx.tcx).ty; // Check the kind of deref to decide match base_ty.kind() { @@ -2208,10 +2191,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { _ => LocalMutationIsAllowed::Yes, }; - self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - mode, - ) + self.is_mutable(place_base, mode) } } } @@ -2229,10 +2209,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } // `Box` 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), } @@ -2286,10 +2265,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // }); // } // ``` - 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, @@ -2298,10 +2275,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } } else { - self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - ) + self.is_mutable(place_base, is_local_mutation_allowed) } } } diff --git a/compiler/rustc_mir/src/borrow_check/prefixes.rs b/compiler/rustc_mir/src/borrow_check/prefixes.rs index 6c5d42296f72b..cf04c55eb68c5 100644 --- a/compiler/rustc_mir/src/borrow_check/prefixes.rs +++ b/compiler/rustc_mir/src/borrow_check/prefixes.rs @@ -10,7 +10,7 @@ use super::MirBorrowckCtxt; use rustc_hir as hir; -use rustc_middle::mir::{Body, Place, PlaceRef, ProjectionElem}; +use rustc_middle::mir::{Body, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, TyCtxt}; pub trait IsPrefixOf<'tcx> { @@ -67,24 +67,23 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { // downcasts here, but may return a base of a downcast). 'cursor: loop { - match &cursor { - PlaceRef { local: _, projection: [] } => { + match cursor.last_projection() { + None => { self.next = None; return Some(cursor); } - PlaceRef { local: _, projection: [proj_base @ .., elem] } => { + Some((cursor_base, elem)) => { match elem { ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { // FIXME: add union handling - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { - cursor = PlaceRef { local: cursor.local, projection: proj_base }; + cursor = cursor_base; continue 'cursor; } ProjectionElem::Deref => { @@ -92,7 +91,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } } - assert_eq!(*elem, ProjectionElem::Deref); + assert_eq!(elem, ProjectionElem::Deref); match self.kind { PrefixSet::Shallow => { @@ -105,8 +104,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { PrefixSet::All => { // All prefixes: just blindly enqueue the base // of the projection. - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } PrefixSet::Supporting => { @@ -119,7 +117,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { // derefs, except we stop at the deref of a shared // reference. - let ty = Place::ty_from(cursor.local, proj_base, self.body, self.tcx).ty; + let ty = PlaceRef::ty(&cursor_base, self.body, self.tcx).ty; match ty.kind() { ty::RawPtr(_) | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Not) => { // don't continue traversing over derefs of raw pointers or shared @@ -129,14 +127,12 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Mut) => { - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } ty::Adt(..) if ty.is_box() => { - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); }