Auto merge of #24330 - pnkfelix:issue-24267, r=nikomatsakis

Extend rustc::middle::dataflow to allow filtering kills from flow-exits.

Fix borrowck analysis so that it will not treat a break that pops through an assignment
```rust
x = { ... break; ... }
```
as a kill of the "moved-out" bit for `x`.

Fix #24267.

[breaking-change], but really, its only breaking code that was already buggy.
This commit is contained in:
bors 2015-04-15 21:05:16 +00:00
commit 07f807d01f
4 changed files with 108 additions and 23 deletions

View file

@ -64,8 +64,14 @@ pub struct DataFlowContext<'a, 'tcx: 'a, O> {
/// bits generated as we exit the cfg node. Updated by `add_gen()`.
gens: Vec<usize>,
/// bits killed as we exit the cfg node. Updated by `add_kill()`.
kills: Vec<usize>,
/// bits killed as we exit the cfg node, or non-locally jump over
/// it. Updated by `add_kill(KillFrom::ScopeEnd)`.
scope_kills: Vec<usize>,
/// bits killed as we exit the cfg node directly; if it is jumped
/// over, e.g. via `break`, the kills are not reflected in the
/// jump's effects. Updated by `add_kill(KillFrom::Execution)`.
action_kills: Vec<usize>,
/// bits that are valid on entry to the cfg node. Updated by
/// `propagate()`.
@ -130,15 +136,23 @@ impl<'a, 'tcx, O:DataFlowOperator> pprust::PpAnn for DataFlowContext<'a, 'tcx, O
"".to_string()
};
let kills = &self.kills[start .. end];
let kills_str = if kills.iter().any(|&u| u != 0) {
format!(" kill: {}", bits_to_string(kills))
let action_kills = &self.action_kills[start .. end];
let action_kills_str = if action_kills.iter().any(|&u| u != 0) {
format!(" action_kill: {}", bits_to_string(action_kills))
} else {
"".to_string()
};
try!(ps.synth_comment(format!("id {}: {}{}{}", id, entry_str,
gens_str, kills_str)));
let scope_kills = &self.scope_kills[start .. end];
let scope_kills_str = if scope_kills.iter().any(|&u| u != 0) {
format!(" scope_kill: {}", bits_to_string(scope_kills))
} else {
"".to_string()
};
try!(ps.synth_comment(
format!("id {}: {}{}{}{}", id, entry_str,
gens_str, action_kills_str, scope_kills_str)));
try!(pp::space(&mut ps.s));
}
Ok(())
@ -187,6 +201,25 @@ fn build_nodeid_to_index(decl: Option<&ast::FnDecl>,
}
}
/// Flag used by `add_kill` to indicate whether the provided kill
/// takes effect only when control flows directly through the node in
/// question, or if the kill's effect is associated with any
/// control-flow directly through or indirectly over the node.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum KillFrom {
/// A `ScopeEnd` kill is one that takes effect when any control
/// flow goes over the node. A kill associated with the end of the
/// scope of a variable declaration `let x;` is an example of a
/// `ScopeEnd` kill.
ScopeEnd,
/// An `Execution` kill is one that takes effect only when control
/// flow goes through the node to completion. A kill associated
/// with an assignment statement `x = expr;` is an example of an
/// `Execution` kill.
Execution,
}
impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
pub fn new(tcx: &'a ty::ctxt<'tcx>,
analysis_name: &'static str,
@ -206,8 +239,10 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
let entry = if oper.initial_value() { usize::MAX } else {0};
let gens: Vec<_> = repeat(0).take(num_nodes * words_per_id).collect();
let kills: Vec<_> = repeat(0).take(num_nodes * words_per_id).collect();
let zeroes: Vec<_> = repeat(0).take(num_nodes * words_per_id).collect();
let gens: Vec<_> = zeroes.clone();
let kills1: Vec<_> = zeroes.clone();
let kills2: Vec<_> = zeroes;
let on_entry: Vec<_> = repeat(entry).take(num_nodes * words_per_id).collect();
let nodeid_to_index = build_nodeid_to_index(decl, cfg);
@ -220,7 +255,8 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
bits_per_id: bits_per_id,
oper: oper,
gens: gens,
kills: kills,
action_kills: kills1,
scope_kills: kills2,
on_entry: on_entry
}
}
@ -240,7 +276,7 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
}
}
pub fn add_kill(&mut self, id: ast::NodeId, bit: usize) {
pub fn add_kill(&mut self, kind: KillFrom, id: ast::NodeId, bit: usize) {
//! Indicates that `id` kills `bit`
debug!("{} add_kill(id={}, bit={})",
self.analysis_name, id, bit);
@ -250,7 +286,10 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
let indices = get_cfg_indices(id, &self.nodeid_to_index);
for &cfgidx in indices {
let (start, end) = self.compute_id_range(cfgidx);
let kills = &mut self.kills[start.. end];
let kills = match kind {
KillFrom::Execution => &mut self.action_kills[start.. end],
KillFrom::ScopeEnd => &mut self.scope_kills[start.. end],
};
set_bit(kills, bit);
}
}
@ -264,7 +303,9 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
let (start, end) = self.compute_id_range(cfgidx);
let gens = &self.gens[start.. end];
bitwise(bits, gens, &Union);
let kills = &self.kills[start.. end];
let kills = &self.action_kills[start.. end];
bitwise(bits, kills, &Subtract);
let kills = &self.scope_kills[start.. end];
bitwise(bits, kills, &Subtract);
debug!("{} apply_gen_kill(cfgidx={:?}, bits={}) [after]",
@ -278,7 +319,8 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
assert!(start < self.gens.len());
assert!(end <= self.gens.len());
assert!(self.gens.len() == self.kills.len());
assert!(self.gens.len() == self.action_kills.len());
assert!(self.gens.len() == self.scope_kills.len());
assert!(self.gens.len() == self.on_entry.len());
(start, end)
@ -412,7 +454,7 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
cfg.graph.each_edge(|_edge_index, edge| {
let flow_exit = edge.source();
let (start, end) = self.compute_id_range(flow_exit);
let mut orig_kills = self.kills[start.. end].to_vec();
let mut orig_kills = self.scope_kills[start.. end].to_vec();
let mut changed = false;
for &node_id in &edge.data.exiting_scopes {
@ -421,8 +463,12 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
Some(indices) => {
for &cfg_idx in indices {
let (start, end) = self.compute_id_range(cfg_idx);
let kills = &self.kills[start.. end];
let kills = &self.scope_kills[start.. end];
if bitwise(&mut orig_kills, kills, &Union) {
debug!("scope exits: scope id={} \
(node={:?} of {:?}) added killset: {}",
node_id, cfg_idx, indices,
bits_to_string(kills));
changed = true;
}
}
@ -436,7 +482,7 @@ impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
}
if changed {
let bits = &mut self.kills[start.. end];
let bits = &mut self.scope_kills[start.. end];
debug!("{} add_kills_from_flow_exits flow_exit={:?} bits={} [before]",
self.analysis_name, flow_exit, mut_bits_to_string(bits));
bits.clone_from_slice(&orig_kills[..]);