]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #69644 - ecstatic-morse:unified-dataflow-cleanup, r=eddyb
authorDylan DPC <dylan.dpc@gmail.com>
Fri, 27 Mar 2020 00:23:47 +0000 (01:23 +0100)
committerGitHub <noreply@github.com>
Fri, 27 Mar 2020 00:23:47 +0000 (01:23 +0100)
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-guide#564.

`clippy` will need to change the path it uses to import the dataflow analysis traits.

28 files changed:
src/librustc_mir/borrow_check/mod.rs
src/librustc_mir/borrow_check/nll.rs
src/librustc_mir/borrow_check/type_check/liveness/mod.rs
src/librustc_mir/borrow_check/type_check/liveness/trace.rs
src/librustc_mir/borrow_check/type_check/mod.rs
src/librustc_mir/dataflow/at_location.rs [deleted file]
src/librustc_mir/dataflow/framework/cursor.rs [new file with mode: 0644]
src/librustc_mir/dataflow/framework/engine.rs [new file with mode: 0644]
src/librustc_mir/dataflow/framework/graphviz.rs [new file with mode: 0644]
src/librustc_mir/dataflow/framework/mod.rs [new file with mode: 0644]
src/librustc_mir/dataflow/framework/tests.rs [new file with mode: 0644]
src/librustc_mir/dataflow/framework/visitor.rs [new file with mode: 0644]
src/librustc_mir/dataflow/generic/cursor.rs [deleted file]
src/librustc_mir/dataflow/generic/engine.rs [deleted file]
src/librustc_mir/dataflow/generic/graphviz.rs [deleted file]
src/librustc_mir/dataflow/generic/mod.rs [deleted file]
src/librustc_mir/dataflow/generic/tests.rs [deleted file]
src/librustc_mir/dataflow/generic/visitor.rs [deleted file]
src/librustc_mir/dataflow/impls/borrowed_locals.rs
src/librustc_mir/dataflow/impls/borrows.rs
src/librustc_mir/dataflow/impls/mod.rs
src/librustc_mir/dataflow/impls/storage_liveness.rs
src/librustc_mir/dataflow/mod.rs
src/librustc_mir/transform/check_consts/resolver.rs
src/librustc_mir/transform/check_consts/validation.rs
src/librustc_mir/transform/elaborate_drops.rs
src/librustc_mir/transform/generator.rs
src/librustc_mir/transform/rustc_peek.rs

index 6c1901455fda95f4868f550e6c65862af19c59e3..882c6bd0333eeaffc6f4cac9c7dd8279c90db799 100644 (file)
 use std::rc::Rc;
 
 use crate::dataflow;
-use crate::dataflow::generic::{Analysis, BorrowckFlowState as Flows, BorrowckResults};
 use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex};
 use crate::dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError};
 use crate::dataflow::Borrows;
 use crate::dataflow::EverInitializedPlaces;
 use crate::dataflow::MoveDataParamEnv;
+use crate::dataflow::{Analysis, BorrowckFlowState as Flows, BorrowckResults};
 use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
 use crate::transform::MirSource;
 
@@ -298,7 +298,7 @@ fn do_mir_borrowck<'a, 'tcx>(
         mbcx.report_move_errors(errors);
     }
 
