1 // Copyright 2012 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.
18 use util::nodemap::NodeMap;
22 struct CFGBuilder<'a> {
24 exit_map: NodeMap<CFGIndex>,
27 loop_scopes: Vec<LoopScope>,
31 loop_id: ast::NodeId, // id of loop/while node
32 continue_index: CFGIndex, // where to go on a `loop`
33 break_index: CFGIndex, // where to go on a `break
36 pub fn construct(tcx: &ty::ctxt,
37 blk: &ast::Block) -> CFG {
38 let mut graph = graph::Graph::new();
39 let entry = add_initial_dummy_node(&mut graph);
41 // `fn_exit` is target of return exprs, which lies somewhere
42 // outside input `blk`. (Distinguishing `fn_exit` and `block_exit`
43 // also resolves chicken-and-egg problem that arises if you try to
44 // have return exprs jump to `block_exit` during construction.)
45 let fn_exit = add_initial_dummy_node(&mut graph);
48 let mut cfg_builder = CFGBuilder {
49 exit_map: NodeMap::new(),
53 loop_scopes: Vec::new()
55 block_exit = cfg_builder.block(blk, entry);
56 cfg_builder.add_contained_edge(block_exit, fn_exit);
57 let CFGBuilder {exit_map, graph, ..} = cfg_builder;
58 CFG {exit_map: exit_map,
64 fn add_initial_dummy_node(g: &mut CFGGraph) -> CFGIndex {
65 g.add_node(CFGNodeData { id: ast::DUMMY_NODE_ID })
68 impl<'a> CFGBuilder<'a> {
69 fn block(&mut self, blk: &ast::Block, pred: CFGIndex) -> CFGIndex {
70 let mut stmts_exit = pred;
71 for stmt in blk.stmts.iter() {
72 stmts_exit = self.stmt(stmt.clone(), stmts_exit);
75 let expr_exit = self.opt_expr(blk.expr.clone(), stmts_exit);
77 self.add_node(blk.id, [expr_exit])
80 fn stmt(&mut self, stmt: Gc<ast::Stmt>, pred: CFGIndex) -> CFGIndex {
82 ast::StmtDecl(ref decl, _) => {
83 self.decl(&**decl, pred)
86 ast::StmtExpr(ref expr, _) | ast::StmtSemi(ref expr, _) => {
87 self.expr(expr.clone(), pred)
91 self.tcx.sess.span_bug(stmt.span, "unexpanded macro");
96 fn decl(&mut self, decl: &ast::Decl, pred: CFGIndex) -> CFGIndex {
98 ast::DeclLocal(ref local) => {
99 let init_exit = self.opt_expr(local.init.clone(), pred);
100 self.pat(&*local.pat, init_exit)
103 ast::DeclItem(_) => {
109 fn pat(&mut self, pat: &ast::Pat, pred: CFGIndex) -> CFGIndex {
111 ast::PatIdent(_, _, None) |
112 ast::PatEnum(_, None) |
115 ast::PatWild | ast::PatWildMulti => {
116 self.add_node(pat.id, [pred])
119 ast::PatBox(ref subpat) |
120 ast::PatRegion(ref subpat) |
121 ast::PatIdent(_, _, Some(ref subpat)) => {
122 let subpat_exit = self.pat(&**subpat, pred);
123 self.add_node(pat.id, [subpat_exit])
126 ast::PatEnum(_, Some(ref subpats)) |
127 ast::PatTup(ref subpats) => {
129 self.pats_all(subpats.iter().map(|p| p.clone()), pred);
130 self.add_node(pat.id, [pats_exit])
133 ast::PatStruct(_, ref subpats, _) => {
135 self.pats_all(subpats.iter().map(|f| f.pat.clone()), pred);
136 self.add_node(pat.id, [pats_exit])
139 ast::PatVec(ref pre, ref vec, ref post) => {
141 self.pats_all(pre.iter().map(|p| *p), pred);
143 self.pats_all(vec.iter().map(|p| *p), pre_exit);
145 self.pats_all(post.iter().map(|p| *p), vec_exit);
146 self.add_node(pat.id, [post_exit])
150 self.tcx.sess.span_bug(pat.span, "unexpanded macro");
155 fn pats_all<I: Iterator<Gc<ast::Pat>>>(&mut self,
157 pred: CFGIndex) -> CFGIndex {
158 //! Handles case where all of the patterns must match.
160 pats.fold(pred, |pred, pat| self.pat(&*pat, pred))
163 fn pats_any(&mut self,
164 pats: &[Gc<ast::Pat>],
165 pred: CFGIndex) -> CFGIndex {
166 //! Handles case where just one of the patterns must match.
169 self.pat(&*pats[0], pred)
171 let collect = self.add_dummy_node([]);
172 for &pat in pats.iter() {
173 let pat_exit = self.pat(&*pat, pred);
174 self.add_contained_edge(pat_exit, collect);
180 fn expr(&mut self, expr: Gc<ast::Expr>, pred: CFGIndex) -> CFGIndex {
182 ast::ExprBlock(ref blk) => {
183 let blk_exit = self.block(&**blk, pred);
184 self.add_node(expr.id, [blk_exit])
187 ast::ExprIf(ref cond, ref then, None) => {
202 let cond_exit = self.expr(cond.clone(), pred); // 1
203 let then_exit = self.block(&**then, cond_exit); // 2
204 self.add_node(expr.id, [cond_exit, then_exit]) // 3,4
207 ast::ExprIf(ref cond, ref then, Some(ref otherwise)) => {
222 let cond_exit = self.expr(cond.clone(), pred); // 1
223 let then_exit = self.block(&**then, cond_exit); // 2
224 let else_exit = self.expr(otherwise.clone(), cond_exit); // 3
225 self.add_node(expr.id, [then_exit, else_exit]) // 4, 5
228 ast::ExprWhile(ref cond, ref body) => {
243 // Note that `break` and `continue` statements
244 // may cause additional edges.
246 // Is the condition considered part of the loop?
247 let loopback = self.add_dummy_node([pred]); // 1
248 let cond_exit = self.expr(cond.clone(), loopback); // 2
249 let expr_exit = self.add_node(expr.id, [cond_exit]); // 3
250 self.loop_scopes.push(LoopScope {
252 continue_index: loopback,
253 break_index: expr_exit
255 let body_exit = self.block(&**body, cond_exit); // 4
256 self.add_contained_edge(body_exit, loopback); // 5
257 self.loop_scopes.pop();
261 ast::ExprForLoop(ref pat, ref head, ref body, _) => {
281 // Note that `break` and `continue` statements
282 // may cause additional edges.
284 let head = self.expr(head.clone(), pred); // 1
285 let loopback = self.add_dummy_node([head]); // 2
286 let cond = self.add_dummy_node([loopback]); // 3
287 let expr_exit = self.add_node(expr.id, [cond]); // 4
288 self.loop_scopes.push(LoopScope {
290 continue_index: loopback,
291 break_index: expr_exit,
293 let pat = self.pat(&**pat, cond); // 5
294 let body = self.block(&**body, pat); // 6
295 self.add_contained_edge(body, loopback); // 7
296 self.loop_scopes.pop();
300 ast::ExprLoop(ref body, _) => {
312 // Note that `break` and `loop` statements
313 // may cause additional edges.
315 let loopback = self.add_dummy_node([pred]); // 1
316 let expr_exit = self.add_node(expr.id, []); // 2
317 self.loop_scopes.push(LoopScope {
319 continue_index: loopback,
320 break_index: expr_exit,
322 let body_exit = self.block(&**body, loopback); // 3
323 self.add_contained_edge(body_exit, loopback); // 4
324 self.loop_scopes.pop();
328 ast::ExprMatch(ref discr, ref arms) => {
352 let discr_exit = self.expr(discr.clone(), pred); // 1
354 let expr_exit = self.add_node(expr.id, []);
355 let mut guard_exit = discr_exit;
356 for arm in arms.iter() {
357 guard_exit = self.opt_expr(arm.guard, guard_exit); // 2
358 let pats_exit = self.pats_any(arm.pats.as_slice(),
360 let body_exit = self.expr(arm.body.clone(), pats_exit); // 4
361 self.add_contained_edge(body_exit, expr_exit); // 5
366 ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => {
381 let l_exit = self.expr(l.clone(), pred); // 1
382 let r_exit = self.expr(r.clone(), l_exit); // 2
383 self.add_node(expr.id, [l_exit, r_exit]) // 3,4
386 ast::ExprRet(ref v) => {
387 let v_exit = self.opt_expr(v.clone(), pred);
388 let b = self.add_node(expr.id, [v_exit]);
389 self.add_returning_edge(expr, b);
390 self.add_node(ast::DUMMY_NODE_ID, [])
393 ast::ExprBreak(label) => {
394 let loop_scope = self.find_scope(expr, label);
395 let b = self.add_node(expr.id, [pred]);
396 self.add_exiting_edge(expr, b,
397 loop_scope, loop_scope.break_index);
398 self.add_node(ast::DUMMY_NODE_ID, [])
401 ast::ExprAgain(label) => {
402 let loop_scope = self.find_scope(expr, label);
403 let a = self.add_node(expr.id, [pred]);
404 self.add_exiting_edge(expr, a,
405 loop_scope, loop_scope.continue_index);
406 self.add_node(ast::DUMMY_NODE_ID, [])
409 ast::ExprVec(ref elems) => {
410 self.straightline(expr, pred, elems.as_slice())
413 ast::ExprCall(ref func, ref args) => {
414 self.call(expr, pred, func.clone(), args.as_slice())
417 ast::ExprMethodCall(_, _, ref args) => {
418 self.call(expr, pred, *args.get(0), args.slice_from(1))
421 ast::ExprIndex(ref l, ref r) |
422 ast::ExprBinary(_, ref l, ref r) if self.is_method_call(&*expr) => {
423 self.call(expr, pred, l.clone(), [r.clone()])
426 ast::ExprUnary(_, ref e) if self.is_method_call(&*expr) => {
427 self.call(expr, pred, e.clone(), [])
430 ast::ExprTup(ref exprs) => {
431 self.straightline(expr, pred, exprs.as_slice())
434 ast::ExprStruct(_, ref fields, base) => {
435 let base_exit = self.opt_expr(base, pred);
436 let field_exprs: Vec<Gc<ast::Expr>> =
437 fields.iter().map(|f| f.expr).collect();
438 self.straightline(expr, base_exit, field_exprs.as_slice())
441 ast::ExprRepeat(elem, count) => {
442 self.straightline(expr, pred, [elem, count])
445 ast::ExprAssign(l, r) |
446 ast::ExprAssignOp(_, l, r) => {
447 self.straightline(expr, pred, [r, l])
450 ast::ExprIndex(l, r) |
451 ast::ExprBinary(_, l, r) => { // NB: && and || handled earlier
452 self.straightline(expr, pred, [l, r])
455 ast::ExprBox(p, e) => {
456 self.straightline(expr, pred, [p, e])
459 ast::ExprAddrOf(_, e) |
460 ast::ExprCast(e, _) |
461 ast::ExprUnary(_, e) |
463 ast::ExprVstore(e, _) |
464 ast::ExprField(e, _, _) => {
465 self.straightline(expr, pred, [e])
468 ast::ExprInlineAsm(ref inline_asm) => {
469 let inputs = inline_asm.inputs.iter();
470 let outputs = inline_asm.outputs.iter();
471 fn extract_expr<A>(&(_, expr): &(A, Gc<ast::Expr>)) -> Gc<ast::Expr> { expr }
472 let post_inputs = self.exprs(inputs.map(|a| {
473 debug!("cfg::construct InlineAsm id:{} input:{:?}", expr.id, a);
476 let post_outputs = self.exprs(outputs.map(|a| {
477 debug!("cfg::construct InlineAsm id:{} output:{:?}", expr.id, a);
480 self.add_node(expr.id, [post_outputs])
484 ast::ExprFnBlock(..) |
486 ast::ExprUnboxedFn(..) |
488 ast::ExprPath(..) => {
489 self.straightline(expr, pred, [])
495 call_expr: Gc<ast::Expr>,
497 func_or_rcvr: Gc<ast::Expr>,
498 args: &[Gc<ast::Expr>]) -> CFGIndex {
499 let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
500 let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
502 let return_ty = ty::node_id_to_type(self.tcx, call_expr.id);
503 let fails = ty::type_is_bot(return_ty);
505 self.add_node(ast::DUMMY_NODE_ID, [])
511 fn exprs<I:Iterator<Gc<ast::Expr>>>(&mut self,
513 pred: CFGIndex) -> CFGIndex {
514 //! Constructs graph for `exprs` evaluated in order
515 exprs.fold(pred, |p, e| self.expr(e, p))
518 fn opt_expr(&mut self,
519 opt_expr: Option<Gc<ast::Expr>>,
520 pred: CFGIndex) -> CFGIndex {
521 //! Constructs graph for `opt_expr` evaluated, if Some
523 opt_expr.iter().fold(pred, |p, &e| self.expr(e, p))
526 fn straightline(&mut self,
529 subexprs: &[Gc<ast::Expr>]) -> CFGIndex {
530 //! Handles case of an expression that evaluates `subexprs` in order
532 let subexprs_exit = self.exprs(subexprs.iter().map(|&e|e), pred);
533 self.add_node(expr.id, [subexprs_exit])
536 fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex {
537 self.add_node(ast::DUMMY_NODE_ID, preds)
540 fn add_node(&mut self, id: ast::NodeId, preds: &[CFGIndex]) -> CFGIndex {
541 assert!(!self.exit_map.contains_key(&id));
542 let node = self.graph.add_node(CFGNodeData {id: id});
543 if id != ast::DUMMY_NODE_ID {
544 assert!(!self.exit_map.contains_key(&id));
545 self.exit_map.insert(id, node);
547 for &pred in preds.iter() {
548 self.add_contained_edge(pred, node);
553 fn add_contained_edge(&mut self,
556 let data = CFGEdgeData {exiting_scopes: vec!() };
557 self.graph.add_edge(source, target, data);
560 fn add_exiting_edge(&mut self,
561 from_expr: Gc<ast::Expr>,
562 from_index: CFGIndex,
564 to_index: CFGIndex) {
565 let mut data = CFGEdgeData {exiting_scopes: vec!() };
566 let mut scope_id = from_expr.id;
567 while scope_id != to_loop.loop_id {
569 data.exiting_scopes.push(scope_id);
570 scope_id = self.tcx.region_maps.encl_scope(scope_id);
572 self.graph.add_edge(from_index, to_index, data);
575 fn add_returning_edge(&mut self,
576 _from_expr: Gc<ast::Expr>,
577 from_index: CFGIndex) {
578 let mut data = CFGEdgeData {
579 exiting_scopes: vec!(),
581 for &LoopScope { loop_id: id, .. } in self.loop_scopes.iter().rev() {
582 data.exiting_scopes.push(id);
584 self.graph.add_edge(from_index, self.fn_exit, data);
589 label: Option<ast::Ident>) -> LoopScope {
592 return *self.loop_scopes.last().unwrap();
596 match self.tcx.def_map.borrow().find(&expr.id) {
597 Some(&def::DefLabel(loop_id)) => {
598 for l in self.loop_scopes.iter() {
599 if l.loop_id == loop_id {
603 self.tcx.sess.span_bug(
605 format!("no loop scope for id {:?}",
606 loop_id).as_slice());
610 self.tcx.sess.span_bug(
612 format!("bad entry `{:?}` in def_map for label",
620 fn is_method_call(&self, expr: &ast::Expr) -> bool {
621 let method_call = typeck::MethodCall::expr(expr.id);
622 self.tcx.method_map.borrow().contains_key(&method_call)