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:
bors 2016-03-22 10:00:12 -07:00
commit e3f2dfdece
34 changed files with 2014 additions and 136 deletions

View file

@ -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"),

View file

@ -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) }

View file

@ -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),
}

View file

@ -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 {

View file

@ -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;

View file

@ -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),
};

View file

@ -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() {

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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,