diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
commit | c23a457e72abe608715ac76f076f47dc42af07a5 (patch) | |
tree | 2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /compiler/rustc_mir_dataflow/src | |
parent | Releasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip |
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_mir_dataflow/src')
11 files changed, 250 insertions, 117 deletions
diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 9e02b0271..c9991e499 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -80,7 +80,7 @@ impl Unwind { fn into_action(self) -> UnwindAction { match self { Unwind::To(bb) => UnwindAction::Cleanup(bb), - Unwind::InCleanup => UnwindAction::Terminate, + Unwind::InCleanup => UnwindAction::Terminate(UnwindTerminateReason::InCleanup), } } @@ -194,6 +194,7 @@ where D: DropElaborator<'b, 'tcx>, 'tcx: 'b, { + #[instrument(level = "trace", skip(self), ret)] fn place_ty(&self, place: Place<'tcx>) -> Ty<'tcx> { place.ty(self.elaborator.body(), self.tcx()).ty } @@ -220,11 +221,9 @@ where // // FIXME: I think we should just control the flags externally, // and then we do not need this machinery. + #[instrument(level = "debug")] pub fn elaborate_drop(&mut self, bb: BasicBlock) { - debug!("elaborate_drop({:?}, {:?})", bb, self); - let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep); - debug!("elaborate_drop({:?}, {:?}): live - {:?}", bb, self, style); - match style { + match self.elaborator.drop_style(self.path, DropFlagMode::Deep) { DropStyle::Dead => { self.elaborator .patch() @@ -973,10 +972,10 @@ where } fn constant_usize(&self, val: u16) -> Operand<'tcx> { - Operand::Constant(Box::new(Constant { + Operand::Constant(Box::new(ConstOperand { span: self.source_info.span, user_ty: None, - literal: ConstantKind::from_usize(self.tcx(), val.into()), + const_: Const::from_usize(self.tcx(), val.into()), })) } diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 8a9e37c5a..70451edd5 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -1,5 +1,5 @@ use rustc_middle::mir::{ - self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdges, UnwindAction, + self, BasicBlock, CallReturnPlaces, Location, SwitchTargets, TerminatorEdges, }; use std::ops::RangeInclusive; @@ -486,10 +486,10 @@ impl Direction for Forward { propagate(target, exit_state); propagate(unwind, exit_state); } - TerminatorEdges::AssignOnReturn { return_, unwind, place } => { + TerminatorEdges::AssignOnReturn { return_, cleanup, place } => { // This must be done *first*, otherwise the unwind path will see the assignments. - if let UnwindAction::Cleanup(unwind) = unwind { - propagate(unwind, exit_state); + if let Some(cleanup) = cleanup { + propagate(cleanup, exit_state); } if let Some(return_) = return_ { analysis.apply_call_return_effect(exit_state, bb, place); diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 1421d9b45..bdddaaebc 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -538,7 +538,7 @@ where fn visit_block_start( &mut self, - _results: &Results<'tcx, A>, + _results: &mut Results<'tcx, A>, state: &Self::FlowState, _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -550,7 +550,7 @@ where fn visit_block_end( &mut self, - _results: &Results<'tcx, A>, + _results: &mut Results<'tcx, A>, state: &Self::FlowState, _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -562,7 +562,7 @@ where fn visit_statement_before_primary_effect( &mut self, - results: &Results<'tcx, A>, + results: &mut Results<'tcx, A>, state: &Self::FlowState, _statement: &mir::Statement<'tcx>, _location: Location, @@ -575,7 +575,7 @@ where fn visit_statement_after_primary_effect( &mut self, - results: &Results<'tcx, A>, + results: &mut Results<'tcx, A>, state: &Self::FlowState, _statement: &mir::Statement<'tcx>, _location: Location, @@ -586,7 +586,7 @@ where fn visit_terminator_before_primary_effect( &mut self, - results: &Results<'tcx, A>, + results: &mut Results<'tcx, A>, state: &Self::FlowState, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -599,7 +599,7 @@ where fn visit_terminator_after_primary_effect( &mut self, - results: &Results<'tcx, A>, + results: &mut Results<'tcx, A>, state: &Self::FlowState, _terminator: &mir::Terminator<'tcx>, _location: Location, diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 76a729827..3cfa7cc1c 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -35,7 +35,7 @@ pub trait ResultsVisitor<'mir, 'tcx, R> { fn visit_block_start( &mut self, - _results: &R, + _results: &mut R, _state: &Self::FlowState, _block_data: &'mir mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -46,7 +46,7 @@ pub trait ResultsVisitor<'mir, 'tcx, R> { /// its `statement_effect`. fn visit_statement_before_primary_effect( &mut self, - _results: &R, + _results: &mut R, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, _location: Location, @@ -57,7 +57,7 @@ pub trait ResultsVisitor<'mir, 'tcx, R> { /// statement applied to `state`. fn visit_statement_after_primary_effect( &mut self, - _results: &R, + _results: &mut R, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, _location: Location, @@ -68,7 +68,7 @@ pub trait ResultsVisitor<'mir, 'tcx, R> { /// its `terminator_effect`. fn visit_terminator_before_primary_effect( &mut self, - _results: &R, + _results: &mut R, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, _location: Location, @@ -81,7 +81,7 @@ pub trait ResultsVisitor<'mir, 'tcx, R> { /// The `call_return_effect` (if one exists) will *not* be applied to `state`. fn visit_terminator_after_primary_effect( &mut self, - _results: &R, + _results: &mut R, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, _location: Location, @@ -90,7 +90,7 @@ pub trait ResultsVisitor<'mir, 'tcx, R> { fn visit_block_end( &mut self, - _results: &R, + _results: &mut R, _state: &Self::FlowState, _block_data: &'mir mir::BasicBlockData<'tcx>, _block: BasicBlock, diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 8d7b50796..f6398c8d0 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -5,7 +5,8 @@ use rustc_middle::mir::*; use crate::{AnalysisDomain, GenKill, GenKillAnalysis}; /// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points -/// to a given local. +/// to a given local. This analysis ignores fake borrows, so it should not be used by +/// borrowck. /// /// At present, this is used as a very limited form of alias analysis. For example, /// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for @@ -91,13 +92,17 @@ where self.super_rvalue(rvalue, location); match rvalue { - Rvalue::AddressOf(_, borrowed_place) | Rvalue::Ref(_, _, borrowed_place) => { + // We ignore fake borrows as these get removed after analysis and shouldn't effect + // the layout of generators. + Rvalue::AddressOf(_, borrowed_place) + | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) => { if !borrowed_place.is_indirect() { self.trans.gen(borrowed_place.local); } } Rvalue::Cast(..) + | Rvalue::Ref(_, BorrowKind::Fake, _) | Rvalue::ShallowInitBox(..) | Rvalue::Use(..) | Rvalue::ThreadLocalRef(..) @@ -131,7 +136,7 @@ where } } - TerminatorKind::Terminate + TerminatorKind::UnwindTerminate(_) | TerminatorKind::Assert { .. } | TerminatorKind::Call { .. } | TerminatorKind::FalseEdge { .. } @@ -139,7 +144,7 @@ where | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } | TerminatorKind::InlineAsm { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 5aa73c7a9..664703795 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -201,7 +201,7 @@ impl DefUse { | NonMutatingUseContext::Inspect | NonMutatingUseContext::Move | NonMutatingUseContext::PlaceMention - | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::FakeBorrow | NonMutatingUseContext::SharedBorrow, ) => Some(DefUse::Use), diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index bea23b7f7..94d6eb67d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -291,14 +291,14 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { // Nothing to do for these. Match exhaustively so this fails to compile when new // variants are added. - TerminatorKind::Terminate + TerminatorKind::UnwindTerminate(_) | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} @@ -328,14 +328,14 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { // Nothing to do for these. Match exhaustively so this fails to compile when new // variants are added. TerminatorKind::Yield { .. } - | TerminatorKind::Terminate + | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs index 7806e8f45..2a7f23ef6 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs @@ -57,6 +57,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> { ProjectionElem::ConstantIndex { offset, min_length, from_end } } ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u), + ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty.lift()), } } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 5052de991..7a5b3585d 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -115,44 +115,126 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { let body = self.builder.body; let tcx = self.builder.tcx; let place_ty = place_ref.ty(body, tcx).ty; - match place_ty.kind() { - ty::Ref(..) | ty::RawPtr(..) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - BorrowedContent { target_place: place_ref.project_deeper(&[elem], tcx) }, - )); - } - ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfTypeWithDestructor { container_ty: place_ty }, - )); - } - ty::Adt(adt, _) if adt.is_union() => { - union_path.get_or_insert(base); - } - ty::Slice(_) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { - ty: place_ty, - is_index: matches!(elem, ProjectionElem::Index(..)), - }, - )); + match elem { + ProjectionElem::Deref => match place_ty.kind() { + ty::Ref(..) | ty::RawPtr(..) => { + return Err(MoveError::cannot_move_out_of( + self.loc, + BorrowedContent { + target_place: place_ref.project_deeper(&[elem], tcx), + }, + )); + } + 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::Slice(_) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::Generator(_, _, _) + | ty::GeneratorWitness(..) + | ty::Never + | ty::Tuple(_) + | 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 Err(MoveError::cannot_move_out_of( + self.loc, + InteriorOfTypeWithDestructor { container_ty: place_ty }, + )); + } + if adt.is_union() { + union_path.get_or_insert(base); + } + } + ty::Closure(_, _) | ty::Generator(_, _, _) | ty::Tuple(_) => (), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::GeneratorWitness(..) + | ty::Never + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Infer(_) + | ty::Error(_) + | ty::Placeholder(_) => bug!( + "When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}" + ), + }, + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { + match place_ty.kind() { + ty::Slice(_) => { + return Err(MoveError::cannot_move_out_of( + self.loc, + InteriorOfSliceOrArray { + ty: place_ty, + is_index: matches!(elem, ProjectionElem::Index(..)), + }, + )); + } + ty::Array(_, _) => (), + _ => bug!("Unexpected type {:#?}", place_ty.is_array()), + } } - - ty::Array(..) => { - if let ProjectionElem::Index(..) = elem { + ProjectionElem::Index(_) => match place_ty.kind() { + ty::Array(..) => { return Err(MoveError::cannot_move_out_of( self.loc, InteriorOfSliceOrArray { ty: place_ty, is_index: true }, )); } - } - - _ => {} - }; - + ty::Slice(_) => { + return Err(MoveError::cannot_move_out_of( + self.loc, + InteriorOfSliceOrArray { + ty: place_ty, + is_index: matches!(elem, ProjectionElem::Index(..)), + }, + )); + } + _ => bug!("Unexpected type {place_ty:#?}"), + }, + // `OpaqueCast`:Only transmutes the type, so no moves there. + // `Downcast` :Only changes information about a `Place` without moving. + // `Subtype` :Only transmutes the type, so moves. + // So it's safe to skip these. + ProjectionElem::OpaqueCast(_) + | ProjectionElem::Subtype(_) + | ProjectionElem::Downcast(_, _) => (), + } if union_path.is_none() { // inlined from add_move_path because of a borrowck conflict with the iterator base = @@ -370,8 +452,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { // this that could possibly access the return place, this doesn't // need recording. | TerminatorKind::Return - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate(_) | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } => {} diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 775c522b4..1ebb59b3a 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -190,7 +190,7 @@ impl PeekCall { if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } = &terminator.kind { - if let ty::FnDef(def_id, fn_args) = *func.literal.ty().kind() { + if let ty::FnDef(def_id, fn_args) = *func.const_.ty().kind() { let name = tcx.item_name(def_id); if !tcx.is_intrinsic(def_id) || name != sym::rustc_peek { return None; diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 766e0257e..83766f311 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -225,7 +225,7 @@ pub trait ValueAnalysis<'tcx> { fn handle_constant( &self, - constant: &Constant<'tcx>, + constant: &ConstOperand<'tcx>, state: &mut State<Self::Value>, ) -> Self::Value { self.super_constant(constant, state) @@ -233,7 +233,7 @@ pub trait ValueAnalysis<'tcx> { fn super_constant( &self, - _constant: &Constant<'tcx>, + _constant: &ConstOperand<'tcx>, _state: &mut State<Self::Value>, ) -> Self::Value { Self::Value::TOP @@ -269,8 +269,8 @@ pub trait ValueAnalysis<'tcx> { return self.handle_switch_int(discr, targets, state); } TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate + | TerminatorKind::UnwindResume + | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::Assert { .. } @@ -532,7 +532,7 @@ impl<V: Clone + HasTop + HasBottom> State<V> { /// places that are non-overlapping or identical. /// /// The target place must have been flooded before calling this method. - fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) { + pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) { let StateData::Reachable(values) = &mut self.0 else { return }; // If both places are tracked, we copy the value to the target. @@ -581,6 +581,14 @@ impl<V: Clone + HasTop + HasBottom> State<V> { } } + /// Retrieve the value stored for a place, or ⊤ if it is not tracked. + pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V { + match map.find_len(place) { + Some(place) => self.get_idx(place, map), + None => V::TOP, + } + } + /// Retrieve the value stored for a place index, or ⊤ if it is not tracked. pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V { match &self.0 { @@ -626,45 +634,36 @@ pub struct Map { } impl Map { - fn new() -> Self { - Self { + /// Returns a map that only tracks places whose type has scalar layout. + /// + /// This is currently the only way to create a [`Map`]. The way in which the tracked places are + /// chosen is an implementation detail and may not be relied upon (other than that their type + /// are scalars). + pub fn new<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) -> Self { + let mut map = Self { locals: IndexVec::new(), projections: FxHashMap::default(), places: IndexVec::new(), value_count: 0, inner_values: IndexVec::new(), inner_values_buffer: Vec::new(), - } - } - - /// Returns a map that only tracks places whose type passes the filter. - /// - /// This is currently the only way to create a [`Map`]. The way in which the tracked places are - /// chosen is an implementation detail and may not be relied upon (other than that their type - /// passes the filter). - pub fn from_filter<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - filter: impl Fn(Ty<'tcx>) -> bool, - value_limit: Option<usize>, - ) -> Self { - let mut map = Self::new(); + }; let exclude = excluded_locals(body); - map.register_with_filter(tcx, body, filter, exclude, value_limit); + map.register(tcx, body, exclude, value_limit); debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len()); map } - /// Register all non-excluded places that pass the filter. - fn register_with_filter<'tcx>( + /// Register all non-excluded places that have scalar layout. + fn register<'tcx>( &mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - filter: impl Fn(Ty<'tcx>) -> bool, exclude: BitSet<Local>, value_limit: Option<usize>, ) { let mut worklist = VecDeque::with_capacity(value_limit.unwrap_or(body.local_decls.len())); + let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); // Start by constructing the places for each bare local. self.locals = IndexVec::from_elem(None, &body.local_decls); @@ -679,7 +678,7 @@ impl Map { self.locals[local] = Some(place); // And push the eventual children places to the worklist. - self.register_children(tcx, place, decl.ty, &filter, &mut worklist); + self.register_children(tcx, param_env, place, decl.ty, &mut worklist); } // `place.elem1.elem2` with type `ty`. @@ -702,7 +701,7 @@ impl Map { } // And push the eventual children places to the worklist. - self.register_children(tcx, place, ty, &filter, &mut worklist); + self.register_children(tcx, param_env, place, ty, &mut worklist); } // Pre-compute the tree of ValueIndex nested in each PlaceIndex. @@ -732,42 +731,54 @@ impl Map { fn register_children<'tcx>( &mut self, tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, place: PlaceIndex, ty: Ty<'tcx>, - filter: &impl Fn(Ty<'tcx>) -> bool, worklist: &mut VecDeque<(PlaceIndex, Option<TrackElem>, TrackElem, Ty<'tcx>)>, ) { // Allocate a value slot if it doesn't have one, and the user requested one. - if self.places[place].value_index.is_none() && filter(ty) { + assert!(self.places[place].value_index.is_none()); + if tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.abi.is_scalar()) { self.places[place].value_index = Some(self.value_count.into()); self.value_count += 1; } // For enums, directly create the `Discriminant`, as that's their main use. if ty.is_enum() { - let discr_ty = ty.discriminant_ty(tcx); - if filter(discr_ty) { - let discr = *self - .projections - .entry((place, TrackElem::Discriminant)) - .or_insert_with(|| { - // Prepend new child to the linked list. - let next = self.places.push(PlaceInfo::new(Some(TrackElem::Discriminant))); - self.places[next].next_sibling = self.places[place].first_child; - self.places[place].first_child = Some(next); - next - }); - - // Allocate a value slot if it doesn't have one. - if self.places[discr].value_index.is_none() { - self.places[discr].value_index = Some(self.value_count.into()); - self.value_count += 1; - } - } + // Prepend new child to the linked list. + let discr = self.places.push(PlaceInfo::new(Some(TrackElem::Discriminant))); + self.places[discr].next_sibling = self.places[place].first_child; + self.places[place].first_child = Some(discr); + let old = self.projections.insert((place, TrackElem::Discriminant), discr); + assert!(old.is_none()); + + // Allocate a value slot since it doesn't have one. + assert!(self.places[discr].value_index.is_none()); + self.places[discr].value_index = Some(self.value_count.into()); + self.value_count += 1; + } + + if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ty::TypeAndMut { ty: ref_ty, .. }) = ty.kind() + && let ty::Slice(..) = ref_ty.kind() + { + assert!(self.places[place].value_index.is_none(), "slices are not scalars"); + + // Prepend new child to the linked list. + let len = self.places.push(PlaceInfo::new(Some(TrackElem::DerefLen))); + self.places[len].next_sibling = self.places[place].first_child; + self.places[place].first_child = Some(len); + + let old = self.projections.insert((place, TrackElem::DerefLen), len); + assert!(old.is_none()); + + // Allocate a value slot since it doesn't have one. + assert!( self.places[len].value_index.is_none() ); + self.places[len].value_index = Some(self.value_count.into()); + self.value_count += 1; } // Recurse with all fields of this place. - iter_fields(ty, tcx, ty::ParamEnv::reveal_all(), |variant, field, ty| { + iter_fields(ty, tcx, param_env, |variant, field, ty| { worklist.push_back(( place, variant.map(TrackElem::Variant), @@ -834,6 +845,11 @@ impl Map { self.find_extra(place, [TrackElem::Discriminant]) } + /// Locates the given place and applies `DerefLen`, if it exists in the tree. + pub fn find_len(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> { + self.find_extra(place, [TrackElem::DerefLen]) + } + /// Iterate over all direct children. pub fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ { Children::new(self, parent) @@ -914,6 +930,31 @@ impl Map { f(v) } } + + /// Invoke a function on each value in the given place and all descendants. + pub fn for_each_projection_value<O>( + &self, + root: PlaceIndex, + value: O, + project: &mut impl FnMut(TrackElem, &O) -> Option<O>, + f: &mut impl FnMut(PlaceIndex, &O), + ) { + // Fast path is there is nothing to do. + if self.inner_values[root].is_empty() { + return; + } + + if self.places[root].value_index.is_some() { + f(root, &value) + } + + for child in self.children(root) { + let elem = self.places[child].proj_elem.unwrap(); + if let Some(value) = project(elem, &value) { + self.for_each_projection_value(child, value, project, f); + } + } + } } /// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`]. @@ -985,6 +1026,8 @@ pub enum TrackElem { Field(FieldIdx), Variant(VariantIdx), Discriminant, + // Length of a slice. + DerefLen, } impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem { @@ -1124,6 +1167,9 @@ fn debug_with_context_rec<V: Debug + Eq>( format!("{}.{}", place_str, field.index()) } } + TrackElem::DerefLen => { + format!("Len(*{})", place_str) + } }; debug_with_context_rec(child, &child_place_str, new, old, map, f)?; } |