• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::collections::BTreeSet;
2 use std::fmt::Display;
3 use std::fmt::Write as _;
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 super::spanview::write_mir_fn_spanview;
10 use either::Either;
11 use rustc_data_structures::fx::FxHashMap;
12 use rustc_hir::def_id::DefId;
13 use rustc_index::Idx;
14 use rustc_middle::mir::interpret::{
15     alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, ConstAllocation, ConstValue,
16     GlobalAlloc, Pointer, Provenance,
17 };
18 use rustc_middle::mir::visit::Visitor;
19 use rustc_middle::mir::*;
20 use rustc_middle::ty::{self, TyCtxt};
21 use rustc_target::abi::Size;
22 
23 const INDENT: &str = "    ";
24 /// Alignment for lining up comments following MIR statements
25 pub(crate) const ALIGN: usize = 40;
26 
27 /// An indication of where we are in the control flow graph. Used for printing
28 /// extra information in `dump_mir`
29 pub enum PassWhere {
30     /// We have not started dumping the control flow graph, but we are about to.
31     BeforeCFG,
32 
33     /// We just finished dumping the control flow graph. This is right before EOF
34     AfterCFG,
35 
36     /// We are about to start dumping the given basic block.
37     BeforeBlock(BasicBlock),
38 
39     /// We are just about to dump the given statement or terminator.
40     BeforeLocation(Location),
41 
42     /// We just dumped the given statement or terminator.
43     AfterLocation(Location),
44 
45     /// We just dumped the terminator for a block but not the closing `}`.
46     AfterTerminator(BasicBlock),
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 #[inline]
dump_mir<'tcx, F>( tcx: TyCtxt<'tcx>, pass_num: bool, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, extra_data: F, ) where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,74 pub fn dump_mir<'tcx, F>(
75     tcx: TyCtxt<'tcx>,
76     pass_num: bool,
77     pass_name: &str,
78     disambiguator: &dyn Display,
79     body: &Body<'tcx>,
80     extra_data: F,
81 ) where
82     F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
83 {
84     if !dump_enabled(tcx, pass_name, body.source.def_id()) {
85         return;
86     }
87 
88     dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data);
89 }
90 
dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool91 pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool {
92     let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir else {
93         return false;
94     };
95     // see notes on #41697 below
96     let node_path = ty::print::with_forced_impl_filename_line!(tcx.def_path_str(def_id));
97     filters.split('|').any(|or_filter| {
98         or_filter.split('&').all(|and_filter| {
99             let and_filter_trimmed = and_filter.trim();
100             and_filter_trimmed == "all"
101                 || pass_name.contains(and_filter_trimmed)
102                 || node_path.contains(and_filter_trimmed)
103         })
104     })
105 }
106 
107 // #41697 -- we use `with_forced_impl_filename_line()` because
108 // `def_path_str()` would otherwise trigger `type_of`, and this can
109 // run while we are already attempting to evaluate `type_of`.
110 
dump_matched_mir_node<'tcx, F>( tcx: TyCtxt<'tcx>, pass_num: bool, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, mut extra_data: F, ) where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,111 fn dump_matched_mir_node<'tcx, F>(
112     tcx: TyCtxt<'tcx>,
113     pass_num: bool,
114     pass_name: &str,
115     disambiguator: &dyn Display,
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, body)?;
123         // see notes on #41697 above
124         let def_path =
125             ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
126         // ignore-tidy-odd-backticks the literal below is fine
127         write!(file, "// MIR for `{}", def_path)?;
128         match body.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, body, &mut extra_data, &mut file)?;
140         extra_data(PassWhere::AfterCFG, &mut file)?;
141     };
142 
143     if tcx.sess.opts.unstable_opts.dump_mir_graphviz {
144         let _: io::Result<()> = try {
145             let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body)?;
146             write_mir_fn_graphviz(tcx, body, false, &mut file)?;
147         };
148     }
149 
150     if let Some(spanview) = tcx.sess.opts.unstable_opts.dump_mir_spanview {
151         let _: io::Result<()> = try {
152             let file_basename = dump_file_basename(tcx, pass_num, pass_name, disambiguator, body);
153             let mut file = create_dump_file_with_basename(tcx, &file_basename, "html")?;
154             if body.source.def_id().is_local() {
155                 write_mir_fn_spanview(tcx, body, spanview, &file_basename, &mut file)?;
156             }
157         };
158     }
159 }
160 
161 /// Returns the file basename portion (without extension) of a filename path
162 /// where we should dump a MIR representation output files.
dump_file_basename<'tcx>( tcx: TyCtxt<'tcx>, pass_num: bool, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, ) -> String163 fn dump_file_basename<'tcx>(
164     tcx: TyCtxt<'tcx>,
165     pass_num: bool,
166     pass_name: &str,
167     disambiguator: &dyn Display,
168     body: &Body<'tcx>,
169 ) -> String {
170     let source = body.source;
171     let promotion_id = match source.promoted {
172         Some(id) => format!("-{:?}", id),
173         None => String::new(),
174     };
175 
176     let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
177         String::new()
178     } else {
179         if pass_num {
180             format!(".{:03}-{:03}", body.phase.phase_index(), body.pass_count)
181         } else {
182             ".-------".to_string()
183         }
184     };
185 
186     let crate_name = tcx.crate_name(source.def_id().krate);
187     let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
188     // All drop shims have the same DefId, so we have to add the type
189     // to get unique file names.
190     let shim_disambiguator = match source.instance {
191         ty::InstanceDef::DropGlue(_, Some(ty)) => {
192             // Unfortunately, pretty-printed typed are not very filename-friendly.
193             // We dome some filtering.
194             let mut s = ".".to_owned();
195             s.extend(ty.to_string().chars().filter_map(|c| match c {
196                 ' ' => None,
197                 ':' | '<' | '>' => Some('_'),
198                 c => Some(c),
199             }));
200             s
201         }
202         _ => String::new(),
203     };
204 
205     format!(
206         "{}.{}{}{}{}.{}.{}",
207         crate_name, item_name, shim_disambiguator, promotion_id, pass_num, pass_name, disambiguator,
208     )
209 }
210 
211 /// Returns the path to the filename where we should dump a given MIR.
212 /// Also used by other bits of code (e.g., NLL inference) that dump
213 /// graphviz data or other things.
dump_path(tcx: TyCtxt<'_>, basename: &str, extension: &str) -> PathBuf214 fn dump_path(tcx: TyCtxt<'_>, basename: &str, extension: &str) -> PathBuf {
215     let mut file_path = PathBuf::new();
216     file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
217 
218     let file_name = format!("{}.{}", basename, extension,);
219 
220     file_path.push(&file_name);
221 
222     file_path
223 }
224 
225 /// Attempts to open the MIR dump file with the given name and extension.
create_dump_file_with_basename( tcx: TyCtxt<'_>, file_basename: &str, extension: &str, ) -> io::Result<io::BufWriter<fs::File>>226 fn create_dump_file_with_basename(
227     tcx: TyCtxt<'_>,
228     file_basename: &str,
229     extension: &str,
230 ) -> io::Result<io::BufWriter<fs::File>> {
231     let file_path = dump_path(tcx, file_basename, extension);
232     if let Some(parent) = file_path.parent() {
233         fs::create_dir_all(parent).map_err(|e| {
234             io::Error::new(
235                 e.kind(),
236                 format!("IO error creating MIR dump directory: {:?}; {}", parent, e),
237             )
238         })?;
239     }
240     Ok(io::BufWriter::new(fs::File::create(&file_path).map_err(|e| {
241         io::Error::new(e.kind(), format!("IO error creating MIR dump file: {:?}; {}", file_path, e))
242     })?))
243 }
244 
245 /// Attempts to open a file where we should dump a given MIR or other
246 /// bit of MIR-related data. Used by `mir-dump`, but also by other
247 /// bits of code (e.g., NLL inference) that dump graphviz data or
248 /// other things, and hence takes the extension as an argument.
create_dump_file<'tcx>( tcx: TyCtxt<'tcx>, extension: &str, pass_num: bool, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, ) -> io::Result<io::BufWriter<fs::File>>249 pub fn create_dump_file<'tcx>(
250     tcx: TyCtxt<'tcx>,
251     extension: &str,
252     pass_num: bool,
253     pass_name: &str,
254     disambiguator: &dyn Display,
255     body: &Body<'tcx>,
256 ) -> io::Result<io::BufWriter<fs::File>> {
257     create_dump_file_with_basename(
258         tcx,
259         &dump_file_basename(tcx, pass_num, pass_name, disambiguator, body),
260         extension,
261     )
262 }
263 
264 /// Write out a human-readable textual representation for the given MIR.
write_mir_pretty<'tcx>( tcx: TyCtxt<'tcx>, single: Option<DefId>, w: &mut dyn Write, ) -> io::Result<()>265 pub fn write_mir_pretty<'tcx>(
266     tcx: TyCtxt<'tcx>,
267     single: Option<DefId>,
268     w: &mut dyn Write,
269 ) -> io::Result<()> {
270     writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
271     writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
272 
273     let mut first = true;
274     for def_id in dump_mir_def_ids(tcx, single) {
275         if first {
276             first = false;
277         } else {
278             // Put empty lines between all items
279             writeln!(w)?;
280         }
281 
282         let render_body = |w: &mut dyn Write, body| -> io::Result<()> {
283             write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
284 
285             for body in tcx.promoted_mir(def_id) {
286                 writeln!(w)?;
287                 write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
288             }
289             Ok(())
290         };
291 
292         // For `const fn` we want to render both the optimized MIR and the MIR for ctfe.
293         if tcx.is_const_fn_raw(def_id) {
294             render_body(w, tcx.optimized_mir(def_id))?;
295             writeln!(w)?;
296             writeln!(w, "// MIR FOR CTFE")?;
297             // Do not use `render_body`, as that would render the promoteds again, but these
298             // are shared between mir_for_ctfe and optimized_mir
299             write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w)?;
300         } else {
301             let instance_mir = tcx.instance_mir(ty::InstanceDef::Item(def_id));
302             render_body(w, instance_mir)?;
303         }
304     }
305     Ok(())
306 }
307 
308 /// Write out a human-readable textual representation for the given function.
write_mir_fn<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, extra_data: &mut F, w: &mut dyn Write, ) -> io::Result<()> where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,309 pub fn write_mir_fn<'tcx, F>(
310     tcx: TyCtxt<'tcx>,
311     body: &Body<'tcx>,
312     extra_data: &mut F,
313     w: &mut dyn Write,
314 ) -> io::Result<()>
315 where
316     F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
317 {
318     write_mir_intro(tcx, body, w)?;
319     for block in body.basic_blocks.indices() {
320         extra_data(PassWhere::BeforeBlock(block), w)?;
321         write_basic_block(tcx, block, body, extra_data, w)?;
322         if block.index() + 1 != body.basic_blocks.len() {
323             writeln!(w)?;
324         }
325     }
326 
327     writeln!(w, "}}")?;
328 
329     write_allocations(tcx, body, w)?;
330 
331     Ok(())
332 }
333 
334 /// Write out a human-readable textual representation for the given basic block.
write_basic_block<'tcx, F>( tcx: TyCtxt<'tcx>, block: BasicBlock, body: &Body<'tcx>, extra_data: &mut F, w: &mut dyn Write, ) -> io::Result<()> where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,335 pub fn write_basic_block<'tcx, F>(
336     tcx: TyCtxt<'tcx>,
337     block: BasicBlock,
338     body: &Body<'tcx>,
339     extra_data: &mut F,
340     w: &mut dyn Write,
341 ) -> io::Result<()>
342 where
343     F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
344 {
345     let data = &body[block];
346 
347     // Basic block label at the top.
348     let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
349     writeln!(w, "{}{:?}{}: {{", INDENT, block, cleanup_text)?;
350 
351     // List of statements in the middle.
352     let mut current_location = Location { block, statement_index: 0 };
353     for statement in &data.statements {
354         extra_data(PassWhere::BeforeLocation(current_location), w)?;
355         let indented_body = format!("{0}{0}{1:?};", INDENT, statement);
356         if tcx.sess.opts.unstable_opts.mir_include_spans {
357             writeln!(
358                 w,
359                 "{:A$} // {}{}",
360                 indented_body,
361                 if tcx.sess.verbose() {
362                     format!("{:?}: ", current_location)
363                 } else {
364                     String::new()
365                 },
366                 comment(tcx, statement.source_info),
367                 A = ALIGN,
368             )?;
369         } else {
370             writeln!(w, "{}", indented_body)?;
371         }
372 
373         write_extra(tcx, w, |visitor| {
374             visitor.visit_statement(statement, current_location);
375         })?;
376 
377         extra_data(PassWhere::AfterLocation(current_location), w)?;
378 
379         current_location.statement_index += 1;
380     }
381 
382     // Terminator at the bottom.
383     extra_data(PassWhere::BeforeLocation(current_location), w)?;
384     let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
385     if tcx.sess.opts.unstable_opts.mir_include_spans {
386         writeln!(
387             w,
388             "{:A$} // {}{}",
389             indented_terminator,
390             if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
391             comment(tcx, data.terminator().source_info),
392             A = ALIGN,
393         )?;
394     } else {
395         writeln!(w, "{}", indented_terminator)?;
396     }
397 
398     write_extra(tcx, w, |visitor| {
399         visitor.visit_terminator(data.terminator(), current_location);
400     })?;
401 
402     extra_data(PassWhere::AfterLocation(current_location), w)?;
403     extra_data(PassWhere::AfterTerminator(block), w)?;
404 
405     writeln!(w, "{}}}", INDENT)
406 }
407 
408 /// After we print the main statement, we sometimes dump extra
409 /// information. There's often a lot of little things "nuzzled up" in
410 /// a statement.
write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()> where F: FnMut(&mut ExtraComments<'tcx>),411 fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()>
412 where
413     F: FnMut(&mut ExtraComments<'tcx>),
414 {
415     if tcx.sess.opts.unstable_opts.mir_include_spans {
416         let mut extra_comments = ExtraComments { tcx, comments: vec![] };
417         visit_op(&mut extra_comments);
418         for comment in extra_comments.comments {
419             writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
420         }
421     }
422     Ok(())
423 }
424 
425 struct ExtraComments<'tcx> {
426     tcx: TyCtxt<'tcx>,
427     comments: Vec<String>,
428 }
429 
430 impl<'tcx> ExtraComments<'tcx> {
push(&mut self, lines: &str)431     fn push(&mut self, lines: &str) {
432         for line in lines.split('\n') {
433             self.comments.push(line.to_string());
434         }
435     }
436 }
437 
use_verbose(ty: Ty<'_>, fn_def: bool) -> bool438 fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
439     match *ty.kind() {
440         ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
441         // Unit type
442         ty::Tuple(g_args) if g_args.is_empty() => false,
443         ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),
444         ty::Array(ty, _) => use_verbose(ty, fn_def),
445         ty::FnDef(..) => fn_def,
446         _ => true,
447     }
448 }
449 
450 impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
visit_constant(&mut self, constant: &Constant<'tcx>, _location: Location)451     fn visit_constant(&mut self, constant: &Constant<'tcx>, _location: Location) {
452         let Constant { span, user_ty, literal } = constant;
453         if use_verbose(literal.ty(), true) {
454             self.push("mir::Constant");
455             self.push(&format!(
456                 "+ span: {}",
457                 self.tcx.sess.source_map().span_to_embeddable_string(*span)
458             ));
459             if let Some(user_ty) = user_ty {
460                 self.push(&format!("+ user_ty: {:?}", user_ty));
461             }
462 
463             // FIXME: this is a poor version of `pretty_print_const_value`.
464             let fmt_val = |val: &ConstValue<'tcx>| match val {
465                 ConstValue::ZeroSized => "<ZST>".to_string(),
466                 ConstValue::Scalar(s) => format!("Scalar({:?})", s),
467                 ConstValue::Slice { .. } => "Slice(..)".to_string(),
468                 ConstValue::ByRef { .. } => "ByRef(..)".to_string(),
469             };
470 
471             let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree {
472                 ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({:?})", leaf),
473                 ty::ValTree::Branch(_) => "ValTree::Branch(..)".to_string(),
474             };
475 
476             let val = match literal {
477                 ConstantKind::Ty(ct) => match ct.kind() {
478                     ty::ConstKind::Param(p) => format!("Param({})", p),
479                     ty::ConstKind::Unevaluated(uv) => {
480                         format!("Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.substs,)
481                     }
482                     ty::ConstKind::Value(val) => format!("Value({})", fmt_valtree(&val)),
483                     ty::ConstKind::Error(_) => "Error".to_string(),
484                     // These variants shouldn't exist in the MIR.
485                     ty::ConstKind::Placeholder(_)
486                     | ty::ConstKind::Infer(_)
487                     | ty::ConstKind::Expr(_)
488                     | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", literal),
489                 },
490                 ConstantKind::Unevaluated(uv, _) => {
491                     format!(
492                         "Unevaluated({}, {:?}, {:?})",
493                         self.tcx.def_path_str(uv.def),
494                         uv.substs,
495                         uv.promoted,
496                     )
497                 }
498                 // To keep the diffs small, we render this like we render `ty::Const::Value`.
499                 //
500                 // This changes once `ty::Const::Value` is represented using valtrees.
501                 ConstantKind::Val(val, _) => format!("Value({})", fmt_val(&val)),
502             };
503 
504             // This reflects what `Const` looked liked before `val` was renamed
505             // as `kind`. We print it like this to avoid having to update
506             // expected output in a lot of tests.
507             self.push(&format!("+ literal: Const {{ ty: {}, val: {} }}", literal.ty(), val));
508         }
509     }
510 
visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location)511     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
512         self.super_rvalue(rvalue, location);
513         if let Rvalue::Aggregate(kind, _) = rvalue {
514             match **kind {
515                 AggregateKind::Closure(def_id, substs) => {
516                     self.push("closure");
517                     self.push(&format!("+ def_id: {:?}", def_id));
518                     self.push(&format!("+ substs: {:#?}", substs));
519                 }
520 
521                 AggregateKind::Generator(def_id, substs, movability) => {
522                     self.push("generator");
523                     self.push(&format!("+ def_id: {:?}", def_id));
524                     self.push(&format!("+ substs: {:#?}", substs));
525                     self.push(&format!("+ movability: {:?}", movability));
526                 }
527 
528                 AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
529                     self.push("adt");
530                     self.push(&format!("+ user_ty: {:?}", user_ty));
531                 }
532 
533                 _ => {}
534             }
535         }
536     }
537 }
538 
539 fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
540     let location = tcx.sess.source_map().span_to_embeddable_string(span);
541     format!("scope {} at {}", scope.index(), location,)
542 }
543 
544 /// Prints local variables in a scope tree.
write_scope_tree( tcx: TyCtxt<'_>, body: &Body<'_>, scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>, w: &mut dyn Write, parent: SourceScope, depth: usize, ) -> io::Result<()>545 fn write_scope_tree(
546     tcx: TyCtxt<'_>,
547     body: &Body<'_>,
548     scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
549     w: &mut dyn Write,
550     parent: SourceScope,
551     depth: usize,
552 ) -> io::Result<()> {
553     let indent = depth * INDENT.len();
554 
555     // Local variable debuginfo.
556     for var_debug_info in &body.var_debug_info {
557         if var_debug_info.source_info.scope != parent {
558             // Not declared in this scope.
559             continue;
560         }
561 
562         let indented_debug_info = format!(
563             "{0:1$}debug {2} => {3:&<4$}{5:?};",
564             INDENT,
565             indent,
566             var_debug_info.name,
567             "",
568             var_debug_info.references as usize,
569             var_debug_info.value,
570         );
571 
572         if tcx.sess.opts.unstable_opts.mir_include_spans {
573             writeln!(
574                 w,
575                 "{0:1$} // in {2}",
576                 indented_debug_info,
577                 ALIGN,
578                 comment(tcx, var_debug_info.source_info),
579             )?;
580         } else {
581             writeln!(w, "{}", indented_debug_info)?;
582         }
583     }
584 
585     // Local variable types.
586     for (local, local_decl) in body.local_decls.iter_enumerated() {
587         if (1..body.arg_count + 1).contains(&local.index()) {
588             // Skip over argument locals, they're printed in the signature.
589             continue;
590         }
591 
592         if local_decl.source_info.scope != parent {
593             // Not declared in this scope.
594             continue;
595         }
596 
597         let mut_str = local_decl.mutability.prefix_str();
598 
599         let mut indented_decl =
600             format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty);
601         if let Some(user_ty) = &local_decl.user_ty {
602             for user_ty in user_ty.projections() {
603                 write!(indented_decl, " as {:?}", user_ty).unwrap();
604             }
605         }
606         indented_decl.push(';');
607 
608         let local_name = if local == RETURN_PLACE { " return place" } else { "" };
609 
610         if tcx.sess.opts.unstable_opts.mir_include_spans {
611             writeln!(
612                 w,
613                 "{0:1$} //{2} in {3}",
614                 indented_decl,
615                 ALIGN,
616                 local_name,
617                 comment(tcx, local_decl.source_info),
618             )?;
619         } else {
620             writeln!(w, "{}", indented_decl,)?;
621         }
622     }
623 
624     let Some(children) = scope_tree.get(&parent) else {
625         return Ok(());
626     };
627 
628     for &child in children {
629         let child_data = &body.source_scopes[child];
630         assert_eq!(child_data.parent_scope, Some(parent));
631 
632         let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
633             (
634                 format!(
635                     " (inlined {}{})",
636                     if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
637                     callee
638                 ),
639                 Some(callsite_span),
640             )
641         } else {
642             (String::new(), None)
643         };
644 
645         let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
646 
647         if tcx.sess.opts.unstable_opts.mir_include_spans {
648             if let Some(span) = span {
649                 writeln!(
650                     w,
651                     "{0:1$} // at {2}",
652                     indented_header,
653                     ALIGN,
654                     tcx.sess.source_map().span_to_embeddable_string(span),
655                 )?;
656             } else {
657                 writeln!(w, "{}", indented_header)?;
658             }
659         } else {
660             writeln!(w, "{}", indented_header)?;
661         }
662 
663         write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;
664         writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
665     }
666 
667     Ok(())
668 }
669 
670 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
671 /// local variables (both user-defined bindings and compiler temporaries).
write_mir_intro<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'_>, w: &mut dyn Write, ) -> io::Result<()>672 pub fn write_mir_intro<'tcx>(
673     tcx: TyCtxt<'tcx>,
674     body: &Body<'_>,
675     w: &mut dyn Write,
676 ) -> io::Result<()> {
677     write_mir_sig(tcx, body, w)?;
678     writeln!(w, "{{")?;
679 
680     // construct a scope tree and write it out
681     let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
682     for (index, scope_data) in body.source_scopes.iter().enumerate() {
683         if let Some(parent) = scope_data.parent_scope {
684             scope_tree.entry(parent).or_default().push(SourceScope::new(index));
685         } else {
686             // Only the argument scope has no parent, because it's the root.
687             assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
688         }
689     }
690 
691     write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?;
692 
693     // Add an empty line before the first block is printed.
694     writeln!(w)?;
695 
696     Ok(())
697 }
698 
699 /// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
700 /// allocations.
write_allocations<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'_>, w: &mut dyn Write, ) -> io::Result<()>701 pub fn write_allocations<'tcx>(
702     tcx: TyCtxt<'tcx>,
703     body: &Body<'_>,
704     w: &mut dyn Write,
705 ) -> io::Result<()> {
706     fn alloc_ids_from_alloc(
707         alloc: ConstAllocation<'_>,
708     ) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
709         alloc.inner().provenance().ptrs().values().map(|id| *id)
710     }
711 
712     fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
713         match val {
714             ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => {
715                 Either::Left(Either::Left(std::iter::once(ptr.provenance)))
716             }
717             ConstValue::Scalar(interpret::Scalar::Int { .. }) => {
718                 Either::Left(Either::Right(std::iter::empty()))
719             }
720             ConstValue::ZeroSized => Either::Left(Either::Right(std::iter::empty())),
721             ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => {
722                 Either::Right(alloc_ids_from_alloc(alloc))
723             }
724         }
725     }
726     struct CollectAllocIds(BTreeSet<AllocId>);
727 
728     impl<'tcx> Visitor<'tcx> for CollectAllocIds {
729         fn visit_constant(&mut self, c: &Constant<'tcx>, _: Location) {
730             match c.literal {
731                 ConstantKind::Ty(_) | ConstantKind::Unevaluated(..) => {}
732                 ConstantKind::Val(val, _) => {
733                     self.0.extend(alloc_ids_from_const_val(val));
734                 }
735             }
736         }
737     }
738 
739     let mut visitor = CollectAllocIds(Default::default());
740     visitor.visit_body(body);
741 
742     // `seen` contains all seen allocations, including the ones we have *not* printed yet.
743     // The protocol is to first `insert` into `seen`, and only if that returns `true`
744     // then push to `todo`.
745     let mut seen = visitor.0;
746     let mut todo: Vec<_> = seen.iter().copied().collect();
747     while let Some(id) = todo.pop() {
748         let mut write_allocation_track_relocs =
749             |w: &mut dyn Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
750                 // `.rev()` because we are popping them from the back of the `todo` vector.
751                 for id in alloc_ids_from_alloc(alloc).rev() {
752                     if seen.insert(id) {
753                         todo.push(id);
754                     }
755                 }
756                 write!(w, "{}", display_allocation(tcx, alloc.inner()))
757             };
758         write!(w, "\n{id:?}")?;
759         match tcx.try_get_global_alloc(id) {
760             // This can't really happen unless there are bugs, but it doesn't cost us anything to
761             // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
762             None => write!(w, " (deallocated)")?,
763             Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?,
764             Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => {
765                 write!(w, " (vtable: impl {trait_ref} for {ty})")?
766             }
767             Some(GlobalAlloc::VTable(ty, None)) => {
768                 write!(w, " (vtable: impl <auto trait> for {ty})")?
769             }
770             Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
771                 match tcx.eval_static_initializer(did) {
772                     Ok(alloc) => {
773                         write!(w, " (static: {}, ", tcx.def_path_str(did))?;
774                         write_allocation_track_relocs(w, alloc)?;
775                     }
776                     Err(_) => write!(
777                         w,
778                         " (static: {}, error during initializer evaluation)",
779                         tcx.def_path_str(did)
780                     )?,
781                 }
782             }
783             Some(GlobalAlloc::Static(did)) => {
784                 write!(w, " (extern static: {})", tcx.def_path_str(did))?
785             }
786             Some(GlobalAlloc::Memory(alloc)) => {
787                 write!(w, " (")?;
788                 write_allocation_track_relocs(w, alloc)?
789             }
790         }
791         writeln!(w)?;
792     }
793     Ok(())
794 }
795 
796 /// Dumps the size and metadata and content of an allocation to the given writer.
797 /// The expectation is that the caller first prints other relevant metadata, so the exact
798 /// format of this function is (*without* leading or trailing newline):
799 ///
800 /// ```text
801 /// size: {}, align: {}) {
802 ///     <bytes>
803 /// }
804 /// ```
805 ///
806 /// The byte format is similar to how hex editors print bytes. Each line starts with the address of
807 /// the start of the line, followed by all bytes in hex format (space separated).
808 /// If the allocation is small enough to fit into a single line, no start address is given.
809 /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
810 /// characters or characters whose value is larger than 127) with a `.`
811 /// This also prints provenance adequately.
display_allocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( tcx: TyCtxt<'tcx>, alloc: &'a Allocation<Prov, Extra, Bytes>, ) -> RenderAllocation<'a, 'tcx, Prov, Extra, Bytes>812 pub fn display_allocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
813     tcx: TyCtxt<'tcx>,
814     alloc: &'a Allocation<Prov, Extra, Bytes>,
815 ) -> RenderAllocation<'a, 'tcx, Prov, Extra, Bytes> {
816     RenderAllocation { tcx, alloc }
817 }
818 
819 #[doc(hidden)]
820 pub struct RenderAllocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> {
821     tcx: TyCtxt<'tcx>,
822     alloc: &'a Allocation<Prov, Extra, Bytes>,
823 }
824 
825 impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> std::fmt::Display
826     for RenderAllocation<'a, 'tcx, Prov, Extra, Bytes>
827 {
fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result828     fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
829         let RenderAllocation { tcx, alloc } = *self;
830         write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?;
831         if alloc.size() == Size::ZERO {
832             // We are done.
833             return write!(w, " {{}}");
834         }
835         // Write allocation bytes.
836         writeln!(w, " {{")?;
837         write_allocation_bytes(tcx, alloc, w, "    ")?;
838         write!(w, "}}")?;
839         Ok(())
840     }
841 }
842 
write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result843 fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result {
844     for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {
845         write!(w, "   ")?;
846     }
847     writeln!(w, " │ {}", ascii)
848 }
849 
850 /// Number of bytes to print per allocation hex dump line.
851 const BYTES_PER_LINE: usize = 16;
852 
853 /// Prints the line start address and returns the new line start address.
write_allocation_newline( w: &mut dyn std::fmt::Write, mut line_start: Size, ascii: &str, pos_width: usize, prefix: &str, ) -> Result<Size, std::fmt::Error>854 fn write_allocation_newline(
855     w: &mut dyn std::fmt::Write,
856     mut line_start: Size,
857     ascii: &str,
858     pos_width: usize,
859     prefix: &str,
860 ) -> Result<Size, std::fmt::Error> {
861     write_allocation_endline(w, ascii)?;
862     line_start += Size::from_bytes(BYTES_PER_LINE);
863     write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;
864     Ok(line_start)
865 }
866 
867 /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
868 /// is only one line). Note that your prefix should contain a trailing space as the lines are
869 /// printed directly after it.
write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( tcx: TyCtxt<'tcx>, alloc: &Allocation<Prov, Extra, Bytes>, w: &mut dyn std::fmt::Write, prefix: &str, ) -> std::fmt::Result870 pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
871     tcx: TyCtxt<'tcx>,
872     alloc: &Allocation<Prov, Extra, Bytes>,
873     w: &mut dyn std::fmt::Write,
874     prefix: &str,
875 ) -> std::fmt::Result {
876     let num_lines = alloc.size().bytes_usize().saturating_sub(BYTES_PER_LINE);
877     // Number of chars needed to represent all line numbers.
878     let pos_width = hex_number_length(alloc.size().bytes());
879 
880     if num_lines > 0 {
881         write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;
882     } else {
883         write!(w, "{}", prefix)?;
884     }
885 
886     let mut i = Size::ZERO;
887     let mut line_start = Size::ZERO;
888 
889     let ptr_size = tcx.data_layout.pointer_size;
890 
891     let mut ascii = String::new();
892 
893     let oversized_ptr = |target: &mut String, width| {
894         if target.len() > width {
895             write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();
896         }
897     };
898 
899     while i < alloc.size() {
900         // The line start already has a space. While we could remove that space from the line start
901         // printing and unconditionally print a space here, that would cause the single-line case
902         // to have a single space before it, which looks weird.
903         if i != line_start {
904             write!(w, " ")?;
905         }
906         if let Some(prov) = alloc.provenance().get_ptr(i) {
907             // Memory with provenance must be defined
908             assert!(alloc.init_mask().is_range_initialized(alloc_range(i, ptr_size)).is_ok());
909             let j = i.bytes_usize();
910             let offset = alloc
911                 .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
912             let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
913             let offset = Size::from_bytes(offset);
914             let provenance_width = |bytes| bytes * 3;
915             let ptr = Pointer::new(prov, offset);
916             let mut target = format!("{:?}", ptr);
917             if target.len() > provenance_width(ptr_size.bytes_usize() - 1) {
918                 // This is too long, try to save some space.
919                 target = format!("{:#?}", ptr);
920             }
921             if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {
922                 // This branch handles the situation where a provenance starts in the current line
923                 // but ends in the next one.
924                 let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);
925                 let overflow = ptr_size - remainder;
926                 let remainder_width = provenance_width(remainder.bytes_usize()) - 2;
927                 let overflow_width = provenance_width(overflow.bytes_usize() - 1) + 1;
928                 ascii.push('╾'); // HEAVY LEFT AND LIGHT RIGHT
929                 for _ in 1..remainder.bytes() {
930                     ascii.push('─'); // LIGHT HORIZONTAL
931                 }
932                 if overflow_width > remainder_width && overflow_width >= target.len() {
933                     // The case where the provenance fits into the part in the next line
934                     write!(w, "╾{0:─^1$}", "", remainder_width)?;
935                     line_start =
936                         write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
937                     ascii.clear();
938                     write!(w, "{0:─^1$}╼", target, overflow_width)?;
939                 } else {
940                     oversized_ptr(&mut target, remainder_width);
941                     write!(w, "╾{0:─^1$}", target, remainder_width)?;
942                     line_start =
943                         write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
944                     write!(w, "{0:─^1$}╼", "", overflow_width)?;
945                     ascii.clear();
946                 }
947                 for _ in 0..overflow.bytes() - 1 {
948                     ascii.push('─');
949                 }
950                 ascii.push('╼'); // LIGHT LEFT AND HEAVY RIGHT
951                 i += ptr_size;
952                 continue;
953             } else {
954                 // This branch handles a provenance that starts and ends in the current line.
955                 let provenance_width = provenance_width(ptr_size.bytes_usize() - 1);
956                 oversized_ptr(&mut target, provenance_width);
957                 ascii.push('╾');
958                 write!(w, "╾{0:─^1$}╼", target, provenance_width)?;
959                 for _ in 0..ptr_size.bytes() - 2 {
960                     ascii.push('─');
961                 }
962                 ascii.push('╼');
963                 i += ptr_size;
964             }
965         } else if let Some(prov) = alloc.provenance().get(i, &tcx) {
966             // Memory with provenance must be defined
967             assert!(
968                 alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok()
969             );
970             ascii.push('━'); // HEAVY HORIZONTAL
971             // We have two characters to display this, which is obviously not enough.
972             // Format is similar to "oversized" above.
973             let j = i.bytes_usize();
974             let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
975             write!(w, "╾{:02x}{:#?} (1 ptr byte)╼", c, prov)?;
976             i += Size::from_bytes(1);
977         } else if alloc
978             .init_mask()
979             .is_range_initialized(alloc_range(i, Size::from_bytes(1)))
980             .is_ok()
981         {
982             let j = i.bytes_usize();
983 
984             // Checked definedness (and thus range) and provenance. This access also doesn't
985             // influence interpreter execution but is only for debugging.
986             let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
987             write!(w, "{:02x}", c)?;
988             if c.is_ascii_control() || c >= 0x80 {
989                 ascii.push('.');
990             } else {
991                 ascii.push(char::from(c));
992             }
993             i += Size::from_bytes(1);
994         } else {
995             write!(w, "__")?;
996             ascii.push('░');
997             i += Size::from_bytes(1);
998         }
999         // Print a new line header if the next line still has some bytes to print.
1000         if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size() {
1001             line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
1002             ascii.clear();
1003         }
1004     }
1005     write_allocation_endline(w, &ascii)?;
1006 
1007     Ok(())
1008 }
1009 
write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Result<()>1010 fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> {
1011     use rustc_hir::def::DefKind;
1012 
1013     trace!("write_mir_sig: {:?}", body.source.instance);
1014     let def_id = body.source.def_id();
1015     let kind = tcx.def_kind(def_id);
1016     let is_function = match kind {
1017         DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
1018         _ => tcx.is_closure(def_id),
1019     };
1020     match (kind, body.source.promoted) {
1021         (_, Some(i)) => write!(w, "{:?} in ", i)?,
1022         (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
1023         (DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?,
1024         (DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?,
1025         (_, _) if is_function => write!(w, "fn ")?,
1026         (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
1027         _ => bug!("Unexpected def kind {:?}", kind),
1028     }
1029 
1030     ty::print::with_forced_impl_filename_line! {
1031         // see notes on #41697 elsewhere
1032         write!(w, "{}", tcx.def_path_str(def_id))?
1033     }
1034 
1035     if body.source.promoted.is_none() && is_function {
1036         write!(w, "(")?;
1037 
1038         // fn argument types.
1039         for (i, arg) in body.args_iter().enumerate() {
1040             if i != 0 {
1041                 write!(w, ", ")?;
1042             }
1043             write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
1044         }
1045 
1046         write!(w, ") -> {}", body.return_ty())?;
1047     } else {
1048         assert_eq!(body.arg_count, 0);
1049         write!(w, ": {} =", body.return_ty())?;
1050     }
1051 
1052     if let Some(yield_ty) = body.yield_ty() {
1053         writeln!(w)?;
1054         writeln!(w, "yields {}", yield_ty)?;
1055     }
1056 
1057     write!(w, " ")?;
1058     // Next thing that gets printed is the opening {
1059 
1060     Ok(())
1061 }
1062 
write_user_type_annotations( tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write, ) -> io::Result<()>1063 fn write_user_type_annotations(
1064     tcx: TyCtxt<'_>,
1065     body: &Body<'_>,
1066     w: &mut dyn Write,
1067 ) -> io::Result<()> {
1068     if !body.user_type_annotations.is_empty() {
1069         writeln!(w, "| User Type Annotations")?;
1070     }
1071     for (index, annotation) in body.user_type_annotations.iter_enumerated() {
1072         writeln!(
1073             w,
1074             "| {:?}: user_ty: {:?}, span: {}, inferred_ty: {:?}",
1075             index.index(),
1076             annotation.user_ty,
1077             tcx.sess.source_map().span_to_embeddable_string(annotation.span),
1078             annotation.inferred_ty,
1079         )?;
1080     }
1081     if !body.user_type_annotations.is_empty() {
1082         writeln!(w, "|")?;
1083     }
1084     Ok(())
1085 }
1086 
dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId>1087 pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
1088     if let Some(i) = single {
1089         vec![i]
1090     } else {
1091         tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
1092     }
1093 }
1094 
1095 /// Calc converted u64 decimal into hex and return it's length in chars
1096 ///
1097 /// ```ignore (cannot-test-private-function)
1098 /// assert_eq!(1, hex_number_length(0));
1099 /// assert_eq!(1, hex_number_length(1));
1100 /// assert_eq!(2, hex_number_length(16));
1101 /// ```
hex_number_length(x: u64) -> usize1102 fn hex_number_length(x: u64) -> usize {
1103     if x == 0 {
1104         return 1;
1105     }
1106     let mut length = 0;
1107     let mut x_left = x;
1108     while x_left > 0 {
1109         x_left /= 16;
1110         length += 1;
1111     }
1112     length
1113 }
1114