Revised mir-dataflow.

Incorporates many fixes contributed by arielb1.

----

revise borrowck::mir::dataflow code to allow varying domain for bitvectors.

This particular code implements the `BitDenotation` trait for three
analyses:

 * `MovingOutStatements`, which, like `borrowck::move_data`, maps each
   bit-index to a move instruction, and a 1 means "the effect of this
   move reaches this point" (and the assigned l-value, if a scoped
   declaration, is still in scope).

 * `MaybeInitializedLvals`, which maps each bit-index to an l-value.
   A 1 means "there exists a control flow path to this point that
   initializes the associated l-value."

 * `MaybeUninitializedLvals`, which maps each bit-index to an l-value
   A 1 means "there exists a control flow path to this point that
   de-initializes the associated l-value."

----

Revised `graphviz` dataflow-rendering support in `borrowck::mir`.

One big difference is that this code is now parameterized over the
`BitDenotation`, so that it can be used to render dataflow results
independent of how the dataflow bitvectors are interpreted; see where
reference to `MoveOut` is replaced by the type parameter `D`.

----

Factor out routine to query subattributes in `#[rustc_mir(..)]`.

(Later commits build upon this for some unit testing and instrumentation.)

----

thread through a tcx so that I can query types of lvalues as part of analysis.

----

Revised `BitDenotation::Ctxt`, allowing variation beyond `MoveData`.

The main motivation is to ease threading through a `TyCtxt`.

(In hindsight it might have been better to instead attach the `TyCtxt`
to each of the different dataflow implementations, but that would
require e.g. switching away from having a `Default` impl, so I am
leaving that experiment for another time.)
This commit is contained in:
Felix S. Klock II 2016-05-02 15:50:27 +02:00
parent 6c72c5fa88
commit c73f3517a2
7 changed files with 1369 additions and 721 deletions

View File

@ -226,7 +226,8 @@ pub struct UpvarDecl {
/// list of the `Mir`.
///
/// (We use a `u32` internally just to save memory.)
#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
RustcEncodable, RustcDecodable)]
pub struct BasicBlock(u32);
impl BasicBlock {

View File

@ -18,7 +18,7 @@ use ty::TyCtxt;
use syntax::ast::NodeId;
/// Where a specific Mir comes from.
#[derive(Copy, Clone)]
#[derive(Debug, Copy, Clone)]
pub enum MirSource {
/// Functions and methods.
Fn(NodeId),

View File

@ -1,629 +0,0 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use syntax::attr::AttrMetaMethods;
use rustc::ty::TyCtxt;
use rustc::mir::repr::{self, Mir};
use std::io;
use std::marker::PhantomData;
use std::mem;
use std::usize;
use super::MirBorrowckCtxt;
use super::gather_moves::{Location, MoveData, MovePathData, MovePathIndex, MoveOutIndex, PathMap};
use super::graphviz;
use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
pub trait Dataflow {
fn dataflow(&mut self);
}
impl<'b, 'a: 'b, 'tcx: 'a> Dataflow for MirBorrowckCtxt<'b, 'a, 'tcx> {
fn dataflow(&mut self) {
self.build_gen_and_kill_sets();
self.pre_dataflow_instrumentation().unwrap();
self.propagate();
self.post_dataflow_instrumentation().unwrap();
}
}
struct PropagationContext<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn>
where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue)
{
mbcx: &'c mut MirBorrowckCtxt<'b, 'a, 'tcx>,
changed: bool,
on_return: OnReturn
}
impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
fn propagate(&mut self) {
let mut temp = vec![0; self.flow_state.sets.words_per_block];
let mut propcx = PropagationContext {
mbcx: &mut *self,
changed: true,
on_return: |move_data, in_out, dest_lval| {
let move_path_index = move_data.rev_lookup.find(dest_lval);
on_all_children_bits(in_out,
&move_data.path_map,
&move_data.move_paths,
move_path_index,
&|in_out, mpi| {
in_out.clear_bit(mpi.idx());
});
},
};
while propcx.changed {
propcx.changed = false;
propcx.reset(&mut temp);
propcx.walk_cfg(&mut temp);
}
}
fn build_gen_and_kill_sets(&mut self) {
// First we need to build the gen- and kill-sets. The
// gather_moves information provides a high-level mapping from
// mir-locations to the MoveOuts (and those correspond
// directly to gen-sets here). But we still need to figure out
// the kill-sets.
let move_data = &self.flow_state.operator;
let move_paths = &move_data.move_paths;
let loc_map = &move_data.loc_map;
let path_map = &move_data.path_map;
let rev_lookup = &move_data.rev_lookup;
for bb in self.mir.all_basic_blocks() {
let &repr::BasicBlockData { ref statements,
ref terminator,
is_cleanup: _ } =
self.mir.basic_block_data(bb);
let mut sets = self.flow_state.sets.for_block(bb.index());
for (j, stmt) in statements.iter().enumerate() {
let loc = Location { block: bb, index: j };
debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
stmt, loc, &loc_map[loc]);
for move_index in &loc_map[loc] {
// Every path deinitialized by a *particular move*
// has corresponding bit, "gen'ed" (i.e. set)
// here, in dataflow vector
zero_to_one(&mut sets.gen_set, *move_index);
}
match stmt.kind {
repr::StatementKind::Assign(ref lvalue, _) => {
// assigning into this `lvalue` kills all
// MoveOuts from it, and *also* all MoveOuts
// for children and associated fragment sets.
let move_path_index = rev_lookup.find(lvalue);
on_all_children_bits(sets.kill_set,
path_map,
move_paths,
move_path_index,
&|kill_set, mpi| {
kill_set.set_bit(mpi.idx());
});
}
}
}
let loc = Location { block: bb, index: statements.len() };
debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
terminator, loc, &loc_map[loc]);
for move_index in &loc_map[loc] {
zero_to_one(&mut sets.gen_set, *move_index);
}
}
fn zero_to_one(gen_set: &mut [usize], move_index: MoveOutIndex) {
let retval = gen_set.set_bit(move_index.idx());
assert!(retval);
}
}
}
fn on_all_children_bits<Each>(set: &mut [usize],
path_map: &PathMap,
move_paths: &MovePathData,
move_path_index: MovePathIndex,
each_child: &Each)
where Each: Fn(&mut [usize], MoveOutIndex)
{
// 1. invoke `each_child` callback for all moves that directly
// influence path for `move_path_index`
for move_index in &path_map[move_path_index] {
each_child(set, *move_index);
}
// 2. for each child of the path (that is named in this
// function), recur.
//
// (Unnamed children are irrelevant to dataflow; by
// definition they have no associated moves.)
let mut next_child_index = move_paths[move_path_index].first_child;
while let Some(child_index) = next_child_index {
on_all_children_bits(set, path_map, move_paths, child_index, each_child);
next_child_index = move_paths[child_index].next_sibling;
}
}
impl<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn> PropagationContext<'c, 'b, 'a, 'tcx, OnReturn>
where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue)
{
fn reset(&mut self, bits: &mut [usize]) {
let e = if MoveData::initial_value() {usize::MAX} else {0};
for b in bits {
*b = e;
}
}
fn walk_cfg(&mut self, in_out: &mut [usize]) {
let &mut MirBorrowckCtxt { ref mir, ref mut flow_state, .. } = self.mbcx;
for (idx, bb) in mir.basic_blocks.iter().enumerate() {
{
let sets = flow_state.sets.for_block(idx);
debug_assert!(in_out.len() == sets.on_entry.len());
in_out.clone_from_slice(sets.on_entry);
bitwise(in_out, sets.gen_set, &Union);
bitwise(in_out, sets.kill_set, &Subtract);
}
flow_state.propagate_bits_into_graph_successors_of(in_out,
&mut self.changed,
bb,
&self.on_return);
}
}
}
impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
fn pre_dataflow_instrumentation(&self) -> io::Result<()> {
self.if_attr_meta_name_found(
"borrowck_graphviz_preflow",
|this, path: &str| {
graphviz::print_borrowck_graph_to(this, "preflow", path)
})
}
fn post_dataflow_instrumentation(&self) -> io::Result<()> {
self.if_attr_meta_name_found(
"borrowck_graphviz_postflow",
|this, path: &str| {
graphviz::print_borrowck_graph_to(this, "postflow", path)
})
}
fn if_attr_meta_name_found<F>(&self,
name: &str,
callback: F) -> io::Result<()>
where F: for <'aa, 'bb> FnOnce(&'aa Self, &'bb str) -> io::Result<()>
{
for attr in self.attributes {
if attr.check_name("rustc_mir") {
let items = attr.meta_item_list();
for item in items.iter().flat_map(|l| l.iter()) {
if item.check_name(name) {
if let Some(s) = item.value_str() {
return callback(self, &s);
} else {
self.bcx.tcx.sess.span_err(
item.span,
&format!("{} attribute requires a path", item.name()));
}
}
}
}
}
Ok(())
}
}
/// Maps each block to a set of bits
#[derive(Clone, Debug)]
struct Bits {
bits: Vec<usize>,
}
impl Bits {
fn new(init_word: usize, num_words: usize) -> Self {
Bits { bits: vec![init_word; num_words] }
}
}
pub struct DataflowState<O: BitDenotation>
{
/// 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,
/// operator used to initialize, combine, and interpret bits.
operator: O,
}
pub struct AllSets {
/// Analysis bitwidth for each block.
bits_per_block: usize,
/// Number of words associated with each block entry
/// equal to bits_per_block / usize::BITS, rounded up.
words_per_block: usize,
/// For each block, bits generated by executing the statements in
/// the block. (For comparison, the Terminator for each block is
/// handled in a flow-specific manner during propagation.)
gen_sets: Bits,
/// For each block, bits killed by executing the statements in the
/// block. (For comparison, the Terminator for each block is
/// handled in a flow-specific manner during propagation.)
kill_sets: Bits,
/// For each block, bits valid on entry to the block.
on_entry_sets: Bits,
}
pub struct BlockSets<'a> {
on_entry: &'a mut [usize],
gen_set: &'a mut [usize],
kill_set: &'a mut [usize],
}
impl AllSets {
pub fn bits_per_block(&self) -> usize { self.bits_per_block }
pub fn bytes_per_block(&self) -> usize { (self.bits_per_block + 7) / 8 }
pub fn for_block(&mut self, block_idx: usize) -> BlockSets {
let offset = self.words_per_block * block_idx;
let range = offset..(offset + self.words_per_block);
BlockSets {
on_entry: &mut self.on_entry_sets.bits[range.clone()],
gen_set: &mut self.gen_sets.bits[range.clone()],
kill_set: &mut self.kill_sets.bits[range],
}
}
fn lookup_set_for<'a>(&self, sets: &'a Bits, block_idx: usize) -> &'a [usize] {
let offset = self.words_per_block * block_idx;
&sets.bits[offset..(offset + self.words_per_block)]
}
pub fn gen_set_for(&self, block_idx: usize) -> &[usize] {
self.lookup_set_for(&self.gen_sets, block_idx)
}
pub fn kill_set_for(&self, block_idx: usize) -> &[usize] {
self.lookup_set_for(&self.kill_sets, block_idx)
}
pub fn on_entry_set_for(&self, block_idx: usize) -> &[usize] {
self.lookup_set_for(&self.on_entry_sets, block_idx)
}
pub fn on_exit_set_for(&self, block_idx: usize) -> Vec<usize> {
let mut set: Vec<_> = self.on_entry_set_for(block_idx).iter()
.map(|x|*x)
.collect();
bitwise(&mut set[..], self.gen_set_for(block_idx), &Union);
bitwise(&mut set[..], self.kill_set_for(block_idx), &Subtract);
return set;
}
}
impl<O: BitDenotation> DataflowState<O> {
fn each_bit<F>(&self, words: &[usize], mut f: F)
where F: FnMut(usize) {
//! Helper for iterating over the bits in a bitvector.
for (word_index, &word) in words.iter().enumerate() {
if word != 0 {
let usize_bits: usize = mem::size_of::<usize>();
let base_index = word_index * usize_bits;
for offset in 0..usize_bits {
let bit = 1 << offset;
if (word & bit) != 0 {
// NB: we round up the total number of bits
// that we store in any given bit set so that
// it is an even multiple of usize::BITS. This
// means that there may be some stray bits at
// the end that do not correspond to any
// actual value; that's why we first check
// that we are in range of bits_per_block.
let bit_index = base_index + offset as usize;
if bit_index >= self.sets.bits_per_block() {
return;
} else {
f(bit_index);
}
}
}
}
}
}
pub fn interpret_set(&self, words: &[usize]) -> Vec<&O::Bit> {
let mut v = Vec::new();
self.each_bit(words, |i| {
v.push(self.operator.interpret(i));
});
v
}
}
pub trait BitwiseOperator {
/// Joins two predecessor bits together, typically either `|` or `&`
fn join(&self, pred1: usize, pred2: usize) -> usize;
}
/// Parameterization for the precise form of data flow that is used.
pub trait DataflowOperator : BitwiseOperator {
/// Specifies the initial value for each bit in the `on_entry` set
fn initial_value() -> bool;
}
pub trait BitDenotation: DataflowOperator {
/// Specifies what is represented by each bit in the dataflow bitvector.
type Bit;
/// Size of each bivector allocated for each block in the analysis.
fn bits_per_block(&self) -> usize;
/// Provides the meaning of each entry in the dataflow bitvector.
/// (Mostly intended for use for better debug instrumentation.)
fn interpret(&self, idx: usize) -> &Self::Bit;
}
impl<D: BitDenotation> DataflowState<D> {
pub fn new(mir: &Mir, denotation: D) -> Self {
let bits_per_block = denotation.bits_per_block();
let usize_bits = mem::size_of::<usize>() * 8;
let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits;
let num_blocks = mir.basic_blocks.len();
let num_words = num_blocks * words_per_block;
let entry = if D::initial_value() { usize::MAX } else {0};
let zeroes = Bits::new(0, num_words);
let on_entry = Bits::new(entry, num_words);
DataflowState {
sets: AllSets {
bits_per_block: bits_per_block,
words_per_block: words_per_block,
gen_sets: zeroes.clone(),
kill_sets: zeroes,
on_entry_sets: on_entry,
},
operator: denotation,
}
}
}
impl<D: BitDenotation> DataflowState<D> {
/// 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 `on_return`
/// callback 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<OnReturn>(
&mut self,
in_out: &mut [usize],
changed: &mut bool,
bb: &repr::BasicBlockData,
on_return: OnReturn) where OnReturn: Fn(&D, &mut [usize], &repr::Lvalue)
{
match bb.terminator().kind {
repr::TerminatorKind::Return |
repr::TerminatorKind::Resume => {}
repr::TerminatorKind::Goto { ref target } |
repr::TerminatorKind::Drop { ref target, value: _, unwind: None } => {
self.propagate_bits_into_entry_set_for(in_out, changed, target);
}
repr::TerminatorKind::Drop { ref target, value: _, unwind: Some(ref unwind) } => {
self.propagate_bits_into_entry_set_for(in_out, changed, target);
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
}
repr::TerminatorKind::If { ref targets, .. } => {
self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0);
self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1);
}
repr::TerminatorKind::Switch { ref targets, .. } |
repr::TerminatorKind::SwitchInt { ref targets, .. } => {
for target in targets {
self.propagate_bits_into_entry_set_for(in_out, changed, target);
}
}
repr::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => {
if let Some(ref unwind) = *cleanup {
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
}
if let Some((ref dest_lval, ref dest_bb)) = *destination {
// N.B.: This must be done *last*, after all other
// propagation, as documented in comment above.
on_return(&self.operator, in_out, dest_lval);
self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb);
}
}
}
}
fn propagate_bits_into_entry_set_for(&mut self,
in_out: &[usize],
changed: &mut bool,
bb: &repr::BasicBlock) {
let entry_set = self.sets.for_block(bb.index()).on_entry;
let set_changed = bitwise(entry_set, in_out, &self.operator);
if set_changed {
*changed = true;
}
}
}
// Dataflow analyses are built upon some interpretation of the
// bitvectors attached to each basic block, represented via a
// zero-sized structure.
//
// Note on PhantomData: Each interpretation will need to instantiate
// the `Bit` and `Ctxt` associated types, and in this case, those
// associated types need an associated lifetime `'tcx`. The
// interpretive structures are zero-sized, so they all need to carry a
// `PhantomData` representing how the structures relate to the `'tcx`
// lifetime.
//
// But, since all of the uses of `'tcx` are solely via instances of
// `Ctxt` that are passed into the `BitDenotation` methods, we can
// consistently use a `PhantomData` that is just a function over a
// `&Ctxt` (== `&MoveData<'tcx>).
/// `MaybeInitializedLvals` tracks all l-values that might be
/// initialized upon reaching a particular point in the control flow
/// for a function.
///
/// For example, in code like the following, we have corresponding
/// dataflow information shown in the right-hand comments.
///
/// ```rust
/// struct S;
/// fn foo(pred: bool) { // maybe-init:
/// // {}
/// let a = S; let b = S; let c; let d; // {a, b}
///
/// if pred {
/// drop(a); // { b}
/// b = S; // { b}
///
/// } else {
/// drop(b); // {a}
/// d = S; // {a, d}
///
/// } // {a, b, d}
///
/// c = S; // {a, b, c, d}
/// }
/// ```
///
/// To determine whether an l-value *must* be initialized at a
/// particular control-flow point, one can take the set-difference
/// between this data and the data from `MaybeUninitializedLvals` at the
/// corresponding control-flow point.
///
/// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeUninitializedLvals` yields the set of
/// l-values that would require a dynamic drop-flag at that statement.
#[derive(Debug, Default)]
pub struct MaybeInitializedLvals<'tcx> {
// See "Note on PhantomData" above.
phantom: PhantomData<for <'a> Fn(&'a MoveData<'tcx>)>,
}
/// `MaybeUninitializedLvals` tracks all l-values that might be
/// uninitialized upon reaching a particular point in the control flow
/// for a function.
///
/// For example, in code like the following, we have corresponding
/// dataflow information shown in the right-hand comments.
///
/// ```rust
/// struct S;
/// fn foo(pred: bool) { // maybe-uninit:
/// // {a, b, c, d}
/// let a = S; let b = S; let c; let d; // { c, d}
///
/// if pred {
/// drop(a); // {a, c, d}
/// b = S; // {a, c, d}
///
/// } else {
/// drop(b); // { b, c, d}
/// d = S; // { b, c }
///
/// } // {a, b, c, d}
///
/// c = S; // {a, b, d}
/// }
/// ```
///
/// To determine whether an l-value *must* be uninitialized at a
/// particular control-flow point, one can take the set-difference
/// between this data and the data from `MaybeInitializedLvals` at the
/// corresponding control-flow point.
///
/// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeInitializedLvals` yields the set of
/// l-values that would require a dynamic drop-flag at that statement.
#[derive(Debug, Default)]
pub struct MaybeUninitializedLvals<'tcx> {
// See "Note on PhantomData" above.
phantom: PhantomData<for <'a> Fn(&'a MoveData<'tcx>)>,
}
/// `MovingOutStatements` tracks the statements that perform moves out
/// of particular l-values. More precisely, it tracks whether the
/// *effect* of such moves (namely, the uninitialization of the
/// l-value in question) can reach some point in the control-flow of
/// the function, or if that effect is "killed" by some intervening
/// operation reinitializing that l-value.
///
/// The resulting dataflow is a more enriched version of
/// `MaybeUninitializedLvals`. Both structures on their own only tell
/// you if an l-value *might* be uninitialized at a given point in the
/// control flow. But `MovingOutStatements` also includes the added
/// data of *which* particular statement causing the deinitialization
/// that the borrow checker's error meessage may need to report.
#[derive(Debug, Default)]
pub struct MovingOutStatements<'tcx> {
// See "Note on PhantomData" above.
phantom: PhantomData<for <'a> Fn(&'a MoveData<'tcx>)>,
}
impl<'a, 'tcx> DataflowState<MoveData<'tcx>> {
pub fn new_move_analysis(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
let move_data = MoveData::gather_moves(mir, tcx);
DataflowState::new(mir, move_data)
}
}
impl<'tcx> BitwiseOperator for MoveData<'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // moves from both preds are in scope
}
}
impl<'tcx> DataflowOperator for MoveData<'tcx> {
#[inline]
fn initial_value() -> bool {
false // no loans in scope by default
}
}
#[inline]
fn bitwise<Op:BitwiseOperator>(out_vec: &mut [usize],
in_vec: &[usize],
op: &Op) -> bool {
assert_eq!(out_vec.len(), in_vec.len());
let mut changed = false;
for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) {
let old_val = *out_elt;
let new_val = op.join(old_val, *in_elt);
*out_elt = new_val;
changed |= old_val != new_val;
}
changed
}
struct Union;
impl BitwiseOperator for Union {
fn join(&self, a: usize, b: usize) -> usize { a | b }
}
struct Subtract;
impl BitwiseOperator for Subtract {
fn join(&self, a: usize, b: usize) -> usize { a & !b }
}

View File

@ -10,30 +10,58 @@
//! Hook into libgraphviz for rendering dataflow graphs for MIR.
use syntax::ast::NodeId;
use rustc::mir::repr::{BasicBlock, Mir};
use dot;
use dot::IntoCow;
use std::fmt::Debug;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::path::Path;
use super::MirBorrowckCtxt;
use super::super::MirBorrowckCtxtPreDataflow;
use bitslice::bits_to_string;
use super::gather_moves::MoveOut;
use super::{BitDenotation, DataflowState};
use super::{HasMoveData};
struct Graph<'c, 'b:'c, 'a:'b, 'tcx:'a> { mbcx: &'c MirBorrowckCtxt<'b, 'a, 'tcx>,
context: &'b str }
pub trait MirWithFlowState<'tcx> {
type BD: BitDenotation;
fn node_id(&self) -> NodeId;
fn mir(&self) -> &Mir<'tcx>;
fn analysis_ctxt(&self) -> &<Self::BD as BitDenotation>::Ctxt;
fn flow_state(&self) -> &DataflowState<Self::BD>;
}
pub fn print_borrowck_graph_to(mbcx: &MirBorrowckCtxt,
context: &str,
path: &str) -> io::Result<()> {
let g = Graph { mbcx: mbcx, context: context };
impl<'a, 'tcx: 'a, BD> MirWithFlowState<'tcx> for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
where 'a, 'tcx: 'a, BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx>
{
type BD = BD;
fn node_id(&self) -> NodeId { self.node_id }
fn mir(&self) -> &Mir<'tcx> { self.flow_state.mir() }
fn analysis_ctxt(&self) -> &BD::Ctxt { &self.flow_state.ctxt }
fn flow_state(&self) -> &DataflowState<Self::BD> { &self.flow_state.flow_state }
}
struct Graph<'a, 'tcx, MWF:'a> where MWF: MirWithFlowState<'tcx>,
{
mbcx: &'a MWF,
phantom: PhantomData<&'tcx ()>
}
pub fn print_borrowck_graph_to<'a, 'tcx, BD>(
mbcx: &MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>,
path: &Path) -> io::Result<()> where BD: BitDenotation,
BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx>
{
let g = Graph { mbcx: mbcx, phantom: PhantomData };
let mut v = Vec::new();
dot::render(&g, &mut v)?;
println!("print_borrowck_graph_to path: {} context: {} node_id: {}",
path, context, mbcx.node_id);
debug!("print_borrowck_graph_to path: {} node_id: {}",
path.display(), mbcx.node_id);
File::create(path).and_then(|mut f| f.write_all(&v))
}
@ -47,13 +75,14 @@ fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec<Edge> {
(0..succ_len).map(|index| Edge { source: bb, index: index}).collect()
}
impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> {
impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF>
where MWF: MirWithFlowState<'tcx>, <MWF::BD as BitDenotation>::Bit: Debug
{
type Node = Node;
type Edge = Edge;
fn graph_id(&self) -> dot::Id {
dot::Id::new(format!("graph_for_node_{}_{}",
self.mbcx.node_id,
self.context))
dot::Id::new(format!("graph_for_node_{}",
self.mbcx.node_id()))
.unwrap()
}
@ -106,10 +135,10 @@ impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> {
const BG_FLOWCONTENT: &'static str = r#"bgcolor="pink""#;
const ALIGN_RIGHT: &'static str = r#"align="right""#;
const FACE_MONOSPACE: &'static str = r#"FACE="Courier""#;
fn chunked_present_left<W:io::Write>(w: &mut W,
interpreted: &[&MoveOut],
chunk_size: usize)
-> io::Result<()>
fn chunked_present_left<D: Debug, W:io::Write>(w: &mut W,
interpreted: &[&D],
chunk_size: usize)
-> io::Result<()>
{
// This function may emit a sequence of <tr>'s, but it
// always finishes with an (unfinished)
@ -137,40 +166,55 @@ impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> {
Ok(())
}
::rustc_mir::graphviz::write_node_label(
*n, self.mbcx.mir, &mut v, 4,
*n, self.mbcx.mir(), &mut v, 4,
|w| {
let flow = &self.mbcx.flow_state;
let entry = flow.interpret_set(flow.sets.on_entry_set_for(i));
chunked_present_left(w, &entry[..], chunk_size)?;
let ctxt = self.mbcx.analysis_ctxt();
let flow = self.mbcx.flow_state();
let entry_interp = flow.interpret_set(ctxt, flow.sets.on_entry_set_for(i));
chunked_present_left(w, &entry_interp[..], chunk_size)?;
let bits_per_block = flow.sets.bits_per_block();
let entry = flow.sets.on_entry_set_for(i);
debug!("entry set for i={i} bits_per_block: {bpb} entry: {e:?} interp: {ei:?}",
i=i, e=entry, bpb=bits_per_block, ei=entry_interp);
write!(w, "= ENTRY:</td><td {bg}><FONT {face}>{entrybits:?}</FONT></td>\
<td></td></tr>",
bg = BG_FLOWCONTENT,
face = FACE_MONOSPACE,
entrybits=bits_to_string(flow.sets.on_entry_set_for(i),
flow.sets.bytes_per_block()))
entrybits=bits_to_string(entry, bits_per_block))
},
|w| {
let flow = &self.mbcx.flow_state;
let gen = flow.interpret_set( flow.sets.gen_set_for(i));
let kill = flow.interpret_set(flow.sets.kill_set_for(i));
chunked_present_left(w, &gen[..], chunk_size)?;
write!(w, " = GEN:</td><td {bg}><FONT {face}>{genbits:?}</FONT></td>\
<td></td></tr>",
bg = BG_FLOWCONTENT,
face = FACE_MONOSPACE,
genbits=bits_to_string( flow.sets.gen_set_for(i),
flow.sets.bytes_per_block()))?;
write!(w, "<tr><td></td><td {bg} {align}>KILL:</td>\
<td {bg}><FONT {face}>{killbits:?}</FONT></td>",
bg = BG_FLOWCONTENT,
align = ALIGN_RIGHT,
face = FACE_MONOSPACE,
killbits=bits_to_string(flow.sets.kill_set_for(i),
flow.sets.bytes_per_block()))?;
let ctxt = self.mbcx.analysis_ctxt();
let flow = self.mbcx.flow_state();
let gen_interp = flow.interpret_set(ctxt, flow.sets.gen_set_for(i));
let kill_interp = flow.interpret_set(ctxt, flow.sets.kill_set_for(i));
chunked_present_left(w, &gen_interp[..], chunk_size)?;
let bits_per_block = flow.sets.bits_per_block();
{
let gen = flow.sets.gen_set_for(i);
debug!("gen set for i={i} bits_per_block: {bpb} gen: {g:?} interp: {gi:?}",
i=i, g=gen, bpb=bits_per_block, gi=gen_interp);
write!(w, " = GEN:</td><td {bg}><FONT {face}>{genbits:?}</FONT></td>\
<td></td></tr>",
bg = BG_FLOWCONTENT,
face = FACE_MONOSPACE,
genbits=bits_to_string(gen, bits_per_block))?;
}
{
let kill = flow.sets.kill_set_for(i);
debug!("kill set for i={i} bits_per_block: {bpb} kill: {k:?} interp: {ki:?}",
i=i, k=kill, bpb=bits_per_block, ki=kill_interp);
write!(w, "<tr><td></td><td {bg} {align}>KILL:</td>\
<td {bg}><FONT {face}>{killbits:?}</FONT></td>",
bg = BG_FLOWCONTENT,
align = ALIGN_RIGHT,
face = FACE_MONOSPACE,
killbits=bits_to_string(kill, bits_per_block))?;
}
// (chunked_present_right)
let mut seen_one = false;
for k in kill.chunks(chunk_size) {
for k in kill_interp.chunks(chunk_size) {
if !seen_one {
// continuation of row; this is fourth <td>
write!(w, "<td {bg}>= {kill:?}</td></tr>",
@ -200,16 +244,18 @@ impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> {
}
}
impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::GraphWalk<'c> for Graph<'c,'b,'a,'tcx> {
impl<'a, 'tcx, MWF> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF>
where MWF: MirWithFlowState<'tcx>
{
type Node = Node;
type Edge = Edge;
fn nodes(&self) -> dot::Nodes<Node> {
self.mbcx.mir.all_basic_blocks().into_cow()
self.mbcx.mir().all_basic_blocks().into_cow()
}
fn edges(&self) -> dot::Edges<Edge> {
let mir = self.mbcx.mir;
let blocks = self.mbcx.mir.all_basic_blocks();
let mir = self.mbcx.mir();
let blocks = mir.all_basic_blocks();
// base initial capacity on assumption every block has at
// least one outgoing edge (Which should be true for all
// blocks but one, the exit-block).
@ -226,7 +272,7 @@ impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::GraphWalk<'c> for Graph<'c,'b,'a,'tcx> {
}
fn target(&self, edge: &Edge) -> Node {
let mir = self.mbcx.mir;
let mir = self.mbcx.mir();
mir.basic_block_data(edge.source).terminator().successors()[edge.index]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,6 @@ use std::fmt;
use std::iter;
use std::ops::Index;
use super::dataflow::BitDenotation;
use super::abs_domain::{AbstractElem, Lift};
// This submodule holds some newtype'd Index wrappers that are using
@ -32,7 +31,7 @@ mod indexes {
macro_rules! new_index {
($Index:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct $Index(NonZero<usize>);
impl $Index {
@ -56,6 +55,12 @@ mod indexes {
pub use self::indexes::MovePathIndex;
pub use self::indexes::MoveOutIndex;
impl self::indexes::MoveOutIndex {
pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex {
move_data.moves[self.idx()].path
}
}
/// `MovePath` is a canonicalized representation of a path that is
/// moved or assigned to.
///
@ -125,6 +130,7 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> {
}
}
#[derive(Debug)]
pub struct MoveData<'tcx> {
pub move_paths: MovePathData<'tcx>,
pub moves: Vec<MoveOut>,
@ -133,6 +139,7 @@ pub struct MoveData<'tcx> {
pub rev_lookup: MovePathLookup<'tcx>,
}
#[derive(Debug)]
pub struct LocMap {
/// Location-indexed (BasicBlock for outer index, index within BB
/// for inner index) map to list of MoveOutIndex's.
@ -153,6 +160,7 @@ impl Index<Location> for LocMap {
}
}
#[derive(Debug)]
pub struct PathMap {
/// Path-indexed map to list of MoveOutIndex's.
///
@ -187,7 +195,7 @@ impl fmt::Debug for MoveOut {
}
}
#[derive(Copy, Clone)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Location {
/// block where action is located
pub block: BasicBlock,
@ -202,10 +210,15 @@ impl fmt::Debug for Location {
}
}
#[derive(Debug)]
pub struct MovePathData<'tcx> {
move_paths: Vec<MovePath<'tcx>>,
}
impl<'tcx> MovePathData<'tcx> {
pub fn len(&self) -> usize { self.move_paths.len() }
}
impl<'tcx> Index<MovePathIndex> for MovePathData<'tcx> {
type Output = MovePath<'tcx>;
fn index(&self, i: MovePathIndex) -> &MovePath<'tcx> {
@ -224,6 +237,7 @@ struct MovePathDataBuilder<'a, 'tcx: 'a> {
}
/// Tables mapping from an l-value to its MovePathIndex.
#[derive(Debug)]
pub struct MovePathLookup<'tcx> {
vars: MovePathInverseMap,
temps: MovePathInverseMap,
@ -272,6 +286,7 @@ impl<T:Clone> FillTo for Vec<T> {
#[derive(Clone, Debug)]
enum LookupKind { Generate, Reuse }
#[derive(Clone, Debug)]
struct Lookup<T>(LookupKind, T);
impl Lookup<MovePathIndex> {
@ -425,6 +440,8 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> {
}
fn move_path_for(&mut self, lval: &Lvalue<'tcx>) -> MovePathIndex {
debug!("move_path_for({:?})", lval);
let lookup: Lookup<MovePathIndex> = self.lookup(lval);
// `lookup` is either the previously assigned index or a
@ -547,7 +564,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
debug_assert!(loc_map_bb.len() == len + 1);
let mut bb_ctxt = BlockContext {
tcx: tcx,
_tcx: tcx,
moves: &mut moves,
builder: builder,
path_map: &mut path_map,
@ -608,23 +625,17 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
// example where I am seeing this arise is
// `TargetDataLayout::parse(&Session)` in
// `rustc::ty::layout`.
debug!("encountered Rvalue::Slice as RHS of Assign, source: {:?} \n{}",
source, {
let mut out = Vec::new();
{
use std::io::Write;
use rustc_mir::pretty::write_mir_named;
let mut w: &mut Write = &mut out;
write_mir_named(tcx, "boo_attempt_move_out_of_slice", mir, &mut w, None).unwrap();
}
String::from_utf8(out).unwrap()
});
//
// this should be removed soon.
debug!("encountered Rvalue::Slice as RHS of Assign, source: {:?}",
source);
}
}
}
}
}
debug!("gather_moves({:?})", bb_data.terminator());
match bb_data.terminator().kind {
TerminatorKind::Goto { target: _ } | TerminatorKind::Resume => { }
@ -642,7 +653,6 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
TerminatorKind::If { ref cond, targets: _ } => {
let source = Location { block: bb,
index: bb_data.statements.len() };
debug!("gather_moves If on_operand {:?} {:?}", cond, source);
bb_ctxt.on_operand(SK::If, cond, source);
}
@ -658,10 +668,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
TerminatorKind::Drop { value: ref lval, target: _, unwind: _ } => {
let source = Location { block: bb,
index: bb_data.statements.len() };
debug!("gather_moves Drop on_move_out_lval {:?} {:?}", lval, source);
bb_ctxt.on_move_out_lval(SK::Drop, lval, source);
}
TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
let source = Location { block: bb,
index: bb_data.statements.len() };
@ -727,7 +735,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
}
struct BlockContext<'b, 'a: 'b, 'tcx: 'a> {
tcx: TyCtxt<'b, 'tcx, 'tcx>,
_tcx: TyCtxt<'b, 'tcx, 'tcx>,
moves: &'b mut Vec<MoveOut>,
builder: MovePathDataBuilder<'a, 'tcx>,
path_map: &'b mut Vec<Vec<MoveOutIndex>>,
@ -739,7 +747,6 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
stmt_kind: StmtKind,
lval: &Lvalue<'tcx>,
source: Location) {
let tcx = self.tcx;
let i = source.index;
let index = MoveOutIndex::new(self.moves.len());
@ -774,13 +781,3 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
}
}
}
impl<'tcx> BitDenotation for MoveData<'tcx>{
type Bit = MoveOut;
fn bits_per_block(&self) -> usize {
self.moves.len()
}
fn interpret(&self, idx: usize) -> &Self::Bit {
&self.moves[idx]
}
}

View File

@ -10,24 +10,49 @@
use borrowck::BorrowckCtxt;
use syntax::ast;
use syntax::codemap::Span;
use syntax::ast::{self, MetaItem};
use syntax::attr::AttrMetaMethods;
use syntax::codemap::{Span, DUMMY_SP};
use syntax::ptr::P;
use rustc::hir;
use rustc::hir::intravisit::{FnKind};
use rustc::mir::repr;
use rustc::mir::repr::{BasicBlock, BasicBlockData, Mir, Statement, Terminator};
use rustc::session::Session;
use rustc::ty::{self, TyCtxt};
mod abs_domain;
mod dataflow;
mod gather_moves;
mod graphviz;
// mod graphviz;
use self::dataflow::{Dataflow, DataflowState};
use self::gather_moves::{MoveData};
use self::dataflow::{BitDenotation};
use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults};
use self::dataflow::{HasMoveData};
use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
use self::gather_moves::{MoveData, MovePathIndex, Location};
use self::gather_moves::{MovePathContent};
pub fn borrowck_mir<'b, 'a: 'b, 'tcx: 'a>(
bcx: &'b mut BorrowckCtxt<'a, 'tcx>,
use std::fmt::Debug;
fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<P<MetaItem>> {
for attr in attrs {
if attr.check_name("rustc_mir") {
let items = attr.meta_item_list();
for item in items.iter().flat_map(|l| l.iter()) {
if item.check_name(name) {
return Some(item.clone())
}
}
}
}
return None;
}
pub fn borrowck_mir<'a, 'tcx: 'a>(
bcx: &mut BorrowckCtxt<'a, 'tcx>,
fk: FnKind,
_decl: &hir::FnDecl,
mir: &'a Mir<'tcx>,
@ -45,29 +70,89 @@ pub fn borrowck_mir<'b, 'a: 'b, 'tcx: 'a>(
}
}
let tcx = bcx.tcx;
let move_data = MoveData::gather_moves(mir, tcx);
let ctxt = (tcx, mir, move_data);
let (ctxt, flow_inits) =
do_dataflow(bcx, mir, id, attributes, ctxt, MaybeInitializedLvals::default());
let ((_, _, move_data), flow_uninits) =
do_dataflow(bcx, mir, id, attributes, ctxt, MaybeUninitializedLvals::default());
let mut mbcx = MirBorrowckCtxt {
flow_state: DataflowState::new_move_analysis(mir, bcx.tcx),
bcx: bcx,
mir: mir,
node_id: id,
attributes: attributes,
move_data: move_data,
flow_inits: flow_inits,
flow_uninits: flow_uninits,
};
for bb in mir.all_basic_blocks() {
mbcx.process_basic_block(bb);
}
mbcx.dataflow();
debug!("borrowck_mir done");
}
fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
node_id: ast::NodeId,
attributes: &[ast::Attribute],
ctxt: BD::Ctxt,
bd: BD) -> (BD::Ctxt, DataflowResults<BD>)
where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx>
{
use syntax::attr::AttrMetaMethods;
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 {
sess.span_err(
item.span,
&format!("{} attribute requires a path", item.name()));
return None;
}
}
return None;
};
let print_preflow_to =
name_found(tcx.sess, attributes, "borrowck_graphviz_preflow");
let print_postflow_to =
name_found(tcx.sess, attributes, "borrowck_graphviz_postflow");
let mut mbcx = MirBorrowckCtxtPreDataflow {
node_id: node_id,
print_preflow_to: print_preflow_to,
print_postflow_to: print_postflow_to,
flow_state: DataflowAnalysis::new(tcx, mir, ctxt, bd),
};
mbcx.dataflow();
mbcx.flow_state.results()
}
pub struct MirBorrowckCtxtPreDataflow<'a, 'tcx: 'a, BD>
where BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx>
{
node_id: ast::NodeId,
flow_state: DataflowAnalysis<'a, 'tcx, BD>,
print_preflow_to: Option<String>,
print_postflow_to: Option<String>,
}
#[allow(dead_code)]
pub struct MirBorrowckCtxt<'b, 'a: 'b, 'tcx: 'a> {
bcx: &'b mut BorrowckCtxt<'a, 'tcx>,
mir: &'b Mir<'tcx>,
node_id: ast::NodeId,
attributes: &'b [ast::Attribute],
flow_state: DataflowState<MoveData<'tcx>>,
move_data: MoveData<'tcx>,
flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx>>,
flow_uninits: DataflowResults<MaybeUninitializedLvals<'a, 'tcx>>
}
impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
@ -89,3 +174,127 @@ impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
debug!("MirBorrowckCtxt::process_terminator({:?}, {:?})", bb, term);
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum DropFlagState {
Live,
Dead
}
fn on_all_children_bits<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
move_path_index: MovePathIndex,
mut each_child: F)
where F: FnMut(MovePathIndex)
{
fn is_terminal_path<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
path: MovePathIndex) -> bool
{
match move_data.move_paths[path].content {
MovePathContent::Lvalue(ref lvalue) => {
match mir.lvalue_ty(tcx, lvalue).to_ty(tcx).sty {
// don't trace paths past arrays, slices, and
// pointers. They can only be accessed while
// their parents are initialized.
//
// FIXME: we have to do something for moving
// slice patterns.
ty::TyArray(..) | ty::TySlice(..) |
ty::TyRef(..) | ty::TyRawPtr(..) => true,
_ => false
}
}
_ => true
}
}
fn on_all_children_bits<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
move_path_index: MovePathIndex,
each_child: &mut F)
where F: FnMut(MovePathIndex)
{
each_child(move_path_index);
if is_terminal_path(tcx, mir, move_data, move_path_index) {
return
}
let mut next_child_index = move_data.move_paths[move_path_index].first_child;
while let Some(child_index) = next_child_index {
on_all_children_bits(tcx, mir, move_data, child_index, each_child);
next_child_index = move_data.move_paths[child_index].next_sibling;
}
}
on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child);
}
fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
mut callback: F)
where F: FnMut(MovePathIndex, DropFlagState)
{
for i in 0..(mir.arg_decls.len() as u32) {
let lvalue = repr::Lvalue::Arg(i);
let move_path_index = move_data.rev_lookup.find(&lvalue);
on_all_children_bits(tcx, mir, move_data,
move_path_index,
|moi| callback(moi, DropFlagState::Live));
}
}
fn drop_flag_effects_for_location<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
loc: Location,
mut callback: F)
where F: FnMut(MovePathIndex, DropFlagState)
{
debug!("drop_flag_effects_for_location({:?})", loc);
// first, move out of the RHS
for mi in &move_data.loc_map[loc] {
let path = mi.move_path_index(move_data);
debug!("moving out of path {:?}", move_data.move_paths[path]);
// don't move out of non-Copy things
if let MovePathContent::Lvalue(ref lvalue) = move_data.move_paths[path].content {
let ty = mir.lvalue_ty(tcx, lvalue).to_ty(tcx);
let empty_param_env = tcx.empty_parameter_environment();
if !ty.moves_by_default(tcx, &empty_param_env, DUMMY_SP) {
continue;
}
}
on_all_children_bits(tcx, mir, move_data,
path,
|moi| callback(moi, DropFlagState::Dead))
}
let bb = mir.basic_block_data(loc.block);
match bb.statements.get(loc.index) {
Some(stmt) => match stmt.kind {
repr::StatementKind::Assign(ref lvalue, _) => {
debug!("drop_flag_effects: assignment {:?}", stmt);
on_all_children_bits(tcx, mir, move_data,
move_data.rev_lookup.find(lvalue),
|moi| callback(moi, DropFlagState::Live))
}
},
None => {
// terminator - no move-ins except for function return edge
let term = bb.terminator();
debug!("drop_flag_effects: terminator {:?}", term);
}
}
}