From: Matthew Jasper Date: Fri, 6 Sep 2019 17:02:12 +0000 (+0100) Subject: Move the HIR cfg to `rustc_ast_borrowck` X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=10f46b69bc32bd1cb5f013ce904957aeb28603bb;p=rust.git Move the HIR cfg to `rustc_ast_borrowck` No new code should be using it. --- diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs deleted file mode 100644 index 0dad2dda837..00000000000 --- a/src/librustc/cfg/construct.rs +++ /dev/null @@ -1,533 +0,0 @@ -use crate::cfg::*; -use crate::middle::region; -use rustc_data_structures::graph::implementation as graph; -use crate::ty::{self, TyCtxt}; - -use crate::hir::{self, PatKind}; -use crate::hir::def_id::DefId; -use crate::hir::ptr::P; - -struct CFGBuilder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - owner_def_id: DefId, - tables: &'a ty::TypeckTables<'tcx>, - graph: CFGGraph, - fn_exit: CFGIndex, - loop_scopes: Vec, - breakable_block_scopes: Vec, -} - -#[derive(Copy, Clone)] -struct BlockScope { - block_expr_id: hir::ItemLocalId, // id of breakable block expr node - break_index: CFGIndex, // where to go on `break` -} - -#[derive(Copy, Clone)] -struct LoopScope { - loop_id: hir::ItemLocalId, // id of loop/while node - continue_index: CFGIndex, // where to go on a `loop` - break_index: CFGIndex, // where to go on a `break` -} - -pub fn construct(tcx: TyCtxt<'_>, body: &hir::Body) -> CFG { - let mut graph = graph::Graph::new(); - let entry = graph.add_node(CFGNodeData::Entry); - - // `fn_exit` is target of return exprs, which lies somewhere - // outside input `body`. (Distinguishing `fn_exit` and `body_exit` - // also resolves chicken-and-egg problem that arises if you try to - // have return exprs jump to `body_exit` during construction.) - let fn_exit = graph.add_node(CFGNodeData::Exit); - let body_exit; - - // Find the tables for this body. - let owner_def_id = tcx.hir().body_owner_def_id(body.id()); - let tables = tcx.typeck_tables_of(owner_def_id); - - let mut cfg_builder = CFGBuilder { - tcx, - owner_def_id, - tables, - graph, - fn_exit, - loop_scopes: Vec::new(), - breakable_block_scopes: Vec::new(), - }; - body_exit = cfg_builder.expr(&body.value, entry); - cfg_builder.add_contained_edge(body_exit, fn_exit); - let CFGBuilder { graph, .. } = cfg_builder; - CFG { - owner_def_id, - graph, - entry, - exit: fn_exit, - } -} - -impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { - fn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex { - if blk.targeted_by_break { - let expr_exit = self.add_ast_node(blk.hir_id.local_id, &[]); - - self.breakable_block_scopes.push(BlockScope { - block_expr_id: blk.hir_id.local_id, - break_index: expr_exit, - }); - - let mut stmts_exit = pred; - for stmt in &blk.stmts { - stmts_exit = self.stmt(stmt, stmts_exit); - } - let blk_expr_exit = self.opt_expr(&blk.expr, stmts_exit); - self.add_contained_edge(blk_expr_exit, expr_exit); - - self.breakable_block_scopes.pop(); - - expr_exit - } else { - let mut stmts_exit = pred; - for stmt in &blk.stmts { - stmts_exit = self.stmt(stmt, stmts_exit); - } - - let expr_exit = self.opt_expr(&blk.expr, stmts_exit); - - self.add_ast_node(blk.hir_id.local_id, &[expr_exit]) - } - } - - fn stmt(&mut self, stmt: &hir::Stmt, pred: CFGIndex) -> CFGIndex { - let exit = match stmt.node { - hir::StmtKind::Local(ref local) => { - let init_exit = self.opt_expr(&local.init, pred); - self.pat(&local.pat, init_exit) - } - hir::StmtKind::Item(_) => { - pred - } - hir::StmtKind::Expr(ref expr) | - hir::StmtKind::Semi(ref expr) => { - self.expr(&expr, pred) - } - }; - self.add_ast_node(stmt.hir_id.local_id, &[exit]) - } - - fn pat(&mut self, pat: &hir::Pat, pred: CFGIndex) -> CFGIndex { - match pat.node { - PatKind::Binding(.., None) | - PatKind::Path(_) | - PatKind::Lit(..) | - PatKind::Range(..) | - PatKind::Wild => self.add_ast_node(pat.hir_id.local_id, &[pred]), - - PatKind::Box(ref subpat) | - PatKind::Ref(ref subpat, _) | - PatKind::Binding(.., Some(ref subpat)) => { - let subpat_exit = self.pat(&subpat, pred); - self.add_ast_node(pat.hir_id.local_id, &[subpat_exit]) - } - - PatKind::TupleStruct(_, ref subpats, _) | - PatKind::Tuple(ref subpats, _) => { - let pats_exit = self.pats_all(subpats.iter(), pred); - self.add_ast_node(pat.hir_id.local_id, &[pats_exit]) - } - - PatKind::Struct(_, ref subpats, _) => { - let pats_exit = self.pats_all(subpats.iter().map(|f| &f.pat), pred); - self.add_ast_node(pat.hir_id.local_id, &[pats_exit]) - } - - PatKind::Or(ref pats) => { - let branches: Vec<_> = pats.iter().map(|p| self.pat(p, pred)).collect(); - self.add_ast_node(pat.hir_id.local_id, &branches) - } - - PatKind::Slice(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_ast_node(pat.hir_id.local_id, &[post_exit]) - } - } - } - - fn pats_all<'b, I: Iterator>>( - &mut self, - pats: I, - pred: CFGIndex - ) -> CFGIndex { - //! Handles case where all of the patterns must match. - pats.fold(pred, |pred, pat| self.pat(&pat, pred)) - } - - fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex { - match expr.node { - hir::ExprKind::Block(ref blk, _) => { - let blk_exit = self.block(&blk, pred); - self.add_ast_node(expr.hir_id.local_id, &[blk_exit]) - } - - hir::ExprKind::Loop(ref body, _, _) => { - // - // [pred] - // | - // v 1 - // [loopback] <---+ - // | 4 | - // v 3 | - // [body] ------+ - // - // [expr] 2 - // - // Note that `break` and `loop` statements - // may cause additional edges. - - let loopback = self.add_dummy_node(&[pred]); // 1 - let expr_exit = self.add_ast_node(expr.hir_id.local_id, &[]); // 2 - self.loop_scopes.push(LoopScope { - loop_id: expr.hir_id.local_id, - continue_index: loopback, - break_index: expr_exit, - }); - let body_exit = self.block(&body, loopback); // 3 - self.add_contained_edge(body_exit, loopback); // 4 - self.loop_scopes.pop(); - expr_exit - } - - hir::ExprKind::Match(ref discr, ref arms, _) => { - self.match_(expr.hir_id.local_id, &discr, &arms, pred) - } - - hir::ExprKind::Binary(op, ref l, ref r) if op.node.is_lazy() => { - // - // [pred] - // | - // v 1 - // [l] - // | - // / \ - // / \ - // v 2 * - // [r] | - // | | - // v 3 v 4 - // [..exit..] - // - let l_exit = self.expr(&l, pred); // 1 - let r_exit = self.expr(&r, l_exit); // 2 - self.add_ast_node(expr.hir_id.local_id, &[l_exit, r_exit]) // 3,4 - } - - hir::ExprKind::Ret(ref v) => { - let v_exit = self.opt_expr(v, pred); - let b = self.add_ast_node(expr.hir_id.local_id, &[v_exit]); - self.add_returning_edge(expr, b); - self.add_unreachable_node() - } - - hir::ExprKind::Break(destination, ref opt_expr) => { - let v = self.opt_expr(opt_expr, pred); - let (target_scope, break_dest) = - self.find_scope_edge(expr, destination, ScopeCfKind::Break); - let b = self.add_ast_node(expr.hir_id.local_id, &[v]); - self.add_exiting_edge(expr, b, target_scope, break_dest); - self.add_unreachable_node() - } - - hir::ExprKind::Continue(destination) => { - let (target_scope, cont_dest) = - self.find_scope_edge(expr, destination, ScopeCfKind::Continue); - let a = self.add_ast_node(expr.hir_id.local_id, &[pred]); - self.add_exiting_edge(expr, a, target_scope, cont_dest); - self.add_unreachable_node() - } - - hir::ExprKind::Array(ref elems) => { - self.straightline(expr, pred, elems.iter().map(|e| &*e)) - } - - hir::ExprKind::Call(ref func, ref args) => { - self.call(expr, pred, &func, args.iter().map(|e| &*e)) - } - - hir::ExprKind::MethodCall(.., ref args) => { - self.call(expr, pred, &args[0], args[1..].iter().map(|e| &*e)) - } - - hir::ExprKind::Index(ref l, ref r) | - hir::ExprKind::Binary(_, ref l, ref r) if self.tables.is_method_call(expr) => { - self.call(expr, pred, &l, Some(&**r).into_iter()) - } - - hir::ExprKind::Unary(_, ref e) if self.tables.is_method_call(expr) => { - self.call(expr, pred, &e, None::.iter()) - } - - hir::ExprKind::Tup(ref exprs) => { - self.straightline(expr, pred, exprs.iter().map(|e| &*e)) - } - - hir::ExprKind::Struct(_, ref fields, ref base) => { - let field_cfg = self.straightline(expr, pred, fields.iter().map(|f| &*f.expr)); - self.opt_expr(base, field_cfg) - } - - hir::ExprKind::Assign(ref l, ref r) | - hir::ExprKind::AssignOp(_, ref l, ref r) => { - self.straightline(expr, pred, [r, l].iter().map(|&e| &**e)) - } - - hir::ExprKind::Index(ref l, ref r) | - hir::ExprKind::Binary(_, ref l, ref r) => { // N.B., && and || handled earlier - self.straightline(expr, pred, [l, r].iter().map(|&e| &**e)) - } - - hir::ExprKind::Box(ref e) | - hir::ExprKind::AddrOf(_, ref e) | - hir::ExprKind::Cast(ref e, _) | - hir::ExprKind::Type(ref e, _) | - hir::ExprKind::DropTemps(ref e) | - hir::ExprKind::Unary(_, ref e) | - hir::ExprKind::Field(ref e, _) | - hir::ExprKind::Yield(ref e, _) | - hir::ExprKind::Repeat(ref e, _) => { - self.straightline(expr, pred, Some(&**e).into_iter()) - } - - hir::ExprKind::InlineAsm(_, ref outputs, ref inputs) => { - let post_outputs = self.exprs(outputs.iter().map(|e| &*e), pred); - let post_inputs = self.exprs(inputs.iter().map(|e| &*e), post_outputs); - self.add_ast_node(expr.hir_id.local_id, &[post_inputs]) - } - - hir::ExprKind::Closure(..) | - hir::ExprKind::Lit(..) | - hir::ExprKind::Path(_) | - hir::ExprKind::Err => { - self.straightline(expr, pred, None::.iter()) - } - } - } - - fn call<'b, I: Iterator>(&mut self, - call_expr: &hir::Expr, - pred: CFGIndex, - func_or_rcvr: &hir::Expr, - args: I) -> CFGIndex { - let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); - let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - let m = self.tcx.hir().get_module_parent(call_expr.hir_id); - if self.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(call_expr)) { - self.add_unreachable_node() - } else { - ret - } - } - - fn exprs<'b, I: Iterator>(&mut self, - exprs: I, - pred: CFGIndex) -> CFGIndex { - //! Constructs graph for `exprs` evaluated in order - exprs.fold(pred, |p, e| self.expr(e, p)) - } - - fn opt_expr(&mut self, - opt_expr: &Option>, - pred: CFGIndex) -> CFGIndex { - //! Constructs graph for `opt_expr` evaluated, if Some - opt_expr.iter().fold(pred, |p, e| self.expr(&e, p)) - } - - fn straightline<'b, I: Iterator>(&mut self, - expr: &hir::Expr, - pred: CFGIndex, - subexprs: I) -> CFGIndex { - //! Handles case of an expression that evaluates `subexprs` in order - - let subexprs_exit = self.exprs(subexprs, pred); - self.add_ast_node(expr.hir_id.local_id, &[subexprs_exit]) - } - - fn match_(&mut self, id: hir::ItemLocalId, discr: &hir::Expr, - arms: &[hir::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 MIR contains. - // 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). MIR 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_guard = None; - let match_scope = region::Scope { id, data: region::ScopeData::Node }; - - 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 bindings_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 = match guard { - hir::Guard::If(ref e) => (&**e, self.expr(e, guard_start)), - }; - // #47295: We used to have very special case code - // here for when a pair of arms are both formed - // solely from constants, and if so, not add these - // edges. But this was not actually sound without - // other constraints that we stopped enforcing at - // some point. - if let Some((prev_guard, prev_index)) = prev_guard.take() { - self.add_exiting_edge(prev_guard, prev_index, match_scope, guard_start); - } - - // Push the guard onto the list of previous guards - prev_guard = Some(guard_exit); - - // Update the exit node for the pattern - pat_exit = guard_exit.1; - } - - // Add an edge from the exit of this pattern to the - // exit of the arm - self.add_contained_edge(pat_exit, bindings_exit); - } - - // Visit the body of this arm - let body_exit = self.expr(&arm.body, bindings_exit); - - let arm_exit = self.add_ast_node(arm.hir_id.local_id, &[body_exit]); - - // Link the body to the exit of the expression - self.add_contained_edge(arm_exit, expr_exit); - } - - expr_exit - } - - fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex { - self.add_node(CFGNodeData::Dummy, preds) - } - - fn add_ast_node(&mut self, id: hir::ItemLocalId, preds: &[CFGIndex]) -> CFGIndex { - 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); - } - node - } - - fn add_contained_edge(&mut self, - source: CFGIndex, - target: CFGIndex) { - let data = CFGEdgeData {exiting_scopes: vec![] }; - self.graph.add_edge(source, target, data); - } - - fn add_exiting_edge(&mut self, - from_expr: &hir::Expr, - from_index: CFGIndex, - target_scope: region::Scope, - to_index: CFGIndex) { - let mut data = CFGEdgeData { exiting_scopes: vec![] }; - let mut scope = region::Scope { - id: from_expr.hir_id.local_id, - data: region::ScopeData::Node - }; - let region_scope_tree = self.tcx.region_scope_tree(self.owner_def_id); - while scope != target_scope { - data.exiting_scopes.push(scope.item_local_id()); - scope = region_scope_tree.encl_scope(scope); - } - self.graph.add_edge(from_index, to_index, data); - } - - fn add_returning_edge(&mut self, - _from_expr: &hir::Expr, - from_index: CFGIndex) { - let data = CFGEdgeData { - exiting_scopes: self.loop_scopes.iter() - .rev() - .map(|&LoopScope { loop_id: id, .. }| id) - .collect() - }; - self.graph.add_edge(from_index, self.fn_exit, data); - } - - fn find_scope_edge(&self, - expr: &hir::Expr, - destination: hir::Destination, - scope_cf_kind: ScopeCfKind) -> (region::Scope, CFGIndex) { - - match destination.target_id { - Ok(loop_id) => { - for b in &self.breakable_block_scopes { - if b.block_expr_id == loop_id.local_id { - let scope = region::Scope { - id: loop_id.local_id, - data: region::ScopeData::Node - }; - return (scope, match scope_cf_kind { - ScopeCfKind::Break => b.break_index, - ScopeCfKind::Continue => bug!("can't continue to block"), - }); - } - } - for l in &self.loop_scopes { - if l.loop_id == loop_id.local_id { - let scope = region::Scope { - id: loop_id.local_id, - data: region::ScopeData::Node - }; - return (scope, match scope_cf_kind { - ScopeCfKind::Break => l.break_index, - ScopeCfKind::Continue => l.continue_index, - }); - } - } - span_bug!(expr.span, "no scope for id {}", loop_id); - } - Err(err) => span_bug!(expr.span, "scope error: {}", err), - } - } -} - -#[derive(Copy, Clone, Eq, PartialEq)] -enum ScopeCfKind { - Break, - Continue, -} diff --git a/src/librustc/cfg/graphviz.rs b/src/librustc/cfg/graphviz.rs deleted file mode 100644 index 918120057d4..00000000000 --- a/src/librustc/cfg/graphviz.rs +++ /dev/null @@ -1,123 +0,0 @@ -/// This module provides linkage between rustc::middle::graph and -/// libgraphviz traits. - -// For clarity, rename the graphviz crate locally to dot. -use graphviz as dot; - -use crate::cfg; -use crate::hir; -use crate::ty::TyCtxt; - -pub type Node<'a> = (cfg::CFGIndex, &'a cfg::CFGNode); -pub type Edge<'a> = &'a cfg::CFGEdge; - -pub struct LabelledCFG<'a, 'tcx> { - pub tcx: TyCtxt<'tcx>, - pub cfg: &'a cfg::CFG, - pub name: String, - /// `labelled_edges` controls whether we emit labels on the edges - pub labelled_edges: bool, -} - -impl<'a, 'tcx> LabelledCFG<'a, 'tcx> { - fn local_id_to_string(&self, local_id: hir::ItemLocalId) -> String { - assert!(self.cfg.owner_def_id.is_local()); - let hir_id = hir::HirId { - owner: self.tcx.hir().def_index_to_hir_id(self.cfg.owner_def_id.index).owner, - local_id - }; - let s = self.tcx.hir().node_to_string(hir_id); - - // Replacing newlines with \\l causes each line to be left-aligned, - // improving presentation of (long) pretty-printed expressions. - if s.contains("\n") { - let mut s = s.replace("\n", "\\l"); - // Apparently left-alignment applies to the line that precedes - // \l, not the line that follows; so, add \l at end of string - // if not already present, ensuring last line gets left-aligned - // as well. - let mut last_two: Vec<_> = - s.chars().rev().take(2).collect(); - last_two.reverse(); - if last_two != ['\\', 'l'] { - s.push_str("\\l"); - } - s - } else { - s - } - } -} - -impl<'a, 'hir> dot::Labeller<'a> for LabelledCFG<'a, 'hir> { - type Node = Node<'a>; - type Edge = Edge<'a>; - fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new(&self.name[..]).unwrap() } - - fn node_id(&'a self, &(i,_): &Node<'a>) -> dot::Id<'a> { - dot::Id::new(format!("N{}", i.node_id())).unwrap() - } - - fn node_label(&'a self, &(i, n): &Node<'a>) -> dot::LabelText<'a> { - if i == self.cfg.entry { - dot::LabelText::LabelStr("entry".into()) - } else if i == self.cfg.exit { - dot::LabelText::LabelStr("exit".into()) - } else if n.data.id() == hir::DUMMY_ITEM_LOCAL_ID { - dot::LabelText::LabelStr("(dummy_node)".into()) - } else { - let s = self.local_id_to_string(n.data.id()); - dot::LabelText::EscStr(s.into()) - } - } - - fn edge_label(&self, e: &Edge<'a>) -> dot::LabelText<'a> { - let mut label = String::new(); - if !self.labelled_edges { - return dot::LabelText::EscStr(label.into()); - } - let mut put_one = false; - for (i, &id) in e.data.exiting_scopes.iter().enumerate() { - if put_one { - label.push_str(",\\l"); - } else { - put_one = true; - } - let s = self.local_id_to_string(id); - label.push_str(&format!("exiting scope_{} {}", - i, - &s[..])); - } - dot::LabelText::EscStr(label.into()) - } -} - -impl<'a> dot::GraphWalk<'a> for &'a cfg::CFG { - type Node = Node<'a>; - type Edge = Edge<'a>; - fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { - let v: Vec<_> = self.graph.enumerated_nodes().collect(); - v.into() - } - fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { - self.graph.all_edges().iter().collect() - } - fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { - let i = edge.source(); - (i, self.graph.node(i)) - } - fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { - let i = edge.target(); - (i, self.graph.node(i)) - } -} - -impl<'a, 'hir> dot::GraphWalk<'a> for LabelledCFG<'a, 'hir> -{ - type Node = Node<'a>; - type Edge = Edge<'a>; - fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.cfg.nodes() } - fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.cfg.edges() } - fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) } - fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) } -} diff --git a/src/librustc/cfg/mod.rs b/src/librustc/cfg/mod.rs deleted file mode 100644 index 88fc7fbfad5..00000000000 --- a/src/librustc/cfg/mod.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! Module that constructs a control-flow graph representing an item. -//! Uses `Graph` as the underlying representation. - -use rustc_data_structures::graph::implementation as graph; -use crate::ty::TyCtxt; -use crate::hir; -use crate::hir::def_id::DefId; - -mod construct; -pub mod graphviz; - -pub struct CFG { - pub owner_def_id: DefId, - pub graph: CFGGraph, - pub entry: CFGIndex, - pub exit: CFGIndex, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum CFGNodeData { - AST(hir::ItemLocalId), - Entry, - Exit, - Dummy, - Unreachable, -} - -impl CFGNodeData { - pub fn id(&self) -> hir::ItemLocalId { - if let CFGNodeData::AST(id) = *self { - id - } else { - hir::DUMMY_ITEM_LOCAL_ID - } - } -} - -#[derive(Debug)] -pub struct CFGEdgeData { - pub exiting_scopes: Vec -} - -pub type CFGIndex = graph::NodeIndex; - -pub type CFGGraph = graph::Graph; - -pub type CFGNode = graph::Node; - -pub type CFGEdge = graph::Edge; - -impl CFG { - pub fn new(tcx: TyCtxt<'_>, body: &hir::Body) -> CFG { - construct::construct(tcx, body) - } - - pub fn node_is_reachable(&self, id: hir::ItemLocalId) -> bool { - self.graph.depth_traverse(self.entry, graph::OUTGOING) - .any(|idx| self.graph.node_data(idx).id() == id) - } -} diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 368f5bb64fe..bf4330a29a7 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -97,7 +97,6 @@ #[macro_use] pub mod arena; -pub mod cfg; pub mod dep_graph; pub mod hir; pub mod ich; diff --git a/src/librustc_ast_borrowck/borrowck/mod.rs b/src/librustc_ast_borrowck/borrowck/mod.rs index 3bbd7ae5c35..23d5480c605 100644 --- a/src/librustc_ast_borrowck/borrowck/mod.rs +++ b/src/librustc_ast_borrowck/borrowck/mod.rs @@ -9,7 +9,6 @@ use rustc::hir::HirId; use rustc::hir::Node; -use rustc::cfg; use rustc::middle::borrowck::{BorrowCheckResult, SignalledError}; use rustc::hir::def_id::{DefId, LocalDefId}; use rustc::middle::mem_categorization as mc; @@ -28,6 +27,7 @@ use rustc::hir; +use crate::cfg; use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom}; pub mod check_loans; diff --git a/src/librustc_ast_borrowck/borrowck/move_data.rs b/src/librustc_ast_borrowck/borrowck/move_data.rs index 887a0e2f20e..67d818161b1 100644 --- a/src/librustc_ast_borrowck/borrowck/move_data.rs +++ b/src/librustc_ast_borrowck/borrowck/move_data.rs @@ -4,7 +4,7 @@ use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom}; use crate::borrowck::*; -use rustc::cfg; +use crate::cfg; use rustc::ty::{self, TyCtxt}; use rustc::util::nodemap::FxHashMap; diff --git a/src/librustc_ast_borrowck/cfg/construct.rs b/src/librustc_ast_borrowck/cfg/construct.rs new file mode 100644 index 00000000000..339d92145c6 --- /dev/null +++ b/src/librustc_ast_borrowck/cfg/construct.rs @@ -0,0 +1,533 @@ +use crate::cfg::*; +use rustc_data_structures::graph::implementation as graph; +use rustc::middle::region; +use rustc::ty::{self, TyCtxt}; + +use rustc::hir::{self, PatKind}; +use rustc::hir::def_id::DefId; +use rustc::hir::ptr::P; + +struct CFGBuilder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + owner_def_id: DefId, + tables: &'a ty::TypeckTables<'tcx>, + graph: CFGGraph, + fn_exit: CFGIndex, + loop_scopes: Vec, + breakable_block_scopes: Vec, +} + +#[derive(Copy, Clone)] +struct BlockScope { + block_expr_id: hir::ItemLocalId, // id of breakable block expr node + break_index: CFGIndex, // where to go on `break` +} + +#[derive(Copy, Clone)] +struct LoopScope { + loop_id: hir::ItemLocalId, // id of loop/while node + continue_index: CFGIndex, // where to go on a `loop` + break_index: CFGIndex, // where to go on a `break` +} + +pub(super) fn construct(tcx: TyCtxt<'_>, body: &hir::Body) -> CFG { + let mut graph = graph::Graph::new(); + let entry = graph.add_node(CFGNodeData::Entry); + + // `fn_exit` is target of return exprs, which lies somewhere + // outside input `body`. (Distinguishing `fn_exit` and `body_exit` + // also resolves chicken-and-egg problem that arises if you try to + // have return exprs jump to `body_exit` during construction.) + let fn_exit = graph.add_node(CFGNodeData::Exit); + let body_exit; + + // Find the tables for this body. + let owner_def_id = tcx.hir().body_owner_def_id(body.id()); + let tables = tcx.typeck_tables_of(owner_def_id); + + let mut cfg_builder = CFGBuilder { + tcx, + owner_def_id, + tables, + graph, + fn_exit, + loop_scopes: Vec::new(), + breakable_block_scopes: Vec::new(), + }; + body_exit = cfg_builder.expr(&body.value, entry); + cfg_builder.add_contained_edge(body_exit, fn_exit); + let CFGBuilder { graph, .. } = cfg_builder; + CFG { + owner_def_id, + graph, + entry, + exit: fn_exit, + } +} + +impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { + fn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex { + if blk.targeted_by_break { + let expr_exit = self.add_ast_node(blk.hir_id.local_id, &[]); + + self.breakable_block_scopes.push(BlockScope { + block_expr_id: blk.hir_id.local_id, + break_index: expr_exit, + }); + + let mut stmts_exit = pred; + for stmt in &blk.stmts { + stmts_exit = self.stmt(stmt, stmts_exit); + } + let blk_expr_exit = self.opt_expr(&blk.expr, stmts_exit); + self.add_contained_edge(blk_expr_exit, expr_exit); + + self.breakable_block_scopes.pop(); + + expr_exit + } else { + let mut stmts_exit = pred; + for stmt in &blk.stmts { + stmts_exit = self.stmt(stmt, stmts_exit); + } + + let expr_exit = self.opt_expr(&blk.expr, stmts_exit); + + self.add_ast_node(blk.hir_id.local_id, &[expr_exit]) + } + } + + fn stmt(&mut self, stmt: &hir::Stmt, pred: CFGIndex) -> CFGIndex { + let exit = match stmt.node { + hir::StmtKind::Local(ref local) => { + let init_exit = self.opt_expr(&local.init, pred); + self.pat(&local.pat, init_exit) + } + hir::StmtKind::Item(_) => { + pred + } + hir::StmtKind::Expr(ref expr) | + hir::StmtKind::Semi(ref expr) => { + self.expr(&expr, pred) + } + }; + self.add_ast_node(stmt.hir_id.local_id, &[exit]) + } + + fn pat(&mut self, pat: &hir::Pat, pred: CFGIndex) -> CFGIndex { + match pat.node { + PatKind::Binding(.., None) | + PatKind::Path(_) | + PatKind::Lit(..) | + PatKind::Range(..) | + PatKind::Wild => self.add_ast_node(pat.hir_id.local_id, &[pred]), + + PatKind::Box(ref subpat) | + PatKind::Ref(ref subpat, _) | + PatKind::Binding(.., Some(ref subpat)) => { + let subpat_exit = self.pat(&subpat, pred); + self.add_ast_node(pat.hir_id.local_id, &[subpat_exit]) + } + + PatKind::TupleStruct(_, ref subpats, _) | + PatKind::Tuple(ref subpats, _) => { + let pats_exit = self.pats_all(subpats.iter(), pred); + self.add_ast_node(pat.hir_id.local_id, &[pats_exit]) + } + + PatKind::Struct(_, ref subpats, _) => { + let pats_exit = self.pats_all(subpats.iter().map(|f| &f.pat), pred); + self.add_ast_node(pat.hir_id.local_id, &[pats_exit]) + } + + PatKind::Or(ref pats) => { + let branches: Vec<_> = pats.iter().map(|p| self.pat(p, pred)).collect(); + self.add_ast_node(pat.hir_id.local_id, &branches) + } + + PatKind::Slice(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_ast_node(pat.hir_id.local_id, &[post_exit]) + } + } + } + + fn pats_all<'b, I: Iterator>>( + &mut self, + pats: I, + pred: CFGIndex + ) -> CFGIndex { + //! Handles case where all of the patterns must match. + pats.fold(pred, |pred, pat| self.pat(&pat, pred)) + } + + fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex { + match expr.node { + hir::ExprKind::Block(ref blk, _) => { + let blk_exit = self.block(&blk, pred); + self.add_ast_node(expr.hir_id.local_id, &[blk_exit]) + } + + hir::ExprKind::Loop(ref body, _, _) => { + // + // [pred] + // | + // v 1 + // [loopback] <---+ + // | 4 | + // v 3 | + // [body] ------+ + // + // [expr] 2 + // + // Note that `break` and `loop` statements + // may cause additional edges. + + let loopback = self.add_dummy_node(&[pred]); // 1 + let expr_exit = self.add_ast_node(expr.hir_id.local_id, &[]); // 2 + self.loop_scopes.push(LoopScope { + loop_id: expr.hir_id.local_id, + continue_index: loopback, + break_index: expr_exit, + }); + let body_exit = self.block(&body, loopback); // 3 + self.add_contained_edge(body_exit, loopback); // 4 + self.loop_scopes.pop(); + expr_exit + } + + hir::ExprKind::Match(ref discr, ref arms, _) => { + self.match_(expr.hir_id.local_id, &discr, &arms, pred) + } + + hir::ExprKind::Binary(op, ref l, ref r) if op.node.is_lazy() => { + // + // [pred] + // | + // v 1 + // [l] + // | + // / \ + // / \ + // v 2 * + // [r] | + // | | + // v 3 v 4 + // [..exit..] + // + let l_exit = self.expr(&l, pred); // 1 + let r_exit = self.expr(&r, l_exit); // 2 + self.add_ast_node(expr.hir_id.local_id, &[l_exit, r_exit]) // 3,4 + } + + hir::ExprKind::Ret(ref v) => { + let v_exit = self.opt_expr(v, pred); + let b = self.add_ast_node(expr.hir_id.local_id, &[v_exit]); + self.add_returning_edge(expr, b); + self.add_unreachable_node() + } + + hir::ExprKind::Break(destination, ref opt_expr) => { + let v = self.opt_expr(opt_expr, pred); + let (target_scope, break_dest) = + self.find_scope_edge(expr, destination, ScopeCfKind::Break); + let b = self.add_ast_node(expr.hir_id.local_id, &[v]); + self.add_exiting_edge(expr, b, target_scope, break_dest); + self.add_unreachable_node() + } + + hir::ExprKind::Continue(destination) => { + let (target_scope, cont_dest) = + self.find_scope_edge(expr, destination, ScopeCfKind::Continue); + let a = self.add_ast_node(expr.hir_id.local_id, &[pred]); + self.add_exiting_edge(expr, a, target_scope, cont_dest); + self.add_unreachable_node() + } + + hir::ExprKind::Array(ref elems) => { + self.straightline(expr, pred, elems.iter().map(|e| &*e)) + } + + hir::ExprKind::Call(ref func, ref args) => { + self.call(expr, pred, &func, args.iter().map(|e| &*e)) + } + + hir::ExprKind::MethodCall(.., ref args) => { + self.call(expr, pred, &args[0], args[1..].iter().map(|e| &*e)) + } + + hir::ExprKind::Index(ref l, ref r) | + hir::ExprKind::Binary(_, ref l, ref r) if self.tables.is_method_call(expr) => { + self.call(expr, pred, &l, Some(&**r).into_iter()) + } + + hir::ExprKind::Unary(_, ref e) if self.tables.is_method_call(expr) => { + self.call(expr, pred, &e, None::.iter()) + } + + hir::ExprKind::Tup(ref exprs) => { + self.straightline(expr, pred, exprs.iter().map(|e| &*e)) + } + + hir::ExprKind::Struct(_, ref fields, ref base) => { + let field_cfg = self.straightline(expr, pred, fields.iter().map(|f| &*f.expr)); + self.opt_expr(base, field_cfg) + } + + hir::ExprKind::Assign(ref l, ref r) | + hir::ExprKind::AssignOp(_, ref l, ref r) => { + self.straightline(expr, pred, [r, l].iter().map(|&e| &**e)) + } + + hir::ExprKind::Index(ref l, ref r) | + hir::ExprKind::Binary(_, ref l, ref r) => { // N.B., && and || handled earlier + self.straightline(expr, pred, [l, r].iter().map(|&e| &**e)) + } + + hir::ExprKind::Box(ref e) | + hir::ExprKind::AddrOf(_, ref e) | + hir::ExprKind::Cast(ref e, _) | + hir::ExprKind::Type(ref e, _) | + hir::ExprKind::DropTemps(ref e) | + hir::ExprKind::Unary(_, ref e) | + hir::ExprKind::Field(ref e, _) | + hir::ExprKind::Yield(ref e, _) | + hir::ExprKind::Repeat(ref e, _) => { + self.straightline(expr, pred, Some(&**e).into_iter()) + } + + hir::ExprKind::InlineAsm(_, ref outputs, ref inputs) => { + let post_outputs = self.exprs(outputs.iter().map(|e| &*e), pred); + let post_inputs = self.exprs(inputs.iter().map(|e| &*e), post_outputs); + self.add_ast_node(expr.hir_id.local_id, &[post_inputs]) + } + + hir::ExprKind::Closure(..) | + hir::ExprKind::Lit(..) | + hir::ExprKind::Path(_) | + hir::ExprKind::Err => { + self.straightline(expr, pred, None::.iter()) + } + } + } + + fn call<'b, I: Iterator>(&mut self, + call_expr: &hir::Expr, + pred: CFGIndex, + func_or_rcvr: &hir::Expr, + args: I) -> CFGIndex { + let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); + let ret = self.straightline(call_expr, func_or_rcvr_exit, args); + let m = self.tcx.hir().get_module_parent(call_expr.hir_id); + if self.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(call_expr)) { + self.add_unreachable_node() + } else { + ret + } + } + + fn exprs<'b, I: Iterator>(&mut self, + exprs: I, + pred: CFGIndex) -> CFGIndex { + //! Constructs graph for `exprs` evaluated in order + exprs.fold(pred, |p, e| self.expr(e, p)) + } + + fn opt_expr(&mut self, + opt_expr: &Option>, + pred: CFGIndex) -> CFGIndex { + //! Constructs graph for `opt_expr` evaluated, if Some + opt_expr.iter().fold(pred, |p, e| self.expr(&e, p)) + } + + fn straightline<'b, I: Iterator>(&mut self, + expr: &hir::Expr, + pred: CFGIndex, + subexprs: I) -> CFGIndex { + //! Handles case of an expression that evaluates `subexprs` in order + + let subexprs_exit = self.exprs(subexprs, pred); + self.add_ast_node(expr.hir_id.local_id, &[subexprs_exit]) + } + + fn match_(&mut self, id: hir::ItemLocalId, discr: &hir::Expr, + arms: &[hir::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 MIR contains. + // 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). MIR 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_guard = None; + let match_scope = region::Scope { id, data: region::ScopeData::Node }; + + 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 bindings_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 = match guard { + hir::Guard::If(ref e) => (&**e, self.expr(e, guard_start)), + }; + // #47295: We used to have very special case code + // here for when a pair of arms are both formed + // solely from constants, and if so, not add these + // edges. But this was not actually sound without + // other constraints that we stopped enforcing at + // some point. + if let Some((prev_guard, prev_index)) = prev_guard.take() { + self.add_exiting_edge(prev_guard, prev_index, match_scope, guard_start); + } + + // Push the guard onto the list of previous guards + prev_guard = Some(guard_exit); + + // Update the exit node for the pattern + pat_exit = guard_exit.1; + } + + // Add an edge from the exit of this pattern to the + // exit of the arm + self.add_contained_edge(pat_exit, bindings_exit); + } + + // Visit the body of this arm + let body_exit = self.expr(&arm.body, bindings_exit); + + let arm_exit = self.add_ast_node(arm.hir_id.local_id, &[body_exit]); + + // Link the body to the exit of the expression + self.add_contained_edge(arm_exit, expr_exit); + } + + expr_exit + } + + fn add_dummy_node(&mut self, preds: &[CFGIndex]) -> CFGIndex { + self.add_node(CFGNodeData::Dummy, preds) + } + + fn add_ast_node(&mut self, id: hir::ItemLocalId, preds: &[CFGIndex]) -> CFGIndex { + 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); + } + node + } + + fn add_contained_edge(&mut self, + source: CFGIndex, + target: CFGIndex) { + let data = CFGEdgeData {exiting_scopes: vec![] }; + self.graph.add_edge(source, target, data); + } + + fn add_exiting_edge(&mut self, + from_expr: &hir::Expr, + from_index: CFGIndex, + target_scope: region::Scope, + to_index: CFGIndex) { + let mut data = CFGEdgeData { exiting_scopes: vec![] }; + let mut scope = region::Scope { + id: from_expr.hir_id.local_id, + data: region::ScopeData::Node + }; + let region_scope_tree = self.tcx.region_scope_tree(self.owner_def_id); + while scope != target_scope { + data.exiting_scopes.push(scope.item_local_id()); + scope = region_scope_tree.encl_scope(scope); + } + self.graph.add_edge(from_index, to_index, data); + } + + fn add_returning_edge(&mut self, + _from_expr: &hir::Expr, + from_index: CFGIndex) { + let data = CFGEdgeData { + exiting_scopes: self.loop_scopes.iter() + .rev() + .map(|&LoopScope { loop_id: id, .. }| id) + .collect() + }; + self.graph.add_edge(from_index, self.fn_exit, data); + } + + fn find_scope_edge(&self, + expr: &hir::Expr, + destination: hir::Destination, + scope_cf_kind: ScopeCfKind) -> (region::Scope, CFGIndex) { + + match destination.target_id { + Ok(loop_id) => { + for b in &self.breakable_block_scopes { + if b.block_expr_id == loop_id.local_id { + let scope = region::Scope { + id: loop_id.local_id, + data: region::ScopeData::Node + }; + return (scope, match scope_cf_kind { + ScopeCfKind::Break => b.break_index, + ScopeCfKind::Continue => bug!("can't continue to block"), + }); + } + } + for l in &self.loop_scopes { + if l.loop_id == loop_id.local_id { + let scope = region::Scope { + id: loop_id.local_id, + data: region::ScopeData::Node + }; + return (scope, match scope_cf_kind { + ScopeCfKind::Break => l.break_index, + ScopeCfKind::Continue => l.continue_index, + }); + } + } + span_bug!(expr.span, "no scope for id {}", loop_id); + } + Err(err) => span_bug!(expr.span, "scope error: {}", err), + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +enum ScopeCfKind { + Break, + Continue, +} diff --git a/src/librustc_ast_borrowck/cfg/graphviz.rs b/src/librustc_ast_borrowck/cfg/graphviz.rs new file mode 100644 index 00000000000..46409f1a1ce --- /dev/null +++ b/src/librustc_ast_borrowck/cfg/graphviz.rs @@ -0,0 +1,120 @@ +/// This module provides linkage between rustc::middle::graph and +/// libgraphviz traits. + +use crate::cfg; +use rustc::hir; +use rustc::ty::TyCtxt; + +pub(crate) type Node<'a> = (cfg::CFGIndex, &'a cfg::CFGNode); +pub(crate) type Edge<'a> = &'a cfg::CFGEdge; + +pub struct LabelledCFG<'a, 'tcx> { + pub tcx: TyCtxt<'tcx>, + pub cfg: &'a cfg::CFG, + pub name: String, + /// `labelled_edges` controls whether we emit labels on the edges + pub labelled_edges: bool, +} + +impl<'a, 'tcx> LabelledCFG<'a, 'tcx> { + fn local_id_to_string(&self, local_id: hir::ItemLocalId) -> String { + assert!(self.cfg.owner_def_id.is_local()); + let hir_id = hir::HirId { + owner: self.tcx.hir().def_index_to_hir_id(self.cfg.owner_def_id.index).owner, + local_id + }; + let s = self.tcx.hir().node_to_string(hir_id); + + // Replacing newlines with \\l causes each line to be left-aligned, + // improving presentation of (long) pretty-printed expressions. + if s.contains("\n") { + let mut s = s.replace("\n", "\\l"); + // Apparently left-alignment applies to the line that precedes + // \l, not the line that follows; so, add \l at end of string + // if not already present, ensuring last line gets left-aligned + // as well. + let mut last_two: Vec<_> = + s.chars().rev().take(2).collect(); + last_two.reverse(); + if last_two != ['\\', 'l'] { + s.push_str("\\l"); + } + s + } else { + s + } + } +} + +impl<'a, 'hir> dot::Labeller<'a> for LabelledCFG<'a, 'hir> { + type Node = Node<'a>; + type Edge = Edge<'a>; + fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new(&self.name[..]).unwrap() } + + fn node_id(&'a self, &(i,_): &Node<'a>) -> dot::Id<'a> { + dot::Id::new(format!("N{}", i.node_id())).unwrap() + } + + fn node_label(&'a self, &(i, n): &Node<'a>) -> dot::LabelText<'a> { + if i == self.cfg.entry { + dot::LabelText::LabelStr("entry".into()) + } else if i == self.cfg.exit { + dot::LabelText::LabelStr("exit".into()) + } else if n.data.id() == hir::DUMMY_ITEM_LOCAL_ID { + dot::LabelText::LabelStr("(dummy_node)".into()) + } else { + let s = self.local_id_to_string(n.data.id()); + dot::LabelText::EscStr(s.into()) + } + } + + fn edge_label(&self, e: &Edge<'a>) -> dot::LabelText<'a> { + let mut label = String::new(); + if !self.labelled_edges { + return dot::LabelText::EscStr(label.into()); + } + let mut put_one = false; + for (i, &id) in e.data.exiting_scopes.iter().enumerate() { + if put_one { + label.push_str(",\\l"); + } else { + put_one = true; + } + let s = self.local_id_to_string(id); + label.push_str(&format!("exiting scope_{} {}", + i, + &s[..])); + } + dot::LabelText::EscStr(label.into()) + } +} + +impl<'a> dot::GraphWalk<'a> for &'a cfg::CFG { + type Node = Node<'a>; + type Edge = Edge<'a>; + fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { + let v: Vec<_> = self.graph.enumerated_nodes().collect(); + v.into() + } + fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { + self.graph.all_edges().iter().collect() + } + fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { + let i = edge.source(); + (i, self.graph.node(i)) + } + fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { + let i = edge.target(); + (i, self.graph.node(i)) + } +} + +impl<'a, 'hir> dot::GraphWalk<'a> for LabelledCFG<'a, 'hir> +{ + type Node = Node<'a>; + type Edge = Edge<'a>; + fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.cfg.nodes() } + fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.cfg.edges() } + fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) } + fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) } +} diff --git a/src/librustc_ast_borrowck/cfg/mod.rs b/src/librustc_ast_borrowck/cfg/mod.rs new file mode 100644 index 00000000000..981199c91d5 --- /dev/null +++ b/src/librustc_ast_borrowck/cfg/mod.rs @@ -0,0 +1,55 @@ +//! Module that constructs a control-flow graph representing an item. +//! Uses `Graph` as the underlying representation. + +use rustc_data_structures::graph::implementation as graph; +use rustc::ty::TyCtxt; +use rustc::hir; +use rustc::hir::def_id::DefId; + +mod construct; +pub mod graphviz; + +pub struct CFG { + owner_def_id: DefId, + pub(crate) graph: CFGGraph, + pub(crate) entry: CFGIndex, + exit: CFGIndex, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum CFGNodeData { + AST(hir::ItemLocalId), + Entry, + Exit, + Dummy, + Unreachable, +} + +impl CFGNodeData { + pub(crate) fn id(&self) -> hir::ItemLocalId { + if let CFGNodeData::AST(id) = *self { + id + } else { + hir::DUMMY_ITEM_LOCAL_ID + } + } +} + +#[derive(Debug)] +pub struct CFGEdgeData { + pub(crate) exiting_scopes: Vec +} + +pub(crate) type CFGIndex = graph::NodeIndex; + +pub(crate) type CFGGraph = graph::Graph; + +pub(crate) type CFGNode = graph::Node; + +pub(crate) type CFGEdge = graph::Edge; + +impl CFG { + pub fn new(tcx: TyCtxt<'_>, body: &hir::Body) -> CFG { + construct::construct(tcx, body) + } +} diff --git a/src/librustc_ast_borrowck/dataflow.rs b/src/librustc_ast_borrowck/dataflow.rs index 3a4c8c92476..a8562901d99 100644 --- a/src/librustc_ast_borrowck/dataflow.rs +++ b/src/librustc_ast_borrowck/dataflow.rs @@ -3,9 +3,7 @@ //! and thus uses bitvectors. Your job is simply to specify the so-called //! GEN and KILL bits for each expression. -use rustc::cfg; -use rustc::cfg::CFGIndex; -use rustc::ty::TyCtxt; +use crate::cfg::{self, CFGIndex}; use std::mem; use std::usize; use log::debug; @@ -16,6 +14,7 @@ use rustc::hir; use rustc::hir::intravisit; use rustc::hir::print as pprust; +use rustc::ty::TyCtxt; #[derive(Copy, Clone, Debug)] pub enum EntryOrExit { diff --git a/src/librustc_ast_borrowck/graphviz.rs b/src/librustc_ast_borrowck/graphviz.rs index 7a8a23ca76a..c077dc828ab 100644 --- a/src/librustc_ast_borrowck/graphviz.rs +++ b/src/librustc_ast_borrowck/graphviz.rs @@ -4,13 +4,12 @@ pub use Variant::*; -pub use rustc::cfg::graphviz::{Node, Edge}; -use rustc::cfg::graphviz as cfg_dot; - +pub(crate) use crate::cfg::graphviz::{Node, Edge}; +use crate::cfg::graphviz as cfg_dot; +use crate::cfg::CFGIndex; use crate::borrowck::{self, BorrowckCtxt, LoanPath}; use crate::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit}; use log::debug; -use rustc::cfg::CFGIndex; use std::rc::Rc; #[derive(Debug, Copy, Clone)] diff --git a/src/librustc_ast_borrowck/lib.rs b/src/librustc_ast_borrowck/lib.rs index dc818278a4b..aea97fea1a9 100644 --- a/src/librustc_ast_borrowck/lib.rs +++ b/src/librustc_ast_borrowck/lib.rs @@ -18,5 +18,6 @@ pub mod graphviz; mod dataflow; +pub mod cfg; pub use borrowck::provide; diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index cb17401f624..c4d3ad946f9 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -1,7 +1,5 @@ //! The various pretty-printing routines. -use rustc::cfg; -use rustc::cfg::graphviz::LabelledCFG; use rustc::hir; use rustc::hir::map as hir_map; use rustc::hir::map::blocks; @@ -14,6 +12,7 @@ use rustc_interface::util::ReplaceBodyWithLoop; use rustc_ast_borrowck as borrowck; use rustc_ast_borrowck::graphviz as borrowck_dot; +use rustc_ast_borrowck::cfg::{self, graphviz::LabelledCFG}; use rustc_mir::util::{write_mir_pretty, write_mir_graphviz}; use syntax::ast;