-    dataflow::generic::visit_results(
+    dataflow::visit_results(
         &*body,
         traversal::reverse_postorder(&*body).map(|(bb, _)| bb),
         &results,
@@ -509,7 +509,7 @@ fn do_mir_borrowck<'a, 'tcx>(
 // 2. loans made in overlapping scopes do not conflict
 // 3. assignments do not affect things loaned out as immutable
 // 4. moves do not affect things loaned out in any way
-impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
+impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
     type FlowState = Flows<'cx, 'tcx>;
 
     fn visit_statement(
index 077ed49ed2cacc8fc248a8e1f5c3ed561b36536f..8e929a4fa22f48ee640d73dee11f434985525069 100644 (file)
@@ -21,9 +21,9 @@
 use self::mir_util::PassWhere;
 use polonius_engine::{Algorithm, Output};
 
-use crate::dataflow::generic::ResultsCursor;
 use crate::dataflow::move_paths::{InitKind, InitLocation, MoveData};
 use crate::dataflow::MaybeInitializedPlaces;
+use crate::dataflow::ResultsCursor;
 use crate::transform::MirSource;
 use crate::util as mir_util;
 use crate::util::pretty;
index cdf962ee31a6ee66bad985e34eb47bb913b539ab..a55529ed0ef12efd2c1adc93d76c40013a480f83 100644 (file)
@@ -3,9 +3,9 @@
 use rustc_data_structures::fx::FxHashSet;
 use std::rc::Rc;
 
-use crate::dataflow::generic::ResultsCursor;
 use crate::dataflow::move_paths::MoveData;
 use crate::dataflow::MaybeInitializedPlaces;
+use crate::dataflow::ResultsCursor;
 
 use crate::borrow_check::{
     constraints::OutlivesConstraintSet,
index 0c49ee44f9a5025ad6f63f779d74f62e1245f672..75b269c79b96e59e16b45957cd4cb9348f3ff06e 100644 (file)
@@ -8,10 +8,10 @@
 use rustc_trait_selection::traits::query::type_op::TypeOp;
 use std::rc::Rc;
 
-use crate::dataflow::generic::ResultsCursor;
 use crate::dataflow::indexes::MovePathIndex;
 use crate::dataflow::move_paths::{HasMoveData, MoveData};
 use crate::dataflow::MaybeInitializedPlaces;
+use crate::dataflow::ResultsCursor;
 
 use crate::borrow_check::{
     region_infer::values::{self, PointIndex, RegionValueElements},
index f94160cc08a19ee4027a020cedb87e632952184a..02f6bcade23a2cdc1200caaa039571b6e0af23f5 100644 (file)
@@ -39,9 +39,9 @@
 use rustc_trait_selection::traits::query::{Fallible, NoSolution};
 use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations};
 
-use crate::dataflow::generic::ResultsCursor;
 use crate::dataflow::move_paths::MoveData;
 use crate::dataflow::MaybeInitializedPlaces;
+use crate::dataflow::ResultsCursor;
 use crate::transform::promote_consts::should_suggest_const_in_array_repeat_expressions_attribute;
 
 use crate::borrow_check::{
diff --git a/src/librustc_mir/dataflow/at_location.rs b/src/librustc_mir/dataflow/at_location.rs
deleted file mode 100644 (file)
index e4eb850..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-//! A nice wrapper to consume dataflow results at several CFG
-//! locations.
-
-use rustc::mir::{BasicBlock, Location};
-use rustc_index::bit_set::{BitIter, BitSet, HybridBitSet};
-
-use crate::dataflow::{BitDenotation, DataflowResults, GenKillSet};
-
-use std::borrow::Borrow;
-use std::iter;
-
-/// A trait for "cartesian products" of multiple FlowAtLocation.
-///
-/// There's probably a way to auto-impl this, but I think
-/// it is cleaner to have manual visitor impls.
-pub trait FlowsAtLocation {
-    /// Reset the state bitvector to represent the entry to block `bb`.
-    fn reset_to_entry_of(&mut self, bb: BasicBlock);
-
-    /// Reset the state bitvector to represent the exit of the
-    /// terminator of block `bb`.
-    ///
-    /// **Important:** In the case of a `Call` terminator, these
-    /// effects do *not* include the result of storing the destination
-    /// of the call, since that is edge-dependent (in other words, the
-    /// effects don't apply to the unwind edge).
-    fn reset_to_exit_of(&mut self, bb: BasicBlock);
-
-    /// Builds gen and kill sets for statement at `loc`.
-    ///
-    /// Note that invoking this method alone does not change the
-    /// `curr_state` -- you must invoke `apply_local_effect`
-    /// afterwards.
-    fn reconstruct_statement_effect(&mut self, loc: Location);
-
-    /// Builds gen and kill sets for terminator for `loc`.
-    ///
-    /// Note that invoking this method alone does not change the
-    /// `curr_state` -- you must invoke `apply_local_effect`
-    /// afterwards.
-    fn reconstruct_terminator_effect(&mut self, loc: Location);
-
-    /// Apply current gen + kill sets to `flow_state`.
-    ///
-    /// (`loc` parameters can be ignored if desired by
-    /// client. For the terminator, the `stmt_idx` will be the number
-    /// of statements in the block.)
-    fn apply_local_effect(&mut self, loc: Location);
-}
-
-/// Represents the state of dataflow at a particular
-/// CFG location, both before and after it is
-/// executed.
-///
-/// Data flow results are typically computed only as basic block
-/// boundaries. A `FlowInProgress` allows you to reconstruct the
-/// effects at any point in the control-flow graph by starting with
-/// the state at the start of the basic block (`reset_to_entry_of`)
-/// and then replaying the effects of statements and terminators
-/// (e.g., via `reconstruct_statement_effect` and
-/// `reconstruct_terminator_effect`; don't forget to call
-/// `apply_local_effect`).
-pub struct FlowAtLocation<'tcx, BD, DR = DataflowResults<'tcx, BD>>
-where
-    BD: BitDenotation<'tcx>,
-    DR: Borrow<DataflowResults<'tcx, BD>>,
-{
-    base_results: DR,
-    curr_state: BitSet<BD::Idx>,
-    stmt_trans: GenKillSet<BD::Idx>,
-}
-
-impl<'tcx, BD, DR> FlowAtLocation<'tcx, BD, DR>
-where
-    BD: BitDenotation<'tcx>,
-    DR: Borrow<DataflowResults<'tcx, BD>>,
-{
-    /// Iterate over each bit set in the current state.
-    pub fn each_state_bit<F>(&self, f: F)
-    where
-        F: FnMut(BD::Idx),
-    {
-        self.curr_state.iter().for_each(f)
-    }
-
-    /// Iterate over each `gen` bit in the current effect (invoke
-    /// `reconstruct_statement_effect` or
-    /// `reconstruct_terminator_effect` first).
-    pub fn each_gen_bit<F>(&self, f: F)
-    where
-        F: FnMut(BD::Idx),
-    {
-        self.stmt_trans.gen_set.iter().for_each(f)
-    }
-
-    pub fn new(results: DR) -> Self {
-        let bits_per_block = results.borrow().sets().bits_per_block();
-        let curr_state = BitSet::new_empty(bits_per_block);
-        let stmt_trans = GenKillSet::from_elem(HybridBitSet::new_empty(bits_per_block));
-        FlowAtLocation { base_results: results, curr_state, stmt_trans }
-    }
-
-    /// Access the underlying operator.
-    pub fn operator(&self) -> &BD {
-        self.base_results.borrow().operator()
-    }
-
-    pub fn contains(&self, x: BD::Idx) -> bool {
-        self.curr_state.contains(x)
-    }
-
-    /// Returns an iterator over the elements present in the current state.
-    pub fn iter_incoming(&self) -> iter::Peekable<BitIter<'_, BD::Idx>> {
-        self.curr_state.iter().peekable()
-    }
-
-    /// Creates a clone of the current state and applies the local
-    /// effects to the clone (leaving the state of self intact).
-    /// Invokes `f` with an iterator over the resulting state.
-    pub fn with_iter_outgoing<F>(&self, f: F)
-    where
-        F: FnOnce(BitIter<'_, BD::Idx>),
-    {
-        let mut curr_state = self.curr_state.clone();
-        self.stmt_trans.apply(&mut curr_state);
-        f(curr_state.iter());
-    }
-
-    /// Returns a bitset of the elements present in the current state.
-    pub fn as_dense(&self) -> &BitSet<BD::Idx> {
-        &self.curr_state
-    }
-}
-
-impl<'tcx, BD, DR> FlowsAtLocation for FlowAtLocation<'tcx, BD, DR>
-where
-    BD: BitDenotation<'tcx>,
-    DR: Borrow<DataflowResults<'tcx, BD>>,
-{
-    fn reset_to_entry_of(&mut self, bb: BasicBlock) {
-        self.curr_state.overwrite(self.base_results.borrow().sets().entry_set_for(bb.index()));
-    }
-
-    fn reset_to_exit_of(&mut self, bb: BasicBlock) {
-        self.reset_to_entry_of(bb);
-        let trans = self.base_results.borrow().sets().trans_for(bb.index());
-        trans.apply(&mut self.curr_state)
-    }
-
-    fn reconstruct_statement_effect(&mut self, loc: Location) {
-        self.stmt_trans.clear();
-        self.base_results.borrow().operator().before_statement_effect(&mut self.stmt_trans, loc);
-        self.stmt_trans.apply(&mut self.curr_state);
-
-        self.base_results.borrow().operator().statement_effect(&mut self.stmt_trans, loc);
-    }
-
-    fn reconstruct_terminator_effect(&mut self, loc: Location) {
-        self.stmt_trans.clear();
-        self.base_results.borrow().operator().before_terminator_effect(&mut self.stmt_trans, loc);
-        self.stmt_trans.apply(&mut self.curr_state);
-
-        self.base_results.borrow().operator().terminator_effect(&mut self.stmt_trans, loc);
-    }
-
-    fn apply_local_effect(&mut self, _loc: Location) {
-        self.stmt_trans.apply(&mut self.curr_state)
-    }
-}
diff --git a/src/librustc_mir/dataflow/framework/cursor.rs b/src/librustc_mir/dataflow/framework/cursor.rs
new file mode 100644 (file)
index 0000000..170157a
--- /dev/null
@@ -0,0 +1,280 @@
+//! Random access inspection of the results of a dataflow analysis.
+
+use std::borrow::Borrow;
+
+use rustc::mir::{self, BasicBlock, Location, TerminatorKind};
+use rustc_index::bit_set::BitSet;
+
+use super::{Analysis, Results};
+
+/// A `ResultsCursor` that borrows the underlying `Results`.
+pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;
+
+/// Allows random access inspection of the results of a dataflow analysis.
+///
+/// This cursor only has linear performance within a basic block when its statements are visited in
+/// order. In the worst case—when statements are visited in *reverse* order—performance will be
+/// quadratic in the number of statements in the block. The order in which basic blocks are
+/// inspected has no impact on performance.
+///
+/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
+/// type of ownership is determined by `R` (see `ResultsRefCursor` above).
+pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
+where
+    A: Analysis<'tcx>,
+{
+    body: &'mir mir::Body<'tcx>,
+    results: R,
+    state: BitSet<A::Idx>,
+
+    pos: CursorPosition,
+
+    /// When this flag is set, the cursor is pointing at a `Call` or `Yield` terminator whose call
+    /// return or resume effect has been applied to `state`.
+    ///
+    /// This flag helps to ensure that multiple calls to `seek_after_assume_success` with the
+    /// same target will result in exactly one invocation of `apply_call_return_effect`. It is
+    /// sufficient to clear this only in `seek_to_block_start`, since seeking away from a
+    /// terminator will always require a cursor reset.
+    success_effect_applied: bool,
+}
+
+impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
+where
+    A: Analysis<'tcx>,
+    R: Borrow<Results<'tcx, A>>,
+{
+    /// Returns a new cursor for `results` that points to the start of the `START_BLOCK`.
+    pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
+        ResultsCursor {
+            body,
+            pos: CursorPosition::BlockStart(mir::START_BLOCK),
+            state: results.borrow().entry_sets[mir::START_BLOCK].clone(),
+            success_effect_applied: false,
+            results,
+        }
+    }
+
+    /// Returns the `Analysis` used to generate the underlying results.
+    pub fn analysis(&self) -> &A {
+        &self.results.borrow().analysis
+    }
+
+    /// Returns the dataflow state at the current location.
+    pub fn get(&self) -> &BitSet<A::Idx> {
+        &self.state
+    }
+
+    /// Returns `true` if the dataflow state at the current location contains the given element.
+    ///
+    /// Shorthand for `self.get().contains(elem)`
+    pub fn contains(&self, elem: A::Idx) -> bool {
+        self.state.contains(elem)
+    }
+
+    /// Resets the cursor to the start of the given basic block.
+    pub fn seek_to_block_start(&mut self, block: BasicBlock) {
+        self.state.overwrite(&self.results.borrow().entry_sets[block]);
+        self.pos = CursorPosition::BlockStart(block);
+        self.success_effect_applied = false;
+    }
+
+    /// Advances the cursor to hold all effects up to and including to the "before" effect of the
+    /// statement (or terminator) at the given location.
+    ///
+    /// If you wish to observe the full effect of a statement or terminator, not just the "before"
+    /// effect, use `seek_after` or `seek_after_assume_success`.
+    pub fn seek_before(&mut self, target: Location) {
+        assert!(target <= self.body.terminator_loc(target.block));
+        self.seek_(target, false);
+    }
+
+    /// Advances the cursor to hold the full effect of all statements (and possibly closing
+    /// terminators) up to and including the `target`.
+    ///
+    /// If the `target` is a `Call` terminator, any call return effect for that terminator will
+    /// **not** be observed. Use `seek_after_assume_success` if you wish to observe the call
+    /// return effect.
+    pub fn seek_after(&mut self, target: Location) {
+        assert!(target <= self.body.terminator_loc(target.block));
+
+        // If we have already applied the call return effect, we are currently pointing at a `Call`
+        // terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo"
+        // the call return effect.
+        if self.success_effect_applied {
+            self.seek_to_block_start(target.block);
+        }
+
+        self.seek_(target, true);
+    }
+
+    /// Advances the cursor to hold all effects up to and including of the statement (or
+    /// terminator) at the given location.
+    ///
+    /// If the `target` is a `Call` or `Yield` terminator, any call return or resume effect for that
+    /// terminator will be observed. Use `seek_after` if you do **not** wish to observe the
+    /// "success" effect.
+    pub fn seek_after_assume_success(&mut self, target: Location) {
+        let terminator_loc = self.body.terminator_loc(target.block);
+        assert!(target.statement_index <= terminator_loc.statement_index);
+
+        self.seek_(target, true);
+
+        if target != terminator_loc || self.success_effect_applied {
+            return;
+        }
+
+        // Apply the effect of the "success" path of the terminator.
+
+        self.success_effect_applied = true;
+        let terminator = self.body.basic_blocks()[target.block].terminator();
+        match &terminator.kind {
+            TerminatorKind::Call { destination: Some((return_place, _)), func, args, .. } => {
+                self.results.borrow().analysis.apply_call_return_effect(
+                    &mut self.state,
+                    target.block,
+                    func,
+                    args,
+                    return_place,
+                );
+            }
+            TerminatorKind::Yield { resume, resume_arg, .. } => {
+                self.results.borrow().analysis.apply_yield_resume_effect(
+                    &mut self.state,
+                    *resume,
+                    resume_arg,
+                );
+            }
+            _ => {}
+        }
+    }
+
+    fn seek_(&mut self, target: Location, apply_after_effect_at_target: bool) {
+        use CursorPosition::*;
+
+        match self.pos {
+            // Return early if we are already at the target location.
+            Before(curr) if curr == target && !apply_after_effect_at_target => return,
+            After(curr) if curr == target && apply_after_effect_at_target => return,
+
+            // Otherwise, we must reset to the start of the target block if...
+
+            // we are in a different block entirely.
+            BlockStart(block) | Before(Location { block, .. }) | After(Location { block, .. })
+                if block != target.block =>
+            {
+                self.seek_to_block_start(target.block)
+            }
+
+            // we are in the same block but have advanced past the target statement.
+            Before(curr) | After(curr) if curr.statement_index > target.statement_index => {
+                self.seek_to_block_start(target.block)
+            }
+
+            // we have already applied the entire effect of a statement but only wish to observe
+            // its "before" effect.
+            After(curr)
+                if curr.statement_index == target.statement_index
+                    && !apply_after_effect_at_target =>
+            {
+                self.seek_to_block_start(target.block)
+            }
+
+            // N.B., `success_effect_applied` is checked in `seek_after`, not here.
+            _ => (),
+        }
+
+        let analysis = &self.results.borrow().analysis;
+        let block_data = &self.body.basic_blocks()[target.block];
+
+        // At this point, the cursor is in the same block as the target location at an earlier
+        // statement.
+        debug_assert_eq!(target.block, self.pos.block());
+
+        // Find the first statement whose transfer function has not yet been applied.
+        let first_unapplied_statement = match self.pos {
+            BlockStart(_) => 0,
+            After(Location { statement_index, .. }) => statement_index + 1,
+
+            // If we have only applied the "before" effect for the current statement, apply the
+            // remainder before continuing.
+            Before(curr) => {
+                if curr.statement_index == block_data.statements.len() {
+                    let terminator = block_data.terminator();
+                    analysis.apply_terminator_effect(&mut self.state, terminator, curr);
+                } else {
+                    let statement = &block_data.statements[curr.statement_index];
+                    analysis.apply_statement_effect(&mut self.state, statement, curr);
+                }
+
+                // If all we needed to do was go from `Before` to `After` in the same statement,
+                // we are now done.
+                if curr.statement_index == target.statement_index {
+                    debug_assert!(apply_after_effect_at_target);
+                    self.pos = After(target);
+                    return;
+                }
+
+                curr.statement_index + 1
+            }
+        };
+
+        // We have now applied all effects prior to `first_unapplied_statement`.
+
+        // Apply the effects of all statements before `target`.
+        let mut location = Location { block: target.block, statement_index: 0 };
+        for statement_index in first_unapplied_statement..target.statement_index {
+            location.statement_index = statement_index;
+            let statement = &block_data.statements[statement_index];
+            analysis.apply_before_statement_effect(&mut self.state, statement, location);
+            analysis.apply_statement_effect(&mut self.state, statement, location);
+        }
+
+        // Apply the effect of the statement (or terminator) at `target`.
+        location.statement_index = target.statement_index;
+        if target.statement_index == block_data.statements.len() {
+            let terminator = &block_data.terminator();
+            analysis.apply_before_terminator_effect(&mut self.state, terminator, location);
+
+            if apply_after_effect_at_target {
+                analysis.apply_terminator_effect(&mut self.state, terminator, location);
+                self.pos = After(target);
+            } else {
+                self.pos = Before(target);
+            }
+        } else {
+            let statement = &block_data.statements[target.statement_index];
+            analysis.apply_before_statement_effect(&mut self.state, statement, location);
+
+            if apply_after_effect_at_target {
+                analysis.apply_statement_effect(&mut self.state, statement, location);
+                self.pos = After(target)
+            } else {
+                self.pos = Before(target);
+            }
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+enum CursorPosition {
+    /// No effects within this block have been applied.
+    BlockStart(BasicBlock),
+
+    /// Only the "before" effect of the statement (or terminator) at this location has been
+    /// applied (along with the effects of all previous statements).
+    Before(Location),
+
+    /// The effects of all statements up to and including the one at this location have been
+    /// applied.
+    After(Location),
+}
+
+impl CursorPosition {
+    fn block(&self) -> BasicBlock {
+        match *self {
+            Self::BlockStart(block) => block,
+            Self::Before(loc) | Self::After(loc) => loc.block,
+        }
+    }
+}
diff --git a/src/librustc_mir/dataflow/framework/engine.rs b/src/librustc_mir/dataflow/framework/engine.rs
new file mode 100644 (file)
index 0000000..d320721
--- /dev/null
@@ -0,0 +1,525 @@
+//! A solver for dataflow problems.
+
+use std::ffi::OsString;
+use std::fs;
+use std::path::PathBuf;
+
+use rustc::mir::{self, traversal, BasicBlock, Location};
+use rustc::ty::{self, TyCtxt};
+use rustc_ast::ast;
+use rustc_data_structures::work_queue::WorkQueue;
+use rustc_hir::def_id::DefId;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_span::symbol::{sym, Symbol};
+
+use super::graphviz;
+use super::{Analysis, GenKillAnalysis, GenKillSet, Results};
+
+/// A solver for dataflow problems.
+pub struct Engine<'a, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    bits_per_block: usize,
+    tcx: TyCtxt<'tcx>,
+    body: &'a mir::Body<'tcx>,
+    def_id: DefId,
+    dead_unwinds: Option<&'a BitSet<BasicBlock>>,
+    entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
+    analysis: A,
+
+    /// Cached, cumulative transfer functions for each block.
+    trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+}
+
+impl<A> Engine<'a, 'tcx, A>
+where
+    A: GenKillAnalysis<'tcx>,
+{
+    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
+    pub fn new_gen_kill(
+        tcx: TyCtxt<'tcx>,
+        body: &'a mir::Body<'tcx>,
+        def_id: DefId,
+        analysis: A,
+    ) -> Self {
+        // If there are no back-edges in the control-flow graph, we only ever need to apply the
+        // transfer function for each block exactly once (assuming that we process blocks in RPO).
+        //
+        // In this case, there's no need to compute the block transfer functions ahead of time.
+        if !body.is_cfg_cyclic() {
+            return Self::new(tcx, body, def_id, analysis, None);
+        }
+
+        // Otherwise, compute and store the cumulative transfer function for each block.
+
+        let bits_per_block = analysis.bits_per_block(body);
+        let mut trans_for_block =
+            IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks());
+
+        for (block, block_data) in body.basic_blocks().iter_enumerated() {
+            let trans = &mut trans_for_block[block];
+
+            for (i, statement) in block_data.statements.iter().enumerate() {
+                let loc = Location { block, statement_index: i };
+                analysis.before_statement_effect(trans, statement, loc);
+                analysis.statement_effect(trans, statement, loc);
+            }
+
+            let terminator = block_data.terminator();
+            let loc = Location { block, statement_index: block_data.statements.len() };
+            analysis.before_terminator_effect(trans, terminator, loc);
+            analysis.terminator_effect(trans, terminator, loc);
+        }
+
+        Self::new(tcx, body, def_id, analysis, Some(trans_for_block))
+    }
+}
+
+impl<A> Engine<'a, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
+    /// function.
+    ///
+    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
+    /// better performance.
+    pub fn new_generic(
+        tcx: TyCtxt<'tcx>,
+        body: &'a mir::Body<'tcx>,
+        def_id: DefId,
+        analysis: A,
+    ) -> Self {
+        Self::new(tcx, body, def_id, analysis, None)
+    }
+
+    fn new(
+        tcx: TyCtxt<'tcx>,
+        body: &'a mir::Body<'tcx>,
+        def_id: DefId,
+        analysis: A,
+        trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+    ) -> Self {
+        let bits_per_block = analysis.bits_per_block(body);
+
+        let bottom_value_set = if A::BOTTOM_VALUE {
+            BitSet::new_filled(bits_per_block)
+        } else {
+            BitSet::new_empty(bits_per_block)
+        };
+
+        let mut entry_sets = IndexVec::from_elem(bottom_value_set, body.basic_blocks());
+        analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
+
+        Engine {
+            analysis,
+            bits_per_block,
+            tcx,
+            body,
+            def_id,
+            dead_unwinds: None,
+            entry_sets,
+            trans_for_block,
+        }
+    }
+
+    /// Signals that we do not want dataflow state to propagate across unwind edges for these
+    /// `BasicBlock`s.
+    ///
+    /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
+    /// unwind during execution. Otherwise, your dataflow results will not be correct.
+    pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
+        self.dead_unwinds = Some(dead_unwinds);
+        self
+    }
+
+    /// Computes the fixpoint for this dataflow problem and returns it.
+    pub fn iterate_to_fixpoint(mut self) -> Results<'tcx, A> {
+        let mut temp_state = BitSet::new_empty(self.bits_per_block);
+
+        let mut dirty_queue: WorkQueue<BasicBlock> =
+            WorkQueue::with_none(self.body.basic_blocks().len());
+
+        for (bb, _) in traversal::reverse_postorder(self.body) {
+            dirty_queue.insert(bb);
+        }
+
+        // Add blocks that are not reachable from START_BLOCK to the work queue. These blocks will
+        // be processed after the ones added above.
+        for bb in self.body.basic_blocks().indices() {
+            dirty_queue.insert(bb);
+        }
+
+        while let Some(bb) = dirty_queue.pop() {
+            let bb_data = &self.body[bb];
+            let on_entry = &self.entry_sets[bb];
+
+            temp_state.overwrite(on_entry);
+            self.apply_whole_block_effect(&mut temp_state, bb, bb_data);
+
+            self.propagate_bits_into_graph_successors_of(
+                &mut temp_state,
+                (bb, bb_data),
+                &mut dirty_queue,
+            );
+        }
+
+        let Engine { tcx, body, def_id, trans_for_block, entry_sets, analysis, .. } = self;
+        let results = Results { analysis, entry_sets };
+
+        let res = write_graphviz_results(tcx, def_id, body, &results, trans_for_block);
+        if let Err(e) = res {
+            warn!("Failed to write graphviz dataflow results: {}", e);
+        }
+
+        results
+    }
+
+    /// Applies the cumulative effect of an entire block, excluding the call return effect if one
+    /// exists.
+    fn apply_whole_block_effect(
+        &self,
+        state: &mut BitSet<A::Idx>,
+        block: BasicBlock,
+        block_data: &mir::BasicBlockData<'tcx>,
+    ) {
+        // Use the cached block transfer function if available.
+        if let Some(trans_for_block) = &self.trans_for_block {
+            trans_for_block[block].apply(state);
+            return;
+        }
+
+        // Otherwise apply effects one-by-one.
+
+        for (statement_index, statement) in block_data.statements.iter().enumerate() {
+            let location = Location { block, statement_index };
+            self.analysis.apply_before_statement_effect(state, statement, location);
+            self.analysis.apply_statement_effect(state, statement, location);
+        }
+
+        let terminator = block_data.terminator();
+        let location = Location { block, statement_index: block_data.statements.len() };
+        self.analysis.apply_before_terminator_effect(state, terminator, location);
+        self.analysis.apply_terminator_effect(state, terminator, location);
+    }
+
+    fn propagate_bits_into_graph_successors_of(
+        &mut self,
+        in_out: &mut BitSet<A::Idx>,
+        (bb, bb_data): (BasicBlock, &'a mir::BasicBlockData<'tcx>),
+        dirty_list: &mut WorkQueue<BasicBlock>,
+    ) {
+        use mir::TerminatorKind::*;
+
+        match bb_data.terminator().kind {
+            Return | Resume | Abort | GeneratorDrop | Unreachable => {}
+
+            Goto { target }
+            | Assert { target, cleanup: None, .. }
+            | Drop { target, location: _, unwind: None }
+            | DropAndReplace { target, value: _, location: _, unwind: None } => {
+                self.propagate_bits_into_entry_set_for(in_out, target, dirty_list)
+            }
+
+            Yield { resume: target, drop, resume_arg, .. } => {
+                if let Some(drop) = drop {
+                    self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list);
+                }
+
+                self.analysis.apply_yield_resume_effect(in_out, target, &resume_arg);
+                self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
+            }
+
+            Assert { target, cleanup: Some(unwind), .. }
+            | Drop { target, location: _, unwind: Some(unwind) }
+            | DropAndReplace { target, value: _, location: _, unwind: Some(unwind) } => {
+                self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
+                if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) {
+                    self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
+                }
+            }
+
+            SwitchInt { ref targets, ref values, ref discr, .. } => {
+                let Engine { tcx, body, .. } = *self;
+                let enum_ = discr
+                    .place()
+                    .and_then(|discr| switch_on_enum_discriminant(tcx, body, bb_data, discr));
+                match enum_ {
+                    // If this is a switch on an enum discriminant, a custom effect may be applied
+                    // along each outgoing edge.
+                    Some((enum_place, enum_def)) => {
+                        self.propagate_bits_into_enum_discriminant_switch_successors(
+                            in_out, bb, enum_def, enum_place, dirty_list, &*values, &*targets,
+                        );
+                    }
+
+                    // Otherwise, it's just a normal `SwitchInt`, and every successor sees the same
+                    // exit state.
+                    None => {
+                        for target in targets.iter().copied() {
+                            self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list);
+                        }
+                    }
+                }
+            }
+
+            Call { cleanup, ref destination, ref func, ref args, .. } => {
+                if let Some(unwind) = cleanup {
+                    if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) {
+                        self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
+                    }
+                }
+
+                if let Some((ref dest_place, dest_bb)) = *destination {
+                    // N.B.: This must be done *last*, otherwise the unwind path will see the call
+                    // return effect.
+                    self.analysis.apply_call_return_effect(in_out, bb, func, args, dest_place);
+                    self.propagate_bits_into_entry_set_for(in_out, dest_bb, dirty_list);
+                }
+            }
+
+            FalseEdges { real_target, imaginary_target } => {
+                self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
+                self.propagate_bits_into_entry_set_for(in_out, imaginary_target, dirty_list);
+            }
+
+            FalseUnwind { real_target, unwind } => {
+                self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
+                if let Some(unwind) = unwind {
+                    if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) {
+                        self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
+                    }
+                }
+            }
+        }
+    }
+
+    fn propagate_bits_into_entry_set_for(
+        &mut self,
+        in_out: &BitSet<A::Idx>,
+        bb: BasicBlock,
+        dirty_queue: &mut WorkQueue<BasicBlock>,
+    ) {
+        let entry_set = &mut self.entry_sets[bb];
+        let set_changed = self.analysis.join(entry_set, &in_out);
+        if set_changed {
+            dirty_queue.insert(bb);
+        }
+    }
+
+    fn propagate_bits_into_enum_discriminant_switch_successors(
+        &mut self,
+        in_out: &mut BitSet<A::Idx>,
+        bb: BasicBlock,
+        enum_def: &'tcx ty::AdtDef,
+        enum_place: &mir::Place<'tcx>,
+        dirty_list: &mut WorkQueue<BasicBlock>,
+        values: &[u128],
+        targets: &[BasicBlock],
+    ) {
+        // MIR building adds discriminants to the `values` array in the same order as they
+        // are yielded by `AdtDef::discriminants`. We rely on this to match each
+        // discriminant in `values` to its corresponding variant in linear time.
+        let mut tmp = BitSet::new_empty(in_out.domain_size());
+        let mut discriminants = enum_def.discriminants(self.tcx);
+        for (value, target) in values.iter().zip(targets.iter().copied()) {
+            let (variant_idx, _) = discriminants.find(|&(_, discr)| discr.val == *value).expect(
+                "Order of `AdtDef::discriminants` differed from that of `SwitchInt::values`",
+            );
+
+            tmp.overwrite(in_out);
+            self.analysis.apply_discriminant_switch_effect(
+                &mut tmp,
+                bb,
+                enum_place,
+                enum_def,
+                variant_idx,
+            );
+            self.propagate_bits_into_entry_set_for(&tmp, target, dirty_list);
+        }
+
+        std::mem::drop(tmp);
+
+        // Propagate dataflow state along the "otherwise" edge.
+        let otherwise = targets.last().copied().unwrap();
+        self.propagate_bits_into_entry_set_for(&in_out, otherwise, dirty_list);
+    }
+}
+
+/// 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:
+///   _42 = discriminant(_1)
+///   SwitchInt(_42, ..)
+///
+/// If the basic block matches this pattern, this function returns the place corresponding to the
+/// enum (`_1` in the example above) as well as the `AdtDef` of that enum.
+fn switch_on_enum_discriminant(
+    tcx: TyCtxt<'tcx>,
+    body: &'mir mir::Body<'tcx>,
+    block: &'mir mir::BasicBlockData<'tcx>,
+    switch_on: &mir::Place<'tcx>,
+) -> Option<(&'mir mir::Place<'tcx>, &'tcx ty::AdtDef)> {
+    match block.statements.last().map(|stmt| &stmt.kind) {
+        Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated))))
+            if lhs == switch_on =>
+        {
+            match &discriminated.ty(body, tcx).ty.kind {
+                ty::Adt(def, _) => Some((discriminated, def)),
+
+                // `Rvalue::Discriminant` is also used to get the active yield point for a
+                // generator, but we do not need edge-specific effects in that case. This may
+                // change in the future.
+                ty::Generator(..) => None,
+
+                t => bug!("`discriminant` called on unexpected type {:?}", t),
+            }
+        }
+
+        _ => None,
+    }
+}
+
+// Graphviz
+
+/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
+/// `rustc_mir` attributes.
+fn write_graphviz_results<A>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    body: &mir::Body<'tcx>,
+    results: &Results<'tcx, A>,
+    block_transfer_functions: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+) -> std::io::Result<()>
+where
+    A: Analysis<'tcx>,
+{
+    let attrs = match RustcMirAttrs::parse(tcx, def_id) {
+        Ok(attrs) => attrs,
+
+        // Invalid `rustc_mir` attrs will be reported using `span_err`.
+        Err(()) => return Ok(()),
+    };
+
+    let path = match attrs.output_path(A::NAME) {
+        Some(path) => path,
+        None => return Ok(()),
+    };
+
+    let bits_per_block = results.analysis.bits_per_block(body);
+
+    let mut formatter: Box<dyn graphviz::StateFormatter<'tcx, _>> = match attrs.formatter {
+        Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)),
+        Some(sym::gen_kill) => {
+            if let Some(trans_for_block) = block_transfer_functions {
+                Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block))
+            } else {
+                Box::new(graphviz::SimpleDiff::new(bits_per_block))
+            }
+        }
+
+        // Default to the `SimpleDiff` output style.
+        _ => Box::new(graphviz::SimpleDiff::new(bits_per_block)),
+    };
+
+    debug!("printing dataflow results for {:?} to {}", def_id, path.display());
+    let mut buf = Vec::new();
+
+    let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter);
+    dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?;
+    fs::write(&path, buf)?;
+    Ok(())
+}
+
+#[derive(Default)]
+struct RustcMirAttrs {
+    basename_and_suffix: Option<PathBuf>,
+    formatter: Option<Symbol>,
+}
+
+impl RustcMirAttrs {
+    fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
+        let attrs = tcx.get_attrs(def_id);
+
+        let mut result = Ok(());
+        let mut ret = RustcMirAttrs::default();
+
+        let rustc_mir_attrs = attrs
+            .iter()
+            .filter(|attr| attr.check_name(sym::rustc_mir))
+            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
+
+        for attr in rustc_mir_attrs {
+            let attr_result = if attr.check_name(sym::borrowck_graphviz_postflow) {
+                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
+                    let path = PathBuf::from(s.to_string());
+                    match path.file_name() {
+                        Some(_) => Ok(path),
+                        None => {
+                            tcx.sess.span_err(attr.span(), "path must end in a filename");
+                            Err(())
+                        }
+                    }
+                })
+            } else if attr.check_name(sym::borrowck_graphviz_format) {
+                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
+                    sym::gen_kill | sym::two_phase => Ok(s),
+                    _ => {
+                        tcx.sess.span_err(attr.span(), "unknown formatter");
+                        Err(())
+                    }
+                })
+            } else {
+                Ok(())
+            };
+
+            result = result.and(attr_result);
+        }
+
+        result.map(|()| ret)
+    }
+
+    fn set_field<T>(
+        field: &mut Option<T>,
+        tcx: TyCtxt<'tcx>,
+        attr: &ast::NestedMetaItem,
+        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
+    ) -> Result<(), ()> {
+        if field.is_some() {
+            tcx.sess
+                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));
+
+            return Err(());
+        }
+
+        if let Some(s) = attr.value_str() {
+            *field = Some(mapper(s)?);
+            Ok(())
+        } else {
+            tcx.sess
+                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
+            Err(())
+        }
+    }
+
+    /// Returns the path where dataflow results should be written, or `None`
+    /// `borrowck_graphviz_postflow` was not specified.
+    ///
+    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
+    ///
+    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
+    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
+        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
+        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
+
+        let mut file_name: OsString = analysis_name.into();
+        file_name.push("_");
+        file_name.push(suffix);
+        ret.set_file_name(file_name);
+
+        Some(ret)
+    }
+}
diff --git a/src/librustc_mir/dataflow/framework/graphviz.rs b/src/librustc_mir/dataflow/framework/graphviz.rs
new file mode 100644 (file)
index 0000000..c15f2a7
--- /dev/null
@@ -0,0 +1,696 @@
+//! A helpful diagram for debugging dataflow problems.
+
+use std::cell::RefCell;
+use std::{io, ops, str};
+
+use rustc::mir::{self, BasicBlock, Body, Location};
+use rustc_hir::def_id::DefId;
+use rustc_index::bit_set::{BitSet, HybridBitSet};
+use rustc_index::vec::{Idx, IndexVec};
+
+use super::{Analysis, GenKillSet, Results, ResultsRefCursor};
+use crate::util::graphviz_safe_def_name;
+
+pub struct Formatter<'a, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    body: &'a Body<'tcx>,
+    def_id: DefId,
+
+    // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
+    block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
+}
+
+impl<A> Formatter<'a, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    pub fn new(
+        body: &'a Body<'tcx>,
+        def_id: DefId,
+        results: &'a Results<'tcx, A>,
+        state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
+    ) -> Self {
+        let block_formatter = BlockFormatter {
+            bg: Background::Light,
+            results: ResultsRefCursor::new(body, results),
+            state_formatter,
+        };
+
+        Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) }
+    }
+}
+
+/// A pair of a basic block and an index into that basic blocks `successors`.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct CfgEdge {
+    source: BasicBlock,
+    index: usize,
+}
+
+fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
+    body[bb]
+        .terminator()
+        .successors()
+        .enumerate()
+        .map(|(index, _)| CfgEdge { source: bb, index })
+        .collect()
+}
+
+impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    type Node = BasicBlock;
+    type Edge = CfgEdge;
+
+    fn graph_id(&self) -> dot::Id<'_> {
+        let name = graphviz_safe_def_name(self.def_id);
+        dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
+    }
+
+    fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
+        dot::Id::new(format!("bb_{}", n.index())).unwrap()
+    }
+
+    fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
+        let mut label = Vec::new();
+        self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap();
+        dot::LabelText::html(String::from_utf8(label).unwrap())
+    }
+
+    fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
+        Some(dot::LabelText::label("none"))
+    }
+
+    fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
+        let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index];
+        dot::LabelText::label(label.clone())
+    }
+}
+
+impl<A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    type Node = BasicBlock;
+    type Edge = CfgEdge;
+
+    fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
+        self.body.basic_blocks().indices().collect::<Vec<_>>().into()
+    }
+
+    fn edges(&self) -> dot::Edges<'_, Self::Edge> {
+        self.body
+            .basic_blocks()
+            .indices()
+            .flat_map(|bb| outgoing_edges(self.body, bb))
+            .collect::<Vec<_>>()
+            .into()
+    }
+
+    fn source(&self, edge: &Self::Edge) -> Self::Node {
+        edge.source
+    }
+
+    fn target(&self, edge: &Self::Edge) -> Self::Node {
+        self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap()
+    }
+}
+
+struct BlockFormatter<'a, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    results: ResultsRefCursor<'a, 'a, 'tcx, A>,
+    bg: Background,
+    state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
+}
+
+impl<A> BlockFormatter<'a, 'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    const HEADER_COLOR: &'static str = "#a0a0a0";
+
+    fn num_state_columns(&self) -> usize {
+        std::cmp::max(1, self.state_formatter.column_names().len())
+    }
+
+    fn toggle_background(&mut self) -> Background {
+        let bg = self.bg;
+        self.bg = !bg;
+        bg
+    }
+
+    fn write_node_label(
+        &mut self,
+        w: &mut impl io::Write,
+        body: &'a Body<'tcx>,
+        block: BasicBlock,
+    ) -> io::Result<()> {
+        //   Sample output:
+        //   +-+-----------------------------------------------+
+        // A |                      bb4                        |
+        //   +-+----------------------------------+------------+
+        // B |                MIR                 |   STATE    |
+        //   +-+----------------------------------+------------+
+        // C | | (on entry)                       | {_0,_2,_3} |
+        //   +-+----------------------------------+------------+
+        // D |0| StorageLive(_7)                  |            |
+        //   +-+----------------------------------+------------+
+        //   |1| StorageLive(_8)                  |            |
+        //   +-+----------------------------------+------------+
+        //   |2| _8 = &mut _1                     | +_8        |
+        //   +-+----------------------------------+------------+
+        // E |T| _4 = const Foo::twiddle(move _2) | -_2        |
+        //   +-+----------------------------------+------------+
+        // F | | (on unwind)                      | {_0,_3,_8} |
+        //   +-+----------------------------------+------------+
+        //   | | (on successful return)           | +_4        |
+        //   +-+----------------------------------+------------+
+
+        // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their
+        // children. This is because `xdot` seemed to have a hard time correctly propagating
+        // attributes. Make sure to test the output before trying to remove the redundancy.
+        // Notably, `align` was found to have no effect when applied only to <table>.
+
+        let table_fmt = concat!(
+            " border=\"1\"",
+            " cellborder=\"1\"",
+            " cellspacing=\"0\"",
+            " cellpadding=\"3\"",
+            " sides=\"rb\"",
+        );
+        write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?;
+
+        // A + B: Block header
+        if self.state_formatter.column_names().is_empty() {
+            self.write_block_header_simple(w, block)?;
+        } else {
+            self.write_block_header_with_state_columns(w, block)?;
+        }
+
+        // C: Entry state
+        self.bg = Background::Light;
+        self.results.seek_to_block_start(block);
+        let block_entry_state = self.results.get().clone();
+
+        self.write_row_with_full_state(w, "", "(on entry)")?;
+
+        // D: Statement transfer functions
+        for (i, statement) in body[block].statements.iter().enumerate() {
+            let location = Location { block, statement_index: i };
+            let statement_str = format!("{:?}", statement);
+            self.write_row_for_location(w, &i.to_string(), &statement_str, location)?;
+        }
+
+        // E: Terminator transfer function
+        let terminator = body[block].terminator();
+        let terminator_loc = body.terminator_loc(block);
+        let mut terminator_str = String::new();
+        terminator.kind.fmt_head(&mut terminator_str).unwrap();
+
+        self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?;
+
+        // F: Exit state
+
+        // Write the full dataflow state immediately after the terminator if it differs from the
+        // state at block entry.
+        self.results.seek_after(terminator_loc);
+        if self.results.get() != &block_entry_state {
+            let after_terminator_name = match terminator.kind {
+                mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)",
+                _ => "(on exit)",
+            };
+
+            self.write_row_with_full_state(w, "", after_terminator_name)?;
+        }
+
+        // Write any changes caused by terminator-specific effects
+        match terminator.kind {
+            mir::TerminatorKind::Call { destination: Some(_), .. } => {
+                let num_state_columns = self.num_state_columns();
+                self.write_row(w, "", "(on successful return)", |this, w, fmt| {
+                    write!(
+                        w,
+                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
+                        colspan = num_state_columns,
+                        fmt = fmt,
+                    )?;
+
+                    let state_on_unwind = this.results.get().clone();
+                    this.results.seek_after_assume_success(terminator_loc);
+                    write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?;
+
+                    write!(w, "</td>")
+                })?;
+            }
+
+            _ => {}
+        };
+
+        write!(w, "</table>")
+    }
+
+    fn write_block_header_simple(
+        &mut self,
+        w: &mut impl io::Write,
+        block: BasicBlock,
+    ) -> io::Result<()> {
+        //   +-------------------------------------------------+
+        // A |                      bb4                        |
+        //   +-----------------------------------+-------------+
+        // B |                MIR                |    STATE    |
+        //   +-+---------------------------------+-------------+
+        //   | |              ...                |             |
+
+        // A
+        write!(
+            w,
+            concat!("<tr>", r#"<td colspan="3" sides="tl">bb{block_id}</td>"#, "</tr>",),
+            block_id = block.index(),
+        )?;
+
+        // B
+        write!(
+            w,
+            concat!(
+                "<tr>",
+                r#"<td colspan="2" {fmt}>MIR</td>"#,
+                r#"<td {fmt}>STATE</td>"#,
+                "</tr>",
+            ),
+            fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR),
+        )
+    }
+
+    fn write_block_header_with_state_columns(
+        &mut self,
+        w: &mut impl io::Write,
+        block: BasicBlock,
+    ) -> io::Result<()> {
+        //   +------------------------------------+-------------+
+        // A |                bb4                 |    STATE    |
+        //   +------------------------------------+------+------+
+        // B |                MIR                 |  GEN | KILL |
+        //   +-+----------------------------------+------+------+
+        //   | |              ...                 |      |      |
+
+        let state_column_names = self.state_formatter.column_names();
+
+        // A
+        write!(
+            w,
+            concat!(
+                "<tr>",
+                r#"<td {fmt} colspan="2">bb{block_id}</td>"#,
+                r#"<td {fmt} colspan="{num_state_cols}">STATE</td>"#,
+                "</tr>",
+            ),
+            fmt = "sides=\"tl\"",
+            num_state_cols = state_column_names.len(),
+            block_id = block.index(),
+        )?;
+
+        // B
+        let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR);
+        write!(w, concat!("<tr>", r#"<td colspan="2" {fmt}>MIR</td>"#,), fmt = fmt,)?;
+
+        for name in state_column_names {
+            write!(w, "<td {fmt}>{name}</td>", fmt = fmt, name = name)?;
+        }
+
+        write!(w, "</tr>")
+    }
+
+    /// Write a row with the given index and MIR, using the function argument to fill in the
+    /// "STATE" column(s).
+    fn write_row<W: io::Write>(
+        &mut self,
+        w: &mut W,
+        i: &str,
+        mir: &str,
+        f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>,
+    ) -> io::Result<()> {
+        let bg = self.toggle_background();
+        let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" };
+
+        let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr());
+
+        write!(
+            w,
+            concat!(
+                "<tr>",
+                r#"<td {fmt} align="right">{i}</td>"#,
+                r#"<td {fmt} align="left">{mir}</td>"#,
+            ),
+            i = i,
+            fmt = fmt,
+            mir = dot::escape_html(mir),
+        )?;
+
+        f(self, w, &fmt)?;
+        write!(w, "</tr>")
+    }
+
+    fn write_row_with_full_state(
+        &mut self,
+        w: &mut impl io::Write,
+        i: &str,
+        mir: &str,
+    ) -> io::Result<()> {
+        self.write_row(w, i, mir, |this, w, fmt| {
+            let state = this.results.get();
+            let analysis = this.results.analysis();
+
+            write!(
+                w,
+                r#"<td colspan="{colspan}" {fmt} align="left">{{"#,
+                colspan = this.num_state_columns(),
+                fmt = fmt,
+            )?;
+            pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?;
+            write!(w, "}}</td>")
+        })
+    }
+
+    fn write_row_for_location(
+        &mut self,
+        w: &mut impl io::Write,
+        i: &str,
+        mir: &str,
+        location: Location,
+    ) -> io::Result<()> {
+        self.write_row(w, i, mir, |this, w, fmt| {
+            this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location)
+        })
+    }
+}
+
+/// Controls what gets printed under the `STATE` header.
+pub trait StateFormatter<'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    /// The columns that will get printed under `STATE`.
+    fn column_names(&self) -> &[&str];
+
+    fn write_state_for_location(
+        &mut self,
+        w: &mut dyn io::Write,
+        fmt: &str,
+        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
+        location: Location,
+    ) -> io::Result<()>;
+}
+
+/// Prints a single column containing the state vector immediately *after* each statement.
+pub struct SimpleDiff<T: Idx> {
+    prev_state: BitSet<T>,
+    prev_loc: Location,
+}
+
+impl<T: Idx> SimpleDiff<T> {
+    pub fn new(bits_per_block: usize) -> Self {
+        SimpleDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
+    }
+}
+
+impl<A> StateFormatter<'tcx, A> for SimpleDiff<A::Idx>
+where
+    A: Analysis<'tcx>,
+{
+    fn column_names(&self) -> &[&str] {
+        &[]
+    }
+
+    fn write_state_for_location(
+        &mut self,
+        mut w: &mut dyn io::Write,
+        fmt: &str,
+        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
+        location: Location,
+    ) -> io::Result<()> {
+        if location.statement_index == 0 {
+            results.seek_to_block_start(location.block);
+            self.prev_state.overwrite(results.get());
+        } else {
+            // Ensure that we are visiting statements in order, so `prev_state` is correct.
+            assert_eq!(self.prev_loc.successor_within_block(), location);
+        }
+
+        self.prev_loc = location;
+        write!(w, r#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?;
+        results.seek_after(location);
+        let curr_state = results.get();
+        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
+        self.prev_state.overwrite(curr_state);
+        write!(w, "</td>")
+    }
+}
+
+/// Prints two state columns, one containing only the "before" effect of each statement and one
+/// containing the full effect.
+pub struct TwoPhaseDiff<T: Idx> {
+    prev_state: BitSet<T>,
+    prev_loc: Location,
+}
+
+impl<T: Idx> TwoPhaseDiff<T> {
+    pub fn new(bits_per_block: usize) -> Self {
+        TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
+    }
+}
+
+impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx>
+where
+    A: Analysis<'tcx>,
+{
+    fn column_names(&self) -> &[&str] {
+        &["BEFORE", " AFTER"]
+    }
+
+    fn write_state_for_location(
+        &mut self,
+        mut w: &mut dyn io::Write,
+        fmt: &str,
+        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
+        location: Location,
+    ) -> io::Result<()> {
+        if location.statement_index == 0 {
+            results.seek_to_block_start(location.block);
+            self.prev_state.overwrite(results.get());
+        } else {
+            // Ensure that we are visiting statements in order, so `prev_state` is correct.
+            assert_eq!(self.prev_loc.successor_within_block(), location);
+        }
+
+        self.prev_loc = location;
+
+        // Before
+
+        write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
+        results.seek_before(location);
+        let curr_state = results.get();
+        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
+        self.prev_state.overwrite(curr_state);
+        write!(w, "</td>")?;
+
+        // After
+
+        write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
+        results.seek_after(location);
+        let curr_state = results.get();
+        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
+        self.prev_state.overwrite(curr_state);
+        write!(w, "</td>")
+    }
+}
+
+/// Prints the gen/kill set for the entire block.
+pub struct BlockTransferFunc<'a, 'tcx, T: Idx> {
+    body: &'a mir::Body<'tcx>,
+    trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
+}
+
+impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> {
+    pub fn new(
+        body: &'mir mir::Body<'tcx>,
+        trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
+    ) -> Self {
+        BlockTransferFunc { body, trans_for_block }
+    }
+}
+
+impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx>
+where
+    A: Analysis<'tcx>,
+{
+    fn column_names(&self) -> &[&str] {
+        &["GEN", "KILL"]
+    }
+
+    fn write_state_for_location(
+        &mut self,
+        mut w: &mut dyn io::Write,
+        fmt: &str,
+        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
+        location: Location,
+    ) -> io::Result<()> {
+        // Only print a single row.
+        if location.statement_index != 0 {
+            return Ok(());
+        }
+
+        let block_trans = &self.trans_for_block[location.block];
+        let rowspan = self.body.basic_blocks()[location.block].statements.len();
+
+        for set in &[&block_trans.gen, &block_trans.kill] {
+            write!(
+                w,
+                r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#,
+                fmt = fmt,
+                rowspan = rowspan
+            )?;
+
+            pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?;
+            write!(w, "</td>")?;
+        }
+
+        Ok(())
+    }
+}
+
+/// Writes two lines, one containing the added bits and one the removed bits.
+fn write_diff<A: Analysis<'tcx>>(
+    w: &mut impl io::Write,
+    analysis: &A,
+    from: &BitSet<A::Idx>,
+    to: &BitSet<A::Idx>,
+) -> io::Result<()> {
+    assert_eq!(from.domain_size(), to.domain_size());
+    let len = from.domain_size();
+
+    let mut set = HybridBitSet::new_empty(len);
+    let mut clear = HybridBitSet::new_empty(len);
+
+    // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets.
+    for i in (0..len).map(A::Idx::new) {
+        match (from.contains(i), to.contains(i)) {
+            (false, true) => set.insert(i),
+            (true, false) => clear.insert(i),
+            _ => continue,
+        };
+    }
+
+    if !set.is_empty() {
+        write!(w, r#"<font color="darkgreen">+"#)?;
+        pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?;
+        write!(w, r#"</font>"#)?;
+    }
+
+    if !set.is_empty() && !clear.is_empty() {
+        write!(w, "{}", BR_LEFT)?;
+    }
+
+    if !clear.is_empty() {
+        write!(w, r#"<font color="red">-"#)?;
+        pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?;
+        write!(w, r#"</font>"#)?;
+    }
+
+    Ok(())
+}
+
+const BR_LEFT: &str = r#"<br align="left"/>"#;
+const BR_LEFT_SPACE: &str = r#"<br align="left"/> "#;
+
+/// Line break policy that breaks at 40 characters and starts the next line with a single space.
+const LIMIT_30_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 });
+
+struct LineBreak {
+    sequence: &'static str,
+    limit: usize,
+}
+
+/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given
+/// separator (`sep`).
+///
+/// Optionally, it will break lines using the given character sequence (usually `<br/>`) and
+/// character limit.
+fn pretty_print_state_elems<A>(
+    w: &mut impl io::Write,
+    analysis: &A,
+    elems: impl Iterator<Item = A::Idx>,
+    sep: &str,
+    line_break: Option<LineBreak>,
+) -> io::Result<bool>
+where
+    A: Analysis<'tcx>,
+{
+    let sep_width = sep.chars().count();
+
+    let mut buf = Vec::new();
+
+    let mut first = true;
+    let mut curr_line_width = 0;
+    let mut line_break_inserted = false;
+
+    for idx in elems {
+        buf.clear();
+        analysis.pretty_print_idx(&mut buf, idx)?;
+        let idx_str =
+            str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8");
+        let escaped = dot::escape_html(idx_str);
+        let escaped_width = escaped.chars().count();
+
+        if first {
+            first = false;
+        } else {
+            write!(w, "{}", sep)?;
+            curr_line_width += sep_width;
+
+            if let Some(line_break) = &line_break {
+                if curr_line_width + sep_width + escaped_width > line_break.limit {
+                    write!(w, "{}", line_break.sequence)?;
+                    line_break_inserted = true;
+                    curr_line_width = 0;
+                }
+            }
+        }
+
+        write!(w, "{}", escaped)?;
+        curr_line_width += escaped_width;
+    }
+
+    Ok(line_break_inserted)
+}
+
+/// The background color used for zebra-striping the table.
+#[derive(Clone, Copy)]
+enum Background {
+    Light,
+    Dark,
+}
+
+impl Background {
+    fn attr(self) -> &'static str {
+        match self {
+            Self::Dark => "bgcolor=\"#f0f0f0\"",
+            Self::Light => "",
+        }
+    }
+}
+
+impl ops::Not for Background {
+    type Output = Self;
+
+    fn not(self) -> Self {
+        match self {
+            Self::Light => Self::Dark,
+            Self::Dark => Self::Light,
+        }
+    }
+}
diff --git a/src/librustc_mir/dataflow/framework/mod.rs b/src/librustc_mir/dataflow/framework/mod.rs
new file mode 100644 (file)
index 0000000..8556be7
--- /dev/null
@@ -0,0 +1,524 @@
+//! A framework that can express both [gen-kill] and generic dataflow problems.
+//!
+//! To actually use this framework, you must implement either the `Analysis` or the
+//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill
+//! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The
+//! `impls` module contains several examples of gen/kill dataflow analyses.
+//!
+//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait,
+//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the
+//! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use
+//! `visit_results`. The following example uses the `ResultsCursor` approach.
+//!
+//! ```ignore(cross-crate-imports)
+//! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available.
+//!
+//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) {
+//!     let analysis = MyAnalysis::new()
+//!         .into_engine(tcx, body, did)
+//!         .iterate_to_fixpoint()
+//!         .into_results_cursor(body);
+//!
+//!     // Print the dataflow state *after* each statement in the start block.
+//!     for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() {
+//!         cursor.seek_after(Location { block: START_BLOCK, statement_index });
+//!         let state = cursor.get();
+//!         println!("{:?}", state);
+//!     }
+//! }
+//! ```
+//!
+//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
+
+use std::io;
+
+use rustc::mir::{self, BasicBlock, Location};
+use rustc::ty::layout::VariantIdx;
+use rustc::ty::{self, TyCtxt};
+use rustc_hir::def_id::DefId;
+use rustc_index::bit_set::{BitSet, HybridBitSet};
+use rustc_index::vec::{Idx, IndexVec};
+
+mod cursor;
+mod engine;
+mod graphviz;
+mod visitor;
+
+pub use self::cursor::{ResultsCursor, ResultsRefCursor};
+pub use self::engine::Engine;
+pub use self::visitor::{visit_results, ResultsVisitor};
+pub use self::visitor::{BorrowckFlowState, BorrowckResults};
+
+/// A dataflow analysis that has converged to fixpoint.
+pub struct Results<'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    pub analysis: A,
+    entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
+}
+
+impl<A> Results<'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    /// Creates a `ResultsCursor` that can inspect these `Results`.
+    pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
+        ResultsCursor::new(body, self)
+    }
+
+    /// Gets the entry set for the given block.
+    pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet<A::Idx> {
+        &self.entry_sets[block]
+    }
+
+    pub fn visit_with(
+        &self,
+        body: &'mir mir::Body<'tcx>,
+        blocks: impl IntoIterator<Item = BasicBlock>,
+        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
+    ) {
+        visit_results(body, blocks, self, vis)
+    }
+
+    pub fn visit_in_rpo_with(
+        &self,
+        body: &'mir mir::Body<'tcx>,
+        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
+    ) {
+        let blocks = mir::traversal::reverse_postorder(body);
+        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
+    }
+}
+
+/// Parameterization for the precise form of data flow that is used.
+///
+/// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
+/// This also determines the semantics of the lattice `join` operator used to merge dataflow
+/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed
+/// point.
+///
+/// This means, for propagation across the graph, that you either want to start at all-zeroes and
+/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect
+/// as your merge when propagating.
+pub trait BottomValue {
+    /// Specifies the initial value for each bit in the entry set for each basic block.
+    const BOTTOM_VALUE: bool;
+
+    /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
+    ///
+    /// It is almost certainly wrong to override this, since it automatically applies
+    /// * `inout_set & in_set` if `BOTTOM_VALUE == true`
+    /// * `inout_set | in_set` if `BOTTOM_VALUE == false`
+    ///
+    /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks.
+    /// For clarity, the above statement again from a different perspective:
+    /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is
+    /// `!BOTTOM_VALUE`.
+    ///
+    /// There are situations where you want the opposite behaviour: propagate only if *all*
+    /// predecessor blocks's value is `!BOTTOM_VALUE`.
+    /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This
+    /// means that all code paths leading to the location must have set the bit, instead of any
+    /// code path leading there.
+    ///
+    /// If you want this kind of "definitely set" analysis, you need to
+    /// 1. Invert `BOTTOM_VALUE`
+    /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE`
+    /// 3. Override `join` to do the opposite from what it's doing now.
+    #[inline]
+    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
+        if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) }
+    }
+}
+
+/// Define the domain of a dataflow problem.
+///
+/// This trait specifies the lattice on which this analysis operates. For now, this must be a
+/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet`
+/// and referred to as the state vector.
+///
+/// This trait also defines the initial value for the dataflow state upon entry to the
+/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging.
+pub trait AnalysisDomain<'tcx>: BottomValue {
+    /// The type of the elements in the state vector.
+    type Idx: Idx;
+
+    /// A descriptive name for this analysis. Used only for debugging.
+    ///
+    /// This name should be brief and contain no spaces, periods or other characters that are not
+    /// suitable as part of a filename.
+    const NAME: &'static str;
+
+    /// The size of the state vector.
+    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
+
+    /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow
+    /// analysis.
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>);
+
+    /// Prints an element in the state vector for debugging.
+    fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
+        write!(w, "{:?}", idx)
+    }
+}
+
+/// A dataflow problem with an arbitrarily complex transfer function.
+pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
+    /// Updates the current dataflow state with the effect of evaluating a statement.
+    fn apply_statement_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        statement: &mir::Statement<'tcx>,
+        location: Location,
+    );
+
+    /// Updates the current dataflow state with an effect that occurs immediately *before* the
+    /// given statement.
+    ///
+    /// This method is useful if the consumer of the results of this analysis needs only 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 implementing `apply_statement_effect`.
+    fn apply_before_statement_effect(
+        &self,
+        _state: &mut BitSet<Self::Idx>,
+        _statement: &mir::Statement<'tcx>,
+        _location: Location,
+    ) {
+    }
+
+    /// Updates the current dataflow state with the effect of evaluating a terminator.
+    ///
+    /// The effect of a successful return from a `Call` terminator should **not** be accounted for
+    /// 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(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        terminator: &mir::Terminator<'tcx>,
+        location: Location,
+    );
+
+    /// 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 implementing `apply_terminator_effect`.
+    fn apply_before_terminator_effect(
+        &self,
+        _state: &mut BitSet<Self::Idx>,
+        _terminator: &mir::Terminator<'tcx>,
+        _location: Location,
+    ) {
+    }
+
+    /// 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.
+    fn apply_call_return_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        block: BasicBlock,
+        func: &mir::Operand<'tcx>,
+        args: &[mir::Operand<'tcx>],
+        return_place: &mir::Place<'tcx>,
+    );
+
+    /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator.
+    ///
+    /// This is similar to `apply_call_return_effect` in that it only takes place after the
+    /// generator is resumed, not when it is dropped.
+    ///
+    /// By default, no effects happen.
+    fn apply_yield_resume_effect(
+        &self,
+        _state: &mut BitSet<Self::Idx>,
+        _resume_block: BasicBlock,
+        _resume_place: &mir::Place<'tcx>,
+    ) {
+    }
+
+    /// Updates the current dataflow state with the effect of taking a particular branch in a
+    /// `SwitchInt` terminator.
+    ///
+    /// Much like `apply_call_return_effect`, this effect is only propagated along a single
+    /// outgoing edge from this basic block.
+    fn apply_discriminant_switch_effect(
+        &self,
+        _state: &mut BitSet<Self::Idx>,
+        _block: BasicBlock,
+        _enum_place: &mir::Place<'tcx>,
+        _adt: &ty::AdtDef,
+        _variant: VariantIdx,
+    ) {
+    }
+
+    /// Creates an `Engine` to find the fixpoint for this dataflow problem.
+    ///
+    /// You shouldn't need to override this outside this module, since the combination of the
+    /// default impl and the one for all `A: GenKillAnalysis` will do the right thing.
+    /// Its purpose is to enable method chaining like so:
+    ///
+    /// ```ignore(cross-crate-imports)
+    /// let results = MyAnalysis::new(tcx, body)
+    ///     .into_engine(tcx, body, def_id)
+    ///     .iterate_to_fixpoint()
+    ///     .into_results_cursor(body);
+    /// ```
+    fn into_engine(
+        self,
+        tcx: TyCtxt<'tcx>,
+        body: &'mir mir::Body<'tcx>,
+        def_id: DefId,
+    ) -> Engine<'mir, 'tcx, Self>
+    where
+        Self: Sized,
+    {
+        Engine::new_generic(tcx, body, def_id, self)
+    }
+}
+
+/// A gen/kill dataflow problem.
+///
+/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
+/// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer
+/// functions for each statement in this way, the transfer function for an entire basic block can
+/// be computed efficiently.
+///
+/// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`.
+pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
+    /// See `Analysis::apply_statement_effect`.
+    fn statement_effect(
+        &self,
+        trans: &mut impl GenKill<Self::Idx>,
+        statement: &mir::Statement<'tcx>,
+        location: Location,
+    );
+
+    /// See `Analysis::apply_before_statement_effect`.
+    fn before_statement_effect(
+        &self,
+        _trans: &mut impl GenKill<Self::Idx>,
+        _statement: &mir::Statement<'tcx>,
+        _location: Location,
+    ) {
+    }
+
+    /// See `Analysis::apply_terminator_effect`.
+    fn terminator_effect(
+        &self,
+        trans: &mut impl GenKill<Self::Idx>,
+        terminator: &mir::Terminator<'tcx>,
+        location: Location,
+    );
+
+    /// See `Analysis::apply_before_terminator_effect`.
+    fn before_terminator_effect(
+        &self,
+        _trans: &mut impl GenKill<Self::Idx>,
+        _terminator: &mir::Terminator<'tcx>,
+        _location: Location,
+    ) {
+    }
+
+    /// See `Analysis::apply_call_return_effect`.
+    fn call_return_effect(
+        &self,
+        trans: &mut impl GenKill<Self::Idx>,
+        block: BasicBlock,
+        func: &mir::Operand<'tcx>,
+        args: &[mir::Operand<'tcx>],
+        return_place: &mir::Place<'tcx>,
+    );
+
+    /// See `Analysis::apply_yield_resume_effect`.
+    fn yield_resume_effect(
+        &self,
+        _trans: &mut BitSet<Self::Idx>,
+        _resume_block: BasicBlock,
+        _resume_place: &mir::Place<'tcx>,
+    ) {
+    }
+
+    /// See `Analysis::apply_discriminant_switch_effect`.
+    fn discriminant_switch_effect(
+        &self,
+        _state: &mut impl GenKill<Self::Idx>,
+        _block: BasicBlock,
+        _enum_place: &mir::Place<'tcx>,
+        _adt: &ty::AdtDef,
+        _variant: VariantIdx,
+    ) {
+    }
+}
+
+impl<A> Analysis<'tcx> for A
+where
+    A: GenKillAnalysis<'tcx>,
+{
+    fn apply_statement_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        statement: &mir::Statement<'tcx>,
+        location: Location,
+    ) {
+        self.statement_effect(state, statement, location);
+    }
+
+    fn apply_before_statement_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        statement: &mir::Statement<'tcx>,
+        location: Location,
+    ) {
+        self.before_statement_effect(state, statement, location);
+    }
+
+    fn apply_terminator_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        terminator: &mir::Terminator<'tcx>,
+        location: Location,
+    ) {
+        self.terminator_effect(state, terminator, location);
+    }
+
+    fn apply_before_terminator_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        terminator: &mir::Terminator<'tcx>,
+        location: Location,
+    ) {
+        self.before_terminator_effect(state, terminator, location);
+    }
+
+    fn apply_call_return_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        block: BasicBlock,
+        func: &mir::Operand<'tcx>,
+        args: &[mir::Operand<'tcx>],
+        return_place: &mir::Place<'tcx>,
+    ) {
+        self.call_return_effect(state, block, func, args, return_place);
+    }
+
+    fn apply_yield_resume_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        resume_block: BasicBlock,
+        resume_place: &mir::Place<'tcx>,
+    ) {
+        self.yield_resume_effect(state, resume_block, resume_place);
+    }
+
+    fn apply_discriminant_switch_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        block: BasicBlock,
+        enum_place: &mir::Place<'tcx>,
+        adt: &ty::AdtDef,
+        variant: VariantIdx,
+    ) {
+        self.discriminant_switch_effect(state, block, enum_place, adt, variant);
+    }
+
+    fn into_engine(
+        self,
+        tcx: TyCtxt<'tcx>,
+        body: &'mir mir::Body<'tcx>,
+        def_id: DefId,
+    ) -> Engine<'mir, 'tcx, Self>
+    where
+        Self: Sized,
+    {
+        Engine::new_gen_kill(tcx, body, def_id, self)
+    }
+}
+
+/// The legal operations for a transfer function in a gen/kill problem.
+///
+/// This abstraction exists because there are two different contexts in which we call the methods in
+/// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently
+/// applied multiple times, such as when computing the cumulative transfer function for each block.
+/// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes,
+/// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the
+/// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to
+/// building up a `GenKillSet` and then throwing it away.
+pub trait GenKill<T> {
+    /// Inserts `elem` into the state vector.
+    fn gen(&mut self, elem: T);
+
+    /// Removes `elem` from the state vector.
+    fn kill(&mut self, elem: T);
+
+    /// Calls `gen` for each element in `elems`.
+    fn gen_all(&mut self, elems: impl IntoIterator<Item = T>) {
+        for elem in elems {
+            self.gen(elem);
+        }
+    }
+
+    /// Calls `kill` for each element in `elems`.
+    fn kill_all(&mut self, elems: impl IntoIterator<Item = T>) {
+        for elem in elems {
+            self.kill(elem);
+        }
+    }
+}
+
+/// Stores a transfer function for a gen/kill problem.
+///
+/// Calling `gen`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be
+/// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for
+/// the same element, the most recent one takes precedence.
+#[derive(Clone)]
+pub struct GenKillSet<T: Idx> {
+    gen: HybridBitSet<T>,
+    kill: HybridBitSet<T>,
+}
+
+impl<T: Idx> GenKillSet<T> {
+    /// Creates a new transfer function that will leave the dataflow state unchanged.
+    pub fn identity(universe: usize) -> Self {
+        GenKillSet {
+            gen: HybridBitSet::new_empty(universe),
+            kill: HybridBitSet::new_empty(universe),
+        }
+    }
+
+    /// Applies this transfer function to the given state vector.
+    pub fn apply(&self, state: &mut BitSet<T>) {
+        state.union(&self.gen);
+        state.subtract(&self.kill);
+    }
+}
+
+impl<T: Idx> GenKill<T> for GenKillSet<T> {
+    fn gen(&mut self, elem: T) {
+        self.gen.insert(elem);
+        self.kill.remove(elem);
+    }
+
+    fn kill(&mut self, elem: T) {
+        self.kill.insert(elem);
+        self.gen.remove(elem);
+    }
+}
+
+impl<T: Idx> GenKill<T> for BitSet<T> {
+    fn gen(&mut self, elem: T) {
+        self.insert(elem);
+    }
+
+    fn kill(&mut self, elem: T) {
+        self.remove(elem);
+    }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/src/librustc_mir/dataflow/framework/tests.rs b/src/librustc_mir/dataflow/framework/tests.rs
new file mode 100644 (file)
index 0000000..8f07a10
--- /dev/null
@@ -0,0 +1,332 @@
+//! A test for the logic that updates the state in a `ResultsCursor` during seek.
+
+use rustc::mir::{self, BasicBlock, Location};
+use rustc::ty;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_span::DUMMY_SP;
+
+use super::*;
+use crate::dataflow::BottomValue;
+
+/// Returns `true` if the given location points to a `Call` terminator that can return
+/// successfully.
+fn is_call_terminator_non_diverging(body: &mir::Body<'_>, loc: Location) -> bool {
+    loc == body.terminator_loc(loc.block)
+        && matches!(
+            body[loc.block].terminator().kind,
+            mir::TerminatorKind::Call { destination: Some(_), ..  }
+        )
+}
+
+/// Creates a `mir::Body` with a few disconnected basic blocks.
+///
+/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not
+/// important.
+fn mock_body() -> mir::Body<'static> {
+    let source_info = mir::SourceInfo { scope: mir::OUTERMOST_SOURCE_SCOPE, span: DUMMY_SP };
+
+    let mut blocks = IndexVec::new();
+    let mut block = |n, kind| {
+        let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop };
+
+        blocks.push(mir::BasicBlockData {
+            statements: std::iter::repeat(&nop).cloned().take(n).collect(),
+            terminator: Some(mir::Terminator { source_info, kind }),
+            is_cleanup: false,
+        })
+    };
+
+    let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() };
+
+    block(4, mir::TerminatorKind::Return);
+    block(1, mir::TerminatorKind::Return);
+    block(
+        2,
+        mir::TerminatorKind::Call {
+            func: mir::Operand::Copy(dummy_place.clone()),
+            args: vec![],
+            destination: Some((dummy_place.clone(), mir::START_BLOCK)),
+            cleanup: None,
+            from_hir_call: false,
+        },
+    );
+    block(3, mir::TerminatorKind::Return);
+    block(0, mir::TerminatorKind::Return);
+    block(
+        4,
+        mir::TerminatorKind::Call {
+            func: mir::Operand::Copy(dummy_place.clone()),
+            args: vec![],
+            destination: Some((dummy_place.clone(), mir::START_BLOCK)),
+            cleanup: None,
+            from_hir_call: false,
+        },
+    );
+
+    mir::Body::new_cfg_only(blocks)
+}
+
+/// A dataflow analysis whose state is unique at every possible `SeekTarget`.
+///
+/// Uniqueness is achieved by having a *locally* unique effect before and after each statement and
+/// terminator (see `effect_at_target`) while ensuring that the entry set for each block is
+/// *globally* unique (see `mock_entry_set`).
+///
+/// For example, a `BasicBlock` with ID `2` and a `Call` terminator has the following state at each
+/// location ("+x" indicates that "x" is added to the state).
+///
+/// | Location               | Before            | After  |
+/// |------------------------|-------------------|--------|
+/// | (on_entry)             | {102}                     ||
+/// | Statement 0            | +0                | +1     |
+/// | statement 1            | +2                | +3     |
+/// | `Call` terminator      | +4                | +5     |
+/// | (on unwind)            | {102,0,1,2,3,4,5}         ||
+/// | (on successful return) | +6                        ||
+///
+/// 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.
+struct MockAnalysis<'tcx> {
+    body: &'tcx mir::Body<'tcx>,
+}
+
+impl MockAnalysis<'tcx> {
+    const BASIC_BLOCK_OFFSET: usize = 100;
+
+    /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to
+    /// avoid colliding with the statement/terminator effects.
+    fn mock_entry_set(&self, bb: BasicBlock) -> BitSet<usize> {
+        let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
+        ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index());
+        ret
+    }
+
+    fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
+        let empty = BitSet::new_empty(self.bits_per_block(self.body));
+        let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks());
+
+        for (bb, _) in self.body.basic_blocks().iter_enumerated() {
+            ret[bb] = self.mock_entry_set(bb);
+        }
+
+        ret
+    }
+
+    /// Returns the index that should be added to the dataflow state at the given target.
+    ///
+    /// This index is only unique within a given basic block. `SeekAfter` and
+    /// `SeekAfterAssumeCallReturns` have the same effect unless `target` is a `Call` terminator.
+    fn effect_at_target(&self, target: SeekTarget) -> Option<usize> {
+        use SeekTarget::*;
+
+        let idx = match target {
+            BlockStart(_) => return None,
+
+            AfterAssumeCallReturns(loc) if is_call_terminator_non_diverging(self.body, loc) => {
+                loc.statement_index * 2 + 2
+            }
+
+            Before(loc) => loc.statement_index * 2,
+            After(loc) | AfterAssumeCallReturns(loc) => loc.statement_index * 2 + 1,
+        };
+
+        assert!(idx < Self::BASIC_BLOCK_OFFSET, "Too many statements in basic block");
+        Some(idx)
+    }
+
+    /// Returns the expected state at the given `SeekTarget`.
+    ///
+    /// This is the union of index of the target basic block, the index assigned to the
+    /// target statement or terminator, and the indices of all preceding statements in the target
+    /// basic block.
+    ///
+    /// For example, the expected state when calling
+    /// `seek_before(Location { block: 2, statement_index: 2 })` would be `[102, 0, 1, 2, 3, 4]`.
+    fn expected_state_at_target(&self, target: SeekTarget) -> BitSet<usize> {
+        let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
+        ret.insert(Self::BASIC_BLOCK_OFFSET + target.block().index());
+
+        if let Some(target_effect) = self.effect_at_target(target) {
+            for i in 0..=target_effect {
+                ret.insert(i);
+            }
+        }
+
+        ret
+    }
+}
+
+impl BottomValue for MockAnalysis<'tcx> {
+    const BOTTOM_VALUE: bool = false;
+}
+
+impl AnalysisDomain<'tcx> for MockAnalysis<'tcx> {
+    type Idx = usize;
+
+    const NAME: &'static str = "mock";
+
+    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
+        Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len()
+    }
+
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+        unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint");
+    }
+}
+
+impl Analysis<'tcx> for MockAnalysis<'tcx> {
+    fn apply_statement_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        _statement: &mir::Statement<'tcx>,
+        location: Location,
+    ) {
+        let idx = self.effect_at_target(SeekTarget::After(location)).unwrap();
+        assert!(state.insert(idx));
+    }
+
+    fn apply_before_statement_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        _statement: &mir::Statement<'tcx>,
+        location: Location,
+    ) {
+        let idx = self.effect_at_target(SeekTarget::Before(location)).unwrap();
+        assert!(state.insert(idx));
+    }
+
+    fn apply_terminator_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        _terminator: &mir::Terminator<'tcx>,
+        location: Location,
+    ) {
+        let idx = self.effect_at_target(SeekTarget::After(location)).unwrap();
+        assert!(state.insert(idx));
+    }
+
+    fn apply_before_terminator_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        _terminator: &mir::Terminator<'tcx>,
+        location: Location,
+    ) {
+        let idx = self.effect_at_target(SeekTarget::Before(location)).unwrap();
+        assert!(state.insert(idx));
+    }
+
+    fn apply_call_return_effect(
+        &self,
+        state: &mut BitSet<Self::Idx>,
+        block: BasicBlock,
+        _func: &mir::Operand<'tcx>,
+        _args: &[mir::Operand<'tcx>],
+        _return_place: &mir::Place<'tcx>,
+    ) {
+        let location = self.body.terminator_loc(block);
+        let idx = self.effect_at_target(SeekTarget::AfterAssumeCallReturns(location)).unwrap();
+        assert!(state.insert(idx));
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum SeekTarget {
+    BlockStart(BasicBlock),
+    Before(Location),
+    After(Location),
+    AfterAssumeCallReturns(Location),
+}
+
+impl SeekTarget {
+    fn block(&self) -> BasicBlock {
+        use SeekTarget::*;
+
+        match *self {
+            BlockStart(block) => block,
+            Before(loc) | After(loc) | AfterAssumeCallReturns(loc) => loc.block,
+        }
+    }
+
+    /// An iterator over all possible `SeekTarget`s in a given block in order, starting with
+    /// `BlockStart`.
+    ///
+    /// This includes both `After` and `AfterAssumeCallReturns` for every `Location`.
+    fn iter_in_block(body: &mir::Body<'_>, block: BasicBlock) -> impl Iterator<Item = Self> {
+        let statements_and_terminator = (0..=body[block].statements.len())
+            .flat_map(|i| (0..3).map(move |j| (i, j)))
+            .map(move |(i, kind)| {
+                let loc = Location { block, statement_index: i };
+                match kind {
+                    0 => SeekTarget::Before(loc),
+                    1 => SeekTarget::After(loc),
+                    2 => SeekTarget::AfterAssumeCallReturns(loc),
+                    _ => unreachable!(),
+                }
+            });
+
+        std::iter::once(SeekTarget::BlockStart(block)).chain(statements_and_terminator)
+    }
+}
+
+#[test]
+fn cursor_seek() {
+    let body = mock_body();
+    let body = &body;
+    let analysis = MockAnalysis { body };
+
+    let mut cursor =
+        Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
+
+    // Sanity check: the mock call return effect is unique and actually being applied.
+    let call_terminator_loc = Location { block: BasicBlock::from_usize(2), statement_index: 2 };
+    assert!(is_call_terminator_non_diverging(body, call_terminator_loc));
+
+    let call_return_effect = cursor
+        .analysis()
+        .effect_at_target(SeekTarget::AfterAssumeCallReturns(call_terminator_loc))
+        .unwrap();
+    assert_ne!(
+        call_return_effect,
+        cursor.analysis().effect_at_target(SeekTarget::After(call_terminator_loc)).unwrap()
+    );
+
+    cursor.seek_after(call_terminator_loc);
+    assert!(!cursor.get().contains(call_return_effect));
+    cursor.seek_after_assume_success(call_terminator_loc);
+    assert!(cursor.get().contains(call_return_effect));
+
+    let every_target = || {
+        body.basic_blocks()
+            .iter_enumerated()
+            .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb))
+    };
+
+    let mut seek_to_target = |targ| {
+        use SeekTarget::*;
+
+        match targ {
+            BlockStart(block) => cursor.seek_to_block_start(block),
+            Before(loc) => cursor.seek_before(loc),
+            After(loc) => cursor.seek_after(loc),
+            AfterAssumeCallReturns(loc) => cursor.seek_after_assume_success(loc),
+        }
+
+        assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ));
+    };
+
+    // Seek *to* every possible `SeekTarget` *from* every possible `SeekTarget`.
+    //
+    // By resetting the cursor to `from` each time it changes, we end up checking some edges twice.
+    // What we really want is an Eulerian cycle for the complete digraph over all possible
+    // `SeekTarget`s, but it's not worth spending the time to compute it.
+    for from in every_target() {
+        seek_to_target(from);
+
+        for to in every_target() {
+            seek_to_target(to);
+            seek_to_target(from);
+        }
+    }
+}
diff --git a/src/librustc_mir/dataflow/framework/visitor.rs b/src/librustc_mir/dataflow/framework/visitor.rs
new file mode 100644 (file)
index 0000000..6e1513b
--- /dev/null
@@ -0,0 +1,272 @@
+use rustc::mir::{self, BasicBlock, Location};
+use rustc_index::bit_set::BitSet;
+
+use super::{Analysis, Results};
+use crate::dataflow::impls::{borrows::Borrows, EverInitializedPlaces, MaybeUninitializedPlaces};
+
+/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
+/// dataflow state at that location.
+pub fn visit_results<F>(
+    body: &'mir mir::Body<'tcx>,
+    blocks: impl IntoIterator<Item = BasicBlock>,
+    results: &impl ResultsVisitable<'tcx, FlowState = F>,
+    vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
+) {
+    let mut state = results.new_flow_state(body);
+
+    for block in blocks {
+        let block_data = &body[block];
+        results.reset_to_block_start(&mut state, block);
+
+        for (statement_index, stmt) in block_data.statements.iter().enumerate() {
+            let loc = Location { block, statement_index };
+
+            results.reconstruct_before_statement_effect(&mut state, stmt, loc);
+            vis.visit_statement(&state, stmt, loc);
+
+            results.reconstruct_statement_effect(&mut state, stmt, loc);
+            vis.visit_statement_exit(&state, stmt, loc);
+        }
+
+        let loc = body.terminator_loc(block);
+        let term = block_data.terminator();
+
+        results.reconstruct_before_terminator_effect(&mut state, term, loc);
+        vis.visit_terminator(&state, term, loc);
+
+        results.reconstruct_terminator_effect(&mut state, term, loc);
+        vis.visit_terminator_exit(&state, term, loc);
+    }
+}
+
+pub trait ResultsVisitor<'mir, 'tcx> {
+    type FlowState;
+
+    /// Called with the `before_statement_effect` of the given statement applied to `state` but not
+    /// its `statement_effect`.
+    fn visit_statement(
+        &mut self,
+        _state: &Self::FlowState,
+        _statement: &'mir mir::Statement<'tcx>,
+        _location: Location,
+    ) {
+    }
+
+    /// Called with both the `before_statement_effect` and the `statement_effect` of the given
+    /// statement applied to `state`.
+    fn visit_statement_exit(
+        &mut self,
+        _state: &Self::FlowState,
+        _statement: &'mir mir::Statement<'tcx>,
+        _location: Location,
+    ) {
+    }
+
+    /// Called with the `before_terminator_effect` of the given terminator applied to `state` but not
+    /// its `terminator_effect`.
+    fn visit_terminator(
+        &mut self,
+        _state: &Self::FlowState,
+        _terminator: &'mir mir::Terminator<'tcx>,
+        _location: Location,
+    ) {
+    }
+
+    /// Called with both the `before_terminator_effect` and the `terminator_effect` of the given
+    /// terminator applied to `state`.
+    ///
+    /// The `call_return_effect` (if one exists) will *not* be applied to `state`.
+    fn visit_terminator_exit(
+        &mut self,
+        _state: &Self::FlowState,
+        _terminator: &'mir mir::Terminator<'tcx>,
+        _location: Location,
+    ) {
+    }
+}
+
+/// Things that can be visited by a `ResultsVisitor`.
+///
+/// This trait exists so that we can visit the results of multiple dataflow analyses simultaneously.
+/// DO NOT IMPLEMENT MANUALLY. Instead, use the `impl_visitable` macro below.
+pub trait ResultsVisitable<'tcx> {
+    type FlowState;
+
+    /// Creates an empty `FlowState` to hold the transient state for these dataflow results.
+    ///
+    /// The value of the newly created `FlowState` will be overwritten by `reset_to_block_start`
+    /// before it can be observed by a `ResultsVisitor`.
+    fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState;
+
+    fn reset_to_block_start(&self, state: &mut Self::FlowState, block: BasicBlock);
+
+    fn reconstruct_before_statement_effect(
+        &self,
+        state: &mut Self::FlowState,
+        statement: &mir::Statement<'tcx>,
+        location: Location,
+    );
+
+    fn reconstruct_statement_effect(
+        &self,
+        state: &mut Self::FlowState,
+        statement: &mir::Statement<'tcx>,
+        location: Location,
+    );
+
+    fn reconstruct_before_terminator_effect(
+        &self,
+        state: &mut Self::FlowState,
+        terminator: &mir::Terminator<'tcx>,
+        location: Location,
+    );
+
+    fn reconstruct_terminator_effect(
+        &self,
+        state: &mut Self::FlowState,
+        terminator: &mir::Terminator<'tcx>,
+        location: Location,
+    );
+}
+
+impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
+where
+    A: Analysis<'tcx>,
+{
+    type FlowState = BitSet<A::Idx>;
+
+    fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
+        BitSet::new_empty(self.analysis.bits_per_block(body))
+    }
+
+    fn reset_to_block_start(&self, state: &mut Self::FlowState, block: BasicBlock) {
+        state.overwrite(&self.entry_set_for_block(block));
+    }
+
+    fn reconstruct_before_statement_effect(
+        &self,
+        state: &mut Self::FlowState,
+        stmt: &mir::Statement<'tcx>,
+        loc: Location,
+    ) {
+        self.analysis.apply_before_statement_effect(state, stmt, loc);
+    }
+
+    fn reconstruct_statement_effect(
+        &self,
+        state: &mut Self::FlowState,
+        stmt: &mir::Statement<'tcx>,
+        loc: Location,
+    ) {
+        self.analysis.apply_statement_effect(state, stmt, loc);
+    }
+
+    fn reconstruct_before_terminator_effect(
+        &self,
+        state: &mut Self::FlowState,
+        term: &mir::Terminator<'tcx>,
+        loc: Location,
+    ) {
+        self.analysis.apply_before_terminator_effect(state, term, loc);
+    }
+
+    fn reconstruct_terminator_effect(
+        &self,
+        state: &mut Self::FlowState,
+        term: &mir::Terminator<'tcx>,
+        loc: Location,
+    ) {
+        self.analysis.apply_terminator_effect(state, term, loc);
+    }
+}
+
+/// A tuple with named fields that can hold either the results or the transient state of the
+/// dataflow analyses used by the borrow checker.
+#[derive(Debug)]
+pub struct BorrowckAnalyses<B, U, E> {
+    pub borrows: B,
+    pub uninits: U,
+    pub ever_inits: E,
+}
+
+/// The results of the dataflow analyses used by the borrow checker.
+pub type BorrowckResults<'mir, 'tcx> = BorrowckAnalyses<
+    Results<'tcx, Borrows<'mir, 'tcx>>,
+    Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>,
+    Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>,
+>;
+
+/// The transient state of the dataflow analyses used by the borrow checker.
+pub type BorrowckFlowState<'mir, 'tcx> =
+    <BorrowckResults<'mir, 'tcx> as ResultsVisitable<'tcx>>::FlowState;
+
+macro_rules! impl_visitable {
+    ( $(
+        $T:ident { $( $field:ident : $A:ident ),* $(,)? }
+    )* ) => { $(
+        impl<'tcx, $($A),*> ResultsVisitable<'tcx> for $T<$( Results<'tcx, $A> ),*>
+        where
+            $( $A: Analysis<'tcx>, )*
+        {
+            type FlowState = $T<$( BitSet<$A::Idx> ),*>;
+
+            fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
+                $T {
+                    $( $field: BitSet::new_empty(self.$field.analysis.bits_per_block(body)) ),*
+                }
+            }
+
+            fn reset_to_block_start(
+                &self,
+                state: &mut Self::FlowState,
+                block: BasicBlock,
+            ) {
+                $( state.$field.overwrite(&self.$field.entry_sets[block]); )*
+            }
+
+            fn reconstruct_before_statement_effect(
+                &self,
+                state: &mut Self::FlowState,
+                stmt: &mir::Statement<'tcx>,
+                loc: Location,
+            ) {
+                $( self.$field.analysis
+                    .apply_before_statement_effect(&mut state.$field, stmt, loc); )*
+            }
+
+            fn reconstruct_statement_effect(
+                &self,
+                state: &mut Self::FlowState,
+                stmt: &mir::Statement<'tcx>,
+                loc: Location,
+            ) {
+                $( self.$field.analysis
+                    .apply_statement_effect(&mut state.$field, stmt, loc); )*
+            }
+
+            fn reconstruct_before_terminator_effect(
+                &self,
+                state: &mut Self::FlowState,
+                term: &mir::Terminator<'tcx>,
+                loc: Location,
+            ) {
+                $( self.$field.analysis
+                    .apply_before_terminator_effect(&mut state.$field, term, loc); )*
+            }
+
+            fn reconstruct_terminator_effect(
+                &self,
+                state: &mut Self::FlowState,
+                term: &mir::Terminator<'tcx>,
+                loc: Location,
+            ) {
+                $( self.$field.analysis
+                    .apply_terminator_effect(&mut state.$field, term, loc); )*
+            }
+        }
+    )* }
+}
+
+impl_visitable! {
+    BorrowckAnalyses { borrows: B, uninits: U, ever_inits: E }
+}
diff --git a/src/librustc_mir/dataflow/generic/cursor.rs b/src/librustc_mir/dataflow/generic/cursor.rs
deleted file mode 100644 (file)
index 170157a..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-//! Random access inspection of the results of a dataflow analysis.
-
-use std::borrow::Borrow;
-
-use rustc::mir::{self, BasicBlock, Location, TerminatorKind};
-use rustc_index::bit_set::BitSet;
-
-use super::{Analysis, Results};
-
-/// A `ResultsCursor` that borrows the underlying `Results`.
-pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;
-
-/// Allows random access inspection of the results of a dataflow analysis.
-///
-/// This cursor only has linear performance within a basic block when its statements are visited in
-/// order. In the worst case—when statements are visited in *reverse* order—performance will be
-/// quadratic in the number of statements in the block. The order in which basic blocks are
-/// inspected has no impact on performance.
-///
-/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
-/// type of ownership is determined by `R` (see `ResultsRefCursor` above).
-pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
-where
-    A: Analysis<'tcx>,
-{
-    body: &'mir mir::Body<'tcx>,
-    results: R,
-    state: BitSet<A::Idx>,
-
-    pos: CursorPosition,
-
-    /// When this flag is set, the cursor is pointing at a `Call` or `Yield` terminator whose call
-    /// return or resume effect has been applied to `state`.
-    ///
-    /// This flag helps to ensure that multiple calls to `seek_after_assume_success` with the
-    /// same target will result in exactly one invocation of `apply_call_return_effect`. It is
-    /// sufficient to clear this only in `seek_to_block_start`, since seeking away from a
-    /// terminator will always require a cursor reset.
-    success_effect_applied: bool,
-}
-
-impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
-where
-    A: Analysis<'tcx>,
-    R: Borrow<Results<'tcx, A>>,
-{
-    /// Returns a new cursor for `results` that points to the start of the `START_BLOCK`.
-    pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
-        ResultsCursor {
-            body,
-            pos: CursorPosition::BlockStart(mir::START_BLOCK),
-            state: results.borrow().entry_sets[mir::START_BLOCK].clone(),
-            success_effect_applied: false,
-            results,
-        }
-    }
-
-    /// Returns the `Analysis` used to generate the underlying results.
-    pub fn analysis(&self) -> &A {
-        &self.results.borrow().analysis
-    }
-
-    /// Returns the dataflow state at the current location.
-    pub fn get(&self) -> &BitSet<A::Idx> {
-        &self.state
-    }
-
-    /// Returns `true` if the dataflow state at the current location contains the given element.
-    ///
-    /// Shorthand for `self.get().contains(elem)`
-    pub fn contains(&self, elem: A::Idx) -> bool {
-        self.state.contains(elem)
-    }
-
-    /// Resets the cursor to the start of the given basic block.
-    pub fn seek_to_block_start(&mut self, block: BasicBlock) {
-        self.state.overwrite(&self.results.borrow().entry_sets[block]);
-        self.pos = CursorPosition::BlockStart(block);
-        self.success_effect_applied = false;
-    }
-
-    /// Advances the cursor to hold all effects up to and including to the "before" effect of the
-    /// statement (or terminator) at the given location.
-    ///
-    /// If you wish to observe the full effect of a statement or terminator, not just the "before"
-    /// effect, use `seek_after` or `seek_after_assume_success`.
-    pub fn seek_before(&mut self, target: Location) {
-        assert!(target <= self.body.terminator_loc(target.block));
-        self.seek_(target, false);
-    }
-
-    /// Advances the cursor to hold the full effect of all statements (and possibly closing
-    /// terminators) up to and including the `target`.
-    ///
-    /// If the `target` is a `Call` terminator, any call return effect for that terminator will
-    /// **not** be observed. Use `seek_after_assume_success` if you wish to observe the call
-    /// return effect.
-    pub fn seek_after(&mut self, target: Location) {
-        assert!(target <= self.body.terminator_loc(target.block));
-
-        // If we have already applied the call return effect, we are currently pointing at a `Call`
-        // terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo"
-        // the call return effect.
-        if self.success_effect_applied {
-            self.seek_to_block_start(target.block);
-        }
-
-        self.seek_(target, true);
-    }
-
-    /// Advances the cursor to hold all effects up to and including of the statement (or
-    /// terminator) at the given location.
-    ///
-    /// If the `target` is a `Call` or `Yield` terminator, any call return or resume effect for that
-    /// terminator will be observed. Use `seek_after` if you do **not** wish to observe the
-    /// "success" effect.
-    pub fn seek_after_assume_success(&mut self, target: Location) {
-        let terminator_loc = self.body.terminator_loc(target.block);
-        assert!(target.statement_index <= terminator_loc.statement_index);
-
-        self.seek_(target, true);
-
-        if target != terminator_loc || self.success_effect_applied {
-            return;
-        }
-
-        // Apply the effect of the "success" path of the terminator.
-
-        self.success_effect_applied = true;
-        let terminator = self.body.basic_blocks()[target.block].terminator();
-        match &terminator.kind {
-            TerminatorKind::Call { destination: Some((return_place, _)), func, args, .. } => {
-                self.results.borrow().analysis.apply_call_return_effect(
-                    &mut self.state,
-                    target.block,
-                    func,
-                    args,
-                    return_place,
-                );
-            }
-            TerminatorKind::Yield { resume, resume_arg, .. } => {
-                self.results.borrow().analysis.apply_yield_resume_effect(
-                    &mut self.state,
-                    *resume,
-                    resume_arg,
-                );
-            }
-            _ => {}
-        }
-    }
-
-    fn seek_(&mut self, target: Location, apply_after_effect_at_target: bool) {
-        use CursorPosition::*;
-
-        match self.pos {
-            // Return early if we are already at the target location.
-            Before(curr) if curr == target && !apply_after_effect_at_target => return,
-            After(curr) if curr == target && apply_after_effect_at_target => return,
-
-            // Otherwise, we must reset to the start of the target block if...
-
-            // we are in a different block entirely.
-            BlockStart(block) | Before(Location { block, .. }) | After(Location { block, .. })
-                if block != target.block =>
-            {
-                self.seek_to_block_start(target.block)
-            }
-
-            // we are in the same block but have advanced past the target statement.
-            Before(curr) | After(curr) if curr.statement_index > target.statement_index => {
-                self.seek_to_block_start(target.block)
-            }
-
-            // we have already applied the entire effect of a statement but only wish to observe
-            // its "before" effect.
-            After(curr)
-                if curr.statement_index == target.statement_index
-                    && !apply_after_effect_at_target =>
-            {
-                self.seek_to_block_start(target.block)
-            }
-
-            // N.B., `success_effect_applied` is checked in `seek_after`, not here.
-            _ => (),
-        }
-
-        let analysis = &self.results.borrow().analysis;
-        let block_data = &self.body.basic_blocks()[target.block];
-
-        // At this point, the cursor is in the same block as the target location at an earlier
-        // statement.
-        debug_assert_eq!(target.block, self.pos.block());
-
-        // Find the first statement whose transfer function has not yet been applied.
-        let first_unapplied_statement = match self.pos {
-            BlockStart(_) => 0,
-            After(Location { statement_index, .. }) => statement_index + 1,
-
-            // If we have only applied the "before" effect for the current statement, apply the
-            // remainder before continuing.
-            Before(curr) => {
-                if curr.statement_index == block_data.statements.len() {
-                    let terminator = block_data.terminator();
-                    analysis.apply_terminator_effect(&mut self.state, terminator, curr);
-                } else {
-                    let statement = &block_data.statements[curr.statement_index];
-                    analysis.apply_statement_effect(&mut self.state, statement, curr);
-                }
-
-                // If all we needed to do was go from `Before` to `After` in the same statement,
-                // we are now done.
-                if curr.statement_index == target.statement_index {
-                    debug_assert!(apply_after_effect_at_target);
-                    self.pos = After(target);
-                    return;
-                }
-
-                curr.statement_index + 1
-            }
-        };
-
-        // We have now applied all effects prior to `first_unapplied_statement`.
-
-        // Apply the effects of all statements before `target`.
-        let mut location = Location { block: target.block, statement_index: 0 };
-        for statement_index in first_unapplied_statement..target.statement_index {
-            location.statement_index = statement_index;
-            let statement = &block_data.statements[statement_index];
-            analysis.apply_before_statement_effect(&mut self.state, statement, location);
-            analysis.apply_statement_effect(&mut self.state, statement, location);
-        }
-
-        // Apply the effect of the statement (or terminator) at `target`.
-        location.statement_index = target.statement_index;
-        if target.statement_index == block_data.statements.len() {
-            let terminator = &block_data.terminator();
-            analysis.apply_before_terminator_effect(&mut self.state, terminator, location);
-
-            if apply_after_effect_at_target {
-                analysis.apply_terminator_effect(&mut self.state, terminator, location);
-                self.pos = After(target);
-            } else {
-                self.pos = Before(target);
-            }
-        } else {
-            let statement = &block_data.statements[target.statement_index];
-            analysis.apply_before_statement_effect(&mut self.state, statement, location);
-
-            if apply_after_effect_at_target {
-                analysis.apply_statement_effect(&mut self.state, statement, location);
-                self.pos = After(target)
-            } else {
-                self.pos = Before(target);
-            }
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug)]
-enum CursorPosition {
-    /// No effects within this block have been applied.
-    BlockStart(BasicBlock),
-
-    /// Only the "before" effect of the statement (or terminator) at this location has been
-    /// applied (along with the effects of all previous statements).
-    Before(Location),
-
-    /// The effects of all statements up to and including the one at this location have been
-    /// applied.
-    After(Location),
-}
-
-impl CursorPosition {
-    fn block(&self) -> BasicBlock {
-        match *self {
-            Self::BlockStart(block) => block,
-            Self::Before(loc) | Self::After(loc) => loc.block,
-        }
-    }
-}
diff --git a/src/librustc_mir/dataflow/generic/engine.rs b/src/librustc_mir/dataflow/generic/engine.rs
deleted file mode 100644 (file)
index d320721..0000000
+++ /dev/null
@@ -1,525 +0,0 @@
-//! A solver for dataflow problems.
-
-use std::ffi::OsString;
-use std::fs;
-use std::path::PathBuf;
-
-use rustc::mir::{self, traversal, BasicBlock, Location};
-use rustc::ty::{self, TyCtxt};
-use rustc_ast::ast;
-use rustc_data_structures::work_queue::WorkQueue;
-use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
-use rustc_span::symbol::{sym, Symbol};
-
-use super::graphviz;
-use super::{Analysis, GenKillAnalysis, GenKillSet, Results};
-
-/// A solver for dataflow problems.
-pub struct Engine<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    bits_per_block: usize,
-    tcx: TyCtxt<'tcx>,
-    body: &'a mir::Body<'tcx>,
-    def_id: DefId,
-    dead_unwinds: Option<&'a BitSet<BasicBlock>>,
-    entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
-    analysis: A,
-
-    /// Cached, cumulative transfer functions for each block.
-    trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
-}
-
-impl<A> Engine<'a, 'tcx, A>
-where
-    A: GenKillAnalysis<'tcx>,
-{
-    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
-    pub fn new_gen_kill(
-        tcx: TyCtxt<'tcx>,
-        body: &'a mir::Body<'tcx>,
-        def_id: DefId,
-        analysis: A,
-    ) -> Self {
-        // If there are no back-edges in the control-flow graph, we only ever need to apply the
-        // transfer function for each block exactly once (assuming that we process blocks in RPO).
-        //
-        // In this case, there's no need to compute the block transfer functions ahead of time.
-        if !body.is_cfg_cyclic() {
-            return Self::new(tcx, body, def_id, analysis, None);
-        }
-
-        // Otherwise, compute and store the cumulative transfer function for each block.
-
-        let bits_per_block = analysis.bits_per_block(body);
-        let mut trans_for_block =
-            IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks());
-
-        for (block, block_data) in body.basic_blocks().iter_enumerated() {
-            let trans = &mut trans_for_block[block];
-
-            for (i, statement) in block_data.statements.iter().enumerate() {
-                let loc = Location { block, statement_index: i };
-                analysis.before_statement_effect(trans, statement, loc);
-                analysis.statement_effect(trans, statement, loc);
-            }
-
-            let terminator = block_data.terminator();
-            let loc = Location { block, statement_index: block_data.statements.len() };
-            analysis.before_terminator_effect(trans, terminator, loc);
-            analysis.terminator_effect(trans, terminator, loc);
-        }
-
-        Self::new(tcx, body, def_id, analysis, Some(trans_for_block))
-    }
-}
-
-impl<A> Engine<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
-    /// function.
-    ///
-    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
-    /// better performance.
-    pub fn new_generic(
-        tcx: TyCtxt<'tcx>,
-        body: &'a mir::Body<'tcx>,
-        def_id: DefId,
-        analysis: A,
-    ) -> Self {
-        Self::new(tcx, body, def_id, analysis, None)
-    }
-
-    fn new(
-        tcx: TyCtxt<'tcx>,
-        body: &'a mir::Body<'tcx>,
-        def_id: DefId,
-        analysis: A,
-        trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
-    ) -> Self {
-        let bits_per_block = analysis.bits_per_block(body);
-
-        let bottom_value_set = if A::BOTTOM_VALUE {
-            BitSet::new_filled(bits_per_block)
-        } else {
-            BitSet::new_empty(bits_per_block)
-        };
-
-        let mut entry_sets = IndexVec::from_elem(bottom_value_set, body.basic_blocks());
-        analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
-
-        Engine {
-            analysis,
-            bits_per_block,
-            tcx,
-            body,
-            def_id,
-            dead_unwinds: None,
-            entry_sets,
-            trans_for_block,
-        }
-    }
-
-    /// Signals that we do not want dataflow state to propagate across unwind edges for these
-    /// `BasicBlock`s.
-    ///
-    /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
-    /// unwind during execution. Otherwise, your dataflow results will not be correct.
-    pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
-        self.dead_unwinds = Some(dead_unwinds);
-        self
-    }
-
-    /// Computes the fixpoint for this dataflow problem and returns it.
-    pub fn iterate_to_fixpoint(mut self) -> Results<'tcx, A> {
-        let mut temp_state = BitSet::new_empty(self.bits_per_block);
-
-        let mut dirty_queue: WorkQueue<BasicBlock> =
-            WorkQueue::with_none(self.body.basic_blocks().len());
-
-        for (bb, _) in traversal::reverse_postorder(self.body) {
-            dirty_queue.insert(bb);
-        }
-
-        // Add blocks that are not reachable from START_BLOCK to the work queue. These blocks will
-        // be processed after the ones added above.
-        for bb in self.body.basic_blocks().indices() {
-            dirty_queue.insert(bb);
-        }
-
-        while let Some(bb) = dirty_queue.pop() {
-            let bb_data = &self.body[bb];
-            let on_entry = &self.entry_sets[bb];
-
-            temp_state.overwrite(on_entry);
-            self.apply_whole_block_effect(&mut temp_state, bb, bb_data);
-
-            self.propagate_bits_into_graph_successors_of(
-                &mut temp_state,
-                (bb, bb_data),
-                &mut dirty_queue,
-            );
-        }
-
-        let Engine { tcx, body, def_id, trans_for_block, entry_sets, analysis, .. } = self;
-        let results = Results { analysis, entry_sets };
-
-        let res = write_graphviz_results(tcx, def_id, body, &results, trans_for_block);
-        if let Err(e) = res {
-            warn!("Failed to write graphviz dataflow results: {}", e);
-        }
-
-        results
-    }
-
-    /// Applies the cumulative effect of an entire block, excluding the call return effect if one
-    /// exists.
-    fn apply_whole_block_effect(
-        &self,
-        state: &mut BitSet<A::Idx>,
-        block: BasicBlock,
-        block_data: &mir::BasicBlockData<'tcx>,
-    ) {
-        // Use the cached block transfer function if available.
-        if let Some(trans_for_block) = &self.trans_for_block {
-            trans_for_block[block].apply(state);
-            return;
-        }
-
-        // Otherwise apply effects one-by-one.
-
-        for (statement_index, statement) in block_data.statements.iter().enumerate() {
-            let location = Location { block, statement_index };
-            self.analysis.apply_before_statement_effect(state, statement, location);
-            self.analysis.apply_statement_effect(state, statement, location);
-        }
-
-        let terminator = block_data.terminator();
-        let location = Location { block, statement_index: block_data.statements.len() };
-        self.analysis.apply_before_terminator_effect(state, terminator, location);
-        self.analysis.apply_terminator_effect(state, terminator, location);
-    }
-
-    fn propagate_bits_into_graph_successors_of(
-        &mut self,
-        in_out: &mut BitSet<A::Idx>,
-        (bb, bb_data): (BasicBlock, &'a mir::BasicBlockData<'tcx>),
-        dirty_list: &mut WorkQueue<BasicBlock>,
-    ) {
-        use mir::TerminatorKind::*;
-
-        match bb_data.terminator().kind {
-            Return | Resume | Abort | GeneratorDrop | Unreachable => {}
-
-            Goto { target }
-            | Assert { target, cleanup: None, .. }
-            | Drop { target, location: _, unwind: None }
-            | DropAndReplace { target, value: _, location: _, unwind: None } => {
-                self.propagate_bits_into_entry_set_for(in_out, target, dirty_list)
-            }
-
-            Yield { resume: target, drop, resume_arg, .. } => {
-                if let Some(drop) = drop {
-                    self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list);
-                }
-
-                self.analysis.apply_yield_resume_effect(in_out, target, &resume_arg);
-                self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
-            }
-
-            Assert { target, cleanup: Some(unwind), .. }
-            | Drop { target, location: _, unwind: Some(unwind) }
-            | DropAndReplace { target, value: _, location: _, unwind: Some(unwind) } => {
-                self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
-                if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) {
-                    self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
-                }
-            }
-
-            SwitchInt { ref targets, ref values, ref discr, .. } => {
-                let Engine { tcx, body, .. } = *self;
-                let enum_ = discr
-                    .place()
-                    .and_then(|discr| switch_on_enum_discriminant(tcx, body, bb_data, discr));
-                match enum_ {
-                    // If this is a switch on an enum discriminant, a custom effect may be applied
-                    // along each outgoing edge.
-                    Some((enum_place, enum_def)) => {
-                        self.propagate_bits_into_enum_discriminant_switch_successors(
-                            in_out, bb, enum_def, enum_place, dirty_list, &*values, &*targets,
-                        );
-                    }
-
-                    // Otherwise, it's just a normal `SwitchInt`, and every successor sees the same
-                    // exit state.
-                    None => {
-                        for target in targets.iter().copied() {
-                            self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list);
-                        }
-                    }
-                }
-            }
-
-            Call { cleanup, ref destination, ref func, ref args, .. } => {
-                if let Some(unwind) = cleanup {
-                    if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) {
-                        self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
-                    }
-                }
-
-                if let Some((ref dest_place, dest_bb)) = *destination {
-                    // N.B.: This must be done *last*, otherwise the unwind path will see the call
-                    // return effect.
-                    self.analysis.apply_call_return_effect(in_out, bb, func, args, dest_place);
-                    self.propagate_bits_into_entry_set_for(in_out, dest_bb, dirty_list);
-                }
-            }
-
-            FalseEdges { real_target, imaginary_target } => {
-                self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
-                self.propagate_bits_into_entry_set_for(in_out, imaginary_target, dirty_list);
-            }
-
-            FalseUnwind { real_target, unwind } => {
-                self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
-                if let Some(unwind) = unwind {
-                    if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) {
-                        self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
-                    }
-                }
-            }
-        }
-    }
-
-    fn propagate_bits_into_entry_set_for(
-        &mut self,
-        in_out: &BitSet<A::Idx>,
-        bb: BasicBlock,
-        dirty_queue: &mut WorkQueue<BasicBlock>,
-    ) {
-        let entry_set = &mut self.entry_sets[bb];
-        let set_changed = self.analysis.join(entry_set, &in_out);
-        if set_changed {
-            dirty_queue.insert(bb);
-        }
-    }
-
-    fn propagate_bits_into_enum_discriminant_switch_successors(
-        &mut self,
-        in_out: &mut BitSet<A::Idx>,
-        bb: BasicBlock,
-        enum_def: &'tcx ty::AdtDef,
-        enum_place: &mir::Place<'tcx>,
-        dirty_list: &mut WorkQueue<BasicBlock>,
-        values: &[u128],
-        targets: &[BasicBlock],
-    ) {
-        // MIR building adds discriminants to the `values` array in the same order as they
-        // are yielded by `AdtDef::discriminants`. We rely on this to match each
-        // discriminant in `values` to its corresponding variant in linear time.
-        let mut tmp = BitSet::new_empty(in_out.domain_size());
-        let mut discriminants = enum_def.discriminants(self.tcx);
-        for (value, target) in values.iter().zip(targets.iter().copied()) {
-            let (variant_idx, _) = discriminants.find(|&(_, discr)| discr.val == *value).expect(
-                "Order of `AdtDef::discriminants` differed from that of `SwitchInt::values`",
-            );
-
-            tmp.overwrite(in_out);
-            self.analysis.apply_discriminant_switch_effect(
-                &mut tmp,
-                bb,
-                enum_place,
-                enum_def,
-                variant_idx,
-            );
-            self.propagate_bits_into_entry_set_for(&tmp, target, dirty_list);
-        }
-
-        std::mem::drop(tmp);
-
-        // Propagate dataflow state along the "otherwise" edge.
-        let otherwise = targets.last().copied().unwrap();
-        self.propagate_bits_into_entry_set_for(&in_out, otherwise, dirty_list);
-    }
-}
-
-/// 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:
-///   _42 = discriminant(_1)
-///   SwitchInt(_42, ..)
-///
-/// If the basic block matches this pattern, this function returns the place corresponding to the
-/// enum (`_1` in the example above) as well as the `AdtDef` of that enum.
-fn switch_on_enum_discriminant(
-    tcx: TyCtxt<'tcx>,
-    body: &'mir mir::Body<'tcx>,
-    block: &'mir mir::BasicBlockData<'tcx>,
-    switch_on: &mir::Place<'tcx>,
-) -> Option<(&'mir mir::Place<'tcx>, &'tcx ty::AdtDef)> {
-    match block.statements.last().map(|stmt| &stmt.kind) {
-        Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated))))
-            if lhs == switch_on =>
-        {
-            match &discriminated.ty(body, tcx).ty.kind {
-                ty::Adt(def, _) => Some((discriminated, def)),
-
-                // `Rvalue::Discriminant` is also used to get the active yield point for a
-                // generator, but we do not need edge-specific effects in that case. This may
-                // change in the future.
-                ty::Generator(..) => None,
-
-                t => bug!("`discriminant` called on unexpected type {:?}", t),
-            }
-        }
-
-        _ => None,
-    }
-}
-
-// Graphviz
-
-/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
-/// `rustc_mir` attributes.
-fn write_graphviz_results<A>(
-    tcx: TyCtxt<'tcx>,
-    def_id: DefId,
-    body: &mir::Body<'tcx>,
-    results: &Results<'tcx, A>,
-    block_transfer_functions: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
-) -> std::io::Result<()>
-where
-    A: Analysis<'tcx>,
-{
-    let attrs = match RustcMirAttrs::parse(tcx, def_id) {
-        Ok(attrs) => attrs,
-
-        // Invalid `rustc_mir` attrs will be reported using `span_err`.
-        Err(()) => return Ok(()),
-    };
-
-    let path = match attrs.output_path(A::NAME) {
-        Some(path) => path,
-        None => return Ok(()),
-    };
-
-    let bits_per_block = results.analysis.bits_per_block(body);
-
-    let mut formatter: Box<dyn graphviz::StateFormatter<'tcx, _>> = match attrs.formatter {
-        Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)),
-        Some(sym::gen_kill) => {
-            if let Some(trans_for_block) = block_transfer_functions {
-                Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block))
-            } else {
-                Box::new(graphviz::SimpleDiff::new(bits_per_block))
-            }
-        }
-
-        // Default to the `SimpleDiff` output style.
-        _ => Box::new(graphviz::SimpleDiff::new(bits_per_block)),
-    };
-
-    debug!("printing dataflow results for {:?} to {}", def_id, path.display());
-    let mut buf = Vec::new();
-
-    let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter);
-    dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?;
-    fs::write(&path, buf)?;
-    Ok(())
-}
-
-#[derive(Default)]
-struct RustcMirAttrs {
-    basename_and_suffix: Option<PathBuf>,
-    formatter: Option<Symbol>,
-}
-
-impl RustcMirAttrs {
-    fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
-        let attrs = tcx.get_attrs(def_id);
-
-        let mut result = Ok(());
-        let mut ret = RustcMirAttrs::default();
-
-        let rustc_mir_attrs = attrs
-            .iter()
-            .filter(|attr| attr.check_name(sym::rustc_mir))
-            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
-
-        for attr in rustc_mir_attrs {
-            let attr_result = if attr.check_name(sym::borrowck_graphviz_postflow) {
-                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
-                    let path = PathBuf::from(s.to_string());
-                    match path.file_name() {
-                        Some(_) => Ok(path),
-                        None => {
-                            tcx.sess.span_err(attr.span(), "path must end in a filename");
-                            Err(())
-                        }
-                    }
-                })
-            } else if attr.check_name(sym::borrowck_graphviz_format) {
-                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
-                    sym::gen_kill | sym::two_phase => Ok(s),
-                    _ => {
-                        tcx.sess.span_err(attr.span(), "unknown formatter");
-                        Err(())
-                    }
-                })
-            } else {
-                Ok(())
-            };
-
-            result = result.and(attr_result);
-        }
-
-        result.map(|()| ret)
-    }
-
-    fn set_field<T>(
-        field: &mut Option<T>,
-        tcx: TyCtxt<'tcx>,
-        attr: &ast::NestedMetaItem,
-        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
-    ) -> Result<(), ()> {
-        if field.is_some() {
-            tcx.sess
-                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));
-
-            return Err(());
-        }
-
-        if let Some(s) = attr.value_str() {
-            *field = Some(mapper(s)?);
-            Ok(())
-        } else {
-            tcx.sess
-                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
-            Err(())
-        }
-    }
-
-    /// Returns the path where dataflow results should be written, or `None`
-    /// `borrowck_graphviz_postflow` was not specified.
-    ///
-    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
-    ///
-    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
-    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
-        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
-        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
-
-        let mut file_name: OsString = analysis_name.into();
-        file_name.push("_");
-        file_name.push(suffix);
-        ret.set_file_name(file_name);
-
-        Some(ret)
-    }
-}
diff --git a/src/librustc_mir/dataflow/generic/graphviz.rs b/src/librustc_mir/dataflow/generic/graphviz.rs
deleted file mode 100644 (file)
index c15f2a7..0000000
+++ /dev/null
@@ -1,696 +0,0 @@
-//! A helpful diagram for debugging dataflow problems.
-
-use std::cell::RefCell;
-use std::{io, ops, str};
-
-use rustc::mir::{self, BasicBlock, Body, Location};
-use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::{BitSet, HybridBitSet};
-use rustc_index::vec::{Idx, IndexVec};
-
-use super::{Analysis, GenKillSet, Results, ResultsRefCursor};
-use crate::util::graphviz_safe_def_name;
-
-pub struct Formatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    body: &'a Body<'tcx>,
-    def_id: DefId,
-
-    // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
-    block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
-}
-
-impl<A> Formatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    pub fn new(
-        body: &'a Body<'tcx>,
-        def_id: DefId,
-        results: &'a Results<'tcx, A>,
-        state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
-    ) -> Self {
-        let block_formatter = BlockFormatter {
-            bg: Background::Light,
-            results: ResultsRefCursor::new(body, results),
-            state_formatter,
-        };
-
-        Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) }
-    }
-}
-
-/// A pair of a basic block and an index into that basic blocks `successors`.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct CfgEdge {
-    source: BasicBlock,
-    index: usize,
-}
-
-fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
-    body[bb]
-        .terminator()
-        .successors()
-        .enumerate()
-        .map(|(index, _)| CfgEdge { source: bb, index })
-        .collect()
-}
-
-impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    type Node = BasicBlock;
-    type Edge = CfgEdge;
-
-    fn graph_id(&self) -> dot::Id<'_> {
-        let name = graphviz_safe_def_name(self.def_id);
-        dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
-    }
-
-    fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
-        dot::Id::new(format!("bb_{}", n.index())).unwrap()
-    }
-
-    fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
-        let mut label = Vec::new();
-        self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap();
-        dot::LabelText::html(String::from_utf8(label).unwrap())
-    }
-
-    fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
-        Some(dot::LabelText::label("none"))
-    }
-
-    fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
-        let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index];
-        dot::LabelText::label(label.clone())
-    }
-}
-
-impl<A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    type Node = BasicBlock;
-    type Edge = CfgEdge;
-
-    fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
-        self.body.basic_blocks().indices().collect::<Vec<_>>().into()
-    }
-
-    fn edges(&self) -> dot::Edges<'_, Self::Edge> {
-        self.body
-            .basic_blocks()
-            .indices()
-            .flat_map(|bb| outgoing_edges(self.body, bb))
-            .collect::<Vec<_>>()
-            .into()
-    }
-
-    fn source(&self, edge: &Self::Edge) -> Self::Node {
-        edge.source
-    }
-
-    fn target(&self, edge: &Self::Edge) -> Self::Node {
-        self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap()
-    }
-}
-
-struct BlockFormatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    results: ResultsRefCursor<'a, 'a, 'tcx, A>,
-    bg: Background,
-    state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
-}
-
-impl<A> BlockFormatter<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    const HEADER_COLOR: &'static str = "#a0a0a0";
-
-    fn num_state_columns(&self) -> usize {
-        std::cmp::max(1, self.state_formatter.column_names().len())
-    }
-
-    fn toggle_background(&mut self) -> Background {
-        let bg = self.bg;
-        self.bg = !bg;
-        bg
-    }
-
-    fn write_node_label(
-        &mut self,
-        w: &mut impl io::Write,
-        body: &'a Body<'tcx>,
-        block: BasicBlock,
-    ) -> io::Result<()> {
-        //   Sample output:
-        //   +-+-----------------------------------------------+
-        // A |                      bb4                        |
-        //   +-+----------------------------------+------------+
-        // B |                MIR                 |   STATE    |
-        //   +-+----------------------------------+------------+
-        // C | | (on entry)                       | {_0,_2,_3} |
-        //   +-+----------------------------------+------------+
-        // D |0| StorageLive(_7)                  |            |
-        //   +-+----------------------------------+------------+
-        //   |1| StorageLive(_8)                  |            |
-        //   +-+----------------------------------+------------+
-        //   |2| _8 = &mut _1                     | +_8        |
-        //   +-+----------------------------------+------------+
-        // E |T| _4 = const Foo::twiddle(move _2) | -_2        |
-        //   +-+----------------------------------+------------+
-        // F | | (on unwind)                      | {_0,_3,_8} |
-        //   +-+----------------------------------+------------+
-        //   | | (on successful return)           | +_4        |
-        //   +-+----------------------------------+------------+
-
-        // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their
-        // children. This is because `xdot` seemed to have a hard time correctly propagating
-        // attributes. Make sure to test the output before trying to remove the redundancy.
-        // Notably, `align` was found to have no effect when applied only to <table>.
-
-        let table_fmt = concat!(
-            " border=\"1\"",
-            " cellborder=\"1\"",
-            " cellspacing=\"0\"",
-            " cellpadding=\"3\"",
-            " sides=\"rb\"",
-        );
-        write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?;
-
-        // A + B: Block header
-        if self.state_formatter.column_names().is_empty() {
-            self.write_block_header_simple(w, block)?;
-        } else {
-            self.write_block_header_with_state_columns(w, block)?;
-        }
-
-        // C: Entry state
-        self.bg = Background::Light;
-        self.results.seek_to_block_start(block);
-        let block_entry_state = self.results.get().clone();
-
-        self.write_row_with_full_state(w, "", "(on entry)")?;
-
-        // D: Statement transfer functions
-        for (i, statement) in body[block].statements.iter().enumerate() {
-            let location = Location { block, statement_index: i };
-            let statement_str = format!("{:?}", statement);
-            self.write_row_for_location(w, &i.to_string(), &statement_str, location)?;
-        }
-
-        // E: Terminator transfer function
-        let terminator = body[block].terminator();
-        let terminator_loc = body.terminator_loc(block);
-        let mut terminator_str = String::new();
-        terminator.kind.fmt_head(&mut terminator_str).unwrap();
-
-        self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?;
-
-        // F: Exit state
-
-        // Write the full dataflow state immediately after the terminator if it differs from the
-        // state at block entry.
-        self.results.seek_after(terminator_loc);
-        if self.results.get() != &block_entry_state {
-            let after_terminator_name = match terminator.kind {
-                mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)",
-                _ => "(on exit)",
-            };
-
-            self.write_row_with_full_state(w, "", after_terminator_name)?;
-        }
-
-        // Write any changes caused by terminator-specific effects
-        match terminator.kind {
-            mir::TerminatorKind::Call { destination: Some(_), .. } => {
-                let num_state_columns = self.num_state_columns();
-                self.write_row(w, "", "(on successful return)", |this, w, fmt| {
-                    write!(
-                        w,
-                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
-                        colspan = num_state_columns,
-                        fmt = fmt,
-                    )?;
-
-                    let state_on_unwind = this.results.get().clone();
-                    this.results.seek_after_assume_success(terminator_loc);
-                    write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?;
-
-                    write!(w, "</td>")
-                })?;
-            }
-
-            _ => {}
-        };
-
-        write!(w, "</table>")
-    }
-
-    fn write_block_header_simple(
-        &mut self,
-        w: &mut impl io::Write,
-        block: BasicBlock,
-    ) -> io::Result<()> {
-        //   +-------------------------------------------------+
-        // A |                      bb4                        |
-        //   +-----------------------------------+-------------+
-        // B |                MIR                |    STATE    |
-        //   +-+---------------------------------+-------------+
-        //   | |              ...                |             |
-
-        // A
-        write!(
-            w,
-            concat!("<tr>", r#"<td colspan="3" sides="tl">bb{block_id}</td>"#, "</tr>",),
-            block_id = block.index(),
-        )?;
-
-        // B
-        write!(
-            w,
-            concat!(
-                "<tr>",
-                r#"<td colspan="2" {fmt}>MIR</td>"#,
-                r#"<td {fmt}>STATE</td>"#,
-                "</tr>",
-            ),
-            fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR),
-        )
-    }
-
-    fn write_block_header_with_state_columns(
-        &mut self,
-        w: &mut impl io::Write,
-        block: BasicBlock,
-    ) -> io::Result<()> {
-        //   +------------------------------------+-------------+
-        // A |                bb4                 |    STATE    |
-        //   +------------------------------------+------+------+
-        // B |                MIR                 |  GEN | KILL |
-        //   +-+----------------------------------+------+------+
-        //   | |              ...                 |      |      |
-
-        let state_column_names = self.state_formatter.column_names();
-
-        // A
-        write!(
-            w,
-            concat!(
-                "<tr>",
-                r#"<td {fmt} colspan="2">bb{block_id}</td>"#,
-                r#"<td {fmt} colspan="{num_state_cols}">STATE</td>"#,
-                "</tr>",
-            ),
-            fmt = "sides=\"tl\"",
-            num_state_cols = state_column_names.len(),
-            block_id = block.index(),
-        )?;
-
-        // B
-        let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR);
-        write!(w, concat!("<tr>", r#"<td colspan="2" {fmt}>MIR</td>"#,), fmt = fmt,)?;
-
-        for name in state_column_names {
-            write!(w, "<td {fmt}>{name}</td>", fmt = fmt, name = name)?;
-        }
-
-        write!(w, "</tr>")
-    }
-
-    /// Write a row with the given index and MIR, using the function argument to fill in the
-    /// "STATE" column(s).
-    fn write_row<W: io::Write>(
-        &mut self,
-        w: &mut W,
-        i: &str,
-        mir: &str,
-        f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>,
-    ) -> io::Result<()> {
-        let bg = self.toggle_background();
-        let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" };
-
-        let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr());
-
-        write!(
-            w,
-            concat!(
-                "<tr>",
-                r#"<td {fmt} align="right">{i}</td>"#,
-                r#"<td {fmt} align="left">{mir}</td>"#,
-            ),
-            i = i,
-            fmt = fmt,
-            mir = dot::escape_html(mir),
-        )?;
-
-        f(self, w, &fmt)?;
-        write!(w, "</tr>")
-    }
-
-    fn write_row_with_full_state(
-        &mut self,
-        w: &mut impl io::Write,
-        i: &str,
-        mir: &str,
-    ) -> io::Result<()> {
-        self.write_row(w, i, mir, |this, w, fmt| {
-            let state = this.results.get();
-            let analysis = this.results.analysis();
-
-            write!(
-                w,
-                r#"<td colspan="{colspan}" {fmt} align="left">{{"#,
-                colspan = this.num_state_columns(),
-                fmt = fmt,
-            )?;
-            pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?;
-            write!(w, "}}</td>")
-        })
-    }
-
-    fn write_row_for_location(
-        &mut self,
-        w: &mut impl io::Write,
-        i: &str,
-        mir: &str,
-        location: Location,
-    ) -> io::Result<()> {
-        self.write_row(w, i, mir, |this, w, fmt| {
-            this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location)
-        })
-    }
-}
-
-/// Controls what gets printed under the `STATE` header.
-pub trait StateFormatter<'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    /// The columns that will get printed under `STATE`.
-    fn column_names(&self) -> &[&str];
-
-    fn write_state_for_location(
-        &mut self,
-        w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()>;
-}
-
-/// Prints a single column containing the state vector immediately *after* each statement.
-pub struct SimpleDiff<T: Idx> {
-    prev_state: BitSet<T>,
-    prev_loc: Location,
-}
-
-impl<T: Idx> SimpleDiff<T> {
-    pub fn new(bits_per_block: usize) -> Self {
-        SimpleDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
-    }
-}
-
-impl<A> StateFormatter<'tcx, A> for SimpleDiff<A::Idx>
-where
-    A: Analysis<'tcx>,
-{
-    fn column_names(&self) -> &[&str] {
-        &[]
-    }
-
-    fn write_state_for_location(
-        &mut self,
-        mut w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()> {
-        if location.statement_index == 0 {
-            results.seek_to_block_start(location.block);
-            self.prev_state.overwrite(results.get());
-        } else {
-            // Ensure that we are visiting statements in order, so `prev_state` is correct.
-            assert_eq!(self.prev_loc.successor_within_block(), location);
-        }
-
-        self.prev_loc = location;
-        write!(w, r#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?;
-        results.seek_after(location);
-        let curr_state = results.get();
-        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
-        self.prev_state.overwrite(curr_state);
-        write!(w, "</td>")
-    }
-}
-
-/// Prints two state columns, one containing only the "before" effect of each statement and one
-/// containing the full effect.
-pub struct TwoPhaseDiff<T: Idx> {
-    prev_state: BitSet<T>,
-    prev_loc: Location,
-}
-
-impl<T: Idx> TwoPhaseDiff<T> {
-    pub fn new(bits_per_block: usize) -> Self {
-        TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
-    }
-}
-
-impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx>
-where
-    A: Analysis<'tcx>,
-{
-    fn column_names(&self) -> &[&str] {
-        &["BEFORE", " AFTER"]
-    }
-
-    fn write_state_for_location(
-        &mut self,
-        mut w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()> {
-        if location.statement_index == 0 {
-            results.seek_to_block_start(location.block);
-            self.prev_state.overwrite(results.get());
-        } else {
-            // Ensure that we are visiting statements in order, so `prev_state` is correct.
-            assert_eq!(self.prev_loc.successor_within_block(), location);
-        }
-
-        self.prev_loc = location;
-
-        // Before
-
-        write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
-        results.seek_before(location);
-        let curr_state = results.get();
-        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
-        self.prev_state.overwrite(curr_state);
-        write!(w, "</td>")?;
-
-        // After
-
-        write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
-        results.seek_after(location);
-        let curr_state = results.get();
-        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
-        self.prev_state.overwrite(curr_state);
-        write!(w, "</td>")
-    }
-}
-
-/// Prints the gen/kill set for the entire block.
-pub struct BlockTransferFunc<'a, 'tcx, T: Idx> {
-    body: &'a mir::Body<'tcx>,
-    trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
-}
-
-impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> {
-    pub fn new(
-        body: &'mir mir::Body<'tcx>,
-        trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
-    ) -> Self {
-        BlockTransferFunc { body, trans_for_block }
-    }
-}
-
-impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx>
-where
-    A: Analysis<'tcx>,
-{
-    fn column_names(&self) -> &[&str] {
-        &["GEN", "KILL"]
-    }
-
-    fn write_state_for_location(
-        &mut self,
-        mut w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()> {
-        // Only print a single row.
-        if location.statement_index != 0 {
-            return Ok(());
-        }
-
-        let block_trans = &self.trans_for_block[location.block];
-        let rowspan = self.body.basic_blocks()[location.block].statements.len();
-
-        for set in &[&block_trans.gen, &block_trans.kill] {
-            write!(
-                w,
-                r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#,
-                fmt = fmt,
-                rowspan = rowspan
-            )?;
-
-            pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?;
-            write!(w, "</td>")?;
-        }
-
-        Ok(())
-    }
-}
-
-/// Writes two lines, one containing the added bits and one the removed bits.
-fn write_diff<A: Analysis<'tcx>>(
-    w: &mut impl io::Write,
-    analysis: &A,
-    from: &BitSet<A::Idx>,
-    to: &BitSet<A::Idx>,
-) -> io::Result<()> {
-    assert_eq!(from.domain_size(), to.domain_size());
-    let len = from.domain_size();
-
-    let mut set = HybridBitSet::new_empty(len);
-    let mut clear = HybridBitSet::new_empty(len);
-
-    // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets.
-    for i in (0..len).map(A::Idx::new) {
-        match (from.contains(i), to.contains(i)) {
-            (false, true) => set.insert(i),
-            (true, false) => clear.insert(i),
-            _ => continue,
-        };
-    }
-
-    if !set.is_empty() {
-        write!(w, r#"<font color="darkgreen">+"#)?;
-        pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?;
-        write!(w, r#"</font>"#)?;
-    }
-
-    if !set.is_empty() && !clear.is_empty() {
-        write!(w, "{}", BR_LEFT)?;
-    }
-
-    if !clear.is_empty() {
-        write!(w, r#"<font color="red">-"#)?;
-        pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?;
-        write!(w, r#"</font>"#)?;
-    }
-
-    Ok(())
-}
-
-const BR_LEFT: &str = r#"<br align="left"/>"#;
-const BR_LEFT_SPACE: &str = r#"<br align="left"/> "#;
-
-/// Line break policy that breaks at 40 characters and starts the next line with a single space.
-const LIMIT_30_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 });
-
-struct LineBreak {
-    sequence: &'static str,
-    limit: usize,
-}
-
-/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given
-/// separator (`sep`).
-///
-/// Optionally, it will break lines using the given character sequence (usually `<br/>`) and
-/// character limit.
-fn pretty_print_state_elems<A>(
-    w: &mut impl io::Write,
-    analysis: &A,
-    elems: impl Iterator<Item = A::Idx>,
-    sep: &str,
-    line_break: Option<LineBreak>,
-) -> io::Result<bool>
-where
-    A: Analysis<'tcx>,
-{
-    let sep_width = sep.chars().count();
-
-    let mut buf = Vec::new();
-
-    let mut first = true;
-    let mut curr_line_width = 0;
-    let mut line_break_inserted = false;
-
-    for idx in elems {
-        buf.clear();
-        analysis.pretty_print_idx(&mut buf, idx)?;
-        let idx_str =
-            str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8");
-        let escaped = dot::escape_html(idx_str);
-        let escaped_width = escaped.chars().count();
-
-        if first {
-            first = false;
-        } else {
-            write!(w, "{}", sep)?;
-            curr_line_width += sep_width;
-
-            if let Some(line_break) = &line_break {
-                if curr_line_width + sep_width + escaped_width > line_break.limit {
-                    write!(w, "{}", line_break.sequence)?;
-                    line_break_inserted = true;
-                    curr_line_width = 0;
-                }
-            }
-        }
-
-        write!(w, "{}", escaped)?;
-        curr_line_width += escaped_width;
-    }
-
-    Ok(line_break_inserted)
-}
-
-/// The background color used for zebra-striping the table.
-#[derive(Clone, Copy)]
-enum Background {
-    Light,
-    Dark,
-}
-
-impl Background {
-    fn attr(self) -> &'static str {
-        match self {
-            Self::Dark => "bgcolor=\"#f0f0f0\"",
-            Self::Light => "",
-        }
-    }
-}
-
-impl ops::Not for Background {
-    type Output = Self;
-
-    fn not(self) -> Self {
-        match self {
-            Self::Light => Self::Dark,
-            Self::Dark => Self::Light,
-        }
-    }
-}
diff --git a/src/librustc_mir/dataflow/generic/mod.rs b/src/librustc_mir/dataflow/generic/mod.rs
deleted file mode 100644 (file)
index fb4b7b9..0000000
+++ /dev/null
@@ -1,487 +0,0 @@
-//! A framework that can express both [gen-kill] and generic dataflow problems.
-//!
-//! There is another interface for dataflow in the compiler in `librustc_mir/dataflow/mod.rs`. The
-//! interface in this module will eventually [replace that one][design-meeting].
-//!
-//! To actually use this framework, you must implement either the `Analysis` or the `GenKillAnalysis`
-//! trait. If your transfer function can be expressed with only gen/kill operations, prefer
-//! `GenKillAnalysis` since it will run faster while iterating to fixpoint. Create an `Engine` using
-//! the appropriate constructor and call `iterate_to_fixpoint`. You can use a `ResultsCursor` to
-//! inspect the fixpoint solution to your dataflow problem.
-//!
-//! ```ignore(cross-crate-imports)
-//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) {
-//!     let analysis = MyAnalysis::new();
-//!
-//!     // If `MyAnalysis` implements `GenKillAnalysis`.
-//!     let results = Engine::new_gen_kill(tcx, body, did, analysis).iterate_to_fixpoint();
-//!
-//!     // If `MyAnalysis` implements `Analysis`.
-//!     // let results = Engine::new_generic(tcx, body, did, analysis).iterate_to_fixpoint();
-//!
-//!     let mut cursor = ResultsCursor::new(body, results);
-//!
-//!     for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() {
-//!         cursor.seek_after(Location { block: START_BLOCK, statement_index });
-//!         let state = cursor.get();
-//!         println!("{:?}", state);
-//!     }
-//! }
-//! ```
-//!
-//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
-//! [design-meeting]https://github.com/rust-lang/compiler-team/issues/202
-
-use std::io;
-
-use rustc::mir::{self, BasicBlock, Location};
-use rustc::ty::layout::VariantIdx;
-use rustc::ty::{self, TyCtxt};
-use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::{BitSet, HybridBitSet};
-use rustc_index::vec::{Idx, IndexVec};
-
-use crate::dataflow::BottomValue;
-
-mod cursor;
-mod engine;
-mod graphviz;
-mod visitor;
-
-pub use self::cursor::{ResultsCursor, ResultsRefCursor};
-pub use self::engine::Engine;
-pub use self::visitor::{visit_results, ResultsVisitor};
-pub use self::visitor::{BorrowckFlowState, BorrowckResults};
-
-/// A dataflow analysis that has converged to fixpoint.
-pub struct Results<'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    pub analysis: A,
-    entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
-}
-
-impl<A> Results<'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    /// Creates a `ResultsCursor` that can inspect these `Results`.
-    pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
-        ResultsCursor::new(body, self)
-    }
-
-    /// Gets the entry set for the given block.
-    pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet<A::Idx> {
-        &self.entry_sets[block]
-    }
-
-    pub fn visit_with(
-        &self,
-        body: &'mir mir::Body<'tcx>,
-        blocks: impl IntoIterator<Item = BasicBlock>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
-    ) {
-        visit_results(body, blocks, self, vis)
-    }
-
-    pub fn visit_in_rpo_with(
-        &self,
-        body: &'mir mir::Body<'tcx>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
-    ) {
-        let blocks = mir::traversal::reverse_postorder(body);
-        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
-    }
-}
-
-/// Define the domain of a dataflow problem.
-///
-/// This trait specifies the lattice on which this analysis operates. For now, this must be a
-/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet`
-/// and referred to as the state vector.
-///
-/// This trait also defines the initial value for the dataflow state upon entry to the
-/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging.
-pub trait AnalysisDomain<'tcx>: BottomValue {
-    /// The type of the elements in the state vector.
-    type Idx: Idx;
-
-    /// A descriptive name for this analysis. Used only for debugging.
-    ///
-    /// This name should be brief and contain no spaces, periods or other characters that are not
-    /// suitable as part of a filename.
-    const NAME: &'static str;
-
-    /// The size of the state vector.
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
-
-    /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow
-    /// analysis.
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>);
-
-    /// Prints an element in the state vector for debugging.
-    fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
-        write!(w, "{:?}", idx)
-    }
-}
-
-/// A dataflow problem with an arbitrarily complex transfer function.
-pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
-    /// Updates the current dataflow state with the effect of evaluating a statement.
-    fn apply_statement_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    );
-
-    /// Updates the current dataflow state with an effect that occurs immediately *before* the
-    /// given statement.
-    ///
-    /// This method is useful if the consumer of the results of this analysis needs only 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 implementing `apply_statement_effect`.
-    fn apply_before_statement_effect(
-        &self,
-        _state: &mut BitSet<Self::Idx>,
-        _statement: &mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// Updates the current dataflow state with the effect of evaluating a terminator.
-    ///
-    /// The effect of a successful return from a `Call` terminator should **not** be accounted for
-    /// 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(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    );
-
-    /// 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 implementing `apply_terminator_effect`.
-    fn apply_before_terminator_effect(
-        &self,
-        _state: &mut BitSet<Self::Idx>,
-        _terminator: &mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// 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.
-    fn apply_call_return_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: &mir::Place<'tcx>,
-    );
-
-    /// Updates the current dataflow state with the effect of resuming from a `Yield` terminator.
-    ///
-    /// This is similar to `apply_call_return_effect` in that it only takes place after the
-    /// generator is resumed, not when it is dropped.
-    ///
-    /// By default, no effects happen.
-    fn apply_yield_resume_effect(
-        &self,
-        _state: &mut BitSet<Self::Idx>,
-        _resume_block: BasicBlock,
-        _resume_place: &mir::Place<'tcx>,
-    ) {
-    }
-
-    /// Updates the current dataflow state with the effect of taking a particular branch in a
-    /// `SwitchInt` terminator.
-    ///
-    /// Much like `apply_call_return_effect`, this effect is only propagated along a single
-    /// outgoing edge from this basic block.
-    fn apply_discriminant_switch_effect(
-        &self,
-        _state: &mut BitSet<Self::Idx>,
-        _block: BasicBlock,
-        _enum_place: &mir::Place<'tcx>,
-        _adt: &ty::AdtDef,
-        _variant: VariantIdx,
-    ) {
-    }
-
-    /// Creates an `Engine` to find the fixpoint for this dataflow problem.
-    ///
-    /// You shouldn't need to override this outside this module, since the combination of the
-    /// default impl and the one for all `A: GenKillAnalysis` will do the right thing.
-    /// Its purpose is to enable method chaining like so:
-    ///
-    /// ```ignore(cross-crate-imports)
-    /// let results = MyAnalysis::new(tcx, body)
-    ///     .into_engine(tcx, body, def_id)
-    ///     .iterate_to_fixpoint()
-    ///     .into_results_cursor(body);
-    /// ```
-    fn into_engine(
-        self,
-        tcx: TyCtxt<'tcx>,
-        body: &'mir mir::Body<'tcx>,
-        def_id: DefId,
-    ) -> Engine<'mir, 'tcx, Self>
-    where
-        Self: Sized,
-    {
-        Engine::new_generic(tcx, body, def_id, self)
-    }
-}
-
-/// A gen/kill dataflow problem.
-///
-/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
-/// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer
-/// functions for each statement in this way, the transfer function for an entire basic block can
-/// be computed efficiently.
-///
-/// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`.
-pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
-    /// See `Analysis::apply_statement_effect`.
-    fn statement_effect(
-        &self,
-        trans: &mut impl GenKill<Self::Idx>,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    );
-
-    /// See `Analysis::apply_before_statement_effect`.
-    fn before_statement_effect(
-        &self,
-        _trans: &mut impl GenKill<Self::Idx>,
-        _statement: &mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// See `Analysis::apply_terminator_effect`.
-    fn terminator_effect(
-        &self,
-        trans: &mut impl GenKill<Self::Idx>,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    );
-
-    /// See `Analysis::apply_before_terminator_effect`.
-    fn before_terminator_effect(
-        &self,
-        _trans: &mut impl GenKill<Self::Idx>,
-        _terminator: &mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// See `Analysis::apply_call_return_effect`.
-    fn call_return_effect(
-        &self,
-        trans: &mut impl GenKill<Self::Idx>,
-        block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: &mir::Place<'tcx>,
-    );
-
-    /// See `Analysis::apply_yield_resume_effect`.
-    fn yield_resume_effect(
-        &self,
-        _trans: &mut BitSet<Self::Idx>,
-        _resume_block: BasicBlock,
-        _resume_place: &mir::Place<'tcx>,
-    ) {
-    }
-
-    /// See `Analysis::apply_discriminant_switch_effect`.
-    fn discriminant_switch_effect(
-        &self,
-        _state: &mut impl GenKill<Self::Idx>,
-        _block: BasicBlock,
-        _enum_place: &mir::Place<'tcx>,
-        _adt: &ty::AdtDef,
-        _variant: VariantIdx,
-    ) {
-    }
-}
-
-impl<A> Analysis<'tcx> for A
-where
-    A: GenKillAnalysis<'tcx>,
-{
-    fn apply_statement_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        self.statement_effect(state, statement, location);
-    }
-
-    fn apply_before_statement_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        self.before_statement_effect(state, statement, location);
-    }
-
-    fn apply_terminator_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    ) {
-        self.terminator_effect(state, terminator, location);
-    }
-
-    fn apply_before_terminator_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    ) {
-        self.before_terminator_effect(state, terminator, location);
-    }
-
-    fn apply_call_return_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        block: BasicBlock,
-        func: &mir::Operand<'tcx>,
-        args: &[mir::Operand<'tcx>],
-        return_place: &mir::Place<'tcx>,
-    ) {
-        self.call_return_effect(state, block, func, args, return_place);
-    }
-
-    fn apply_yield_resume_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        resume_block: BasicBlock,
-        resume_place: &mir::Place<'tcx>,
-    ) {
-        self.yield_resume_effect(state, resume_block, resume_place);
-    }
-
-    fn apply_discriminant_switch_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        block: BasicBlock,
-        enum_place: &mir::Place<'tcx>,
-        adt: &ty::AdtDef,
-        variant: VariantIdx,
-    ) {
-        self.discriminant_switch_effect(state, block, enum_place, adt, variant);
-    }
-
-    fn into_engine(
-        self,
-        tcx: TyCtxt<'tcx>,
-        body: &'mir mir::Body<'tcx>,
-        def_id: DefId,
-    ) -> Engine<'mir, 'tcx, Self>
-    where
-        Self: Sized,
-    {
-        Engine::new_gen_kill(tcx, body, def_id, self)
-    }
-}
-
-/// The legal operations for a transfer function in a gen/kill problem.
-///
-/// This abstraction exists because there are two different contexts in which we call the methods in
-/// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently
-/// applied multiple times, such as when computing the cumulative transfer function for each block.
-/// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes,
-/// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the
-/// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to
-/// building up a `GenKillSet` and then throwing it away.
-pub trait GenKill<T> {
-    /// Inserts `elem` into the state vector.
-    fn gen(&mut self, elem: T);
-
-    /// Removes `elem` from the state vector.
-    fn kill(&mut self, elem: T);
-
-    /// Calls `gen` for each element in `elems`.
-    fn gen_all(&mut self, elems: impl IntoIterator<Item = T>) {
-        for elem in elems {
-            self.gen(elem);
-        }
-    }
-
-    /// Calls `kill` for each element in `elems`.
-    fn kill_all(&mut self, elems: impl IntoIterator<Item = T>) {
-        for elem in elems {
-            self.kill(elem);
-        }
-    }
-}
-
-/// Stores a transfer function for a gen/kill problem.
-///
-/// Calling `gen`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be
-/// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for
-/// the same element, the most recent one takes precedence.
-#[derive(Clone)]
-pub struct GenKillSet<T: Idx> {
-    gen: HybridBitSet<T>,
-    kill: HybridBitSet<T>,
-}
-
-impl<T: Idx> GenKillSet<T> {
-    /// Creates a new transfer function that will leave the dataflow state unchanged.
-    pub fn identity(universe: usize) -> Self {
-        GenKillSet {
-            gen: HybridBitSet::new_empty(universe),
-            kill: HybridBitSet::new_empty(universe),
-        }
-    }
-
-    /// Applies this transfer function to the given state vector.
-    pub fn apply(&self, state: &mut BitSet<T>) {
-        state.union(&self.gen);
-        state.subtract(&self.kill);
-    }
-}
-
-impl<T: Idx> GenKill<T> for GenKillSet<T> {
-    fn gen(&mut self, elem: T) {
-        self.gen.insert(elem);
-        self.kill.remove(elem);
-    }
-
-    fn kill(&mut self, elem: T) {
-        self.kill.insert(elem);
-        self.gen.remove(elem);
-    }
-}
-
-impl<T: Idx> GenKill<T> for BitSet<T> {
-    fn gen(&mut self, elem: T) {
-        self.insert(elem);
-    }
-
-    fn kill(&mut self, elem: T) {
-        self.remove(elem);
-    }
-}
-
-#[cfg(test)]
-mod tests;
diff --git a/src/librustc_mir/dataflow/generic/tests.rs b/src/librustc_mir/dataflow/generic/tests.rs
deleted file mode 100644 (file)
index 8f07a10..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-//! A test for the logic that updates the state in a `ResultsCursor` during seek.
-
-use rustc::mir::{self, BasicBlock, Location};
-use rustc::ty;
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
-use rustc_span::DUMMY_SP;
-
-use super::*;
-use crate::dataflow::BottomValue;
-
-/// Returns `true` if the given location points to a `Call` terminator that can return
-/// successfully.
-fn is_call_terminator_non_diverging(body: &mir::Body<'_>, loc: Location) -> bool {
-    loc == body.terminator_loc(loc.block)
-        && matches!(
-            body[loc.block].terminator().kind,
-            mir::TerminatorKind::Call { destination: Some(_), ..  }
-        )
-}
-
-/// Creates a `mir::Body` with a few disconnected basic blocks.
-///
-/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not
-/// important.
-fn mock_body() -> mir::Body<'static> {
-    let source_info = mir::SourceInfo { scope: mir::OUTERMOST_SOURCE_SCOPE, span: DUMMY_SP };
-
-    let mut blocks = IndexVec::new();
-    let mut block = |n, kind| {
-        let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop };
-
-        blocks.push(mir::BasicBlockData {
-            statements: std::iter::repeat(&nop).cloned().take(n).collect(),
-            terminator: Some(mir::Terminator { source_info, kind }),
-            is_cleanup: false,
-        })
-    };
-
-    let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() };
-
-    block(4, mir::TerminatorKind::Return);
-    block(1, mir::TerminatorKind::Return);
-    block(
-        2,
-        mir::TerminatorKind::Call {
-            func: mir::Operand::Copy(dummy_place.clone()),
-            args: vec![],
-            destination: Some((dummy_place.clone(), mir::START_BLOCK)),
-            cleanup: None,
-            from_hir_call: false,
-        },
-    );
-    block(3, mir::TerminatorKind::Return);
-    block(0, mir::TerminatorKind::Return);
-    block(
-        4,
-        mir::TerminatorKind::Call {
-            func: mir::Operand::Copy(dummy_place.clone()),
-            args: vec![],
-            destination: Some((dummy_place.clone(), mir::START_BLOCK)),
-            cleanup: None,
-            from_hir_call: false,
-        },
-    );
-
-    mir::Body::new_cfg_only(blocks)
-}
-
-/// A dataflow analysis whose state is unique at every possible `SeekTarget`.
-///
-/// Uniqueness is achieved by having a *locally* unique effect before and after each statement and
-/// terminator (see `effect_at_target`) while ensuring that the entry set for each block is
-/// *globally* unique (see `mock_entry_set`).
-///
-/// For example, a `BasicBlock` with ID `2` and a `Call` terminator has the following state at each
-/// location ("+x" indicates that "x" is added to the state).
-///
-/// | Location               | Before            | After  |
-/// |------------------------|-------------------|--------|
-/// | (on_entry)             | {102}                     ||
-/// | Statement 0            | +0                | +1     |
-/// | statement 1            | +2                | +3     |
-/// | `Call` terminator      | +4                | +5     |
-/// | (on unwind)            | {102,0,1,2,3,4,5}         ||
-/// | (on successful return) | +6                        ||
-///
-/// 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.
-struct MockAnalysis<'tcx> {
-    body: &'tcx mir::Body<'tcx>,
-}
-
-impl MockAnalysis<'tcx> {
-    const BASIC_BLOCK_OFFSET: usize = 100;
-
-    /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to
-    /// avoid colliding with the statement/terminator effects.
-    fn mock_entry_set(&self, bb: BasicBlock) -> BitSet<usize> {
-        let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
-        ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index());
-        ret
-    }
-
-    fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
-        let empty = BitSet::new_empty(self.bits_per_block(self.body));
-        let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks());
-
-        for (bb, _) in self.body.basic_blocks().iter_enumerated() {
-            ret[bb] = self.mock_entry_set(bb);
-        }
-
-        ret
-    }
-
-    /// Returns the index that should be added to the dataflow state at the given target.
-    ///
-    /// This index is only unique within a given basic block. `SeekAfter` and
-    /// `SeekAfterAssumeCallReturns` have the same effect unless `target` is a `Call` terminator.
-    fn effect_at_target(&self, target: SeekTarget) -> Option<usize> {
-        use SeekTarget::*;
-
-        let idx = match target {
-            BlockStart(_) => return None,
-
-            AfterAssumeCallReturns(loc) if is_call_terminator_non_diverging(self.body, loc) => {
-                loc.statement_index * 2 + 2
-            }
-
-            Before(loc) => loc.statement_index * 2,
-            After(loc) | AfterAssumeCallReturns(loc) => loc.statement_index * 2 + 1,
-        };
-
-        assert!(idx < Self::BASIC_BLOCK_OFFSET, "Too many statements in basic block");
-        Some(idx)
-    }
-
-    /// Returns the expected state at the given `SeekTarget`.
-    ///
-    /// This is the union of index of the target basic block, the index assigned to the
-    /// target statement or terminator, and the indices of all preceding statements in the target
-    /// basic block.
-    ///
-    /// For example, the expected state when calling
-    /// `seek_before(Location { block: 2, statement_index: 2 })` would be `[102, 0, 1, 2, 3, 4]`.
-    fn expected_state_at_target(&self, target: SeekTarget) -> BitSet<usize> {
-        let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
-        ret.insert(Self::BASIC_BLOCK_OFFSET + target.block().index());
-
-        if let Some(target_effect) = self.effect_at_target(target) {
-            for i in 0..=target_effect {
-                ret.insert(i);
-            }
-        }
-
-        ret
-    }
-}
-
-impl BottomValue for MockAnalysis<'tcx> {
-    const BOTTOM_VALUE: bool = false;
-}
-
-impl AnalysisDomain<'tcx> for MockAnalysis<'tcx> {
-    type Idx = usize;
-
-    const NAME: &'static str = "mock";
-
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len()
-    }
-
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
-        unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint");
-    }
-}
-
-impl Analysis<'tcx> for MockAnalysis<'tcx> {
-    fn apply_statement_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        _statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        let idx = self.effect_at_target(SeekTarget::After(location)).unwrap();
-        assert!(state.insert(idx));
-    }
-
-    fn apply_before_statement_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        _statement: &mir::Statement<'tcx>,
-        location: Location,
-    ) {
-        let idx = self.effect_at_target(SeekTarget::Before(location)).unwrap();
-        assert!(state.insert(idx));
-    }
-
-    fn apply_terminator_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        _terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    ) {
-        let idx = self.effect_at_target(SeekTarget::After(location)).unwrap();
-        assert!(state.insert(idx));
-    }
-
-    fn apply_before_terminator_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        _terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    ) {
-        let idx = self.effect_at_target(SeekTarget::Before(location)).unwrap();
-        assert!(state.insert(idx));
-    }
-
-    fn apply_call_return_effect(
-        &self,
-        state: &mut BitSet<Self::Idx>,
-        block: BasicBlock,
-        _func: &mir::Operand<'tcx>,
-        _args: &[mir::Operand<'tcx>],
-        _return_place: &mir::Place<'tcx>,
-    ) {
-        let location = self.body.terminator_loc(block);
-        let idx = self.effect_at_target(SeekTarget::AfterAssumeCallReturns(location)).unwrap();
-        assert!(state.insert(idx));
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-enum SeekTarget {
-    BlockStart(BasicBlock),
-    Before(Location),
-    After(Location),
-    AfterAssumeCallReturns(Location),
-}
-
-impl SeekTarget {
-    fn block(&self) -> BasicBlock {
-        use SeekTarget::*;
-
-        match *self {
-            BlockStart(block) => block,
-            Before(loc) | After(loc) | AfterAssumeCallReturns(loc) => loc.block,
-        }
-    }
-
-    /// An iterator over all possible `SeekTarget`s in a given block in order, starting with
-    /// `BlockStart`.
-    ///
-    /// This includes both `After` and `AfterAssumeCallReturns` for every `Location`.
-    fn iter_in_block(body: &mir::Body<'_>, block: BasicBlock) -> impl Iterator<Item = Self> {
-        let statements_and_terminator = (0..=body[block].statements.len())
-            .flat_map(|i| (0..3).map(move |j| (i, j)))
-            .map(move |(i, kind)| {
-                let loc = Location { block, statement_index: i };
-                match kind {
-                    0 => SeekTarget::Before(loc),
-                    1 => SeekTarget::After(loc),
-                    2 => SeekTarget::AfterAssumeCallReturns(loc),
-                    _ => unreachable!(),
-                }
-            });
-
-        std::iter::once(SeekTarget::BlockStart(block)).chain(statements_and_terminator)
-    }
-}
-
-#[test]
-fn cursor_seek() {
-    let body = mock_body();
-    let body = &body;
-    let analysis = MockAnalysis { body };
-
-    let mut cursor =
-        Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
-
-    // Sanity check: the mock call return effect is unique and actually being applied.
-    let call_terminator_loc = Location { block: BasicBlock::from_usize(2), statement_index: 2 };
-    assert!(is_call_terminator_non_diverging(body, call_terminator_loc));
-
-    let call_return_effect = cursor
-        .analysis()
-        .effect_at_target(SeekTarget::AfterAssumeCallReturns(call_terminator_loc))
-        .unwrap();
-    assert_ne!(
-        call_return_effect,
-        cursor.analysis().effect_at_target(SeekTarget::After(call_terminator_loc)).unwrap()
-    );
-
-    cursor.seek_after(call_terminator_loc);
-    assert!(!cursor.get().contains(call_return_effect));
-    cursor.seek_after_assume_success(call_terminator_loc);
-    assert!(cursor.get().contains(call_return_effect));
-
-    let every_target = || {
-        body.basic_blocks()
-            .iter_enumerated()
-            .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb))
-    };
-
-    let mut seek_to_target = |targ| {
-        use SeekTarget::*;
-
-        match targ {
-            BlockStart(block) => cursor.seek_to_block_start(block),
-            Before(loc) => cursor.seek_before(loc),
-            After(loc) => cursor.seek_after(loc),
-            AfterAssumeCallReturns(loc) => cursor.seek_after_assume_success(loc),
-        }
-
-        assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ));
-    };
-
-    // Seek *to* every possible `SeekTarget` *from* every possible `SeekTarget`.
-    //
-    // By resetting the cursor to `from` each time it changes, we end up checking some edges twice.
-    // What we really want is an Eulerian cycle for the complete digraph over all possible
-    // `SeekTarget`s, but it's not worth spending the time to compute it.
-    for from in every_target() {
-        seek_to_target(from);
-
-        for to in every_target() {
-            seek_to_target(to);
-            seek_to_target(from);
-        }
-    }
-}
diff --git a/src/librustc_mir/dataflow/generic/visitor.rs b/src/librustc_mir/dataflow/generic/visitor.rs
deleted file mode 100644 (file)
index 6e1513b..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-use rustc::mir::{self, BasicBlock, Location};
-use rustc_index::bit_set::BitSet;
-
-use super::{Analysis, Results};
-use crate::dataflow::impls::{borrows::Borrows, EverInitializedPlaces, MaybeUninitializedPlaces};
-
-/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
-/// dataflow state at that location.
-pub fn visit_results<F>(
-    body: &'mir mir::Body<'tcx>,
-    blocks: impl IntoIterator<Item = BasicBlock>,
-    results: &impl ResultsVisitable<'tcx, FlowState = F>,
-    vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
-) {
-    let mut state = results.new_flow_state(body);
-
-    for block in blocks {
-        let block_data = &body[block];
-        results.reset_to_block_start(&mut state, block);
-
-        for (statement_index, stmt) in block_data.statements.iter().enumerate() {
-            let loc = Location { block, statement_index };
-
-            results.reconstruct_before_statement_effect(&mut state, stmt, loc);
-            vis.visit_statement(&state, stmt, loc);
-
-            results.reconstruct_statement_effect(&mut state, stmt, loc);
-            vis.visit_statement_exit(&state, stmt, loc);
-        }
-
-        let loc = body.terminator_loc(block);
-        let term = block_data.terminator();
-
-        results.reconstruct_before_terminator_effect(&mut state, term, loc);
-        vis.visit_terminator(&state, term, loc);
-
-        results.reconstruct_terminator_effect(&mut state, term, loc);
-        vis.visit_terminator_exit(&state, term, loc);
-    }
-}
-
-pub trait ResultsVisitor<'mir, 'tcx> {
-    type FlowState;
-
-    /// Called with the `before_statement_effect` of the given statement applied to `state` but not
-    /// its `statement_effect`.
-    fn visit_statement(
-        &mut self,
-        _state: &Self::FlowState,
-        _statement: &'mir mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// Called with both the `before_statement_effect` and the `statement_effect` of the given
-    /// statement applied to `state`.
-    fn visit_statement_exit(
-        &mut self,
-        _state: &Self::FlowState,
-        _statement: &'mir mir::Statement<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// Called with the `before_terminator_effect` of the given terminator applied to `state` but not
-    /// its `terminator_effect`.
-    fn visit_terminator(
-        &mut self,
-        _state: &Self::FlowState,
-        _terminator: &'mir mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-    }
-
-    /// Called with both the `before_terminator_effect` and the `terminator_effect` of the given
-    /// terminator applied to `state`.
-    ///
-    /// The `call_return_effect` (if one exists) will *not* be applied to `state`.
-    fn visit_terminator_exit(
-        &mut self,
-        _state: &Self::FlowState,
-        _terminator: &'mir mir::Terminator<'tcx>,
-        _location: Location,
-    ) {
-    }
-}
-
-/// Things that can be visited by a `ResultsVisitor`.
-///
-/// This trait exists so that we can visit the results of multiple dataflow analyses simultaneously.
-/// DO NOT IMPLEMENT MANUALLY. Instead, use the `impl_visitable` macro below.
-pub trait ResultsVisitable<'tcx> {
-    type FlowState;
-
-    /// Creates an empty `FlowState` to hold the transient state for these dataflow results.
-    ///
-    /// The value of the newly created `FlowState` will be overwritten by `reset_to_block_start`
-    /// before it can be observed by a `ResultsVisitor`.
-    fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState;
-
-    fn reset_to_block_start(&self, state: &mut Self::FlowState, block: BasicBlock);
-
-    fn reconstruct_before_statement_effect(
-        &self,
-        state: &mut Self::FlowState,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    );
-
-    fn reconstruct_statement_effect(
-        &self,
-        state: &mut Self::FlowState,
-        statement: &mir::Statement<'tcx>,
-        location: Location,
-    );
-
-    fn reconstruct_before_terminator_effect(
-        &self,
-        state: &mut Self::FlowState,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    );
-
-    fn reconstruct_terminator_effect(
-        &self,
-        state: &mut Self::FlowState,
-        terminator: &mir::Terminator<'tcx>,
-        location: Location,
-    );
-}
-
-impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    type FlowState = BitSet<A::Idx>;
-
-    fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
-        BitSet::new_empty(self.analysis.bits_per_block(body))
-    }
-
-    fn reset_to_block_start(&self, state: &mut Self::FlowState, block: BasicBlock) {
-        state.overwrite(&self.entry_set_for_block(block));
-    }
-
-    fn reconstruct_before_statement_effect(
-        &self,
-        state: &mut Self::FlowState,
-        stmt: &mir::Statement<'tcx>,
-        loc: Location,
-    ) {
-        self.analysis.apply_before_statement_effect(state, stmt, loc);
-    }
-
-    fn reconstruct_statement_effect(
-        &self,
-        state: &mut Self::FlowState,
-        stmt: &mir::Statement<'tcx>,
-        loc: Location,
-    ) {
-        self.analysis.apply_statement_effect(state, stmt, loc);
-    }
-
-    fn reconstruct_before_terminator_effect(
-        &self,
-        state: &mut Self::FlowState,
-        term: &mir::Terminator<'tcx>,
-        loc: Location,
-    ) {
-        self.analysis.apply_before_terminator_effect(state, term, loc);
-    }
-
-    fn reconstruct_terminator_effect(
-        &self,
-        state: &mut Self::FlowState,
-        term: &mir::Terminator<'tcx>,
-        loc: Location,
-    ) {
-        self.analysis.apply_terminator_effect(state, term, loc);
-    }
-}
-
-/// A tuple with named fields that can hold either the results or the transient state of the
-/// dataflow analyses used by the borrow checker.
-#[derive(Debug)]
-pub struct BorrowckAnalyses<B, U, E> {
-    pub borrows: B,
-    pub uninits: U,
-    pub ever_inits: E,
-}
-
-/// The results of the dataflow analyses used by the borrow checker.
-pub type BorrowckResults<'mir, 'tcx> = BorrowckAnalyses<
-    Results<'tcx, Borrows<'mir, 'tcx>>,
-    Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>,
-    Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>,
->;
-
-/// The transient state of the dataflow analyses used by the borrow checker.
-pub type BorrowckFlowState<'mir, 'tcx> =
-    <BorrowckResults<'mir, 'tcx> as ResultsVisitable<'tcx>>::FlowState;
-
-macro_rules! impl_visitable {
-    ( $(
-        $T:ident { $( $field:ident : $A:ident ),* $(,)? }
-    )* ) => { $(
-        impl<'tcx, $($A),*> ResultsVisitable<'tcx> for $T<$( Results<'tcx, $A> ),*>
-        where
-            $( $A: Analysis<'tcx>, )*
-        {
-            type FlowState = $T<$( BitSet<$A::Idx> ),*>;
-
-            fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
-                $T {
-                    $( $field: BitSet::new_empty(self.$field.analysis.bits_per_block(body)) ),*
-                }
-            }
-
-            fn reset_to_block_start(
-                &self,
-                state: &mut Self::FlowState,
-                block: BasicBlock,
-            ) {
-                $( state.$field.overwrite(&self.$field.entry_sets[block]); )*
-            }
-
-            fn reconstruct_before_statement_effect(
-                &self,
-                state: &mut Self::FlowState,
-                stmt: &mir::Statement<'tcx>,
-                loc: Location,
-            ) {
-                $( self.$field.analysis
-                    .apply_before_statement_effect(&mut state.$field, stmt, loc); )*
-            }
-
-            fn reconstruct_statement_effect(
-                &self,
-                state: &mut Self::FlowState,
-                stmt: &mir::Statement<'tcx>,
-                loc: Location,
-            ) {
-                $( self.$field.analysis
-                    .apply_statement_effect(&mut state.$field, stmt, loc); )*
-            }
-
-            fn reconstruct_before_terminator_effect(
-                &self,
-                state: &mut Self::FlowState,
-                term: &mir::Terminator<'tcx>,
-                loc: Location,
-            ) {
-                $( self.$field.analysis
-                    .apply_before_terminator_effect(&mut state.$field, term, loc); )*
-            }
-
-            fn reconstruct_terminator_effect(
-                &self,
-                state: &mut Self::FlowState,
-                term: &mir::Terminator<'tcx>,
-                loc: Location,
-            ) {
-                $( self.$field.analysis
-                    .apply_terminator_effect(&mut state.$field, term, loc); )*
-            }
-        }
-    )* }
-}
-
-impl_visitable! {
-    BorrowckAnalyses { borrows: B, uninits: U, ever_inits: E }
-}
index 95a676c0892c5472983d030c628452ec600980ac..955021d83aa098a69ba4e50cbde0f58219546286 100644 (file)
@@ -1,6 +1,6 @@
 pub use super::*;
 
-use crate::dataflow::generic::{AnalysisDomain, GenKill, GenKillAnalysis};
+use crate::dataflow::{AnalysisDomain, GenKill, GenKillAnalysis};
 use rustc::mir::visit::Visitor;
 use rustc::mir::*;
 use rustc::ty::{ParamEnv, TyCtxt};
index a7c0efd63b1a80330edaaeb563d7d0b5c6e99e0c..9e9e414ad648bf4445c815de5ef5c2f6177b7fd9 100644 (file)
@@ -8,8 +8,8 @@
 use crate::borrow_check::{
     places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid,
 };
-use crate::dataflow::generic::{self, GenKill};
 use crate::dataflow::BottomValue;
+use crate::dataflow::{self, GenKill};
 
 use std::rc::Rc;
 
@@ -226,7 +226,7 @@ fn kill_borrows_on_place(&self, trans: &mut impl GenKill<BorrowIndex>, place: &P
     }
 }
 
-impl<'tcx> generic::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
+impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
     type Idx = BorrowIndex;
 
     const NAME: &'static str = "borrows";
@@ -245,7 +245,7 @@ fn pretty_print_idx(&self, w: &mut impl std::io::Write, idx: Self::Idx) -> std::
     }
 }
 
-impl<'tcx> generic::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
+impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
     fn before_statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
index 87d8e9e411c6fba6f494d362441d8149be914955..180094e412b15ce665d905c802aea283a3b7b00f 100644 (file)
@@ -12,9 +12,8 @@
 
 use crate::util::elaborate_drops::DropFlagState;
 
-use super::generic::{AnalysisDomain, GenKill, GenKillAnalysis};
 use super::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
-use super::BottomValue;
+use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis};
 
 use super::drop_flag_effects_for_function_entry;
 use super::drop_flag_effects_for_location;
index 5341d661b1db62379e1fc424fdbbe36fedbb3254..f8d1efabd33c055197790e198f4f9532ed309ed2 100644 (file)
@@ -1,7 +1,7 @@
 pub use super::*;
 
-use crate::dataflow::generic::{self as dataflow, GenKill, Results, ResultsRefCursor};
 use crate::dataflow::BottomValue;
+use crate::dataflow::{self, GenKill, Results, ResultsRefCursor};
 use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
 use rustc::mir::*;
 use std::cell::RefCell;
index c98a5e84729ab949d8b188b77d3ff9317ad3a928..96e5b6936a191e7523f6d5a609196309fd5736cc 100644 (file)
@@ -1,35 +1,22 @@
-use rustc::mir::traversal;
-use rustc::mir::{self, BasicBlock, BasicBlockData, Body, Location, Statement, Terminator};
-use rustc::ty::{self, TyCtxt};
+use rustc::ty;
 use rustc_ast::ast::{self, MetaItem};
-use rustc_ast_pretty::pprust;
-use rustc_data_structures::work_queue::WorkQueue;
-use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::{BitSet, HybridBitSet};
-use rustc_index::vec::Idx;
-use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
 
-use std::borrow::Borrow;
-use std::fmt;
-use std::io;
-use std::path::PathBuf;
-
-pub use self::at_location::{FlowAtLocation, FlowsAtLocation};
 pub(crate) use self::drop_flag_effects::*;
-pub use self::impls::borrows::Borrows;
-pub use self::impls::DefinitelyInitializedPlaces;
-pub use self::impls::EverInitializedPlaces;
-pub use self::impls::{MaybeBorrowedLocals, MaybeMutBorrowedLocals};
-pub use self::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
-pub use self::impls::{MaybeRequiresStorage, MaybeStorageLive};
+pub use self::framework::{
+    visit_results, Analysis, AnalysisDomain, BorrowckFlowState, BorrowckResults, BottomValue,
+    Engine, GenKill, GenKillAnalysis, Results, ResultsCursor, ResultsRefCursor, ResultsVisitor,
+};
+pub use self::impls::{
+    borrows::Borrows, DefinitelyInitializedPlaces, EverInitializedPlaces, MaybeBorrowedLocals,
+    MaybeInitializedPlaces, MaybeMutBorrowedLocals, MaybeRequiresStorage, MaybeStorageLive,
+    MaybeUninitializedPlaces,
+};
 
 use self::move_paths::MoveData;
 
