rust/compiler/rustc_mir_dataflow/src/framework/visitor.rs
Nicholas Nethercote 92799b6f89 Separate Analysis and Results.
`Results` contains and `Analysis` and an `EntryStates`. The unfortunate
thing about this is that the analysis needs to be mutable everywhere
(`&mut Analysis`) which forces the `Results` to be mutable everywhere,
even though `EntryStates` is immutable everywhere.

To fix this, this commit renames `Results` as `AnalysisAndResults`,
renames `EntryStates` as `Results`, and separates the analysis and
results as much as possible. (`AnalysisAndResults` doesn't get much use,
it's mostly there to facilitate method chaining of
`iterate_to_fixpoint`.)

`Results` is immutable everywhere, which:
- is a bit clearer on how the data is used,
- avoids an unnecessary clone of entry states in
  `locals_live_across_suspend_points`, and
- moves the results outside the `RefCell` in Formatter.

The commit also reformulates `ResultsHandle` as the generic `CowMut`,
which is simpler than `ResultsHandle` because it doesn't need the
`'tcx` lifetime and the trait bounds. It also which sits nicely
alongside the new use of `Cow` in `ResultsCursor`.
2025-04-24 11:36:07 +10:00

96 lines
2.9 KiB
Rust

use rustc_middle::mir::{self, BasicBlock, Location, traversal};
use super::{Analysis, Direction, Results};
/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
/// dataflow state at that location.
pub fn visit_results<'mir, 'tcx, A>(
body: &'mir mir::Body<'tcx>,
blocks: impl IntoIterator<Item = BasicBlock>,
analysis: &mut A,
results: &Results<A::Domain>,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
{
let mut state = analysis.bottom_value(body);
#[cfg(debug_assertions)]
let reachable_blocks = mir::traversal::reachable_as_bitset(body);
for block in blocks {
#[cfg(debug_assertions)]
assert!(reachable_blocks.contains(block));
let block_data = &body[block];
state.clone_from(&results[block]);
A::Direction::visit_results_in_block(&mut state, block, block_data, analysis, vis);
}
}
/// Like `visit_results`, but only for reachable blocks.
pub fn visit_reachable_results<'mir, 'tcx, A>(
body: &'mir mir::Body<'tcx>,
analysis: &mut A,
results: &Results<A::Domain>,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
{
let blocks = traversal::reachable(body).map(|(bb, _)| bb);
visit_results(body, blocks, analysis, results, vis)
}
/// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in
/// many or all locations; use `ResultsCursor` if you want to inspect domain values only in certain
/// locations.
pub trait ResultsVisitor<'tcx, A>
where
A: Analysis<'tcx>,
{
fn visit_block_start(&mut self, _state: &A::Domain) {}
/// Called after the "early" effect of the given statement is applied to `state`.
fn visit_after_early_statement_effect(
&mut self,
_analysis: &mut A,
_state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
) {
}
/// Called after the "primary" effect of the given statement is applied to `state`.
fn visit_after_primary_statement_effect(
&mut self,
_analysis: &mut A,
_state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
) {
}
/// Called after the "early" effect of the given terminator is applied to `state`.
fn visit_after_early_terminator_effect(
&mut self,
_analysis: &mut A,
_state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
}
/// 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_after_primary_terminator_effect(
&mut self,
_analysis: &mut A,
_state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
}
fn visit_block_end(&mut self, _state: &A::Domain) {}
}