]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/mir_map.rs
Auto merge of #31077 - nagisa:mir-temp-promotion, r=dotdash
[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 graphviz;
25 use pretty;
26 use transform::*;
27 use rustc::dep_graph::DepNode;
28 use rustc::mir::repr::Mir;
29 use hair::cx::Cx;
30 use std::fs::File;
31
32 use self::rustc::middle::infer;
33 use self::rustc::middle::region::CodeExtentData;
34 use self::rustc::middle::ty::{self, Ty};
35 use self::rustc::util::common::ErrorReported;
36 use self::rustc::util::nodemap::NodeMap;
37 use self::rustc_front::hir;
38 use self::rustc_front::intravisit::{self, Visitor};
39 use self::syntax::ast;
40 use self::syntax::attr::AttrMetaMethods;
41 use self::syntax::codemap::Span;
42
43 pub type MirMap<'tcx> = NodeMap<Mir<'tcx>>;
44
45 pub fn build_mir_for_crate<'tcx>(tcx: &ty::ctxt<'tcx>) -> MirMap<'tcx> {
46     let mut map = NodeMap();
47     {
48         let mut dump = OuterDump {
49             tcx: tcx,
50             map: &mut map,
51         };
52         tcx.visit_all_items_in_krate(DepNode::MirMapConstruction, &mut dump);
53     }
54     map
55 }
56
57 ///////////////////////////////////////////////////////////////////////////
58 // OuterDump -- walks a crate, looking for fn items and methods to build MIR from
59
60 struct OuterDump<'a, 'tcx: 'a> {
61     tcx: &'a ty::ctxt<'tcx>,
62     map: &'a mut MirMap<'tcx>,
63 }
64
65 impl<'a, 'tcx> OuterDump<'a, 'tcx> {
66     fn visit_mir<OP>(&mut self, attributes: &'a [ast::Attribute], mut walk_op: OP)
67         where OP: for<'m> FnMut(&mut InnerDump<'a, 'm, 'tcx>)
68     {
69         let mut closure_dump = InnerDump {
70             tcx: self.tcx,
71             attr: None,
72             map: &mut *self.map,
73         };
74         for attr in attributes {
75             if attr.check_name("rustc_mir") {
76                 closure_dump.attr = Some(attr);
77             }
78         }
79         walk_op(&mut closure_dump);
80     }
81 }
82
83
84 impl<'a, 'tcx> Visitor<'tcx> for OuterDump<'a, 'tcx> {
85     fn visit_item(&mut self, item: &'tcx hir::Item) {
86         self.visit_mir(&item.attrs, |c| intravisit::walk_item(c, item));
87         intravisit::walk_item(self, item);
88     }
89
90     fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
91         match trait_item.node {
92             hir::MethodTraitItem(_, Some(_)) => {
93                 self.visit_mir(&trait_item.attrs, |c| intravisit::walk_trait_item(c, trait_item));
94             }
95             hir::MethodTraitItem(_, None) |
96             hir::ConstTraitItem(..) |
97             hir::TypeTraitItem(..) => {}
98         }
99         intravisit::walk_trait_item(self, trait_item);
100     }
101
102     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
103         match impl_item.node {
104             hir::ImplItemKind::Method(..) => {
105                 self.visit_mir(&impl_item.attrs, |c| intravisit::walk_impl_item(c, impl_item));
106             }
107             hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(..) => {}
108         }
109         intravisit::walk_impl_item(self, impl_item);
110     }
111 }
112
113 ///////////////////////////////////////////////////////////////////////////
114 // InnerDump -- dumps MIR for a single fn and its contained closures
115
116 struct InnerDump<'a, 'm, 'tcx: 'a + 'm> {
117     tcx: &'a ty::ctxt<'tcx>,
118     map: &'m mut MirMap<'tcx>,
119     attr: Option<&'a ast::Attribute>,
120 }
121
122 impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
123     fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {
124         // ignore methods; the outer dump will call us for them independently
125     }
126
127     fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {
128         // ignore methods; the outer dump will call us for them independently
129     }
130
131     fn visit_fn(&mut self,
132                 fk: intravisit::FnKind<'tcx>,
133                 decl: &'tcx hir::FnDecl,
134                 body: &'tcx hir::Block,
135                 span: Span,
136                 id: ast::NodeId) {
137         let (prefix, implicit_arg_tys) = match fk {
138             intravisit::FnKind::Closure =>
139                 (format!("{}-", id), vec![closure_self_ty(&self.tcx, id, body.id)]),
140             _ =>
141                 (format!(""), vec![]),
142         };
143
144         let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
145
146         let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
147
148         match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
149             Ok(mut mir) => {
150                 simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir);
151
152                 let meta_item_list = self.attr
153                                          .iter()
154                                          .flat_map(|a| a.meta_item_list())
155                                          .flat_map(|l| l.iter());
156                 for item in meta_item_list {
157                     if item.check_name("graphviz") || item.check_name("pretty") {
158                         match item.value_str() {
159                             Some(s) => {
160                                 let filename = format!("{}{}", prefix, s);
161                                 let result = File::create(&filename).and_then(|ref mut output| {
162                                     if item.check_name("graphviz") {
163                                         graphviz::write_mir_graphviz(&mir, output)
164                                     } else {
165                                         pretty::write_mir_pretty(&mir, output)
166                                     }
167                                 });
168
169                                 if let Err(e) = result {
170                                     self.tcx.sess.span_fatal(
171                                         item.span,
172                                         &format!("Error writing MIR {} results to `{}`: {}",
173                                                  item.name(), filename, e));
174                                 }
175                             }
176                             None => {
177                                 self.tcx.sess.span_err(
178                                     item.span,
179                                     &format!("{} attribute requires a path", item.name()));
180                             }
181                         }
182                     }
183                 }
184
185                 let previous = self.map.insert(id, mir);
186                 assert!(previous.is_none());
187             }
188             Err(ErrorReported) => {}
189         }
190
191         intravisit::walk_fn(self, fk, decl, body, span);
192     }
193 }
194
195 fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
196                          implicit_arg_tys: Vec<Ty<'tcx>>,
197                          fn_id: ast::NodeId,
198                          span: Span,
199                          decl: &'tcx hir::FnDecl,
200                          body: &'tcx hir::Block)
201                          -> Result<Mir<'tcx>, ErrorReported> {
202     // fetch the fully liberated fn signature (that is, all bound
203     // types/lifetimes replaced)
204     let fn_sig = match cx.tcx().tables.borrow().liberated_fn_sigs.get(&fn_id) {
205         Some(f) => f.clone(),
206         None => {
207             cx.tcx().sess.span_bug(span,
208                                    &format!("no liberated fn sig for {:?}", fn_id));
209         }
210     };
211
212     let arguments =
213         decl.inputs
214             .iter()
215             .enumerate()
216             .map(|(index, arg)| {
217                 (fn_sig.inputs[index], &*arg.pat)
218             })
219             .collect();
220
221     let parameter_scope =
222         cx.tcx().region_maps.lookup_code_extent(
223             CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id });
224     Ok(build::construct(cx,
225                         span,
226                         implicit_arg_tys,
227                         arguments,
228                         parameter_scope,
229                         fn_sig.output,
230                         body))
231 }
232
233 fn closure_self_ty<'a, 'tcx>(tcx: &ty::ctxt<'tcx>,
234                              closure_expr_id: ast::NodeId,
235                              body_id: ast::NodeId)
236                              -> Ty<'tcx> {
237     let closure_ty = tcx.node_id_to_type(closure_expr_id);
238
239     // We're just hard-coding the idea that the signature will be
240     // &self or &mut self and hence will have a bound region with
241     // number 0, hokey.
242     let region = ty::Region::ReFree(ty::FreeRegion {
243         scope: tcx.region_maps.item_extent(body_id),
244         bound_region: ty::BoundRegion::BrAnon(0),
245     });
246     let region = tcx.mk_region(region);
247
248     match tcx.closure_kind(tcx.map.local_def_id(closure_expr_id)) {
249         ty::ClosureKind::FnClosureKind =>
250             tcx.mk_ref(region,
251                        ty::TypeAndMut { ty: closure_ty,
252                                         mutbl: hir::MutImmutable }),
253         ty::ClosureKind::FnMutClosureKind =>
254             tcx.mk_ref(region,
255                        ty::TypeAndMut { ty: closure_ty,
256                                         mutbl: hir::MutMutable }),
257         ty::ClosureKind::FnOnceClosureKind =>
258             closure_ty
259     }
260 }