Rollup merge of #133938 - nnethercote:rustc_mir_dataflow-renamings, r=oli-obk
`rustc_mir_dataflow` cleanups, including some renamings Some opinionated commits in this collection, let's see how we go. r? `@cjgillot`
This commit is contained in:
commit
c6ebe6fe3b
23 changed files with 362 additions and 424 deletions
|
@ -179,15 +179,15 @@ where
|
|||
/// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is
|
||||
/// applied.
|
||||
///
|
||||
/// The "before" effect at the target location *will be* applied.
|
||||
/// The "early" effect at the target location *will be* applied.
|
||||
pub fn seek_before_primary_effect(&mut self, target: Location) {
|
||||
self.seek_after(target, Effect::Before)
|
||||
self.seek_after(target, Effect::Early)
|
||||
}
|
||||
|
||||
/// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is
|
||||
/// applied.
|
||||
///
|
||||
/// The "before" effect at the target location will be applied as well.
|
||||
/// The "early" effect at the target location will be applied as well.
|
||||
pub fn seek_after_primary_effect(&mut self, target: Location) {
|
||||
self.seek_after(target, Effect::Primary)
|
||||
}
|
||||
|
@ -222,12 +222,12 @@ where
|
|||
#[rustfmt::skip]
|
||||
let next_effect = if A::Direction::IS_FORWARD {
|
||||
self.pos.curr_effect_index.map_or_else(
|
||||
|| Effect::Before.at_index(0),
|
||||
|| Effect::Early.at_index(0),
|
||||
EffectIndex::next_in_forward_order,
|
||||
)
|
||||
} else {
|
||||
self.pos.curr_effect_index.map_or_else(
|
||||
|| Effect::Before.at_index(block_data.statements.len()),
|
||||
|| Effect::Early.at_index(block_data.statements.len()),
|
||||
EffectIndex::next_in_backward_order,
|
||||
)
|
||||
};
|
||||
|
|
|
@ -66,12 +66,12 @@ impl Direction for Backward {
|
|||
{
|
||||
let terminator = block_data.terminator();
|
||||
let location = Location { block, statement_index: block_data.statements.len() };
|
||||
analysis.apply_before_terminator_effect(state, terminator, location);
|
||||
analysis.apply_terminator_effect(state, terminator, location);
|
||||
analysis.apply_early_terminator_effect(state, terminator, location);
|
||||
analysis.apply_primary_terminator_effect(state, terminator, location);
|
||||
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
|
||||
let location = Location { block, statement_index };
|
||||
analysis.apply_before_statement_effect(state, statement, location);
|
||||
analysis.apply_statement_effect(state, statement, location);
|
||||
analysis.apply_early_statement_effect(state, statement, location);
|
||||
analysis.apply_primary_statement_effect(state, statement, location);
|
||||
}
|
||||
|
||||
let exit_state = state;
|
||||
|
@ -159,14 +159,14 @@ impl Direction for Backward {
|
|||
let location = Location { block, statement_index: from.statement_index };
|
||||
let terminator = block_data.terminator();
|
||||
|
||||
if from.effect == Effect::Before {
|
||||
analysis.apply_before_terminator_effect(state, terminator, location);
|
||||
if to == Effect::Before.at_index(terminator_index) {
|
||||
if from.effect == Effect::Early {
|
||||
analysis.apply_early_terminator_effect(state, terminator, location);
|
||||
if to == Effect::Early.at_index(terminator_index) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
analysis.apply_terminator_effect(state, terminator, location);
|
||||
analysis.apply_primary_terminator_effect(state, terminator, location);
|
||||
if to == Effect::Primary.at_index(terminator_index) {
|
||||
return;
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ impl Direction for Backward {
|
|||
let location = Location { block, statement_index: from.statement_index };
|
||||
let statement = &block_data.statements[from.statement_index];
|
||||
|
||||
analysis.apply_statement_effect(state, statement, location);
|
||||
analysis.apply_primary_statement_effect(state, statement, location);
|
||||
if to == Effect::Primary.at_index(from.statement_index) {
|
||||
return;
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ impl Direction for Backward {
|
|||
from.statement_index - 1
|
||||
}
|
||||
|
||||
Effect::Before => from.statement_index,
|
||||
Effect::Early => from.statement_index,
|
||||
};
|
||||
|
||||
// Handle all statements between `first_unapplied_idx` and `to.statement_index`.
|
||||
|
@ -196,21 +196,21 @@ impl Direction for Backward {
|
|||
for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) {
|
||||
let location = Location { block, statement_index };
|
||||
let statement = &block_data.statements[statement_index];
|
||||
analysis.apply_before_statement_effect(state, statement, location);
|
||||
analysis.apply_statement_effect(state, statement, location);
|
||||
analysis.apply_early_statement_effect(state, statement, location);
|
||||
analysis.apply_primary_statement_effect(state, statement, location);
|
||||
}
|
||||
|
||||
// Handle the statement at `to`.
|
||||
|
||||
let location = Location { block, statement_index: to.statement_index };
|
||||
let statement = &block_data.statements[to.statement_index];
|
||||
analysis.apply_before_statement_effect(state, statement, location);
|
||||
analysis.apply_early_statement_effect(state, statement, location);
|
||||
|
||||
if to.effect == Effect::Before {
|
||||
if to.effect == Effect::Early {
|
||||
return;
|
||||
}
|
||||
|
||||
analysis.apply_statement_effect(state, statement, location);
|
||||
analysis.apply_primary_statement_effect(state, statement, location);
|
||||
}
|
||||
|
||||
fn visit_results_in_block<'mir, 'tcx, A>(
|
||||
|
@ -228,17 +228,17 @@ impl Direction for Backward {
|
|||
|
||||
let loc = Location { block, statement_index: block_data.statements.len() };
|
||||
let term = block_data.terminator();
|
||||
results.analysis.apply_before_terminator_effect(state, term, loc);
|
||||
vis.visit_terminator_before_primary_effect(results, state, term, loc);
|
||||
results.analysis.apply_terminator_effect(state, term, loc);
|
||||
vis.visit_terminator_after_primary_effect(results, state, term, loc);
|
||||
results.analysis.apply_early_terminator_effect(state, term, loc);
|
||||
vis.visit_after_early_terminator_effect(results, state, term, loc);
|
||||
results.analysis.apply_primary_terminator_effect(state, term, loc);
|
||||
vis.visit_after_primary_terminator_effect(results, state, term, loc);
|
||||
|
||||
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
|
||||
let loc = Location { block, statement_index };
|
||||
results.analysis.apply_before_statement_effect(state, stmt, loc);
|
||||
vis.visit_statement_before_primary_effect(results, state, stmt, loc);
|
||||
results.analysis.apply_statement_effect(state, stmt, loc);
|
||||
vis.visit_statement_after_primary_effect(results, state, stmt, loc);
|
||||
results.analysis.apply_early_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_early_statement_effect(results, state, stmt, loc);
|
||||
results.analysis.apply_primary_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_primary_statement_effect(results, state, stmt, loc);
|
||||
}
|
||||
|
||||
vis.visit_block_start(state);
|
||||
|
@ -294,13 +294,13 @@ impl Direction for Forward {
|
|||
{
|
||||
for (statement_index, statement) in block_data.statements.iter().enumerate() {
|
||||
let location = Location { block, statement_index };
|
||||
analysis.apply_before_statement_effect(state, statement, location);
|
||||
analysis.apply_statement_effect(state, statement, location);
|
||||
analysis.apply_early_statement_effect(state, statement, location);
|
||||
analysis.apply_primary_statement_effect(state, statement, location);
|
||||
}
|
||||
let terminator = block_data.terminator();
|
||||
let location = Location { block, statement_index: block_data.statements.len() };
|
||||
analysis.apply_before_terminator_effect(state, terminator, location);
|
||||
let edges = analysis.apply_terminator_effect(state, terminator, location);
|
||||
analysis.apply_early_terminator_effect(state, terminator, location);
|
||||
let edges = analysis.apply_primary_terminator_effect(state, terminator, location);
|
||||
|
||||
let exit_state = state;
|
||||
match edges {
|
||||
|
@ -368,21 +368,21 @@ impl Direction for Forward {
|
|||
// after effect, do so now and start the loop below from the next statement.
|
||||
|
||||
let first_unapplied_index = match from.effect {
|
||||
Effect::Before => from.statement_index,
|
||||
Effect::Early => from.statement_index,
|
||||
|
||||
Effect::Primary if from.statement_index == terminator_index => {
|
||||
debug_assert_eq!(from, to);
|
||||
|
||||
let location = Location { block, statement_index: terminator_index };
|
||||
let terminator = block_data.terminator();
|
||||
analysis.apply_terminator_effect(state, terminator, location);
|
||||
analysis.apply_primary_terminator_effect(state, terminator, location);
|
||||
return;
|
||||
}
|
||||
|
||||
Effect::Primary => {
|
||||
let location = Location { block, statement_index: from.statement_index };
|
||||
let statement = &block_data.statements[from.statement_index];
|
||||
analysis.apply_statement_effect(state, statement, location);
|
||||
analysis.apply_primary_statement_effect(state, statement, location);
|
||||
|
||||
// If we only needed to apply the after effect of the statement at `idx`, we are
|
||||
// done.
|
||||
|
@ -399,8 +399,8 @@ impl Direction for Forward {
|
|||
for statement_index in first_unapplied_index..to.statement_index {
|
||||
let location = Location { block, statement_index };
|
||||
let statement = &block_data.statements[statement_index];
|
||||
analysis.apply_before_statement_effect(state, statement, location);
|
||||
analysis.apply_statement_effect(state, statement, location);
|
||||
analysis.apply_early_statement_effect(state, statement, location);
|
||||
analysis.apply_primary_statement_effect(state, statement, location);
|
||||
}
|
||||
|
||||
// Handle the statement or terminator at `to`.
|
||||
|
@ -408,17 +408,17 @@ impl Direction for Forward {
|
|||
let location = Location { block, statement_index: to.statement_index };
|
||||
if to.statement_index == terminator_index {
|
||||
let terminator = block_data.terminator();
|
||||
analysis.apply_before_terminator_effect(state, terminator, location);
|
||||
analysis.apply_early_terminator_effect(state, terminator, location);
|
||||
|
||||
if to.effect == Effect::Primary {
|
||||
analysis.apply_terminator_effect(state, terminator, location);
|
||||
analysis.apply_primary_terminator_effect(state, terminator, location);
|
||||
}
|
||||
} else {
|
||||
let statement = &block_data.statements[to.statement_index];
|
||||
analysis.apply_before_statement_effect(state, statement, location);
|
||||
analysis.apply_early_statement_effect(state, statement, location);
|
||||
|
||||
if to.effect == Effect::Primary {
|
||||
analysis.apply_statement_effect(state, statement, location);
|
||||
analysis.apply_primary_statement_effect(state, statement, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -438,18 +438,18 @@ impl Direction for Forward {
|
|||
|
||||
for (statement_index, stmt) in block_data.statements.iter().enumerate() {
|
||||
let loc = Location { block, statement_index };
|
||||
results.analysis.apply_before_statement_effect(state, stmt, loc);
|
||||
vis.visit_statement_before_primary_effect(results, state, stmt, loc);
|
||||
results.analysis.apply_statement_effect(state, stmt, loc);
|
||||
vis.visit_statement_after_primary_effect(results, state, stmt, loc);
|
||||
results.analysis.apply_early_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_early_statement_effect(results, state, stmt, loc);
|
||||
results.analysis.apply_primary_statement_effect(state, stmt, loc);
|
||||
vis.visit_after_primary_statement_effect(results, state, stmt, loc);
|
||||
}
|
||||
|
||||
let loc = Location { block, statement_index: block_data.statements.len() };
|
||||
let term = block_data.terminator();
|
||||
results.analysis.apply_before_terminator_effect(state, term, loc);
|
||||
vis.visit_terminator_before_primary_effect(results, state, term, loc);
|
||||
results.analysis.apply_terminator_effect(state, term, loc);
|
||||
vis.visit_terminator_after_primary_effect(results, state, term, loc);
|
||||
results.analysis.apply_early_terminator_effect(state, term, loc);
|
||||
vis.visit_after_early_terminator_effect(results, state, term, loc);
|
||||
results.analysis.apply_primary_terminator_effect(state, term, loc);
|
||||
vis.visit_after_primary_terminator_effect(results, state, term, loc);
|
||||
|
||||
vis.visit_block_end(state);
|
||||
}
|
||||
|
|
|
@ -724,7 +724,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_statement_before_primary_effect(
|
||||
fn visit_after_early_statement_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, A>,
|
||||
state: &A::Domain,
|
||||
|
@ -737,7 +737,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_statement_after_primary_effect(
|
||||
fn visit_after_primary_statement_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, A>,
|
||||
state: &A::Domain,
|
||||
|
@ -748,7 +748,7 @@ where
|
|||
self.prev_state.clone_from(state)
|
||||
}
|
||||
|
||||
fn visit_terminator_before_primary_effect(
|
||||
fn visit_after_early_terminator_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, A>,
|
||||
state: &A::Domain,
|
||||
|
@ -761,7 +761,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_terminator_after_primary_effect(
|
||||
fn visit_after_primary_terminator_effect(
|
||||
&mut self,
|
||||
results: &mut Results<'tcx, A>,
|
||||
state: &A::Domain,
|
||||
|
|
|
@ -38,10 +38,8 @@
|
|||
//! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram
|
||||
//! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
|
||||
|
||||
use std::iter;
|
||||
|
||||
use rustc_index::Idx;
|
||||
use rustc_index::bit_set::{BitSet, MixedBitSet};
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
|
||||
use crate::framework::BitSetExt;
|
||||
|
||||
|
@ -70,53 +68,6 @@ pub trait HasTop {
|
|||
const TOP: Self;
|
||||
}
|
||||
|
||||
/// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom:
|
||||
///
|
||||
/// ```text
|
||||
/// true
|
||||
/// |
|
||||
/// false
|
||||
/// ```
|
||||
impl JoinSemiLattice for bool {
|
||||
fn join(&mut self, other: &Self) -> bool {
|
||||
if let (false, true) = (*self, *other) {
|
||||
*self = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl HasBottom for bool {
|
||||
const BOTTOM: Self = false;
|
||||
|
||||
fn is_bottom(&self) -> bool {
|
||||
!self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasTop for bool {
|
||||
const TOP: Self = true;
|
||||
}
|
||||
|
||||
/// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation
|
||||
/// of the least upper bounds of each element of the tuple (or list).
|
||||
///
|
||||
/// In other words:
|
||||
/// (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ)
|
||||
impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> {
|
||||
fn join(&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.join(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`.
|
||||
|
@ -197,18 +148,6 @@ impl<T> MaybeReachable<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> HasBottom for MaybeReachable<T> {
|
||||
const BOTTOM: Self = MaybeReachable::Unreachable;
|
||||
|
||||
fn is_bottom(&self) -> bool {
|
||||
matches!(self, Self::Unreachable)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: HasTop> HasTop for MaybeReachable<T> {
|
||||
const TOP: Self = MaybeReachable::Reachable(T::TOP);
|
||||
}
|
||||
|
||||
impl<S> MaybeReachable<S> {
|
||||
/// Return whether the current state contains the given element. If the state is unreachable,
|
||||
/// it does no contain anything.
|
||||
|
|
|
@ -56,7 +56,7 @@ mod visitor;
|
|||
pub use self::cursor::ResultsCursor;
|
||||
pub use self::direction::{Backward, Direction, Forward};
|
||||
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
|
||||
pub use self::results::{EntrySets, Results};
|
||||
pub use self::results::{EntryStates, Results};
|
||||
pub use self::visitor::{ResultsVisitor, visit_results};
|
||||
|
||||
/// Analysis domains are all bitsets of various kinds. This trait holds
|
||||
|
@ -122,8 +122,23 @@ pub trait Analysis<'tcx> {
|
|||
// `resume`). It's not obvious how to handle `yield` points in coroutines, however.
|
||||
fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain);
|
||||
|
||||
/// Updates the current dataflow state with an "early" effect, i.e. one
|
||||
/// that occurs immediately before the given statement.
|
||||
///
|
||||
/// This method is useful if the consumer of the results of this analysis only needs to observe
|
||||
/// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
|
||||
/// analyses should not implement this without also implementing
|
||||
/// `apply_primary_statement_effect`.
|
||||
fn apply_early_statement_effect(
|
||||
&mut self,
|
||||
_state: &mut Self::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
_location: Location,
|
||||
) {
|
||||
}
|
||||
|
||||
/// Updates the current dataflow state with the effect of evaluating a statement.
|
||||
fn apply_statement_effect(
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
state: &mut Self::Domain,
|
||||
statement: &mir::Statement<'tcx>,
|
||||
|
@ -131,15 +146,16 @@ pub trait Analysis<'tcx> {
|
|||
);
|
||||
|
||||
/// Updates the current dataflow state with an effect that occurs immediately *before* the
|
||||
/// given statement.
|
||||
/// given terminator.
|
||||
///
|
||||
/// This method is useful if the consumer of the results of this analysis only needs to observe
|
||||
/// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
|
||||
/// analyses should not implement this without also implementing `apply_statement_effect`.
|
||||
fn apply_before_statement_effect(
|
||||
/// This method is useful if the consumer of the results of this analysis needs only to observe
|
||||
/// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
|
||||
/// analyses should not implement this without also implementing
|
||||
/// `apply_primary_terminator_effect`.
|
||||
fn apply_early_terminator_effect(
|
||||
&mut self,
|
||||
_state: &mut Self::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
_location: Location,
|
||||
) {
|
||||
}
|
||||
|
@ -150,7 +166,7 @@ pub trait Analysis<'tcx> {
|
|||
/// in this function. That should go in `apply_call_return_effect`. For example, in the
|
||||
/// `InitializedPlaces` analyses, the return place for a function call is not marked as
|
||||
/// initialized here.
|
||||
fn apply_terminator_effect<'mir>(
|
||||
fn apply_primary_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
_state: &mut Self::Domain,
|
||||
terminator: &'mir mir::Terminator<'tcx>,
|
||||
|
@ -159,27 +175,13 @@ pub trait Analysis<'tcx> {
|
|||
terminator.edges()
|
||||
}
|
||||
|
||||
/// Updates the current dataflow state with an effect that occurs immediately *before* the
|
||||
/// given terminator.
|
||||
///
|
||||
/// This method is useful if the consumer of the results of this analysis needs only to observe
|
||||
/// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
|
||||
/// analyses should not implement this without also implementing `apply_terminator_effect`.
|
||||
fn apply_before_terminator_effect(
|
||||
&mut self,
|
||||
_state: &mut Self::Domain,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
_location: Location,
|
||||
) {
|
||||
}
|
||||
|
||||
/* Edge-specific effects */
|
||||
|
||||
/// Updates the current dataflow state with the effect of a successful return from a `Call`
|
||||
/// terminator.
|
||||
///
|
||||
/// This is separate from `apply_terminator_effect` to properly track state across unwind
|
||||
/// edges.
|
||||
/// This is separate from `apply_primary_terminator_effect` to properly track state across
|
||||
/// unwind edges.
|
||||
fn apply_call_return_effect(
|
||||
&mut self,
|
||||
_state: &mut Self::Domain,
|
||||
|
@ -234,11 +236,12 @@ pub trait Analysis<'tcx> {
|
|||
Self: Sized,
|
||||
Self::Domain: DebugWithContext<Self>,
|
||||
{
|
||||
let mut entry_sets =
|
||||
let mut entry_states =
|
||||
IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len());
|
||||
self.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
|
||||
self.initialize_start_block(body, &mut entry_states[mir::START_BLOCK]);
|
||||
|
||||
if Self::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != self.bottom_value(body) {
|
||||
if Self::Direction::IS_BACKWARD && entry_states[mir::START_BLOCK] != self.bottom_value(body)
|
||||
{
|
||||
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
|
||||
}
|
||||
|
||||
|
@ -262,9 +265,9 @@ pub trait Analysis<'tcx> {
|
|||
let mut state = self.bottom_value(body);
|
||||
while let Some(bb) = dirty_queue.pop() {
|
||||
// Set the state to the entry state of the block.
|
||||
// This is equivalent to `state = entry_sets[bb].clone()`,
|
||||
// This is equivalent to `state = entry_states[bb].clone()`,
|
||||
// but it saves an allocation, thus improving compile times.
|
||||
state.clone_from(&entry_sets[bb]);
|
||||
state.clone_from(&entry_states[bb]);
|
||||
|
||||
Self::Direction::apply_effects_in_block(
|
||||
&mut self,
|
||||
|
@ -273,7 +276,7 @@ pub trait Analysis<'tcx> {
|
|||
bb,
|
||||
&body[bb],
|
||||
|target: BasicBlock, state: &Self::Domain| {
|
||||
let set_changed = entry_sets[target].join(state);
|
||||
let set_changed = entry_states[target].join(state);
|
||||
if set_changed {
|
||||
dirty_queue.insert(target);
|
||||
}
|
||||
|
@ -281,7 +284,7 @@ pub trait Analysis<'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
let mut results = Results { analysis: self, entry_sets };
|
||||
let mut results = Results { analysis: self, entry_states };
|
||||
|
||||
if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
|
||||
let res = write_graphviz_results(tcx, body, &mut results, pass_name);
|
||||
|
@ -358,11 +361,10 @@ impl<T, S: GenKill<T>> GenKill<T> for MaybeReachable<S> {
|
|||
// 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 {
|
||||
/// The "before" effect (e.g., `apply_before_statement_effect`) for a statement (or
|
||||
/// terminator).
|
||||
Before,
|
||||
/// The "early" effect (e.g., `apply_early_statement_effect`) for a statement/terminator.
|
||||
Early,
|
||||
|
||||
/// The "primary" effect (e.g., `apply_statement_effect`) for a statement (or terminator).
|
||||
/// The "primary" effect (e.g., `apply_primary_statement_effect`) for a statement/terminator.
|
||||
Primary,
|
||||
}
|
||||
|
||||
|
@ -381,15 +383,15 @@ pub struct EffectIndex {
|
|||
impl EffectIndex {
|
||||
fn next_in_forward_order(self) -> Self {
|
||||
match self.effect {
|
||||
Effect::Before => Effect::Primary.at_index(self.statement_index),
|
||||
Effect::Primary => Effect::Before.at_index(self.statement_index + 1),
|
||||
Effect::Early => Effect::Primary.at_index(self.statement_index),
|
||||
Effect::Primary => Effect::Early.at_index(self.statement_index + 1),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_in_backward_order(self) -> Self {
|
||||
match self.effect {
|
||||
Effect::Before => Effect::Primary.at_index(self.statement_index),
|
||||
Effect::Primary => Effect::Before.at_index(self.statement_index - 1),
|
||||
Effect::Early => Effect::Primary.at_index(self.statement_index),
|
||||
Effect::Primary => Effect::Early.at_index(self.statement_index - 1),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_middle::mir::{BasicBlock, Body, traversal};
|
|||
use super::{Analysis, ResultsCursor, ResultsVisitor, visit_results};
|
||||
use crate::framework::cursor::ResultsHandle;
|
||||
|
||||
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
|
||||
pub type EntryStates<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
|
||||
|
||||
/// A dataflow analysis that has converged to fixpoint. It only holds the domain values at the
|
||||
/// entry of each basic block. Domain values in other parts of the block are recomputed on the fly
|
||||
|
@ -17,7 +17,7 @@ where
|
|||
A: Analysis<'tcx>,
|
||||
{
|
||||
pub analysis: A,
|
||||
pub entry_sets: EntrySets<'tcx, A>,
|
||||
pub entry_states: EntryStates<'tcx, A>,
|
||||
}
|
||||
|
||||
impl<'tcx, A> Results<'tcx, A>
|
||||
|
@ -40,7 +40,7 @@ where
|
|||
|
||||
/// Gets the dataflow state for the given block.
|
||||
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
|
||||
&self.entry_sets[block]
|
||||
&self.entry_states[block]
|
||||
}
|
||||
|
||||
pub fn visit_with<'mir>(
|
||||
|
|
|
@ -73,7 +73,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> {
|
|||
///
|
||||
/// The `102` in the block's entry set is derived from the basic block index and ensures that the
|
||||
/// expected state is unique across all basic blocks. Remember, it is generated by
|
||||
/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint.
|
||||
/// `mock_entry_states`, not from actually running `MockAnalysis` to fixpoint.
|
||||
struct MockAnalysis<'tcx, D> {
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
dir: PhantomData<D>,
|
||||
|
@ -90,7 +90,7 @@ impl<D: Direction> MockAnalysis<'_, D> {
|
|||
ret
|
||||
}
|
||||
|
||||
fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
|
||||
fn mock_entry_states(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
|
||||
let empty = self.bottom_value(self.body);
|
||||
let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks);
|
||||
|
||||
|
@ -104,7 +104,7 @@ impl<D: Direction> MockAnalysis<'_, D> {
|
|||
/// Returns the index that should be added to the dataflow state at the given target.
|
||||
fn effect(&self, loc: EffectIndex) -> usize {
|
||||
let idx = match loc.effect {
|
||||
Effect::Before => loc.statement_index * 2,
|
||||
Effect::Early => loc.statement_index * 2,
|
||||
Effect::Primary => loc.statement_index * 2 + 1,
|
||||
};
|
||||
|
||||
|
@ -128,14 +128,14 @@ impl<D: Direction> MockAnalysis<'_, D> {
|
|||
|
||||
let target = match target {
|
||||
SeekTarget::BlockEntry { .. } => return ret,
|
||||
SeekTarget::Before(loc) => Effect::Before.at_index(loc.statement_index),
|
||||
SeekTarget::Early(loc) => Effect::Early.at_index(loc.statement_index),
|
||||
SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index),
|
||||
};
|
||||
|
||||
let mut pos = if D::IS_FORWARD {
|
||||
Effect::Before.at_index(0)
|
||||
Effect::Early.at_index(0)
|
||||
} else {
|
||||
Effect::Before.at_index(self.body[block].statements.len())
|
||||
Effect::Early.at_index(self.body[block].statements.len())
|
||||
};
|
||||
|
||||
loop {
|
||||
|
@ -168,7 +168,17 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
|
|||
unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint");
|
||||
}
|
||||
|
||||
fn apply_statement_effect(
|
||||
fn apply_early_statement_effect(
|
||||
&mut self,
|
||||
state: &mut Self::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
let idx = self.effect(Effect::Early.at_index(location.statement_index));
|
||||
assert!(state.insert(idx));
|
||||
}
|
||||
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
state: &mut Self::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
|
@ -178,17 +188,17 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
|
|||
assert!(state.insert(idx));
|
||||
}
|
||||
|
||||
fn apply_before_statement_effect(
|
||||
fn apply_early_terminator_effect(
|
||||
&mut self,
|
||||
state: &mut Self::Domain,
|
||||
_statement: &mir::Statement<'tcx>,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
let idx = self.effect(Effect::Before.at_index(location.statement_index));
|
||||
let idx = self.effect(Effect::Early.at_index(location.statement_index));
|
||||
assert!(state.insert(idx));
|
||||
}
|
||||
|
||||
fn apply_terminator_effect<'mir>(
|
||||
fn apply_primary_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
state: &mut Self::Domain,
|
||||
terminator: &'mir mir::Terminator<'tcx>,
|
||||
|
@ -198,22 +208,12 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
|
|||
assert!(state.insert(idx));
|
||||
terminator.edges()
|
||||
}
|
||||
|
||||
fn apply_before_terminator_effect(
|
||||
&mut self,
|
||||
state: &mut Self::Domain,
|
||||
_terminator: &mir::Terminator<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
let idx = self.effect(Effect::Before.at_index(location.statement_index));
|
||||
assert!(state.insert(idx));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum SeekTarget {
|
||||
BlockEntry(BasicBlock),
|
||||
Before(Location),
|
||||
Early(Location),
|
||||
After(Location),
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ impl SeekTarget {
|
|||
|
||||
match *self {
|
||||
BlockEntry(block) => block,
|
||||
Before(loc) | After(loc) => loc.block,
|
||||
Early(loc) | After(loc) => loc.block,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,7 +235,7 @@ impl SeekTarget {
|
|||
.map(move |(i, kind)| {
|
||||
let loc = Location { block, statement_index: i };
|
||||
match kind {
|
||||
0 => SeekTarget::Before(loc),
|
||||
0 => SeekTarget::Early(loc),
|
||||
1 => SeekTarget::After(loc),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
|
|||
let body = analysis.body;
|
||||
|
||||
let mut cursor =
|
||||
Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
|
||||
Results { entry_states: analysis.mock_entry_states(), analysis }.into_results_cursor(body);
|
||||
|
||||
cursor.allow_unreachable();
|
||||
|
||||
|
@ -262,7 +262,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
|
|||
|
||||
match targ {
|
||||
BlockEntry(block) => cursor.seek_to_block_entry(block),
|
||||
Before(loc) => cursor.seek_before_primary_effect(loc),
|
||||
Early(loc) => cursor.seek_before_primary_effect(loc),
|
||||
After(loc) => cursor.seek_after_primary_effect(loc),
|
||||
}
|
||||
|
||||
|
|
|
@ -35,9 +35,9 @@ where
|
|||
{
|
||||
fn visit_block_start(&mut self, _state: &A::Domain) {}
|
||||
|
||||
/// Called with the `before_statement_effect` of the given statement applied to `state` but not
|
||||
/// its `statement_effect`.
|
||||
fn visit_statement_before_primary_effect(
|
||||
/// // njn: grep for "before", "primary", etc.
|
||||
/// Called after the "early" effect of the given statement is applied to `state`.
|
||||
fn visit_after_early_statement_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_state: &A::Domain,
|
||||
|
@ -46,9 +46,8 @@ where
|
|||
) {
|
||||
}
|
||||
|
||||
/// Called with both the `before_statement_effect` and the `statement_effect` of the given
|
||||
/// statement applied to `state`.
|
||||
fn visit_statement_after_primary_effect(
|
||||
/// Called after the "primary" effect of the given statement is applied to `state`.
|
||||
fn visit_after_primary_statement_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_state: &A::Domain,
|
||||
|
@ -57,9 +56,8 @@ where
|
|||
) {
|
||||
}
|
||||
|
||||
/// Called with the `before_terminator_effect` of the given terminator applied to `state` but
|
||||
/// not its `terminator_effect`.
|
||||
fn visit_terminator_before_primary_effect(
|
||||
/// Called after the "early" effect of the given terminator is applied to `state`.
|
||||
fn visit_after_early_terminator_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_state: &A::Domain,
|
||||
|
@ -68,11 +66,10 @@ where
|
|||
) {
|
||||
}
|
||||
|
||||
/// Called with both the `before_terminator_effect` and the `terminator_effect` of the given
|
||||
/// terminator applied to `state`.
|
||||
/// Called after the "primary" effect of the given terminator is applied to `state`.
|
||||
///
|
||||
/// The `call_return_effect` (if one exists) will *not* be applied to `state`.
|
||||
fn visit_terminator_after_primary_effect(
|
||||
fn visit_after_primary_terminator_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
_state: &A::Domain,
|
||||
|
|
|
@ -33,22 +33,22 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals {
|
|||
// No locals are aliased on function entry
|
||||
}
|
||||
|
||||
fn apply_statement_effect(
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
statement: &Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
Self::transfer_function(trans).visit_statement(statement, location);
|
||||
Self::transfer_function(state).visit_statement(statement, location);
|
||||
}
|
||||
|
||||
fn apply_terminator_effect<'mir>(
|
||||
fn apply_primary_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
terminator: &'mir Terminator<'tcx>,
|
||||
location: Location,
|
||||
) -> TerminatorEdges<'mir, 'tcx> {
|
||||
Self::transfer_function(trans).visit_terminator(terminator, location);
|
||||
Self::transfer_function(state).visit_terminator(terminator, location);
|
||||
terminator.edges()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
|
|||
pub fn is_unwind_dead(
|
||||
&self,
|
||||
place: mir::Place<'tcx>,
|
||||
state: &MaybeReachable<MixedBitSet<MovePathIndex>>,
|
||||
state: &<Self as Analysis<'tcx>>::Domain,
|
||||
) -> bool {
|
||||
if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) {
|
||||
let mut maybe_live = false;
|
||||
|
@ -218,26 +218,26 @@ impl<'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
|||
|
||||
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
|
||||
fn update_bits(
|
||||
trans: &mut <Self as Analysis<'tcx>>::Domain,
|
||||
state: &mut <Self as Analysis<'tcx>>::Domain,
|
||||
path: MovePathIndex,
|
||||
state: DropFlagState,
|
||||
dfstate: DropFlagState,
|
||||
) {
|
||||
match state {
|
||||
DropFlagState::Absent => trans.kill(path),
|
||||
DropFlagState::Present => trans.gen_(path),
|
||||
match dfstate {
|
||||
DropFlagState::Absent => state.kill(path),
|
||||
DropFlagState::Present => state.gen_(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
|
||||
fn update_bits(
|
||||
trans: &mut <Self as Analysis<'tcx>>::Domain,
|
||||
state: &mut <Self as Analysis<'tcx>>::Domain,
|
||||
path: MovePathIndex,
|
||||
state: DropFlagState,
|
||||
dfstate: DropFlagState,
|
||||
) {
|
||||
match state {
|
||||
DropFlagState::Absent => trans.gen_(path),
|
||||
DropFlagState::Present => trans.kill(path),
|
||||
match dfstate {
|
||||
DropFlagState::Absent => state.gen_(path),
|
||||
DropFlagState::Present => state.kill(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -263,14 +263,14 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
|||
});
|
||||
}
|
||||
|
||||
fn apply_statement_effect(
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &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)
|
||||
Self::update_bits(state, path, s)
|
||||
});
|
||||
|
||||
// Mark all places as "maybe init" if they are mutably borrowed. See #90752.
|
||||
|
@ -282,12 +282,12 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
|||
&& let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref())
|
||||
{
|
||||
on_all_children_bits(self.move_data(), mpi, |child| {
|
||||
trans.gen_(child);
|
||||
state.gen_(child);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_terminator_effect<'mir>(
|
||||
fn apply_primary_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
state: &mut Self::Domain,
|
||||
terminator: &'mir mir::Terminator<'tcx>,
|
||||
|
@ -309,7 +309,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
|||
|
||||
fn apply_call_return_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
_block: mir::BasicBlock,
|
||||
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||
) {
|
||||
|
@ -320,7 +320,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
|||
self.move_data(),
|
||||
self.move_data().rev_lookup.find(place.as_ref()),
|
||||
|mpi| {
|
||||
trans.gen_(mpi);
|
||||
state.gen_(mpi);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
@ -345,7 +345,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
|||
};
|
||||
|
||||
let mut discriminants = enum_def.discriminants(self.tcx);
|
||||
edge_effects.apply(|trans, edge| {
|
||||
edge_effects.apply(|state, edge| {
|
||||
let Some(value) = edge.value else {
|
||||
return;
|
||||
};
|
||||
|
@ -363,25 +363,27 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
|
|||
self.move_data(),
|
||||
enum_place,
|
||||
variant,
|
||||
|mpi| trans.kill(mpi),
|
||||
|mpi| state.kill(mpi),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>;
|
||||
|
||||
impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
||||
/// 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.
|
||||
type Domain = MixedBitSet<MovePathIndex>;
|
||||
type Domain = MaybeUninitializedPlacesDomain;
|
||||
|
||||
const NAME: &'static str = "maybe_uninit";
|
||||
|
||||
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
|
||||
// bottom = initialized (start_block_effect counters this at outset)
|
||||
// bottom = initialized (`initialize_start_block` overwrites this on first entry)
|
||||
MixedBitSet::new_empty(self.move_data().move_paths.len())
|
||||
}
|
||||
|
||||
// sets on_entry bits for Arg places
|
||||
// sets state bits for Arg places
|
||||
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();
|
||||
|
@ -392,28 +394,28 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
|||
});
|
||||
}
|
||||
|
||||
fn apply_statement_effect(
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &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)
|
||||
Self::update_bits(state, path, s)
|
||||
});
|
||||
|
||||
// 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.
|
||||
}
|
||||
|
||||
fn apply_terminator_effect<'mir>(
|
||||
fn apply_primary_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &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)
|
||||
Self::update_bits(state, path, s)
|
||||
});
|
||||
if self.skip_unreachable_unwind.contains(location.block) {
|
||||
let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { bug!() };
|
||||
|
@ -426,7 +428,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
|||
|
||||
fn apply_call_return_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
_block: mir::BasicBlock,
|
||||
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||
) {
|
||||
|
@ -437,7 +439,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
|||
self.move_data(),
|
||||
self.move_data().rev_lookup.find(place.as_ref()),
|
||||
|mpi| {
|
||||
trans.kill(mpi);
|
||||
state.kill(mpi);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
@ -466,7 +468,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
|||
};
|
||||
|
||||
let mut discriminants = enum_def.discriminants(self.tcx);
|
||||
edge_effects.apply(|trans, edge| {
|
||||
edge_effects.apply(|state, edge| {
|
||||
let Some(value) = edge.value else {
|
||||
return;
|
||||
};
|
||||
|
@ -484,16 +486,18 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
|
|||
self.move_data(),
|
||||
enum_place,
|
||||
variant,
|
||||
|mpi| trans.gen_(mpi),
|
||||
|mpi| state.gen_(mpi),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>;
|
||||
|
||||
impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
||||
/// 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.
|
||||
type Domain = MixedBitSet<InitIndex>;
|
||||
type Domain = EverInitializedPlacesDomain;
|
||||
|
||||
const NAME: &'static str = "ever_init";
|
||||
|
||||
|
@ -508,10 +512,10 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, trans), level = "debug")]
|
||||
fn apply_statement_effect(
|
||||
#[instrument(skip(self, state), level = "debug")]
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
stmt: &mir::Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
|
@ -521,7 +525,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
|||
let rev_lookup = &move_data.rev_lookup;
|
||||
|
||||
debug!("initializes move_indexes {:?}", init_loc_map[location]);
|
||||
trans.gen_all(init_loc_map[location].iter().copied());
|
||||
state.gen_all(init_loc_map[location].iter().copied());
|
||||
|
||||
if let mir::StatementKind::StorageDead(local) = stmt.kind {
|
||||
// End inits for StorageDead, so that an immutable variable can
|
||||
|
@ -531,15 +535,15 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
|||
"clears the ever initialized status of {:?}",
|
||||
init_path_map[move_path_index]
|
||||
);
|
||||
trans.kill_all(init_path_map[move_path_index].iter().copied());
|
||||
state.kill_all(init_path_map[move_path_index].iter().copied());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, trans, terminator), level = "debug")]
|
||||
fn apply_terminator_effect<'mir>(
|
||||
#[instrument(skip(self, state, terminator), level = "debug")]
|
||||
fn apply_primary_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
terminator: &'mir mir::Terminator<'tcx>,
|
||||
location: Location,
|
||||
) -> TerminatorEdges<'mir, 'tcx> {
|
||||
|
@ -548,7 +552,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
|||
let init_loc_map = &move_data.init_loc_map;
|
||||
debug!(?term);
|
||||
debug!("initializes move_indexes {:?}", init_loc_map[location]);
|
||||
trans.gen_all(
|
||||
state.gen_all(
|
||||
init_loc_map[location]
|
||||
.iter()
|
||||
.filter(|init_index| {
|
||||
|
@ -561,7 +565,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
|||
|
||||
fn apply_call_return_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
block: mir::BasicBlock,
|
||||
_return_places: CallReturnPlaces<'_, 'tcx>,
|
||||
) {
|
||||
|
@ -570,7 +574,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
|
|||
|
||||
let call_loc = self.body.terminator_loc(block);
|
||||
for init_index in &init_loc_map[call_loc] {
|
||||
trans.gen_(*init_index);
|
||||
state.gen_(*init_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,33 +40,33 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
|
|||
// No variables are live until we observe a use
|
||||
}
|
||||
|
||||
fn apply_statement_effect(
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
statement: &mir::Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
TransferFunction(trans).visit_statement(statement, location);
|
||||
TransferFunction(state).visit_statement(statement, location);
|
||||
}
|
||||
|
||||
fn apply_terminator_effect<'mir>(
|
||||
fn apply_primary_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
terminator: &'mir mir::Terminator<'tcx>,
|
||||
location: Location,
|
||||
) -> TerminatorEdges<'mir, 'tcx> {
|
||||
TransferFunction(trans).visit_terminator(terminator, location);
|
||||
TransferFunction(state).visit_terminator(terminator, location);
|
||||
terminator.edges()
|
||||
}
|
||||
|
||||
fn apply_call_return_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
_block: mir::BasicBlock,
|
||||
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||
) {
|
||||
if let CallReturnPlaces::Yield(resume_place) = return_places {
|
||||
YieldResumeEffect(trans).visit_place(
|
||||
YieldResumeEffect(state).visit_place(
|
||||
&resume_place,
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Yield),
|
||||
Location::START,
|
||||
|
@ -74,7 +74,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
|
|||
} else {
|
||||
return_places.for_each(|place| {
|
||||
if let Some(local) = place.as_local() {
|
||||
trans.kill(local);
|
||||
state.kill(local);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -137,10 +137,10 @@ enum DefUse {
|
|||
}
|
||||
|
||||
impl DefUse {
|
||||
fn apply(trans: &mut BitSet<Local>, place: Place<'_>, context: PlaceContext) {
|
||||
fn apply(state: &mut BitSet<Local>, place: Place<'_>, context: PlaceContext) {
|
||||
match DefUse::for_place(place, context) {
|
||||
Some(DefUse::Def) => trans.kill(place.local),
|
||||
Some(DefUse::Use) => trans.gen_(place.local),
|
||||
Some(DefUse::Def) => state.kill(place.local),
|
||||
Some(DefUse::Use) => state.gen_(place.local),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
@ -232,9 +232,9 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
|
|||
// No variables are live until we observe a use
|
||||
}
|
||||
|
||||
fn apply_statement_effect(
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
statement: &mir::Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
|
@ -258,34 +258,34 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
|
|||
};
|
||||
if let Some(destination) = destination {
|
||||
if !destination.is_indirect()
|
||||
&& !trans.contains(destination.local)
|
||||
&& !state.contains(destination.local)
|
||||
&& !self.always_live.contains(destination.local)
|
||||
{
|
||||
// This store is dead
|
||||
return;
|
||||
}
|
||||
}
|
||||
TransferFunction(trans).visit_statement(statement, location);
|
||||
TransferFunction(state).visit_statement(statement, location);
|
||||
}
|
||||
|
||||
fn apply_terminator_effect<'mir>(
|
||||
fn apply_primary_terminator_effect<'mir>(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
terminator: &'mir mir::Terminator<'tcx>,
|
||||
location: Location,
|
||||
) -> TerminatorEdges<'mir, 'tcx> {
|
||||
TransferFunction(trans).visit_terminator(terminator, location);
|
||||
TransferFunction(state).visit_terminator(terminator, location);
|
||||
terminator.edges()
|
||||
}
|
||||
|
||||
fn apply_call_return_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
_block: mir::BasicBlock,
|
||||
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||
) {
|
||||
if let CallReturnPlaces::Yield(resume_place) = return_places {
|
||||
YieldResumeEffect(trans).visit_place(
|
||||
YieldResumeEffect(state).visit_place(
|
||||
&resume_place,
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Yield),
|
||||
Location::START,
|
||||
|
@ -293,7 +293,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
|
|||
} else {
|
||||
return_places.for_each(|place| {
|
||||
if let Some(local) = place.as_local() {
|
||||
trans.remove(local);
|
||||
state.remove(local);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ mod storage_liveness;
|
|||
|
||||
pub use self::borrowed_locals::{MaybeBorrowedLocals, borrowed_locals};
|
||||
pub use self::initialized::{
|
||||
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
|
||||
EverInitializedPlaces, EverInitializedPlacesDomain, MaybeInitializedPlaces,
|
||||
MaybeUninitializedPlaces, MaybeUninitializedPlacesDomain,
|
||||
};
|
||||
pub use self::liveness::{
|
||||
MaybeLiveLocals, MaybeTransitiveLiveLocals, TransferFunction as LivenessTransferFunction,
|
||||
|
|
|
@ -44,23 +44,23 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> {
|
|||
BitSet::new_empty(body.local_decls.len())
|
||||
}
|
||||
|
||||
fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
|
||||
on_entry.union(&*self.always_live_locals);
|
||||
fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
|
||||
state.union(&*self.always_live_locals);
|
||||
|
||||
for arg in body.args_iter() {
|
||||
on_entry.insert(arg);
|
||||
state.insert(arg);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_statement_effect(
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
stmt: &Statement<'tcx>,
|
||||
_: Location,
|
||||
) {
|
||||
match stmt.kind {
|
||||
StatementKind::StorageLive(l) => trans.gen_(l),
|
||||
StatementKind::StorageDead(l) => trans.kill(l),
|
||||
StatementKind::StorageLive(l) => state.gen_(l),
|
||||
StatementKind::StorageDead(l) => state.kill(l),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -86,25 +86,25 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> {
|
|||
BitSet::new_empty(body.local_decls.len())
|
||||
}
|
||||
|
||||
fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
|
||||
fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
|
||||
assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
|
||||
// Do not iterate on return place and args, as they are trivially always live.
|
||||
for local in body.vars_and_temps_iter() {
|
||||
if !self.always_live_locals.contains(local) {
|
||||
on_entry.insert(local);
|
||||
state.insert(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_statement_effect(
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
stmt: &Statement<'tcx>,
|
||||
_: Location,
|
||||
) {
|
||||
match stmt.kind {
|
||||
StatementKind::StorageLive(l) => trans.kill(l),
|
||||
StatementKind::StorageDead(l) => trans.gen_(l),
|
||||
StatementKind::StorageLive(l) => state.kill(l),
|
||||
StatementKind::StorageDead(l) => state.gen_(l),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -134,31 +134,31 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
|||
BitSet::new_empty(body.local_decls.len())
|
||||
}
|
||||
|
||||
fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
|
||||
fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
|
||||
// The resume argument is live on function entry (we don't care about
|
||||
// the `self` argument)
|
||||
for arg in body.args_iter().skip(1) {
|
||||
on_entry.insert(arg);
|
||||
state.insert(arg);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_before_statement_effect(
|
||||
fn apply_early_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
stmt: &Statement<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
// If a place is borrowed in a statement, it needs storage for that statement.
|
||||
MaybeBorrowedLocals::transfer_function(trans).visit_statement(stmt, loc);
|
||||
MaybeBorrowedLocals::transfer_function(state).visit_statement(stmt, loc);
|
||||
|
||||
match &stmt.kind {
|
||||
StatementKind::StorageDead(l) => trans.kill(*l),
|
||||
StatementKind::StorageDead(l) => state.kill(*l),
|
||||
|
||||
// If a place is assigned to in a statement, it needs storage for that statement.
|
||||
StatementKind::Assign(box (place, _))
|
||||
| StatementKind::SetDiscriminant { box place, .. }
|
||||
| StatementKind::Deinit(box place) => {
|
||||
trans.gen_(place.local);
|
||||
state.gen_(place.local);
|
||||
}
|
||||
|
||||
// Nothing to do for these. Match exhaustively so this fails to compile when new
|
||||
|
@ -176,29 +176,29 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_statement_effect(
|
||||
fn apply_primary_statement_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
_: &Statement<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
// If we move from a place then it only stops needing storage *after*
|
||||
// that statement.
|
||||
self.check_for_move(trans, loc);
|
||||
self.check_for_move(state, loc);
|
||||
}
|
||||
|
||||
fn apply_before_terminator_effect(
|
||||
fn apply_early_terminator_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
terminator: &Terminator<'tcx>,
|
||||
loc: Location,
|
||||
) {
|
||||
// If a place is borrowed in a terminator, it needs storage for that terminator.
|
||||
MaybeBorrowedLocals::transfer_function(trans).visit_terminator(terminator, loc);
|
||||
MaybeBorrowedLocals::transfer_function(state).visit_terminator(terminator, loc);
|
||||
|
||||
match &terminator.kind {
|
||||
TerminatorKind::Call { destination, .. } => {
|
||||
trans.gen_(destination.local);
|
||||
state.gen_(destination.local);
|
||||
}
|
||||
|
||||
// Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for
|
||||
|
@ -213,7 +213,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
|||
InlineAsmOperand::Out { place, .. }
|
||||
| InlineAsmOperand::InOut { out_place: place, .. } => {
|
||||
if let Some(place) = place {
|
||||
trans.gen_(place.local);
|
||||
state.gen_(place.local);
|
||||
}
|
||||
}
|
||||
InlineAsmOperand::In { .. }
|
||||
|
@ -242,9 +242,9 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_terminator_effect<'t>(
|
||||
fn apply_primary_terminator_effect<'t>(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
terminator: &'t Terminator<'tcx>,
|
||||
loc: Location,
|
||||
) -> TerminatorEdges<'t, 'tcx> {
|
||||
|
@ -254,12 +254,12 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
|||
// Since `propagate_call_unwind` doesn't exist, we have to kill the
|
||||
// destination here, and then gen it again in `call_return_effect`.
|
||||
TerminatorKind::Call { destination, .. } => {
|
||||
trans.kill(destination.local);
|
||||
state.kill(destination.local);
|
||||
}
|
||||
|
||||
// The same applies to InlineAsm outputs.
|
||||
TerminatorKind::InlineAsm { ref operands, .. } => {
|
||||
CallReturnPlaces::InlineAsm(operands).for_each(|place| trans.kill(place.local));
|
||||
CallReturnPlaces::InlineAsm(operands).for_each(|place| state.kill(place.local));
|
||||
}
|
||||
|
||||
// Nothing to do for these. Match exhaustively so this fails to compile when new
|
||||
|
@ -279,32 +279,32 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
|
|||
| TerminatorKind::Unreachable => {}
|
||||
}
|
||||
|
||||
self.check_for_move(trans, loc);
|
||||
self.check_for_move(state, loc);
|
||||
terminator.edges()
|
||||
}
|
||||
|
||||
fn apply_call_return_effect(
|
||||
&mut self,
|
||||
trans: &mut Self::Domain,
|
||||
state: &mut Self::Domain,
|
||||
_block: BasicBlock,
|
||||
return_places: CallReturnPlaces<'_, 'tcx>,
|
||||
) {
|
||||
return_places.for_each(|place| trans.gen_(place.local));
|
||||
return_places.for_each(|place| state.gen_(place.local));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MaybeRequiresStorage<'_, 'tcx> {
|
||||
/// Kill locals that are fully moved and have not been borrowed.
|
||||
fn check_for_move(&mut self, trans: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) {
|
||||
fn check_for_move(&mut self, state: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) {
|
||||
let body = self.borrowed_locals.body();
|
||||
let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals };
|
||||
let mut visitor = MoveVisitor { state, borrowed_locals: &mut self.borrowed_locals };
|
||||
visitor.visit_location(body, loc);
|
||||
}
|
||||
}
|
||||
|
||||
struct MoveVisitor<'a, 'mir, 'tcx> {
|
||||
borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>,
|
||||
trans: &'a mut BitSet<Local>,
|
||||
state: &'a mut BitSet<Local>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> {
|
||||
|
@ -312,7 +312,7 @@ impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> {
|
|||
if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
|
||||
self.borrowed_locals.seek_before_primary_effect(loc);
|
||||
if !self.borrowed_locals.get().contains(local) {
|
||||
self.trans.kill(local);
|
||||
self.state.kill(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ pub use self::drop_flag_effects::{
|
|||
move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
|
||||
};
|
||||
pub use self::framework::{
|
||||
Analysis, Backward, Direction, EntrySets, Forward, GenKill, JoinSemiLattice, MaybeReachable,
|
||||
Analysis, Backward, Direction, EntryStates, Forward, GenKill, JoinSemiLattice, MaybeReachable,
|
||||
Results, ResultsCursor, ResultsVisitor, SwitchIntEdgeEffects, fmt, graphviz, lattice,
|
||||
visit_results,
|
||||
};
|
||||
|
|
|
@ -125,7 +125,7 @@ where
|
|||
A: Analysis<'tcx, Domain = BitSet<N>>,
|
||||
N: Idx,
|
||||
{
|
||||
fn visit_statement_after_primary_effect(
|
||||
fn visit_after_primary_statement_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
state: &A::Domain,
|
||||
|
@ -139,7 +139,7 @@ where
|
|||
});
|
||||
}
|
||||
|
||||
fn visit_terminator_after_primary_effect(
|
||||
fn visit_after_primary_terminator_effect(
|
||||
&mut self,
|
||||
_results: &mut Results<'tcx, A>,
|
||||
state: &A::Domain,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use rustc_ast::MetaItem;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::{self, Body, Local, Location};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
|
@ -254,7 +253,7 @@ impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
|
|||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
place: mir::Place<'tcx>,
|
||||
state: &BitSet<Local>,
|
||||
state: &Self::Domain,
|
||||
call: PeekCall,
|
||||
) {
|
||||
info!(?place, "peek_at");
|
||||
|
|
|
@ -67,7 +67,7 @@ impl<V: Clone> Clone for StateData<V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for StateData<V> {
|
||||
impl<V: JoinSemiLattice + Clone> JoinSemiLattice for StateData<V> {
|
||||
fn join(&mut self, other: &Self) -> bool {
|
||||
let mut changed = false;
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
|
@ -342,7 +342,7 @@ impl<V: Clone + HasBottom> State<V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for State<V> {
|
||||
impl<V: JoinSemiLattice + Clone> JoinSemiLattice for State<V> {
|
||||
fn join(&mut self, other: &Self) -> bool {
|
||||
match (&mut *self, other) {
|
||||
(_, State::Unreachable) => false,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue