]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/pretty.rs
improve naming
[rust.git] / src / librustc_mir / util / pretty.rs
1 use std::collections::BTreeSet;
2 use std::fmt::Write as _;
3 use std::fmt::{Debug, Display};
4 use std::fs;
5 use std::io::{self, Write};
6 use std::path::{Path, PathBuf};
7
8 use super::graphviz::write_mir_fn_graphviz;
9 use crate::transform::MirSource;
10 use either::Either;
11 use rustc_data_structures::fx::FxHashMap;
12 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
13 use rustc_index::vec::Idx;
14 use rustc_middle::mir::interpret::{
15     read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer,
16 };
17 use rustc_middle::mir::visit::Visitor;
18 use rustc_middle::mir::*;
19 use rustc_middle::ty::{self, TyCtxt, TypeFoldable, TypeVisitor};
20 use rustc_target::abi::Size;
21
22 const INDENT: &str = "    ";
23 /// Alignment for lining up comments following MIR statements
24 pub(crate) const ALIGN: usize = 40;
25
26 /// An indication of where we are in the control flow graph. Used for printing
27 /// extra information in `dump_mir`
28 pub enum PassWhere {
29     /// We have not started dumping the control flow graph, but we are about to.
30     BeforeCFG,
31
32     /// We just finished dumping the control flow graph. This is right before EOF
33     AfterCFG,
34
35     /// We are about to start dumping the given basic block.
36     BeforeBlock(BasicBlock),
37
38     /// We are just about to dump the given statement or terminator.
39     BeforeLocation(Location),
40
41     /// We just dumped the given statement or terminator.
42     AfterLocation(Location),
43
44     /// We just dumped the terminator for a block but not the closing `}`.
45     AfterTerminator(BasicBlock),
46 }
47
48 /// If the session is properly configured, dumps a human-readable
49 /// representation of the mir into:
50 ///
51 /// ```text
52 /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
53 /// ```
54 ///
55 /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
56 /// where `<filter>` takes the following forms:
57 ///
58 /// - `all` -- dump MIR for all fns, all passes, all everything
59 /// - a filter defined by a set of substrings combined with `&` and `|`
60 ///   (`&` has higher precedence). At least one of the `|`-separated groups
61 ///   must match; an `|`-separated group matches if all of its `&`-separated
62 ///   substrings are matched.
63 ///
64 /// Example:
65 ///
66 /// - `nll` == match if `nll` appears in the name
67 /// - `foo & nll` == match if `foo` and `nll` both appear in the name
68 /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
69 ///   or `typeck` appears in the name.
70 /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
71 ///   or `typeck` and `bar` both appear in the name.
72 pub fn dump_mir<'tcx, F>(
73     tcx: TyCtxt<'tcx>,
74     pass_num: Option<&dyn Display>,
75     pass_name: &str,
76     disambiguator: &dyn Display,
77     source: MirSource<'tcx>,
78     body: &Body<'tcx>,
79     extra_data: F,
80 ) where
81     F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
82 {
83     if !dump_enabled(tcx, pass_name, source.def_id()) {
84         return;
85     }
86
87     dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, source, body, extra_data);
88 }
89
90 pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool {
91     let filters = match tcx.sess.opts.debugging_opts.dump_mir {
92         None => return false,
93         Some(ref filters) => filters,
94     };
95     let node_path = ty::print::with_forced_impl_filename_line(|| {
96         // see notes on #41697 below
97         tcx.def_path_str(def_id)
98     });
99     filters.split('|').any(|or_filter| {
100         or_filter.split('&').all(|and_filter| {
101             and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter)
102         })
103     })
104 }
105
106 // #41697 -- we use `with_forced_impl_filename_line()` because
107 // `def_path_str()` would otherwise trigger `type_of`, and this can
108 // run while we are already attempting to evaluate `type_of`.
109
110 fn dump_matched_mir_node<'tcx, F>(
111     tcx: TyCtxt<'tcx>,
112     pass_num: Option<&dyn Display>,
113     pass_name: &str,
114     disambiguator: &dyn Display,
115     source: MirSource<'tcx>,
116     body: &Body<'tcx>,
117     mut extra_data: F,
118 ) where
119     F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
120 {
121     let _: io::Result<()> = try {
122         let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?;
123         let def_path = ty::print::with_forced_impl_filename_line(|| {
124             // see notes on #41697 above
125             tcx.def_path_str(source.def_id())
126         });
127         write!(file, "// MIR for `{}", def_path)?;
128         match source.promoted {
129             None => write!(file, "`")?,
130             Some(promoted) => write!(file, "::{:?}`", promoted)?,
131         }
132         writeln!(file, " {} {}", disambiguator, pass_name)?;
133         if let Some(ref layout) = body.generator_layout {
134             writeln!(file, "/* generator_layout = {:#?} */", layout)?;
135         }
136         writeln!(file)?;
137         extra_data(PassWhere::BeforeCFG, &mut file)?;
138         write_user_type_annotations(tcx, body, &mut file)?;
139         write_mir_fn(tcx, source, body, &mut extra_data, &mut file)?;
140         extra_data(PassWhere::AfterCFG, &mut file)?;
141     };
142
143     if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
144         let _: io::Result<()> = try {
145             let mut file =
146                 create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?;
147             write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?;
148         };
149     }
150 }
151
152 /// Returns the path to the filename where we should dump a given MIR.
153 /// Also used by other bits of code (e.g., NLL inference) that dump
154 /// graphviz data or other things.
155 fn dump_path(
156     tcx: TyCtxt<'_>,
157     extension: &str,
158     pass_num: Option<&dyn Display>,
159     pass_name: &str,
160     disambiguator: &dyn Display,
161     source: MirSource<'tcx>,
162 ) -> PathBuf {
163     let promotion_id = match source.promoted {
164         Some(id) => format!("-{:?}", id),
165         None => String::new(),
166     };
167
168     let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
169         String::new()
170     } else {
171         match pass_num {
172             None => ".-------".to_string(),
173             Some(pass_num) => format!(".{}", pass_num),
174         }
175     };
176
177     let mut file_path = PathBuf::new();
178     file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir));
179
180     let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
181     // All drop shims have the same DefId, so we have to add the type
182     // to get unique file names.
183     let shim_disambiguator = match source.instance {
184         ty::InstanceDef::DropGlue(_, Some(ty)) => {
185             // Unfortunately, pretty-printed typed are not very filename-friendly.
186             // We dome some filtering.
187             let mut s = ".".to_owned();
188             s.extend(ty.to_string().chars().filter_map(|c| match c {
189                 ' ' => None,
190                 ':' | '<' | '>' => Some('_'),
191                 c => Some(c),
192             }));
193             s
194         }
195         _ => String::new(),
196     };
197
198     let file_name = format!(
199         "rustc.{}{}{}{}.{}.{}.{}",
200         item_name, shim_disambiguator, promotion_id, pass_num, pass_name, disambiguator, extension,
201     );
202
203     file_path.push(&file_name);
204
205     file_path
206 }
207
208 /// Attempts to open a file where we should dump a given MIR or other
209 /// bit of MIR-related data. Used by `mir-dump`, but also by other
210 /// bits of code (e.g., NLL inference) that dump graphviz data or
211 /// other things, and hence takes the extension as an argument.
212 pub(crate) fn create_dump_file(
213     tcx: TyCtxt<'_>,
214     extension: &str,
215     pass_num: Option<&dyn Display>,
216     pass_name: &str,
217     disambiguator: &dyn Display,
218     source: MirSource<'tcx>,
219 ) -> io::Result<io::BufWriter<fs::File>> {
220     let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source);
221     if let Some(parent) = file_path.parent() {
222         fs::create_dir_all(parent)?;
223     }
224     Ok(io::BufWriter::new(fs::File::create(&file_path)?))
225 }
226
227 /// Write out a human-readable textual representation for the given MIR.
228 pub fn write_mir_pretty<'tcx>(
229     tcx: TyCtxt<'tcx>,
230     single: Option<DefId>,
231     w: &mut dyn Write,
232 ) -> io::Result<()> {
233     writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
234     writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
235
236     let mut first = true;
237     for def_id in dump_mir_def_ids(tcx, single) {
238         let body = &tcx.optimized_mir(def_id);
239
240         if first {
241             first = false;
242         } else {
243             // Put empty lines between all items
244             writeln!(w)?;
245         }
246
247         write_mir_fn(tcx, MirSource::item(def_id), body, &mut |_, _| Ok(()), w)?;
248
249         for (i, body) in tcx.promoted_mir(def_id).iter_enumerated() {
250             writeln!(w)?;
251             let src = MirSource {
252                 instance: ty::InstanceDef::Item(ty::WithOptConstParam::dummy(def_id)),
253                 promoted: Some(i),
254             };
255             write_mir_fn(tcx, src, body, &mut |_, _| Ok(()), w)?;
256         }
257     }
258     Ok(())
259 }
260
261 /// Write out a human-readable textual representation for the given function.
262 pub fn write_mir_fn<'tcx, F>(
263     tcx: TyCtxt<'tcx>,
264     src: MirSource<'tcx>,
265     body: &Body<'tcx>,
266     extra_data: &mut F,
267     w: &mut dyn Write,
268 ) -> io::Result<()>
269 where
270     F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
271 {
272     write_mir_intro(tcx, src, body, w)?;
273     for block in body.basic_blocks().indices() {
274         extra_data(PassWhere::BeforeBlock(block), w)?;
275         write_basic_block(tcx, block, body, extra_data, w)?;
276         if block.index() + 1 != body.basic_blocks().len() {
277             writeln!(w)?;
278         }
279     }
280
281     writeln!(w, "}}")?;
282
283     write_allocations(tcx, body, w)?;
284
285     Ok(())
286 }
287
288 /// Write out a human-readable textual representation for the given basic block.
289 pub fn write_basic_block<'tcx, F>(
290     tcx: TyCtxt<'tcx>,
291     block: BasicBlock,
292     body: &Body<'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 = &body[block];
300
301     // Basic block label at the top.
302     let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
303     writeln!(w, "{}{:?}{}: {{", INDENT, block, cleanup_text)?;
304
305     // List of statements in the middle.
306     let mut current_location = Location { block, statement_index: 0 };
307     for statement in &data.statements {
308         extra_data(PassWhere::BeforeLocation(current_location), w)?;
309         let indented_body = format!("{0}{0}{1:?};", INDENT, statement);
310         writeln!(
311             w,
312             "{:A$} // {}{}",
313             indented_body,
314             if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
315             comment(tcx, statement.source_info),
316             A = ALIGN,
317         )?;
318
319         write_extra(tcx, w, |visitor| {
320             visitor.visit_statement(statement, current_location);
321         })?;
322
323         extra_data(PassWhere::AfterLocation(current_location), w)?;
324
325         current_location.statement_index += 1;
326     }
327
328     // Terminator at the bottom.
329     extra_data(PassWhere::BeforeLocation(current_location), w)?;
330     let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
331     writeln!(
332         w,
333         "{:A$} // {}{}",
334         indented_terminator,
335         if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
336         comment(tcx, data.terminator().source_info),
337         A = ALIGN,
338     )?;
339
340     write_extra(tcx, w, |visitor| {
341         visitor.visit_terminator(data.terminator(), current_location);
342     })?;
343
344     extra_data(PassWhere::AfterLocation(current_location), w)?;
345     extra_data(PassWhere::AfterTerminator(block), w)?;
346
347     writeln!(w, "{}}}", INDENT)
348 }
349
350 /// After we print the main statement, we sometimes dump extra
351 /// information. There's often a lot of little things "nuzzled up" in
352 /// a statement.
353 fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()>
354 where
355     F: FnMut(&mut ExtraComments<'tcx>),
356 {
357     let mut extra_comments = ExtraComments { tcx, comments: vec![] };
358     visit_op(&mut extra_comments);
359     for comment in extra_comments.comments {
360         writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
361     }
362     Ok(())
363 }
364
365 struct ExtraComments<'tcx> {
366     tcx: TyCtxt<'tcx>,
367     comments: Vec<String>,
368 }
369
370 impl ExtraComments<'tcx> {
371     fn push(&mut self, lines: &str) {
372         for line in lines.split('\n') {
373             self.comments.push(line.to_string());
374         }
375     }
376 }
377
378 impl Visitor<'tcx> for ExtraComments<'tcx> {
379     fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
380         self.super_constant(constant, location);
381         let Constant { span, user_ty, literal } = constant;
382         self.push("mir::Constant");
383         self.push(&format!("+ span: {}", self.tcx.sess.source_map().span_to_string(*span)));
384         if let Some(user_ty) = user_ty {
385             self.push(&format!("+ user_ty: {:?}", user_ty));
386         }
387         self.push(&format!("+ literal: {:?}", literal));
388     }
389
390     fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
391         self.super_const(constant);
392         let ty::Const { ty, val, .. } = constant;
393         self.push("ty::Const");
394         self.push(&format!("+ ty: {:?}", ty));
395         self.push(&format!("+ val: {:?}", val));
396     }
397
398     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
399         self.super_rvalue(rvalue, location);
400         if let Rvalue::Aggregate(kind, _) = rvalue {
401             match **kind {
402                 AggregateKind::Closure(def_id, substs) => {
403                     self.push("closure");
404                     self.push(&format!("+ def_id: {:?}", def_id));
405                     self.push(&format!("+ substs: {:#?}", substs));
406                 }
407
408                 AggregateKind::Generator(def_id, substs, movability) => {
409                     self.push("generator");
410                     self.push(&format!("+ def_id: {:?}", def_id));
411                     self.push(&format!("+ substs: {:#?}", substs));
412                     self.push(&format!("+ movability: {:?}", movability));
413                 }
414
415                 AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
416                     self.push("adt");
417                     self.push(&format!("+ user_ty: {:?}", user_ty));
418                 }
419
420                 _ => {}
421             }
422         }
423     }
424 }
425
426 fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
427     format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_string(span))
428 }
429
430 /// Prints local variables in a scope tree.
431 fn write_scope_tree(
432     tcx: TyCtxt<'_>,
433     body: &Body<'_>,
434     scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
435     w: &mut dyn Write,
436     parent: SourceScope,
437     depth: usize,
438 ) -> io::Result<()> {
439     let indent = depth * INDENT.len();
440
441     // Local variable debuginfo.
442     for var_debug_info in &body.var_debug_info {
443         if var_debug_info.source_info.scope != parent {
444             // Not declared in this scope.
445             continue;
446         }
447
448         let indented_debug_info = format!(
449             "{0:1$}debug {2} => {3:?};",
450             INDENT, indent, var_debug_info.name, var_debug_info.place,
451         );
452
453         writeln!(
454             w,
455             "{0:1$} // in {2}",
456             indented_debug_info,
457             ALIGN,
458             comment(tcx, var_debug_info.source_info),
459         )?;
460     }
461
462     // Local variable types.
463     for (local, local_decl) in body.local_decls.iter_enumerated() {
464         if (1..body.arg_count + 1).contains(&local.index()) {
465             // Skip over argument locals, they're printed in the signature.
466             continue;
467         }
468
469         if local_decl.source_info.scope != parent {
470             // Not declared in this scope.
471             continue;
472         }
473
474         let mut_str = if local_decl.mutability == Mutability::Mut { "mut " } else { "" };
475
476         let mut indented_decl =
477             format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty);
478         if let Some(user_ty) = &local_decl.user_ty {
479             for user_ty in user_ty.projections() {
480                 write!(indented_decl, " as {:?}", user_ty).unwrap();
481             }
482         }
483         indented_decl.push_str(";");
484
485         let local_name =
486             if local == RETURN_PLACE { " return place".to_string() } else { String::new() };
487
488         writeln!(
489             w,
490             "{0:1$} //{2} in {3}",
491             indented_decl,
492             ALIGN,
493             local_name,
494             comment(tcx, local_decl.source_info),
495         )?;
496     }
497
498     let children = match scope_tree.get(&parent) {
499         Some(children) => children,
500         None => return Ok(()),
501     };
502
503     for &child in children {
504         assert_eq!(body.source_scopes[child].parent_scope, Some(parent));
505         writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
506         write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;
507         writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
508     }
509
510     Ok(())
511 }
512
513 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
514 /// local variables (both user-defined bindings and compiler temporaries).
515 pub fn write_mir_intro<'tcx>(
516     tcx: TyCtxt<'tcx>,
517     src: MirSource<'tcx>,
518     body: &Body<'_>,
519     w: &mut dyn Write,
520 ) -> io::Result<()> {
521     write_mir_sig(tcx, src, body, w)?;
522     writeln!(w, "{{")?;
523
524     // construct a scope tree and write it out
525     let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
526     for (index, scope_data) in body.source_scopes.iter().enumerate() {
527         if let Some(parent) = scope_data.parent_scope {
528             scope_tree.entry(parent).or_default().push(SourceScope::new(index));
529         } else {
530             // Only the argument scope has no parent, because it's the root.
531             assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
532         }
533     }
534
535     write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?;
536
537     // Add an empty line before the first block is printed.
538     writeln!(w)?;
539
540     Ok(())
541 }
542
543 /// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
544 /// allocations.
545 pub fn write_allocations<'tcx>(
546     tcx: TyCtxt<'tcx>,
547     body: &Body<'_>,
548     w: &mut dyn Write,
549 ) -> io::Result<()> {
550     fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
551         alloc.relocations().values().map(|(_, id)| *id)
552     }
553     fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
554         match val {
555             ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => {
556                 Either::Left(Either::Left(std::iter::once(ptr.alloc_id)))
557             }
558             ConstValue::Scalar(interpret::Scalar::Raw { .. }) => {
559                 Either::Left(Either::Right(std::iter::empty()))
560             }
561             ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => {
562                 Either::Right(alloc_ids_from_alloc(alloc))
563             }
564         }
565     }
566     struct CollectAllocIds(BTreeSet<AllocId>);
567     impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds {
568         fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
569             if let ty::ConstKind::Value(val) = c.val {
570                 self.0.extend(alloc_ids_from_const(val));
571             }
572             c.super_visit_with(self)
573         }
574     }
575     let mut visitor = CollectAllocIds(Default::default());
576     body.visit_with(&mut visitor);
577     // `seen` contains all seen allocations, including the ones we have *not* printed yet.
578     // The protocol is to first `insert` into `seen`, and only if that returns `true`
579     // then push to `todo`.
580     let mut seen = visitor.0;
581     let mut todo: Vec<_> = seen.iter().copied().collect();
582     while let Some(id) = todo.pop() {
583         let mut write_allocation_track_relocs =
584             |w: &mut dyn Write, alloc: &Allocation| -> io::Result<()> {
585                 // `.rev()` because we are popping them from the back of the `todo` vector.
586                 for id in alloc_ids_from_alloc(alloc).rev() {
587                     if seen.insert(id) {
588                         todo.push(id);
589                     }
590                 }
591                 write_allocation(tcx, alloc, w)
592             };
593         write!(w, "\n{}", id)?;
594         match tcx.get_global_alloc(id) {
595             // This can't really happen unless there are bugs, but it doesn't cost us anything to
596             // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
597             None => write!(w, " (deallocated)")?,
598             Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?,
599             Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
600                 match tcx.const_eval_poly(did) {
601                     Ok(ConstValue::ByRef { alloc, .. }) => {
602                         write!(w, " (static: {}, ", tcx.def_path_str(did))?;
603                         write_allocation_track_relocs(w, alloc)?;
604                     }
605                     Ok(_) => {
606                         span_bug!(tcx.def_span(did), " static item without `ByRef` initializer")
607                     }
608                     Err(_) => write!(
609                         w,
610                         " (static: {}, error during initializer evaluation)",
611                         tcx.def_path_str(did)
612                     )?,
613                 }
614             }
615             Some(GlobalAlloc::Static(did)) => {
616                 write!(w, " (extern static: {})", tcx.def_path_str(did))?
617             }
618             Some(GlobalAlloc::Memory(alloc)) => {
619                 write!(w, " (")?;
620                 write_allocation_track_relocs(w, alloc)?
621             }
622         }
623         writeln!(w)?;
624     }
625     Ok(())
626 }
627
628 /// Dumps the size and metadata and content of an allocation to the given writer.
629 /// The expectation is that the caller first prints other relevant metadata, so the exact
630 /// format of this function is (*without* leading or trailing newline):
631 /// ```
632 /// size: {}, align: {}) {
633 ///     <bytes>
634 /// }
635 /// ```
636 ///
637 /// The byte format is similar to how hex editors print bytes. Each line starts with the address of
638 /// the start of the line, followed by all bytes in hex format (space separated).
639 /// If the allocation is small enough to fit into a single line, no start address is given.
640 /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
641 /// characters or characters whose value is larger than 127) with a `.`
642 /// This also prints relocations adequately.
643 pub fn write_allocation<Tag: Copy + Debug, Extra>(
644     tcx: TyCtxt<'tcx>,
645     alloc: &Allocation<Tag, Extra>,
646     w: &mut dyn Write,
647 ) -> io::Result<()> {
648     write!(w, "size: {}, align: {})", alloc.size.bytes(), alloc.align.bytes())?;
649     if alloc.size == Size::ZERO {
650         // We are done.
651         return write!(w, " {{}}");
652     }
653     // Write allocation bytes.
654     writeln!(w, " {{")?;
655     write_allocation_bytes(tcx, alloc, w, "    ")?;
656     write!(w, "}}")?;
657     Ok(())
658 }
659
660 fn write_allocation_endline(w: &mut dyn Write, ascii: &str) -> io::Result<()> {
661     for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {
662         write!(w, "   ")?;
663     }
664     writeln!(w, " │ {}", ascii)
665 }
666
667 /// Number of bytes to print per allocation hex dump line.
668 const BYTES_PER_LINE: usize = 16;
669
670 /// Prints the line start address and returns the new line start address.
671 fn write_allocation_newline(
672     w: &mut dyn Write,
673     mut line_start: Size,
674     ascii: &str,
675     pos_width: usize,
676     prefix: &str,
677 ) -> io::Result<Size> {
678     write_allocation_endline(w, ascii)?;
679     line_start += Size::from_bytes(BYTES_PER_LINE);
680     write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;
681     Ok(line_start)
682 }
683
684 /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
685 /// is only one line). Note that your prefix should contain a trailing space as the lines are
686 /// printed directly after it.
687 fn write_allocation_bytes<Tag: Copy + Debug, Extra>(
688     tcx: TyCtxt<'tcx>,
689     alloc: &Allocation<Tag, Extra>,
690     w: &mut dyn Write,
691     prefix: &str,
692 ) -> io::Result<()> {
693     let num_lines = alloc.size.bytes_usize().saturating_sub(BYTES_PER_LINE);
694     // Number of chars needed to represent all line numbers.
695     let pos_width = format!("{:x}", alloc.size.bytes()).len();
696
697     if num_lines > 0 {
698         write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;
699     } else {
700         write!(w, "{}", prefix)?;
701     }
702
703     let mut i = Size::ZERO;
704     let mut line_start = Size::ZERO;
705
706     let ptr_size = tcx.data_layout.pointer_size;
707
708     let mut ascii = String::new();
709
710     let oversized_ptr = |target: &mut String, width| {
711         if target.len() > width {
712             write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();
713         }
714     };
715
716     while i < alloc.size {
717         // The line start already has a space. While we could remove that space from the line start
718         // printing and unconditionally print a space here, that would cause the single-line case
719         // to have a single space before it, which looks weird.
720         if i != line_start {
721             write!(w, " ")?;
722         }
723         if let Some(&(tag, target_id)) = alloc.relocations().get(&i) {
724             // Memory with a relocation must be defined
725             let j = i.bytes_usize();
726             let offset =
727                 alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
728             let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
729             let offset = Size::from_bytes(offset);
730             let relocation_width = |bytes| bytes * 3;
731             let ptr = Pointer::new_with_tag(target_id, offset, tag);
732             let mut target = format!("{:?}", ptr);
733             if target.len() > relocation_width(ptr_size.bytes_usize() - 1) {
734                 // This is too long, try to save some space.
735                 target = format!("{:#?}", ptr);
736             }
737             if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {
738                 // This branch handles the situation where a relocation starts in the current line
739                 // but ends in the next one.
740                 let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);
741                 let overflow = ptr_size - remainder;
742                 let remainder_width = relocation_width(remainder.bytes_usize()) - 2;
743                 let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1;
744                 ascii.push('╾');
745                 for _ in 0..remainder.bytes() - 1 {
746                     ascii.push('─');
747                 }
748                 if overflow_width > remainder_width && overflow_width >= target.len() {
749                     // The case where the relocation fits into the part in the next line
750                     write!(w, "╾{0:─^1$}", "", remainder_width)?;
751                     line_start =
752                         write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
753                     ascii.clear();
754                     write!(w, "{0:─^1$}╼", target, overflow_width)?;
755                 } else {
756                     oversized_ptr(&mut target, remainder_width);
757                     write!(w, "╾{0:─^1$}", target, remainder_width)?;
758                     line_start =
759                         write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
760                     write!(w, "{0:─^1$}╼", "", overflow_width)?;
761                     ascii.clear();
762                 }
763                 for _ in 0..overflow.bytes() - 1 {
764                     ascii.push('─');
765                 }
766                 ascii.push('╼');
767                 i += ptr_size;
768                 continue;
769             } else {
770                 // This branch handles a relocation that starts and ends in the current line.
771                 let relocation_width = relocation_width(ptr_size.bytes_usize() - 1);
772                 oversized_ptr(&mut target, relocation_width);
773                 ascii.push('╾');
774                 write!(w, "╾{0:─^1$}╼", target, relocation_width)?;
775                 for _ in 0..ptr_size.bytes() - 2 {
776                     ascii.push('─');
777                 }
778                 ascii.push('╼');
779                 i += ptr_size;
780             }
781         } else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() {
782             let j = i.bytes_usize();
783
784             // Checked definedness (and thus range) and relocations. This access also doesn't
785             // influence interpreter execution but is only for debugging.
786             let c = alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + 1)[0];
787             write!(w, "{:02x}", c)?;
788             if c.is_ascii_control() || c >= 0x80 {
789                 ascii.push('.');
790             } else {
791                 ascii.push(char::from(c));
792             }
793             i += Size::from_bytes(1);
794         } else {
795             write!(w, "__")?;
796             ascii.push('░');
797             i += Size::from_bytes(1);
798         }
799         // Print a new line header if the next line still has some bytes to print.
800         if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size {
801             line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
802             ascii.clear();
803         }
804     }
805     write_allocation_endline(w, &ascii)?;
806
807     Ok(())
808 }
809
810 fn write_mir_sig(
811     tcx: TyCtxt<'_>,
812     src: MirSource<'tcx>,
813     body: &Body<'_>,
814     w: &mut dyn Write,
815 ) -> io::Result<()> {
816     use rustc_hir::def::DefKind;
817
818     trace!("write_mir_sig: {:?}", src.instance);
819     let kind = tcx.def_kind(src.def_id());
820     let is_function = match kind {
821         DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
822         _ => tcx.is_closure(src.def_id()),
823     };
824     match (kind, src.promoted) {
825         (_, Some(i)) => write!(w, "{:?} in ", i)?,
826         (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
827         (DefKind::Static, _) => {
828             write!(w, "static {}", if tcx.is_mutable_static(src.def_id()) { "mut " } else { "" })?
829         }
830         (_, _) if is_function => write!(w, "fn ")?,
831         (DefKind::AnonConst, _) => {} // things like anon const, not an item
832         _ => bug!("Unexpected def kind {:?}", kind),
833     }
834
835     ty::print::with_forced_impl_filename_line(|| {
836         // see notes on #41697 elsewhere
837         write!(w, "{}", tcx.def_path_str(src.def_id()))
838     })?;
839
840     if src.promoted.is_none() && is_function {
841         write!(w, "(")?;
842
843         // fn argument types.
844         for (i, arg) in body.args_iter().enumerate() {
845             if i != 0 {
846                 write!(w, ", ")?;
847             }
848             write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
849         }
850
851         write!(w, ") -> {}", body.return_ty())?;
852     } else {
853         assert_eq!(body.arg_count, 0);
854         write!(w, ": {} =", body.return_ty())?;
855     }
856
857     if let Some(yield_ty) = body.yield_ty {
858         writeln!(w)?;
859         writeln!(w, "yields {}", yield_ty)?;
860     }
861
862     write!(w, " ")?;
863     // Next thing that gets printed is the opening {
864
865     Ok(())
866 }
867
868 fn write_user_type_annotations(
869     tcx: TyCtxt<'_>,
870     body: &Body<'_>,
871     w: &mut dyn Write,
872 ) -> io::Result<()> {
873     if !body.user_type_annotations.is_empty() {
874         writeln!(w, "| User Type Annotations")?;
875     }
876     for (index, annotation) in body.user_type_annotations.iter_enumerated() {
877         writeln!(
878             w,
879             "| {:?}: {:?} at {}",
880             index.index(),
881             annotation.user_ty,
882             tcx.sess.source_map().span_to_string(annotation.span)
883         )?;
884     }
885     if !body.user_type_annotations.is_empty() {
886         writeln!(w, "|")?;
887     }
888     Ok(())
889 }
890
891 pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
892     if let Some(i) = single {
893         vec![i]
894     } else {
895         tcx.mir_keys(LOCAL_CRATE).iter().map(|def_id| def_id.to_def_id()).collect()
896     }
897 }