diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index 9d2f43b9513..a3653fa9735 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -434,10 +434,37 @@ impl<'a> LabelText<'a> { /// Renders text as string suitable for a label in a .dot file. pub fn escape(&self) -> String { match self { - &LabelStr(ref s) => s.as_slice().escape_default().to_string(), - &EscStr(ref s) => LabelText::escape_str(s.as_slice()).to_string(), + &LabelStr(ref s) => s.as_slice().escape_default(), + &EscStr(ref s) => LabelText::escape_str(s.as_slice()), } } + + /// Decomposes content into string suitable for making EscStr that + /// yields same content as self. The result obeys the law + /// render(`lt`) == render(`EscStr(lt.pre_escaped_content())`) for + /// all `lt: LabelText`. + fn pre_escaped_content(self) -> str::MaybeOwned<'a> { + match self { + EscStr(s) => s, + LabelStr(s) => if s.as_slice().contains_char('\\') { + str::Owned(s.as_slice().escape_default()) + } else { + s + }, + } + } + + /// Puts `prefix` on a line above this label, with a blank line separator. + pub fn prefix_line(self, prefix: LabelText) -> LabelText { + prefix.suffix_line(self) + } + + /// Puts `suffix` on a line below this label, with a blank line separator. + pub fn suffix_line(self, suffix: LabelText) -> LabelText { + let prefix = self.pre_escaped_content().into_string(); + let suffix = suffix.pre_escaped_content(); + EscStr(str::Owned(prefix.append(r"\n\n").append(suffix.as_slice()))) + } } pub type Nodes<'a,N> = MaybeOwnedVector<'a,N>; @@ -664,10 +691,7 @@ mod tests { let mut writer = MemWriter::new(); render(&g, &mut writer).unwrap(); let mut r = BufReader::new(writer.get_ref()); - match r.read_to_string() { - Ok(string) => Ok(string.to_string()), - Err(err) => Err(err), - } + r.read_to_string() } // All of the tests use raw-strings as the format for the expected outputs, diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index b726c50afe9..9fe32c6fbcb 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -179,7 +179,11 @@ debugging_opts!( AST_JSON, AST_JSON_NOEXPAND, LS, - SAVE_ANALYSIS + SAVE_ANALYSIS, + FLOWGRAPH_PRINT_LOANS, + FLOWGRAPH_PRINT_MOVES, + FLOWGRAPH_PRINT_ASSIGNS, + FLOWGRAPH_PRINT_ALL ] 0 ) @@ -215,7 +219,15 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> { ("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND), ("ls", "List the symbols defined by a library crate", LS), ("save-analysis", "Write syntax and type analysis information \ - in addition to normal output", SAVE_ANALYSIS)) + in addition to normal output", SAVE_ANALYSIS), + ("flowgraph-print-loans", "Include loan analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_LOANS), + ("flowgraph-print-moves", "Include move analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_MOVES), + ("flowgraph-print-assigns", "Include assignment analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_ASSIGNS), + ("flowgraph-print-all", "Include all dataflow analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_ALL)) } /// Declare a macro that will define all CodegenOptions fields and parsers all diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index d03bf3593fa..ca9cbe1306e 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -19,6 +19,9 @@ use lib::llvm::{ContextRef, ModuleRef}; use lint; use metadata::common::LinkMeta; use metadata::creader; +use middle::borrowck::{FnPartsWithCFG}; +use middle::borrowck; +use borrowck_dot = middle::borrowck::graphviz; use middle::cfg; use middle::cfg::graphviz::LabelledCFG; use middle::{trans, freevars, stability, kind, ty, typeck, reachable}; @@ -40,6 +43,7 @@ use std::io; use std::io::fs; use std::io::MemReader; use syntax::ast; +use syntax::ast_map::blocks; use syntax::attr; use syntax::attr::{AttrMetaMethods}; use syntax::diagnostics; @@ -662,6 +666,25 @@ impl pprust::PpAnn for TypedAnnotation { } } +fn gather_flowgraph_variants(sess: &Session) -> Vec { + let print_loans = config::FLOWGRAPH_PRINT_LOANS; + let print_moves = config::FLOWGRAPH_PRINT_MOVES; + let print_assigns = config::FLOWGRAPH_PRINT_ASSIGNS; + let print_all = config::FLOWGRAPH_PRINT_ALL; + let opt = |print_which| sess.debugging_opt(print_which); + let mut variants = Vec::new(); + if opt(print_all) || opt(print_loans) { + variants.push(borrowck_dot::Loans); + } + if opt(print_all) || opt(print_moves) { + variants.push(borrowck_dot::Moves); + } + if opt(print_all) || opt(print_assigns) { + variants.push(borrowck_dot::Assigns); + } + variants +} + pub fn pretty_print_input(sess: Session, cfg: ast::CrateConfig, input: &Input, @@ -733,10 +756,17 @@ pub fn pretty_print_input(sess: Session, sess.fatal(format!("--pretty flowgraph couldn't find id: {}", nodeid).as_slice()) }); - let block = match node { - syntax::ast_map::NodeBlock(block) => block, - _ => { - let message = format!("--pretty=flowgraph needs block, got {:?}", + let code = blocks::Code::from_node(node); + match code { + Some(code) => { + let variants = gather_flowgraph_variants(&sess); + let analysis = phase_3_run_analysis_passes(sess, &krate, + ast_map, id); + print_flowgraph(variants, analysis, code, out) + } + None => { + let message = format!("--pretty=flowgraph needs \ + block, fn, or method; got {:?}", node); // point to what was found, if there's an @@ -746,10 +776,7 @@ pub fn pretty_print_input(sess: Session, None => sess.fatal(message.as_slice()) } } - }; - let analysis = phase_3_run_analysis_passes(sess, &krate, - ast_map, id); - print_flowgraph(analysis, block, out) + } } _ => { pprust::print_crate(sess.codemap(), @@ -765,17 +792,52 @@ pub fn pretty_print_input(sess: Session, } -fn print_flowgraph(analysis: CrateAnalysis, - block: ast::P, +fn print_flowgraph(variants: Vec, + analysis: CrateAnalysis, + code: blocks::Code, mut out: W) -> io::IoResult<()> { let ty_cx = &analysis.ty_cx; - let cfg = cfg::CFG::new(ty_cx, &*block); - let lcfg = LabelledCFG { ast_map: &ty_cx.map, - cfg: &cfg, - name: format!("block{}", block.id), }; + let cfg = match code { + blocks::BlockCode(block) => cfg::CFG::new(ty_cx, &*block), + blocks::FnLikeCode(fn_like) => cfg::CFG::new(ty_cx, fn_like.body()), + }; debug!("cfg: {:?}", cfg); - let r = dot::render(&lcfg, &mut out); - return expand_err_details(r); + + match code { + _ if variants.len() == 0 => { + let lcfg = LabelledCFG { + ast_map: &ty_cx.map, + cfg: &cfg, + name: format!("node_{}", code.id()), + }; + let r = dot::render(&lcfg, &mut out); + return expand_err_details(r); + } + blocks::BlockCode(_) => { + ty_cx.sess.err("--pretty flowgraph with -Z flowgraph-print \ + annotations requires fn-like node id."); + return Ok(()) + } + blocks::FnLikeCode(fn_like) => { + let fn_parts = FnPartsWithCFG::from_fn_like(&fn_like, &cfg); + let (bccx, analysis_data) = + borrowck::build_borrowck_dataflow_data_for_fn(ty_cx, fn_parts); + + let lcfg = LabelledCFG { + ast_map: &ty_cx.map, + cfg: &cfg, + name: format!("node_{}", code.id()), + }; + let lcfg = borrowck_dot::DataflowLabeller { + inner: lcfg, + variants: variants, + borrowck_ctxt: &bccx, + analysis_data: &analysis_data, + }; + let r = dot::render(&lcfg, &mut out); + return expand_err_details(r); + } + } fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> { r.map_err(|ioerr| { diff --git a/src/librustc/middle/borrowck/graphviz.rs b/src/librustc/middle/borrowck/graphviz.rs new file mode 100644 index 00000000000..e2ddb4a703a --- /dev/null +++ b/src/librustc/middle/borrowck/graphviz.rs @@ -0,0 +1,148 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This module provides linkage between rustc::middle::graph and +//! libgraphviz traits, specialized to attaching borrowck analysis +//! data to rendered labels. + +/// For clarity, rename the graphviz crate locally to dot. +use dot = graphviz; +pub use middle::cfg::graphviz::{Node, Edge}; +use cfg_dot = middle::cfg::graphviz; + +use middle::borrowck; +use middle::borrowck::{BorrowckCtxt, LoanPath}; +use middle::cfg::{CFGIndex}; +use middle::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit}; +use middle::dataflow; + +use std::rc::Rc; +use std::str; + +#[deriving(Show)] +pub enum Variant { + Loans, + Moves, + Assigns, +} + +impl Variant { + pub fn short_name(&self) -> &'static str { + match *self { + Loans => "loans", + Moves => "moves", + Assigns => "assigns", + } + } +} + +pub struct DataflowLabeller<'a> { + pub inner: cfg_dot::LabelledCFG<'a>, + pub variants: Vec, + pub borrowck_ctxt: &'a BorrowckCtxt<'a>, + pub analysis_data: &'a borrowck::AnalysisData<'a>, +} + +impl<'a> DataflowLabeller<'a> { + fn dataflow_for(&self, e: EntryOrExit, n: &Node<'a>) -> String { + let id = n.val1().data.id; + debug!("dataflow_for({}, id={}) {}", e, id, self.variants); + let mut sets = "".to_string(); + let mut seen_one = false; + for &variant in self.variants.iter() { + if seen_one { sets.push_str(" "); } else { seen_one = true; } + sets.push_str(variant.short_name()); + sets.push_str(": "); + sets.push_str(self.dataflow_for_variant(e, n, variant).as_slice()); + } + sets + } + + fn dataflow_for_variant(&self, e: EntryOrExit, n: &Node, v: Variant) -> String { + let cfgidx = n.val0(); + match v { + Loans => self.dataflow_loans_for(e, cfgidx), + Moves => self.dataflow_moves_for(e, cfgidx), + Assigns => self.dataflow_assigns_for(e, cfgidx), + } + } + + fn build_set(&self, + e: EntryOrExit, + cfgidx: CFGIndex, + dfcx: &DataFlowContext<'a, O>, + to_lp: |uint| -> Rc) -> String { + let mut saw_some = false; + let mut set = "{".to_string(); + dfcx.each_bit_for_node(e, cfgidx, |index| { + let lp = to_lp(index); + if saw_some { + set.push_str(", "); + } + let loan_str = self.borrowck_ctxt.loan_path_to_string(&*lp); + set.push_str(loan_str.as_slice()); + saw_some = true; + true + }); + set.append("}") + } + + fn dataflow_loans_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String { + let dfcx = &self.analysis_data.loans; + let loan_index_to_path = |loan_index| { + let all_loans = &self.analysis_data.all_loans; + all_loans.get(loan_index).loan_path() + }; + self.build_set(e, cfgidx, dfcx, loan_index_to_path) + } + + fn dataflow_moves_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String { + let dfcx = &self.analysis_data.move_data.dfcx_moves; + let move_index_to_path = |move_index| { + let move_data = &self.analysis_data.move_data.move_data; + let moves = move_data.moves.borrow(); + let move = moves.get(move_index); + move_data.path_loan_path(move.path) + }; + self.build_set(e, cfgidx, dfcx, move_index_to_path) + } + + fn dataflow_assigns_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String { + let dfcx = &self.analysis_data.move_data.dfcx_assign; + let assign_index_to_path = |assign_index| { + let move_data = &self.analysis_data.move_data.move_data; + let assignments = move_data.var_assignments.borrow(); + let assignment = assignments.get(assign_index); + move_data.path_loan_path(assignment.path) + }; + self.build_set(e, cfgidx, dfcx, assign_index_to_path) + } +} + +impl<'a> dot::Labeller<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a> { + fn graph_id(&'a self) -> dot::Id<'a> { self.inner.graph_id() } + fn node_id(&'a self, n: &Node<'a>) -> dot::Id<'a> { self.inner.node_id(n) } + fn node_label(&'a self, n: &Node<'a>) -> dot::LabelText<'a> { + let prefix = self.dataflow_for(dataflow::Entry, n); + let suffix = self.dataflow_for(dataflow::Exit, n); + let inner_label = self.inner.node_label(n); + inner_label + .prefix_line(dot::LabelStr(str::Owned(prefix))) + .suffix_line(dot::LabelStr(str::Owned(suffix))) + } + fn edge_label(&'a self, e: &Edge<'a>) -> dot::LabelText<'a> { self.inner.edge_label(e) } +} + +impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a> { + fn nodes(&self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() } + fn edges(&self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() } + fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) } + fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.target(edge) } +} diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 426a1fbede5..77b3cfafa63 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -28,6 +28,7 @@ use std::gc::{Gc, GC}; use std::string::String; use syntax::ast; use syntax::ast_map; +use syntax::ast_map::blocks::{FnLikeNode, FnParts}; use syntax::ast_util; use syntax::codemap::Span; use syntax::parse::token; @@ -50,6 +51,8 @@ pub mod check_loans; pub mod gather_loans; +pub mod graphviz; + pub mod move_data; #[deriving(Clone)] @@ -116,6 +119,13 @@ fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) { } } +/// Collection of conclusions determined via borrow checker analyses. +pub struct AnalysisData<'a> { + pub all_loans: Vec, + pub loans: DataFlowContext<'a, LoanDataFlowOperator>, + pub move_data: move_data::FlowedMoveData<'a>, +} + fn borrowck_fn(this: &mut BorrowckCtxt, fk: &FnKind, decl: &ast::FnDecl, @@ -123,18 +133,35 @@ fn borrowck_fn(this: &mut BorrowckCtxt, sp: Span, id: ast::NodeId) { debug!("borrowck_fn(id={})", id); + let cfg = cfg::CFG::new(this.tcx, body); + let AnalysisData { all_loans, + loans: loan_dfcx, + move_data:flowed_moves } = + build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id); + check_loans::check_loans(this, &loan_dfcx, flowed_moves, + all_loans.as_slice(), decl, body); + + visit::walk_fn(this, fk, decl, body, sp, ()); +} + +fn build_borrowck_dataflow_data<'a>(this: &mut BorrowckCtxt<'a>, + fk: &FnKind, + decl: &ast::FnDecl, + cfg: &cfg::CFG, + body: &ast::Block, + sp: Span, + id: ast::NodeId) -> AnalysisData<'a> { // Check the body of fn items. let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id); let (all_loans, move_data) = gather_loans::gather_loans_in_fn(this, decl, body); - let cfg = cfg::CFG::new(this.tcx, body); let mut loan_dfcx = DataFlowContext::new(this.tcx, "borrowck", Some(decl), - &cfg, + cfg, LoanDataFlowOperator, id_range, all_loans.len()); @@ -142,20 +169,57 @@ fn borrowck_fn(this: &mut BorrowckCtxt, loan_dfcx.add_gen(loan.gen_scope, loan_idx); loan_dfcx.add_kill(loan.kill_scope, loan_idx); } - loan_dfcx.add_kills_from_flow_exits(&cfg); - loan_dfcx.propagate(&cfg, body); + loan_dfcx.add_kills_from_flow_exits(cfg); + loan_dfcx.propagate(cfg, body); let flowed_moves = move_data::FlowedMoveData::new(move_data, this.tcx, - &cfg, + cfg, id_range, decl, body); - check_loans::check_loans(this, &loan_dfcx, flowed_moves, - all_loans.as_slice(), decl, body); + AnalysisData { all_loans: all_loans, + loans: loan_dfcx, + move_data:flowed_moves } +} - visit::walk_fn(this, fk, decl, body, sp, ()); +/// This and a `ty::ctxt` is all you need to run the dataflow analyses +/// used in the borrow checker. +pub struct FnPartsWithCFG<'a> { + pub fn_parts: FnParts<'a>, + pub cfg: &'a cfg::CFG, +} + +impl<'a> FnPartsWithCFG<'a> { + pub fn from_fn_like(f: &'a FnLikeNode, + g: &'a cfg::CFG) -> FnPartsWithCFG<'a> { + FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g } + } +} + +/// Accessor for introspective clients inspecting `AnalysisData` and +/// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer. +pub fn build_borrowck_dataflow_data_for_fn<'a>( + tcx: &'a ty::ctxt, + input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a>, AnalysisData<'a>) { + + let mut bccx = BorrowckCtxt { + tcx: tcx, + stats: box(GC) BorrowStats { + loaned_paths_same: Cell::new(0), + loaned_paths_imm: Cell::new(0), + stable_paths: Cell::new(0), + guaranteed_paths: Cell::new(0), + } + }; + + let p = input.fn_parts; + + let dataflow_data = build_borrowck_dataflow_data( + &mut bccx, &p.kind, p.decl, input.cfg, p.body, p.span, p.id); + + (bccx, dataflow_data) } // ---------------------------------------------------------------------- @@ -198,6 +262,12 @@ pub struct Loan { cause: euv::LoanCause, } +impl Loan { + pub fn loan_path(&self) -> Rc { + self.loan_path.clone() + } +} + #[deriving(PartialEq, Eq, Hash)] pub enum LoanPath { LpVar(ast::NodeId), // `x` in doc.rs diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs index b61596908e6..a9c312fc0a4 100644 --- a/src/librustc/middle/borrowck/move_data.rs +++ b/src/librustc/middle/borrowck/move_data.rs @@ -189,7 +189,7 @@ impl MoveData { } } - fn path_loan_path(&self, index: MovePathIndex) -> Rc { + pub fn path_loan_path(&self, index: MovePathIndex) -> Rc { self.paths.borrow().get(index.get()).loan_path.clone() } @@ -534,7 +534,7 @@ impl MoveData { impl<'a> FlowedMoveData<'a> { pub fn new(move_data: MoveData, tcx: &'a ty::ctxt, - cfg: &'a cfg::CFG, + cfg: &cfg::CFG, id_range: ast_util::IdRange, decl: &ast::FnDecl, body: &ast::Block) diff --git a/src/librustc/middle/cfg/graphviz.rs b/src/librustc/middle/cfg/graphviz.rs index 9f44f0babc7..e9bcdff070d 100644 --- a/src/librustc/middle/cfg/graphviz.rs +++ b/src/librustc/middle/cfg/graphviz.rs @@ -117,3 +117,4 @@ impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for LabelledCFG<'a> fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) } fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) } } + diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index 7d9178162a6..b28c0158584 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -28,6 +28,9 @@ use syntax::visit; use syntax::print::{pp, pprust}; use util::nodemap::NodeMap; +#[deriving(Show)] +pub enum EntryOrExit { Entry, Exit } + #[deriving(Clone)] pub struct DataFlowContext<'a, O> { tcx: &'a ty::ctxt, @@ -93,17 +96,18 @@ fn to_cfgidx_or_die(id: ast::NodeId, index: &NodeMap) -> CFGIndex { } impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> { - fn has_bitset(&self, n: ast::NodeId) -> bool { + fn has_bitset_for_nodeid(&self, n: ast::NodeId) -> bool { assert!(n != ast::DUMMY_NODE_ID); match self.nodeid_to_index.find(&n) { None => false, - Some(&cfgidx) => { - let node_id = cfgidx.node_id(); - node_id < self.index_to_bitset.len() && - self.index_to_bitset.get(node_id).is_some() - } + Some(&cfgidx) => self.has_bitset_for_cfgidx(cfgidx), } } + fn has_bitset_for_cfgidx(&self, cfgidx: CFGIndex) -> bool { + let node_id = cfgidx.node_id(); + node_id < self.index_to_bitset.len() && + self.index_to_bitset.get(node_id).is_some() + } fn get_bitset_index(&self, cfgidx: CFGIndex) -> uint { let node_id = cfgidx.node_id(); self.index_to_bitset.get(node_id).unwrap() @@ -160,7 +164,7 @@ impl<'a, O:DataFlowOperator> pprust::PpAnn for DataFlowContext<'a, O> { pprust::NodePat(pat) => pat.id }; - if self.has_bitset(id) { + if self.has_bitset_for_nodeid(id) { let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index); let (start, end) = self.compute_id_range_frozen(cfgidx); let on_entry = self.on_entry.slice(start, end); @@ -287,7 +291,7 @@ impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> { } fn apply_gen_kill(&mut self, cfgidx: CFGIndex, bits: &mut [uint]) { - //! Applies the gen and kill sets for `id` to `bits` + //! Applies the gen and kill sets for `cfgidx` to `bits` debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [before]", self.analysis_name, cfgidx, mut_bits_to_string(bits)); let (start, end) = self.compute_id_range(cfgidx); @@ -300,6 +304,21 @@ impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> { self.analysis_name, cfgidx, mut_bits_to_string(bits)); } + fn apply_gen_kill_frozen(&self, cfgidx: CFGIndex, bits: &mut [uint]) { + //! Applies the gen and kill sets for `cfgidx` to `bits` + //! Only useful after `propagate()` has been called. + debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [before]", + self.analysis_name, cfgidx, mut_bits_to_string(bits)); + let (start, end) = self.compute_id_range_frozen(cfgidx); + let gens = self.gens.slice(start, end); + bitwise(bits, gens, &Union); + let kills = self.kills.slice(start, end); + bitwise(bits, kills, &Subtract); + + debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [after]", + self.analysis_name, cfgidx, mut_bits_to_string(bits)); + } + fn compute_id_range_frozen(&self, cfgidx: CFGIndex) -> (uint, uint) { let n = self.get_bitset_index(cfgidx); let start = n * self.words_per_id; @@ -327,21 +346,45 @@ impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> { -> bool { //! Iterates through each bit that is set on entry to `id`. //! Only useful after `propagate()` has been called. - if !self.has_bitset(id) { + 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) + } + + pub fn each_bit_for_node(&self, + e: EntryOrExit, + cfgidx: CFGIndex, + f: |uint| -> bool) + -> bool { + //! Iterates through each bit that is set on entry/exit to `cfgidx`. + //! Only useful after `propagate()` has been called. + if !self.has_bitset_for_cfgidx(cfgidx) { + return true; + } let (start, end) = self.compute_id_range_frozen(cfgidx); let on_entry = self.on_entry.slice(start, end); - debug!("{:s} each_bit_on_entry_frozen(id={:?}, on_entry={})", - self.analysis_name, id, bits_to_string(on_entry)); - self.each_bit(on_entry, f) + let temp_bits; + let slice = match e { + Entry => on_entry, + Exit => { + let mut t = on_entry.to_owned(); + self.apply_gen_kill_frozen(cfgidx, t.as_mut_slice()); + temp_bits = t; + temp_bits.as_slice() + } + }; + debug!("{:s} each_bit_for_node({}, cfgidx={}) bits={}", + self.analysis_name, e, cfgidx, bits_to_string(slice)); + self.each_bit(slice, f) } pub fn each_gen_bit_frozen(&self, id: ast::NodeId, f: |uint| -> bool) -> bool { //! Iterates through each bit in the gen set for `id`. - if !self.has_bitset(id) { + //! Only useful after `propagate()` has been called. + if !self.has_bitset_for_nodeid(id) { return true; } let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index); diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs new file mode 100644 index 00000000000..1280b884f11 --- /dev/null +++ b/src/libsyntax/ast_map/blocks.rs @@ -0,0 +1,218 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This module provides a simplified abstraction for working with +//! code blocks identified by their integer node-id. In particular, +//! it captures a common set of attributes that all "function-like +//! things" (represented by `FnLike` instances) share. For example, +//! all `FnLike` instances have a type signature (be it explicit or +//! inferred). And all `FnLike` instances have a body, i.e. the code +//! that is run when the function-like thing it represents is invoked. +//! +//! With the above abstraction in place, one can treat the program +//! text as a collection of blocks of code (and most such blocks are +//! nested within a uniquely determined `FnLike`), and users can ask +//! for the `Code` associated with a particular NodeId. + +use abi; +use ast::{P, Block, FnDecl, NodeId}; +use ast; +use ast_map::{Node}; +use ast_map; +use ast_util; +use codemap::Span; +use visit; + +/// An FnLikeNode is a Node that is like a fn, in that it has a decl +/// and a body (as well as a NodeId, a span, etc). +/// +/// More specifically, it is one of either: +/// - A function item, +/// - A closure expr (i.e. an ExprFnBlock or ExprProc), or +/// - The default implementation for a trait method. +/// +/// To construct one, use the `Code::from_node` function. +pub struct FnLikeNode { node: ast_map::Node } + +/// MaybeFnLike wraps a method that indicates if an object +/// corresponds to some FnLikeNode. +pub trait MaybeFnLike { fn is_fn_like(&self) -> bool; } + +/// Components shared by fn-like things (fn items, methods, closures). +pub struct FnParts<'a> { + pub decl: P, + pub body: P, + pub kind: visit::FnKind<'a>, + pub span: Span, + pub id: NodeId, +} + +impl MaybeFnLike for ast::Item { + fn is_fn_like(&self) -> bool { + match self.node { ast::ItemFn(..) => true, _ => false, } + } +} + +impl MaybeFnLike for ast::TraitMethod { + fn is_fn_like(&self) -> bool { + match *self { ast::Provided(_) => true, _ => false, } + } +} + +impl MaybeFnLike for ast::Expr { + fn is_fn_like(&self) -> bool { + match self.node { + ast::ExprFnBlock(..) | ast::ExprProc(..) => true, + _ => false, + } + } +} + +/// Carries either an FnLikeNode or a Block, as these are the two +/// constructs that correspond to "code" (as in, something from which +/// we can construct a control-flow graph). +pub enum Code { + FnLikeCode(FnLikeNode), + BlockCode(P), +} + +impl Code { + pub fn id(&self) -> ast::NodeId { + match *self { + FnLikeCode(node) => node.id(), + BlockCode(block) => block.id, + } + } + + /// Attempts to construct a Code from presumed FnLike or Block node input. + pub fn from_node(node: Node) -> Option { + fn new(node: Node) -> FnLikeNode { FnLikeNode { node: node } } + match node { + ast_map::NodeItem(item) if item.is_fn_like() => + Some(FnLikeCode(new(node))), + ast_map::NodeTraitMethod(tm) if tm.is_fn_like() => + Some(FnLikeCode(new(node))), + ast_map::NodeMethod(_) => + Some(FnLikeCode(new(node))), + ast_map::NodeExpr(e) if e.is_fn_like() => + Some(FnLikeCode(new(node))), + ast_map::NodeBlock(block) => + Some(BlockCode(block)), + _ => + None, + } + } +} + +/// These are all the components one can extract from a fn item for +/// use when implementing FnLikeNode operations. +struct ItemFnParts<'a> { + ident: ast::Ident, + decl: P, + style: ast::FnStyle, + abi: abi::Abi, + generics: &'a ast::Generics, + body: P, + id: ast::NodeId, + span: Span +} + +/// These are all the components one can extract from a closure expr +/// for use when implementing FnLikeNode operations. +struct ClosureParts { + decl: P, + body: P, + id: NodeId, + span: Span +} + +impl ClosureParts { + fn new(d: P, b: P, id: NodeId, s: Span) -> ClosureParts { + ClosureParts { decl: d, body: b, id: id, span: s } + } +} + +impl FnLikeNode { + pub fn to_fn_parts<'a>(&'a self) -> FnParts<'a> { + FnParts { + decl: self.decl(), + body: self.body(), + kind: self.kind(), + span: self.span(), + id: self.id(), + } + } + + pub fn body<'a>(&'a self) -> P { + self.handle(|i: ItemFnParts| i.body, + |m: &'a ast::Method| ast_util::method_body(m), + |c: ClosureParts| c.body) + } + + pub fn decl<'a>(&'a self) -> P { + self.handle(|i: ItemFnParts| i.decl, + |m: &'a ast::Method| ast_util::method_fn_decl(m), + |c: ClosureParts| c.decl) + } + + pub fn span<'a>(&'a self) -> Span { + self.handle(|i: ItemFnParts| i.span, + |m: &'a ast::Method| m.span, + |c: ClosureParts| c.span) + } + + pub fn id<'a>(&'a self) -> NodeId { + self.handle(|i: ItemFnParts| i.id, + |m: &'a ast::Method| m.id, + |c: ClosureParts| c.id) + } + + pub fn kind<'a>(&'a self) -> visit::FnKind<'a> { + let item = |p: ItemFnParts<'a>| -> visit::FnKind<'a> { + visit::FkItemFn(p.ident, p.generics, p.style, p.abi) + }; + let closure = |_: ClosureParts| { + visit::FkFnBlock + }; + let method = |m: &'a ast::Method| { + visit::FkMethod(ast_util::method_ident(m), ast_util::method_generics(m), m) + }; + self.handle(item, method, closure) + } + + fn handle<'a, A>(&'a self, + item_fn: |ItemFnParts<'a>| -> A, + method: |&'a ast::Method| -> A, + closure: |ClosureParts| -> A) -> A { + match self.node { + ast_map::NodeItem(ref i) => match i.node { + ast::ItemFn(decl, style, abi, ref generics, block) => + item_fn(ItemFnParts{ + ident: i.ident, decl: decl, style: style, body: block, + generics: generics, abi: abi, id: i.id, span: i.span + }), + _ => fail!("item FnLikeNode that is not fn-like"), + }, + ast_map::NodeTraitMethod(ref t) => match **t { + ast::Provided(ref m) => method(&**m), + _ => fail!("trait method FnLikeNode that is not fn-like"), + }, + ast_map::NodeMethod(ref m) => method(&**m), + ast_map::NodeExpr(ref e) => match e.node { + ast::ExprFnBlock(ref decl, ref block) => + closure(ClosureParts::new(*decl, *block, e.id, e.span)), + ast::ExprProc(ref decl, ref block) => + closure(ClosureParts::new(*decl, *block, e.id, e.span)), + _ => fail!("expr FnLikeNode that is not fn-like"), + }, + _ => fail!("other FnLikeNode that is not fn-like"), + } + } +} diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map/mod.rs similarity index 99% rename from src/libsyntax/ast_map.rs rename to src/libsyntax/ast_map/mod.rs index b8a0a31f9c3..50e487b63db 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map/mod.rs @@ -24,6 +24,8 @@ use std::gc::{Gc, GC}; use std::iter; use std::slice; +pub mod blocks; + #[deriving(Clone, PartialEq)] pub enum PathElem { PathMod(Name),