-mod at_location;
 pub mod drop_flag_effects;
-pub mod generic;
-mod graphviz;
+mod framework;
 mod impls;
 pub mod move_paths;
 
@@ -40,74 +27,9 @@ pub(crate) mod indexes {
     };
 }
 
-pub(crate) struct DataflowBuilder<'a, 'tcx, BD>
-where
-    BD: BitDenotation<'tcx>,
-{
-    def_id: DefId,
-    flow_state: DataflowAnalysis<'a, 'tcx, BD>,
-    print_preflow_to: Option<String>,
-    print_postflow_to: Option<String>,
-}
-
-/// `DebugFormatted` encapsulates the "{:?}" rendering of some
-/// arbitrary value. This way: you pay cost of allocating an extra
-/// string (as well as that of rendering up-front); in exchange, you
-/// don't have to hand over ownership of your value or deal with
-/// borrowing it.
-pub struct DebugFormatted(String);
-
-impl DebugFormatted {
-    pub fn new(input: &dyn fmt::Debug) -> DebugFormatted {
-        DebugFormatted(format!("{:?}", input))
-    }
-}
-
-impl fmt::Debug for DebugFormatted {
-    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(w, "{}", self.0)
-    }
-}
-
-pub trait Dataflow<'tcx, BD: BitDenotation<'tcx>> {
-    /// Sets up and runs the dataflow problem, using `p` to render results if
-    /// implementation so chooses.
-    fn dataflow<P>(&mut self, p: P)
-    where
-        P: Fn(&BD, BD::Idx) -> DebugFormatted,
-    {
-        let _ = p; // default implementation does not instrument process.
-        self.build_sets();
-        self.propagate();
-    }
-
-    /// Sets up the entry, gen, and kill sets for this instance of a dataflow problem.
-    fn build_sets(&mut self);
-
-    /// Finds a fixed-point solution to this instance of a dataflow problem.
-    fn propagate(&mut self);
-}
-
-impl<'a, 'tcx, BD> Dataflow<'tcx, BD> for DataflowBuilder<'a, 'tcx, BD>
-where
-    BD: BitDenotation<'tcx>,
-{
-    fn dataflow<P>(&mut self, p: P)
-    where
-        P: Fn(&BD, BD::Idx) -> DebugFormatted,
-    {
-        self.flow_state.build_sets();
-        self.pre_dataflow_instrumentation(|c, i| p(c, i)).unwrap();
-        self.flow_state.propagate();
-        self.post_dataflow_instrumentation(|c, i| p(c, i)).unwrap();
-    }
-
-    fn build_sets(&mut self) {
-        self.flow_state.build_sets();
-    }
-    fn propagate(&mut self) {
-        self.flow_state.propagate();
-    }
+pub struct MoveDataParamEnv<'tcx> {
+    pub(crate) move_data: MoveData<'tcx>,
+    pub(crate) param_env: ty::ParamEnv<'tcx>,
 }
 
 pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Option<MetaItem> {
@@ -124,811 +46,3 @@ pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Opti
     }
     None
 }
