1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
19 use util::nodemap::NodeMap;
21 struct CFGBuilder<'a, 'tcx: 'a> {
22 tcx: &'a ty::ctxt<'tcx>,
23 exit_map: NodeMap<CFGIndex>,
26 loop_scopes: Vec<LoopScope>,
30 loop_id: ast::NodeId, // id of loop/while node
31 continue_index: CFGIndex, // where to go on a `loop`
32 break_index: CFGIndex, // where to go on a `break
35 pub fn construct(tcx: &ty::ctxt,
36 blk: &ast::Block) -> CFG {
37 let mut graph = graph::Graph::new();
38 let entry = add_initial_dummy_node(&mut graph);
40 // `fn_exit` is target of return exprs, which lies somewhere
41 // outside input `blk`. (Distinguishing `fn_exit` and `block_exit`
42 // also resolves chicken-and-egg problem that arises if you try to
43 // have return exprs jump to `block_exit` during construction.)
44 let fn_exit = add_initial_dummy_node(&mut graph);
47 let mut cfg_builder = CFGBuilder {
48 exit_map: NodeMap::new(),
52 loop_scopes: Vec::new()
54 block_exit = cfg_builder.block(blk, entry);
55 cfg_builder.add_contained_edge(block_exit, fn_exit);
56 let CFGBuilder {exit_map, graph, ..} = cfg_builder;
57 CFG {exit_map: exit_map,
63 fn add_initial_dummy_node(g: &mut CFGGraph) -> CFGIndex {
64 g.add_node(CFGNodeData { id: ast::DUMMY_NODE_ID })
67 impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
68 fn block(&mut self, blk: &ast::Block, pred: CFGIndex) -> CFGIndex {
69 let mut stmts_exit = pred;
70 for stmt in blk.stmts.iter() {
71 stmts_exit = self.stmt(&**stmt, stmts_exit);
74 let expr_exit = self.opt_expr(&blk.expr, stmts_exit);
76 self.add_node(blk.id, [expr_exit])
79 fn stmt(&mut self, stmt: &ast::Stmt, pred: CFGIndex) -> CFGIndex {
81 ast::StmtDecl(ref decl, id) => {
82 let exit = self.decl(&**decl, pred);
83 self.add_node(id, [exit])
86 ast::StmtExpr(ref expr, id) | ast::StmtSemi(ref expr, id) => {
87 let exit = self.expr(&**expr, pred);
88 self.add_node(id, [exit])
92 self.tcx.sess.span_bug(stmt.span, "unexpanded macro");
97 fn decl(&mut self, decl: &ast::Decl, pred: CFGIndex) -> CFGIndex {
99 ast::DeclLocal(ref local) => {
100 let init_exit = self.opt_expr(&local.init, pred);
101 self.pat(&*local.pat, init_exit)
104 ast::DeclItem(_) => {
110 fn pat(&mut self, pat: &ast::Pat, pred: CFGIndex) -> CFGIndex {
112 ast::PatIdent(_, _, None) |
113 ast::PatEnum(_, None) |
117 self.add_node(pat.id, [pred])
120 ast::PatBox(ref subpat) |
121 ast::PatRegion(ref subpat) |
122 ast::PatIdent(_, _, Some(ref subpat)) => {
123 let subpat_exit = self.pat(&**subpat, pred);
124 self.add_node(pat.id, [subpat_exit])
127 ast::PatEnum(_, Some(ref subpats)) |
128 ast::PatTup(ref subpats) => {
129 let pats_exit = self.pats_all(subpats.iter(), pred);
130 self.add_node(pat.id, [pats_exit])
133 ast::PatStruct(_, ref subpats, _) => {
135 self.pats_all(subpats.iter().map(|f| &f.pat), pred);
136 self.add_node(pat.id, [pats_exit])
139 ast::PatVec(ref pre, ref vec, ref post) => {
140 let pre_exit = self.pats_all(pre.iter(), pred);
141 let vec_exit = self.pats_all(vec.iter(), pre_exit);
142 let post_exit = self.pats_all(post.iter(), vec_exit);
143 self.add_node(pat.id, [post_exit])
147 self.tcx.sess.span_bug(pat.span, "unexpanded macro");
152 fn pats_all<'a, I: Iterator<&'a P<ast::Pat>>>(&mut self,
154 pred: CFGIndex) -> CFGIndex {
155 //! Handles case where all of the patterns must match.
157 pats.fold(pred, |pred, pat| self.pat(&**pat, pred))
160 fn pats_any(&mut self,
161 pats: &[P<ast::Pat>],
162 pred: CFGIndex) -> CFGIndex {
163 //! Handles case where just one of the patterns must match.
166 self.pat(&*pats[0], pred)
168 let collect = self.add_dummy_node([]);
169 for pat in pats.iter() {
170 let pat_exit = self.pat(&**pat, pred);
171 self.add_contained_edge(pat_exit, collect);
177 fn expr(&mut self, expr: &ast::Expr, pred: CFGIndex) -> CFGIndex {
179 ast::ExprBlock(ref blk) => {
180 let blk_exit = self.block(&**blk, pred);
181 self.add_node(expr.id, [blk_exit])
184 ast::ExprIf(ref cond, ref then, None) => {
199 let cond_exit = self.expr(&**cond, pred); // 1
200 let then_exit = self.block(&**then, cond_exit); // 2
201 self.add_node(expr.id, [cond_exit, then_exit]) // 3,4
204 ast::ExprIf(ref cond, ref then, Some(ref otherwise)) => {
219 let cond_exit = self.expr(&**cond, pred); // 1
220 let then_exit = self.block(&**then, cond_exit); // 2
221 let else_exit = self.expr(&**otherwise, cond_exit); // 3
222 self.add_node(expr.id, [then_exit, else_exit]) // 4, 5
225 ast::ExprWhile(ref cond, ref body, _) => {
240 // Note that `break` and `continue` statements
241 // may cause additional edges.
243 // Is the condition considered part of the loop?
244 let loopback = self.add_dummy_node([pred]); // 1
245 let cond_exit = self.expr(&**cond, loopback); // 2
246 let expr_exit = self.add_node(expr.id, [cond_exit]); // 3
247 self.loop_scopes.push(LoopScope {
249 continue_index: loopback,
250 break_index: expr_exit
252 let body_exit = self.block(&**body, cond_exit); // 4
253 self.add_contained_edge(body_exit, loopback); // 5
254 self.loop_scopes.pop();
258 ast::ExprForLoop(ref pat, ref head, ref body, _) => {
278 // Note that `break` and `continue` statements
279 // may cause additional edges.
281 let head = self.expr(&**head, pred); // 1
282 let loopback = self.add_dummy_node([head]); // 2
283 let cond = self.add_dummy_node([loopback]); // 3
284 let expr_exit = self.add_node(expr.id, [cond]); // 4
285 self.loop_scopes.push(LoopScope {
287 continue_index: loopback,
288 break_index: expr_exit,
290 let pat = self.pat(&**pat, cond); // 5
291 let body = self.block(&**body, pat); // 6
292 self.add_contained_edge(body, loopback); // 7
293 self.loop_scopes.pop();
297 ast::ExprLoop(ref body, _) => {
309 // Note that `break` and `loop` statements
310 // may cause additional edges.
312 let loopback = self.add_dummy_node([pred]); // 1
313 let expr_exit = self.add_node(expr.id, []); // 2
314 self.loop_scopes.push(LoopScope {
316 continue_index: loopback,
317 break_index: expr_exit,
319 let body_exit = self.block(&**body, loopback); // 3
320 self.add_contained_edge(body_exit, loopback); // 4
321 self.loop_scopes.pop();
325 ast::ExprMatch(ref discr, ref arms) => {
351 let discr_exit = self.expr(&**discr, pred); // 1
353 let expr_exit = self.add_node(expr.id, []);
354 let mut cond_exit = discr_exit;
355 for arm in arms.iter() {
356 cond_exit = self.add_dummy_node([cond_exit]); // 2
357 let pats_exit = self.pats_any(arm.pats.as_slice(),
359 let guard_exit = self.opt_expr(&arm.guard,
361 let body_exit = self.expr(&*arm.body, guard_exit); // 5
362 self.add_contained_edge(body_exit, expr_exit); // 6
367 ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => {
382 let l_exit = self.expr(&**l, pred); // 1
383 let r_exit = self.expr(&**r, l_exit); // 2
384 self.add_node(expr.id, [l_exit, r_exit]) // 3,4
387 ast::ExprRet(ref v) => {
388 let v_exit = self.opt_expr(v, pred);
389 let b = self.add_node(expr.id, [v_exit]);
390 self.add_returning_edge(expr, b);
391 self.add_node(ast::DUMMY_NODE_ID, [])
394 ast::ExprBreak(label) => {
395 let loop_scope = self.find_scope(expr, label);
396 let b = self.add_node(expr.id, [pred]);
397 self.add_exiting_edge(expr, b,
398 loop_scope, loop_scope.break_index);
399 self.add_node(ast::DUMMY_NODE_ID, [])
402 ast::ExprAgain(label) => {
403 let loop_scope = self.find_scope(expr, label);
404 let a = self.add_node(expr.id, [pred]);
405 self.add_exiting_edge(expr, a,
406 loop_scope, loop_scope.continue_index);
407 self.add_node(ast::DUMMY_NODE_ID, [])
410 ast::ExprVec(ref elems) => {
411 self.straightline(expr, pred, elems.iter().map(|e| &**e))
414 ast::ExprCall(ref func, ref args) => {
415 self.call(expr, pred, &**func, args.iter().map(|e| &**e))
418 ast::ExprMethodCall(_, _, ref args) => {
419 self.call(expr, pred, &**args.get(0), args.slice_from(1).iter().map(|e| &**e))
422 ast::ExprIndex(ref l, ref r) |
423 ast::ExprBinary(_, ref l, ref r) if self.is_method_call(expr) => {
424 self.call(expr, pred, &**l, Some(&**r).into_iter())
427 ast::ExprSlice(ref base, ref start, ref end, _) => {
431 start.iter().chain(end.iter()).map(|x| &**x))
434 ast::ExprUnary(_, ref e) if self.is_method_call(expr) => {
435 self.call(expr, pred, &**e, None::<ast::Expr>.iter())
438 ast::ExprTup(ref exprs) => {
439 self.straightline(expr, pred, exprs.iter().map(|e| &**e))
442 ast::ExprStruct(_, ref fields, ref base) => {
443 let base_exit = self.opt_expr(base, pred);
444 self.straightline(expr, base_exit, fields.iter().map(|f| &*f.expr))
447 ast::ExprRepeat(ref elem, ref count) => {
448 self.straightline(expr, pred, [elem, count].iter().map(|&e| &**e))
451 ast::ExprAssign(ref l, ref r) |
452 ast::ExprAssignOp(_, ref l, ref r) => {
453 self.straightline(expr, pred, [r, l].iter().map(|&e| &**e))
456 ast::ExprIndex(ref l, ref r) |
457 ast::ExprBinary(_, ref l, ref r) => { // NB: && and || handled earlier
458 self.straightline(expr, pred, [l, r].iter().map(|&e| &**e))
461 ast::ExprBox(ref p, ref e) => {
462 self.straightline(expr, pred, [p, e].iter().map(|&e| &**e))
465 ast::ExprAddrOf(_, ref e) |
466 ast::ExprCast(ref e, _) |
467 ast::ExprUnary(_, ref e) |
468 ast::ExprParen(ref e) |
469 ast::ExprField(ref e, _, _) |
470 ast::ExprTupField(ref e, _, _) => {
471 self.straightline(expr, pred, Some(&**e).into_iter())
474 ast::ExprInlineAsm(ref inline_asm) => {
475 let inputs = inline_asm.inputs.iter();
476 let outputs = inline_asm.outputs.iter();
477 let post_inputs = self.exprs(inputs.map(|a| {
478 debug!("cfg::construct InlineAsm id:{} input:{:?}", expr.id, a);
479 let &(_, ref expr) = a;
482 let post_outputs = self.exprs(outputs.map(|a| {
483 debug!("cfg::construct InlineAsm id:{} output:{:?}", expr.id, a);
484 let &(_, ref expr, _) = a;
487 self.add_node(expr.id, [post_outputs])
491 ast::ExprFnBlock(..) |
493 ast::ExprUnboxedFn(..) |
495 ast::ExprPath(..) => {
496 self.straightline(expr, pred, None::<ast::Expr>.iter())
501 fn call<'a, I: Iterator<&'a ast::Expr>>(&mut self,
502 call_expr: &ast::Expr,
504 func_or_rcvr: &ast::Expr,
505 args: I) -> CFGIndex {
506 let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
507 let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
509 let return_ty = ty::node_id_to_type(self.tcx, call_expr.id);
510 let fails = ty::type_is_bot(return_ty);
512 self.add_node(ast::DUMMY_NODE_ID, [])
518 fn exprs<'a, I: Iterator<&'a ast::Expr>>(&mut self,
520 pred: CFGIndex) -> CFGIndex {
521 //! Constructs graph for `exprs` evaluated in order
522 exprs.fold(pred, |p, e| self.expr(e, p))
525 fn opt_expr(&mut self,
526 opt_expr: &Option<P<ast::Expr>>,
527 pred: CFGIndex) -> CFGIndex {
528 //! Constructs graph for `opt_expr` evaluated, if Some
529 opt_expr.iter().fold(pred, |p, e| self.expr(&**e, p))
532 fn straightline<'a, I: Iterator<&'a ast::Expr>>(&mut self,
535 subexprs: I) -> CFGIndex {
536 //! Handles case of an expression that evaluates `subexprs` in order
538 let subexprs_exit = self.exprs(subexprs, pred);
539 self.add_node(expr.id, [subexprs_exit])
542 fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex {
543 self.add_node(ast::DUMMY_NODE_ID, preds)
546 fn add_node(&mut self, id: ast::NodeId, preds: &[CFGIndex]) -> CFGIndex {
547 assert!(!self.exit_map.contains_key(&id));
548 let node = self.graph.add_node(CFGNodeData {id: id});
549 if id != ast::DUMMY_NODE_ID {
550 assert!(!self.exit_map.contains_key(&id));
551 self.exit_map.insert(id, node);
553 for &pred in preds.iter() {
554 self.add_contained_edge(pred, node);
559 fn add_contained_edge(&mut self,
562 let data = CFGEdgeData {exiting_scopes: vec!() };
563 self.graph.add_edge(source, target, data);
566 fn add_exiting_edge(&mut self,
567 from_expr: &ast::Expr,
568 from_index: CFGIndex,
570 to_index: CFGIndex) {
571 let mut data = CFGEdgeData {exiting_scopes: vec!() };
572 let mut scope_id = from_expr.id;
573 while scope_id != to_loop.loop_id {
575 data.exiting_scopes.push(scope_id);
576 scope_id = self.tcx.region_maps.encl_scope(scope_id);
578 self.graph.add_edge(from_index, to_index, data);
581 fn add_returning_edge(&mut self,
582 _from_expr: &ast::Expr,
583 from_index: CFGIndex) {
584 let mut data = CFGEdgeData {
585 exiting_scopes: vec!(),
587 for &LoopScope { loop_id: id, .. } in self.loop_scopes.iter().rev() {
588 data.exiting_scopes.push(id);
590 self.graph.add_edge(from_index, self.fn_exit, data);
595 label: Option<ast::Ident>) -> LoopScope {
598 return *self.loop_scopes.last().unwrap();
602 match self.tcx.def_map.borrow().find(&expr.id) {
603 Some(&def::DefLabel(loop_id)) => {
604 for l in self.loop_scopes.iter() {
605 if l.loop_id == loop_id {
609 self.tcx.sess.span_bug(
611 format!("no loop scope for id {:?}",
612 loop_id).as_slice());
616 self.tcx.sess.span_bug(
618 format!("bad entry `{:?}` in def_map for label",
626 fn is_method_call(&self, expr: &ast::Expr) -> bool {
627 let method_call = typeck::MethodCall::expr(expr.id);
628 self.tcx.method_map.borrow().contains_key(&method_call)