Skip to content

Remove the DefinitelyInitializedPlaces analysis. #133326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions compiler/rustc_mir_dataflow/src/framework/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,16 +230,3 @@ where
write!(f, "{}", ctxt.move_data().move_paths[*self])
}
}

impl<T, C> DebugWithContext<C> for crate::lattice::Dual<T>
where
T: DebugWithContext<C>,
{
fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.0).fmt_with(ctxt, f)
}

fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.0).fmt_diff_with(&old.0, ctxt, f)
}
}
108 changes: 2 additions & 106 deletions compiler/rustc_mir_dataflow/src/framework/lattice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
//!
//! ## `PartialOrd`
//!
//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`]
//! and [`MeetSemiLattice`] do not have [`PartialOrd`] as a supertrait. This
//! Given that it represents a partially ordered set, you may be surprised that [`JoinSemiLattice`]
//! does not have [`PartialOrd`] as a supertrait. This
//! is because most standard library types use lexicographic ordering instead of set inclusion for
//! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a
//! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The
Expand Down Expand Up @@ -58,23 +58,6 @@ pub trait JoinSemiLattice: Eq {
fn join(&mut self, other: &Self) -> bool;
}

/// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of
/// elements in the set.
///
/// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not
/// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both
/// so that they can be used with [`Dual`].
///
/// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum
/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
pub trait MeetSemiLattice: Eq {
/// Computes the greatest lower bound of two elements, storing the result in `self` and
/// returning `true` if `self` has changed.
///
/// The lattice meet operator is abbreviated as `∧`.
fn meet(&mut self, other: &Self) -> bool;
}

/// A set that has a "bottom" element, which is less than or equal to any other element.
pub trait HasBottom {
const BOTTOM: Self;
Expand Down Expand Up @@ -105,17 +88,6 @@ impl JoinSemiLattice for bool {
}
}

impl MeetSemiLattice for bool {
fn meet(&mut self, other: &Self) -> bool {
if let (true, false) = (*self, *other) {
*self = false;
return true;
}

false
}
}

impl HasBottom for bool {
const BOTTOM: Self = false;

Expand Down Expand Up @@ -145,18 +117,6 @@ impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> {
}
}

impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> {
fn meet(&mut self, other: &Self) -> bool {
assert_eq!(self.len(), other.len());

let mut changed = false;
for (a, b) in iter::zip(self, other) {
changed |= a.meet(b);
}
changed
}
}

/// A `BitSet` represents the lattice formed by the powerset of all possible values of
/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices,
/// one for each possible value of `T`.
Expand All @@ -166,60 +126,12 @@ impl<T: Idx> JoinSemiLattice for BitSet<T> {
}
}

impl<T: Idx> MeetSemiLattice for BitSet<T> {
fn meet(&mut self, other: &Self) -> bool {
self.intersect(other)
}
}

impl<T: Idx> JoinSemiLattice for ChunkedBitSet<T> {
fn join(&mut self, other: &Self) -> bool {
self.union(other)
}
}

impl<T: Idx> MeetSemiLattice for ChunkedBitSet<T> {
fn meet(&mut self, other: &Self) -> bool {
self.intersect(other)
}
}

/// The counterpart of a given semilattice `T` using the [inverse order].
///
/// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a
/// powerset has the empty set as its top element and the full set as its bottom element and uses
/// set *intersection* as its join operator.
///
/// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory)
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Dual<T>(pub T);

impl<T: Idx> BitSetExt<T> for Dual<BitSet<T>> {
fn contains(&self, elem: T) -> bool {
self.0.contains(elem)
}

fn union(&mut self, other: &HybridBitSet<T>) {
self.0.union(other);
}

fn subtract(&mut self, other: &HybridBitSet<T>) {
self.0.subtract(other);
}
}

impl<T: MeetSemiLattice> JoinSemiLattice for Dual<T> {
fn join(&mut self, other: &Self) -> bool {
self.0.meet(&other.0)
}
}

impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> {
fn meet(&mut self, other: &Self) -> bool {
self.0.join(&other.0)
}
}

/// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no
/// value of `T` is comparable with any other.
///
Expand Down Expand Up @@ -257,22 +169,6 @@ impl<T: Clone + Eq> JoinSemiLattice for FlatSet<T> {
}
}

impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> {
fn meet(&mut self, other: &Self) -> bool {
let result = match (&*self, other) {
(Self::Bottom, _) | (_, Self::Top) => return false,
(Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false,

(Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()),

_ => Self::Bottom,
};

*self = result;
true
}
}

impl<T> HasBottom for FlatSet<T> {
const BOTTOM: Self = Self::Bottom;

Expand Down
10 changes: 0 additions & 10 deletions compiler/rustc_mir_dataflow/src/framework/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,16 +378,6 @@ impl<T, S: GenKill<T>> GenKill<T> for MaybeReachable<S> {
}
}

impl<T: Idx> GenKill<T> for lattice::Dual<BitSet<T>> {
fn gen_(&mut self, elem: T) {
self.0.insert(elem);
}

fn kill(&mut self, elem: T) {
self.0.remove(elem);
}
}

// NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Effect {
Expand Down
146 changes: 9 additions & 137 deletions compiler/rustc_mir_dataflow/src/impls/initialized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::framework::SwitchIntEdgeEffects;
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
use crate::{
Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry,
drop_flag_effects_for_location, lattice, on_all_children_bits, on_lookup_result_bits,
drop_flag_effects_for_location, on_all_children_bits, on_lookup_result_bits,
};

/// `MaybeInitializedPlaces` tracks all places that might be
Expand Down Expand Up @@ -42,10 +42,10 @@ use crate::{
/// }
/// ```
///
/// To determine whether a place *must* be initialized at a
/// particular control-flow point, one can take the set-difference
/// between this data and the data from `MaybeUninitializedPlaces` at the
/// corresponding control-flow point.
/// 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.
///
/// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeUninitializedPlaces` yields the set of
Expand Down Expand Up @@ -117,10 +117,10 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
/// }
/// ```
///
/// To determine whether a place *must* be uninitialized at a
/// particular control-flow point, one can take the set-difference
/// between this data and the data from `MaybeInitializedPlaces` at the
/// corresponding control-flow point.
/// 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.
///
/// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeInitializedPlaces` yields the set of
Expand Down Expand Up @@ -170,57 +170,6 @@ impl<'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
}
}

/// `DefinitelyInitializedPlaces` tracks all places that are definitely
/// 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) { // definite-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}
///
/// } // { }
///
/// c = S; // { c }
/// }
/// ```
///
/// To determine whether a place *may* be uninitialized at a
/// particular control-flow point, one can take the set-complement
/// of this data.
///
/// Similarly, at a given `drop` statement, the set-difference between
/// this data and `MaybeInitializedPlaces` yields the set of places
/// that would require a dynamic drop-flag at that statement.
pub struct DefinitelyInitializedPlaces<'a, 'tcx> {
body: &'a Body<'tcx>,
move_data: &'a MoveData<'tcx>,
}

impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
pub fn new(body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
DefinitelyInitializedPlaces { body, move_data }
}
}

impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> {
self.move_data
}
}

/// `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`.
Expand Down Expand Up @@ -293,19 +242,6 @@ impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
}
}

impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
fn update_bits(
trans: &mut <Self as Analysis<'tcx>>::Domain,
path: MovePathIndex,
state: DropFlagState,
) {
match state {
DropFlagState::Absent => trans.kill(path),
DropFlagState::Present => trans.gen_(path),
}
}
}

impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
/// There can be many more `MovePathIndex` than there are locals in a MIR body.
/// We use a chunked bitset to avoid paying too high a memory footprint.
Expand Down Expand Up @@ -554,70 +490,6 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
}
}

impl<'a, 'tcx> Analysis<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
/// Use set intersection as the join operator.
type Domain = lattice::Dual<BitSet<MovePathIndex>>;

const NAME: &'static str = "definite_init";

fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
// bottom = initialized (start_block_effect counters this at outset)
lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len()))
}

// sets on_entry bits for Arg places
fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
state.0.clear();

drop_flag_effects_for_function_entry(self.body, self.move_data, |path, s| {
assert!(s == DropFlagState::Present);
state.0.insert(path);
});
}

fn apply_statement_effect(
&mut self,
trans: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
) {
drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
Self::update_bits(trans, path, s)
})
}

fn apply_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) -> TerminatorEdges<'mir, 'tcx> {
drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
Self::update_bits(trans, path, s)
});
terminator.edges()
}

fn apply_call_return_effect(
&mut self,
trans: &mut Self::Domain,
_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| {
trans.gen_(mpi);
},
);
});
}
}

impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
/// There can be many more `InitIndex` than there are locals in a MIR body.
/// We use a chunked bitset to avoid paying too high a memory footprint.
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_mir_dataflow/src/impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ mod storage_liveness;

pub use self::borrowed_locals::{MaybeBorrowedLocals, borrowed_locals};
pub use self::initialized::{
DefinitelyInitializedPlaces, EverInitializedPlaces, MaybeInitializedPlaces,
MaybeUninitializedPlaces,
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
};
pub use self::liveness::{
MaybeLiveLocals, MaybeTransitiveLiveLocals, TransferFunction as LivenessTransferFunction,
Expand Down
Loading
Loading