提交 e64f594f 编写于 作者: F Felix S. Klock II

Extend --pretty flowgraph=ID to include dataflow results in output.

Use one or more of the following `-Z` flag options to tell the
graphviz renderer to include the corresponding dataflow sets (after
the iterative constraint propagation reaches a fixed-point solution):

  * `-Z flowgraph-print-loans` : loans computed via middle::borrowck
  * `-Z flowgraph-print-moves` : moves computed via middle::borrowck::move_data
  * `-Z flowgraph-print-assigns` : assignments, via middle::borrowck::move_data
  * `-Z flowgraph-print-all` : all of the available sets are included.

Fix #15016.

----

This also adds a module, `syntax::ast_map::blocks`, that captures a
common abstraction shared amongst code blocks and procedure-like
things.  As part of this, moved `ast_map.rs` to subdir
`ast_map/mod.rs`, to follow our directory layout conventions.

(incorporated review feedback from huon, acrichto.)
上级 996263a0
......@@ -434,10 +434,37 @@ fn escape_str(s: &str) -> String {
/// 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 @@ fn test_input(g: LabelledGraph) -> IoResult<String> {
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,
......
......@@ -179,7 +179,11 @@ pub enum CrateType {
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
......
......@@ -19,6 +19,9 @@
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::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 @@ fn post(&self,
}
}
fn gather_flowgraph_variants(sess: &Session) -> Vec<borrowck_dot::Variant> {
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<W:io::Writer>(analysis: CrateAnalysis,
block: ast::P<ast::Block>,
fn print_flowgraph<W:io::Writer>(variants: Vec<borrowck_dot::Variant>,
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| {
......
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Variant>,
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<O:DataFlowOperator>(&self,
e: EntryOrExit,
cfgidx: CFGIndex,
dfcx: &DataFlowContext<'a, O>,
to_lp: |uint| -> Rc<LoanPath>) -> 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) }
}
......@@ -28,6 +28,7 @@
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 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<Loan>,
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<LoanPath> {
self.loan_path.clone()
}
}
#[deriving(PartialEq, Eq, Hash)]
pub enum LoanPath {
LpVar(ast::NodeId), // `x` in doc.rs
......
......@@ -189,7 +189,7 @@ pub fn new() -> MoveData {
}
}
fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath> {
pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath> {
self.paths.borrow().get(index.get()).loan_path.clone()
}
......@@ -534,7 +534,7 @@ fn kill_moves(&self,
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)
......
......@@ -117,3 +117,4 @@ fn edges(&self) -> dot::Edges<'a, Edge<'a>> { self.cfg.edges() }
fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) }
fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) }
}
......@@ -28,6 +28,9 @@
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>) -> 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 @@ fn pre(&self,
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 @@ pub fn add_kill(&mut self, id: ast::NodeId, bit: uint) {
}
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 @@ fn apply_gen_kill(&mut self, cfgidx: CFGIndex, bits: &mut [uint]) {
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 @@ pub fn each_bit_on_entry_frozen(&self,
-> 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);
......
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<FnDecl>,
pub body: P<Block>,
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<Block>),
}
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<Code> {
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<ast::FnDecl>,
style: ast::FnStyle,
abi: abi::Abi,
generics: &'a ast::Generics,
body: P<Block>,
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<FnDecl>,
body: P<Block>,
id: NodeId,
span: Span
}
impl ClosureParts {
fn new(d: P<FnDecl>, b: P<Block>, 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<Block> {
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<FnDecl> {
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"),
}
}
}
......@@ -24,6 +24,8 @@
use std::iter;
use std::slice;
pub mod blocks;
#[deriving(Clone, PartialEq)]
pub enum PathElem {
PathMod(Name),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册