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