Auto merge of #32156 - pnkfelix:borrowck-on-mir-move-analysis, r=nikomatsakis
Move analysis for MIR borrowck This PR adds code for doing MIR-based gathering of the moves in a `fn` and the dataflow to determine where uninitialized locations flow to, analogous to how the same thing is done in `borrowck`. It also adds a couple attributes to print out graphviz visualizations of the analyzed MIR that includes the dataflow analysis results. cc @nikomatsakis
This commit is contained in:
commit
e3f2dfdece
34 changed files with 2014 additions and 136 deletions
|
|
@ -97,7 +97,7 @@ DEPS_rustc := syntax fmt_macros flate arena serialize getopts rbml rustc_front\
|
|||
log graphviz rustc_llvm rustc_back rustc_data_structures\
|
||||
rustc_const_eval
|
||||
DEPS_rustc_back := std syntax rustc_llvm rustc_front flate log libc
|
||||
DEPS_rustc_borrowck := rustc rustc_front log graphviz syntax
|
||||
DEPS_rustc_borrowck := rustc rustc_front rustc_mir log graphviz syntax
|
||||
DEPS_rustc_data_structures := std log serialize
|
||||
DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
|
||||
rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \
|
||||
|
|
|
|||
|
|
@ -62,7 +62,9 @@
|
|||
//! dot::render(&edges, output).unwrap()
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a> dot::Labeller<'a, Nd, Ed> for Edges {
|
||||
//! impl<'a> dot::Labeller<'a> for Edges {
|
||||
//! type Node = Nd;
|
||||
//! type Edge = Ed;
|
||||
//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example1").unwrap() }
|
||||
//!
|
||||
//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> {
|
||||
|
|
@ -70,7 +72,9 @@
|
|||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a> dot::GraphWalk<'a, Nd, Ed> for Edges {
|
||||
//! impl<'a> dot::GraphWalk<'a> for Edges {
|
||||
//! type Node = Nd;
|
||||
//! type Edge = Ed;
|
||||
//! fn nodes(&self) -> dot::Nodes<'a,Nd> {
|
||||
//! // (assumes that |N| \approxeq |E|)
|
||||
//! let &Edges(ref v) = self;
|
||||
|
|
@ -167,7 +171,9 @@
|
|||
//! dot::render(&graph, output).unwrap()
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a> dot::Labeller<'a, Nd, Ed<'a>> for Graph {
|
||||
//! impl<'a> dot::Labeller<'a> for Graph {
|
||||
//! type Node = Nd;
|
||||
//! type Edge = Ed<'a>;
|
||||
//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example2").unwrap() }
|
||||
//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> {
|
||||
//! dot::Id::new(format!("N{}", n)).unwrap()
|
||||
|
|
@ -180,7 +186,9 @@
|
|||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a> dot::GraphWalk<'a, Nd, Ed<'a>> for Graph {
|
||||
//! impl<'a> dot::GraphWalk<'a> for Graph {
|
||||
//! type Node = Nd;
|
||||
//! type Edge = Ed<'a>;
|
||||
//! fn nodes(&self) -> dot::Nodes<'a,Nd> { (0..self.nodes.len()).collect() }
|
||||
//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { self.edges.iter().collect() }
|
||||
//! fn source(&self, e: &Ed) -> Nd { let & &(s,_) = e; s }
|
||||
|
|
@ -225,7 +233,9 @@
|
|||
//! dot::render(&graph, output).unwrap()
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a> dot::Labeller<'a, Nd<'a>, Ed<'a>> for Graph {
|
||||
//! impl<'a> dot::Labeller<'a> for Graph {
|
||||
//! type Node = Nd<'a>;
|
||||
//! type Edge = Ed<'a>;
|
||||
//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example3").unwrap() }
|
||||
//! fn node_id(&'a self, n: &Nd<'a>) -> dot::Id<'a> {
|
||||
//! dot::Id::new(format!("N{}", n.0)).unwrap()
|
||||
|
|
@ -239,7 +249,9 @@
|
|||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a> dot::GraphWalk<'a, Nd<'a>, Ed<'a>> for Graph {
|
||||
//! impl<'a> dot::GraphWalk<'a> for Graph {
|
||||
//! type Node = Nd<'a>;
|
||||
//! type Edge = Ed<'a>;
|
||||
//! fn nodes(&'a self) -> dot::Nodes<'a,Nd<'a>> {
|
||||
//! self.nodes.iter().map(|s| &s[..]).enumerate().collect()
|
||||
//! }
|
||||
|
|
@ -447,45 +459,48 @@ impl<'a> Id<'a> {
|
|||
/// The graph instance is responsible for providing the DOT compatible
|
||||
/// identifiers for the nodes and (optionally) rendered labels for the nodes and
|
||||
/// edges, as well as an identifier for the graph itself.
|
||||
pub trait Labeller<'a,N,E> {
|
||||
pub trait Labeller<'a> {
|
||||
type Node;
|
||||
type Edge;
|
||||
|
||||
/// Must return a DOT compatible identifier naming the graph.
|
||||
fn graph_id(&'a self) -> Id<'a>;
|
||||
|
||||
/// Maps `n` to a unique identifier with respect to `self`. The
|
||||
/// implementor is responsible for ensuring that the returned name
|
||||
/// is a valid DOT identifier.
|
||||
fn node_id(&'a self, n: &N) -> Id<'a>;
|
||||
fn node_id(&'a self, n: &Self::Node) -> Id<'a>;
|
||||
|
||||
/// Maps `n` to one of the [graphviz `shape` names][1]. If `None`
|
||||
/// is returned, no `shape` attribute is specified.
|
||||
///
|
||||
/// [1]: http://www.graphviz.org/content/node-shapes
|
||||
fn node_shape(&'a self, _node: &N) -> Option<LabelText<'a>> {
|
||||
fn node_shape(&'a self, _node: &Self::Node) -> Option<LabelText<'a>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Maps `n` to a label that will be used in the rendered output.
|
||||
/// The label need not be unique, and may be the empty string; the
|
||||
/// default is just the output from `node_id`.
|
||||
fn node_label(&'a self, n: &N) -> LabelText<'a> {
|
||||
fn node_label(&'a self, n: &Self::Node) -> LabelText<'a> {
|
||||
LabelStr(self.node_id(n).name)
|
||||
}
|
||||
|
||||
/// Maps `e` to a label that will be used in the rendered output.
|
||||
/// The label need not be unique, and may be the empty string; the
|
||||
/// default is in fact the empty string.
|
||||
fn edge_label(&'a self, e: &E) -> LabelText<'a> {
|
||||
fn edge_label(&'a self, e: &Self::Edge) -> LabelText<'a> {
|
||||
let _ignored = e;
|
||||
LabelStr("".into_cow())
|
||||
}
|
||||
|
||||
/// Maps `n` to a style that will be used in the rendered output.
|
||||
fn node_style(&'a self, _n: &N) -> Style {
|
||||
fn node_style(&'a self, _n: &Self::Node) -> Style {
|
||||
Style::None
|
||||
}
|
||||
|
||||
/// Maps `e` to a style that will be used in the rendered output.
|
||||
fn edge_style(&'a self, _e: &E) -> Style {
|
||||
fn edge_style(&'a self, _e: &Self::Edge) -> Style {
|
||||
Style::None
|
||||
}
|
||||
}
|
||||
|
|
@ -596,15 +611,18 @@ pub type Edges<'a,E> = Cow<'a,[E]>;
|
|||
/// `Cow<[T]>` to leave implementors the freedom to create
|
||||
/// entirely new vectors or to pass back slices into internally owned
|
||||
/// vectors.
|
||||
pub trait GraphWalk<'a, N: Clone, E: Clone> {
|
||||
pub trait GraphWalk<'a> {
|
||||
type Node: Clone;
|
||||
type Edge: Clone;
|
||||
|
||||
/// Returns all the nodes in this graph.
|
||||
fn nodes(&'a self) -> Nodes<'a, N>;
|
||||
fn nodes(&'a self) -> Nodes<'a, Self::Node>;
|
||||
/// Returns all of the edges in this graph.
|
||||
fn edges(&'a self) -> Edges<'a, E>;
|
||||
fn edges(&'a self) -> Edges<'a, Self::Edge>;
|
||||
/// The source node for `edge`.
|
||||
fn source(&'a self, edge: &E) -> N;
|
||||
fn source(&'a self, edge: &Self::Edge) -> Self::Node;
|
||||
/// The target node for `edge`.
|
||||
fn target(&'a self, edge: &E) -> N;
|
||||
fn target(&'a self, edge: &Self::Edge) -> Self::Node;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
|
|
@ -622,28 +640,26 @@ pub fn default_options() -> Vec<RenderOption> {
|
|||
|
||||
/// Renders directed graph `g` into the writer `w` in DOT syntax.
|
||||
/// (Simple wrapper around `render_opts` that passes a default set of options.)
|
||||
pub fn render<'a,
|
||||
N: Clone + 'a,
|
||||
E: Clone + 'a,
|
||||
G: Labeller<'a, N, E> + GraphWalk<'a, N, E>,
|
||||
W: Write>
|
||||
(g: &'a G,
|
||||
w: &mut W)
|
||||
-> io::Result<()> {
|
||||
pub fn render<'a,N,E,G,W>(g: &'a G, w: &mut W) -> io::Result<()>
|
||||
where N: Clone + 'a,
|
||||
E: Clone + 'a,
|
||||
G: Labeller<'a, Node=N, Edge=E> + GraphWalk<'a, Node=N, Edge=E>,
|
||||
W: Write
|
||||
{
|
||||
render_opts(g, w, &[])
|
||||
}
|
||||
|
||||
/// Renders directed graph `g` into the writer `w` in DOT syntax.
|
||||
/// (Main entry point for the library.)
|
||||
pub fn render_opts<'a,
|
||||
N: Clone + 'a,
|
||||
E: Clone + 'a,
|
||||
G: Labeller<'a, N, E> + GraphWalk<'a, N, E>,
|
||||
W: Write>
|
||||
(g: &'a G,
|
||||
w: &mut W,
|
||||
options: &[RenderOption])
|
||||
-> io::Result<()> {
|
||||
pub fn render_opts<'a, N, E, G, W>(g: &'a G,
|
||||
w: &mut W,
|
||||
options: &[RenderOption])
|
||||
-> io::Result<()>
|
||||
where N: Clone + 'a,
|
||||
E: Clone + 'a,
|
||||
G: Labeller<'a, Node=N, Edge=E> + GraphWalk<'a, Node=N, Edge=E>,
|
||||
W: Write
|
||||
{
|
||||
fn writeln<W: Write>(w: &mut W, arg: &[&str]) -> io::Result<()> {
|
||||
for &s in arg {
|
||||
try!(w.write_all(s.as_bytes()));
|
||||
|
|
@ -858,7 +874,9 @@ mod tests {
|
|||
Id::new(format!("N{}", *n)).unwrap()
|
||||
}
|
||||
|
||||
impl<'a> Labeller<'a, Node, &'a Edge> for LabelledGraph {
|
||||
impl<'a> Labeller<'a> for LabelledGraph {
|
||||
type Node = Node;
|
||||
type Edge = &'a Edge;
|
||||
fn graph_id(&'a self) -> Id<'a> {
|
||||
Id::new(&self.name[..]).unwrap()
|
||||
}
|
||||
|
|
@ -882,7 +900,9 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Labeller<'a, Node, &'a Edge> for LabelledGraphWithEscStrs {
|
||||
impl<'a> Labeller<'a> for LabelledGraphWithEscStrs {
|
||||
type Node = Node;
|
||||
type Edge = &'a Edge;
|
||||
fn graph_id(&'a self) -> Id<'a> {
|
||||
self.graph.graph_id()
|
||||
}
|
||||
|
|
@ -901,7 +921,9 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> GraphWalk<'a, Node, &'a Edge> for LabelledGraph {
|
||||
impl<'a> GraphWalk<'a> for LabelledGraph {
|
||||
type Node = Node;
|
||||
type Edge = &'a Edge;
|
||||
fn nodes(&'a self) -> Nodes<'a, Node> {
|
||||
(0..self.node_labels.len()).collect()
|
||||
}
|
||||
|
|
@ -916,7 +938,9 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> GraphWalk<'a, Node, &'a Edge> for LabelledGraphWithEscStrs {
|
||||
impl<'a> GraphWalk<'a> for LabelledGraphWithEscStrs {
|
||||
type Node = Node;
|
||||
type Edge = &'a Edge;
|
||||
fn nodes(&'a self) -> Nodes<'a, Node> {
|
||||
self.graph.nodes()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ pub use self::Code::*;
|
|||
use front::map::{self, Node};
|
||||
use syntax::abi;
|
||||
use rustc_front::hir::{Block, FnDecl};
|
||||
use syntax::ast::{Name, NodeId};
|
||||
use syntax::ast::{Attribute, Name, NodeId};
|
||||
use syntax::attr::ThinAttributesExt;
|
||||
use rustc_front::hir as ast;
|
||||
use syntax::codemap::Span;
|
||||
use rustc_front::intravisit::FnKind;
|
||||
|
|
@ -116,7 +117,8 @@ struct ItemFnParts<'a> {
|
|||
generics: &'a ast::Generics,
|
||||
body: &'a Block,
|
||||
id: NodeId,
|
||||
span: Span
|
||||
span: Span,
|
||||
attrs: &'a [Attribute],
|
||||
}
|
||||
|
||||
/// These are all the components one can extract from a closure expr
|
||||
|
|
@ -125,12 +127,13 @@ struct ClosureParts<'a> {
|
|||
decl: &'a FnDecl,
|
||||
body: &'a Block,
|
||||
id: NodeId,
|
||||
span: Span
|
||||
span: Span,
|
||||
attrs: &'a [Attribute],
|
||||
}
|
||||
|
||||
impl<'a> ClosureParts<'a> {
|
||||
fn new(d: &'a FnDecl, b: &'a Block, id: NodeId, s: Span) -> ClosureParts<'a> {
|
||||
ClosureParts { decl: d, body: b, id: id, span: s }
|
||||
fn new(d: &'a FnDecl, b: &'a Block, id: NodeId, s: Span, attrs: &'a [Attribute]) -> Self {
|
||||
ClosureParts { decl: d, body: b, id: id, span: s, attrs: attrs }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,37 +168,37 @@ impl<'a> FnLikeNode<'a> {
|
|||
|
||||
pub fn body(self) -> &'a Block {
|
||||
self.handle(|i: ItemFnParts<'a>| &*i.body,
|
||||
|_, _, _: &'a ast::MethodSig, _, body: &'a ast::Block, _| body,
|
||||
|_, _, _: &'a ast::MethodSig, _, body: &'a ast::Block, _, _| body,
|
||||
|c: ClosureParts<'a>| c.body)
|
||||
}
|
||||
|
||||
pub fn decl(self) -> &'a FnDecl {
|
||||
self.handle(|i: ItemFnParts<'a>| &*i.decl,
|
||||
|_, _, sig: &'a ast::MethodSig, _, _, _| &sig.decl,
|
||||
|_, _, sig: &'a ast::MethodSig, _, _, _, _| &sig.decl,
|
||||
|c: ClosureParts<'a>| c.decl)
|
||||
}
|
||||
|
||||
pub fn span(self) -> Span {
|
||||
self.handle(|i: ItemFnParts| i.span,
|
||||
|_, _, _: &'a ast::MethodSig, _, _, span| span,
|
||||
|_, _, _: &'a ast::MethodSig, _, _, span, _| span,
|
||||
|c: ClosureParts| c.span)
|
||||
}
|
||||
|
||||
pub fn id(self) -> NodeId {
|
||||
self.handle(|i: ItemFnParts| i.id,
|
||||
|id, _, _: &'a ast::MethodSig, _, _, _| id,
|
||||
|id, _, _: &'a ast::MethodSig, _, _, _, _| id,
|
||||
|c: ClosureParts| c.id)
|
||||
}
|
||||
|
||||
pub fn kind(self) -> FnKind<'a> {
|
||||
let item = |p: ItemFnParts<'a>| -> FnKind<'a> {
|
||||
FnKind::ItemFn(p.name, p.generics, p.unsafety, p.constness, p.abi, p.vis)
|
||||
FnKind::ItemFn(p.name, p.generics, p.unsafety, p.constness, p.abi, p.vis, p.attrs)
|
||||
};
|
||||
let closure = |_: ClosureParts| {
|
||||
FnKind::Closure
|
||||
let closure = |c: ClosureParts<'a>| {
|
||||
FnKind::Closure(c.attrs)
|
||||
};
|
||||
let method = |_, name: Name, sig: &'a ast::MethodSig, vis, _, _| {
|
||||
FnKind::Method(name, sig, vis)
|
||||
let method = |_, name: Name, sig: &'a ast::MethodSig, vis, _, _, attrs| {
|
||||
FnKind::Method(name, sig, vis, attrs)
|
||||
};
|
||||
self.handle(item, method, closure)
|
||||
}
|
||||
|
|
@ -207,7 +210,8 @@ impl<'a> FnLikeNode<'a> {
|
|||
&'a ast::MethodSig,
|
||||
Option<ast::Visibility>,
|
||||
&'a ast::Block,
|
||||
Span)
|
||||
Span,
|
||||
&'a [Attribute])
|
||||
-> A,
|
||||
C: FnOnce(ClosureParts<'a>) -> A,
|
||||
{
|
||||
|
|
@ -224,20 +228,21 @@ impl<'a> FnLikeNode<'a> {
|
|||
abi: abi,
|
||||
vis: i.vis,
|
||||
constness: constness,
|
||||
span: i.span
|
||||
span: i.span,
|
||||
attrs: &i.attrs,
|
||||
}),
|
||||
_ => panic!("item FnLikeNode that is not fn-like"),
|
||||
},
|
||||
map::NodeTraitItem(ti) => match ti.node {
|
||||
ast::MethodTraitItem(ref sig, Some(ref body)) => {
|
||||
method(ti.id, ti.name, sig, None, body, ti.span)
|
||||
method(ti.id, ti.name, sig, None, body, ti.span, &ti.attrs)
|
||||
}
|
||||
_ => panic!("trait method FnLikeNode that is not fn-like"),
|
||||
},
|
||||
map::NodeImplItem(ii) => {
|
||||
match ii.node {
|
||||
ast::ImplItemKind::Method(ref sig, ref body) => {
|
||||
method(ii.id, ii.name, sig, Some(ii.vis), body, ii.span)
|
||||
method(ii.id, ii.name, sig, Some(ii.vis), body, ii.span, &ii.attrs)
|
||||
}
|
||||
_ => {
|
||||
panic!("impl method FnLikeNode that is not fn-like")
|
||||
|
|
@ -246,7 +251,11 @@ impl<'a> FnLikeNode<'a> {
|
|||
}
|
||||
map::NodeExpr(e) => match e.node {
|
||||
ast::ExprClosure(_, ref decl, ref block) =>
|
||||
closure(ClosureParts::new(&decl, &block, e.id, e.span)),
|
||||
closure(ClosureParts::new(&decl,
|
||||
&block,
|
||||
e.id,
|
||||
e.span,
|
||||
e.attrs.as_attr_slice())),
|
||||
_ => panic!("expr FnLikeNode that is not fn-like"),
|
||||
},
|
||||
_ => panic!("other FnLikeNode that is not fn-like"),
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ fn replace_newline_with_backslash_l(s: String) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ast> dot::Labeller<'a, Node<'a>, Edge<'a>> for LabelledCFG<'a, 'ast> {
|
||||
impl<'a, 'ast> dot::Labeller<'a> for LabelledCFG<'a, 'ast> {
|
||||
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> {
|
||||
|
|
@ -97,7 +99,9 @@ impl<'a, 'ast> dot::Labeller<'a, Node<'a>, Edge<'a>> for LabelledCFG<'a, 'ast> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for &'a cfg::CFG {
|
||||
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 mut v = Vec::new();
|
||||
self.graph.each_node(|i, nd| { v.push((i, nd)); true });
|
||||
|
|
@ -116,8 +120,10 @@ impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for &'a cfg::CFG {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ast> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for LabelledCFG<'a, 'ast>
|
||||
impl<'a, 'ast> dot::GraphWalk<'a> for LabelledCFG<'a, 'ast>
|
||||
{
|
||||
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) }
|
||||
|
|
|
|||
|
|
@ -1017,7 +1017,7 @@ fn check_fn(cx: &mut MatchCheckCtxt,
|
|||
sp: Span,
|
||||
fn_id: NodeId) {
|
||||
match kind {
|
||||
FnKind::Closure => {}
|
||||
FnKind::Closure(_) => {}
|
||||
_ => cx.param_env = ParameterEnvironment::for_item(cx.tcx, fn_id),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -226,10 +226,10 @@ pub fn lookup_const_fn_by_id<'tcx>(tcx: &TyCtxt<'tcx>, def_id: DefId)
|
|||
};
|
||||
|
||||
match fn_like.kind() {
|
||||
FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _) => {
|
||||
FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _, _) => {
|
||||
Some(fn_like)
|
||||
}
|
||||
FnKind::Method(_, m, _) => {
|
||||
FnKind::Method(_, m, _, _) => {
|
||||
if m.constness == hir::Constness::Const {
|
||||
Some(fn_like)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -653,7 +653,7 @@ fn set_bit(words: &mut [usize], bit: usize) -> bool {
|
|||
let word = bit / usize_bits;
|
||||
let bit_in_word = bit % usize_bits;
|
||||
let bit_mask = 1 << bit_in_word;
|
||||
debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, word);
|
||||
debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, bit_mask);
|
||||
let oldv = words[word];
|
||||
let newv = oldv | bit_mask;
|
||||
words[word] = newv;
|
||||
|
|
|
|||
|
|
@ -82,9 +82,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
|
|||
block: &'v hir::Block, span: Span, _: ast::NodeId) {
|
||||
|
||||
let (is_item_fn, is_unsafe_fn) = match fn_kind {
|
||||
FnKind::ItemFn(_, _, unsafety, _, _, _) =>
|
||||
FnKind::ItemFn(_, _, unsafety, _, _, _, _) =>
|
||||
(true, unsafety == hir::Unsafety::Unsafe),
|
||||
FnKind::Method(_, sig, _) =>
|
||||
FnKind::Method(_, sig, _, _) =>
|
||||
(true, sig.unsafety == hir::Unsafety::Unsafe),
|
||||
_ => (false, false),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -173,7 +173,9 @@ impl<'a, 'tcx> ConstraintGraph<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> dot::Labeller<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> {
|
||||
impl<'a, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'tcx> {
|
||||
type Node = Node;
|
||||
type Edge = Edge;
|
||||
fn graph_id(&self) -> dot::Id {
|
||||
dot::Id::new(&*self.graph_name).unwrap()
|
||||
}
|
||||
|
|
@ -224,7 +226,9 @@ fn edge_to_nodes(e: &Edge) -> (Node, Node) {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> dot::GraphWalk<'a, Node, Edge> for ConstraintGraph<'a, 'tcx> {
|
||||
impl<'a, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'tcx> {
|
||||
type Node = Node;
|
||||
type Edge = Edge;
|
||||
fn nodes(&self) -> dot::Nodes<Node> {
|
||||
let mut set = FnvHashSet();
|
||||
for node in self.node_ids.keys() {
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
|
|||
intravisit::walk_fn(self, fk, fd, b, s);
|
||||
self.param_envs.pop();
|
||||
}
|
||||
FnKind::Closure => {
|
||||
FnKind::Closure(..) => {
|
||||
intravisit::walk_fn(self, fk, fd, b, s);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,17 +182,17 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
|
|||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, s: Span, fn_id: ast::NodeId) {
|
||||
match fk {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _) => {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _, _) => {
|
||||
self.visit_early_late(subst::FnSpace, generics, |this| {
|
||||
this.add_scope_and_walk_fn(fk, fd, b, s, fn_id)
|
||||
})
|
||||
}
|
||||
FnKind::Method(_, sig, _) => {
|
||||
FnKind::Method(_, sig, _, _) => {
|
||||
self.visit_early_late(subst::FnSpace, &sig.generics, |this| {
|
||||
this.add_scope_and_walk_fn(fk, fd, b, s, fn_id)
|
||||
})
|
||||
}
|
||||
FnKind::Closure => {
|
||||
FnKind::Closure(_) => {
|
||||
self.add_scope_and_walk_fn(fk, fd, b, s, fn_id)
|
||||
}
|
||||
}
|
||||
|
|
@ -471,16 +471,16 @@ impl<'a> LifetimeContext<'a> {
|
|||
fn_id: ast::NodeId) {
|
||||
|
||||
match fk {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _) => {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _, _) => {
|
||||
intravisit::walk_fn_decl(self, fd);
|
||||
self.visit_generics(generics);
|
||||
}
|
||||
FnKind::Method(_, sig, _) => {
|
||||
FnKind::Method(_, sig, _, _) => {
|
||||
intravisit::walk_fn_decl(self, fd);
|
||||
self.visit_generics(&sig.generics);
|
||||
self.visit_explicit_self(&sig.explicit_self);
|
||||
}
|
||||
FnKind::Closure => {
|
||||
FnKind::Closure(_) => {
|
||||
intravisit::walk_fn_decl(self, fd);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -499,13 +499,13 @@ pub enum Lvalue<'tcx> {
|
|||
/// or `*B` or `B[index]`. Note that it is parameterized because it is
|
||||
/// shared between `Constant` and `Lvalue`. See the aliases
|
||||
/// `LvalueProjection` etc below.
|
||||
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
|
||||
pub struct Projection<'tcx, B, V> {
|
||||
pub base: B,
|
||||
pub elem: ProjectionElem<'tcx, V>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
|
||||
pub enum ProjectionElem<'tcx, V> {
|
||||
Deref,
|
||||
Field(Field, Ty<'tcx>),
|
||||
|
|
@ -857,7 +857,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
/// this does not necessarily mean that they are "==" in Rust -- in
|
||||
/// particular one must be wary of `NaN`!
|
||||
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
|
||||
pub struct Constant<'tcx> {
|
||||
pub span: Span,
|
||||
pub ty: Ty<'tcx>,
|
||||
|
|
@ -877,7 +877,7 @@ impl<'tcx> Debug for TypedConstVal<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
|
||||
pub enum Literal<'tcx> {
|
||||
Item {
|
||||
def_id: DefId,
|
||||
|
|
|
|||
|
|
@ -14,3 +14,4 @@ syntax = { path = "../libsyntax" }
|
|||
graphviz = { path = "../libgraphviz" }
|
||||
rustc = { path = "../librustc" }
|
||||
rustc_front = { path = "../librustc_front" }
|
||||
rustc_mir = { path = "../librustc_mir" }
|
||||
|
|
|
|||
105
src/librustc_borrowck/bitslice.rs
Normal file
105
src/librustc_borrowck/bitslice.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright 2012-2016 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.
|
||||
|
||||
use std::mem;
|
||||
|
||||
/// `BitSlice` provides helper methods for treating a `[usize]`
|
||||
/// as a bitvector.
|
||||
pub trait BitSlice {
|
||||
fn clear_bit(&mut self, idx: usize) -> bool;
|
||||
fn set_bit(&mut self, idx: usize) -> bool;
|
||||
fn get_bit(&self, idx: usize) -> bool;
|
||||
}
|
||||
|
||||
impl BitSlice for [usize] {
|
||||
/// Clears bit at `idx` to 0; returns true iff this changed `self.`
|
||||
fn clear_bit(&mut self, idx: usize) -> bool {
|
||||
let words = self;
|
||||
debug!("clear_bit: words={} idx={}",
|
||||
bits_to_string(words, words.len() * mem::size_of::<usize>()), bit_str(idx));
|
||||
let BitLookup { word, bit_in_word, bit_mask } = bit_lookup(idx);
|
||||
debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, bit_mask);
|
||||
let oldv = words[word];
|
||||
let newv = oldv & !bit_mask;
|
||||
words[word] = newv;
|
||||
oldv != newv
|
||||
}
|
||||
|
||||
/// Sets bit at `idx` to 1; returns true iff this changed `self.`
|
||||
fn set_bit(&mut self, idx: usize) -> bool {
|
||||
let words = self;
|
||||
debug!("set_bit: words={} idx={}",
|
||||
bits_to_string(words, words.len() * mem::size_of::<usize>()), bit_str(idx));
|
||||
let BitLookup { word, bit_in_word, bit_mask } = bit_lookup(idx);
|
||||
debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, bit_mask);
|
||||
let oldv = words[word];
|
||||
let newv = oldv | bit_mask;
|
||||
words[word] = newv;
|
||||
oldv != newv
|
||||
}
|
||||
|
||||
/// Extracts value of bit at `idx` in `self`.
|
||||
fn get_bit(&self, idx: usize) -> bool {
|
||||
let words = self;
|
||||
let BitLookup { word, bit_mask, .. } = bit_lookup(idx);
|
||||
(words[word] & bit_mask) != 0
|
||||
}
|
||||
}
|
||||
|
||||
struct BitLookup {
|
||||
/// An index of the word holding the bit in original `[usize]` of query.
|
||||
word: usize,
|
||||
/// Index of the particular bit within the word holding the bit.
|
||||
bit_in_word: usize,
|
||||
/// Word with single 1-bit set corresponding to where the bit is located.
|
||||
bit_mask: usize,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bit_lookup(bit: usize) -> BitLookup {
|
||||
let usize_bits = mem::size_of::<usize>() * 8;
|
||||
let word = bit / usize_bits;
|
||||
let bit_in_word = bit % usize_bits;
|
||||
let bit_mask = 1 << bit_in_word;
|
||||
BitLookup { word: word, bit_in_word: bit_in_word, bit_mask: bit_mask }
|
||||
}
|
||||
|
||||
|
||||
fn bit_str(bit: usize) -> String {
|
||||
let byte = bit >> 8;
|
||||
let lobits = 1 << (bit & 0xFF);
|
||||
format!("[{}:{}-{:02x}]", bit, byte, lobits)
|
||||
}
|
||||
|
||||
pub fn bits_to_string(words: &[usize], bytes: usize) -> String {
|
||||
let mut result = String::new();
|
||||
let mut sep = '[';
|
||||
|
||||
// Note: this is a little endian printout of bytes.
|
||||
|
||||
let mut i = 0;
|
||||
for &word in words.iter() {
|
||||
let mut v = word;
|
||||
for _ in 0..mem::size_of::<usize>() {
|
||||
let byte = v & 0xFF;
|
||||
if i >= bytes {
|
||||
assert!(byte == 0);
|
||||
} else {
|
||||
result.push(sep);
|
||||
result.push_str(&format!("{:02x}", byte));
|
||||
}
|
||||
v >>= 8;
|
||||
i += 1;
|
||||
sep = '-';
|
||||
}
|
||||
}
|
||||
result.push(']');
|
||||
return result
|
||||
}
|
||||
62
src/librustc_borrowck/borrowck/mir/abs_domain.rs
Normal file
62
src/librustc_borrowck/borrowck/mir/abs_domain.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2012-2016 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.
|
||||
|
||||
//! The move-analysis portion of borrowck needs to work in an abstract
|
||||
//! domain of lifted Lvalues. Most of the Lvalue variants fall into a
|
||||
//! one-to-one mapping between the concrete and abstract (e.g. a
|
||||
//! field-deref on a local-variable, `x.field`, has the same meaning
|
||||
//! in both domains). Indexed-Projections are the exception: `a[x]`
|
||||
//! needs to be treated as mapping to the same move path as `a[y]` as
|
||||
//! well as `a[13]`, et cetera.
|
||||
//!
|
||||
//! (In theory the analysis could be extended to work with sets of
|
||||
//! paths, so that `a[0]` and `a[13]` could be kept distinct, while
|
||||
//! `a[x]` would still overlap them both. But that is not this
|
||||
//! representation does today.)
|
||||
|
||||
use rustc::mir::repr::{Lvalue, LvalueElem};
|
||||
use rustc::mir::repr::{Operand, Projection, ProjectionElem};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct AbstractOperand;
|
||||
pub type AbstractProjection<'tcx> =
|
||||
Projection<'tcx, Lvalue<'tcx>, AbstractOperand>;
|
||||
pub type AbstractElem<'tcx> =
|
||||
ProjectionElem<'tcx, AbstractOperand>;
|
||||
|
||||
pub trait Lift {
|
||||
type Abstract;
|
||||
fn lift(&self) -> Self::Abstract;
|
||||
}
|
||||
impl<'tcx> Lift for Operand<'tcx> {
|
||||
type Abstract = AbstractOperand;
|
||||
fn lift(&self) -> Self::Abstract { AbstractOperand }
|
||||
}
|
||||
impl<'tcx> Lift for LvalueElem<'tcx> {
|
||||
type Abstract = AbstractElem<'tcx>;
|
||||
fn lift(&self) -> Self::Abstract {
|
||||
match *self {
|
||||
ProjectionElem::Deref =>
|
||||
ProjectionElem::Deref,
|
||||
ProjectionElem::Field(ref f, ty) =>
|
||||
ProjectionElem::Field(f.clone(), ty.clone()),
|
||||
ProjectionElem::Index(ref i) =>
|
||||
ProjectionElem::Index(i.lift()),
|
||||
ProjectionElem::ConstantIndex {offset,min_length,from_end} =>
|
||||
ProjectionElem::ConstantIndex {
|
||||
offset: offset,
|
||||
min_length: min_length,
|
||||
from_end: from_end
|
||||
},
|
||||
ProjectionElem::Downcast(a, u) =>
|
||||
ProjectionElem::Downcast(a.clone(), u.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
505
src/librustc_borrowck/borrowck/mir/dataflow.rs
Normal file
505
src/librustc_borrowck/borrowck/mir/dataflow.rs
Normal file
|
|
@ -0,0 +1,505 @@
|
|||
// Copyright 2012-2016 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.
|
||||
|
||||
use syntax::attr::AttrMetaMethods;
|
||||
|
||||
use rustc::middle::ty;
|
||||
use rustc::mir::repr::{self, Mir};
|
||||
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::usize;
|
||||
|
||||
use super::MirBorrowckCtxt;
|
||||
use super::gather_moves::{Location, MoveData, MovePathData, MovePathIndex, MoveOutIndex, PathMap};
|
||||
use super::graphviz;
|
||||
use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
|
||||
|
||||
pub trait Dataflow {
|
||||
fn dataflow(&mut self);
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b, 'tcx: 'a> Dataflow for MirBorrowckCtxt<'b, 'a, 'tcx> {
|
||||
fn dataflow(&mut self) {
|
||||
self.build_gen_and_kill_sets();
|
||||
self.pre_dataflow_instrumentation().unwrap();
|
||||
self.propagate();
|
||||
self.post_dataflow_instrumentation().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
struct PropagationContext<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn>
|
||||
where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue)
|
||||
{
|
||||
mbcx: &'c mut MirBorrowckCtxt<'b, 'a, 'tcx>,
|
||||
changed: bool,
|
||||
on_return: OnReturn
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
|
||||
fn propagate(&mut self) {
|
||||
let mut temp = vec![0; self.flow_state.sets.words_per_block];
|
||||
let mut propcx = PropagationContext {
|
||||
mbcx: &mut *self,
|
||||
changed: true,
|
||||
on_return: |move_data, in_out, dest_lval| {
|
||||
let move_path_index = move_data.rev_lookup.find(dest_lval);
|
||||
on_all_children_bits(in_out,
|
||||
&move_data.path_map,
|
||||
&move_data.move_paths,
|
||||
move_path_index,
|
||||
&|in_out, mpi| {
|
||||
in_out.clear_bit(mpi.idx());
|
||||
});
|
||||
},
|
||||
};
|
||||
while propcx.changed {
|
||||
propcx.changed = false;
|
||||
propcx.reset(&mut temp);
|
||||
propcx.walk_cfg(&mut temp);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_gen_and_kill_sets(&mut self) {
|
||||
// First we need to build the gen- and kill-sets. The
|
||||
// gather_moves information provides a high-level mapping from
|
||||
// mir-locations to the MoveOuts (and those correspond
|
||||
// directly to gen-sets here). But we still need to figure out
|
||||
// the kill-sets.
|
||||
|
||||
let move_data = &self.flow_state.operator;
|
||||
let move_paths = &move_data.move_paths;
|
||||
let loc_map = &move_data.loc_map;
|
||||
let path_map = &move_data.path_map;
|
||||
let rev_lookup = &move_data.rev_lookup;
|
||||
|
||||
for bb in self.mir.all_basic_blocks() {
|
||||
let &repr::BasicBlockData { ref statements,
|
||||
ref terminator,
|
||||
is_cleanup: _ } =
|
||||
self.mir.basic_block_data(bb);
|
||||
|
||||
let mut sets = self.flow_state.sets.for_block(bb.index());
|
||||
for (j, stmt) in statements.iter().enumerate() {
|
||||
let loc = Location { block: bb, index: j };
|
||||
debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
|
||||
stmt, loc, &loc_map[loc]);
|
||||
for move_index in &loc_map[loc] {
|
||||
// Every path deinitialized by a *particular move*
|
||||
// has corresponding bit, "gen'ed" (i.e. set)
|
||||
// here, in dataflow vector
|
||||
zero_to_one(&mut sets.gen_set, *move_index);
|
||||
}
|
||||
match stmt.kind {
|
||||
repr::StatementKind::Assign(ref lvalue, _) => {
|
||||
// assigning into this `lvalue` kills all
|
||||
// MoveOuts from it, and *also* all MoveOuts
|
||||
// for children and associated fragment sets.
|
||||
let move_path_index = rev_lookup.find(lvalue);
|
||||
|
||||
on_all_children_bits(sets.kill_set,
|
||||
path_map,
|
||||
move_paths,
|
||||
move_path_index,
|
||||
&|kill_set, mpi| {
|
||||
kill_set.set_bit(mpi.idx());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let loc = Location { block: bb, index: statements.len() };
|
||||
debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
|
||||
terminator, loc, &loc_map[loc]);
|
||||
for move_index in &loc_map[loc] {
|
||||
zero_to_one(&mut sets.gen_set, *move_index);
|
||||
}
|
||||
}
|
||||
|
||||
fn zero_to_one(gen_set: &mut [usize], move_index: MoveOutIndex) {
|
||||
let retval = gen_set.set_bit(move_index.idx());
|
||||
assert!(retval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_all_children_bits<Each>(set: &mut [usize],
|
||||
path_map: &PathMap,
|
||||
move_paths: &MovePathData,
|
||||
move_path_index: MovePathIndex,
|
||||
each_child: &Each)
|
||||
where Each: Fn(&mut [usize], MoveOutIndex)
|
||||
{
|
||||
// 1. invoke `each_child` callback for all moves that directly
|
||||
// influence path for `move_path_index`
|
||||
for move_index in &path_map[move_path_index] {
|
||||
each_child(set, *move_index);
|
||||
}
|
||||
|
||||
// 2. for each child of the path (that is named in this
|
||||
// function), recur.
|
||||
//
|
||||
// (Unnamed children are irrelevant to dataflow; by
|
||||
// definition they have no associated moves.)
|
||||
let mut next_child_index = move_paths[move_path_index].first_child;
|
||||
while let Some(child_index) = next_child_index {
|
||||
on_all_children_bits(set, path_map, move_paths, child_index, each_child);
|
||||
next_child_index = move_paths[child_index].next_sibling;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn> PropagationContext<'c, 'b, 'a, 'tcx, OnReturn>
|
||||
where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue)
|
||||
{
|
||||
fn reset(&mut self, bits: &mut [usize]) {
|
||||
let e = if self.mbcx.flow_state.operator.initial_value() {usize::MAX} else {0};
|
||||
for b in bits {
|
||||
*b = e;
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_cfg(&mut self, in_out: &mut [usize]) {
|
||||
let &mut MirBorrowckCtxt { ref mir, ref mut flow_state, .. } = self.mbcx;
|
||||
for (idx, bb) in mir.basic_blocks.iter().enumerate() {
|
||||
{
|
||||
let sets = flow_state.sets.for_block(idx);
|
||||
debug_assert!(in_out.len() == sets.on_entry.len());
|
||||
in_out.clone_from_slice(sets.on_entry);
|
||||
bitwise(in_out, sets.gen_set, &Union);
|
||||
bitwise(in_out, sets.kill_set, &Subtract);
|
||||
}
|
||||
flow_state.propagate_bits_into_graph_successors_of(in_out,
|
||||
&mut self.changed,
|
||||
bb,
|
||||
&self.on_return);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
|
||||
fn pre_dataflow_instrumentation(&self) -> io::Result<()> {
|
||||
self.if_attr_meta_name_found(
|
||||
"borrowck_graphviz_preflow",
|
||||
|this, path: &str| {
|
||||
graphviz::print_borrowck_graph_to(this, "preflow", path)
|
||||
})
|
||||
}
|
||||
|
||||
fn post_dataflow_instrumentation(&self) -> io::Result<()> {
|
||||
self.if_attr_meta_name_found(
|
||||
"borrowck_graphviz_postflow",
|
||||
|this, path: &str| {
|
||||
graphviz::print_borrowck_graph_to(this, "postflow", path)
|
||||
})
|
||||
}
|
||||
|
||||
fn if_attr_meta_name_found<F>(&self,
|
||||
name: &str,
|
||||
callback: F) -> io::Result<()>
|
||||
where F: for <'aa, 'bb> FnOnce(&'aa Self, &'bb str) -> io::Result<()>
|
||||
{
|
||||
for attr in self.attributes {
|
||||
if attr.check_name("rustc_mir") {
|
||||
let items = attr.meta_item_list();
|
||||
for item in items.iter().flat_map(|l| l.iter()) {
|
||||
if item.check_name(name) {
|
||||
if let Some(s) = item.value_str() {
|
||||
return callback(self, &s);
|
||||
} else {
|
||||
self.bcx.tcx.sess.span_err(
|
||||
item.span,
|
||||
&format!("{} attribute requires a path", item.name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps each block to a set of bits
|
||||
#[derive(Clone, Debug)]
|
||||
struct Bits {
|
||||
bits: Vec<usize>,
|
||||
}
|
||||
|
||||
impl Bits {
|
||||
fn new(init_word: usize, num_words: usize) -> Self {
|
||||
Bits { bits: vec![init_word; num_words] }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DataflowState<O: BitDenotation>
|
||||
{
|
||||
/// All the sets for the analysis. (Factored into its
|
||||
/// own structure so that we can borrow it mutably
|
||||
/// on its own separate from other fields.)
|
||||
pub sets: AllSets,
|
||||
|
||||
/// operator used to initialize, combine, and interpret bits.
|
||||
operator: O,
|
||||
}
|
||||
|
||||
pub struct AllSets {
|
||||
/// Analysis bitwidth for each block.
|
||||
bits_per_block: usize,
|
||||
|
||||
/// Number of words associated with each block entry
|
||||
/// equal to bits_per_block / usize::BITS, rounded up.
|
||||
words_per_block: usize,
|
||||
|
||||
/// For each block, bits generated by executing the statements in
|
||||
/// the block. (For comparison, the Terminator for each block is
|
||||
/// handled in a flow-specific manner during propagation.)
|
||||
gen_sets: Bits,
|
||||
|
||||
/// For each block, bits killed by executing the statements in the
|
||||
/// block. (For comparison, the Terminator for each block is
|
||||
/// handled in a flow-specific manner during propagation.)
|
||||
kill_sets: Bits,
|
||||
|
||||
/// For each block, bits valid on entry to the block.
|
||||
on_entry_sets: Bits,
|
||||
}
|
||||
|
||||
pub struct BlockSets<'a> {
|
||||
on_entry: &'a mut [usize],
|
||||
gen_set: &'a mut [usize],
|
||||
kill_set: &'a mut [usize],
|
||||
}
|
||||
|
||||
impl AllSets {
|
||||
pub fn bits_per_block(&self) -> usize { self.bits_per_block }
|
||||
pub fn bytes_per_block(&self) -> usize { (self.bits_per_block + 7) / 8 }
|
||||
pub fn for_block(&mut self, block_idx: usize) -> BlockSets {
|
||||
let offset = self.words_per_block * block_idx;
|
||||
let range = offset..(offset + self.words_per_block);
|
||||
BlockSets {
|
||||
on_entry: &mut self.on_entry_sets.bits[range.clone()],
|
||||
gen_set: &mut self.gen_sets.bits[range.clone()],
|
||||
kill_set: &mut self.kill_sets.bits[range],
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_set_for<'a>(&self, sets: &'a Bits, block_idx: usize) -> &'a [usize] {
|
||||
let offset = self.words_per_block * block_idx;
|
||||
&sets.bits[offset..(offset + self.words_per_block)]
|
||||
}
|
||||
pub fn gen_set_for(&self, block_idx: usize) -> &[usize] {
|
||||
self.lookup_set_for(&self.gen_sets, block_idx)
|
||||
}
|
||||
pub fn kill_set_for(&self, block_idx: usize) -> &[usize] {
|
||||
self.lookup_set_for(&self.kill_sets, block_idx)
|
||||
}
|
||||
pub fn on_entry_set_for(&self, block_idx: usize) -> &[usize] {
|
||||
self.lookup_set_for(&self.on_entry_sets, block_idx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: BitDenotation> DataflowState<O> {
|
||||
fn each_bit<F>(&self, words: &[usize], mut f: F)
|
||||
where F: FnMut(usize) {
|
||||
//! Helper for iterating over the bits in a bitvector.
|
||||
|
||||
for (word_index, &word) in words.iter().enumerate() {
|
||||
if word != 0 {
|
||||
let usize_bits: usize = mem::size_of::<usize>();
|
||||
let base_index = word_index * usize_bits;
|
||||
for offset in 0..usize_bits {
|
||||
let bit = 1 << offset;
|
||||
if (word & bit) != 0 {
|
||||
// NB: we round up the total number of bits
|
||||
// that we store in any given bit set so that
|
||||
// it is an even multiple of usize::BITS. This
|
||||
// means that there may be some stray bits at
|
||||
// the end that do not correspond to any
|
||||
// actual value; that's why we first check
|
||||
// that we are in range of bits_per_block.
|
||||
let bit_index = base_index + offset as usize;
|
||||
if bit_index >= self.sets.bits_per_block() {
|
||||
return;
|
||||
} else {
|
||||
f(bit_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interpret_set(&self, words: &[usize]) -> Vec<&O::Bit> {
|
||||
let mut v = Vec::new();
|
||||
self.each_bit(words, |i| {
|
||||
v.push(self.operator.interpret(i));
|
||||
});
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BitwiseOperator {
|
||||
/// Joins two predecessor bits together, typically either `|` or `&`
|
||||
fn join(&self, pred1: usize, pred2: usize) -> usize;
|
||||
}
|
||||
|
||||
/// Parameterization for the precise form of data flow that is used.
|
||||
pub trait DataflowOperator : BitwiseOperator {
|
||||
/// Specifies the initial value for each bit in the `on_entry` set
|
||||
fn initial_value(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait BitDenotation: DataflowOperator {
|
||||
/// Specifies what is represented by each bit in the dataflow bitvector.
|
||||
type Bit;
|
||||
/// Size of each bivector allocated for each block in the analysis.
|
||||
fn bits_per_block(&self) -> usize;
|
||||
/// Provides the meaning of each entry in the dataflow bitvector.
|
||||
/// (Mostly intended for use for better debug instrumentation.)
|
||||
fn interpret(&self, idx: usize) -> &Self::Bit;
|
||||
}
|
||||
|
||||
impl<D: BitDenotation> DataflowState<D> {
|
||||
pub fn new(mir: &Mir, denotation: D) -> Self {
|
||||
let bits_per_block = denotation.bits_per_block();
|
||||
let usize_bits = mem::size_of::<usize>() * 8;
|
||||
let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits;
|
||||
let num_blocks = mir.basic_blocks.len();
|
||||
let num_words = num_blocks * words_per_block;
|
||||
|
||||
let entry = if denotation.initial_value() { usize::MAX } else {0};
|
||||
|
||||
let zeroes = Bits::new(0, num_words);
|
||||
let on_entry = Bits::new(entry, num_words);
|
||||
|
||||
DataflowState {
|
||||
sets: AllSets {
|
||||
bits_per_block: bits_per_block,
|
||||
words_per_block: words_per_block,
|
||||
gen_sets: zeroes.clone(),
|
||||
kill_sets: zeroes,
|
||||
on_entry_sets: on_entry,
|
||||
},
|
||||
operator: denotation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: BitDenotation> DataflowState<D> {
|
||||
/// Propagates the bits of `in_out` into all the successors of `bb`,
|
||||
/// using bitwise operator denoted by `self.operator`.
|
||||
///
|
||||
/// For most blocks, this is entirely uniform. However, for blocks
|
||||
/// that end with a call terminator, the effect of the call on the
|
||||
/// dataflow state may depend on whether the call returned
|
||||
/// successfully or unwound. To reflect this, the `on_return`
|
||||
/// callback mutates `in_out` when propagating `in_out` via a call
|
||||
/// terminator; such mutation is performed *last*, to ensure its
|
||||
/// side-effects do not leak elsewhere (e.g. into unwind target).
|
||||
fn propagate_bits_into_graph_successors_of<OnReturn>(
|
||||
&mut self,
|
||||
in_out: &mut [usize],
|
||||
changed: &mut bool,
|
||||
bb: &repr::BasicBlockData,
|
||||
on_return: OnReturn) where OnReturn: Fn(&D, &mut [usize], &repr::Lvalue)
|
||||
{
|
||||
let term = if let Some(ref term) = bb.terminator { term } else { return };
|
||||
match *term {
|
||||
repr::Terminator::Return |
|
||||
repr::Terminator::Resume => {}
|
||||
repr::Terminator::Goto { ref target } |
|
||||
repr::Terminator::Drop { ref target, value: _, unwind: None } => {
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, target);
|
||||
}
|
||||
repr::Terminator::Drop { ref target, value: _, unwind: Some(ref unwind) } => {
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, target);
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
|
||||
}
|
||||
repr::Terminator::If { ref targets, .. } => {
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0);
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1);
|
||||
}
|
||||
repr::Terminator::Switch { ref targets, .. } |
|
||||
repr::Terminator::SwitchInt { ref targets, .. } => {
|
||||
for target in targets {
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, target);
|
||||
}
|
||||
}
|
||||
repr::Terminator::Call { ref cleanup, ref destination, func: _, args: _ } => {
|
||||
if let Some(ref unwind) = *cleanup {
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
|
||||
}
|
||||
if let Some((ref dest_lval, ref dest_bb)) = *destination {
|
||||
// N.B.: This must be done *last*, after all other
|
||||
// propagation, as documented in comment above.
|
||||
on_return(&self.operator, in_out, dest_lval);
|
||||
self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn propagate_bits_into_entry_set_for(&mut self,
|
||||
in_out: &[usize],
|
||||
changed: &mut bool,
|
||||
bb: &repr::BasicBlock) {
|
||||
let entry_set = self.sets.for_block(bb.index()).on_entry;
|
||||
let set_changed = bitwise(entry_set, in_out, &self.operator);
|
||||
if set_changed {
|
||||
*changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'tcx> DataflowState<MoveData<'tcx>> {
|
||||
pub fn new_move_analysis(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> Self {
|
||||
let move_data = MoveData::gather_moves(mir, tcx);
|
||||
DataflowState::new(mir, move_data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> BitwiseOperator for MoveData<'tcx> {
|
||||
#[inline]
|
||||
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
||||
pred1 | pred2 // moves from both preds are in scope
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> DataflowOperator for MoveData<'tcx> {
|
||||
#[inline]
|
||||
fn initial_value(&self) -> bool {
|
||||
false // no loans in scope by default
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bitwise<Op:BitwiseOperator>(out_vec: &mut [usize],
|
||||
in_vec: &[usize],
|
||||
op: &Op) -> bool {
|
||||
assert_eq!(out_vec.len(), in_vec.len());
|
||||
let mut changed = false;
|
||||
for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) {
|
||||
let old_val = *out_elt;
|
||||
let new_val = op.join(old_val, *in_elt);
|
||||
*out_elt = new_val;
|
||||
changed |= old_val != new_val;
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
struct Union;
|
||||
impl BitwiseOperator for Union {
|
||||
fn join(&self, a: usize, b: usize) -> usize { a | b }
|
||||
}
|
||||
struct Subtract;
|
||||
impl BitwiseOperator for Subtract {
|
||||
fn join(&self, a: usize, b: usize) -> usize { a & !b }
|
||||
}
|
||||
747
src/librustc_borrowck/borrowck/mir/gather_moves.rs
Normal file
747
src/librustc_borrowck/borrowck/mir/gather_moves.rs
Normal file
|
|
@ -0,0 +1,747 @@
|
|||
// Copyright 2012-2016 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.
|
||||
|
||||
|
||||
use rustc::middle::ty;
|
||||
use rustc::mir::repr::{self, Mir, BasicBlock, Lvalue, Rvalue};
|
||||
use rustc::mir::repr::{StatementKind, Terminator};
|
||||
use rustc::util::nodemap::FnvHashMap;
|
||||
|
||||
use std::cell::{Cell};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use std::ops::Index;
|
||||
|
||||
use super::dataflow::BitDenotation;
|
||||
use super::abs_domain::{AbstractElem, Lift};
|
||||
|
||||
// This submodule holds some newtype'd Index wrappers that are using
|
||||
// NonZero to ensure that Option<Index> occupies only a single word.
|
||||
// They are in a submodule to impose privacy restrictions; namely, to
|
||||
// ensure that other code does not accidentally access `index.0`
|
||||
// (which is likely to yield a subtle off-by-one error).
|
||||
mod indexes {
|
||||
use core::nonzero::NonZero;
|
||||
|
||||
macro_rules! new_index {
|
||||
($Index:ident) => {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct $Index(NonZero<usize>);
|
||||
|
||||
impl $Index {
|
||||
pub fn new(idx: usize) -> Self {
|
||||
unsafe { $Index(NonZero::new(idx + 1)) }
|
||||
}
|
||||
pub fn idx(&self) -> usize {
|
||||
*self.0 - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Index into MovePathData.move_paths
|
||||
new_index!(MovePathIndex);
|
||||
|
||||
/// Index into MoveData.moves.
|
||||
new_index!(MoveOutIndex);
|
||||
}
|
||||
|
||||
pub use self::indexes::MovePathIndex;
|
||||
pub use self::indexes::MoveOutIndex;
|
||||
|
||||
/// `MovePath` is a canonicalized representation of a path that is
|
||||
/// moved or assigned to.
|
||||
///
|
||||
/// It follows a tree structure.
|
||||
///
|
||||
/// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;`
|
||||
/// move *out* of the l-value `x.m`.
|
||||
///
|
||||
/// The MovePaths representing `x.m` and `x.n` are siblings (that is,
|
||||
/// one of them will link to the other via the `next_sibling` field,
|
||||
/// and the other will have no entry in its `next_sibling` field), and
|
||||
/// they both have the MovePath representing `x` as their parent.
|
||||
#[derive(Clone)]
|
||||
pub struct MovePath<'tcx> {
|
||||
pub next_sibling: Option<MovePathIndex>,
|
||||
pub first_child: Option<MovePathIndex>,
|
||||
pub parent: Option<MovePathIndex>,
|
||||
pub content: MovePathContent<'tcx>,
|
||||
}
|
||||
|
||||
/// MovePaths usually represent a single l-value. The exceptions are
|
||||
/// forms that arise due to erroneous input code: static data holds
|
||||
/// l-values that we cannot actually move out of. Therefore we map
|
||||
/// statics to a special marker value (`MovePathContent::Static`)
|
||||
/// representing an invalid origin.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MovePathContent<'tcx> {
|
||||
Lvalue(Lvalue<'tcx>),
|
||||
Static,
|
||||
}
|
||||
|
||||
/// During construction of the MovePath's, we use PreMovePath to
|
||||
/// represent accumulated state while we are gathering up all the
|
||||
/// children of each path.
|
||||
#[derive(Clone)]
|
||||
struct PreMovePath<'tcx> {
|
||||
pub next_sibling: Option<MovePathIndex>,
|
||||
pub first_child: Cell<Option<MovePathIndex>>,
|
||||
pub parent: Option<MovePathIndex>,
|
||||
pub content: MovePathContent<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> PreMovePath<'tcx> {
|
||||
fn into_move_path(self) -> MovePath<'tcx> {
|
||||
MovePath {
|
||||
next_sibling: self.next_sibling,
|
||||
parent: self.parent,
|
||||
content: self.content,
|
||||
first_child: self.first_child.get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Debug for MovePath<'tcx> {
|
||||
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||
try!(write!(w, "MovePath {{"));
|
||||
if let Some(parent) = self.parent {
|
||||
try!(write!(w, " parent: {:?},", parent));
|
||||
}
|
||||
if let Some(first_child) = self.first_child {
|
||||
try!(write!(w, " first_child: {:?},", first_child));
|
||||
}
|
||||
if let Some(next_sibling) = self.next_sibling {
|
||||
try!(write!(w, " next_sibling: {:?}", next_sibling));
|
||||
}
|
||||
write!(w, " content: {:?} }}", self.content)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MoveData<'tcx> {
|
||||
pub move_paths: MovePathData<'tcx>,
|
||||
pub moves: Vec<MoveOut>,
|
||||
pub loc_map: LocMap,
|
||||
pub path_map: PathMap,
|
||||
pub rev_lookup: MovePathLookup<'tcx>,
|
||||
}
|
||||
|
||||
pub struct LocMap {
|
||||
/// Location-indexed (BasicBlock for outer index, index within BB
|
||||
/// for inner index) map to list of MoveOutIndex's.
|
||||
///
|
||||
/// Each Location `l` is mapped to the MoveOut's that are effects
|
||||
/// of executing the code at `l`. (There can be multiple MoveOut's
|
||||
/// for a given `l` because each MoveOut is associated with one
|
||||
/// particular path being moved.)
|
||||
map: Vec<Vec<Vec<MoveOutIndex>>>,
|
||||
}
|
||||
|
||||
impl Index<Location> for LocMap {
|
||||
type Output = [MoveOutIndex];
|
||||
fn index(&self, index: Location) -> &Self::Output {
|
||||
assert!(index.block.index() < self.map.len());
|
||||
assert!(index.index < self.map[index.block.index()].len());
|
||||
&self.map[index.block.index()][index.index]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PathMap {
|
||||
/// Path-indexed map to list of MoveOutIndex's.
|
||||
///
|
||||
/// Each Path `p` is mapped to the MoveOut's that move out of `p`.
|
||||
map: Vec<Vec<MoveOutIndex>>,
|
||||
}
|
||||
|
||||
impl Index<MovePathIndex> for PathMap {
|
||||
type Output = [MoveOutIndex];
|
||||
fn index(&self, index: MovePathIndex) -> &Self::Output {
|
||||
&self.map[index.idx()]
|
||||
}
|
||||
}
|
||||
|
||||
/// `MoveOut` represents a point in a program that moves out of some
|
||||
/// L-value; i.e., "creates" uninitialized memory.
|
||||
///
|
||||
/// With respect to dataflow analysis:
|
||||
/// - Generated by moves and declaration of uninitialized variables.
|
||||
/// - Killed by assignments to the memory.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct MoveOut {
|
||||
/// path being moved
|
||||
pub path: MovePathIndex,
|
||||
/// location of move
|
||||
pub source: Location,
|
||||
}
|
||||
|
||||
impl fmt::Debug for MoveOut {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "p{}@{:?}", self.path.idx(), self.source)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Location {
|
||||
/// block where action is located
|
||||
pub block: BasicBlock,
|
||||
/// index within above block; statement when < statments.len) or
|
||||
/// the terminator (when = statements.len).
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Location {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{:?}[{}]", self.block, self.index)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MovePathData<'tcx> {
|
||||
move_paths: Vec<MovePath<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> Index<MovePathIndex> for MovePathData<'tcx> {
|
||||
type Output = MovePath<'tcx>;
|
||||
fn index(&self, i: MovePathIndex) -> &MovePath<'tcx> {
|
||||
&self.move_paths[i.idx()]
|
||||
}
|
||||
}
|
||||
|
||||
/// MovePathInverseMap maps from a uint in an lvalue-category to the
|
||||
/// MovePathIndex for the MovePath for that lvalue.
|
||||
type MovePathInverseMap = Vec<Option<MovePathIndex>>;
|
||||
|
||||
struct MovePathDataBuilder<'a, 'tcx: 'a> {
|
||||
mir: &'a Mir<'tcx>,
|
||||
pre_move_paths: Vec<PreMovePath<'tcx>>,
|
||||
rev_lookup: MovePathLookup<'tcx>,
|
||||
}
|
||||
|
||||
/// Tables mapping from an l-value to its MovePathIndex.
|
||||
pub struct MovePathLookup<'tcx> {
|
||||
vars: MovePathInverseMap,
|
||||
temps: MovePathInverseMap,
|
||||
args: MovePathInverseMap,
|
||||
|
||||
/// The move path representing the return value is constructed
|
||||
/// lazily when we first encounter it in the input MIR.
|
||||
return_ptr: Option<MovePathIndex>,
|
||||
|
||||
/// A single move path (representing any static data referenced)
|
||||
/// is constructed lazily when we first encounter statics in the
|
||||
/// input MIR.
|
||||
statics: Option<MovePathIndex>,
|
||||
|
||||
/// projections are made from a base-lvalue and a projection
|
||||
/// elem. The base-lvalue will have a unique MovePathIndex; we use
|
||||
/// the latter as the index into the outer vector (narrowing
|
||||
/// subsequent search so that it is solely relative to that
|
||||
/// base-lvalue). For the remaining lookup, we map the projection
|
||||
/// elem to the associated MovePathIndex.
|
||||
projections: Vec<FnvHashMap<AbstractElem<'tcx>, MovePathIndex>>,
|
||||
|
||||
/// Tracks the next index to allocate during construction of the
|
||||
/// MovePathData. Unused after MovePathData is fully constructed.
|
||||
next_index: MovePathIndex,
|
||||
}
|
||||
|
||||
trait FillTo {
|
||||
type T;
|
||||
fn fill_to_with(&mut self, idx: usize, x: Self::T);
|
||||
fn fill_to(&mut self, idx: usize) where Self::T: Default {
|
||||
self.fill_to_with(idx, Default::default())
|
||||
}
|
||||
}
|
||||
impl<T:Clone> FillTo for Vec<T> {
|
||||
type T = T;
|
||||
fn fill_to_with(&mut self, idx: usize, x: T) {
|
||||
if idx >= self.len() {
|
||||
let delta = idx + 1 - self.len();
|
||||
assert_eq!(idx + 1, self.len() + delta);
|
||||
self.extend(iter::repeat(x).take(delta))
|
||||
}
|
||||
debug_assert!(idx < self.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum LookupKind { Generate, Reuse }
|
||||
struct Lookup<T>(LookupKind, T);
|
||||
|
||||
impl Lookup<MovePathIndex> {
|
||||
fn idx(&self) -> usize { (self.1).idx() }
|
||||
}
|
||||
|
||||
impl<'tcx> MovePathLookup<'tcx> {
|
||||
fn new() -> Self {
|
||||
MovePathLookup {
|
||||
vars: vec![],
|
||||
temps: vec![],
|
||||
args: vec![],
|
||||
statics: None,
|
||||
return_ptr: None,
|
||||
projections: vec![],
|
||||
next_index: MovePathIndex::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_index(next: &mut MovePathIndex) -> MovePathIndex {
|
||||
let i = *next;
|
||||
*next = MovePathIndex::new(i.idx() + 1);
|
||||
i
|
||||
}
|
||||
|
||||
fn lookup_or_generate(vec: &mut Vec<Option<MovePathIndex>>,
|
||||
idx: u32,
|
||||
next_index: &mut MovePathIndex) -> Lookup<MovePathIndex> {
|
||||
let idx = idx as usize;
|
||||
vec.fill_to_with(idx, None);
|
||||
let entry = &mut vec[idx];
|
||||
match *entry {
|
||||
None => {
|
||||
let i = Self::next_index(next_index);
|
||||
*entry = Some(i);
|
||||
Lookup(LookupKind::Generate, i)
|
||||
}
|
||||
Some(entry_idx) => {
|
||||
Lookup(LookupKind::Reuse, entry_idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_var(&mut self, var_idx: u32) -> Lookup<MovePathIndex> {
|
||||
Self::lookup_or_generate(&mut self.vars,
|
||||
var_idx,
|
||||
&mut self.next_index)
|
||||
}
|
||||
|
||||
fn lookup_temp(&mut self, temp_idx: u32) -> Lookup<MovePathIndex> {
|
||||
Self::lookup_or_generate(&mut self.temps,
|
||||
temp_idx,
|
||||
&mut self.next_index)
|
||||
}
|
||||
|
||||
fn lookup_arg(&mut self, arg_idx: u32) -> Lookup<MovePathIndex> {
|
||||
Self::lookup_or_generate(&mut self.args,
|
||||
arg_idx,
|
||||
&mut self.next_index)
|
||||
}
|
||||
|
||||
fn lookup_static(&mut self) -> Lookup<MovePathIndex> {
|
||||
match self.statics {
|
||||
Some(mpi) => {
|
||||
Lookup(LookupKind::Reuse, mpi)
|
||||
}
|
||||
ref mut ret @ None => {
|
||||
let mpi = Self::next_index(&mut self.next_index);
|
||||
*ret = Some(mpi);
|
||||
Lookup(LookupKind::Generate, mpi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_return_pointer(&mut self) -> Lookup<MovePathIndex> {
|
||||
match self.return_ptr {
|
||||
Some(mpi) => {
|
||||
Lookup(LookupKind::Reuse, mpi)
|
||||
}
|
||||
ref mut ret @ None => {
|
||||
let mpi = Self::next_index(&mut self.next_index);
|
||||
*ret = Some(mpi);
|
||||
Lookup(LookupKind::Generate, mpi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_proj(&mut self,
|
||||
proj: &repr::LvalueProjection<'tcx>,
|
||||
base: MovePathIndex) -> Lookup<MovePathIndex> {
|
||||
let MovePathLookup { ref mut projections,
|
||||
ref mut next_index, .. } = *self;
|
||||
projections.fill_to(base.idx());
|
||||
match projections[base.idx()].entry(proj.elem.lift()) {
|
||||
Entry::Occupied(ent) => {
|
||||
Lookup(LookupKind::Reuse, *ent.get())
|
||||
}
|
||||
Entry::Vacant(ent) => {
|
||||
let mpi = Self::next_index(next_index);
|
||||
ent.insert(mpi);
|
||||
Lookup(LookupKind::Generate, mpi)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MovePathLookup<'tcx> {
|
||||
// Unlike the builder `fn move_path_for` below, this lookup
|
||||
// alternative will *not* create a MovePath on the fly for an
|
||||
// unknown l-value; it will simply panic.
|
||||
pub fn find(&self, lval: &Lvalue<'tcx>) -> MovePathIndex {
|
||||
match *lval {
|
||||
Lvalue::Var(var_idx) => self.vars[var_idx as usize].unwrap(),
|
||||
Lvalue::Temp(temp_idx) => self.temps[temp_idx as usize].unwrap(),
|
||||
Lvalue::Arg(arg_idx) => self.args[arg_idx as usize].unwrap(),
|
||||
Lvalue::Static(ref _def_id) => self.statics.unwrap(),
|
||||
Lvalue::ReturnPointer => self.return_ptr.unwrap(),
|
||||
Lvalue::Projection(ref proj) => {
|
||||
let base_index = self.find(&proj.base);
|
||||
self.projections[base_index.idx()][&proj.elem.lift()]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> {
|
||||
fn lookup(&mut self, lval: &Lvalue<'tcx>) -> Lookup<MovePathIndex> {
|
||||
let proj = match *lval {
|
||||
Lvalue::Var(var_idx) =>
|
||||
return self.rev_lookup.lookup_var(var_idx),
|
||||
Lvalue::Temp(temp_idx) =>
|
||||
return self.rev_lookup.lookup_temp(temp_idx),
|
||||
Lvalue::Arg(arg_idx) =>
|
||||
return self.rev_lookup.lookup_arg(arg_idx),
|
||||
Lvalue::Static(_def_id) =>
|
||||
return self.rev_lookup.lookup_static(),
|
||||
Lvalue::ReturnPointer =>
|
||||
return self.rev_lookup.lookup_return_pointer(),
|
||||
Lvalue::Projection(ref proj) => {
|
||||
proj
|
||||
}
|
||||
};
|
||||
|
||||
let base_index = self.move_path_for(&proj.base);
|
||||
self.rev_lookup.lookup_proj(proj, base_index)
|
||||
}
|
||||
|
||||
fn move_path_for(&mut self, lval: &Lvalue<'tcx>) -> MovePathIndex {
|
||||
let lookup: Lookup<MovePathIndex> = self.lookup(lval);
|
||||
|
||||
// `lookup` is either the previously assigned index or a
|
||||
// newly-allocated one.
|
||||
debug_assert!(lookup.idx() <= self.pre_move_paths.len());
|
||||
|
||||
if let Lookup(LookupKind::Generate, mpi) = lookup {
|
||||
let parent;
|
||||
let sibling;
|
||||
// tracks whether content is Some non-static; statics map to None.
|
||||
let content: Option<&Lvalue<'tcx>>;
|
||||
|
||||
match *lval {
|
||||
Lvalue::Static(_) => {
|
||||
content = None;
|
||||
sibling = None;
|
||||
parent = None;
|
||||
}
|
||||
|
||||
Lvalue::Var(_) | Lvalue::Temp(_) | Lvalue::Arg(_) |
|
||||
Lvalue::ReturnPointer => {
|
||||
content = Some(lval);
|
||||
sibling = None;
|
||||
parent = None;
|
||||
}
|
||||
Lvalue::Projection(ref proj) => {
|
||||
content = Some(lval);
|
||||
|
||||
// Here, install new MovePath as new first_child.
|
||||
|
||||
// Note: `parent` previously allocated (Projection
|
||||
// case of match above established this).
|
||||
let idx = self.move_path_for(&proj.base);
|
||||
parent = Some(idx);
|
||||
|
||||
let parent_move_path = &mut self.pre_move_paths[idx.idx()];
|
||||
|
||||
// At last: Swap in the new first_child.
|
||||
sibling = parent_move_path.first_child.get();
|
||||
parent_move_path.first_child.set(Some(mpi));
|
||||
}
|
||||
};
|
||||
|
||||
let content = match content {
|
||||
Some(lval) => MovePathContent::Lvalue(lval.clone()),
|
||||
None => MovePathContent::Static,
|
||||
};
|
||||
|
||||
let move_path = PreMovePath {
|
||||
next_sibling: sibling,
|
||||
parent: parent,
|
||||
content: content,
|
||||
first_child: Cell::new(None),
|
||||
};
|
||||
|
||||
self.pre_move_paths.push(move_path);
|
||||
}
|
||||
|
||||
return lookup.1;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MoveData<'tcx> {
|
||||
pub fn gather_moves(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> Self {
|
||||
gather_moves(mir, tcx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum StmtKind {
|
||||
Use, Repeat, Cast, BinaryOp, UnaryOp, Box,
|
||||
Aggregate, Drop, CallFn, CallArg, Return,
|
||||
}
|
||||
|
||||
fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> {
|
||||
use self::StmtKind as SK;
|
||||
|
||||
let bbs = mir.all_basic_blocks();
|
||||
let mut moves = Vec::with_capacity(bbs.len());
|
||||
let mut loc_map: Vec<_> = iter::repeat(Vec::new()).take(bbs.len()).collect();
|
||||
let mut path_map = Vec::new();
|
||||
|
||||
// this is mutable only because we will move it to and fro' the
|
||||
// BlockContexts constructed on each iteration. (Moving is more
|
||||
// straight-forward than mutable borrows in this instance.)
|
||||
let mut builder = MovePathDataBuilder {
|
||||
mir: mir,
|
||||
pre_move_paths: Vec::new(),
|
||||
rev_lookup: MovePathLookup::new(),
|
||||
};
|
||||
|
||||
for bb in bbs {
|
||||
let loc_map_bb = &mut loc_map[bb.index()];
|
||||
let bb_data = mir.basic_block_data(bb);
|
||||
|
||||
debug_assert!(loc_map_bb.len() == 0);
|
||||
let len = bb_data.statements.len();
|
||||
loc_map_bb.fill_to(len);
|
||||
debug_assert!(loc_map_bb.len() == len + 1);
|
||||
|
||||
let mut bb_ctxt = BlockContext {
|
||||
tcx: tcx,
|
||||
moves: &mut moves,
|
||||
builder: builder,
|
||||
path_map: &mut path_map,
|
||||
loc_map_bb: loc_map_bb,
|
||||
};
|
||||
|
||||
for (i, stmt) in bb_data.statements.iter().enumerate() {
|
||||
let source = Location { block: bb, index: i };
|
||||
match stmt.kind {
|
||||
StatementKind::Assign(ref lval, ref rval) => {
|
||||
// ensure MovePath created for `lval`.
|
||||
bb_ctxt.builder.move_path_for(lval);
|
||||
|
||||
match *rval {
|
||||
Rvalue::Use(ref operand) => {
|
||||
bb_ctxt.on_operand(SK::Use, operand, source)
|
||||
}
|
||||
Rvalue::Repeat(ref operand, ref _const) =>
|
||||
bb_ctxt.on_operand(SK::Repeat, operand, source),
|
||||
Rvalue::Cast(ref _kind, ref operand, ref _ty) =>
|
||||
bb_ctxt.on_operand(SK::Cast, operand, source),
|
||||
Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) => {
|
||||
bb_ctxt.on_operand(SK::BinaryOp, operand1, source);
|
||||
bb_ctxt.on_operand(SK::BinaryOp, operand2, source);
|
||||
}
|
||||
Rvalue::UnaryOp(ref _unop, ref operand) => {
|
||||
bb_ctxt.on_operand(SK::UnaryOp, operand, source);
|
||||
}
|
||||
Rvalue::Box(ref _ty) => {
|
||||
// this is creating uninitialized
|
||||
// memory that needs to be initialized.
|
||||
let deref_lval = Lvalue::Projection(Box::new( repr::Projection {
|
||||
base: lval.clone(),
|
||||
elem: repr::ProjectionElem::Deref,
|
||||
}));
|
||||
bb_ctxt.on_move_out_lval(SK::Box, &deref_lval, source);
|
||||
}
|
||||
Rvalue::Aggregate(ref _kind, ref operands) => {
|
||||
for operand in operands {
|
||||
bb_ctxt.on_operand(SK::Aggregate, operand, source);
|
||||
}
|
||||
}
|
||||
Rvalue::Ref(..) |
|
||||
Rvalue::Len(..) |
|
||||
Rvalue::InlineAsm { .. } => {}
|
||||
|
||||
Rvalue::Slice {..} => {
|
||||
bb_ctxt.tcx.sess.bug("cannot move out of slice");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref term) = bb_data.terminator {
|
||||
match *term {
|
||||
Terminator::Goto { target: _ } | Terminator::Resume => { }
|
||||
|
||||
Terminator::Return => {
|
||||
let source = Location { block: bb,
|
||||
index: bb_data.statements.len() };
|
||||
let lval = &Lvalue::ReturnPointer.deref();
|
||||
bb_ctxt.on_move_out_lval(SK::Return, lval, source);
|
||||
}
|
||||
|
||||
Terminator::If { ref cond, targets: _ } => {
|
||||
// The `cond` is always of (copyable) type `bool`,
|
||||
// so there will never be anything to move.
|
||||
let _ = cond;
|
||||
}
|
||||
|
||||
Terminator::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } |
|
||||
Terminator::Switch { adt_def: _, targets: _, ref discr } => {
|
||||
// The `discr` is not consumed; that is instead
|
||||
// encoded on specific match arms (and for
|
||||
// SwitchInt`, it is always a copyable integer
|
||||
// type anyway).
|
||||
let _ = discr;
|
||||
}
|
||||
|
||||
Terminator::Drop { value: ref lval, target: _, unwind: _ } => {
|
||||
let source = Location { block: bb,
|
||||
index: bb_data.statements.len() };
|
||||
bb_ctxt.on_move_out_lval(SK::Drop, lval, source);
|
||||
}
|
||||
|
||||
Terminator::Call { ref func, ref args, ref destination, cleanup: _ } => {
|
||||
let source = Location { block: bb,
|
||||
index: bb_data.statements.len() };
|
||||
bb_ctxt.on_operand(SK::CallFn, func, source);
|
||||
for arg in args {
|
||||
bb_ctxt.on_operand(SK::CallArg, arg, source);
|
||||
}
|
||||
if let Some((ref destination, _bb)) = *destination {
|
||||
// Create MovePath for `destination`, then
|
||||
// discard returned index.
|
||||
bb_ctxt.builder.move_path_for(destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder = bb_ctxt.builder;
|
||||
}
|
||||
|
||||
// At this point, we may have created some MovePaths that do not
|
||||
// have corresponding entries in the path map.
|
||||
//
|
||||
// (For example, creating the path `a.b.c` may, as a side-effect,
|
||||
// create a path for the parent path `a.b`.)
|
||||
//
|
||||
// All such paths were not referenced ...
|
||||
//
|
||||
// well you know, lets actually try just asserting that the path map *is* complete.
|
||||
assert_eq!(path_map.len(), builder.pre_move_paths.len());
|
||||
path_map.fill_to(builder.pre_move_paths.len() - 1);
|
||||
|
||||
let pre_move_paths = builder.pre_move_paths;
|
||||
let move_paths: Vec<_> = pre_move_paths.into_iter()
|
||||
.map(|p| p.into_move_path())
|
||||
.collect();
|
||||
|
||||
debug!("{}", {
|
||||
let mut seen: Vec<_> = move_paths.iter().map(|_| false).collect();
|
||||
for (j, &MoveOut { ref path, ref source }) in moves.iter().enumerate() {
|
||||
debug!("MovePathData moves[{}]: MoveOut {{ path: {:?} = {:?}, source: {:?} }}",
|
||||
j, path, move_paths[path.idx()], source);
|
||||
seen[path.idx()] = true;
|
||||
}
|
||||
for (j, path) in move_paths.iter().enumerate() {
|
||||
if !seen[j] {
|
||||
debug!("MovePathData move_paths[{}]: {:?}", j, path);
|
||||
}
|
||||
}
|
||||
"done dumping MovePathData"
|
||||
});
|
||||
|
||||
MoveData {
|
||||
move_paths: MovePathData { move_paths: move_paths, },
|
||||
moves: moves,
|
||||
loc_map: LocMap { map: loc_map },
|
||||
path_map: PathMap { map: path_map },
|
||||
rev_lookup: builder.rev_lookup,
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockContext<'b, 'a: 'b, 'tcx: 'a> {
|
||||
tcx: &'b ty::TyCtxt<'tcx>,
|
||||
moves: &'b mut Vec<MoveOut>,
|
||||
builder: MovePathDataBuilder<'a, 'tcx>,
|
||||
path_map: &'b mut Vec<Vec<MoveOutIndex>>,
|
||||
loc_map_bb: &'b mut Vec<Vec<MoveOutIndex>>,
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
|
||||
fn on_move_out_lval(&mut self,
|
||||
stmt_kind: StmtKind,
|
||||
lval: &repr::Lvalue<'tcx>,
|
||||
source: Location) {
|
||||
let tcx = self.tcx;
|
||||
let lval_ty = self.builder.mir.lvalue_ty(tcx, lval);
|
||||
|
||||
// FIXME: does lvalue_ty ever return TyError, or is it
|
||||
// guaranteed to always return non-Infer/non-Error values?
|
||||
|
||||
// This code is just trying to avoid creating a MoveOut
|
||||
// entry for values that do not need move semantics.
|
||||
//
|
||||
// type_contents is imprecise (may claim needs drop for
|
||||
// types that in fact have no destructor). But that is
|
||||
// still usable for our purposes here.
|
||||
let consumed = lval_ty.to_ty(tcx).type_contents(tcx).needs_drop(tcx);
|
||||
|
||||
if !consumed {
|
||||
debug!("ctxt: {:?} no consume of lval: {:?} of type {:?}",
|
||||
stmt_kind, lval, lval_ty);
|
||||
return;
|
||||
}
|
||||
let i = source.index;
|
||||
let index = MoveOutIndex::new(self.moves.len());
|
||||
|
||||
let path = self.builder.move_path_for(lval);
|
||||
self.moves.push(MoveOut { path: path, source: source.clone() });
|
||||
self.path_map.fill_to(path.idx());
|
||||
|
||||
debug!("ctxt: {:?} add consume of lval: {:?} \
|
||||
at index: {:?} \
|
||||
to path_map for path: {:?} and \
|
||||
to loc_map for loc: {:?}",
|
||||
stmt_kind, lval, index, path, source);
|
||||
|
||||
debug_assert!(path.idx() < self.path_map.len());
|
||||
// this is actually a questionable assert; at the very
|
||||
// least, incorrect input code can probably cause it to
|
||||
// fire.
|
||||
assert!(self.path_map[path.idx()].iter().find(|idx| **idx == index).is_none());
|
||||
self.path_map[path.idx()].push(index);
|
||||
|
||||
debug_assert!(i < self.loc_map_bb.len());
|
||||
debug_assert!(self.loc_map_bb[i].iter().find(|idx| **idx == index).is_none());
|
||||
self.loc_map_bb[i].push(index);
|
||||
}
|
||||
|
||||
fn on_operand(&mut self, stmt_kind: StmtKind, operand: &repr::Operand<'tcx>, source: Location) {
|
||||
match *operand {
|
||||
repr::Operand::Constant(..) => {} // not-a-move
|
||||
repr::Operand::Consume(ref lval) => { // a move
|
||||
self.on_move_out_lval(stmt_kind, lval, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> BitDenotation for MoveData<'tcx>{
|
||||
type Bit = MoveOut;
|
||||
fn bits_per_block(&self) -> usize {
|
||||
self.moves.len()
|
||||
}
|
||||
fn interpret(&self, idx: usize) -> &Self::Bit {
|
||||
&self.moves[idx]
|
||||
}
|
||||
}
|
||||
232
src/librustc_borrowck/borrowck/mir/graphviz.rs
Normal file
232
src/librustc_borrowck/borrowck/mir/graphviz.rs
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// Copyright 2012-2016 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.
|
||||
|
||||
//! Hook into libgraphviz for rendering dataflow graphs for MIR.
|
||||
|
||||
use rustc::mir::repr::{BasicBlock, Mir};
|
||||
|
||||
use dot;
|
||||
use dot::IntoCow;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use super::MirBorrowckCtxt;
|
||||
use bitslice::bits_to_string;
|
||||
use super::gather_moves::MoveOut;
|
||||
|
||||
struct Graph<'c, 'b:'c, 'a:'b, 'tcx:'a> { mbcx: &'c MirBorrowckCtxt<'b, 'a, 'tcx>,
|
||||
context: &'b str }
|
||||
|
||||
pub fn print_borrowck_graph_to(mbcx: &MirBorrowckCtxt,
|
||||
context: &str,
|
||||
path: &str) -> io::Result<()> {
|
||||
let g = Graph { mbcx: mbcx, context: context };
|
||||
let mut v = Vec::new();
|
||||
try!(dot::render(&g, &mut v));
|
||||
println!("print_borrowck_graph_to path: {} context: {} node_id: {}",
|
||||
path, context, mbcx.node_id);
|
||||
File::create(path).and_then(|mut f| f.write_all(&v))
|
||||
}
|
||||
|
||||
pub type Node = BasicBlock;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Edge { source: BasicBlock, index: usize }
|
||||
|
||||
fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec<Edge> {
|
||||
let succ_len = mir.basic_block_data(bb).terminator().successors().len();
|
||||
(0..succ_len).map(|index| Edge { source: bb, index: index}).collect()
|
||||
}
|
||||
|
||||
impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> {
|
||||
type Node = Node;
|
||||
type Edge = Edge;
|
||||
fn graph_id(&self) -> dot::Id {
|
||||
dot::Id::new(format!("graph_for_node_{}_{}",
|
||||
self.mbcx.node_id,
|
||||
self.context))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn node_id(&self, n: &Node) -> dot::Id {
|
||||
dot::Id::new(format!("bb_{}", n.index()))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn node_label(&self, n: &Node) -> dot::LabelText {
|
||||
// A standard MIR label, as generated by write_node_label, is
|
||||
// presented in a single column in a table.
|
||||
//
|
||||
// The code below does a bunch of formatting work to format a
|
||||
// node (i.e. MIR basic-block) label with extra
|
||||
// dataflow-enriched information. In particular, the goal is
|
||||
// to add extra columns that present the three dataflow
|
||||
// bitvectors, and the data those bitvectors represent.
|
||||
//
|
||||
// It presents it in the following format (where I am
|
||||
// presenting the table rendering via ASCII art, one line per
|
||||
// row of the table, and a chunk size of 3 rather than 5):
|
||||
//
|
||||
// ------ ----------------------- ------------ --------------------
|
||||
// [e1, e3, e4]
|
||||
// [e8, e9] "= ENTRY:" <ENTRY-BITS>
|
||||
// ------ ----------------------- ------------ --------------------
|
||||
// Left
|
||||
// Most
|
||||
// Column
|
||||
// Is
|
||||
// Just
|
||||
// Normal
|
||||
// Series
|
||||
// Of
|
||||
// MIR
|
||||
// Stmts
|
||||
// ------ ----------------------- ------------ --------------------
|
||||
// [g1, g4, g5] "= GEN:" <GEN-BITS>
|
||||
// ------ ----------------------- ------------ --------------------
|
||||
// "KILL:" <KILL-BITS> "=" [k1, k3, k8]
|
||||
// [k9]
|
||||
// ------ ----------------------- ------------ --------------------
|
||||
//
|
||||
// (In addition, the added dataflow is rendered with a colored
|
||||
// background just so it will stand out compared to the
|
||||
// statements.)
|
||||
let mut v = Vec::new();
|
||||
let i = n.index();
|
||||
let chunk_size = 5;
|
||||
const BG_FLOWCONTENT: &'static str = r#"bgcolor="pink""#;
|
||||
const ALIGN_RIGHT: &'static str = r#"align="right""#;
|
||||
const FACE_MONOSPACE: &'static str = r#"FACE="Courier""#;
|
||||
fn chunked_present_left<W:io::Write>(w: &mut W,
|
||||
interpreted: &[&MoveOut],
|
||||
chunk_size: usize)
|
||||
-> io::Result<()>
|
||||
{
|
||||
// This function may emit a sequence of <tr>'s, but it
|
||||
// always finishes with an (unfinished)
|
||||
// <tr><td></td><td>
|
||||
//
|
||||
// Thus, after being called, one should finish both the
|
||||
// pending <td> as well as the <tr> itself.
|
||||
let mut seen_one = false;
|
||||
for c in interpreted.chunks(chunk_size) {
|
||||
if seen_one {
|
||||
// if not the first row, finish off the previous row
|
||||
try!(write!(w, "</td><td></td><td></td></tr>"));
|
||||
}
|
||||
try!(write!(w, "<tr><td></td><td {bg} {align}>{objs:?}",
|
||||
bg = BG_FLOWCONTENT,
|
||||
align = ALIGN_RIGHT,
|
||||
objs = c));
|
||||
seen_one = true;
|
||||
}
|
||||
if !seen_one {
|
||||
try!(write!(w, "<tr><td></td><td {bg} {align}>[]",
|
||||
bg = BG_FLOWCONTENT,
|
||||
align = ALIGN_RIGHT));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
::rustc_mir::graphviz::write_node_label(
|
||||
*n, self.mbcx.mir, &mut v, 4,
|
||||
|w| {
|
||||
let flow = &self.mbcx.flow_state;
|
||||
let entry = flow.interpret_set(flow.sets.on_entry_set_for(i));
|
||||
try!(chunked_present_left(w, &entry[..], chunk_size));
|
||||
write!(w, "= ENTRY:</td><td {bg}><FONT {face}>{entrybits:?}</FONT></td>\
|
||||
<td></td></tr>",
|
||||
bg = BG_FLOWCONTENT,
|
||||
face = FACE_MONOSPACE,
|
||||
entrybits=bits_to_string(flow.sets.on_entry_set_for(i),
|
||||
flow.sets.bytes_per_block()))
|
||||
},
|
||||
|w| {
|
||||
let flow = &self.mbcx.flow_state;
|
||||
let gen = flow.interpret_set( flow.sets.gen_set_for(i));
|
||||
let kill = flow.interpret_set(flow.sets.kill_set_for(i));
|
||||
try!(chunked_present_left(w, &gen[..], chunk_size));
|
||||
try!(write!(w, " = GEN:</td><td {bg}><FONT {face}>{genbits:?}</FONT></td>\
|
||||
<td></td></tr>",
|
||||
bg = BG_FLOWCONTENT,
|
||||
face = FACE_MONOSPACE,
|
||||
genbits=bits_to_string( flow.sets.gen_set_for(i),
|
||||
flow.sets.bytes_per_block())));
|
||||
try!(write!(w, "<tr><td></td><td {bg} {align}>KILL:</td>\
|
||||
<td {bg}><FONT {face}>{killbits:?}</FONT></td>",
|
||||
bg = BG_FLOWCONTENT,
|
||||
align = ALIGN_RIGHT,
|
||||
face = FACE_MONOSPACE,
|
||||
killbits=bits_to_string(flow.sets.kill_set_for(i),
|
||||
flow.sets.bytes_per_block())));
|
||||
|
||||
// (chunked_present_right)
|
||||
let mut seen_one = false;
|
||||
for k in kill.chunks(chunk_size) {
|
||||
if !seen_one {
|
||||
// continuation of row; this is fourth <td>
|
||||
try!(write!(w, "<td {bg}>= {kill:?}</td></tr>",
|
||||
bg = BG_FLOWCONTENT,
|
||||
kill=k));
|
||||
} else {
|
||||
// new row, with indent of three <td>'s
|
||||
try!(write!(w, "<tr><td></td><td></td><td></td><td {bg}>{kill:?}</td></tr>",
|
||||
bg = BG_FLOWCONTENT,
|
||||
kill=k));
|
||||
}
|
||||
seen_one = true;
|
||||
}
|
||||
if !seen_one {
|
||||
try!(write!(w, "<td {bg}>= []</td></tr>",
|
||||
bg = BG_FLOWCONTENT));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
dot::LabelText::html(String::from_utf8(v).unwrap())
|
||||
}
|
||||
|
||||
fn node_shape(&self, _n: &Node) -> Option<dot::LabelText> {
|
||||
Some(dot::LabelText::label("none"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::GraphWalk<'c> for Graph<'c,'b,'a,'tcx> {
|
||||
type Node = Node;
|
||||
type Edge = Edge;
|
||||
fn nodes(&self) -> dot::Nodes<Node> {
|
||||
self.mbcx.mir.all_basic_blocks().into_cow()
|
||||
}
|
||||
|
||||
fn edges(&self) -> dot::Edges<Edge> {
|
||||
let mir = self.mbcx.mir;
|
||||
let blocks = self.mbcx.mir.all_basic_blocks();
|
||||
// base initial capacity on assumption every block has at
|
||||
// least one outgoing edge (Which should be true for all
|
||||
// blocks but one, the exit-block).
|
||||
let mut edges = Vec::with_capacity(blocks.len());
|
||||
for bb in blocks {
|
||||
let outgoing = outgoing(mir, bb);
|
||||
edges.extend(outgoing.into_iter());
|
||||
}
|
||||
edges.into_cow()
|
||||
}
|
||||
|
||||
fn source(&self, edge: &Edge) -> Node {
|
||||
edge.source
|
||||
}
|
||||
|
||||
fn target(&self, edge: &Edge) -> Node {
|
||||
let mir = self.mbcx.mir;
|
||||
mir.basic_block_data(edge.source).terminator().successors()[edge.index]
|
||||
}
|
||||
}
|
||||
91
src/librustc_borrowck/borrowck/mir/mod.rs
Normal file
91
src/librustc_borrowck/borrowck/mir/mod.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2012-2016 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.
|
||||
|
||||
use borrowck::BorrowckCtxt;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
use rustc_front::hir;
|
||||
use rustc_front::intravisit::{FnKind};
|
||||
|
||||
use rustc::mir::repr::{BasicBlock, BasicBlockData, Mir, Statement, Terminator};
|
||||
|
||||
mod abs_domain;
|
||||
mod dataflow;
|
||||
mod gather_moves;
|
||||
mod graphviz;
|
||||
|
||||
use self::dataflow::{Dataflow, DataflowState};
|
||||
use self::gather_moves::{MoveData};
|
||||
|
||||
pub fn borrowck_mir<'b, 'a: 'b, 'tcx: 'a>(
|
||||
bcx: &'b mut BorrowckCtxt<'a, 'tcx>,
|
||||
fk: FnKind,
|
||||
_decl: &hir::FnDecl,
|
||||
mir: &'a Mir<'tcx>,
|
||||
body: &hir::Block,
|
||||
_sp: Span,
|
||||
id: ast::NodeId,
|
||||
attributes: &[ast::Attribute]) {
|
||||
match fk {
|
||||
FnKind::ItemFn(name, _, _, _, _, _, _) |
|
||||
FnKind::Method(name, _, _, _) => {
|
||||
debug!("borrowck_mir({}) UNIMPLEMENTED", name);
|
||||
}
|
||||
FnKind::Closure(_) => {
|
||||
debug!("borrowck_mir closure (body.id={}) UNIMPLEMENTED", body.id);
|
||||
}
|
||||
}
|
||||
|
||||
let mut mbcx = MirBorrowckCtxt {
|
||||
bcx: bcx,
|
||||
mir: mir,
|
||||
node_id: id,
|
||||
attributes: attributes,
|
||||
flow_state: DataflowState::new_move_analysis(mir, bcx.tcx),
|
||||
};
|
||||
|
||||
for bb in mir.all_basic_blocks() {
|
||||
mbcx.process_basic_block(bb);
|
||||
}
|
||||
|
||||
mbcx.dataflow();
|
||||
|
||||
debug!("borrowck_mir done");
|
||||
}
|
||||
|
||||
pub struct MirBorrowckCtxt<'b, 'a: 'b, 'tcx: 'a> {
|
||||
bcx: &'b mut BorrowckCtxt<'a, 'tcx>,
|
||||
mir: &'b Mir<'tcx>,
|
||||
node_id: ast::NodeId,
|
||||
attributes: &'b [ast::Attribute],
|
||||
flow_state: DataflowState<MoveData<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
|
||||
fn process_basic_block(&mut self, bb: BasicBlock) {
|
||||
let &BasicBlockData { ref statements, ref terminator, is_cleanup: _ } =
|
||||
self.mir.basic_block_data(bb);
|
||||
for stmt in statements {
|
||||
self.process_statement(bb, stmt);
|
||||
}
|
||||
|
||||
self.process_terminator(bb, terminator);
|
||||
}
|
||||
|
||||
fn process_statement(&mut self, bb: BasicBlock, stmt: &Statement<'tcx>) {
|
||||
debug!("MirBorrowckCtxt::process_statement({:?}, {:?}", bb, stmt);
|
||||
}
|
||||
|
||||
fn process_terminator(&mut self, bb: BasicBlock, term: &Option<Terminator<'tcx>>) {
|
||||
debug!("MirBorrowckCtxt::process_terminator({:?}, {:?})", bb, term);
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ use std::fmt;
|
|||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use syntax::ast;
|
||||
use syntax::attr::AttrMetaMethods;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::errors::DiagnosticBuilder;
|
||||
|
||||
|
|
@ -49,12 +50,16 @@ use rustc_front::intravisit;
|
|||
use rustc_front::intravisit::{Visitor, FnKind};
|
||||
use rustc_front::util as hir_util;
|
||||
|
||||
use rustc::mir::mir_map::MirMap;
|
||||
|
||||
pub mod check_loans;
|
||||
|
||||
pub mod gather_loans;
|
||||
|
||||
pub mod move_data;
|
||||
|
||||
mod mir;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LoanDataFlowOperator;
|
||||
|
||||
|
|
@ -66,15 +71,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
|
|||
match fk {
|
||||
FnKind::ItemFn(..) |
|
||||
FnKind::Method(..) => {
|
||||
let new_free_region_map = self.tcx.free_region_map(id);
|
||||
let old_free_region_map =
|
||||
mem::replace(&mut self.free_region_map, new_free_region_map);
|
||||
borrowck_fn(self, fk, fd, b, s, id);
|
||||
self.free_region_map = old_free_region_map;
|
||||
self.with_temp_region_map(id, |this| {
|
||||
borrowck_fn(this, fk, fd, b, s, id, fk.attrs())
|
||||
});
|
||||
}
|
||||
|
||||
FnKind::Closure => {
|
||||
borrowck_fn(self, fk, fd, b, s, id);
|
||||
FnKind::Closure(..) => {
|
||||
borrowck_fn(self, fk, fd, b, s, id, fk.attrs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -98,9 +101,10 @@ impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_crate(tcx: &TyCtxt) {
|
||||
pub fn check_crate<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &MirMap<'tcx>) {
|
||||
let mut bccx = BorrowckCtxt {
|
||||
tcx: tcx,
|
||||
mir_map: Some(mir_map),
|
||||
free_region_map: FreeRegionMap::new(),
|
||||
stats: BorrowStats {
|
||||
loaned_paths_same: 0,
|
||||
|
|
@ -159,8 +163,17 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
|
|||
decl: &hir::FnDecl,
|
||||
body: &hir::Block,
|
||||
sp: Span,
|
||||
id: ast::NodeId) {
|
||||
id: ast::NodeId,
|
||||
attributes: &[ast::Attribute]) {
|
||||
debug!("borrowck_fn(id={})", id);
|
||||
|
||||
if attributes.iter().any(|item| item.check_name("rustc_mir_borrowck")) {
|
||||
let mir = this.mir_map.unwrap().map.get(&id).unwrap();
|
||||
this.with_temp_region_map(id, |this| {
|
||||
mir::borrowck_mir(this, fk, decl, mir, body, sp, id, attributes)
|
||||
});
|
||||
}
|
||||
|
||||
let cfg = cfg::CFG::new(this.tcx, body);
|
||||
let AnalysisData { all_loans,
|
||||
loans: loan_dfcx,
|
||||
|
|
@ -233,6 +246,7 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
|
|||
/// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
|
||||
pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
|
||||
tcx: &'a TyCtxt<'tcx>,
|
||||
mir_map: Option<&'a MirMap<'tcx>>,
|
||||
fn_parts: FnParts<'a>,
|
||||
cfg: &cfg::CFG)
|
||||
-> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>)
|
||||
|
|
@ -240,6 +254,7 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
|
|||
|
||||
let mut bccx = BorrowckCtxt {
|
||||
tcx: tcx,
|
||||
mir_map: mir_map,
|
||||
free_region_map: FreeRegionMap::new(),
|
||||
stats: BorrowStats {
|
||||
loaned_paths_same: 0,
|
||||
|
|
@ -279,9 +294,13 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
|
|||
free_region_map: FreeRegionMap,
|
||||
|
||||
// Statistics:
|
||||
stats: BorrowStats
|
||||
stats: BorrowStats,
|
||||
|
||||
// NodeId to MIR mapping (for methods that carry the #[rustc_mir] attribute).
|
||||
mir_map: Option<&'a MirMap<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct BorrowStats {
|
||||
loaned_paths_same: usize,
|
||||
loaned_paths_imm: usize,
|
||||
|
|
@ -574,6 +593,15 @@ pub enum MovedValueUseKind {
|
|||
// Misc
|
||||
|
||||
impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
fn with_temp_region_map<F>(&mut self, id: ast::NodeId, f: F)
|
||||
where F: for <'b> FnOnce(&'b mut BorrowckCtxt<'a, 'tcx>)
|
||||
{
|
||||
let new_free_region_map = self.tcx.free_region_map(id);
|
||||
let old_free_region_map = mem::replace(&mut self.free_region_map, new_free_region_map);
|
||||
f(self);
|
||||
self.free_region_map = old_free_region_map;
|
||||
}
|
||||
|
||||
pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
|
||||
-> bool
|
||||
{
|
||||
|
|
|
|||
|
|
@ -129,7 +129,9 @@ impl<'a, 'tcx> DataflowLabeller<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> dot::Labeller<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a, 'tcx> {
|
||||
impl<'a, 'tcx> dot::Labeller<'a> for DataflowLabeller<'a, 'tcx> {
|
||||
type Node = Node<'a>;
|
||||
type Edge = Edge<'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> {
|
||||
|
|
@ -143,7 +145,9 @@ impl<'a, 'tcx> dot::Labeller<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a, 't
|
|||
fn edge_label(&'a self, e: &Edge<'a>) -> dot::LabelText<'a> { self.inner.edge_label(e) }
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a, 'tcx> {
|
||||
impl<'a, 'tcx> dot::GraphWalk<'a> for DataflowLabeller<'a, 'tcx> {
|
||||
type Node = Node<'a>;
|
||||
type Edge = Edge<'a>;
|
||||
fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() }
|
||||
fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() }
|
||||
fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) }
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
#![feature(rustc_diagnostic_macros)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(staged_api)]
|
||||
|
||||
#![feature(associated_consts)]
|
||||
#![feature(nonzero)]
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate syntax;
|
||||
|
||||
|
|
@ -32,6 +33,8 @@
|
|||
extern crate graphviz as dot;
|
||||
extern crate rustc;
|
||||
extern crate rustc_front;
|
||||
extern crate rustc_mir;
|
||||
extern crate core; // for NonZero
|
||||
|
||||
pub use borrowck::check_crate;
|
||||
pub use borrowck::build_borrowck_dataflow_data_for_fn;
|
||||
|
|
@ -42,6 +45,7 @@ pub use borrowck::{AnalysisData, BorrowckCtxt};
|
|||
pub mod diagnostics;
|
||||
|
||||
mod borrowck;
|
||||
mod bitslice;
|
||||
|
||||
pub mod graphviz;
|
||||
|
||||
|
|
|
|||
|
|
@ -880,7 +880,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
|
|||
|
||||
time(time_passes,
|
||||
"borrow checking",
|
||||
|| borrowck::check_crate(tcx));
|
||||
|| borrowck::check_crate(tcx, &mir_map));
|
||||
|
||||
// Avoid overwhelming user with errors if type checking failed.
|
||||
// I'm not sure how helpful this is, to be honest, but it avoids
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ use rustc_front::hir;
|
|||
use rustc_front::lowering::{lower_crate, LoweringContext};
|
||||
use rustc_front::print::pprust as pprust_hir;
|
||||
|
||||
use rustc::mir::mir_map::MirMap;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum PpSourceMode {
|
||||
PpmNormal,
|
||||
|
|
@ -875,9 +877,10 @@ pub fn pretty_print_input(sess: Session,
|
|||
&arenas,
|
||||
&id,
|
||||
resolve::MakeGlobMap::No,
|
||||
|tcx, _, _, _| {
|
||||
|tcx, mir_map, _, _| {
|
||||
print_flowgraph(variants,
|
||||
tcx,
|
||||
mir_map.as_ref(),
|
||||
code,
|
||||
mode,
|
||||
out)
|
||||
|
|
@ -911,12 +914,13 @@ pub fn pretty_print_input(sess: Session,
|
|||
}
|
||||
}
|
||||
|
||||
fn print_flowgraph<W: Write>(variants: Vec<borrowck_dot::Variant>,
|
||||
tcx: &TyCtxt,
|
||||
code: blocks::Code,
|
||||
mode: PpFlowGraphMode,
|
||||
mut out: W)
|
||||
-> io::Result<()> {
|
||||
fn print_flowgraph<'tcx, W: Write>(variants: Vec<borrowck_dot::Variant>,
|
||||
tcx: &TyCtxt<'tcx>,
|
||||
mir_map: Option<&MirMap<'tcx>>,
|
||||
code: blocks::Code,
|
||||
mode: PpFlowGraphMode,
|
||||
mut out: W)
|
||||
-> io::Result<()> {
|
||||
let cfg = match code {
|
||||
blocks::BlockCode(block) => cfg::CFG::new(tcx, &block),
|
||||
blocks::FnLikeCode(fn_like) => cfg::CFG::new(tcx, &fn_like.body()),
|
||||
|
|
@ -942,6 +946,7 @@ fn print_flowgraph<W: Write>(variants: Vec<borrowck_dot::Variant>,
|
|||
blocks::FnLikeCode(fn_like) => {
|
||||
let (bccx, analysis_data) =
|
||||
borrowck::build_borrowck_dataflow_data_for_fn(tcx,
|
||||
mir_map,
|
||||
fn_like.to_fn_parts(),
|
||||
&cfg);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,19 +27,30 @@
|
|||
|
||||
use syntax::abi::Abi;
|
||||
use syntax::ast::{NodeId, CRATE_NODE_ID, Name, Attribute};
|
||||
use syntax::attr::ThinAttributesExt;
|
||||
use syntax::codemap::Span;
|
||||
use hir::*;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum FnKind<'a> {
|
||||
/// fn foo() or extern "Abi" fn foo()
|
||||
ItemFn(Name, &'a Generics, Unsafety, Constness, Abi, Visibility),
|
||||
ItemFn(Name, &'a Generics, Unsafety, Constness, Abi, Visibility, &'a [Attribute]),
|
||||
|
||||
/// fn foo(&self)
|
||||
Method(Name, &'a MethodSig, Option<Visibility>),
|
||||
Method(Name, &'a MethodSig, Option<Visibility>, &'a [Attribute]),
|
||||
|
||||
/// |x, y| {}
|
||||
Closure,
|
||||
Closure(&'a [Attribute]),
|
||||
}
|
||||
|
||||
impl<'a> FnKind<'a> {
|
||||
pub fn attrs(&self) -> &'a [Attribute] {
|
||||
match *self {
|
||||
FnKind::ItemFn(_, _, _, _, _, _, attrs) => attrs,
|
||||
FnKind::Method(_, _, _, attrs) => attrs,
|
||||
FnKind::Closure(attrs) => attrs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Each method of the Visitor trait is a hook to be potentially
|
||||
|
|
@ -310,7 +321,8 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
|
|||
unsafety,
|
||||
constness,
|
||||
abi,
|
||||
item.vis),
|
||||
item.vis,
|
||||
&item.attrs),
|
||||
declaration,
|
||||
body,
|
||||
item.span,
|
||||
|
|
@ -595,14 +607,14 @@ pub fn walk_fn_decl_nopat<'v, V: Visitor<'v>>(visitor: &mut V, function_declarat
|
|||
|
||||
pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<'v>) {
|
||||
match function_kind {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _) => {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _, _) => {
|
||||
visitor.visit_generics(generics);
|
||||
}
|
||||
FnKind::Method(_, sig, _) => {
|
||||
FnKind::Method(_, sig, _, _) => {
|
||||
visitor.visit_generics(&sig.generics);
|
||||
visitor.visit_explicit_self(&sig.explicit_self);
|
||||
}
|
||||
FnKind::Closure => {}
|
||||
FnKind::Closure(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -630,7 +642,10 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai
|
|||
walk_fn_decl(visitor, &sig.decl);
|
||||
}
|
||||
MethodTraitItem(ref sig, Some(ref body)) => {
|
||||
visitor.visit_fn(FnKind::Method(trait_item.name, sig, None),
|
||||
visitor.visit_fn(FnKind::Method(trait_item.name,
|
||||
sig,
|
||||
None,
|
||||
&trait_item.attrs),
|
||||
&sig.decl,
|
||||
body,
|
||||
trait_item.span,
|
||||
|
|
@ -652,7 +667,10 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt
|
|||
visitor.visit_expr(expr);
|
||||
}
|
||||
ImplItemKind::Method(ref sig, ref body) => {
|
||||
visitor.visit_fn(FnKind::Method(impl_item.name, sig, Some(impl_item.vis)),
|
||||
visitor.visit_fn(FnKind::Method(impl_item.name,
|
||||
sig,
|
||||
Some(impl_item.vis),
|
||||
&impl_item.attrs),
|
||||
&sig.decl,
|
||||
body,
|
||||
impl_item.span,
|
||||
|
|
@ -758,7 +776,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
|
|||
walk_list!(visitor, visit_arm, arms);
|
||||
}
|
||||
ExprClosure(_, ref function_declaration, ref body) => {
|
||||
visitor.visit_fn(FnKind::Closure,
|
||||
visitor.visit_fn(FnKind::Closure(expression.attrs.as_attr_slice()),
|
||||
function_declaration,
|
||||
body,
|
||||
expression.span,
|
||||
|
|
|
|||
|
|
@ -254,13 +254,13 @@ impl<'a, 'v, O: ast_util::IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O>
|
|||
self.operation.visit_id(node_id);
|
||||
|
||||
match function_kind {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _) => {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _, _) => {
|
||||
self.visit_generics_helper(generics)
|
||||
}
|
||||
FnKind::Method(_, sig, _) => {
|
||||
FnKind::Method(_, sig, _, _) => {
|
||||
self.visit_generics_helper(&sig.generics)
|
||||
}
|
||||
FnKind::Closure => {}
|
||||
FnKind::Closure(_) => {}
|
||||
}
|
||||
|
||||
for argument in &function_declaration.inputs {
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ impl LateLintPass for NonSnakeCase {
|
|||
fk: FnKind, _: &hir::FnDecl,
|
||||
_: &hir::Block, span: Span, id: ast::NodeId) {
|
||||
match fk {
|
||||
FnKind::Method(name, _, _) => match method_context(cx, id, span) {
|
||||
FnKind::Method(name, _, _, _) => match method_context(cx, id, span) {
|
||||
MethodLateContext::PlainImpl => {
|
||||
self.check_snake_case(cx, "method", &name.as_str(), Some(span))
|
||||
},
|
||||
|
|
@ -246,10 +246,10 @@ impl LateLintPass for NonSnakeCase {
|
|||
},
|
||||
_ => (),
|
||||
},
|
||||
FnKind::ItemFn(name, _, _, _, _, _) => {
|
||||
FnKind::ItemFn(name, _, _, _, _, _, _) => {
|
||||
self.check_snake_case(cx, "function", &name.as_str(), Some(span))
|
||||
},
|
||||
_ => (),
|
||||
FnKind::Closure(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -223,10 +223,10 @@ impl LateLintPass for UnsafeCode {
|
|||
fn check_fn(&mut self, cx: &LateContext, fk: FnKind, _: &hir::FnDecl,
|
||||
_: &hir::Block, span: Span, _: ast::NodeId) {
|
||||
match fk {
|
||||
FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, _, _, _) =>
|
||||
FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, _, _, _, _) =>
|
||||
cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function"),
|
||||
|
||||
FnKind::Method(_, sig, _) => {
|
||||
FnKind::Method(_, sig, _, _) => {
|
||||
if sig.unsafety == hir::Unsafety::Unsafe {
|
||||
cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method")
|
||||
}
|
||||
|
|
@ -670,7 +670,7 @@ impl LateLintPass for UnconditionalRecursion {
|
|||
cx.tcx.impl_or_trait_item(cx.tcx.map.local_def_id(id)).as_opt_method()
|
||||
}
|
||||
// closures can't recur, so they don't matter.
|
||||
FnKind::Closure => return
|
||||
FnKind::Closure(_) => return
|
||||
};
|
||||
|
||||
// Walk through this function (say `f`) looking to see if
|
||||
|
|
|
|||
|
|
@ -43,16 +43,33 @@ where W: Write, I: Iterator<Item=(&'a NodeId, &'a Mir<'a>)> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Write a graphviz DOT node for the given basic block.
|
||||
fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
|
||||
/// Write a graphviz HTML-styled label for the given basic block, with
|
||||
/// all necessary escaping already performed. (This is suitable for
|
||||
/// emitting directly, as is done in this module, or for use with the
|
||||
/// LabelText::HtmlStr from libgraphviz.)
|
||||
///
|
||||
/// `init` and `fini` are callbacks for emitting additional rows of
|
||||
/// data (using HTML enclosed with `<tr>` in the emitted text).
|
||||
pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
|
||||
mir: &Mir,
|
||||
w: &mut W,
|
||||
num_cols: u32,
|
||||
init: INIT,
|
||||
fini: FINI) -> io::Result<()>
|
||||
where INIT: Fn(&mut W) -> io::Result<()>,
|
||||
FINI: Fn(&mut W) -> io::Result<()>
|
||||
{
|
||||
let data = mir.basic_block_data(block);
|
||||
|
||||
// Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
|
||||
try!(write!(w, r#" {} [shape="none", label=<"#, node(block)));
|
||||
try!(write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#));
|
||||
|
||||
// Basic block number at the top.
|
||||
try!(write!(w, r#"<tr><td bgcolor="gray" align="center">{}</td></tr>"#, block.index()));
|
||||
try!(write!(w, r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
|
||||
attrs=r#"bgcolor="gray" align="center""#,
|
||||
colspan=num_cols,
|
||||
blk=block.index()));
|
||||
|
||||
try!(init(w));
|
||||
|
||||
// List of statements in the middle.
|
||||
if !data.statements.is_empty() {
|
||||
|
|
@ -69,8 +86,19 @@ fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<(
|
|||
data.terminator().fmt_head(&mut terminator_head).unwrap();
|
||||
try!(write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head)));
|
||||
|
||||
// Close the table, node label, and the node itself.
|
||||
writeln!(w, "</table>>];")
|
||||
try!(fini(w));
|
||||
|
||||
// Close the table
|
||||
writeln!(w, "</table>")
|
||||
}
|
||||
|
||||
/// Write a graphviz DOT node for the given basic block.
|
||||
fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
|
||||
// Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
|
||||
try!(write!(w, r#" {} [shape="none", label=<"#, node(block)));
|
||||
try!(write_node_label(block, mir, w, 1, |_| Ok(()), |_| Ok(())));
|
||||
// Close the node label and the node itself.
|
||||
writeln!(w, ">];")
|
||||
}
|
||||
|
||||
/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
|
|||
body: &'tcx hir::Block,
|
||||
span: Span,
|
||||
id: ast::NodeId) {
|
||||
let implicit_arg_tys = if let intravisit::FnKind::Closure = fk {
|
||||
let implicit_arg_tys = if let intravisit::FnKind::Closure(..) = fk {
|
||||
vec![closure_self_ty(&self.tcx, id, body.id)]
|
||||
} else {
|
||||
vec![]
|
||||
|
|
|
|||
|
|
@ -132,10 +132,10 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
let mode = match fk {
|
||||
FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _) => {
|
||||
FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _, _) => {
|
||||
Mode::ConstFn
|
||||
}
|
||||
FnKind::Method(_, m, _) => {
|
||||
FnKind::Method(_, m, _, _) => {
|
||||
if m.constness == hir::Constness::Const {
|
||||
Mode::ConstFn
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -634,16 +634,16 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Resolver<'a, 'tcx> {
|
|||
_: Span,
|
||||
node_id: NodeId) {
|
||||
let rib_kind = match function_kind {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _) => {
|
||||
FnKind::ItemFn(_, generics, _, _, _, _, _) => {
|
||||
self.visit_generics(generics);
|
||||
ItemRibKind
|
||||
}
|
||||
FnKind::Method(_, sig, _) => {
|
||||
FnKind::Method(_, sig, _, _) => {
|
||||
self.visit_generics(&sig.generics);
|
||||
self.visit_explicit_self(&sig.explicit_self);
|
||||
MethodRibKind
|
||||
}
|
||||
FnKind::Closure => ClosureRibKind(node_id),
|
||||
FnKind::Closure(_) => ClosureRibKind(node_id),
|
||||
};
|
||||
self.resolve_function(rib_kind, declaration, block);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,7 +253,9 @@ fn dump_graph(tcx: &TyCtxt) {
|
|||
|
||||
pub struct GraphvizDepGraph(FnvHashSet<DepNode>, Vec<(DepNode, DepNode)>);
|
||||
|
||||
impl<'a, 'tcx> dot::GraphWalk<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph {
|
||||
impl<'a, 'tcx> dot::GraphWalk<'a> for GraphvizDepGraph {
|
||||
type Node = DepNode;
|
||||
type Edge = (DepNode, DepNode);
|
||||
fn nodes(&self) -> dot::Nodes<DepNode> {
|
||||
let nodes: Vec<_> = self.0.iter().cloned().collect();
|
||||
nodes.into_cow()
|
||||
|
|
@ -269,7 +271,9 @@ impl<'a, 'tcx> dot::GraphWalk<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGr
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> dot::Labeller<'a, DepNode, (DepNode, DepNode)> for GraphvizDepGraph {
|
||||
impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
|
||||
type Node = DepNode;
|
||||
type Edge = (DepNode, DepNode);
|
||||
fn graph_id(&self) -> dot::Id {
|
||||
dot::Id::new("DependencyGraph").unwrap()
|
||||
}
|
||||
|
|
|
|||
1
src/rustc/Cargo.lock
generated
1
src/rustc/Cargo.lock
generated
|
|
@ -109,6 +109,7 @@ dependencies = [
|
|||
"log 0.0.0",
|
||||
"rustc 0.0.0",
|
||||
"rustc_front 0.0.0",
|
||||
"rustc_mir 0.0.0",
|
||||
"syntax 0.0.0",
|
||||
]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue