]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/mir_map.rs
Rollup merge of #29660 - steveklabnik:gh28461, r=brson
[rust.git] / src / librustc_mir / mir_map.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! An experimental pass that scources for `#[rustc_mir]` attributes,
12 //! builds the resulting MIR, and dumps it out into a file for inspection.
13 //!
14 //! The attribute formats that are currently accepted are:
15 //!
16 //! - `#[rustc_mir(graphviz="file.gv")]`
17 //! - `#[rustc_mir(pretty="file.mir")]`
18
19 extern crate syntax;
20 extern crate rustc;
21 extern crate rustc_front;
22
23 use build;
24 use dot;
25 use repr::Mir;
26 use hair::cx::{PatNode, Cx};
27 use std::fs::File;
28
29 use self::rustc::middle::infer;
30 use self::rustc::middle::region::CodeExtentData;
31 use self::rustc::middle::ty::{self, Ty};
32 use self::rustc::util::common::ErrorReported;
33 use self::rustc::util::nodemap::NodeMap;
34 use self::rustc_front::hir;
35 use self::rustc_front::visit;
36 use self::syntax::ast;
37 use self::syntax::attr::AttrMetaMethods;
38 use self::syntax::codemap::Span;
39
40 pub type MirMap<'tcx> = NodeMap<Mir<'tcx>>;
41
42 pub fn build_mir_for_crate<'tcx>(tcx: &ty::ctxt<'tcx>) -> MirMap<'tcx> {
43     let mut map = NodeMap();
44     {
45         let mut dump = OuterDump {
46             tcx: tcx,
47             map: &mut map,
48         };
49         visit::walk_crate(&mut dump, tcx.map.krate());
50     }
51     map
52 }
53
54 ///////////////////////////////////////////////////////////////////////////
55 // OuterDump -- walks a crate, looking for fn items and methods to build MIR from
56
57 struct OuterDump<'a, 'tcx: 'a> {
58     tcx: &'a ty::ctxt<'tcx>,
59     map: &'a mut MirMap<'tcx>,
60 }
61
62 impl<'a, 'tcx> OuterDump<'a, 'tcx> {
63     fn visit_mir<OP>(&mut self, attributes: &'a [ast::Attribute], mut walk_op: OP)
64         where OP: for<'m> FnMut(&mut InnerDump<'a, 'm, 'tcx>)
65     {
66         let mut closure_dump = InnerDump {
67             tcx: self.tcx,
68             attr: None,
69             map: &mut *self.map,
70         };
71         for attr in attributes {
72             if attr.check_name("rustc_mir") {
73                 closure_dump.attr = Some(attr);
74             }
75         }
76         walk_op(&mut closure_dump);
77     }
78 }
79
80
81 impl<'a, 'tcx> visit::Visitor<'tcx> for OuterDump<'a, 'tcx> {
82     fn visit_item(&mut self, item: &'tcx hir::Item) {
83         self.visit_mir(&item.attrs, |c| visit::walk_item(c, item));
84         visit::walk_item(self, item);
85     }
86
87     fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
88         match trait_item.node {
89             hir::MethodTraitItem(_, Some(_)) => {
90                 self.visit_mir(&trait_item.attrs, |c| visit::walk_trait_item(c, trait_item));
91             }
92             hir::MethodTraitItem(_, None) |
93             hir::ConstTraitItem(..) |
94             hir::TypeTraitItem(..) => {}
95         }
96         visit::walk_trait_item(self, trait_item);
97     }
98
99     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
100         match impl_item.node {
101             hir::MethodImplItem(..) => {
102                 self.visit_mir(&impl_item.attrs, |c| visit::walk_impl_item(c, impl_item));
103             }
104             hir::ConstImplItem(..) | hir::TypeImplItem(..) => {}
105         }
106         visit::walk_impl_item(self, impl_item);
107     }
108 }
109
110 ///////////////////////////////////////////////////////////////////////////
111 // InnerDump -- dumps MIR for a single fn and its contained closures
112
113 struct InnerDump<'a, 'm, 'tcx: 'a + 'm> {
114     tcx: &'a ty::ctxt<'tcx>,
115     map: &'m mut MirMap<'tcx>,
116     attr: Option<&'a ast::Attribute>,
117 }
118
119 impl<'a, 'm, 'tcx> visit::Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
120     fn visit_item(&mut self, _: &'tcx hir::Item) {
121         // ignore nested items; they need their own graphviz annotation
122     }
123
124     fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {
125         // ignore nested items; they need their own graphviz annotation
126     }
127
128     fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {
129         // ignore nested items; they need their own graphviz annotation
130     }
131
132     fn visit_fn(&mut self,
133                 fk: visit::FnKind<'tcx>,
134                 decl: &'tcx hir::FnDecl,
135                 body: &'tcx hir::Block,
136                 span: Span,
137                 id: ast::NodeId) {
138         let (prefix, implicit_arg_tys) = match fk {
139             visit::FnKind::Closure =>
140                 (format!("{}-", id), vec![closure_self_ty(&self.tcx, id, body.id)]),
141             _ =>
142                 (format!(""), vec![]),
143         };
144
145         let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
146
147         let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env), true);
148
149         match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
150             Ok(mir) => {
151                 let meta_item_list = self.attr
152                                          .iter()
153                                          .flat_map(|a| a.meta_item_list())
154                                          .flat_map(|l| l.iter());
155                 for item in meta_item_list {
156                     if item.check_name("graphviz") {
157                         match item.value_str() {
158                             Some(s) => {
159                                 match
160                                     File::create(format!("{}{}", prefix, s))
161                                     .and_then(|ref mut output| dot::render(&mir, output))
162                                 {
163                                     Ok(()) => { }
164                                     Err(e) => {
165                                         self.tcx.sess.span_fatal(
166                                             item.span,
167                                             &format!("Error writing graphviz \
168                                                       results to `{}`: {}",
169                                                      s, e));
170                                     }
171                                 }
172                             }
173                             None => {
174                                 self.tcx.sess.span_err(
175                                     item.span,
176                                     "graphviz attribute requires a path");
177                             }
178                         }
179                     }
180                 }
181
182                 let previous = self.map.insert(id, mir);
183                 assert!(previous.is_none());
184             }
185             Err(ErrorReported) => {}
186         }
187
188         visit::walk_fn(self, fk, decl, body, span);
189     }
190 }
191
192 fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
193                          implicit_arg_tys: Vec<Ty<'tcx>>,
194                          fn_id: ast::NodeId,
195                          span: Span,
196                          decl: &'tcx hir::FnDecl,
197                          body: &'tcx hir::Block)
198                          -> Result<Mir<'tcx>, ErrorReported> {
199     // fetch the fully liberated fn signature (that is, all bound
200     // types/lifetimes replaced)
201     let fn_sig = match cx.tcx().tables.borrow().liberated_fn_sigs.get(&fn_id) {
202         Some(f) => f.clone(),
203         None => {
204             cx.tcx().sess.span_bug(span,
205                                    &format!("no liberated fn sig for {:?}", fn_id));
206         }
207     };
208
209     let arguments =
210         decl.inputs
211             .iter()
212             .enumerate()
213             .map(|(index, arg)| {
214                 (fn_sig.inputs[index], PatNode::irrefutable(&arg.pat))
215             })
216             .collect();
217
218     let parameter_scope =
219         cx.tcx().region_maps.lookup_code_extent(
220             CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id });
221     Ok(build::construct(cx,
222                         span,
223                         implicit_arg_tys,
224                         arguments,
225                         parameter_scope,
226                         fn_sig.output,
227                         body))
228 }
229
230 fn closure_self_ty<'a, 'tcx>(tcx: &ty::ctxt<'tcx>,
231                              closure_expr_id: ast::NodeId,
232                              body_id: ast::NodeId)
233                              -> Ty<'tcx> {
234     let closure_ty = tcx.node_id_to_type(closure_expr_id);
235
236     // We're just hard-coding the idea that the signature will be
237     // &self or &mut self and hence will have a bound region with
238     // number 0, hokey.
239     let region = ty::Region::ReFree(ty::FreeRegion {
240         scope: tcx.region_maps.item_extent(body_id),
241         bound_region: ty::BoundRegion::BrAnon(0),
242     });
243     let region = tcx.mk_region(region);
244
245     match tcx.closure_kind(tcx.map.local_def_id(closure_expr_id)) {
246         ty::ClosureKind::FnClosureKind =>
247             tcx.mk_ref(region,
248                        ty::TypeAndMut { ty: closure_ty,
249                                         mutbl: hir::MutImmutable }),
250         ty::ClosureKind::FnMutClosureKind =>
251             tcx.mk_ref(region,
252                        ty::TypeAndMut { ty: closure_ty,
253                                         mutbl: hir::MutMutable }),
254         ty::ClosureKind::FnOnceClosureKind =>
255             closure_ty
256     }
257 }