summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_dataflow/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /compiler/rustc_mir_dataflow/src
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-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')
-rw-r--r--compiler/rustc_mir_dataflow/src/elaborate_drops.rs13
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/direction.rs8
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/graphviz.rs12
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/visitor.rs12
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs13
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs8
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs1
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs150
-rw-r--r--compiler/rustc_mir_dataflow/src/rustc_peek.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/value_analysis.rs146
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)?;
}