Skip to content

Commit 0f6144a

Browse files
authored
Rollup merge of #69644 - ecstatic-morse:unified-dataflow-cleanup, r=eddyb
Remove framework in `dataflow/mod.rs` in favor of "generic" one This is the culmination of the work described in rust-lang/compiler-team#202. All dataflow analyses (including the one in `clippy`) have been ported to use the framework in `dataflow/generic`, which can efficiently handle both gen/kill and generic problems. This PR moves the framework in `dataflow/generic` to `dataflow/framework`, and removes the gen/kill framework in `dataflow/mod.rs`. More comprehensive documentation for the new framework is tracked in rust-lang/rustc-dev-guide#564. `clippy` will need to change the path it uses to import the dataflow analysis traits.
2 parents 62c6006 + 89d6009 commit 0f6144a

22 files changed

+89
-1108
lines changed

src/librustc_mir/borrow_check/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ use std::mem;
2929
use std::rc::Rc;
3030

3131
use crate::dataflow;
32-
use crate::dataflow::generic::{Analysis, BorrowckFlowState as Flows, BorrowckResults};
3332
use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex};
3433
use crate::dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError};
3534
use crate::dataflow::Borrows;
3635
use crate::dataflow::EverInitializedPlaces;
3736
use crate::dataflow::MoveDataParamEnv;
37+
use crate::dataflow::{Analysis, BorrowckFlowState as Flows, BorrowckResults};
3838
use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
3939
use crate::transform::MirSource;
4040

@@ -298,7 +298,7 @@ fn do_mir_borrowck<'a, 'tcx>(
298298
mbcx.report_move_errors(errors);
299299
}
300300

