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.)
This commit is contained in:
parent
996263a015
commit
e64f594f10
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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<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| {
|
||||
|
148
src/librustc/middle/borrowck/graphviz.rs
Normal file
148
src/librustc/middle/borrowck/graphviz.rs
Normal file
@ -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 <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::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<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 @@ impl 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 @@ 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)
|
||||
|
@ -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) }
|
||||
}
|
||||
|
||||
|
@ -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>) -> 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);
|
||||
|
218
src/libsyntax/ast_map/blocks.rs
Normal file
218
src/libsyntax/ast_map/blocks.rs
Normal file
@ -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 <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::gc::{Gc, GC};
|
||||
use std::iter;
|
||||
use std::slice;
|
||||
|
||||
pub mod blocks;
|
||||
|
||||
#[deriving(Clone, PartialEq)]
|
||||
pub enum PathElem {
|
||||
PathMod(Name),
|
Loading…
x
Reference in New Issue
Block a user