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) => {
354 let discr_exit = self.expr(discr.clone(), pred); // 1
356 let expr_exit = self.add_node(expr.id, []);
357 let mut cond_exit = discr_exit;
358 for arm in arms.iter() {
359 cond_exit = self.add_dummy_node([cond_exit]); // 2
360 let pats_exit = self.pats_any(arm.pats.as_slice(),
362 let guard_exit = self.opt_expr(arm.guard,
364 let body_exit = self.expr(arm.body.clone(),
366 self.add_contained_edge(body_exit, expr_exit); // 6
371 ast::ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => {
386 let l_exit = self.expr(l.clone(), pred); // 1
387 let r_exit = self.expr(r.clone(), l_exit); // 2
388 self.add_node(expr.id, [l_exit, r_exit]) // 3,4
391 ast::ExprRet(ref v) => {
392 let v_exit = self.opt_expr(v.clone(), pred);
393 let b = self.add_node(expr.id, [v_exit]);
394 self.add_returning_edge(expr, b);
395 self.add_node(ast::DUMMY_NODE_ID, [])
398 ast::ExprBreak(label) => {
399 let loop_scope = self.find_scope(expr, label);
400 let b = self.add_node(expr.id, [pred]);
401 self.add_exiting_edge(expr, b,
402 loop_scope, loop_scope.break_index);
403 self.add_node(ast::DUMMY_NODE_ID, [])
406 ast::ExprAgain(label) => {
407 let loop_scope = self.find_scope(expr, label);
408 let a = self.add_node(expr.id, [pred]);
409 self.add_exiting_edge(expr, a,
410 loop_scope, loop_scope.continue_index);
411 self.add_node(ast::DUMMY_NODE_ID, [])
414 ast::ExprVec(ref elems) => {
415 self.straightline(expr, pred, elems.as_slice())
418 ast::ExprCall(ref func, ref args) => {
419 self.call(expr, pred, func.clone(), args.as_slice())
422 ast::ExprMethodCall(_, _, ref args) => {
423 self.call(expr, pred, *args.get(0), args.slice_from(1))
426 ast::ExprIndex(ref l, ref r) |
427 ast::ExprBinary(_, ref l, ref r) if self.is_method_call(&*expr) => {
428 self.call(expr, pred, l.clone(), [r.clone()])
431 ast::ExprUnary(_, ref e) if self.is_method_call(&*expr) => {
432 self.call(expr, pred, e.clone(), [])
435 ast::ExprTup(ref exprs) => {
436 self.straightline(expr, pred, exprs.as_slice())
439 ast::ExprStruct(_, ref fields, base) => {
440 let base_exit = self.opt_expr(base, pred);
441 let field_exprs: Vec<Gc<ast::Expr>> =
442 fields.iter().map(|f| f.expr).collect();
443 self.straightline(expr, base_exit, field_exprs.as_slice())
446 ast::ExprRepeat(elem, count) => {
447 self.straightline(expr, pred, [elem, count])
450 ast::ExprAssign(l, r) |
451 ast::ExprAssignOp(_, l, r) => {
452 self.straightline(expr, pred, [r, l])
455 ast::ExprIndex(l, r) |
456 ast::ExprBinary(_, l, r) => { // NB: && and || handled earlier
457 self.straightline(expr, pred, [l, r])
460 ast::ExprBox(p, e) => {
461 self.straightline(expr, pred, [p, e])
464 ast::ExprAddrOf(_, e) |
465 ast::ExprCast(e, _) |
466 ast::ExprUnary(_, e) |
468 ast::ExprVstore(e, _) |
469 ast::ExprField(e, _, _) => {
470 self.straightline(expr, pred, [e])
473 ast::ExprInlineAsm(ref inline_asm) => {
474 let inputs = inline_asm.inputs.iter();
475 let outputs = inline_asm.outputs.iter();
476 fn extract_expr<A>(&(_, expr): &(A, Gc<ast::Expr>)) -> Gc<ast::Expr> { expr }
477 let post_inputs = self.exprs(inputs.map(|a| {
478 debug!("cfg::construct InlineAsm id:{} input:{:?}", expr.id, a);
481 let post_outputs = self.exprs(outputs.map(|a| {
482 debug!("cfg::construct InlineAsm id:{} output:{:?}", expr.id, a);
485 self.add_node(expr.id, [post_outputs])
489 ast::ExprFnBlock(..) |
491 ast::ExprUnboxedFn(..) |
493 ast::ExprPath(..) => {
494 self.straightline(expr, pred, [])
500 call_expr: Gc<ast::Expr>,
502 func_or_rcvr: Gc<ast::Expr>,
503 args: &[Gc<ast::Expr>]) -> CFGIndex {
504 let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
505 let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
507 let return_ty = ty::node_id_to_type(self.tcx, call_expr.id);
508 let fails = ty::type_is_bot(return_ty);
510 self.add_node(ast::DUMMY_NODE_ID, [])
516 fn exprs<I:Iterator<Gc<ast::Expr>>>(&mut self,
518 pred: CFGIndex) -> CFGIndex {
519 //! Constructs graph for `exprs` evaluated in order
520 exprs.fold(pred, |p, e| self.expr(e, p))
523 fn opt_expr(&mut self,
524 opt_expr: Option<Gc<ast::Expr>>,
525 pred: CFGIndex) -> CFGIndex {
526 //! Constructs graph for `opt_expr` evaluated, if Some
528 opt_expr.iter().fold(pred, |p, &e| self.expr(e, p))
531 fn straightline(&mut self,
534 subexprs: &[Gc<ast::Expr>]) -> CFGIndex {
535 //! Handles case of an expression that evaluates `subexprs` in order
537 let subexprs_exit = self.exprs(subexprs.iter().map(|&e|e), pred);
538 self.add_node(expr.id, [subexprs_exit])
541 fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex {
542 self.add_node(ast::DUMMY_NODE_ID, preds)
545 fn add_node(&mut self, id: ast::NodeId, preds: &[CFGIndex]) -> CFGIndex {
546 assert!(!self.exit_map.contains_key(&id));
547 let node = self.graph.add_node(CFGNodeData {id: id});
548 if id != ast::DUMMY_NODE_ID {
549 assert!(!self.exit_map.contains_key(&id));
550 self.exit_map.insert(id, node);
552 for &pred in preds.iter() {
553 self.add_contained_edge(pred, node);
558 fn add_contained_edge(&mut self,
561 let data = CFGEdgeData {exiting_scopes: vec!() };
562 self.graph.add_edge(source, target, data);
565 fn add_exiting_edge(&mut self,
566 from_expr: Gc<ast::Expr>,
567 from_index: CFGIndex,
569 to_index: CFGIndex) {
570 let mut data = CFGEdgeData {exiting_scopes: vec!() };
571 let mut scope_id = from_expr.id;
572 while scope_id != to_loop.loop_id {
574 data.exiting_scopes.push(scope_id);
575 scope_id = self.tcx.region_maps.encl_scope(scope_id);
577 self.graph.add_edge(from_index, to_index, data);
580 fn add_returning_edge(&mut self,
581 _from_expr: Gc<ast::Expr>,
582 from_index: CFGIndex) {
583 let mut data = CFGEdgeData {
584 exiting_scopes: vec!(),
586 for &LoopScope { loop_id: id, .. } in self.loop_scopes.iter().rev() {
587 data.exiting_scopes.push(id);
589 self.graph.add_edge(from_index, self.fn_exit, data);
594 label: Option<ast::Ident>) -> LoopScope {
597 return *self.loop_scopes.last().unwrap();
601 match self.tcx.def_map.borrow().find(&expr.id) {
602 Some(&def::DefLabel(loop_id)) => {
603 for l in self.loop_scopes.iter() {
604 if l.loop_id == loop_id {
608 self.tcx.sess.span_bug(
610 format!("no loop scope for id {:?}",
611 loop_id).as_slice());
615 self.tcx.sess.span_bug(
617 format!("bad entry `{:?}` in def_map for label",
625 fn is_method_call(&self, expr: &ast::Expr) -> bool {
626 let method_call = typeck::MethodCall::expr(expr.id);
627 self.tcx.method_map.borrow().contains_key(&method_call)