2024-08-11 12:10:36 -04:00
|
|
|
use std::assert_matches::assert_matches;
|
|
|
|
|
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
use rustc_abi::VariantIdx;
|
2023-05-06 14:13:12 +00:00
|
|
|
use rustc_index::Idx;
|
2025-01-07 15:19:05 +00:00
|
|
|
use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
|
2024-04-29 13:56:41 +10:00
|
|
|
use rustc_middle::bug;
|
2025-02-14 09:54:01 +11:00
|
|
|
use rustc_middle::mir::{
|
|
|
|
|
self, Body, CallReturnPlaces, Location, SwitchTargetValue, TerminatorEdges,
|
|
|
|
|
};
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
use rustc_middle::ty::util::Discr;
|
2023-05-06 14:13:12 +00:00
|
|
|
use rustc_middle::ty::{self, TyCtxt};
|
2024-05-22 14:45:14 +10:00
|
|
|
use tracing::{debug, instrument};
|
2023-05-06 14:13:12 +00:00
|
|
|
|
2025-02-14 15:30:51 +11:00
|
|
|
use crate::drop_flag_effects::DropFlagState;
|
2023-05-06 14:13:12 +00:00
|
|
|
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
|
|
|
|
|
use crate::{
|
2024-10-10 11:46:29 +11:00
|
|
|
Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry,
|
2024-11-22 14:39:29 +11:00
|
|
|
drop_flag_effects_for_location, on_all_children_bits, on_lookup_result_bits,
|
2023-05-19 17:41:04 +00:00
|
|
|
};
|
2023-05-06 14:13:12 +00:00
|
|
|
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
// Used by both `MaybeInitializedPlaces` and `MaybeUninitializedPlaces`.
|
|
|
|
|
pub struct MaybePlacesSwitchIntData<'tcx> {
|
|
|
|
|
enum_place: mir::Place<'tcx>,
|
|
|
|
|
discriminants: Vec<(VariantIdx, Discr<'tcx>)>,
|
|
|
|
|
index: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'tcx> MaybePlacesSwitchIntData<'tcx> {
|
|
|
|
|
// The discriminant order in the `SwitchInt` targets should match the order yielded by
|
|
|
|
|
// `AdtDef::discriminants`. We rely on this to match each discriminant in the targets to its
|
|
|
|
|
// corresponding variant in linear time.
|
|
|
|
|
fn next_discr(&mut self, value: u128) -> VariantIdx {
|
|
|
|
|
// An out-of-bounds abort will occur if the discriminant ordering isn't as described above.
|
|
|
|
|
loop {
|
|
|
|
|
let (variant, discr) = self.discriminants[self.index];
|
|
|
|
|
self.index += 1;
|
|
|
|
|
if discr.val == value {
|
|
|
|
|
return variant;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-22 08:39:34 +11:00
|
|
|
impl<'tcx> MaybePlacesSwitchIntData<'tcx> {
|
|
|
|
|
fn new(
|
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
|
body: &Body<'tcx>,
|
|
|
|
|
block: mir::BasicBlock,
|
|
|
|
|
discr: &mir::Operand<'tcx>,
|
|
|
|
|
) -> Option<Self> {
|
|
|
|
|
let Some(discr) = discr.place() else { return None };
|
|
|
|
|
|
|
|
|
|
// Inspect a `SwitchInt`-terminated basic block to see if the condition of that `SwitchInt`
|
|
|
|
|
// is an enum discriminant.
|
|
|
|
|
//
|
|
|
|
|
// We expect such blocks to have a call to `discriminant` as their last statement like so:
|
|
|
|
|
// ```text
|
|
|
|
|
// ...
|
|
|
|
|
// _42 = discriminant(_1)
|
|
|
|
|
// SwitchInt(_42, ..)
|
|
|
|
|
// ```
|
|
|
|
|
// If the basic block matches this pattern, this function gathers the place corresponding
|
|
|
|
|
// to the enum (`_1` in the example above) as well as the discriminants.
|
|
|
|
|
let block_data = &body[block];
|
|
|
|
|
for statement in block_data.statements.iter().rev() {
|
|
|
|
|
match statement.kind {
|
|
|
|
|
mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(enum_place)))
|
|
|
|
|
if lhs == discr =>
|
|
|
|
|
{
|
|
|
|
|
match enum_place.ty(body, tcx).ty.kind() {
|
|
|
|
|
ty::Adt(enum_def, _) => {
|
|
|
|
|
return Some(MaybePlacesSwitchIntData {
|
|
|
|
|
enum_place,
|
|
|
|
|
discriminants: enum_def.discriminants(tcx).collect(),
|
|
|
|
|
index: 0,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `Rvalue::Discriminant` is also used to get the active yield point for a
|
|
|
|
|
// coroutine, but we do not need edge-specific effects in that case. This
|
|
|
|
|
// may change in the future.
|
|
|
|
|
ty::Coroutine(..) => break,
|
|
|
|
|
|
|
|
|
|
t => bug!("`discriminant` called on unexpected type {:?}", t),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mir::StatementKind::Coverage(_) => continue,
|
|
|
|
|
_ => break,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-06 14:13:12 +00:00
|
|
|
/// `MaybeInitializedPlaces` tracks all places that might be
|
|
|
|
|
/// initialized upon reaching a particular point in the control flow
|
|
|
|
|
/// for a function.
|
|
|
|
|
///
|
|
|
|
|
/// For example, in code like the following, we have corresponding
|
|
|
|
|
/// dataflow information shown in the right-hand comments.
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// struct S;
|
|
|
|
|
/// fn foo(pred: bool) { // maybe-init:
|
|
|
|
|
/// // {}
|
|
|
|
|
/// let a = S; let mut b = S; let c; let d; // {a, b}
|
|
|
|
|
///
|
|
|
|
|
/// if pred {
|
|
|
|
|
/// drop(a); // { b}
|
|
|
|
|
/// b = S; // { b}
|
|
|
|
|
///
|
|
|
|
|
/// } else {
|
|
|
|
|
/// drop(b); // {a}
|
|
|
|
|
/// d = S; // {a, d}
|
|
|
|
|
///
|
|
|
|
|
/// } // {a, b, d}
|
|
|
|
|
///
|
|
|
|
|
/// c = S; // {a, b, c, d}
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
2024-11-22 14:39:29 +11:00
|
|
|
/// To determine whether a place is *definitely* initialized at a
|
|
|
|
|
/// particular control-flow point, one can take the set-complement
|
|
|
|
|
/// of the data from `MaybeUninitializedPlaces` at the corresponding
|
|
|
|
|
/// control-flow point.
|
2023-05-06 14:13:12 +00:00
|
|
|
///
|
|
|
|
|
/// Similarly, at a given `drop` statement, the set-intersection
|
|
|
|
|
/// between this data and `MaybeUninitializedPlaces` yields the set of
|
|
|
|
|
/// places that would require a dynamic drop-flag at that statement.
|
2024-09-06 09:18:17 +10:00
|
|
|
pub struct MaybeInitializedPlaces<'a, 'tcx> {
|
2023-05-06 14:13:12 +00:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2024-09-06 09:18:17 +10:00
|
|
|
body: &'a Body<'tcx>,
|
2024-07-24 15:58:34 -04:00
|
|
|
move_data: &'a MoveData<'tcx>,
|
2023-05-06 07:57:05 +00:00
|
|
|
skip_unreachable_unwind: bool,
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-06 09:18:17 +10:00
|
|
|
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
|
|
|
|
|
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
|
2024-07-24 15:58:34 -04:00
|
|
|
MaybeInitializedPlaces { tcx, body, move_data, skip_unreachable_unwind: false }
|
2023-05-06 07:57:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn skipping_unreachable_unwind(mut self) -> Self {
|
|
|
|
|
self.skip_unreachable_unwind = true;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_unwind_dead(
|
|
|
|
|
&self,
|
|
|
|
|
place: mir::Place<'tcx>,
|
2024-11-26 14:17:12 +11:00
|
|
|
state: &<Self as Analysis<'tcx>>::Domain,
|
2023-05-06 07:57:05 +00:00
|
|
|
) -> bool {
|
|
|
|
|
if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) {
|
|
|
|
|
let mut maybe_live = false;
|
2023-11-24 06:26:15 +11:00
|
|
|
on_all_children_bits(self.move_data(), path, |child| {
|
2023-05-06 07:57:05 +00:00
|
|
|
maybe_live |= state.contains(child);
|
|
|
|
|
});
|
|
|
|
|
!maybe_live
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 09:18:17 +10:00
|
|
|
impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
|
2023-05-06 14:13:12 +00:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> {
|
2024-07-24 15:58:34 -04:00
|
|
|
self.move_data
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// `MaybeUninitializedPlaces` tracks all places that might be
|
|
|
|
|
/// uninitialized upon reaching a particular point in the control flow
|
|
|
|
|
/// for a function.
|
|
|
|
|
///
|
|
|
|
|
/// For example, in code like the following, we have corresponding
|
|
|
|
|
/// dataflow information shown in the right-hand comments.
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// struct S;
|
|
|
|
|
/// fn foo(pred: bool) { // maybe-uninit:
|
|
|
|
|
/// // {a, b, c, d}
|
|
|
|
|
/// let a = S; let mut b = S; let c; let d; // { c, d}
|
|
|
|
|
///
|
|
|
|
|
/// if pred {
|
|
|
|
|
/// drop(a); // {a, c, d}
|
|
|
|
|
/// b = S; // {a, c, d}
|
|
|
|
|
///
|
|
|
|
|
/// } else {
|
|
|
|
|
/// drop(b); // { b, c, d}
|
|
|
|
|
/// d = S; // { b, c }
|
|
|
|
|
///
|
|
|
|
|
/// } // {a, b, c, d}
|
|
|
|
|
///
|
|
|
|
|
/// c = S; // {a, b, d}
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
2024-11-22 14:39:29 +11:00
|
|
|
/// To determine whether a place is *definitely* uninitialized at a
|
|
|
|
|
/// particular control-flow point, one can take the set-complement
|
|
|
|
|
/// of the data from `MaybeInitializedPlaces` at the corresponding
|
|
|
|
|
/// control-flow point.
|
2023-05-06 14:13:12 +00:00
|
|
|
///
|
|
|
|
|
/// Similarly, at a given `drop` statement, the set-intersection
|
|
|
|
|
/// between this data and `MaybeInitializedPlaces` yields the set of
|
|
|
|
|
/// places that would require a dynamic drop-flag at that statement.
|
2024-09-06 09:18:17 +10:00
|
|
|
pub struct MaybeUninitializedPlaces<'a, 'tcx> {
|
2023-05-06 14:13:12 +00:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2024-09-06 09:18:17 +10:00
|
|
|
body: &'a Body<'tcx>,
|
2024-07-24 15:58:34 -04:00
|
|
|
move_data: &'a MoveData<'tcx>,
|
2023-05-06 14:13:12 +00:00
|
|
|
|
|
|
|
|
mark_inactive_variants_as_uninit: bool,
|
2025-01-07 15:19:05 +00:00
|
|
|
skip_unreachable_unwind: DenseBitSet<mir::BasicBlock>,
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-06 09:18:17 +10:00
|
|
|
impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
|
|
|
|
|
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
|
2023-05-06 07:57:05 +00:00
|
|
|
MaybeUninitializedPlaces {
|
|
|
|
|
tcx,
|
|
|
|
|
body,
|
2024-07-24 15:58:34 -04:00
|
|
|
move_data,
|
2023-05-06 07:57:05 +00:00
|
|
|
mark_inactive_variants_as_uninit: false,
|
2025-01-07 15:19:05 +00:00
|
|
|
skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()),
|
2023-05-06 07:57:05 +00:00
|
|
|
}
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an
|
|
|
|
|
/// enum discriminant.
|
|
|
|
|
///
|
|
|
|
|
/// This is correct in a vacuum but is not the default because it causes problems in the borrow
|
|
|
|
|
/// checker, where this information gets propagated along `FakeEdge`s.
|
|
|
|
|
pub fn mark_inactive_variants_as_uninit(mut self) -> Self {
|
|
|
|
|
self.mark_inactive_variants_as_uninit = true;
|
|
|
|
|
self
|
|
|
|
|
}
|
2023-05-06 07:57:05 +00:00
|
|
|
|
|
|
|
|
pub fn skipping_unreachable_unwind(
|
|
|
|
|
mut self,
|
2025-01-07 15:19:05 +00:00
|
|
|
unreachable_unwind: DenseBitSet<mir::BasicBlock>,
|
2023-05-06 07:57:05 +00:00
|
|
|
) -> Self {
|
|
|
|
|
self.skip_unreachable_unwind = unreachable_unwind;
|
|
|
|
|
self
|
|
|
|
|
}
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-06 09:18:17 +10:00
|
|
|
impl<'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
2023-05-06 14:13:12 +00:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> {
|
2024-07-24 15:58:34 -04:00
|
|
|
self.move_data
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// `EverInitializedPlaces` tracks all places that might have ever been
|
|
|
|
|
/// initialized upon reaching a particular point in the control flow
|
|
|
|
|
/// for a function, without an intervening `StorageDead`.
|
|
|
|
|
///
|
|
|
|
|
/// This dataflow is used to determine if an immutable local variable may
|
|
|
|
|
/// be assigned to.
|
|
|
|
|
///
|
|
|
|
|
/// For example, in code like the following, we have corresponding
|
|
|
|
|
/// dataflow information shown in the right-hand comments.
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// struct S;
|
|
|
|
|
/// fn foo(pred: bool) { // ever-init:
|
|
|
|
|
/// // { }
|
|
|
|
|
/// let a = S; let mut b = S; let c; let d; // {a, b }
|
|
|
|
|
///
|
|
|
|
|
/// if pred {
|
|
|
|
|
/// drop(a); // {a, b, }
|
|
|
|
|
/// b = S; // {a, b, }
|
|
|
|
|
///
|
|
|
|
|
/// } else {
|
|
|
|
|
/// drop(b); // {a, b, }
|
|
|
|
|
/// d = S; // {a, b, d }
|
|
|
|
|
///
|
|
|
|
|
/// } // {a, b, d }
|
|
|
|
|
///
|
|
|
|
|
/// c = S; // {a, b, c, d }
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
2024-09-06 09:18:17 +10:00
|
|
|
pub struct EverInitializedPlaces<'a, 'tcx> {
|
|
|
|
|
body: &'a Body<'tcx>,
|
2024-07-24 15:58:34 -04:00
|
|
|
move_data: &'a MoveData<'tcx>,
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-06 09:18:17 +10:00
|
|
|
impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
|
|
|
|
|
pub fn new(body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
|
2024-07-24 15:58:34 -04:00
|
|
|
EverInitializedPlaces { body, move_data }
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 09:18:17 +10:00
|
|
|
impl<'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
2023-05-06 14:13:12 +00:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> {
|
2024-07-24 15:58:34 -04:00
|
|
|
self.move_data
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 09:18:17 +10:00
|
|
|
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
|
2023-05-06 14:13:12 +00:00
|
|
|
fn update_bits(
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut <Self as Analysis<'tcx>>::Domain,
|
2023-05-06 14:13:12 +00:00
|
|
|
path: MovePathIndex,
|
2024-11-26 14:30:49 +11:00
|
|
|
dfstate: DropFlagState,
|
2023-05-06 14:13:12 +00:00
|
|
|
) {
|
2024-11-26 14:30:49 +11:00
|
|
|
match dfstate {
|
|
|
|
|
DropFlagState::Absent => state.kill(path),
|
|
|
|
|
DropFlagState::Present => state.gen_(path),
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 09:18:17 +10:00
|
|
|
impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
|
2023-05-06 14:13:12 +00:00
|
|
|
fn update_bits(
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut <Self as Analysis<'tcx>>::Domain,
|
2023-05-06 14:13:12 +00:00
|
|
|
path: MovePathIndex,
|
2024-11-26 14:30:49 +11:00
|
|
|
dfstate: DropFlagState,
|
2023-05-06 14:13:12 +00:00
|
|
|
) {
|
2024-11-26 14:30:49 +11:00
|
|
|
match dfstate {
|
|
|
|
|
DropFlagState::Absent => state.gen_(path),
|
|
|
|
|
DropFlagState::Present => state.kill(path),
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-10 11:46:29 +11:00
|
|
|
impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
2023-09-27 17:16:30 +00:00
|
|
|
/// There can be many more `MovePathIndex` than there are locals in a MIR body.
|
2024-12-05 14:20:05 +11:00
|
|
|
/// We use a mixed bitset to avoid paying too high a memory footprint.
|
|
|
|
|
type Domain = MaybeReachable<MixedBitSet<MovePathIndex>>;
|
2023-09-27 17:16:30 +00:00
|
|
|
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
|
|
|
|
|
|
2023-05-06 14:13:12 +00:00
|
|
|
const NAME: &'static str = "maybe_init";
|
|
|
|
|
|
|
|
|
|
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
|
|
|
|
|
// bottom = uninitialized
|
2023-05-19 17:41:04 +00:00
|
|
|
MaybeReachable::Unreachable
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
|
2023-05-19 17:41:04 +00:00
|
|
|
*state =
|
2024-12-05 14:20:05 +11:00
|
|
|
MaybeReachable::Reachable(MixedBitSet::new_empty(self.move_data().move_paths.len()));
|
2024-07-24 15:58:34 -04:00
|
|
|
drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
|
2023-05-06 14:13:12 +00:00
|
|
|
assert!(s == DropFlagState::Present);
|
2024-07-14 13:50:27 -04:00
|
|
|
state.gen_(path);
|
2023-05-06 14:13:12 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 15:31:02 +11:00
|
|
|
fn apply_primary_statement_effect(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut Self::Domain,
|
2023-05-06 14:13:12 +00:00
|
|
|
statement: &mir::Statement<'tcx>,
|
|
|
|
|
location: Location,
|
|
|
|
|
) {
|
2024-07-24 15:58:34 -04:00
|
|
|
drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
|
2024-11-26 14:30:49 +11:00
|
|
|
Self::update_bits(state, path, s)
|
2023-05-06 14:13:12 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Mark all places as "maybe init" if they are mutably borrowed. See #90752.
|
|
|
|
|
if self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration
|
|
|
|
|
&& let Some((_, rvalue)) = statement.kind.as_assign()
|
|
|
|
|
&& let mir::Rvalue::Ref(_, mir::BorrowKind::Mut { .. }, place)
|
|
|
|
|
// FIXME: Does `&raw const foo` allow mutation? See #90413.
|
2024-08-12 10:57:57 +02:00
|
|
|
| mir::Rvalue::RawPtr(_, place) = rvalue
|
2023-05-06 14:13:12 +00:00
|
|
|
&& let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref())
|
|
|
|
|
{
|
2023-11-24 06:26:15 +11:00
|
|
|
on_all_children_bits(self.move_data(), mpi, |child| {
|
2024-11-26 14:30:49 +11:00
|
|
|
state.gen_(child);
|
2023-05-06 14:13:12 +00:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 15:31:02 +11:00
|
|
|
fn apply_primary_terminator_effect<'mir>(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
2023-05-07 10:20:43 +00:00
|
|
|
state: &mut Self::Domain,
|
|
|
|
|
terminator: &'mir mir::Terminator<'tcx>,
|
2023-05-06 14:13:12 +00:00
|
|
|
location: Location,
|
2023-05-27 16:27:04 +00:00
|
|
|
) -> TerminatorEdges<'mir, 'tcx> {
|
2024-11-22 06:20:53 +11:00
|
|
|
// Note: `edges` must be computed first because `drop_flag_effects_for_location` can change
|
|
|
|
|
// the result of `is_unwind_dead`.
|
2023-05-06 07:57:05 +00:00
|
|
|
let mut edges = terminator.edges();
|
|
|
|
|
if self.skip_unreachable_unwind
|
2024-08-26 16:45:15 +03:00
|
|
|
&& let mir::TerminatorKind::Drop {
|
|
|
|
|
target,
|
|
|
|
|
unwind,
|
|
|
|
|
place,
|
|
|
|
|
replace: _,
|
|
|
|
|
drop: _,
|
|
|
|
|
async_fut: _,
|
|
|
|
|
} = terminator.kind
|
2023-05-06 07:57:05 +00:00
|
|
|
&& matches!(unwind, mir::UnwindAction::Cleanup(_))
|
|
|
|
|
&& self.is_unwind_dead(place, state)
|
|
|
|
|
{
|
2023-05-27 16:27:04 +00:00
|
|
|
edges = TerminatorEdges::Single(target);
|
2023-05-06 07:57:05 +00:00
|
|
|
}
|
2024-07-24 15:58:34 -04:00
|
|
|
drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
|
2023-05-07 10:20:43 +00:00
|
|
|
Self::update_bits(state, path, s)
|
2023-05-06 14:13:12 +00:00
|
|
|
});
|
2023-05-06 07:57:05 +00:00
|
|
|
edges
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-10 09:49:33 +11:00
|
|
|
fn apply_call_return_effect(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut Self::Domain,
|
2023-05-06 14:13:12 +00:00
|
|
|
_block: mir::BasicBlock,
|
|
|
|
|
return_places: CallReturnPlaces<'_, 'tcx>,
|
|
|
|
|
) {
|
|
|
|
|
return_places.for_each(|place| {
|
|
|
|
|
// when a call returns successfully, that means we need to set
|
|
|
|
|
// the bits for that dest_place to 1 (initialized).
|
|
|
|
|
on_lookup_result_bits(
|
|
|
|
|
self.move_data(),
|
|
|
|
|
self.move_data().rev_lookup.find(place.as_ref()),
|
|
|
|
|
|mpi| {
|
2024-11-26 14:30:49 +11:00
|
|
|
state.gen_(mpi);
|
2023-05-06 14:13:12 +00:00
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
fn get_switch_int_data(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
|
|
|
|
block: mir::BasicBlock,
|
|
|
|
|
discr: &mir::Operand<'tcx>,
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
) -> Option<Self::SwitchIntData> {
|
2023-05-06 14:13:12 +00:00
|
|
|
if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
return None;
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-22 08:39:34 +11:00
|
|
|
MaybePlacesSwitchIntData::new(self.tcx, self.body, block, discr)
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
}
|
2023-05-06 14:13:12 +00:00
|
|
|
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
fn apply_switch_int_edge_effect(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut Self::SwitchIntData,
|
|
|
|
|
state: &mut Self::Domain,
|
2025-02-14 13:23:00 +11:00
|
|
|
value: SwitchTargetValue,
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
) {
|
2025-02-14 13:23:00 +11:00
|
|
|
if let SwitchTargetValue::Normal(value) = value {
|
2023-05-06 14:13:12 +00:00
|
|
|
// Kill all move paths that correspond to variants we know to be inactive along this
|
|
|
|
|
// particular outgoing edge of a `SwitchInt`.
|
|
|
|
|
drop_flag_effects::on_all_inactive_variants(
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
self.move_data,
|
|
|
|
|
data.enum_place,
|
|
|
|
|
data.next_discr(value),
|
2024-11-26 14:30:49 +11:00
|
|
|
|mpi| state.kill(mpi),
|
2023-05-06 14:13:12 +00:00
|
|
|
);
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
}
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 14:02:10 +11:00
|
|
|
/// There can be many more `MovePathIndex` than there are locals in a MIR body.
|
|
|
|
|
/// We use a mixed bitset to avoid paying too high a memory footprint.
|
|
|
|
|
pub type MaybeUninitializedPlacesDomain = MixedBitSet<MovePathIndex>;
|
|
|
|
|
|
2024-10-10 11:46:29 +11:00
|
|
|
impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
2024-11-26 14:02:10 +11:00
|
|
|
type Domain = MaybeUninitializedPlacesDomain;
|
2023-05-06 14:13:12 +00:00
|
|
|
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
type SwitchIntData = MaybePlacesSwitchIntData<'tcx>;
|
|
|
|
|
|
2023-05-06 14:13:12 +00:00
|
|
|
const NAME: &'static str = "maybe_uninit";
|
|
|
|
|
|
|
|
|
|
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
|
2024-11-26 13:04:28 +11:00
|
|
|
// bottom = initialized (`initialize_start_block` overwrites this on first entry)
|
2024-12-05 14:20:05 +11:00
|
|
|
MixedBitSet::new_empty(self.move_data().move_paths.len())
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-26 14:30:49 +11:00
|
|
|
// sets state bits for Arg places
|
2023-05-06 14:13:12 +00:00
|
|
|
fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
|
|
|
|
|
// set all bits to 1 (uninit) before gathering counter-evidence
|
|
|
|
|
state.insert_all();
|
|
|
|
|
|
2024-07-24 15:58:34 -04:00
|
|
|
drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
|
2023-05-06 14:13:12 +00:00
|
|
|
assert!(s == DropFlagState::Present);
|
|
|
|
|
state.remove(path);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 15:31:02 +11:00
|
|
|
fn apply_primary_statement_effect(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut Self::Domain,
|
2023-05-06 14:13:12 +00:00
|
|
|
_statement: &mir::Statement<'tcx>,
|
|
|
|
|
location: Location,
|
|
|
|
|
) {
|
2024-07-24 15:58:34 -04:00
|
|
|
drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
|
2024-11-26 14:30:49 +11:00
|
|
|
Self::update_bits(state, path, s)
|
2023-05-06 14:13:12 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a
|
|
|
|
|
// mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 15:31:02 +11:00
|
|
|
fn apply_primary_terminator_effect<'mir>(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut Self::Domain,
|
2023-05-07 10:20:43 +00:00
|
|
|
terminator: &'mir mir::Terminator<'tcx>,
|
2023-05-06 14:13:12 +00:00
|
|
|
location: Location,
|
2023-05-27 16:27:04 +00:00
|
|
|
) -> TerminatorEdges<'mir, 'tcx> {
|
2024-07-24 15:58:34 -04:00
|
|
|
drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
|
2024-11-26 14:30:49 +11:00
|
|
|
Self::update_bits(state, path, s)
|
2023-05-06 14:13:12 +00:00
|
|
|
});
|
2023-05-06 07:57:05 +00:00
|
|
|
if self.skip_unreachable_unwind.contains(location.block) {
|
|
|
|
|
let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { bug!() };
|
2024-08-11 12:10:36 -04:00
|
|
|
assert_matches!(unwind, mir::UnwindAction::Cleanup(_));
|
2023-05-27 16:27:04 +00:00
|
|
|
TerminatorEdges::Single(target)
|
2023-05-06 07:57:05 +00:00
|
|
|
} else {
|
|
|
|
|
terminator.edges()
|
|
|
|
|
}
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-10 09:49:33 +11:00
|
|
|
fn apply_call_return_effect(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut Self::Domain,
|
2023-05-06 14:13:12 +00:00
|
|
|
_block: mir::BasicBlock,
|
|
|
|
|
return_places: CallReturnPlaces<'_, 'tcx>,
|
|
|
|
|
) {
|
|
|
|
|
return_places.for_each(|place| {
|
|
|
|
|
// when a call returns successfully, that means we need to set
|
|
|
|
|
// the bits for that dest_place to 0 (initialized).
|
|
|
|
|
on_lookup_result_bits(
|
|
|
|
|
self.move_data(),
|
|
|
|
|
self.move_data().rev_lookup.find(place.as_ref()),
|
|
|
|
|
|mpi| {
|
2024-11-26 14:30:49 +11:00
|
|
|
state.kill(mpi);
|
2023-05-06 14:13:12 +00:00
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
fn get_switch_int_data(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
|
|
|
|
block: mir::BasicBlock,
|
|
|
|
|
discr: &mir::Operand<'tcx>,
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
) -> Option<Self::SwitchIntData> {
|
2023-05-06 14:13:12 +00:00
|
|
|
if !self.tcx.sess.opts.unstable_opts.precise_enum_drop_elaboration {
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
return None;
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !self.mark_inactive_variants_as_uninit {
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
return None;
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-22 08:39:34 +11:00
|
|
|
MaybePlacesSwitchIntData::new(self.tcx, self.body, block, discr)
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
}
|
2023-05-06 14:13:12 +00:00
|
|
|
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
fn apply_switch_int_edge_effect(
|
|
|
|
|
&mut self,
|
|
|
|
|
data: &mut Self::SwitchIntData,
|
|
|
|
|
state: &mut Self::Domain,
|
2025-02-14 13:23:00 +11:00
|
|
|
value: SwitchTargetValue,
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
) {
|
2025-02-14 13:23:00 +11:00
|
|
|
if let SwitchTargetValue::Normal(value) = value {
|
2023-05-06 14:13:12 +00:00
|
|
|
// Mark all move paths that correspond to variants other than this one as maybe
|
|
|
|
|
// uninitialized (in reality, they are *definitely* uninitialized).
|
|
|
|
|
drop_flag_effects::on_all_inactive_variants(
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
self.move_data,
|
|
|
|
|
data.enum_place,
|
|
|
|
|
data.next_discr(value),
|
2024-11-26 14:30:49 +11:00
|
|
|
|mpi| state.gen_(mpi),
|
2023-05-06 14:13:12 +00:00
|
|
|
);
|
Simplify dataflow `SwitchInt` handling.
Current `SwitchInt` handling has complicated control flow.
- The dataflow engine calls `Analysis::apply_switch_int_edge_effects`,
passing in an "applier" that impls `SwitchIntEdgeEffects`.
- `apply_switch_int_edge_effects` possibly calls `apply` on the applier,
passing it a closure.
- The `apply` method calls the closure on each `SwitchInt` edge.
- The closure operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the applier
(which came from the engine), to the closure (which came from the
analysis). It took me a while to work this out.
This commit changes to a simpler structure that maintains the important
characteristics.
- The dataflow engine calls `Analysis::get_switch_int_data`.
- `get_switch_int_data` returns an `Option<Self::SwitchIntData>` value.
- If that returned value was `Some`, the dataflow engine calls
`Analysis::apply_switch_int_edge_effect` on each edge, passing the
`Self::SwitchIntData` value.
- `Analysis::apply_switch_int_edge_effect` operates on the edge.
I.e. control flow goes from the engine, to the analysis, to the
engine, to the analysis.
Added:
- The `Analysis::SwitchIntData` assoc type and the
`Analysis::get_switch_int_data` method. Both only need to be
defined by analyses that look at `SwitchInt` terminators.
- The `MaybePlacesSwitchIntData` struct, which has three fields.
Changes:
- `Analysis::apply_switch_int_edge_effects` becomes
`Analysis::apply_switch_int_edge_effect`, which is a little simpler
because it's dealing with a single edge instead of all edges.
Removed:
- The `SwitchIntEdgeEffects` trait, and its two impls:
`BackwardSwitchIntEdgeEffectsApplier` (which has six fields) and
`ForwardSwitchIntEdgeEffectsApplier` structs (which has four fields).
- The closure.
The new structure is more concise and simpler.
2024-11-21 10:20:30 +11:00
|
|
|
}
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 14:02:10 +11:00
|
|
|
/// There can be many more `InitIndex` than there are locals in a MIR body.
|
|
|
|
|
/// We use a mixed bitset to avoid paying too high a memory footprint.
|
|
|
|
|
pub type EverInitializedPlacesDomain = MixedBitSet<InitIndex>;
|
|
|
|
|
|
2024-10-10 11:46:29 +11:00
|
|
|
impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
2024-11-26 14:02:10 +11:00
|
|
|
type Domain = EverInitializedPlacesDomain;
|
2023-05-06 14:13:12 +00:00
|
|
|
|
|
|
|
|
const NAME: &'static str = "ever_init";
|
|
|
|
|
|
|
|
|
|
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
|
|
|
|
|
// bottom = no initialized variables by default
|
2024-12-05 14:20:05 +11:00
|
|
|
MixedBitSet::new_empty(self.move_data().inits.len())
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
|
|
|
|
|
for arg_init in 0..body.arg_count {
|
|
|
|
|
state.insert(InitIndex::new(arg_init));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 14:30:49 +11:00
|
|
|
#[instrument(skip(self, state), level = "debug")]
|
2024-11-26 15:31:02 +11:00
|
|
|
fn apply_primary_statement_effect(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut Self::Domain,
|
2023-05-06 14:13:12 +00:00
|
|
|
stmt: &mir::Statement<'tcx>,
|
|
|
|
|
location: Location,
|
|
|
|
|
) {
|
|
|
|
|
let move_data = self.move_data();
|
|
|
|
|
let init_path_map = &move_data.init_path_map;
|
|
|
|
|
let init_loc_map = &move_data.init_loc_map;
|
|
|
|
|
let rev_lookup = &move_data.rev_lookup;
|
|
|
|
|
|
2024-07-19 11:51:21 -04:00
|
|
|
debug!("initializes move_indexes {:?}", init_loc_map[location]);
|
2024-11-26 14:30:49 +11:00
|
|
|
state.gen_all(init_loc_map[location].iter().copied());
|
2023-05-06 14:13:12 +00:00
|
|
|
|
|
|
|
|
if let mir::StatementKind::StorageDead(local) = stmt.kind {
|
|
|
|
|
// End inits for StorageDead, so that an immutable variable can
|
|
|
|
|
// be reinitialized on the next iteration of the loop.
|
2023-10-12 17:19:19 +00:00
|
|
|
if let Some(move_path_index) = rev_lookup.find_local(local) {
|
|
|
|
|
debug!(
|
|
|
|
|
"clears the ever initialized status of {:?}",
|
|
|
|
|
init_path_map[move_path_index]
|
|
|
|
|
);
|
2024-11-26 14:30:49 +11:00
|
|
|
state.kill_all(init_path_map[move_path_index].iter().copied());
|
2023-10-12 17:19:19 +00:00
|
|
|
}
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 14:30:49 +11:00
|
|
|
#[instrument(skip(self, state, terminator), level = "debug")]
|
2024-11-26 15:31:02 +11:00
|
|
|
fn apply_primary_terminator_effect<'mir>(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut Self::Domain,
|
2023-05-07 10:20:43 +00:00
|
|
|
terminator: &'mir mir::Terminator<'tcx>,
|
2023-05-06 14:13:12 +00:00
|
|
|
location: Location,
|
2023-05-27 16:27:04 +00:00
|
|
|
) -> TerminatorEdges<'mir, 'tcx> {
|
2023-05-06 14:13:12 +00:00
|
|
|
let (body, move_data) = (self.body, self.move_data());
|
|
|
|
|
let term = body[location.block].terminator();
|
|
|
|
|
let init_loc_map = &move_data.init_loc_map;
|
|
|
|
|
debug!(?term);
|
|
|
|
|
debug!("initializes move_indexes {:?}", init_loc_map[location]);
|
2024-11-26 14:30:49 +11:00
|
|
|
state.gen_all(
|
2023-05-06 14:13:12 +00:00
|
|
|
init_loc_map[location]
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|init_index| {
|
|
|
|
|
move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
|
|
|
|
|
})
|
|
|
|
|
.copied(),
|
|
|
|
|
);
|
2023-05-07 10:20:43 +00:00
|
|
|
terminator.edges()
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-10 09:49:33 +11:00
|
|
|
fn apply_call_return_effect(
|
2023-05-06 14:13:12 +00:00
|
|
|
&mut self,
|
2024-11-26 14:30:49 +11:00
|
|
|
state: &mut Self::Domain,
|
2023-05-06 14:13:12 +00:00
|
|
|
block: mir::BasicBlock,
|
|
|
|
|
_return_places: CallReturnPlaces<'_, 'tcx>,
|
|
|
|
|
) {
|
|
|
|
|
let move_data = self.move_data();
|
|
|
|
|
let init_loc_map = &move_data.init_loc_map;
|
|
|
|
|
|
|
|
|
|
let call_loc = self.body.terminator_loc(block);
|
|
|
|
|
for init_index in &init_loc_map[call_loc] {
|
2024-11-26 14:30:49 +11:00
|
|
|
state.gen_(*init_index);
|
2023-05-06 14:13:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|