mirror of
https://github.com/rust-lang/rust
synced 2024-10-14 04:23:37 +00:00
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:
commit
d7df353377
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
25
src/test/compile-fail/move-in-guard-1.rs
Normal file
25
src/test/compile-fail/move-in-guard-1.rs
Normal 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 }
|
25
src/test/compile-fail/move-in-guard-2.rs
Normal file
25
src/test/compile-fail/move-in-guard-2.rs
Normal 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 }
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
25
src/test/run-pass/move-guard-const.rs
Normal file
25
src/test/run-pass/move-guard-const.rs
Normal 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 }
|
Loading…
Reference in a new issue