]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/pretty.rs
Auto merge of #47353 - nikomatsakis:nll-issue-47189, r=pnkfelix+nmatsakis
[rust.git] / src / librustc_mir / util / pretty.rs
1 // Copyright 2014 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 use rustc::hir;
12 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
13 use rustc::mir::*;
14 use rustc::mir::visit::Visitor;
15 use rustc::ty::{self, TyCtxt};
16 use rustc::ty::item_path;
17 use rustc_data_structures::fx::FxHashMap;
18 use rustc_data_structures::indexed_vec::Idx;
19 use std::fmt::Display;
20 use std::fs;
21 use std::io::{self, Write};
22 use std::path::{Path, PathBuf};
23 use super::graphviz::write_mir_fn_graphviz;
24 use transform::MirSource;
25
26 const INDENT: &'static str = "    ";
27 /// Alignment for lining up comments following MIR statements
28 pub(crate) const ALIGN: usize = 40;
29
30 /// An indication of where we are in the control flow graph. Used for printing
31 /// extra information in `dump_mir`
32 pub enum PassWhere {
33     /// We have not started dumping the control flow graph, but we are about to.
34     BeforeCFG,
35
36     /// We just finished dumping the control flow graph. This is right before EOF
37     AfterCFG,
38
39     /// We are about to start dumping the given basic block.
40     BeforeBlock(BasicBlock),
41
42     /// We are just about to dump the given statement or terminator.
43     BeforeLocation(Location),
44
45     /// We just dumped the given statement or terminator.
46     AfterLocation(Location),
47 }
48
49 /// If the session is properly configured, dumps a human-readable
50 /// representation of the mir into:
51 ///
52 /// ```text
53 /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
54 /// ```
55 ///
56 /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
57 /// where `<filter>` takes the following forms:
58 ///
59 /// - `all` -- dump MIR for all fns, all passes, all everything
60 /// - a filter defined by a set of substrings combined with `&` and `|`
61 ///   (`&` has higher precedence). At least one of the `|`-separated groups
62 ///   must match; an `|`-separated group matches if all of its `&`-separated
63 ///   substrings are matched.
64 ///
65 /// Example:
66 ///
67 /// - `nll` == match if `nll` appears in the name
68 /// - `foo & nll` == match if `foo` and `nll` both appear in the name
69 /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
70 ///   or `typeck` appears in the name.
71 /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
72 ///   or `typeck` and `bar` both appear in the name.
73 pub fn dump_mir<'a, 'gcx, 'tcx, F>(
74     tcx: TyCtxt<'a, 'gcx, 'tcx>,
75     pass_num: Option<&Display>,
76     pass_name: &str,
77     disambiguator: &Display,
78     source: MirSource,
79     mir: &Mir<'tcx>,
80     extra_data: F,
81 ) where
82     F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
83 {
84     if !dump_enabled(tcx, pass_name, source) {
85         return;
86     }
87
88     let node_path = item_path::with_forced_impl_filename_line(|| {
89         // see notes on #41697 below
90         tcx.item_path_str(source.def_id)
91     });
92     dump_matched_mir_node(
93         tcx,
94         pass_num,
95         pass_name,
96         &node_path,
97         disambiguator,
98         source,
99         mir,
100         extra_data,
101     );
102 }
103
104 pub fn dump_enabled<'a, 'gcx, 'tcx>(
105     tcx: TyCtxt<'a, 'gcx, 'tcx>,
106     pass_name: &str,
107     source: MirSource,
108 ) -> bool {
109     let filters = match tcx.sess.opts.debugging_opts.dump_mir {
110         None => return false,
111         Some(ref filters) => filters,
112     };
113     let node_path = item_path::with_forced_impl_filename_line(|| {
114         // see notes on #41697 below
115         tcx.item_path_str(source.def_id)
116     });
117     filters.split("|").any(|or_filter| {
118         or_filter.split("&").all(|and_filter| {
119             and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter)
120         })
121     })
122 }
123
124 // #41697 -- we use `with_forced_impl_filename_line()` because
125 // `item_path_str()` would otherwise trigger `type_of`, and this can
126 // run while we are already attempting to evaluate `type_of`.
127
128 fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(
129     tcx: TyCtxt<'a, 'gcx, 'tcx>,
130     pass_num: Option<&Display>,
131     pass_name: &str,
132     node_path: &str,
133     disambiguator: &Display,
134     source: MirSource,
135     mir: &Mir<'tcx>,
136     mut extra_data: F,
137 ) where
138     F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
139 {
140     let _: io::Result<()> = do catch {
141         let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?;
142         writeln!(file, "// MIR for `{}`", node_path)?;
143         writeln!(file, "// source = {:?}", source)?;
144         writeln!(file, "// pass_name = {}", pass_name)?;
145         writeln!(file, "// disambiguator = {}", disambiguator)?;
146         if let Some(ref layout) = mir.generator_layout {
147             writeln!(file, "// generator_layout = {:?}", layout)?;
148         }
149         writeln!(file, "")?;
150         extra_data(PassWhere::BeforeCFG, &mut file)?;
151         write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?;
152         extra_data(PassWhere::AfterCFG, &mut file)?;
153         Ok(())
154     };
155
156     if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
157         let _: io::Result<()> = do catch {
158             let mut file =
159                 create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?;
160             write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?;
161             Ok(())
162         };
163     }
164 }
165
166 /// Returns the path to the filename where we should dump a given MIR.
167 /// Also used by other bits of code (e.g., NLL inference) that dump
168 /// graphviz data or other things.
169 fn dump_path(
170     tcx: TyCtxt<'_, '_, '_>,
171     extension: &str,
172     pass_num: Option<&Display>,
173     pass_name: &str,
174     disambiguator: &Display,
175     source: MirSource,
176 ) -> PathBuf {
177     let promotion_id = match source.promoted {
178         Some(id) => format!("-{:?}", id),
179         None => String::new(),
180     };
181
182     let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
183         format!("")
184     } else {
185         match pass_num {
186             None => format!(".-------"),
187             Some(pass_num) => format!(".{}", pass_num),
188         }
189     };
190
191     let mut file_path = PathBuf::new();
192     file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir));
193
194     let item_name = tcx.hir
195         .def_path(source.def_id)
196         .to_filename_friendly_no_crate();
197
198     let file_name = format!(
199         "rustc.{}{}{}.{}.{}.{}",
200         item_name,
201         promotion_id,
202         pass_num,
203         pass_name,
204         disambiguator,
205         extension,
206     );
207
208     file_path.push(&file_name);
209
210     file_path
211 }
212
213 /// Attempts to open a file where we should dump a given MIR or other
214 /// bit of MIR-related data. Used by `mir-dump`, but also by other
215 /// bits of code (e.g., NLL inference) that dump graphviz data or
216 /// other things, and hence takes the extension as an argument.
217 pub(crate) fn create_dump_file(
218     tcx: TyCtxt<'_, '_, '_>,
219     extension: &str,
220     pass_num: Option<&Display>,
221     pass_name: &str,
222     disambiguator: &Display,
223     source: MirSource,
224 ) -> io::Result<fs::File> {
225     let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source);
226     if let Some(parent) = file_path.parent() {
227         fs::create_dir_all(parent)?;
228     }
229     fs::File::create(&file_path)
230 }
231
232 /// Write out a human-readable textual representation for the given MIR.
233 pub fn write_mir_pretty<'a, 'gcx, 'tcx>(
234     tcx: TyCtxt<'a, 'gcx, 'tcx>,
235     single: Option<DefId>,
236     w: &mut Write,
237 ) -> io::Result<()> {
238     writeln!(
239         w,
240         "// WARNING: This output format is intended for human consumers only"
241     )?;
242     writeln!(
243         w,
244         "// and is subject to change without notice. Knock yourself out."
245     )?;
246
247     let mut first = true;
248     for def_id in dump_mir_def_ids(tcx, single) {
249         let mir = &tcx.optimized_mir(def_id);
250
251         if first {
252             first = false;
253         } else {
254             // Put empty lines between all items
255             writeln!(w, "")?;
256         }
257
258         write_mir_fn(tcx, MirSource::item(def_id), mir, &mut |_, _| Ok(()), w)?;
259
260         for (i, mir) in mir.promoted.iter_enumerated() {
261             writeln!(w, "")?;
262             let src = MirSource {
263                 def_id,
264                 promoted: Some(i),
265             };
266             write_mir_fn(tcx, src, mir, &mut |_, _| Ok(()), w)?;
267         }
268     }
269     Ok(())
270 }
271
272 pub fn write_mir_fn<'a, 'gcx, 'tcx, F>(
273     tcx: TyCtxt<'a, 'gcx, 'tcx>,
274     src: MirSource,
275     mir: &Mir<'tcx>,
276     extra_data: &mut F,
277     w: &mut Write,
278 ) -> io::Result<()>
279 where
280     F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
281 {
282     write_mir_intro(tcx, src, mir, w)?;
283     for block in mir.basic_blocks().indices() {
284         extra_data(PassWhere::BeforeBlock(block), w)?;
285         write_basic_block(tcx, block, mir, extra_data, w)?;
286         if block.index() + 1 != mir.basic_blocks().len() {
287             writeln!(w, "")?;
288         }
289     }
290
291     writeln!(w, "}}")?;
292     Ok(())
293 }
294
295 /// Write out a human-readable textual representation for the given basic block.
296 pub fn write_basic_block<'cx, 'gcx, 'tcx, F>(
297     tcx: TyCtxt<'cx, 'gcx, 'tcx>,
298     block: BasicBlock,
299     mir: &Mir<'tcx>,
300     extra_data: &mut F,
301     w: &mut Write,
302 ) -> io::Result<()>
303 where
304     F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
305 {
306     let data = &mir[block];
307
308     // Basic block label at the top.
309     let cleanup_text = if data.is_cleanup { " // cleanup" } else { "" };
310     let lbl = format!("{}{:?}: {{", INDENT, block);
311     writeln!(w, "{0:1$}{2}", lbl, ALIGN, cleanup_text)?;
312
313     // List of statements in the middle.
314     let mut current_location = Location {
315         block: block,
316         statement_index: 0,
317     };
318     for statement in &data.statements {
319         extra_data(PassWhere::BeforeLocation(current_location), w)?;
320         let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
321         writeln!(
322             w,
323             "{:A$} // {:?}: {}",
324             indented_mir,
325             current_location,
326             comment(tcx, statement.source_info),
327             A = ALIGN,
328         )?;
329
330         write_extra(tcx, w, |visitor| {
331             visitor.visit_statement(current_location.block, statement, current_location);
332         })?;
333
334         extra_data(PassWhere::AfterLocation(current_location), w)?;
335
336         current_location.statement_index += 1;
337     }
338
339     // Terminator at the bottom.
340     extra_data(PassWhere::BeforeLocation(current_location), w)?;
341     let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
342     writeln!(
343         w,
344         "{:A$} // {:?}: {}",
345         indented_terminator,
346         current_location,
347         comment(tcx, data.terminator().source_info),
348         A = ALIGN,
349     )?;
350
351     write_extra(tcx, w, |visitor| {
352         visitor.visit_terminator(current_location.block, data.terminator(), current_location);
353     })?;
354
355     extra_data(PassWhere::AfterLocation(current_location), w)?;
356
357     writeln!(w, "{}}}", INDENT)
358 }
359
360 /// After we print the main statement, we sometimes dump extra
361 /// information. There's often a lot of little things "nuzzled up" in
362 /// a statement.
363 fn write_extra<'cx, 'gcx, 'tcx, F>(
364     tcx: TyCtxt<'cx, 'gcx, 'tcx>,
365     write: &mut Write,
366     mut visit_op: F,
367 ) -> io::Result<()>
368 where
369     F: FnMut(&mut ExtraComments<'cx, 'gcx, 'tcx>),
370 {
371     let mut extra_comments = ExtraComments {
372         _tcx: tcx,
373         comments: vec![],
374     };
375     visit_op(&mut extra_comments);
376     for comment in extra_comments.comments {
377         writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
378     }
379     Ok(())
380 }
381
382 struct ExtraComments<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
383     _tcx: TyCtxt<'cx, 'gcx, 'tcx>, // don't need it now, but bet we will soon
384     comments: Vec<String>,
385 }
386
387 impl<'cx, 'gcx, 'tcx> ExtraComments<'cx, 'gcx, 'tcx> {
388     fn push(&mut self, lines: &str) {
389         for line in lines.split("\n") {
390             self.comments.push(line.to_string());
391         }
392     }
393 }
394
395 impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ExtraComments<'cx, 'gcx, 'tcx> {
396     fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
397         self.super_constant(constant, location);
398         let Constant { span, ty, literal } = constant;
399         self.push(&format!("mir::Constant"));
400         self.push(&format!("└ span: {:?}", span));
401         self.push(&format!("└ ty: {:?}", ty));
402         self.push(&format!("└ literal: {:?}", literal));
403     }
404
405     fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
406         self.super_const(constant);
407         let ty::Const { ty, val } = constant;
408         self.push(&format!("ty::Const"));
409         self.push(&format!("└ ty: {:?}", ty));
410         self.push(&format!("└ val: {:?}", val));
411     }
412
413     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
414         self.super_rvalue(rvalue, location);
415         match rvalue {
416             Rvalue::Aggregate(kind, _) => match **kind {
417                 AggregateKind::Closure(def_id, substs) => {
418                     self.push(&format!("closure"));
419                     self.push(&format!("└ def_id: {:?}", def_id));
420                     self.push(&format!("└ substs: {:#?}", substs));
421                 }
422
423                 AggregateKind::Generator(def_id, substs, interior) => {
424                     self.push(&format!("generator"));
425                     self.push(&format!("└ def_id: {:?}", def_id));
426                     self.push(&format!("└ substs: {:#?}", substs));
427                     self.push(&format!("└ interior: {:?}", interior));
428                 }
429
430                 _ => {}
431             },
432
433             _ => {}
434         }
435     }
436 }
437
438 fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
439     format!(
440         "scope {} at {}",
441         scope.index(),
442         tcx.sess.codemap().span_to_string(span)
443     )
444 }
445
446 /// Prints user-defined variables in a scope tree.
447 ///
448 /// Returns the total number of variables printed.
449 fn write_scope_tree(
450     tcx: TyCtxt,
451     mir: &Mir,
452     scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
453     w: &mut Write,
454     parent: VisibilityScope,
455     depth: usize,
456 ) -> io::Result<()> {
457     let indent = depth * INDENT.len();
458
459     let children = match scope_tree.get(&parent) {
460         Some(childs) => childs,
461         None => return Ok(()),
462     };
463
464     for &child in children {
465         let data = &mir.visibility_scopes[child];
466         assert_eq!(data.parent_scope, Some(parent));
467         writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
468
469         // User variable types (including the user's name in a comment).
470         for local in mir.vars_iter() {
471             let var = &mir.local_decls[local];
472             let (name, source_info) = if var.source_info.scope == child {
473                 (var.name.unwrap(), var.source_info)
474             } else {
475                 // Not a variable or not declared in this scope.
476                 continue;
477             };
478
479             let mut_str = if var.mutability == Mutability::Mut {
480                 "mut "
481             } else {
482                 ""
483             };
484
485             let indent = indent + INDENT.len();
486             let indented_var = format!(
487                 "{0:1$}let {2}{3:?}: {4:?};",
488                 INDENT,
489                 indent,
490                 mut_str,
491                 local,
492                 var.ty
493             );
494             writeln!(
495                 w,
496                 "{0:1$} // \"{2}\" in {3}",
497                 indented_var,
498                 ALIGN,
499                 name,
500                 comment(tcx, source_info)
501             )?;
502         }
503
504         write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
505
506         writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
507     }
508
509     Ok(())
510 }
511
512 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
513 /// local variables (both user-defined bindings and compiler temporaries).
514 pub fn write_mir_intro<'a, 'gcx, 'tcx>(
515     tcx: TyCtxt<'a, 'gcx, 'tcx>,
516     src: MirSource,
517     mir: &Mir,
518     w: &mut Write,
519 ) -> io::Result<()> {
520     write_mir_sig(tcx, src, mir, w)?;
521     writeln!(w, "{{")?;
522
523     // construct a scope tree and write it out
524     let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
525     for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
526         if let Some(parent) = scope_data.parent_scope {
527             scope_tree
528                 .entry(parent)
529                 .or_insert(vec![])
530                 .push(VisibilityScope::new(index));
531         } else {
532             // Only the argument scope has no parent, because it's the root.
533             assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
534         }
535     }
536
537     // Print return place
538     let indented_retptr = format!("{}let mut {:?}: {};",
539                                   INDENT,
540                                   RETURN_PLACE,
541                                   mir.local_decls[RETURN_PLACE].ty);
542     writeln!(w, "{0:1$} // return place",
543              indented_retptr,
544              ALIGN)?;
545
546     write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
547
548     write_temp_decls(mir, w)?;
549
550     // Add an empty line before the first block is printed.
551     writeln!(w, "")?;
552
553     Ok(())
554 }
555
556 fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) -> io::Result<()> {
557     let id = tcx.hir.as_local_node_id(src.def_id).unwrap();
558     let body_owner_kind = tcx.hir.body_owner_kind(id);
559     match (body_owner_kind, src.promoted) {
560         (_, Some(i)) => write!(w, "{:?} in", i)?,
561         (hir::BodyOwnerKind::Fn, _) => write!(w, "fn")?,
562         (hir::BodyOwnerKind::Const, _) => write!(w, "const")?,
563         (hir::BodyOwnerKind::Static(hir::MutImmutable), _) => write!(w, "static")?,
564         (hir::BodyOwnerKind::Static(hir::MutMutable), _) => write!(w, "static mut")?,
565     }
566
567     item_path::with_forced_impl_filename_line(|| {
568         // see notes on #41697 elsewhere
569         write!(w, " {}", tcx.item_path_str(src.def_id))
570     })?;
571
572     match (body_owner_kind, src.promoted) {
573         (hir::BodyOwnerKind::Fn, None) => {
574             write!(w, "(")?;
575
576             // fn argument types.
577             for (i, arg) in mir.args_iter().enumerate() {
578                 if i != 0 {
579                     write!(w, ", ")?;
580                 }
581                 write!(w, "{:?}: {}", Place::Local(arg), mir.local_decls[arg].ty)?;
582             }
583
584             write!(w, ") -> {}", mir.return_ty())?;
585         }
586         (hir::BodyOwnerKind::Const, _) | (hir::BodyOwnerKind::Static(_), _) | (_, Some(_)) => {
587             assert_eq!(mir.arg_count, 0);
588             write!(w, ": {} =", mir.return_ty())?;
589         }
590     }
591
592     if let Some(yield_ty) = mir.yield_ty {
593         writeln!(w)?;
594         writeln!(w, "yields {}", yield_ty)?;
595     }
596
597     Ok(())
598 }
599
600 fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
601     // Compiler-introduced temporary types.
602     for temp in mir.temps_iter() {
603         writeln!(
604             w,
605             "{}let mut {:?}: {};",
606             INDENT,
607             temp,
608             mir.local_decls[temp].ty
609         )?;
610     }
611
612     Ok(())
613 }
614
615 pub fn dump_mir_def_ids(tcx: TyCtxt, single: Option<DefId>) -> Vec<DefId> {
616     if let Some(i) = single {
617         vec![i]
618     } else {
619         tcx.mir_keys(LOCAL_CRATE).iter().cloned().collect()
620     }
621 }