Rollup merge of #22580 - pnkfelix:guard-pat-cfg2, r=pnkfelix

aatch's cfg revisions, namely to match expressions

Revise handling of match expressions so that arms branch to next arm.

Update the graphviz tests accordingly.

Fixes #22073. (Includes regression test for the issue.)
This commit is contained in:
Manish Goregaokar 2015-02-24 07:17:20 +05:30
commit d7df353377
14 changed files with 353 additions and 166 deletions

View file

@ -1856,7 +1856,7 @@ fn check_fn(&mut self, cx: &Context, fn_kind: visit::FnKind, _: &ast::FnDecl,
continue
}
visited.insert(cfg_id);
let node_id = cfg.graph.node_data(idx).id;
let node_id = cfg.graph.node_data(idx).id();
// is this a recursive call?
if node_id != ast::DUMMY_NODE_ID && checker(cx.tcx, impl_node_id, id, name, node_id) {

View file

@ -11,16 +11,15 @@
use middle::cfg::*;
use middle::def;
use middle::graph;
use middle::pat_util;
use middle::region::CodeExtent;
use middle::ty;
use syntax::ast;
use syntax::ast_util;
use syntax::ptr::P;
use util::nodemap::NodeMap;
struct CFGBuilder<'a, 'tcx: 'a> {
tcx: &'a ty::ctxt<'tcx>,
exit_map: NodeMap<CFGIndex>,
graph: CFGGraph,
fn_exit: CFGIndex,
loop_scopes: Vec<LoopScope>,
@ -36,17 +35,16 @@ struct LoopScope {
pub fn construct(tcx: &ty::ctxt,
blk: &ast::Block) -> CFG {
let mut graph = graph::Graph::new();
let entry = add_initial_dummy_node(&mut graph);
let entry = graph.add_node(CFGNodeData::Entry);
// `fn_exit` is target of return exprs, which lies somewhere
// outside input `blk`. (Distinguishing `fn_exit` and `block_exit`
// also resolves chicken-and-egg problem that arises if you try to
// have return exprs jump to `block_exit` during construction.)
let fn_exit = add_initial_dummy_node(&mut graph);
let fn_exit = graph.add_node(CFGNodeData::Exit);
let block_exit;
let mut cfg_builder = CFGBuilder {
exit_map: NodeMap(),
graph: graph,
fn_exit: fn_exit,
tcx: tcx,
@ -54,17 +52,12 @@ pub fn construct(tcx: &ty::ctxt,
};
block_exit = cfg_builder.block(blk, entry);
cfg_builder.add_contained_edge(block_exit, fn_exit);
let CFGBuilder {exit_map, graph, ..} = cfg_builder;
CFG {exit_map: exit_map,
graph: graph,
let CFGBuilder {graph, ..} = cfg_builder;
CFG {graph: graph,
entry: entry,
exit: fn_exit}
}
fn add_initial_dummy_node(g: &mut CFGGraph) -> CFGIndex {
g.add_node(CFGNodeData { id: ast::DUMMY_NODE_ID })
}
impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
fn block(&mut self, blk: &ast::Block, pred: CFGIndex) -> CFGIndex {
let mut stmts_exit = pred;
@ -74,19 +67,19 @@ fn block(&mut self, blk: &ast::Block, pred: CFGIndex) -> CFGIndex {
let expr_exit = self.opt_expr(&blk.expr, stmts_exit);
self.add_node(blk.id, &[expr_exit])
self.add_ast_node(blk.id, &[expr_exit])
}
fn stmt(&mut self, stmt: &ast::Stmt, pred: CFGIndex) -> CFGIndex {
match stmt.node {
ast::StmtDecl(ref decl, id) => {
let exit = self.decl(&**decl, pred);
self.add_node(id, &[exit])
self.add_ast_node(id, &[exit])
}
ast::StmtExpr(ref expr, id) | ast::StmtSemi(ref expr, id) => {
let exit = self.expr(&**expr, pred);
self.add_node(id, &[exit])
self.add_ast_node(id, &[exit])
}
ast::StmtMac(..) => {
@ -115,33 +108,33 @@ fn pat(&mut self, pat: &ast::Pat, pred: CFGIndex) -> CFGIndex {
ast::PatLit(..) |
ast::PatRange(..) |
ast::PatWild(_) => {
self.add_node(pat.id, &[pred])
self.add_ast_node(pat.id, &[pred])
}
ast::PatBox(ref subpat) |
ast::PatRegion(ref subpat, _) |
ast::PatIdent(_, _, Some(ref subpat)) => {
let subpat_exit = self.pat(&**subpat, pred);
self.add_node(pat.id, &[subpat_exit])
self.add_ast_node(pat.id, &[subpat_exit])
}
ast::PatEnum(_, Some(ref subpats)) |
ast::PatTup(ref subpats) => {
let pats_exit = self.pats_all(subpats.iter(), pred);
self.add_node(pat.id, &[pats_exit])
self.add_ast_node(pat.id, &[pats_exit])
}
ast::PatStruct(_, ref subpats, _) => {
let pats_exit =
self.pats_all(subpats.iter().map(|f| &f.node.pat), pred);
self.add_node(pat.id, &[pats_exit])
self.add_ast_node(pat.id, &[pats_exit])
}
ast::PatVec(ref pre, ref vec, ref post) => {
let pre_exit = self.pats_all(pre.iter(), pred);
let vec_exit = self.pats_all(vec.iter(), pre_exit);
let post_exit = self.pats_all(post.iter(), vec_exit);
self.add_node(pat.id, &[post_exit])
self.add_ast_node(pat.id, &[post_exit])
}
ast::PatMac(_) => {
@ -157,28 +150,11 @@ fn pats_all<'b, I: Iterator<Item=&'b P<ast::Pat>>>(&mut self,
pats.fold(pred, |pred, pat| self.pat(&**pat, pred))
}
fn pats_any(&mut self,
pats: &[P<ast::Pat>],
pred: CFGIndex) -> CFGIndex {
//! Handles case where just one of the patterns must match.
if pats.len() == 1 {
self.pat(&*pats[0], pred)
} else {
let collect = self.add_dummy_node(&[]);
for pat in pats {
let pat_exit = self.pat(&**pat, pred);
self.add_contained_edge(pat_exit, collect);
}
collect
}
}
fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
match expr.node {
ast::ExprBlock(ref blk) => {
let blk_exit = self.block(&**blk, pred);
self.add_node(expr.id, &[blk_exit])
self.add_ast_node(expr.id, &[blk_exit])
}
ast::ExprIf(ref cond, ref then, None) => {
@ -198,7 +174,7 @@ fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
//
let cond_exit = self.expr(&**cond, pred); // 1
let then_exit = self.block(&**then, cond_exit); // 2
self.add_node(expr.id, &[cond_exit, then_exit]) // 3,4
self.add_ast_node(expr.id, &[cond_exit, then_exit]) // 3,4
}
ast::ExprIf(ref cond, ref then, Some(ref otherwise)) => {
@ -219,7 +195,7 @@ fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
let cond_exit = self.expr(&**cond, pred); // 1
let then_exit = self.block(&**then, cond_exit); // 2
let else_exit = self.expr(&**otherwise, cond_exit); // 3
self.add_node(expr.id, &[then_exit, else_exit]) // 4, 5
self.add_ast_node(expr.id, &[then_exit, else_exit]) // 4, 5
}
ast::ExprIfLet(..) => {
@ -247,7 +223,7 @@ fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
// Is the condition considered part of the loop?
let loopback = self.add_dummy_node(&[pred]); // 1
let cond_exit = self.expr(&**cond, loopback); // 2
let expr_exit = self.add_node(expr.id, &[cond_exit]); // 3
let expr_exit = self.add_ast_node(expr.id, &[cond_exit]); // 3
self.loop_scopes.push(LoopScope {
loop_id: expr.id,
continue_index: loopback,
@ -283,7 +259,7 @@ fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
// may cause additional edges.
let loopback = self.add_dummy_node(&[pred]); // 1
let expr_exit = self.add_node(expr.id, &[]); // 2
let expr_exit = self.add_ast_node(expr.id, &[]); // 2
self.loop_scopes.push(LoopScope {
loop_id: expr.id,
continue_index: loopback,
@ -296,45 +272,7 @@ fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
}
ast::ExprMatch(ref discr, ref arms, _) => {
//
// [pred]
// |
// v 1
// [discr]
// |
// v 2
// [cond1]
// / \
// | \
// v 3 \
// [pat1] \
// | |
// v 4 |
// [guard1] |
// | |
// | |
// v 5 v
// [body1] [cond2]
// | / \
// | ... ...
// | | |
// v 6 v v
// [.....expr.....]
//
let discr_exit = self.expr(&**discr, pred); // 1
let expr_exit = self.add_node(expr.id, &[]);
let mut cond_exit = discr_exit;
for arm in arms {
cond_exit = self.add_dummy_node(&[cond_exit]); // 2
let pats_exit = self.pats_any(&arm.pats,
cond_exit); // 3
let guard_exit = self.opt_expr(&arm.guard,
pats_exit); // 4
let body_exit = self.expr(&*arm.body, guard_exit); // 5
self.add_contained_edge(body_exit, expr_exit); // 6
}
expr_exit
self.match_(expr.id, &discr, &arms, pred)
}
ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op.node) => {
@ -354,30 +292,30 @@ fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
//
let l_exit = self.expr(&**l, pred); // 1
let r_exit = self.expr(&**r, l_exit); // 2
self.add_node(expr.id, &[l_exit, r_exit]) // 3,4
self.add_ast_node(expr.id, &[l_exit, r_exit]) // 3,4
}
ast::ExprRet(ref v) => {
let v_exit = self.opt_expr(v, pred);
let b = self.add_node(expr.id, &[v_exit]);
let b = self.add_ast_node(expr.id, &[v_exit]);
self.add_returning_edge(expr, b);
self.add_node(ast::DUMMY_NODE_ID, &[])
self.add_unreachable_node()
}
ast::ExprBreak(label) => {
let loop_scope = self.find_scope(expr, label);
let b = self.add_node(expr.id, &[pred]);
let b = self.add_ast_node(expr.id, &[pred]);
self.add_exiting_edge(expr, b,
loop_scope, loop_scope.break_index);
self.add_node(ast::DUMMY_NODE_ID, &[])
self.add_unreachable_node()
}
ast::ExprAgain(label) => {
let loop_scope = self.find_scope(expr, label);
let a = self.add_node(expr.id, &[pred]);
let a = self.add_ast_node(expr.id, &[pred]);
self.add_exiting_edge(expr, a,
loop_scope, loop_scope.continue_index);
self.add_node(ast::DUMMY_NODE_ID, &[])
self.add_unreachable_node()
}
ast::ExprVec(ref elems) => {
@ -454,7 +392,7 @@ fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
let &(_, ref expr, _) = a;
&**expr
}), post_inputs);
self.add_node(expr.id, &[post_outputs])
self.add_ast_node(expr.id, &[post_outputs])
}
ast::ExprMac(..) |
@ -481,7 +419,7 @@ fn call<'b, I: Iterator<Item=&'b ast::Expr>>(&mut self,
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
if return_ty.diverges() {
self.add_node(ast::DUMMY_NODE_ID, &[])
self.add_unreachable_node()
} else {
ret
}
@ -508,20 +446,126 @@ fn straightline<'b, I: Iterator<Item=&'b ast::Expr>>(&mut self,
//! Handles case of an expression that evaluates `subexprs` in order
let subexprs_exit = self.exprs(subexprs, pred);
self.add_node(expr.id, &[subexprs_exit])
self.add_ast_node(expr.id, &[subexprs_exit])
}
fn match_(&mut self, id: ast::NodeId, discr: &ast::Expr,
arms: &[ast::Arm], pred: CFGIndex) -> CFGIndex {
// The CFG for match expression is quite complex, so no ASCII
// art for it (yet).
//
// The CFG generated below matches roughly what trans puts
// out. Each pattern and guard is visited in parallel, with
// arms containing multiple patterns generating multiple nodes
// for the same guard expression. The guard expressions chain
// into each other from top to bottom, with a specific
// exception to allow some additional valid programs
// (explained below). Trans differs slightly in that the
// pattern matching may continue after a guard but the visible
// behaviour should be the same.
//
// What is going on is explained in further comments.
// Visit the discriminant expression
let discr_exit = self.expr(discr, pred);
// Add a node for the exit of the match expression as a whole.
let expr_exit = self.add_ast_node(id, &[]);
// Keep track of the previous guard expressions
let mut prev_guards = Vec::new();
// Track if the previous pattern contained bindings or wildcards
let mut prev_has_bindings = false;
for arm in arms {
// Add an exit node for when we've visited all the
// patterns and the guard (if there is one) in the arm.
let arm_exit = self.add_dummy_node(&[]);
for pat in &arm.pats {
// Visit the pattern, coming from the discriminant exit
let mut pat_exit = self.pat(&**pat, discr_exit);
// If there is a guard expression, handle it here
if let Some(ref guard) = arm.guard {
// Add a dummy node for the previous guard
// expression to target
let guard_start = self.add_dummy_node(&[pat_exit]);
// Visit the guard expression
let guard_exit = self.expr(&**guard, guard_start);
let this_has_bindings = pat_util::pat_contains_bindings_or_wild(
&self.tcx.def_map, &**pat);
// If both this pattern and the previous pattern
// were free of bindings, they must consist only
// of "constant" patterns. Note we cannot match an
// all-constant pattern, fail the guard, and then
// match *another* all-constant pattern. This is
// because if the previous pattern matches, then
// we *cannot* match this one, unless all the
// constants are the same (which is rejected by
// `check_match`).
//
// We can use this to be smarter about the flow
// along guards. If the previous pattern matched,
// then we know we will not visit the guard in
// this one (whether or not the guard succeeded),
// if the previous pattern failed, then we know
// the guard for that pattern will not have been
// visited. Thus, it is not possible to visit both
// the previous guard and the current one when
// both patterns consist only of constant
// sub-patterns.
//
// However, if the above does not hold, then all
// previous guards need to be wired to visit the
// current guard pattern.
if prev_has_bindings || this_has_bindings {
while let Some(prev) = prev_guards.pop() {
self.add_contained_edge(prev, guard_start);
}
}
prev_has_bindings = this_has_bindings;
// Push the guard onto the list of previous guards
prev_guards.push(guard_exit);
// Update the exit node for the pattern
pat_exit = guard_exit;
}
// Add an edge from the exit of this pattern to the
// exit of the arm
self.add_contained_edge(pat_exit, arm_exit);
}
// Visit the body of this arm
let body_exit = self.expr(&arm.body, arm_exit);
// Link the body to the exit of the expression
self.add_contained_edge(body_exit, expr_exit);
}
expr_exit
}
fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex {
self.add_node(ast::DUMMY_NODE_ID, preds)
self.add_node(CFGNodeData::Dummy, preds)
}
fn add_node(&mut self, id: ast::NodeId, preds: &[CFGIndex]) -> CFGIndex {
assert!(!self.exit_map.contains_key(&id));
let node = self.graph.add_node(CFGNodeData {id: id});
if id != ast::DUMMY_NODE_ID {
assert!(!self.exit_map.contains_key(&id));
self.exit_map.insert(id, node);
}
fn add_ast_node(&mut self, id: ast::NodeId, preds: &[CFGIndex]) -> CFGIndex {
assert!(id != ast::DUMMY_NODE_ID);
self.add_node(CFGNodeData::AST(id), preds)
}
fn add_unreachable_node(&mut self) -> CFGIndex {
self.add_node(CFGNodeData::Unreachable, &[])
}
fn add_node(&mut self, data: CFGNodeData, preds: &[CFGIndex]) -> CFGIndex {
let node = self.graph.add_node(data);
for &pred in preds {
self.add_contained_edge(pred, node);
}

View file

@ -65,10 +65,10 @@ fn node_label(&'a self, &(i, n): &Node<'a>) -> dot::LabelText<'a> {
dot::LabelText::LabelStr("entry".into_cow())
} else if i == self.cfg.exit {
dot::LabelText::LabelStr("exit".into_cow())
} else if n.data.id == ast::DUMMY_NODE_ID {
} else if n.data.id() == ast::DUMMY_NODE_ID {
dot::LabelText::LabelStr("(dummy_node)".into_cow())
} else {
let s = self.ast_map.node_to_string(n.data.id);
let s = self.ast_map.node_to_string(n.data.id());
// left-aligns the lines
let s = replace_newline_with_backslash_l(s);
dot::LabelText::EscStr(s.into_cow())

View file

@ -14,21 +14,33 @@
use middle::graph;
use middle::ty;
use syntax::ast;
use util::nodemap::NodeMap;
mod construct;
pub mod graphviz;
pub struct CFG {
pub exit_map: NodeMap<CFGIndex>,
pub graph: CFGGraph,
pub entry: CFGIndex,
pub exit: CFGIndex,
}
#[derive(Copy)]
pub struct CFGNodeData {
pub id: ast::NodeId
#[derive(Copy, PartialEq)]
pub enum CFGNodeData {
AST(ast::NodeId),
Entry,
Exit,
Dummy,
Unreachable,
}
impl CFGNodeData {
pub fn id(&self) -> ast::NodeId {
if let CFGNodeData::AST(id) = *self {
id
} else {
ast::DUMMY_NODE_ID
}
}
}
pub struct CFGEdgeData {
@ -50,6 +62,6 @@ pub fn new(tcx: &ty::ctxt,
}
pub fn node_is_reachable(&self, id: ast::NodeId) -> bool {
self.graph.depth_traverse(self.entry).any(|node| node.id == id)
self.graph.depth_traverse(self.entry).any(|node| node.id() == id)
}
}

View file

@ -53,7 +53,7 @@ pub struct DataFlowContext<'a, 'tcx: 'a, O> {
// mapping from node to cfg node index
// FIXME (#6298): Shouldn't this go with CFG?
nodeid_to_index: NodeMap<CFGIndex>,
nodeid_to_index: NodeMap<Vec<CFGIndex>>,
// Bit sets per cfg node. The following three fields (`gens`, `kills`,
// and `on_entry`) all have the same structure. For each id in
@ -88,11 +88,9 @@ struct PropagationContext<'a, 'b: 'a, 'tcx: 'b, O: 'a> {
changed: bool
}
fn to_cfgidx_or_die(id: ast::NodeId, index: &NodeMap<CFGIndex>) -> CFGIndex {
let opt_cfgindex = index.get(&id).cloned();
opt_cfgindex.unwrap_or_else(|| {
panic!("nodeid_to_index does not have entry for NodeId {}", id);
})
fn get_cfg_indices<'a>(id: ast::NodeId, index: &'a NodeMap<Vec<CFGIndex>>) -> &'a [CFGIndex] {
let opt_indices = index.get(&id);
opt_indices.map(|v| &v[..]).unwrap_or(&[])
}
impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
@ -114,9 +112,13 @@ fn pre(&self,
pprust::NodePat(pat) => pat.id
};
if self.has_bitset_for_nodeid(id) {
assert!(self.bits_per_id > 0);
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
if !self.has_bitset_for_nodeid(id) {
return Ok(());
}
assert!(self.bits_per_id > 0);
let indices = get_cfg_indices(id, &self.nodeid_to_index);
for &cfgidx in indices {
let (start, end) = self.compute_id_range(cfgidx);
let on_entry = &self.on_entry[start.. end];
let entry_str = bits_to_string(on_entry);
@ -144,7 +146,7 @@ fn pre(&self,
}
fn build_nodeid_to_index(decl: Option<&ast::FnDecl>,
cfg: &cfg::CFG) -> NodeMap<CFGIndex> {
cfg: &cfg::CFG) -> NodeMap<Vec<CFGIndex>> {
let mut index = NodeMap();
// FIXME (#6298): Would it be better to fold formals from decl
@ -157,28 +159,38 @@ fn build_nodeid_to_index(decl: Option<&ast::FnDecl>,
}
cfg.graph.each_node(|node_idx, node| {
if node.data.id != ast::DUMMY_NODE_ID {
index.insert(node.data.id, node_idx);
if let cfg::CFGNodeData::AST(id) = node.data {
match index.entry(id).get() {
Ok(v) => v.push(node_idx),
Err(e) => {
e.insert(vec![node_idx]);
}
}
}
true
});
return index;
fn add_entries_from_fn_decl(index: &mut NodeMap<CFGIndex>,
fn add_entries_from_fn_decl(index: &mut NodeMap<Vec<CFGIndex>>,
decl: &ast::FnDecl,
entry: CFGIndex) {
//! add mappings from the ast nodes for the formal bindings to
//! the entry-node in the graph.
struct Formals<'a> {
entry: CFGIndex,
index: &'a mut NodeMap<CFGIndex>,
index: &'a mut NodeMap<Vec<CFGIndex>>,
}
let mut formals = Formals { entry: entry, index: index };
visit::walk_fn_decl(&mut formals, decl);
impl<'a, 'v> visit::Visitor<'v> for Formals<'a> {
fn visit_pat(&mut self, p: &ast::Pat) {
self.index.insert(p.id, self.entry);
match self.index.entry(p.id).get() {
Ok(v) => v.push(self.entry),
Err(e) => {
e.insert(vec![self.entry]);
}
}
visit::walk_pat(self, p)
}
}
@ -230,10 +242,12 @@ pub fn add_gen(&mut self, id: ast::NodeId, bit: uint) {
assert!(self.nodeid_to_index.contains_key(&id));
assert!(self.bits_per_id > 0);
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
let (start, end) = self.compute_id_range(cfgidx);
let gens = &mut self.gens[start.. end];
set_bit(gens, bit);
let indices = get_cfg_indices(id, &self.nodeid_to_index);
for &cfgidx in indices {
let (start, end) = self.compute_id_range(cfgidx);
let gens = &mut self.gens[start.. end];
set_bit(gens, bit);
}
}
pub fn add_kill(&mut self, id: ast::NodeId, bit: uint) {
@ -243,10 +257,12 @@ pub fn add_kill(&mut self, id: ast::NodeId, bit: uint) {
assert!(self.nodeid_to_index.contains_key(&id));
assert!(self.bits_per_id > 0);
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
let (start, end) = self.compute_id_range(cfgidx);
let kills = &mut self.kills[start.. end];
set_bit(kills, bit);
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];
set_bit(kills, bit);
}
}
fn apply_gen_kill(&self, cfgidx: CFGIndex, bits: &mut [uint]) {
@ -279,7 +295,7 @@ fn compute_id_range(&self, cfgidx: CFGIndex) -> (uint, uint) {
}
pub fn each_bit_on_entry<F>(&self, id: ast::NodeId, f: F) -> bool where
pub fn each_bit_on_entry<F>(&self, id: ast::NodeId, mut f: F) -> bool where
F: FnMut(uint) -> bool,
{
//! Iterates through each bit that is set on entry to `id`.
@ -287,8 +303,13 @@ pub fn each_bit_on_entry<F>(&self, id: ast::NodeId, f: F) -> bool where
if !self.has_bitset_for_nodeid(id) {
return true;
}
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
self.each_bit_for_node(Entry, cfgidx, f)
let indices = get_cfg_indices(id, &self.nodeid_to_index);
for &cfgidx in indices {
if !self.each_bit_for_node(Entry, cfgidx, |i| f(i)) {
return false;
}
}
return true;
}
pub fn each_bit_for_node<F>(&self, e: EntryOrExit, cfgidx: CFGIndex, f: F) -> bool where
@ -320,7 +341,7 @@ pub fn each_bit_for_node<F>(&self, e: EntryOrExit, cfgidx: CFGIndex, f: F) -> bo
self.each_bit(slice, f)
}
pub fn each_gen_bit<F>(&self, id: ast::NodeId, f: F) -> bool where
pub fn each_gen_bit<F>(&self, id: ast::NodeId, mut f: F) -> bool where
F: FnMut(uint) -> bool,
{
//! Iterates through each bit in the gen set for `id`.
@ -334,12 +355,17 @@ pub fn each_gen_bit<F>(&self, id: ast::NodeId, f: F) -> bool where
return true;
}
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
let (start, end) = self.compute_id_range(cfgidx);
let gens = &self.gens[start.. end];
debug!("{} each_gen_bit(id={}, gens={})",
self.analysis_name, id, bits_to_string(gens));
self.each_bit(gens, f)
let indices = get_cfg_indices(id, &self.nodeid_to_index);
for &cfgidx in indices {
let (start, end) = self.compute_id_range(cfgidx);
let gens = &self.gens[start.. end];
debug!("{} each_gen_bit(id={}, gens={})",
self.analysis_name, id, bits_to_string(gens));
if !self.each_bit(gens, |i| f(i)) {
return false;
}
}
return true;
}
fn each_bit<F>(&self, words: &[uint], mut f: F) -> bool where
@ -400,13 +426,15 @@ pub fn add_kills_from_flow_exits(&mut self, cfg: &cfg::CFG) {
let mut changed = false;
for &node_id in &edge.data.exiting_scopes {
let opt_cfg_idx = self.nodeid_to_index.get(&node_id).cloned();
let opt_cfg_idx = self.nodeid_to_index.get(&node_id);
match opt_cfg_idx {
Some(cfg_idx) => {
let (start, end) = self.compute_id_range(cfg_idx);
let kills = &self.kills[start.. end];
if bitwise(&mut orig_kills, kills, &Union) {
changed = true;
Some(indices) => {
for &cfg_idx in indices {
let (start, end) = self.compute_id_range(cfg_idx);
let kills = &self.kills[start.. end];
if bitwise(&mut orig_kills, kills, &Union) {
changed = true;
}
}
}
None => {
@ -482,7 +510,7 @@ fn walk_cfg(&mut self,
cfg.graph.each_node(|node_index, node| {
debug!("DataFlowContext::walk_cfg idx={:?} id={} begin in_out={}",
node_index, node.data.id, bits_to_string(in_out));
node_index, node.data.id(), bits_to_string(in_out));
let (start, end) = self.dfcx.compute_id_range(node_index);

View file

@ -119,6 +119,21 @@ pub fn pat_contains_bindings(dm: &DefMap, pat: &ast::Pat) -> bool {
contains_bindings
}
/// Checks if the pattern contains any patterns that bind something to
/// an ident or wildcard, e.g. `foo`, or `Foo(_)`, `foo @ Bar(..)`,
pub fn pat_contains_bindings_or_wild(dm: &DefMap, pat: &ast::Pat) -> bool {
let mut contains_bindings = false;
walk_pat(pat, |p| {
if pat_is_binding_or_wild(dm, p) {
contains_bindings = true;
false // there's at least one binding/wildcard, can short circuit now.
} else {
true
}
});
contains_bindings
}
pub fn simple_identifier<'a>(pat: &'a ast::Pat) -> Option<&'a ast::Ident> {
match pat.node {
ast::PatIdent(ast::BindByValue(_), ref path1, None) => {

View file

@ -612,13 +612,26 @@ pub fn report_use_of_moved_value<'b>(&self,
};
let (suggestion, _) =
move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
self.tcx.sess.span_note(
expr_span,
&format!("`{}` moved here{} because it has type `{}`, which is {}",
ol,
moved_lp_msg,
expr_ty.user_string(self.tcx),
suggestion));
// If the two spans are the same, it's because the expression will be evaluated
// multiple times. Avoid printing the same span and adjust the wording so it makes
// more sense that it's from multiple evalutations.
if expr_span == use_span {
self.tcx.sess.note(
&format!("`{}` was previously moved here{} because it has type `{}`, \
which is {}",
ol,
moved_lp_msg,
expr_ty.user_string(self.tcx),
suggestion));
} else {
self.tcx.sess.span_note(
expr_span,
&format!("`{}` moved here{} because it has type `{}`, which is {}",
ol,
moved_lp_msg,
expr_ty.user_string(self.tcx),
suggestion));
}
}
move_data::MovePat => {

View file

@ -52,7 +52,7 @@ pub struct DataflowLabeller<'a, 'tcx: 'a> {
impl<'a, 'tcx> DataflowLabeller<'a, 'tcx> {
fn dataflow_for(&self, e: EntryOrExit, n: &Node<'a>) -> String {
let id = n.1.data.id;
let id = n.1.data.id();
debug!("dataflow_for({:?}, id={}) {:?}", e, id, self.variants);
let mut sets = "".to_string();
let mut seen_one = false;

View file

@ -1353,7 +1353,7 @@ fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option<cfg::CFG>)
// the clobbering of the existing value in the return slot.
fn has_nested_returns(tcx: &ty::ctxt, cfg: &cfg::CFG, blk_id: ast::NodeId) -> bool {
for n in cfg.graph.depth_traverse(cfg.entry) {
match tcx.map.find(n.id) {
match tcx.map.find(n.id()) {
Some(ast_map::NodeExpr(ex)) => {
if let ast::ExprRet(Some(ref ret_expr)) = ex.node {
let mut visitor = FindNestedReturn::new();

View file

@ -0,0 +1,25 @@
// Copyright 2015 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.
#![feature(box_syntax)]
pub fn main() {
let x = box 1;
let v = (1, 2);
match v {
(1, _) if take(x) => (),
(_, 2) if take(x) => (), //~ ERROR use of moved value: `x`
_ => (),
}
}
fn take<T>(_: T) -> bool { false }

View file

@ -0,0 +1,25 @@
// Copyright 2015 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.
#![feature(box_syntax)]
pub fn main() {
let x = box 1;
let v = (1, 2);
match v {
(1, _) |
(_, 2) if take(x) => (), //~ ERROR use of moved value: `x`
_ => (),
}
}
fn take<T>(_: T) -> bool { false }

View file

@ -22,12 +22,12 @@ digraph block {
N3 -> N4;
N4 -> N5;
N5 -> N6;
N6 -> N8;
N8 -> N9;
N6 -> N9;
N9 -> N10;
N10 -> N11;
N11 -> N12;
N12 -> N13;
N12 -> N8;
N8 -> N13;
N13 -> N14;
N14 -> N15;
N15 -> N7;

View file

@ -32,16 +32,16 @@ digraph block {
N6 -> N7;
N7 -> N8;
N8 -> N9;
N9 -> N11;
N11 -> N12;
N12 -> N13;
N9 -> N12;
N12 -> N11;
N11 -> N13;
N13 -> N14;
N14 -> N15;
N15 -> N10;
N11 -> N16;
N16 -> N17;
N9 -> N17;
N17 -> N18;
N18 -> N19;
N18 -> N16;
N16 -> N19;
N19 -> N20;
N20 -> N21;
N21 -> N22;

View file

@ -0,0 +1,25 @@
// Copyright 2015 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.
#![feature(box_syntax)]
fn main() {
let x = box 1;
let v = (1, 2);
match v {
(2, 1) if take(x) => (),
(1, 2) if take(x) => (),
_ => (),
}
}
fn take<T>(_: T) -> bool { false }