diff --git a/mk/crates.mk b/mk/crates.mk index 1c7f141627bb3..54733083aaeb0 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -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 \ diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index 2e26cc1b2660a..f1317e80b0343 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -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> { + fn node_shape(&'a self, _node: &Self::Node) -> Option> { 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 { /// 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: &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() } diff --git a/src/librustc/front/map/blocks.rs b/src/librustc/front/map/blocks.rs index 752b625f529c0..976a8c6dda046 100644 --- a/src/librustc/front/map/blocks.rs +++ b/src/librustc/front/map/blocks.rs @@ -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, &'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"), diff --git a/src/librustc/middle/cfg/graphviz.rs b/src/librustc/middle/cfg/graphviz.rs index e807092507082..c9c712c2d6e10 100644 --- a/src/librustc/middle/cfg/graphviz.rs +++ b/src/librustc/middle/cfg/graphviz.rs @@ -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) } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 16e0a334440ff..b456291b17a0a 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -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), } diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 47b6c49fddb6d..228a7d21007e5 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -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 { diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index c746fab3ea13b..de3f7f1b0863b 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -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; diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index e6821cf639eca..d4ff4b797c839 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -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), }; diff --git a/src/librustc/middle/infer/region_inference/graphviz.rs b/src/librustc/middle/infer/region_inference/graphviz.rs index b6c9b5636d9af..23559e7b340eb 100644 --- a/src/librustc/middle/infer/region_inference/graphviz.rs +++ b/src/librustc/middle/infer/region_inference/graphviz.rs @@ -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 { let mut set = FnvHashSet(); for node in self.node_ids.keys() { diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index fd857513e5b56..233e55cdbb5dd 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -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); } } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index f29e14f67d73f..b5ea365f880a2 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -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); } } diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 06d68af883899..1476c3513dcf7 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -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, diff --git a/src/librustc_borrowck/Cargo.toml b/src/librustc_borrowck/Cargo.toml index f78f6fb86ae22..fdb97bd8d2d18 100644 --- a/src/librustc_borrowck/Cargo.toml +++ b/src/librustc_borrowck/Cargo.toml @@ -14,3 +14,4 @@ syntax = { path = "../libsyntax" } graphviz = { path = "../libgraphviz" } rustc = { path = "../librustc" } rustc_front = { path = "../librustc_front" } +rustc_mir = { path = "../librustc_mir" } diff --git a/src/librustc_borrowck/bitslice.rs b/src/librustc_borrowck/bitslice.rs new file mode 100644 index 0000000000000..a4aa7ae15744d --- /dev/null +++ b/src/librustc_borrowck/bitslice.rs @@ -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 or the MIT license +// , 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::()), 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::()), 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::() * 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::() { + 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 +} diff --git a/src/librustc_borrowck/borrowck/mir/abs_domain.rs b/src/librustc_borrowck/borrowck/mir/abs_domain.rs new file mode 100644 index 0000000000000..aa885eb47424d --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/abs_domain.rs @@ -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 or the MIT license +// , 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()), + } + } +} diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs new file mode 100644 index 0000000000000..69aaae91c49fa --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/dataflow.rs @@ -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 or the MIT license +// , 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(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(&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, +} + +impl Bits { + fn new(init_word: usize, num_words: usize) -> Self { + Bits { bits: vec![init_word; num_words] } + } +} + +pub struct DataflowState +{ + /// 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 DataflowState { + fn each_bit(&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::(); + 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 DataflowState { + pub fn new(mir: &Mir, denotation: D) -> Self { + let bits_per_block = denotation.bits_per_block(); + let usize_bits = mem::size_of::() * 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 DataflowState { + /// 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( + &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> { + 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(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 } +} diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs new file mode 100644 index 0000000000000..17a016a3ae300 --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -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 or the MIT license +// , 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 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); + + 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, + pub first_child: Option, + pub parent: Option, + 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, + pub first_child: Cell>, + pub parent: Option, + 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, + 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>>, +} + +impl Index 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>, +} + +impl Index 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>, +} + +impl<'tcx> Index 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>; + +struct MovePathDataBuilder<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, + pre_move_paths: Vec>, + 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, + + /// A single move path (representing any static data referenced) + /// is constructed lazily when we first encounter statics in the + /// input MIR. + statics: Option, + + /// 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, 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 FillTo for Vec { + 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(LookupKind, T); + +impl Lookup { + 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>, + idx: u32, + next_index: &mut MovePathIndex) -> Lookup { + 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 { + Self::lookup_or_generate(&mut self.vars, + var_idx, + &mut self.next_index) + } + + fn lookup_temp(&mut self, temp_idx: u32) -> Lookup { + Self::lookup_or_generate(&mut self.temps, + temp_idx, + &mut self.next_index) + } + + fn lookup_arg(&mut self, arg_idx: u32) -> Lookup { + Self::lookup_or_generate(&mut self.args, + arg_idx, + &mut self.next_index) + } + + fn lookup_static(&mut self) -> Lookup { + 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 { + 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 { + 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 { + 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 = 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, + builder: MovePathDataBuilder<'a, 'tcx>, + path_map: &'b mut Vec>, + loc_map_bb: &'b mut Vec>, +} + +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] + } +} diff --git a/src/librustc_borrowck/borrowck/mir/graphviz.rs b/src/librustc_borrowck/borrowck/mir/graphviz.rs new file mode 100644 index 0000000000000..b81ef186a4f9d --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/graphviz.rs @@ -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 or the MIT license +// , 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 { + 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:" + // ------ ----------------------- ------------ -------------------- + // Left + // Most + // Column + // Is + // Just + // Normal + // Series + // Of + // MIR + // Stmts + // ------ ----------------------- ------------ -------------------- + // [g1, g4, g5] "= GEN:" + // ------ ----------------------- ------------ -------------------- + // "KILL:" "=" [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: &mut W, + interpreted: &[&MoveOut], + chunk_size: usize) + -> io::Result<()> + { + // This function may emit a sequence of 's, but it + // always finishes with an (unfinished) + // + // + // Thus, after being called, one should finish both the + // pending as well as the 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, "")); + } + try!(write!(w, "{objs:?}", + bg = BG_FLOWCONTENT, + align = ALIGN_RIGHT, + objs = c)); + seen_one = true; + } + if !seen_one { + try!(write!(w, "[]", + 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:{entrybits:?}\ + ", + 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:{genbits:?}\ + ", + bg = BG_FLOWCONTENT, + face = FACE_MONOSPACE, + genbits=bits_to_string( flow.sets.gen_set_for(i), + flow.sets.bytes_per_block()))); + try!(write!(w, "KILL:\ + {killbits:?}", + 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 + try!(write!(w, "= {kill:?}", + bg = BG_FLOWCONTENT, + kill=k)); + } else { + // new row, with indent of three 's + try!(write!(w, "{kill:?}", + bg = BG_FLOWCONTENT, + kill=k)); + } + seen_one = true; + } + if !seen_one { + try!(write!(w, "= []", + bg = BG_FLOWCONTENT)); + } + + Ok(()) + }) + .unwrap(); + dot::LabelText::html(String::from_utf8(v).unwrap()) + } + + fn node_shape(&self, _n: &Node) -> Option { + 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 { + self.mbcx.mir.all_basic_blocks().into_cow() + } + + fn edges(&self) -> dot::Edges { + 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] + } +} diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs new file mode 100644 index 0000000000000..d1335811858b8 --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -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 or the MIT license +// , 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>, +} + +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>) { + debug!("MirBorrowckCtxt::process_terminator({:?}, {:?})", bb, term); + } +} diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index cab8125a61b33..bb2fe7acb9536 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -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(&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 { diff --git a/src/librustc_borrowck/graphviz.rs b/src/librustc_borrowck/graphviz.rs index 7a5491cdbe7f3..fd23772bcda13 100644 --- a/src/librustc_borrowck/graphviz.rs +++ b/src/librustc_borrowck/graphviz.rs @@ -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) } diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index e7f5fddc7bbf2..65e9b79224181 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -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; diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 8dac25cc0cbf1..c39d0d7587f43 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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 diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 4792fa72831b8..5134278de2183 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -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(variants: Vec, - tcx: &TyCtxt, - code: blocks::Code, - mode: PpFlowGraphMode, - mut out: W) - -> io::Result<()> { +fn print_flowgraph<'tcx, W: Write>(variants: Vec, + 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(variants: Vec, blocks::FnLikeCode(fn_like) => { let (bccx, analysis_data) = borrowck::build_borrowck_dataflow_data_for_fn(tcx, + mir_map, fn_like.to_fn_parts(), &cfg); diff --git a/src/librustc_front/intravisit.rs b/src/librustc_front/intravisit.rs index e031dfc5b161d..be1cc528d889e 100644 --- a/src/librustc_front/intravisit.rs +++ b/src/librustc_front/intravisit.rs @@ -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), + Method(Name, &'a MethodSig, Option, &'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, diff --git a/src/librustc_front/util.rs b/src/librustc_front/util.rs index 2c86c713b1b7a..f4f9cb75eaf36 100644 --- a/src/librustc_front/util.rs +++ b/src/librustc_front/util.rs @@ -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 { diff --git a/src/librustc_lint/bad_style.rs b/src/librustc_lint/bad_style.rs index 925aec9894eaa..e399270197e2b 100644 --- a/src/librustc_lint/bad_style.rs +++ b/src/librustc_lint/bad_style.rs @@ -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(_) => (), } } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 88027931022e7..2ccb905b6ecdf 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -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 diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs index f705c0591b559..bed83708ff8b5 100644 --- a/src/librustc_mir/graphviz.rs +++ b/src/librustc_mir/graphviz.rs @@ -43,16 +43,33 @@ where W: Write, I: Iterator)> { Ok(()) } -/// Write a graphviz DOT node for the given basic block. -fn write_node(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 `` in the emitted text). +pub fn write_node_label(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#""#)); // Basic block number at the top. - try!(write!(w, r#""#, block.index())); + try!(write!(w, r#""#, + 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(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<( data.terminator().fmt_head(&mut terminator_head).unwrap(); try!(write!(w, r#""#, dot::escape_html(&terminator_head))); - // Close the table, node label, and the node itself. - writeln!(w, "
{}
{blk}
{}
>];") + try!(fini(w)); + + // Close the table + writeln!(w, "") +} + +/// Write a graphviz DOT node for the given basic block. +fn write_node(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. diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 4717c54ca6491..05dbd63ef1a42 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -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![] diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index c964179d4076a..c2b764594180c 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -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 { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 9d09e8d1eb1a1..af8c9d8168742 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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); } diff --git a/src/librustc_trans/trans/assert_dep_graph.rs b/src/librustc_trans/trans/assert_dep_graph.rs index a186a8ea99ce9..11386715492b6 100644 --- a/src/librustc_trans/trans/assert_dep_graph.rs +++ b/src/librustc_trans/trans/assert_dep_graph.rs @@ -253,7 +253,9 @@ fn dump_graph(tcx: &TyCtxt) { pub struct GraphvizDepGraph(FnvHashSet, 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 { 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() } diff --git a/src/rustc/Cargo.lock b/src/rustc/Cargo.lock index 03e7aaca0f78c..9ef0b40002298 100644 --- a/src/rustc/Cargo.lock +++ b/src/rustc/Cargo.lock @@ -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", ]