-
-pub struct MoveDataParamEnv<'tcx> {
-    pub(crate) move_data: MoveData<'tcx>,
-    pub(crate) param_env: ty::ParamEnv<'tcx>,
-}
-
-pub fn do_dataflow<'a, 'tcx, BD, P>(
-    tcx: TyCtxt<'tcx>,
-    body: &'a Body<'tcx>,
-    def_id: DefId,
-    attributes: &[ast::Attribute],
-    dead_unwinds: &BitSet<BasicBlock>,
-    bd: BD,
-    p: P,
-) -> DataflowResults<'tcx, BD>
-where
-    BD: BitDenotation<'tcx>,
-    P: Fn(&BD, BD::Idx) -> DebugFormatted,
-{
-    let flow_state = DataflowAnalysis::new(body, dead_unwinds, bd);
-    flow_state.run(tcx, def_id, attributes, p)
-}
-
-impl<'a, 'tcx, BD> DataflowAnalysis<'a, 'tcx, BD>
-where
-    BD: BitDenotation<'tcx>,
-{
-    pub(crate) fn run<P>(
-        self,
-        tcx: TyCtxt<'tcx>,
-        def_id: DefId,
-        attributes: &[ast::Attribute],
-        p: P,
-    ) -> DataflowResults<'tcx, BD>
-    where
-        P: Fn(&BD, BD::Idx) -> DebugFormatted,
-    {
-        let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
-            if let Some(item) = has_rustc_mir_with(attrs, name) {
-                if let Some(s) = item.value_str() {
-                    return Some(s.to_string());
-                } else {
-                    let path = pprust::path_to_string(&item.path);
-                    sess.span_err(item.span, &format!("{} attribute requires a path", path));
-                    return None;
-                }
-            }
-            None
-        };
-
-        let print_preflow_to = name_found(tcx.sess, attributes, sym::borrowck_graphviz_preflow);
-        let print_postflow_to = name_found(tcx.sess, attributes, sym::borrowck_graphviz_postflow);
-
-        let mut mbcx =
-            DataflowBuilder { def_id, print_preflow_to, print_postflow_to, flow_state: self };
-
-        mbcx.dataflow(p);
-        mbcx.flow_state.results()
-    }
-}
-
-struct PropagationContext<'b, 'a, 'tcx, O>
-where
-    O: BitDenotation<'tcx>,
-{
-    builder: &'b mut DataflowAnalysis<'a, 'tcx, O>,
-}
-
-impl<'a, 'tcx, BD> DataflowAnalysis<'a, 'tcx, BD>
-where
-    BD: BitDenotation<'tcx>,
-{
-    fn propagate(&mut self) {
-        let mut temp = BitSet::new_empty(self.flow_state.sets.bits_per_block);
-        let mut propcx = PropagationContext { builder: self };
-        propcx.walk_cfg(&mut temp);
-    }
-
-    fn build_sets(&mut self) {
-        // Build the transfer function for each block.
-        for (bb, data) in self.body.basic_blocks().iter_enumerated() {
-            let &mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = data;
-
-            let trans = self.flow_state.sets.trans_mut_for(bb.index());
-            for j_stmt in 0..statements.len() {
-                let location = Location { block: bb, statement_index: j_stmt };
-                self.flow_state.operator.before_statement_effect(trans, location);
-                self.flow_state.operator.statement_effect(trans, location);
-            }
-
-            if terminator.is_some() {
-                let location = Location { block: bb, statement_index: statements.len() };
-                self.flow_state.operator.before_terminator_effect(trans, location);
-                self.flow_state.operator.terminator_effect(trans, location);
-            }
-        }
-
-        // Initialize the flow state at entry to the start block.
-        let on_entry = self.flow_state.sets.entry_set_mut_for(mir::START_BLOCK.index());
-        self.flow_state.operator.start_block_effect(on_entry);
-    }
-}
-
-impl<'b, 'a, 'tcx, BD> PropagationContext<'b, 'a, 'tcx, BD>
-where
-    BD: BitDenotation<'tcx>,
-{
-    fn walk_cfg(&mut self, in_out: &mut BitSet<BD::Idx>) {
-        let body = self.builder.body;
-
-        // Initialize the dirty queue in reverse post-order. This makes it more likely that the
-        // entry state for each basic block will have the effects of its predecessors applied
-        // before it is processed. In fact, for CFGs without back edges, this guarantees that
-        // dataflow will converge in exactly `N` iterations, where `N` is the number of basic
-        // blocks.
-        let mut dirty_queue: WorkQueue<mir::BasicBlock> =
-            WorkQueue::with_none(body.basic_blocks().len());
-        for (bb, _) in traversal::reverse_postorder(body) {
-            dirty_queue.insert(bb);
-        }
-
-        // Add blocks which are not reachable from START_BLOCK to the work queue. These blocks will
-        // be processed after the ones added above.
-        for bb in body.basic_blocks().indices() {
-            dirty_queue.insert(bb);
-        }
-
-        while let Some(bb) = dirty_queue.pop() {
-            let (on_entry, trans) = self.builder.flow_state.sets.get_mut(bb.index());
-            debug_assert!(in_out.words().len() == on_entry.words().len());
-            in_out.overwrite(on_entry);
-            trans.apply(in_out);
-
-            let bb_data = &body[bb];
-            self.builder.propagate_bits_into_graph_successors_of(
-                in_out,
-                (bb, bb_data),
-                &mut dirty_queue,
-            );
-        }
-    }
-}
-
-fn dataflow_path(context: &str, path: &str) -> PathBuf {
-    let mut path = PathBuf::from(path);
-    let new_file_name = {
-        let orig_file_name = path.file_name().unwrap().to_str().unwrap();
-        format!("{}_{}", context, orig_file_name)
-    };
-    path.set_file_name(new_file_name);
-    path
-}
-
-impl<'a, 'tcx, BD> DataflowBuilder<'a, 'tcx, BD>
-where
-    BD: BitDenotation<'tcx>,
-{
-    fn pre_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
-    where
-        P: Fn(&BD, BD::Idx) -> DebugFormatted,
-    {
-        if let Some(ref path_str) = self.print_preflow_to {
-            let path = dataflow_path(BD::name(), path_str);
-            graphviz::print_borrowck_graph_to(self, &path, p)
-        } else {
-            Ok(())
-        }
-    }
-
-    fn post_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
-    where
-        P: Fn(&BD, BD::Idx) -> DebugFormatted,
-    {
-        if let Some(ref path_str) = self.print_postflow_to {
-            let path = dataflow_path(BD::name(), path_str);
-            graphviz::print_borrowck_graph_to(self, &path, p)
-        } else {
-            Ok(())
-        }
-    }
-}
-
-/// DataflowResultsConsumer abstracts over walking the MIR with some
-/// already constructed dataflow results.
-///
-/// It abstracts over the FlowState and also completely hides the
-/// underlying flow analysis results, because it needs to handle cases
-/// where we are combining the results of *multiple* flow analyses
-/// (e.g., borrows + inits + uninits).
-pub(crate) trait DataflowResultsConsumer<'a, 'tcx: 'a> {
-    type FlowState: FlowsAtLocation;
-
-    // Observation Hooks: override (at least one of) these to get analysis feedback.
-    fn visit_block_entry(&mut self, _bb: BasicBlock, _flow_state: &Self::FlowState) {}
-
-    fn visit_statement_entry(
-        &mut self,
-        _loc: Location,
-        _stmt: &'a Statement<'tcx>,
-        _flow_state: &Self::FlowState,
-    ) {
-    }
-
-    fn visit_terminator_entry(
-        &mut self,
-        _loc: Location,
-        _term: &'a Terminator<'tcx>,
-        _flow_state: &Self::FlowState,
-    ) {
-    }
-
-    // Main entry point: this drives the processing of results.
-
-    fn analyze_results(&mut self, flow_uninit: &mut Self::FlowState) {
-        let flow = flow_uninit;
-        for (bb, _) in traversal::reverse_postorder(self.body()) {
-            flow.reset_to_entry_of(bb);
-            self.process_basic_block(bb, flow);
-        }
-    }
-
-    fn process_basic_block(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
-        self.visit_block_entry(bb, flow_state);
-
-        let BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = self.body()[bb];
-        let mut location = Location { block: bb, statement_index: 0 };
-        for stmt in statements.iter() {
-            flow_state.reconstruct_statement_effect(location);
-            self.visit_statement_entry(location, stmt, flow_state);
-            flow_state.apply_local_effect(location);
-            location.statement_index += 1;
-        }
-
-        if let Some(ref term) = *terminator {
-            flow_state.reconstruct_terminator_effect(location);
-            self.visit_terminator_entry(location, term, flow_state);
-
-            // We don't need to apply the effect of the terminator,
-            // since we are only visiting dataflow state on control
-            // flow entry to the various nodes. (But we still need to
-            // reconstruct the effect, because the visit method might
-            // inspect it.)
-        }
-    }
-
-    // Delegated Hooks: Provide access to the MIR and process the flow state.
-
-    fn body(&self) -> &'a Body<'tcx>;
-}
-
-/// Allows iterating dataflow results in a flexible and reasonably fast way.
-pub struct DataflowResultsCursor<'mir, 'tcx, BD, DR = DataflowResults<'tcx, BD>>
-where
-    BD: BitDenotation<'tcx>,
-    DR: Borrow<DataflowResults<'tcx, BD>>,
-{
-    flow_state: FlowAtLocation<'tcx, BD, DR>,
-
-    // The statement (or terminator) whose effect has been reconstructed in
-    // flow_state.
-    curr_loc: Option<Location>,
-
-    body: &'mir Body<'tcx>,
-}
-
-pub type DataflowResultsRefCursor<'mir, 'tcx, BD> =
-    DataflowResultsCursor<'mir, 'tcx, BD, &'mir DataflowResults<'tcx, BD>>;
-
-impl<'mir, 'tcx, BD, DR> DataflowResultsCursor<'mir, 'tcx, BD, DR>
-where
-    BD: BitDenotation<'tcx>,
-    DR: Borrow<DataflowResults<'tcx, BD>>,
-{
-    pub fn new(result: DR, body: &'mir Body<'tcx>) -> Self {
-        DataflowResultsCursor { flow_state: FlowAtLocation::new(result), curr_loc: None, body }
-    }
-
-    /// Seek to the given location in MIR. This method is fast if you are
-    /// traversing your MIR statements in order.
-    ///
-    /// After calling `seek`, the current state will reflect all effects up to
-    /// and including the `before_statement_effect` of the statement at location
-    /// `loc`. The `statement_effect` of the statement at `loc` will be
-    /// available as the current effect (see e.g. `each_gen_bit`).
-    ///
-    /// If `loc.statement_index` equals the number of statements in the block,
-    /// we will reconstruct the terminator effect in the same way as described
-    /// above.
-    pub fn seek(&mut self, loc: Location) {
-        if self.curr_loc.map(|cur| loc == cur).unwrap_or(false) {
-            return;
-        }
-
-        let start_index;
-        let should_reset = match self.curr_loc {
-            None => true,
-            Some(cur) if loc.block != cur.block || loc.statement_index < cur.statement_index => {
-                true
-            }
-            _ => false,
-        };
-        if should_reset {
-            self.flow_state.reset_to_entry_of(loc.block);
-            start_index = 0;
-        } else {
-            let curr_loc = self.curr_loc.unwrap();
-            start_index = curr_loc.statement_index;
-            // Apply the effect from the last seek to the current state.
-            self.flow_state.apply_local_effect(curr_loc);
-        }
-
-        for stmt in start_index..loc.statement_index {
-            let mut stmt_loc = loc;
-            stmt_loc.statement_index = stmt;
-            self.flow_state.reconstruct_statement_effect(stmt_loc);
-            self.flow_state.apply_local_effect(stmt_loc);
-        }
-
-        if loc.statement_index == self.body[loc.block].statements.len() {
-            self.flow_state.reconstruct_terminator_effect(loc);
-        } else {
-            self.flow_state.reconstruct_statement_effect(loc);
-        }
-        self.curr_loc = Some(loc);
-    }
-
-    /// Return whether the current state contains bit `x`.
-    pub fn contains(&self, x: BD::Idx) -> bool {
-        self.flow_state.contains(x)
-    }
-
-    /// Iterate over each `gen` bit in the current effect (invoke `seek` first).
-    pub fn each_gen_bit<F>(&self, f: F)
-    where
-        F: FnMut(BD::Idx),
-    {
-        self.flow_state.each_gen_bit(f)
-    }
-
-    pub fn get(&self) -> &BitSet<BD::Idx> {
-        self.flow_state.as_dense()
-    }
-}
-
-pub struct DataflowAnalysis<'a, 'tcx, O>
-where
-    O: BitDenotation<'tcx>,
-{
-    flow_state: DataflowState<'tcx, O>,
-    dead_unwinds: &'a BitSet<mir::BasicBlock>,
-    body: &'a Body<'tcx>,
-}
-
-impl<'a, 'tcx, O> DataflowAnalysis<'a, 'tcx, O>
-where
-    O: BitDenotation<'tcx>,
-{
-    pub fn results(self) -> DataflowResults<'tcx, O> {
-        DataflowResults(self.flow_state)
-    }
-
-    pub fn body(&self) -> &'a Body<'tcx> {
-        self.body
-    }
-}
-
-pub struct DataflowResults<'tcx, O>(pub(crate) DataflowState<'tcx, O>)
-where
-    O: BitDenotation<'tcx>;
-
-impl<'tcx, O: BitDenotation<'tcx>> DataflowResults<'tcx, O> {
-    pub fn sets(&self) -> &AllSets<O::Idx> {
-        &self.0.sets
-    }
-
-    pub fn operator(&self) -> &O {
-        &self.0.operator
-    }
-}
-
-/// State of a dataflow analysis; couples a collection of bit sets
-/// with operator used to initialize and merge bits during analysis.
-pub struct DataflowState<'tcx, O: BitDenotation<'tcx>> {
-    /// All the sets for the analysis. (Factored into its
-    /// own structure so that we can borrow it mutably
-    /// on its own separate from other fields.)
-    pub sets: AllSets<O::Idx>,
-
-    /// operator used to initialize, combine, and interpret bits.
-    pub(crate) operator: O,
-}
-
-impl<'tcx, O: BitDenotation<'tcx>> DataflowState<'tcx, O> {
-    pub(crate) fn interpret_set<'c, P>(
-        &self,
-        o: &'c O,
-        set: &BitSet<O::Idx>,
-        render_idx: &P,
-    ) -> Vec<DebugFormatted>
-    where
-        P: Fn(&O, O::Idx) -> DebugFormatted,
-    {
-        set.iter().map(|i| render_idx(o, i)).collect()
-    }
-
-    pub(crate) fn interpret_hybrid_set<'c, P>(
-        &self,
-        o: &'c O,
-        set: &HybridBitSet<O::Idx>,
-        render_idx: &P,
-    ) -> Vec<DebugFormatted>
-    where
-        P: Fn(&O, O::Idx) -> DebugFormatted,
-    {
-        set.iter().map(|i| render_idx(o, i)).collect()
-    }
-}
-
-/// A 2-tuple representing the "gen" and "kill" bitsets during
-/// dataflow analysis.
-///
-/// It is best to ensure that the intersection of `gen_set` and
-/// `kill_set` is empty; otherwise the results of the dataflow will
-/// have a hidden dependency on what order the bits are generated and
-/// killed during the iteration. (This is such a good idea that the
-/// `fn gen` and `fn kill` methods that set their state enforce this
-/// for you.)
-#[derive(Debug, Clone, Copy)]
-pub struct GenKill<T> {
-    pub(crate) gen_set: T,
-    pub(crate) kill_set: T,
-}
-
-pub type GenKillSet<T> = GenKill<HybridBitSet<T>>;
-
-impl<T> GenKill<T> {
-    /// Creates a new tuple where `gen_set == kill_set == elem`.
-    pub(crate) fn from_elem(elem: T) -> Self
-    where
-        T: Clone,
-    {
-        GenKill { gen_set: elem.clone(), kill_set: elem }
-    }
-}
-
-impl<E: Idx> GenKillSet<E> {
-    pub fn clear(&mut self) {
-        self.gen_set.clear();
-        self.kill_set.clear();
-    }
-
-    pub fn gen(&mut self, e: E) {
-        self.gen_set.insert(e);
-        self.kill_set.remove(e);
-    }
-
-    pub fn gen_all(&mut self, i: impl IntoIterator<Item: Borrow<E>>) {
-        for j in i {
-            self.gen(*j.borrow());
-        }
-    }
-
-    pub fn kill(&mut self, e: E) {
-        self.gen_set.remove(e);
-        self.kill_set.insert(e);
-    }
-
-    pub fn kill_all(&mut self, i: impl IntoIterator<Item: Borrow<E>>) {
-        for j in i {
-            self.kill(*j.borrow());
-        }
-    }
-
-    /// Computes `(set ∪ gen) - kill` and assigns the result to `set`.
-    pub(crate) fn apply(&self, set: &mut BitSet<E>) {
-        set.union(&self.gen_set);
-        set.subtract(&self.kill_set);
-    }
-}
-
-#[derive(Debug)]
-pub struct AllSets<E: Idx> {
-    /// Analysis bitwidth for each block.
-    bits_per_block: usize,
-
-    /// For each block, bits valid on entry to the block.
-    on_entry: Vec<BitSet<E>>,
-
-    /// The transfer function of each block expressed as the set of bits
-    /// generated and killed by executing the statements + terminator in the
-    /// block -- with one caveat. In particular, for *call terminators*, the
-    /// effect of storing the destination is not included, since that only takes
-    /// effect on the **success** edge (and not the unwind edge).
-    trans: Vec<GenKillSet<E>>,
-}
-
-impl<E: Idx> AllSets<E> {
-    pub fn bits_per_block(&self) -> usize {
-        self.bits_per_block
-    }
-
-    pub fn get_mut(&mut self, block_idx: usize) -> (&mut BitSet<E>, &mut GenKillSet<E>) {
-        (&mut self.on_entry[block_idx], &mut self.trans[block_idx])
-    }
-
-    pub fn trans_for(&self, block_idx: usize) -> &GenKillSet<E> {
-        &self.trans[block_idx]
-    }
-    pub fn trans_mut_for(&mut self, block_idx: usize) -> &mut GenKillSet<E> {
-        &mut self.trans[block_idx]
-    }
-    pub fn entry_set_for(&self, block_idx: usize) -> &BitSet<E> {
-        &self.on_entry[block_idx]
-    }
-    pub fn entry_set_mut_for(&mut self, block_idx: usize) -> &mut BitSet<E> {
-        &mut self.on_entry[block_idx]
-    }
-    pub fn gen_set_for(&self, block_idx: usize) -> &HybridBitSet<E> {
-        &self.trans_for(block_idx).gen_set
-    }
-    pub fn kill_set_for(&self, block_idx: usize) -> &HybridBitSet<E> {
-        &self.trans_for(block_idx).kill_set
-    }
-}
-
-/// Parameterization for the precise form of data flow that is used.
-///
-/// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
-/// This also determines the semantics of the lattice `join` operator used to merge dataflow
-/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed
-/// point.
-///
-/// This means, for propagation across the graph, that you either want to start at all-zeroes and
-/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect
-/// as your merge when propagating.
-pub trait BottomValue {
-    /// Specifies the initial value for each bit in the entry set for each basic block.
-    const BOTTOM_VALUE: bool;
-
-    /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
-    ///
-    /// It is almost certainly wrong to override this, since it automatically applies
-    /// * `inout_set & in_set` if `BOTTOM_VALUE == true`
-    /// * `inout_set | in_set` if `BOTTOM_VALUE == false`
-    ///
-    /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks.
-    /// For clarity, the above statement again from a different perspective:
-    /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is
-    /// `!BOTTOM_VALUE`.
-    ///
-    /// There are situations where you want the opposite behaviour: propagate only if *all*
-    /// predecessor blocks's value is `!BOTTOM_VALUE`.
-    /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This
-    /// means that all code paths leading to the location must have set the bit, instead of any
-    /// code path leading there.
-    ///
-    /// If you want this kind of "definitely set" analysis, you need to
-    /// 1. Invert `BOTTOM_VALUE`
-    /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE`
-    /// 3. Override `join` to do the opposite from what it's doing now.
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) }
-    }
-}
-
-/// A specific flavor of dataflow analysis.
-///
-/// To run a dataflow analysis, one sets up an initial state for the
-/// `START_BLOCK` via `start_block_effect` and a transfer function (`trans`)
-/// for each block individually. The entry set for all other basic blocks is
-/// initialized to `Self::BOTTOM_VALUE`. The dataflow analysis then
-/// iteratively modifies the various entry sets (but leaves the the transfer
-/// function unchanged). `BottomValue::join` is used to merge the bitsets from
-/// two blocks (e.g. when two blocks' terminator jumps to a single block, that
-/// target block's state is the merged state of both incoming blocks).
-pub trait BitDenotation<'tcx>: BottomValue {
-    /// Specifies what index type is used to access the bitvector.
-    type Idx: Idx;
-
-    /// A name describing the dataflow analysis that this
-    /// `BitDenotation` is supporting. The name should be something
-    /// suitable for plugging in as part of a filename (i.e., avoid
-    /// space-characters or other things that tend to look bad on a
-    /// file system, like slashes or periods). It is also better for
-    /// the name to be reasonably short, again because it will be
-    /// plugged into a filename.
-    fn name() -> &'static str;
-
-    /// Size of each bitvector allocated for each block in the analysis.
-    fn bits_per_block(&self) -> usize;
-
-    /// Mutates the entry set according to the effects that
-    /// have been established *prior* to entering the start
-    /// block. This can't access the gen/kill sets, because
-    /// these won't be accounted for correctly.
-    ///
-    /// (For example, establishing the call arguments.)
-    fn start_block_effect(&self, entry_set: &mut BitSet<Self::Idx>);
-
-    /// Similar to `statement_effect`, except it applies
-    /// *just before* the statement rather than *just after* it.
-    ///
-    /// This matters for "dataflow at location" APIs, because the
-    /// before-statement effect is visible while visiting the
-    /// statement, while the after-statement effect only becomes
-    /// visible at the next statement.
-    ///
-    /// Both the before-statement and after-statement effects are
-    /// applied, in that order, before moving for the next
-    /// statement.
-    fn before_statement_effect(&self, _trans: &mut GenKillSet<Self::Idx>, _location: Location) {}
-
-    /// Mutates the block-sets (the flow sets for the given
-    /// basic block) according to the effects of evaluating statement.
-    ///
-    /// This is used, in particular, for building up the
-    /// "transfer-function" representing the overall-effect of the
-    /// block, represented via GEN and KILL sets.
-    ///
-    /// The statement is identified as `bb_data[idx_stmt]`, where
-    /// `bb_data` is the sequence of statements identified by `bb` in
-    /// the MIR.
-    fn statement_effect(&self, trans: &mut GenKillSet<Self::Idx>, location: Location);
-
-    /// Similar to `terminator_effect`, except it applies
-    /// *just before* the terminator rather than *just after* it.
-    ///
-    /// This matters for "dataflow at location" APIs, because the
-    /// before-terminator effect is visible while visiting the
-    /// terminator, while the after-terminator effect only becomes
-    /// visible at the terminator's successors.
-    ///
-    /// Both the before-terminator and after-terminator effects are
-    /// applied, in that order, before moving for the next
-    /// terminator.
-    fn before_terminator_effect(&self, _trans: &mut GenKillSet<Self::Idx>, _location: Location) {}
-
-    /// Mutates the block-sets (the flow sets for the given
-    /// basic block) according to the effects of evaluating
-    /// the terminator.
-    ///
-    /// This is used, in particular, for building up the
-    /// "transfer-function" representing the overall-effect of the
-    /// block, represented via GEN and KILL sets.
-    ///
-    /// The effects applied here cannot depend on which branch the
-    /// terminator took.
-    fn terminator_effect(&self, trans: &mut GenKillSet<Self::Idx>, location: Location);
-
-    /// Mutates the block-sets according to the (flow-dependent)
-    /// effect of a successful return from a Call terminator.
-    ///
-    /// If basic-block BB_x ends with a call-instruction that, upon
-    /// successful return, flows to BB_y, then this method will be
-    /// called on the exit flow-state of BB_x in order to set up the
-    /// entry flow-state of BB_y.
-    ///
-    /// This is used, in particular, as a special case during the
-    /// "propagate" loop where all of the basic blocks are repeatedly
-    /// visited. Since the effects of a Call terminator are
-    /// flow-dependent, the current MIR cannot encode them via just
-    /// GEN and KILL sets attached to the block, and so instead we add
-    /// this extra machinery to represent the flow-dependent effect.
-    //
-    // FIXME: right now this is a bit of a wart in the API. It might
-    // be better to represent this as an additional gen- and
-    // kill-sets associated with each edge coming out of the basic
-    // block.
-    fn propagate_call_return(
-        &self,
-        in_out: &mut BitSet<Self::Idx>,
-        call_bb: mir::BasicBlock,
-        dest_bb: mir::BasicBlock,
-        dest_place: &mir::Place<'tcx>,
-    );
-}
-
-impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D>
-where
-    D: BitDenotation<'tcx>,
-{
-    pub fn new(
-        body: &'a Body<'tcx>,
-        dead_unwinds: &'a BitSet<mir::BasicBlock>,
-        denotation: D,
-    ) -> Self {
-        let bits_per_block = denotation.bits_per_block();
-        let num_blocks = body.basic_blocks().len();
-
-        let on_entry = if D::BOTTOM_VALUE {
-            vec![BitSet::new_filled(bits_per_block); num_blocks]
-        } else {
-            vec![BitSet::new_empty(bits_per_block); num_blocks]
-        };
-        let nop = GenKill::from_elem(HybridBitSet::new_empty(bits_per_block));
-
-        DataflowAnalysis {
-            body,
-            dead_unwinds,
-            flow_state: DataflowState {
-                sets: AllSets { bits_per_block, on_entry, trans: vec![nop; num_blocks] },
-                operator: denotation,
-            },
-        }
-    }
-}
-
-impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D>
-where
-    D: BitDenotation<'tcx>,
-{
-    /// Propagates the bits of `in_out` into all the successors of `bb`,
-    /// using bitwise operator denoted by `self.operator`.
-    ///
-    /// For most blocks, this is entirely uniform. However, for blocks
-    /// that end with a call terminator, the effect of the call on the
-    /// dataflow state may depend on whether the call returned
-    /// successfully or unwound.
-    ///
-    /// To reflect this, the `propagate_call_return` method of the
-    /// `BitDenotation` mutates `in_out` when propagating `in_out` via
-    /// a call terminator; such mutation is performed *last*, to
-    /// ensure its side-effects do not leak elsewhere (e.g., into
-    /// unwind target).
-    fn propagate_bits_into_graph_successors_of(
-        &mut self,
-        in_out: &mut BitSet<D::Idx>,
-        (bb, bb_data): (mir::BasicBlock, &mir::BasicBlockData<'tcx>),
-        dirty_list: &mut WorkQueue<mir::BasicBlock>,
-    ) {
-        match bb_data.terminator().kind {
-            mir::TerminatorKind::Return
-            | mir::TerminatorKind::Resume
-            | mir::TerminatorKind::Abort
-            | mir::TerminatorKind::GeneratorDrop
-            | mir::TerminatorKind::Unreachable => {}
-            mir::TerminatorKind::Goto { target }
-            | mir::TerminatorKind::Assert { target, cleanup: None, .. }
-            | mir::TerminatorKind::Yield { resume: target, drop: None, .. }
-            | mir::TerminatorKind::Drop { target, location: _, unwind: None }
-            | mir::TerminatorKind::DropAndReplace { target, value: _, location: _, unwind: None } =>
-            {
-                self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
-            }
-            mir::TerminatorKind::Yield { resume: target, drop: Some(drop), .. } => {
-                self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
-                self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list);
-            }
-            mir::TerminatorKind::Assert { target, cleanup: Some(unwind), .. }
-            | mir::TerminatorKind::Drop { target, location: _, unwind: Some(unwind) }
-            | mir::TerminatorKind::DropAndReplace {
-                target,
-                value: _,
-                location: _,
-                unwind: Some(unwind),
-            } => {
-                self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
-                if !self.dead_unwinds.contains(bb) {
-                    self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
-                }
-            }
-            mir::TerminatorKind::SwitchInt { ref targets, .. } => {
-                for target in targets {
-                    self.propagate_bits_into_entry_set_for(in_out, *target, dirty_list);
-                }
-            }
-            mir::TerminatorKind::Call { cleanup, ref destination, .. } => {
-                if let Some(unwind) = cleanup {
-                    if !self.dead_unwinds.contains(bb) {
-                        self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
-                    }
-                }
-                if let Some((ref dest_place, dest_bb)) = *destination {
-                    // N.B.: This must be done *last*, after all other
-                    // propagation, as documented in comment above.
-                    self.flow_state.operator.propagate_call_return(in_out, bb, dest_bb, dest_place);
-                    self.propagate_bits_into_entry_set_for(in_out, dest_bb, dirty_list);
-                }
-            }
-            mir::TerminatorKind::FalseEdges { real_target, imaginary_target } => {
-                self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
-                self.propagate_bits_into_entry_set_for(in_out, imaginary_target, dirty_list);
-            }
-            mir::TerminatorKind::FalseUnwind { real_target, unwind } => {
-                self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
-                if let Some(unwind) = unwind {
-                    if !self.dead_unwinds.contains(bb) {
-                        self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
-                    }
-                }
-            }
-        }
-    }
-
-    fn propagate_bits_into_entry_set_for(
-        &mut self,
-        in_out: &BitSet<D::Idx>,
-        bb: mir::BasicBlock,
-        dirty_queue: &mut WorkQueue<mir::BasicBlock>,
-    ) {
-        let entry_set = self.flow_state.sets.entry_set_mut_for(bb.index());
-        let set_changed = self.flow_state.operator.join(entry_set, &in_out);
-        if set_changed {
-            dirty_queue.insert(bb);
-        }
-    }
-}
index e42f64b5c7384adaeba81508f28224324cff8486..5f761ce344880baec15db92e3a2164d1ec1f6b15 100644 (file)
@@ -9,7 +9,7 @@
 use std::marker::PhantomData;
 
 use super::{qualifs, Item, Qualif};
-use crate::dataflow::{self as old_dataflow, generic as dataflow};
+use crate::dataflow;
 
 /// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
 /// `FlowSensitiveAnalysis`.
@@ -165,7 +165,7 @@ fn transfer_function(
     }
 }
 
-impl<Q> old_dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> {
+impl<Q> dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> {
     const BOTTOM_VALUE: bool = false;
 }
 
index be461c0e03d8635767f5d007db2f39f5458bf6fa..7f4714e9f9c1999e2276ba5c4a6747db92ab69bd 100644 (file)
@@ -22,8 +22,8 @@
 use super::resolver::FlowSensitiveAnalysis;
 use super::{is_lang_panic_fn, ConstKind, Item, Qualif};
 use crate::const_eval::{is_const_fn, is_unstable_const_fn};
-use crate::dataflow::generic::{self as dataflow, Analysis};
 use crate::dataflow::MaybeMutBorrowedLocals;
+use crate::dataflow::{self, Analysis};
 
 // We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated
 // through a pointer prior to the given point. This is okay even though `MaybeMutBorrowedLocals`
index 5d02074aaaa6b42344a684ff21281d5d718bc9ae..4ec4ef0206105c82e0eee6f372f3273c01946546 100644 (file)
@@ -1,9 +1,9 @@
 use crate::dataflow;
-use crate::dataflow::generic::{Analysis, ResultsCursor};
 use crate::dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
 use crate::dataflow::on_lookup_result_bits;
 use crate::dataflow::MoveDataParamEnv;
 use crate::dataflow::{on_all_children_bits, on_all_drop_children_bits};
+use crate::dataflow::{Analysis, ResultsCursor};
 use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
 use crate::transform::{MirPass, MirSource};
 use crate::util::elaborate_drops::{elaborate_drop, DropFlagState, Unwind};
index 349cda831053f851cf487f634183d0ac8fe8c099..1945efb6bf7ccf8f9b3d78c418c8a69a494942bb 100644 (file)
@@ -49,7 +49,7 @@
 //! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
 //! Otherwise it drops all the values in scope at the last suspension point.
 
-use crate::dataflow::generic::{self as dataflow, Analysis};
+use crate::dataflow::{self, Analysis};
 use crate::dataflow::{MaybeBorrowedLocals, MaybeRequiresStorage, MaybeStorageLive};
 use crate::transform::no_landing_pads::no_landing_pads;
 use crate::transform::simplify;
index 22ac3410a75ab7d3458ee81a3aa88257adf94e22..c9a00166f0f9c9b61f757d6ae974552fd2ef6c1b 100644 (file)
@@ -9,11 +9,11 @@
 use rustc_hir::def_id::DefId;
 use rustc_index::bit_set::BitSet;
 
-use crate::dataflow::generic::{Analysis, Results, ResultsCursor};
 use crate::dataflow::move_paths::{HasMoveData, MoveData};
 use crate::dataflow::move_paths::{LookupResult, MovePathIndex};
 use crate::dataflow::MaybeMutBorrowedLocals;
 use crate::dataflow::MoveDataParamEnv;
+use crate::dataflow::{Analysis, Results, ResultsCursor};
 use crate::dataflow::{
     DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
 };