301-
dataflow::generic::visit_results(
301+
dataflow::visit_results(
302302
&*body,
303303
traversal::reverse_postorder(&*body).map(|(bb, _)| bb),
304304
&results,
@@ -509,7 +509,7 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
509509
// 2. loans made in overlapping scopes do not conflict
510510
// 3. assignments do not affect things loaned out as immutable
511511
// 4. moves do not affect things loaned out in any way
512-
impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
512+
impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
513513
type FlowState = Flows<'cx, 'tcx>;
514514

515515
fn visit_statement(

src/librustc_mir/borrow_check/nll.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ use std::str::FromStr;
2121
use self::mir_util::PassWhere;
2222
use polonius_engine::{Algorithm, Output};
2323

24-
use crate::dataflow::generic::ResultsCursor;
2524
use crate::dataflow::move_paths::{InitKind, InitLocation, MoveData};
2625
use crate::dataflow::MaybeInitializedPlaces;
26+
use crate::dataflow::ResultsCursor;
2727
use crate::transform::MirSource;
2828
use crate::util as mir_util;
2929
use crate::util::pretty;

src/librustc_mir/borrow_check/type_check/liveness/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use rustc::ty::{RegionVid, TyCtxt};
33
use rustc_data_structures::fx::FxHashSet;
44
use std::rc::Rc;
55

6-
use crate::dataflow::generic::ResultsCursor;
76
use crate::dataflow::move_paths::MoveData;
87
use crate::dataflow::MaybeInitializedPlaces;
8+
use crate::dataflow::ResultsCursor;
99

1010
use crate::borrow_check::{
1111
constraints::OutlivesConstraintSet,

src/librustc_mir/borrow_check/type_check/liveness/trace.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
88
use rustc_trait_selection::traits::query::type_op::TypeOp;
99
use std::rc::Rc;
1010

11-
use crate::dataflow::generic::ResultsCursor;
1211
use crate::dataflow::indexes::MovePathIndex;
1312
use crate::dataflow::move_paths::{HasMoveData, MoveData};
1413
use crate::dataflow::MaybeInitializedPlaces;
14+
use crate::dataflow::ResultsCursor;
1515

1616
use crate::borrow_check::{
1717
region_infer::values::{self, PointIndex, RegionValueElements},

src/librustc_mir/borrow_check/type_check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
3939
use rustc_trait_selection::traits::query::{Fallible, NoSolution};
4040
use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations};
4141

42-
use crate::dataflow::generic::ResultsCursor;
4342
use crate::dataflow::move_paths::MoveData;
4443
use crate::dataflow::MaybeInitializedPlaces;
44+
use crate::dataflow::ResultsCursor;
4545
use crate::transform::promote_consts::should_suggest_const_in_array_repeat_expressions_attribute;
4646

4747
use crate::borrow_check::{

src/librustc_mir/dataflow/at_location.rs

-169
This file was deleted.

src/librustc_mir/dataflow/generic/mod.rs renamed to src/librustc_mir/dataflow/framework/mod.rs

+56-19
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
//! A framework that can express both [gen-kill] and generic dataflow problems.
22
//!
3-
//! There is another interface for dataflow in the compiler in `librustc_mir/dataflow/mod.rs`. The
4-
//! interface in this module will eventually [replace that one][design-meeting].
3+
//! To actually use this framework, you must implement either the `Analysis` or the
4+
//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill
5+
//! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The
6+
//! `impls` module contains several examples of gen/kill dataflow analyses.
57
//!
6-
//! To actually use this framework, you must implement either the `Analysis` or the `GenKillAnalysis`
7-
//! trait. If your transfer function can be expressed with only gen/kill operations, prefer
8-
//! `GenKillAnalysis` since it will run faster while iterating to fixpoint. Create an `Engine` using
9-
//! the appropriate constructor and call `iterate_to_fixpoint`. You can use a `ResultsCursor` to
10-
//! inspect the fixpoint solution to your dataflow problem.
8+
//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait,
9+
//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the
10+
//! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use
11+
//! `visit_results`. The following example uses the `ResultsCursor` approach.
1112
//!
1213
//! ```ignore(cross-crate-imports)
13-
//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) {
14-
//! let analysis = MyAnalysis::new();
15-
//!
16-
//! // If `MyAnalysis` implements `GenKillAnalysis`.
17-
//! let results = Engine::new_gen_kill(tcx, body, did, analysis).iterate_to_fixpoint();
14+
//! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available.
1815
//!
19-
//! // If `MyAnalysis` implements `Analysis`.
20-
//! // let results = Engine::new_generic(tcx, body, did, analysis).iterate_to_fixpoint();
21-
//!
22-
//! let mut cursor = ResultsCursor::new(body, results);
16+
//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) {
17+
//! let analysis = MyAnalysis::new()
18+
//! .into_engine(tcx, body, did)
19+
//! .iterate_to_fixpoint()
20+
//! .into_results_cursor(body);
2321
//!
22+
//! // Print the dataflow state *after* each statement in the start block.
2423
//! for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() {
2524
//! cursor.seek_after(Location { block: START_BLOCK, statement_index });
2625
//! let state = cursor.get();
@@ -30,7 +29,6 @@
3029
//! ```
3130
//!
3231
//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
33-
//! [design-meeting]https://github.com/rust-lang/compiler-team/issues/202
3432
3533
use std::io;
3634

@@ -41,8 +39,6 @@ use rustc_hir::def_id::DefId;
4139
use rustc_index::bit_set::{BitSet, HybridBitSet};
4240
use rustc_index::vec::{Idx, IndexVec};
4341

44-
use crate::dataflow::BottomValue;
45-
4642
mod cursor;
4743
mod engine;
4844
mod graphviz;
@@ -95,6 +91,47 @@ where
9591
}
9692
}
9793

94+
/// Parameterization for the precise form of data flow that is used.
95+
///
96+
/// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
97+
/// This also determines the semantics of the lattice `join` operator used to merge dataflow
98+
/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed
99+
/// point.
100+
///
101+
/// This means, for propagation across the graph, that you either want to start at all-zeroes and
102+
/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect
103+
/// as your merge when propagating.
104+
pub trait BottomValue {
105+
/// Specifies the initial value for each bit in the entry set for each basic block.
106+
const BOTTOM_VALUE: bool;
107+
108+
/// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
109+
///
110+
/// It is almost certainly wrong to override this, since it automatically applies
111+
/// * `inout_set & in_set` if `BOTTOM_VALUE == true`
112+
/// * `inout_set | in_set` if `BOTTOM_VALUE == false`
113+
///
114+
/// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks.
115+
/// For clarity, the above statement again from a different perspective:
116+
/// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is
117+
/// `!BOTTOM_VALUE`.
118+
///
119+
/// There are situations where you want the opposite behaviour: propagate only if *all*
120+
/// predecessor blocks's value is `!BOTTOM_VALUE`.
121+
/// E.g. if you want to know whether a bit is *definitely* set at a specific location. This
122+
/// means that all code paths leading to the location must have set the bit, instead of any
123+
/// code path leading there.
124+
///
125+
/// If you want this kind of "definitely set" analysis, you need to
126+
/// 1. Invert `BOTTOM_VALUE`
127+
/// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE`
128+
/// 3. Override `join` to do the opposite from what it's doing now.
129+
#[inline]
130+
fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
131+
if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) }
132+
}
133+
}
134+
98135
/// Define the domain of a dataflow problem.
99136
///
100137
/// This trait specifies the lattice on which this analysis operates. For now, this must be a

src/librustc_mir/dataflow/impls/borrowed_locals.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pub use super::*;
22

3-
use crate::dataflow::generic::{AnalysisDomain, GenKill, GenKillAnalysis};
3+
use crate::dataflow::{AnalysisDomain, GenKill, GenKillAnalysis};
44
use rustc::mir::visit::Visitor;
55
use rustc::mir::*;
66
use rustc::ty::{ParamEnv, TyCtxt};

src/librustc_mir/dataflow/impls/borrows.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use rustc_index::bit_set::BitSet;
88
use crate::borrow_check::{
99
places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid,
1010
};
11-
use crate::dataflow::generic::{self, GenKill};
1211
use crate::dataflow::BottomValue;
12+
use crate::dataflow::{self, GenKill};
1313

1414
use std::rc::Rc;
1515

@@ -226,7 +226,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
226226
}
227227
}
228228

229-
impl<'tcx> generic::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
229+
impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
230230
type Idx = BorrowIndex;
231231

232232
const NAME: &'static str = "borrows";
@@ -245,7 +245,7 @@ impl<'tcx> generic::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
245245
}
246246
}
247247

248-
impl<'tcx> generic::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
248+
impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
249249
fn before_statement_effect(
250250
&self,
251251
trans: &mut impl GenKill<Self::Idx>,

0 commit comments

Comments
 (0)