diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index c60713cea030..8342c509c141 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -9,7 +9,7 @@ use tracing::debug; use super::{ Init, InitIndex, InitKind, InitLocation, LocationMap, LookupResult, MoveData, MoveOut, - MoveOutIndex, MovePath, MovePathIndex, MovePathLookup, + MoveOutIndex, MovePath, MovePathIndex, MovePathLookup, MoveSubPath, MoveSubPathResult, }; struct MoveDataBuilder<'a, 'tcx, F> { @@ -94,26 +94,25 @@ fn new_move_path<'tcx>( move_path } -enum MovePathResult { - Path(MovePathIndex), - Union(MovePathIndex), - Error, -} - impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { - /// This creates a MovePath for a given place, returning an `MovePathError` - /// if that place can't be moved from. + /// This creates a MovePath for a given place, calling `on_move` + /// if it can be moved from. If theres a union in the path, its + /// move place will be given to `on_move`. If there's a subslice + /// projection, `on_move` will be called for each element. /// /// NOTE: places behind references *do not* get a move path, which is /// problematic for borrowck. /// /// Maybe we should have separate "borrowck" and "moveck" modes. - fn move_path_for(&mut self, place: Place<'tcx>) -> MovePathResult { + fn move_path_for(&mut self, place: Place<'tcx>, mut on_move: G) + where + G: FnMut(&mut Self, MovePathIndex), + { let data = &mut self.data; debug!("lookup({:?})", place); let Some(mut base) = data.rev_lookup.find_local(place.local) else { - return MovePathResult::Error; + return; }; // The move path index of the first union that we find. Once this is @@ -123,144 +122,185 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { // from `*(u.f: &_)` isn't allowed. let mut union_path = None; - for (place_ref, elem) in data.rev_lookup.un_derefer.iter_projections(place.as_ref()) { + let mut iter = data.rev_lookup.un_derefer.iter_projections(place.as_ref()); + while let Some((place_ref, elem)) = iter.next() { let body = self.body; let tcx = self.tcx; let place_ty = place_ref.ty(body, tcx).ty; if place_ty.references_error() { - return MovePathResult::Error; + return; } - match elem { - ProjectionElem::Deref => match place_ty.kind() { - ty::Ref(..) | ty::RawPtr(..) => { - return MovePathResult::Error; - } - ty::Adt(adt, _) => { - if !adt.is_box() { - bug!("Adt should be a box type when Place is deref"); - } - } - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Pat(_, _) - | ty::Slice(_) - | ty::FnDef(_, _) - | ty::FnPtr(..) - | ty::Dynamic(_, _) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(_, _) - | ty::CoroutineWitness(..) - | ty::Never - | ty::Tuple(_) - | ty::UnsafeBinder(_) - | ty::Alias(_, _) - | ty::Param(_) - | ty::Bound(_, _) - | ty::Infer(_) - | ty::Error(_) - | ty::Placeholder(_) => { - bug!("When Place is Deref it's type shouldn't be {place_ty:#?}") - } - }, - ProjectionElem::Field(_, _) => match place_ty.kind() { - ty::Adt(adt, _) => { - if adt.has_dtor(tcx) { - return MovePathResult::Error; - } - if adt.is_union() { - union_path.get_or_insert(base); - } - } - ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(_, _) - | ty::Tuple(_) => (), - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Pat(_, _) - | ty::Slice(_) - | ty::RawPtr(_, _) - | ty::Ref(_, _, _) - | ty::FnDef(_, _) - | ty::FnPtr(..) - | ty::Dynamic(_, _) - | ty::CoroutineWitness(..) - | ty::Never - | ty::UnsafeBinder(_) - | ty::Alias(_, _) - | ty::Param(_) - | ty::Bound(_, _) - | ty::Infer(_) - | ty::Error(_) - | ty::Placeholder(_) => bug!( - "When Place contains ProjectionElem::Field its type shouldn't be {place_ty:#?}" - ), - }, - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { - match place_ty.kind() { - ty::Slice(_) => { - return MovePathResult::Error; - } - ty::Array(_, _) => (), - _ => bug!("Unexpected type {:#?}", place_ty.is_array()), - } + + let res = MoveSubPath::of(elem.kind()); + + let move_elem = match res { + MoveSubPathResult::One(move_elem) => { + match move_elem { + MoveSubPath::Deref => match place_ty.kind() { + ty::Ref(..) | ty::RawPtr(..) => { + return; + } + ty::Adt(adt, _) => { + if !adt.is_box() { + bug!("Adt should be a box type when Place is deref"); + } + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::Slice(_) + | ty::FnDef(_, _) + | ty::FnPtr(..) + | ty::Dynamic(_, _) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::UnsafeBinder(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Infer(_) + | ty::Error(_) + | ty::Placeholder(_) => { + bug!("When Place is Deref it's type shouldn't be {place_ty:#?}") + } + }, + MoveSubPath::Field(_) => match place_ty.kind() { + ty::Adt(adt, _) => { + if adt.has_dtor(tcx) { + return; + } + if adt.is_union() { + union_path.get_or_insert(base); + } + } + ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(_, _) + | ty::Tuple(_) => (), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(..) + | ty::Dynamic(_, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::UnsafeBinder(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Infer(_) + | ty::Error(_) + | ty::Placeholder(_) => bug!( + "When Place contains ProjectionElem::Field its type shouldn't be {place_ty:#?}" + ), + }, + MoveSubPath::ConstantIndex(_) => match place_ty.kind() { + ty::Slice(_) => { + return; + } + ty::Array(_, _) => (), + _ => bug!("Unexpected type {:#?}", place_ty.is_array()), + }, + MoveSubPath::Downcast(_) => (), + }; + + move_elem } - ProjectionElem::Index(_) => match place_ty.kind() { - ty::Array(..) | ty::Slice(_) => { - return MovePathResult::Error; + + // Split `Subslice` patterns into the corresponding list of + // `ConstIndex` patterns. This is done to ensure that all move paths + // are disjoint, which is expected by drop elaboration. + MoveSubPathResult::Subslice { from, to } => { + assert!( + iter.all( + |(_, elem)| MoveSubPath::of(elem.kind()) == MoveSubPathResult::Skip + ) + ); + drop(iter); // drop for borrowck + + let (&elem_ty, len) = match place_ty.kind() { + ty::Array(ty, size) => ( + ty, + size.try_to_target_usize(self.tcx) + .expect("expected subslice projection on fixed-size array"), + ), + _ => bug!("from_end: false slice pattern of non-array type"), + }; + + if !(self.filter)(elem_ty) { + return; } - _ => bug!("Unexpected type {place_ty:#?}"), - }, - ProjectionElem::UnwrapUnsafeBinder(_) => {} - // `OpaqueCast`:Only transmutes the type, so no moves there. - // `Downcast` :Only changes information about a `Place` without moving. - // So it's safe to skip these. - ProjectionElem::OpaqueCast(_) | ProjectionElem::Downcast(_, _) => (), - } + + for offset in from..to { + let place_elem = + PlaceElem::ConstantIndex { offset, min_length: len, from_end: false }; + let subpath_elem = MoveSubPath::ConstantIndex(offset); + + let mpi = self.add_move_path(base, subpath_elem, |tcx| { + place_ref.project_deeper(&[place_elem], tcx) + }); + on_move(self, mpi); + } + + return; + } + + MoveSubPathResult::Skip => continue, + MoveSubPathResult::Stop => return, + }; + let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty; if !(self.filter)(elem_ty) { - return MovePathResult::Error; + return; } if union_path.is_none() { // inlined from add_move_path because of a borrowck conflict with the iterator - base = - *data.rev_lookup.projections.entry((base, elem.kind())).or_insert_with(|| { - new_move_path( - &mut data.move_paths, - &mut data.path_map, - &mut data.init_path_map, - Some(base), - place_ref.project_deeper(&[elem], tcx), - ) - }) + base = *data.rev_lookup.projections.entry((base, move_elem)).or_insert_with(|| { + new_move_path( + &mut data.move_paths, + &mut data.path_map, + &mut data.init_path_map, + Some(base), + place_ref.project_deeper(&[elem], tcx), + ) + }) } } + drop(iter); // drop for borrowck + if let Some(base) = union_path { // Move out of union - always move the entire union. - MovePathResult::Union(base) + on_move(self, base); } else { - MovePathResult::Path(base) + on_move(self, base); } } fn add_move_path( &mut self, base: MovePathIndex, - elem: PlaceElem<'tcx>, + elem: MoveSubPath, mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>, ) -> MovePathIndex { let MoveDataBuilder { @@ -268,7 +308,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { tcx, .. } = self; - *rev_lookup.projections.entry((base, elem.kind())).or_insert_with(move || { + *rev_lookup.projections.entry((base, elem)).or_insert_with(move || { new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx)) }) } @@ -276,7 +316,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { fn create_move_path(&mut self, place: Place<'tcx>) { // This is an non-moving access (such as an overwrite or // drop), so this not being a valid move path is OK. - let _ = self.move_path_for(place); + self.move_path_for(place, |_, _| ()); } fn finalize(self) -> MoveData<'tcx> { @@ -525,46 +565,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { fn gather_move(&mut self, place: Place<'tcx>) { debug!("gather_move({:?}, {:?})", self.loc, place); - if let [ref base @ .., ProjectionElem::Subslice { from, to, from_end: false }] = - **place.projection - { - // Split `Subslice` patterns into the corresponding list of - // `ConstIndex` patterns. This is done to ensure that all move paths - // are disjoint, which is expected by drop elaboration. - let base_place = - Place { local: place.local, projection: self.tcx.mk_place_elems(base) }; - let base_path = match self.move_path_for(base_place) { - MovePathResult::Path(path) => path, - MovePathResult::Union(path) => { - self.record_move(place, path); - return; - } - MovePathResult::Error => { - return; - } - }; - let base_ty = base_place.ty(self.body, self.tcx).ty; - let len: u64 = match base_ty.kind() { - ty::Array(_, size) => size - .try_to_target_usize(self.tcx) - .expect("expected subslice projection on fixed-size array"), - _ => bug!("from_end: false slice pattern of non-array type"), - }; - for offset in from..to { - let elem = - ProjectionElem::ConstantIndex { offset, min_length: len, from_end: false }; - let path = - self.add_move_path(base_path, elem, |tcx| tcx.mk_place_elem(base_place, elem)); - self.record_move(place, path); - } - } else { - match self.move_path_for(place) { - MovePathResult::Path(path) | MovePathResult::Union(path) => { - self.record_move(place, path) - } - MovePathResult::Error => {} - }; - } + self.move_path_for(place, |this, mpi| this.record_move(place, mpi)); } fn record_move(&mut self, place: Place<'tcx>, path: MovePathIndex) { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 466416d63f53..669ab9458210 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -13,6 +13,7 @@ use std::fmt; use std::ops::{Index, IndexMut}; +use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxHashMap; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::*; @@ -309,7 +310,7 @@ pub struct MovePathLookup<'tcx> { /// subsequent search so that it is solely relative to that /// base-place). For the remaining lookup, we map the projection /// elem to the associated MovePathIndex. - projections: FxHashMap<(MovePathIndex, ProjectionKind), MovePathIndex>, + projections: FxHashMap<(MovePathIndex, MoveSubPath), MovePathIndex>, un_derefer: UnDerefer<'tcx>, } @@ -333,7 +334,14 @@ impl<'tcx> MovePathLookup<'tcx> { }; for (_, elem) in self.un_derefer.iter_projections(place) { - if let Some(&subpath) = self.projections.get(&(result, elem.kind())) { + let subpath = match MoveSubPath::of(elem.kind()) { + MoveSubPathResult::One(kind) => self.projections.get(&(result, kind)), + MoveSubPathResult::Subslice { .. } => None, // just use the parent MovePath + MoveSubPathResult::Skip => continue, + MoveSubPathResult::Stop => None, + }; + + if let Some(&subpath) = subpath { result = subpath; } else { return LookupResult::Parent(Some(result)); @@ -390,3 +398,54 @@ impl<'tcx> MoveData<'tcx> { self.move_paths[root].find_descendant(&self.move_paths, pred) } } + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum MoveSubPath { + Deref, + Field(FieldIdx), + ConstantIndex(u64), + Downcast(VariantIdx), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum MoveSubPathResult { + One(MoveSubPath), + Subslice { from: u64, to: u64 }, + Skip, + Stop, +} + +impl MoveSubPath { + pub fn of(elem: ProjectionKind) -> MoveSubPathResult { + let subpath = match elem { + // correspond to a MoveSubPath + ProjectionKind::Deref => MoveSubPath::Deref, + ProjectionKind::Field(idx, _) => MoveSubPath::Field(idx), + ProjectionKind::ConstantIndex { offset, min_length: _, from_end: false } => { + MoveSubPath::ConstantIndex(offset) + } + ProjectionKind::Downcast(_, idx) => MoveSubPath::Downcast(idx), + + // these should be the same move path as their parent + // they're fine to skip because they cannot have sibling move paths + ProjectionKind::OpaqueCast(_) | ProjectionKind::UnwrapUnsafeBinder(_) => { + return MoveSubPathResult::Skip; + } + + // these cannot be moved through + ProjectionKind::Index(_) + | ProjectionKind::ConstantIndex { offset: _, min_length: _, from_end: true } + | ProjectionKind::Subslice { from: _, to: _, from_end: true } => { + return MoveSubPathResult::Stop; + } + + // subslice is special. + // it needs to be split into individual move paths + ProjectionKind::Subslice { from, to, from_end: false } => { + return MoveSubPathResult::Subslice { from, to }; + } + }; + + MoveSubPathResult::One(subpath) + } +}