rustc_middle/mir/
pretty.rs

1use std::collections::BTreeSet;
2use std::fmt::{Display, Write as _};
3use std::path::{Path, PathBuf};
4use std::{fs, io};
5
6use rustc_abi::Size;
7use rustc_ast::InlineAsmTemplatePiece;
8use tracing::trace;
9use ty::print::PrettyPrinter;
10
11use super::graphviz::write_mir_fn_graphviz;
12use crate::mir::interpret::{
13    AllocBytes, AllocId, Allocation, ConstAllocation, GlobalAlloc, Pointer, Provenance,
14    alloc_range, read_target_uint,
15};
16use crate::mir::visit::Visitor;
17use crate::mir::*;
18
19const INDENT: &str = "    ";
20/// Alignment for lining up comments following MIR statements
21pub(crate) const ALIGN: usize = 40;
22
23/// An indication of where we are in the control flow graph. Used for printing
24/// extra information in `dump_mir`
25#[derive(Clone, Copy)]
26pub enum PassWhere {
27    /// We have not started dumping the control flow graph, but we are about to.
28    BeforeCFG,
29
30    /// We just finished dumping the control flow graph. This is right before EOF
31    AfterCFG,
32
33    /// We are about to start dumping the given basic block.
34    BeforeBlock(BasicBlock),
35
36    /// We are just about to dump the given statement or terminator.
37    BeforeLocation(Location),
38
39    /// We just dumped the given statement or terminator.
40    AfterLocation(Location),
41
42    /// We just dumped the terminator for a block but not the closing `}`.
43    AfterTerminator(BasicBlock),
44}
45
46/// Cosmetic options for pretty-printing the MIR contents, gathered from the CLI. Each pass can
47/// override these when dumping its own specific MIR information with `dump_mir`.
48#[derive(Copy, Clone)]
49pub struct PrettyPrintMirOptions {
50    /// Whether to include extra comments, like span info. From `-Z mir-include-spans`.
51    pub include_extra_comments: bool,
52}
53
54impl PrettyPrintMirOptions {
55    /// Create the default set of MIR pretty-printing options from the CLI flags.
56    pub fn from_cli(tcx: TyCtxt<'_>) -> Self {
57        Self { include_extra_comments: tcx.sess.opts.unstable_opts.mir_include_spans.is_enabled() }
58    }
59}
60
61/// Manages MIR dumping, which is MIR writing done to a file with a specific name. In particular,
62/// it makes it impossible to dump MIR to one of these files when it hasn't been requested from the
63/// command line. Layered on top of `MirWriter`, which does the actual writing.
64pub struct MirDumper<'dis, 'de, 'tcx> {
65    show_pass_num: bool,
66    pass_name: &'static str,
67    disambiguator: &'dis dyn Display,
68    writer: MirWriter<'de, 'tcx>,
69}
70
71impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> {
72    // If dumping should be performed (e.g. because it was requested on the
73    // CLI), returns a `MirDumper` with default values for the following fields:
74    // - `show_pass_num`: `false`
75    // - `disambiguator`: `&0`
76    // - `writer.extra_data`: a no-op
77    // - `writer.options`: default options derived from CLI flags
78    pub fn new(tcx: TyCtxt<'tcx>, pass_name: &'static str, body: &Body<'tcx>) -> Option<Self> {
79        let dump_enabled = if let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir {
80            // see notes on #41697 below
81            let node_path =
82                ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
83            filters.split('|').any(|or_filter| {
84                or_filter.split('&').all(|and_filter| {
85                    let and_filter_trimmed = and_filter.trim();
86                    and_filter_trimmed == "all"
87                        || pass_name.contains(and_filter_trimmed)
88                        || node_path.contains(and_filter_trimmed)
89                })
90            })
91        } else {
92            false
93        };
94
95        dump_enabled.then_some(MirDumper {
96            show_pass_num: false,
97            pass_name,
98            disambiguator: &0,
99            writer: MirWriter::new(tcx),
100        })
101    }
102
103    pub fn tcx(&self) -> TyCtxt<'tcx> {
104        self.writer.tcx
105    }
106
107    #[must_use]
108    pub fn set_show_pass_num(mut self) -> Self {
109        self.show_pass_num = true;
110        self
111    }
112
113    #[must_use]
114    pub fn set_disambiguator(mut self, disambiguator: &'dis dyn Display) -> Self {
115        self.disambiguator = disambiguator;
116        self
117    }
118
119    #[must_use]
120    pub fn set_extra_data(
121        mut self,
122        extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,
123    ) -> Self {
124        self.writer.extra_data = extra_data;
125        self
126    }
127
128    #[must_use]
129    pub fn set_options(mut self, options: PrettyPrintMirOptions) -> Self {
130        self.writer.options = options;
131        self
132    }
133
134    /// If the session is properly configured, dumps a human-readable representation of the MIR
135    /// (with default pretty-printing options) into:
136    ///
137    /// ```text
138    /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
139    /// ```
140    ///
141    /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
142    /// where `<filter>` takes the following forms:
143    ///
144    /// - `all` -- dump MIR for all fns, all passes, all everything
145    /// - a filter defined by a set of substrings combined with `&` and `|`
146    ///   (`&` has higher precedence). At least one of the `|`-separated groups
147    ///   must match; an `|`-separated group matches if all of its `&`-separated
148    ///   substrings are matched.
149    ///
150    /// Example:
151    ///
152    /// - `nll` == match if `nll` appears in the name
153    /// - `foo & nll` == match if `foo` and `nll` both appear in the name
154    /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
155    ///   or `typeck` appears in the name.
156    /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
157    ///   or `typeck` and `bar` both appear in the name.
158    pub fn dump_mir(&self, body: &Body<'tcx>) {
159        let _: io::Result<()> = try {
160            let mut file = self.create_dump_file("mir", body)?;
161            self.dump_mir_to_writer(body, &mut file)?;
162        };
163
164        if self.tcx().sess.opts.unstable_opts.dump_mir_graphviz {
165            let _: io::Result<()> = try {
166                let mut file = self.create_dump_file("dot", body)?;
167                write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?;
168            };
169        }
170    }
171
172    // #41697 -- we use `with_forced_impl_filename_line()` because `def_path_str()` would otherwise
173    // trigger `type_of`, and this can run while we are already attempting to evaluate `type_of`.
174    pub fn dump_mir_to_writer(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {
175        // see notes on #41697 above
176        let def_path = ty::print::with_forced_impl_filename_line!(
177            self.tcx().def_path_str(body.source.def_id())
178        );
179        // ignore-tidy-odd-backticks the literal below is fine
180        write!(w, "// MIR for `{def_path}")?;
181        match body.source.promoted {
182            None => write!(w, "`")?,
183            Some(promoted) => write!(w, "::{promoted:?}`")?,
184        }
185        writeln!(w, " {} {}", self.disambiguator, self.pass_name)?;
186        if let Some(ref layout) = body.coroutine_layout_raw() {
187            writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
188        }
189        writeln!(w)?;
190        (self.writer.extra_data)(PassWhere::BeforeCFG, w)?;
191        write_user_type_annotations(self.tcx(), body, w)?;
192        self.writer.write_mir_fn(body, w)?;
193        (self.writer.extra_data)(PassWhere::AfterCFG, w)
194    }
195
196    /// Returns the path to the filename where we should dump a given MIR.
197    /// Also used by other bits of code (e.g., NLL inference) that dump
198    /// graphviz data or other things.
199    fn dump_path(&self, extension: &str, body: &Body<'tcx>) -> PathBuf {
200        let tcx = self.tcx();
201        let source = body.source;
202        let promotion_id = match source.promoted {
203            Some(id) => format!("-{id:?}"),
204            None => String::new(),
205        };
206
207        let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
208            String::new()
209        } else if self.show_pass_num {
210            let (dialect_index, phase_index) = body.phase.index();
211            format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count)
212        } else {
213            ".-------".to_string()
214        };
215
216        let crate_name = tcx.crate_name(source.def_id().krate);
217        let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
218        // All drop shims have the same DefId, so we have to add the type
219        // to get unique file names.
220        let shim_disambiguator = match source.instance {
221            ty::InstanceKind::DropGlue(_, Some(ty)) => {
222                // Unfortunately, pretty-printed types are not very filename-friendly.
223                // We do some filtering.
224                let mut s = ".".to_owned();
225                s.extend(ty.to_string().chars().filter_map(|c| match c {
226                    ' ' => None,
227                    ':' | '<' | '>' => Some('_'),
228                    c => Some(c),
229                }));
230                s
231            }
232            ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
233                let mut s = ".".to_owned();
234                s.extend(ty.to_string().chars().filter_map(|c| match c {
235                    ' ' => None,
236                    ':' | '<' | '>' => Some('_'),
237                    c => Some(c),
238                }));
239                s
240            }
241            ty::InstanceKind::AsyncDropGlue(_, ty) => {
242                let ty::Coroutine(_, args) = ty.kind() else {
243                    bug!();
244                };
245                let ty = args.first().unwrap().expect_ty();
246                let mut s = ".".to_owned();
247                s.extend(ty.to_string().chars().filter_map(|c| match c {
248                    ' ' => None,
249                    ':' | '<' | '>' => Some('_'),
250                    c => Some(c),
251                }));
252                s
253            }
254            ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {
255                let mut s = ".".to_owned();
256                s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {
257                    ' ' => None,
258                    ':' | '<' | '>' => Some('_'),
259                    c => Some(c),
260                }));
261                s.push('.');
262                s.extend(impl_cor.to_string().chars().filter_map(|c| match c {
263                    ' ' => None,
264                    ':' | '<' | '>' => Some('_'),
265                    c => Some(c),
266                }));
267                s
268            }
269            _ => String::new(),
270        };
271
272        let mut file_path = PathBuf::new();
273        file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
274
275        let pass_name = self.pass_name;
276        let disambiguator = self.disambiguator;
277        let file_name = format!(
278            "{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",
279        );
280
281        file_path.push(&file_name);
282
283        file_path
284    }
285
286    /// Attempts to open a file where we should dump a given MIR or other
287    /// bit of MIR-related data. Used by `mir-dump`, but also by other
288    /// bits of code (e.g., NLL inference) that dump graphviz data or
289    /// other things, and hence takes the extension as an argument.
290    pub fn create_dump_file(
291        &self,
292        extension: &str,
293        body: &Body<'tcx>,
294    ) -> io::Result<io::BufWriter<fs::File>> {
295        let file_path = self.dump_path(extension, body);
296        if let Some(parent) = file_path.parent() {
297            fs::create_dir_all(parent).map_err(|e| {
298                io::Error::new(
299                    e.kind(),
300                    format!("IO error creating MIR dump directory: {parent:?}; {e}"),
301                )
302            })?;
303        }
304        fs::File::create_buffered(&file_path).map_err(|e| {
305            io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
306        })
307    }
308}
309
310///////////////////////////////////////////////////////////////////////////
311// Whole MIR bodies
312
313/// Write out a human-readable textual representation for the given MIR, with the default
314/// [PrettyPrintMirOptions].
315pub fn write_mir_pretty<'tcx>(
316    tcx: TyCtxt<'tcx>,
317    single: Option<DefId>,
318    w: &mut dyn io::Write,
319) -> io::Result<()> {
320    let writer = MirWriter::new(tcx);
321
322    writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
323    writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
324    writeln!(w, "// HINT: See also -Z dump-mir for MIR at specific points during compilation.")?;
325
326    let mut first = true;
327    for def_id in dump_mir_def_ids(tcx, single) {
328        if first {
329            first = false;
330        } else {
331            // Put empty lines between all items
332            writeln!(w)?;
333        }
334
335        let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
336            writer.write_mir_fn(body, w)?;
337
338            for body in tcx.promoted_mir(def_id) {
339                writeln!(w)?;
340                writer.write_mir_fn(body, w)?;
341            }
342            Ok(())
343        };
344
345        // For `const fn` we want to render both the optimized MIR and the MIR for ctfe.
346        if tcx.is_const_fn(def_id) {
347            render_body(w, tcx.optimized_mir(def_id))?;
348            writeln!(w)?;
349            writeln!(w, "// MIR FOR CTFE")?;
350            // Do not use `render_body`, as that would render the promoteds again, but these
351            // are shared between mir_for_ctfe and optimized_mir
352            writer.write_mir_fn(tcx.mir_for_ctfe(def_id), w)?;
353        } else {
354            let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
355            render_body(w, instance_mir)?;
356        }
357    }
358    Ok(())
359}
360
361/// Does the writing of MIR to output, e.g. a file.
362pub struct MirWriter<'de, 'tcx> {
363    tcx: TyCtxt<'tcx>,
364    extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,
365    options: PrettyPrintMirOptions,
366}
367
368impl<'de, 'tcx> MirWriter<'de, 'tcx> {
369    pub fn new(tcx: TyCtxt<'tcx>) -> Self {
370        MirWriter { tcx, extra_data: &|_, _| Ok(()), options: PrettyPrintMirOptions::from_cli(tcx) }
371    }
372
373    /// Write out a human-readable textual representation for the given function.
374    pub fn write_mir_fn(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {
375        write_mir_intro(self.tcx, body, w, self.options)?;
376        for block in body.basic_blocks.indices() {
377            (self.extra_data)(PassWhere::BeforeBlock(block), w)?;
378            self.write_basic_block(block, body, w)?;
379            if block.index() + 1 != body.basic_blocks.len() {
380                writeln!(w)?;
381            }
382        }
383
384        writeln!(w, "}}")?;
385
386        write_allocations(self.tcx, body, w)?;
387
388        Ok(())
389    }
390}
391
392/// Prints local variables in a scope tree.
393fn write_scope_tree(
394    tcx: TyCtxt<'_>,
395    body: &Body<'_>,
396    scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
397    w: &mut dyn io::Write,
398    parent: SourceScope,
399    depth: usize,
400    options: PrettyPrintMirOptions,
401) -> io::Result<()> {
402    let indent = depth * INDENT.len();
403
404    // Local variable debuginfo.
405    for var_debug_info in &body.var_debug_info {
406        if var_debug_info.source_info.scope != parent {
407            // Not declared in this scope.
408            continue;
409        }
410
411        let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
412
413        if options.include_extra_comments {
414            writeln!(
415                w,
416                "{0:1$} // in {2}",
417                indented_debug_info,
418                ALIGN,
419                comment(tcx, var_debug_info.source_info),
420            )?;
421        } else {
422            writeln!(w, "{indented_debug_info}")?;
423        }
424    }
425
426    // Local variable types.
427    for (local, local_decl) in body.local_decls.iter_enumerated() {
428        if (1..body.arg_count + 1).contains(&local.index()) {
429            // Skip over argument locals, they're printed in the signature.
430            continue;
431        }
432
433        if local_decl.source_info.scope != parent {
434            // Not declared in this scope.
435            continue;
436        }
437
438        let mut_str = local_decl.mutability.prefix_str();
439
440        let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(
441            "{0:1$}let {2}{3:?}: {4}",
442            INDENT, indent, mut_str, local, local_decl.ty
443        ));
444        if let Some(user_ty) = &local_decl.user_ty {
445            for user_ty in user_ty.projections() {
446                write!(indented_decl, " as {user_ty:?}").unwrap();
447            }
448        }
449        indented_decl.push(';');
450
451        let local_name = if local == RETURN_PLACE { " return place" } else { "" };
452
453        if options.include_extra_comments {
454            writeln!(
455                w,
456                "{0:1$} //{2} in {3}",
457                indented_decl,
458                ALIGN,
459                local_name,
460                comment(tcx, local_decl.source_info),
461            )?;
462        } else {
463            writeln!(w, "{indented_decl}",)?;
464        }
465    }
466
467    let Some(children) = scope_tree.get(&parent) else {
468        return Ok(());
469    };
470
471    for &child in children {
472        let child_data = &body.source_scopes[child];
473        assert_eq!(child_data.parent_scope, Some(parent));
474
475        let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
476            (
477                format!(
478                    " (inlined {}{})",
479                    if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
480                    callee
481                ),
482                Some(callsite_span),
483            )
484        } else {
485            (String::new(), None)
486        };
487
488        let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
489
490        if options.include_extra_comments {
491            if let Some(span) = span {
492                writeln!(
493                    w,
494                    "{0:1$} // at {2}",
495                    indented_header,
496                    ALIGN,
497                    tcx.sess.source_map().span_to_embeddable_string(span),
498                )?;
499            } else {
500                writeln!(w, "{indented_header}")?;
501            }
502        } else {
503            writeln!(w, "{indented_header}")?;
504        }
505
506        write_scope_tree(tcx, body, scope_tree, w, child, depth + 1, options)?;
507        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
508    }
509
510    Ok(())
511}
512
513impl Debug for VarDebugInfo<'_> {
514    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
515        if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
516            pre_fmt_projection(&projection[..], fmt)?;
517            write!(fmt, "({}: {})", self.name, ty)?;
518            post_fmt_projection(&projection[..], fmt)?;
519        } else {
520            write!(fmt, "{}", self.name)?;
521        }
522
523        write!(fmt, " => {:?}", self.value)
524    }
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).
529fn write_mir_intro<'tcx>(
530    tcx: TyCtxt<'tcx>,
531    body: &Body<'_>,
532    w: &mut dyn io::Write,
533    options: PrettyPrintMirOptions,
534) -> io::Result<()> {
535    write_mir_sig(tcx, body, 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 body.source_scopes.iter_enumerated() {
541        if let Some(parent) = scope_data.parent_scope {
542            scope_tree.entry(parent).or_default().push(index);
543        } else {
544            // Only the argument scope has no parent, because it's the root.
545            assert_eq!(index, OUTERMOST_SOURCE_SCOPE);
546        }
547    }
548
549    write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1, options)?;
550
551    // Add an empty line before the first block is printed.
552    writeln!(w)?;
553
554    if let Some(coverage_info_hi) = &body.coverage_info_hi {
555        write_coverage_info_hi(coverage_info_hi, w)?;
556    }
557    if let Some(function_coverage_info) = &body.function_coverage_info {
558        write_function_coverage_info(function_coverage_info, w)?;
559    }
560
561    Ok(())
562}
563
564fn write_coverage_info_hi(
565    coverage_info_hi: &coverage::CoverageInfoHi,
566    w: &mut dyn io::Write,
567) -> io::Result<()> {
568    let coverage::CoverageInfoHi { num_block_markers: _, branch_spans } = coverage_info_hi;
569
570    // Only add an extra trailing newline if we printed at least one thing.
571    let mut did_print = false;
572
573    for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {
574        writeln!(
575            w,
576            "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
577        )?;
578        did_print = true;
579    }
580
581    if did_print {
582        writeln!(w)?;
583    }
584
585    Ok(())
586}
587
588fn write_function_coverage_info(
589    function_coverage_info: &coverage::FunctionCoverageInfo,
590    w: &mut dyn io::Write,
591) -> io::Result<()> {
592    let coverage::FunctionCoverageInfo { mappings, .. } = function_coverage_info;
593
594    for coverage::Mapping { kind, span } in mappings {
595        writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?;
596    }
597    writeln!(w)?;
598
599    Ok(())
600}
601
602fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io::Result<()> {
603    use rustc_hir::def::DefKind;
604
605    trace!("write_mir_sig: {:?}", body.source.instance);
606    let def_id = body.source.def_id();
607    let kind = tcx.def_kind(def_id);
608    let is_function = match kind {
609        DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::SyntheticCoroutineBody => {
610            true
611        }
612        _ => tcx.is_closure_like(def_id),
613    };
614    match (kind, body.source.promoted) {
615        (_, Some(_)) => write!(w, "const ")?, // promoteds are the closest to consts
616        (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
617        (DefKind::Static { safety: _, mutability: hir::Mutability::Not, nested: false }, _) => {
618            write!(w, "static ")?
619        }
620        (DefKind::Static { safety: _, mutability: hir::Mutability::Mut, nested: false }, _) => {
621            write!(w, "static mut ")?
622        }
623        (_, _) if is_function => write!(w, "fn ")?,
624        // things like anon const, not an item
625        (DefKind::AnonConst | DefKind::InlineConst, _) => {}
626        // `global_asm!` have fake bodies, which we may dump after mir-build
627        (DefKind::GlobalAsm, _) => {}
628        _ => bug!("Unexpected def kind {:?}", kind),
629    }
630
631    ty::print::with_forced_impl_filename_line! {
632        // see notes on #41697 elsewhere
633        write!(w, "{}", tcx.def_path_str(def_id))?
634    }
635    if let Some(p) = body.source.promoted {
636        write!(w, "::{p:?}")?;
637    }
638
639    if body.source.promoted.is_none() && is_function {
640        write!(w, "(")?;
641
642        // fn argument types.
643        for (i, arg) in body.args_iter().enumerate() {
644            if i != 0 {
645                write!(w, ", ")?;
646            }
647            write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
648        }
649
650        write!(w, ") -> {}", body.return_ty())?;
651    } else {
652        assert_eq!(body.arg_count, 0);
653        write!(w, ": {} =", body.return_ty())?;
654    }
655
656    if let Some(yield_ty) = body.yield_ty() {
657        writeln!(w)?;
658        writeln!(w, "yields {yield_ty}")?;
659    }
660
661    write!(w, " ")?;
662    // Next thing that gets printed is the opening {
663
664    Ok(())
665}
666
667fn write_user_type_annotations(
668    tcx: TyCtxt<'_>,
669    body: &Body<'_>,
670    w: &mut dyn io::Write,
671) -> io::Result<()> {
672    if !body.user_type_annotations.is_empty() {
673        writeln!(w, "| User Type Annotations")?;
674    }
675    for (index, annotation) in body.user_type_annotations.iter_enumerated() {
676        writeln!(
677            w,
678            "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
679            index.index(),
680            annotation.user_ty,
681            tcx.sess.source_map().span_to_embeddable_string(annotation.span),
682            with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
683        )?;
684    }
685    if !body.user_type_annotations.is_empty() {
686        writeln!(w, "|")?;
687    }
688    Ok(())
689}
690
691pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
692    if let Some(i) = single {
693        vec![i]
694    } else {
695        tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
696    }
697}
698
699///////////////////////////////////////////////////////////////////////////
700// Basic blocks and their parts (statements, terminators, ...)
701
702impl<'de, 'tcx> MirWriter<'de, 'tcx> {
703    /// Write out a human-readable textual representation for the given basic block.
704    fn write_basic_block(
705        &self,
706        block: BasicBlock,
707        body: &Body<'tcx>,
708        w: &mut dyn io::Write,
709    ) -> io::Result<()> {
710        let data = &body[block];
711
712        // Basic block label at the top.
713        let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
714        writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
715
716        // List of statements in the middle.
717        let mut current_location = Location { block, statement_index: 0 };
718        for statement in &data.statements {
719            (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
720            let indented_body = format!("{INDENT}{INDENT}{statement:?};");
721            if self.options.include_extra_comments {
722                writeln!(
723                    w,
724                    "{:A$} // {}{}",
725                    indented_body,
726                    if self.tcx.sess.verbose_internals() {
727                        format!("{current_location:?}: ")
728                    } else {
729                        String::new()
730                    },
731                    comment(self.tcx, statement.source_info),
732                    A = ALIGN,
733                )?;
734            } else {
735                writeln!(w, "{indented_body}")?;
736            }
737
738            write_extra(
739                self.tcx,
740                w,
741                &|visitor| visitor.visit_statement(statement, current_location),
742                self.options,
743            )?;
744
745            (self.extra_data)(PassWhere::AfterLocation(current_location), w)?;
746
747            current_location.statement_index += 1;
748        }
749
750        // Terminator at the bottom.
751        (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
752        if data.terminator.is_some() {
753            let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
754            if self.options.include_extra_comments {
755                writeln!(
756                    w,
757                    "{:A$} // {}{}",
758                    indented_terminator,
759                    if self.tcx.sess.verbose_internals() {
760                        format!("{current_location:?}: ")
761                    } else {
762                        String::new()
763                    },
764                    comment(self.tcx, data.terminator().source_info),
765                    A = ALIGN,
766                )?;
767            } else {
768                writeln!(w, "{indented_terminator}")?;
769            }
770
771            write_extra(
772                self.tcx,
773                w,
774                &|visitor| visitor.visit_terminator(data.terminator(), current_location),
775                self.options,
776            )?;
777        }
778
779        (self.extra_data)(PassWhere::AfterLocation(current_location), w)?;
780        (self.extra_data)(PassWhere::AfterTerminator(block), w)?;
781
782        writeln!(w, "{INDENT}}}")
783    }
784}
785
786impl Debug for Statement<'_> {
787    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
788        use self::StatementKind::*;
789        match self.kind {
790            Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),
791            FakeRead(box (ref cause, ref place)) => {
792                write!(fmt, "FakeRead({cause:?}, {place:?})")
793            }
794            Retag(ref kind, ref place) => write!(
795                fmt,
796                "Retag({}{:?})",
797                match kind {
798                    RetagKind::FnEntry => "[fn entry] ",
799                    RetagKind::TwoPhase => "[2phase] ",
800                    RetagKind::Raw => "[raw] ",
801                    RetagKind::Default => "",
802                },
803                place,
804            ),
805            StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),
806            StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),
807            SetDiscriminant { ref place, variant_index } => {
808                write!(fmt, "discriminant({place:?}) = {variant_index:?}")
809            }
810            Deinit(ref place) => write!(fmt, "Deinit({place:?})"),
811            PlaceMention(ref place) => {
812                write!(fmt, "PlaceMention({place:?})")
813            }
814            AscribeUserType(box (ref place, ref c_ty), ref variance) => {
815                write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
816            }
817            Coverage(ref kind) => write!(fmt, "Coverage::{kind:?}"),
818            Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
819            ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
820            Nop => write!(fmt, "nop"),
821            BackwardIncompatibleDropHint { ref place, reason: _ } => {
822                // For now, we don't record the reason because there is only one use case,
823                // which is to report breaking change in drop order by Edition 2024
824                write!(fmt, "BackwardIncompatibleDropHint({place:?})")
825            }
826        }
827    }
828}
829
830impl Display for NonDivergingIntrinsic<'_> {
831    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
832        match self {
833            Self::Assume(op) => write!(f, "assume({op:?})"),
834            Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => {
835                write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})")
836            }
837        }
838    }
839}
840
841impl<'tcx> Debug for TerminatorKind<'tcx> {
842    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
843        self.fmt_head(fmt)?;
844        let successor_count = self.successors().count();
845        let labels = self.fmt_successor_labels();
846        assert_eq!(successor_count, labels.len());
847
848        // `Cleanup` is already included in successors
849        let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
850        let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
851            write!(fmt, "unwind ")?;
852            match self.unwind() {
853                // Not needed or included in successors
854                None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
855                Some(UnwindAction::Continue) => write!(fmt, "continue"),
856                Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
857                Some(UnwindAction::Terminate(reason)) => {
858                    write!(fmt, "terminate({})", reason.as_short_str())
859                }
860            }
861        };
862
863        match (successor_count, show_unwind) {
864            (0, false) => Ok(()),
865            (0, true) => {
866                write!(fmt, " -> ")?;
867                fmt_unwind(fmt)
868            }
869            (1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
870            _ => {
871                write!(fmt, " -> [")?;
872                for (i, target) in self.successors().enumerate() {
873                    if i > 0 {
874                        write!(fmt, ", ")?;
875                    }
876                    write!(fmt, "{}: {:?}", labels[i], target)?;
877                }
878                if show_unwind {
879                    write!(fmt, ", ")?;
880                    fmt_unwind(fmt)?;
881                }
882                write!(fmt, "]")
883            }
884        }
885    }
886}
887
888impl<'tcx> TerminatorKind<'tcx> {
889    /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
890    /// successor basic block, if any. The only information not included is the list of possible
891    /// successors, which may be rendered differently between the text and the graphviz format.
892    pub fn fmt_head<W: fmt::Write>(&self, fmt: &mut W) -> fmt::Result {
893        use self::TerminatorKind::*;
894        match self {
895            Goto { .. } => write!(fmt, "goto"),
896            SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
897            Return => write!(fmt, "return"),
898            CoroutineDrop => write!(fmt, "coroutine_drop"),
899            UnwindResume => write!(fmt, "resume"),
900            UnwindTerminate(reason) => {
901                write!(fmt, "terminate({})", reason.as_short_str())
902            }
903            Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
904            Unreachable => write!(fmt, "unreachable"),
905            Drop { place, async_fut: None, .. } => write!(fmt, "drop({place:?})"),
906            Drop { place, async_fut: Some(async_fut), .. } => {
907                write!(fmt, "async drop({place:?}; poll={async_fut:?})")
908            }
909            Call { func, args, destination, .. } => {
910                write!(fmt, "{destination:?} = ")?;
911                write!(fmt, "{func:?}(")?;
912                for (index, arg) in args.iter().enumerate() {
913                    if index > 0 {
914                        write!(fmt, ", ")?;
915                    }
916                    write!(fmt, "{:?}", arg.node)?;
917                }
918                write!(fmt, ")")
919            }
920            TailCall { func, args, .. } => {
921                write!(fmt, "tailcall {func:?}(")?;
922                for (index, arg) in args.iter().enumerate() {
923                    if index > 0 {
924                        write!(fmt, ", ")?;
925                    }
926                    write!(fmt, "{:?}", arg.node)?;
927                }
928                write!(fmt, ")")
929            }
930            Assert { cond, expected, msg, .. } => {
931                write!(fmt, "assert(")?;
932                if !expected {
933                    write!(fmt, "!")?;
934                }
935                write!(fmt, "{cond:?}, ")?;
936                msg.fmt_assert_args(fmt)?;
937                write!(fmt, ")")
938            }
939            FalseEdge { .. } => write!(fmt, "falseEdge"),
940            FalseUnwind { .. } => write!(fmt, "falseUnwind"),
941            InlineAsm { template, operands, options, .. } => {
942                write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
943                for op in operands {
944                    write!(fmt, ", ")?;
945                    let print_late = |&late| if late { "late" } else { "" };
946                    match op {
947                        InlineAsmOperand::In { reg, value } => {
948                            write!(fmt, "in({reg}) {value:?}")?;
949                        }
950                        InlineAsmOperand::Out { reg, late, place: Some(place) } => {
951                            write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
952                        }
953                        InlineAsmOperand::Out { reg, late, place: None } => {
954                            write!(fmt, "{}out({}) _", print_late(late), reg)?;
955                        }
956                        InlineAsmOperand::InOut {
957                            reg,
958                            late,
959                            in_value,
960                            out_place: Some(out_place),
961                        } => {
962                            write!(
963                                fmt,
964                                "in{}out({}) {:?} => {:?}",
965                                print_late(late),
966                                reg,
967                                in_value,
968                                out_place
969                            )?;
970                        }
971                        InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
972                            write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
973                        }
974                        InlineAsmOperand::Const { value } => {
975                            write!(fmt, "const {value:?}")?;
976                        }
977                        InlineAsmOperand::SymFn { value } => {
978                            write!(fmt, "sym_fn {value:?}")?;
979                        }
980                        InlineAsmOperand::SymStatic { def_id } => {
981                            write!(fmt, "sym_static {def_id:?}")?;
982                        }
983                        InlineAsmOperand::Label { target_index } => {
984                            write!(fmt, "label {target_index}")?;
985                        }
986                    }
987                }
988                write!(fmt, ", options({options:?}))")
989            }
990        }
991    }
992
993    /// Returns the list of labels for the edges to the successor basic blocks.
994    pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
995        use self::TerminatorKind::*;
996        match *self {
997            Return
998            | TailCall { .. }
999            | UnwindResume
1000            | UnwindTerminate(_)
1001            | Unreachable
1002            | CoroutineDrop => vec![],
1003            Goto { .. } => vec!["".into()],
1004            SwitchInt { ref targets, .. } => targets
1005                .values
1006                .iter()
1007                .map(|&u| Cow::Owned(u.to_string()))
1008                .chain(iter::once("otherwise".into()))
1009                .collect(),
1010            Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
1011                vec!["return".into(), "unwind".into()]
1012            }
1013            Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
1014            Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
1015            Call { target: None, unwind: _, .. } => vec![],
1016            Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
1017            Yield { drop: None, .. } => vec!["resume".into()],
1018            Drop { unwind: UnwindAction::Cleanup(_), drop: Some(_), .. } => {
1019                vec!["return".into(), "unwind".into(), "drop".into()]
1020            }
1021            Drop { unwind: UnwindAction::Cleanup(_), drop: None, .. } => {
1022                vec!["return".into(), "unwind".into()]
1023            }
1024            Drop { unwind: _, drop: Some(_), .. } => vec!["return".into(), "drop".into()],
1025            Drop { unwind: _, .. } => vec!["return".into()],
1026            Assert { unwind: UnwindAction::Cleanup(_), .. } => {
1027                vec!["success".into(), "unwind".into()]
1028            }
1029            Assert { unwind: _, .. } => vec!["success".into()],
1030            FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
1031            FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {
1032                vec!["real".into(), "unwind".into()]
1033            }
1034            FalseUnwind { unwind: _, .. } => vec!["real".into()],
1035            InlineAsm { asm_macro, options, ref targets, unwind, .. } => {
1036                let mut vec = Vec::with_capacity(targets.len() + 1);
1037                if !asm_macro.diverges(options) {
1038                    vec.push("return".into());
1039                }
1040                vec.resize(targets.len(), "label".into());
1041
1042                if let UnwindAction::Cleanup(_) = unwind {
1043                    vec.push("unwind".into());
1044                }
1045
1046                vec
1047            }
1048        }
1049    }
1050}
1051
1052impl<'tcx> Debug for Rvalue<'tcx> {
1053    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1054        use self::Rvalue::*;
1055
1056        match *self {
1057            Use(ref place) => write!(fmt, "{place:?}"),
1058            Repeat(ref a, b) => {
1059                write!(fmt, "[{a:?}; ")?;
1060                pretty_print_const(b, fmt, false)?;
1061                write!(fmt, "]")
1062            }
1063            Len(ref a) => write!(fmt, "Len({a:?})"),
1064            Cast(ref kind, ref place, ref ty) => {
1065                with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
1066            }
1067            BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
1068            UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
1069            Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
1070            NullaryOp(ref op, ref t) => {
1071                let t = with_no_trimmed_paths!(format!("{}", t));
1072                match op {
1073                    NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
1074                    NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
1075                    NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
1076                    NullOp::UbChecks => write!(fmt, "UbChecks()"),
1077                    NullOp::ContractChecks => write!(fmt, "ContractChecks()"),
1078                }
1079            }
1080            ThreadLocalRef(did) => ty::tls::with(|tcx| {
1081                let muta = tcx.static_mutability(did).unwrap().prefix_str();
1082                write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
1083            }),
1084            Ref(region, borrow_kind, ref place) => {
1085                let kind_str = match borrow_kind {
1086                    BorrowKind::Shared => "",
1087                    BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ",
1088                    BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ",
1089                    BorrowKind::Mut { .. } => "mut ",
1090                };
1091
1092                // When printing regions, add trailing space if necessary.
1093                let print_region = ty::tls::with(|tcx| {
1094                    tcx.sess.verbose_internals() || tcx.sess.opts.unstable_opts.identify_regions
1095                });
1096                let region = if print_region {
1097                    let mut region = region.to_string();
1098                    if !region.is_empty() {
1099                        region.push(' ');
1100                    }
1101                    region
1102                } else {
1103                    // Do not even print 'static
1104                    String::new()
1105                };
1106                write!(fmt, "&{region}{kind_str}{place:?}")
1107            }
1108
1109            CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
1110
1111            RawPtr(mutability, ref place) => {
1112                write!(fmt, "&raw {mut_str} {place:?}", mut_str = mutability.ptr_str())
1113            }
1114
1115            Aggregate(ref kind, ref places) => {
1116                let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
1117                    let mut tuple_fmt = fmt.debug_tuple(name);
1118                    for place in places {
1119                        tuple_fmt.field(place);
1120                    }
1121                    tuple_fmt.finish()
1122                };
1123
1124                match **kind {
1125                    AggregateKind::Array(_) => write!(fmt, "{places:?}"),
1126
1127                    AggregateKind::Tuple => {
1128                        if places.is_empty() {
1129                            write!(fmt, "()")
1130                        } else {
1131                            fmt_tuple(fmt, "")
1132                        }
1133                    }
1134
1135                    AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {
1136                        ty::tls::with(|tcx| {
1137                            let variant_def = &tcx.adt_def(adt_did).variant(variant);
1138                            let args = tcx.lift(args).expect("could not lift for printing");
1139                            let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |p| {
1140                                p.print_def_path(variant_def.def_id, args)
1141                            })?;
1142
1143                            match variant_def.ctor_kind() {
1144                                Some(CtorKind::Const) => fmt.write_str(&name),
1145                                Some(CtorKind::Fn) => fmt_tuple(fmt, &name),
1146                                None => {
1147                                    let mut struct_fmt = fmt.debug_struct(&name);
1148                                    for (field, place) in iter::zip(&variant_def.fields, places) {
1149                                        struct_fmt.field(field.name.as_str(), place);
1150                                    }
1151                                    struct_fmt.finish()
1152                                }
1153                            }
1154                        })
1155                    }
1156
1157                    AggregateKind::Closure(def_id, args)
1158                    | AggregateKind::CoroutineClosure(def_id, args) => ty::tls::with(|tcx| {
1159                        let name = if tcx.sess.opts.unstable_opts.span_free_formats {
1160                            let args = tcx.lift(args).unwrap();
1161                            format!("{{closure@{}}}", tcx.def_path_str_with_args(def_id, args),)
1162                        } else {
1163                            let span = tcx.def_span(def_id);
1164                            format!(
1165                                "{{closure@{}}}",
1166                                tcx.sess.source_map().span_to_diagnostic_string(span)
1167                            )
1168                        };
1169                        let mut struct_fmt = fmt.debug_struct(&name);
1170
1171                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places
1172                        if let Some(def_id) = def_id.as_local()
1173                            && let Some(upvars) = tcx.upvars_mentioned(def_id)
1174                        {
1175                            for (&var_id, place) in iter::zip(upvars.keys(), places) {
1176                                let var_name = tcx.hir_name(var_id);
1177                                struct_fmt.field(var_name.as_str(), place);
1178                            }
1179                        } else {
1180                            for (index, place) in places.iter().enumerate() {
1181                                struct_fmt.field(&format!("{index}"), place);
1182                            }
1183                        }
1184
1185                        struct_fmt.finish()
1186                    }),
1187
1188                    AggregateKind::Coroutine(def_id, _) => ty::tls::with(|tcx| {
1189                        let name = format!("{{coroutine@{:?}}}", tcx.def_span(def_id));
1190                        let mut struct_fmt = fmt.debug_struct(&name);
1191
1192                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places
1193                        if let Some(def_id) = def_id.as_local()
1194                            && let Some(upvars) = tcx.upvars_mentioned(def_id)
1195                        {
1196                            for (&var_id, place) in iter::zip(upvars.keys(), places) {
1197                                let var_name = tcx.hir_name(var_id);
1198                                struct_fmt.field(var_name.as_str(), place);
1199                            }
1200                        } else {
1201                            for (index, place) in places.iter().enumerate() {
1202                                struct_fmt.field(&format!("{index}"), place);
1203                            }
1204                        }
1205
1206                        struct_fmt.finish()
1207                    }),
1208
1209                    AggregateKind::RawPtr(pointee_ty, mutability) => {
1210                        let kind_str = match mutability {
1211                            Mutability::Mut => "mut",
1212                            Mutability::Not => "const",
1213                        };
1214                        with_no_trimmed_paths!(write!(fmt, "*{kind_str} {pointee_ty} from "))?;
1215                        fmt_tuple(fmt, "")
1216                    }
1217                }
1218            }
1219
1220            ShallowInitBox(ref place, ref ty) => {
1221                with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
1222            }
1223
1224            WrapUnsafeBinder(ref op, ty) => {
1225                with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})"))
1226            }
1227        }
1228    }
1229}
1230
1231impl<'tcx> Debug for Operand<'tcx> {
1232    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1233        use self::Operand::*;
1234        match *self {
1235            Constant(ref a) => write!(fmt, "{a:?}"),
1236            Copy(ref place) => write!(fmt, "copy {place:?}"),
1237            Move(ref place) => write!(fmt, "move {place:?}"),
1238        }
1239    }
1240}
1241
1242impl<'tcx> Debug for ConstOperand<'tcx> {
1243    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1244        write!(fmt, "{self}")
1245    }
1246}
1247
1248impl<'tcx> Display for ConstOperand<'tcx> {
1249    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1250        match self.ty().kind() {
1251            ty::FnDef(..) => {}
1252            _ => write!(fmt, "const ")?,
1253        }
1254        Display::fmt(&self.const_, fmt)
1255    }
1256}
1257
1258impl Debug for Place<'_> {
1259    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1260        self.as_ref().fmt(fmt)
1261    }
1262}
1263
1264impl Debug for PlaceRef<'_> {
1265    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1266        pre_fmt_projection(self.projection, fmt)?;
1267        write!(fmt, "{:?}", self.local)?;
1268        post_fmt_projection(self.projection, fmt)
1269    }
1270}
1271
1272fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
1273    for &elem in projection.iter().rev() {
1274        match elem {
1275            ProjectionElem::OpaqueCast(_)
1276            | ProjectionElem::Subtype(_)
1277            | ProjectionElem::Downcast(_, _)
1278            | ProjectionElem::Field(_, _) => {
1279                write!(fmt, "(")?;
1280            }
1281            ProjectionElem::Deref => {
1282                write!(fmt, "(*")?;
1283            }
1284            ProjectionElem::Index(_)
1285            | ProjectionElem::ConstantIndex { .. }
1286            | ProjectionElem::Subslice { .. } => {}
1287            ProjectionElem::UnwrapUnsafeBinder(_) => {
1288                write!(fmt, "unwrap_binder!(")?;
1289            }
1290        }
1291    }
1292
1293    Ok(())
1294}
1295
1296fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
1297    for &elem in projection.iter() {
1298        match elem {
1299            ProjectionElem::OpaqueCast(ty) => {
1300                write!(fmt, " as {ty})")?;
1301            }
1302            ProjectionElem::Subtype(ty) => {
1303                write!(fmt, " as subtype {ty})")?;
1304            }
1305            ProjectionElem::Downcast(Some(name), _index) => {
1306                write!(fmt, " as {name})")?;
1307            }
1308            ProjectionElem::Downcast(None, index) => {
1309                write!(fmt, " as variant#{index:?})")?;
1310            }
1311            ProjectionElem::Deref => {
1312                write!(fmt, ")")?;
1313            }
1314            ProjectionElem::Field(field, ty) => {
1315                with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
1316            }
1317            ProjectionElem::Index(ref index) => {
1318                write!(fmt, "[{index:?}]")?;
1319            }
1320            ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
1321                write!(fmt, "[{offset:?} of {min_length:?}]")?;
1322            }
1323            ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
1324                write!(fmt, "[-{offset:?} of {min_length:?}]")?;
1325            }
1326            ProjectionElem::Subslice { from, to: 0, from_end: true } => {
1327                write!(fmt, "[{from:?}:]")?;
1328            }
1329            ProjectionElem::Subslice { from: 0, to, from_end: true } => {
1330                write!(fmt, "[:-{to:?}]")?;
1331            }
1332            ProjectionElem::Subslice { from, to, from_end: true } => {
1333                write!(fmt, "[{from:?}:-{to:?}]")?;
1334            }
1335            ProjectionElem::Subslice { from, to, from_end: false } => {
1336                write!(fmt, "[{from:?}..{to:?}]")?;
1337            }
1338            ProjectionElem::UnwrapUnsafeBinder(ty) => {
1339                write!(fmt, "; {ty})")?;
1340            }
1341        }
1342    }
1343
1344    Ok(())
1345}
1346
1347/// After we print the main statement, we sometimes dump extra
1348/// information. There's often a lot of little things "nuzzled up" in
1349/// a statement.
1350fn write_extra<'tcx>(
1351    tcx: TyCtxt<'tcx>,
1352    write: &mut dyn io::Write,
1353    visit_op: &dyn Fn(&mut ExtraComments<'tcx>),
1354    options: PrettyPrintMirOptions,
1355) -> io::Result<()> {
1356    if options.include_extra_comments {
1357        let mut extra_comments = ExtraComments { tcx, comments: vec![] };
1358        visit_op(&mut extra_comments);
1359        for comment in extra_comments.comments {
1360            writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
1361        }
1362    }
1363    Ok(())
1364}
1365
1366struct ExtraComments<'tcx> {
1367    tcx: TyCtxt<'tcx>,
1368    comments: Vec<String>,
1369}
1370
1371impl<'tcx> ExtraComments<'tcx> {
1372    fn push(&mut self, lines: &str) {
1373        for line in lines.split('\n') {
1374            self.comments.push(line.to_string());
1375        }
1376    }
1377}
1378
1379fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
1380    match *ty.kind() {
1381        ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
1382        // Unit type
1383        ty::Tuple(g_args) if g_args.is_empty() => false,
1384        ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),
1385        ty::Array(ty, _) => use_verbose(ty, fn_def),
1386        ty::FnDef(..) => fn_def,
1387        _ => true,
1388    }
1389}
1390
1391impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
1392    fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _location: Location) {
1393        let ConstOperand { span, user_ty, const_ } = constant;
1394        if use_verbose(const_.ty(), true) {
1395            self.push("mir::ConstOperand");
1396            self.push(&format!(
1397                "+ span: {}",
1398                self.tcx.sess.source_map().span_to_embeddable_string(*span)
1399            ));
1400            if let Some(user_ty) = user_ty {
1401                self.push(&format!("+ user_ty: {user_ty:?}"));
1402            }
1403
1404            let fmt_val = |val: ConstValue, ty: Ty<'tcx>| {
1405                let tcx = self.tcx;
1406                rustc_data_structures::make_display(move |fmt| {
1407                    pretty_print_const_value_tcx(tcx, val, ty, fmt)
1408                })
1409            };
1410
1411            let fmt_valtree = |cv: &ty::Value<'tcx>| {
1412                let mut p = FmtPrinter::new(self.tcx, Namespace::ValueNS);
1413                p.pretty_print_const_valtree(*cv, /*print_ty*/ true).unwrap();
1414                p.into_buffer()
1415            };
1416
1417            let val = match const_ {
1418                Const::Ty(_, ct) => match ct.kind() {
1419                    ty::ConstKind::Param(p) => format!("ty::Param({p})"),
1420                    ty::ConstKind::Unevaluated(uv) => {
1421                        format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
1422                    }
1423                    ty::ConstKind::Value(cv) => {
1424                        format!("ty::Valtree({})", fmt_valtree(&cv))
1425                    }
1426                    // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
1427                    ty::ConstKind::Error(_) => "Error".to_string(),
1428                    // These variants shouldn't exist in the MIR.
1429                    ty::ConstKind::Placeholder(_)
1430                    | ty::ConstKind::Infer(_)
1431                    | ty::ConstKind::Expr(_)
1432                    | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", const_),
1433                },
1434                Const::Unevaluated(uv, _) => {
1435                    format!(
1436                        "Unevaluated({}, {:?}, {:?})",
1437                        self.tcx.def_path_str(uv.def),
1438                        uv.args,
1439                        uv.promoted,
1440                    )
1441                }
1442                Const::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)),
1443            };
1444
1445            // This reflects what `Const` looked liked before `val` was renamed
1446            // as `kind`. We print it like this to avoid having to update
1447            // expected output in a lot of tests.
1448            self.push(&format!("+ const_: Const {{ ty: {}, val: {} }}", const_.ty(), val));
1449        }
1450    }
1451
1452    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1453        self.super_rvalue(rvalue, location);
1454        if let Rvalue::Aggregate(kind, _) = rvalue {
1455            match **kind {
1456                AggregateKind::Closure(def_id, args) => {
1457                    self.push("closure");
1458                    self.push(&format!("+ def_id: {def_id:?}"));
1459                    self.push(&format!("+ args: {args:#?}"));
1460                }
1461
1462                AggregateKind::Coroutine(def_id, args) => {
1463                    self.push("coroutine");
1464                    self.push(&format!("+ def_id: {def_id:?}"));
1465                    self.push(&format!("+ args: {args:#?}"));
1466                    self.push(&format!("+ kind: {:?}", self.tcx.coroutine_kind(def_id)));
1467                }
1468
1469                AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
1470                    self.push("adt");
1471                    self.push(&format!("+ user_ty: {user_ty:?}"));
1472                }
1473
1474                _ => {}
1475            }
1476        }
1477    }
1478}
1479
1480fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
1481    let location = tcx.sess.source_map().span_to_embeddable_string(span);
1482    format!("scope {} at {}", scope.index(), location,)
1483}
1484
1485///////////////////////////////////////////////////////////////////////////
1486// Allocations
1487
1488/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
1489/// allocations.
1490pub fn write_allocations<'tcx>(
1491    tcx: TyCtxt<'tcx>,
1492    body: &Body<'_>,
1493    w: &mut dyn io::Write,
1494) -> io::Result<()> {
1495    fn alloc_ids_from_alloc(
1496        alloc: ConstAllocation<'_>,
1497    ) -> impl DoubleEndedIterator<Item = AllocId> {
1498        alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id())
1499    }
1500
1501    fn alloc_id_from_const_val(val: ConstValue) -> Option<AllocId> {
1502        match val {
1503            ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()),
1504            ConstValue::Scalar(interpret::Scalar::Int { .. }) => None,
1505            ConstValue::ZeroSized => None,
1506            ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => {
1507                // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
1508                // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.
1509                Some(alloc_id)
1510            }
1511        }
1512    }
1513    struct CollectAllocIds(BTreeSet<AllocId>);
1514
1515    impl<'tcx> Visitor<'tcx> for CollectAllocIds {
1516        fn visit_const_operand(&mut self, c: &ConstOperand<'tcx>, _: Location) {
1517            match c.const_ {
1518                Const::Ty(_, _) | Const::Unevaluated(..) => {}
1519                Const::Val(val, _) => {
1520                    if let Some(id) = alloc_id_from_const_val(val) {
1521                        self.0.insert(id);
1522                    }
1523                }
1524            }
1525        }
1526    }
1527
1528    let mut visitor = CollectAllocIds(Default::default());
1529    visitor.visit_body(body);
1530
1531    // `seen` contains all seen allocations, including the ones we have *not* printed yet.
1532    // The protocol is to first `insert` into `seen`, and only if that returns `true`
1533    // then push to `todo`.
1534    let mut seen = visitor.0;
1535    let mut todo: Vec<_> = seen.iter().copied().collect();
1536    while let Some(id) = todo.pop() {
1537        let mut write_allocation_track_relocs =
1538            |w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
1539                // `.rev()` because we are popping them from the back of the `todo` vector.
1540                for id in alloc_ids_from_alloc(alloc).rev() {
1541                    if seen.insert(id) {
1542                        todo.push(id);
1543                    }
1544                }
1545                write!(w, "{}", display_allocation(tcx, alloc.inner()))
1546            };
1547        write!(w, "\n{id:?}")?;
1548        match tcx.try_get_global_alloc(id) {
1549            // This can't really happen unless there are bugs, but it doesn't cost us anything to
1550            // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
1551            None => write!(w, " (deallocated)")?,
1552            Some(GlobalAlloc::Function { instance, .. }) => write!(w, " (fn: {instance})")?,
1553            Some(GlobalAlloc::VTable(ty, dyn_ty)) => {
1554                write!(w, " (vtable: impl {dyn_ty} for {ty})")?
1555            }
1556            Some(GlobalAlloc::TypeId { ty }) => write!(w, " (typeid for {ty})")?,
1557            Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
1558                write!(w, " (static: {}", tcx.def_path_str(did))?;
1559                if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup)
1560                    && body
1561                        .source
1562                        .def_id()
1563                        .as_local()
1564                        .is_some_and(|def_id| tcx.hir_body_const_context(def_id).is_some())
1565                {
1566                    // Statics may be cyclic and evaluating them too early
1567                    // in the MIR pipeline may cause cycle errors even though
1568                    // normal compilation is fine.
1569                    write!(w, ")")?;
1570                } else {
1571                    match tcx.eval_static_initializer(did) {
1572                        Ok(alloc) => {
1573                            write!(w, ", ")?;
1574                            write_allocation_track_relocs(w, alloc)?;
1575                        }
1576                        Err(_) => write!(w, ", error during initializer evaluation)")?,
1577                    }
1578                }
1579            }
1580            Some(GlobalAlloc::Static(did)) => {
1581                write!(w, " (extern static: {})", tcx.def_path_str(did))?
1582            }
1583            Some(GlobalAlloc::Memory(alloc)) => {
1584                write!(w, " (")?;
1585                write_allocation_track_relocs(w, alloc)?
1586            }
1587        }
1588        writeln!(w)?;
1589    }
1590    Ok(())
1591}
1592
1593/// Dumps the size and metadata and content of an allocation to the given writer.
1594/// The expectation is that the caller first prints other relevant metadata, so the exact
1595/// format of this function is (*without* leading or trailing newline):
1596///
1597/// ```text
1598/// size: {}, align: {}) {
1599///     <bytes>
1600/// }
1601/// ```
1602///
1603/// The byte format is similar to how hex editors print bytes. Each line starts with the address of
1604/// the start of the line, followed by all bytes in hex format (space separated).
1605/// If the allocation is small enough to fit into a single line, no start address is given.
1606/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
1607/// characters or characters whose value is larger than 127) with a `.`
1608/// This also prints provenance adequately.
1609pub fn display_allocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
1610    tcx: TyCtxt<'tcx>,
1611    alloc: &'a Allocation<Prov, Extra, Bytes>,
1612) -> RenderAllocation<'a, 'tcx, Prov, Extra, Bytes> {
1613    RenderAllocation { tcx, alloc }
1614}
1615
1616#[doc(hidden)]
1617pub struct RenderAllocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> {
1618    tcx: TyCtxt<'tcx>,
1619    alloc: &'a Allocation<Prov, Extra, Bytes>,
1620}
1621
1622impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> std::fmt::Display
1623    for RenderAllocation<'a, 'tcx, Prov, Extra, Bytes>
1624{
1625    fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1626        let RenderAllocation { tcx, alloc } = *self;
1627        write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?;
1628        if alloc.size() == Size::ZERO {
1629            // We are done.
1630            return write!(w, " {{}}");
1631        }
1632        if tcx.sess.opts.unstable_opts.dump_mir_exclude_alloc_bytes {
1633            return write!(w, " {{ .. }}");
1634        }
1635        // Write allocation bytes.
1636        writeln!(w, " {{")?;
1637        write_allocation_bytes(tcx, alloc, w, "    ")?;
1638        write!(w, "}}")?;
1639        Ok(())
1640    }
1641}
1642
1643fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result {
1644    for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {
1645        write!(w, "   ")?;
1646    }
1647    writeln!(w, " │ {ascii}")
1648}
1649
1650/// Number of bytes to print per allocation hex dump line.
1651const BYTES_PER_LINE: usize = 16;
1652
1653/// Prints the line start address and returns the new line start address.
1654fn write_allocation_newline(
1655    w: &mut dyn std::fmt::Write,
1656    mut line_start: Size,
1657    ascii: &str,
1658    pos_width: usize,
1659    prefix: &str,
1660) -> Result<Size, std::fmt::Error> {
1661    write_allocation_endline(w, ascii)?;
1662    line_start += Size::from_bytes(BYTES_PER_LINE);
1663    write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;
1664    Ok(line_start)
1665}
1666
1667/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
1668/// is only one line). Note that your prefix should contain a trailing space as the lines are
1669/// printed directly after it.
1670pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
1671    tcx: TyCtxt<'tcx>,
1672    alloc: &Allocation<Prov, Extra, Bytes>,
1673    w: &mut dyn std::fmt::Write,
1674    prefix: &str,
1675) -> std::fmt::Result {
1676    let num_lines = alloc.size().bytes_usize().saturating_sub(BYTES_PER_LINE);
1677    // Number of chars needed to represent all line numbers.
1678    let pos_width = hex_number_length(alloc.size().bytes());
1679
1680    if num_lines > 0 {
1681        write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;
1682    } else {
1683        write!(w, "{prefix}")?;
1684    }
1685
1686    let mut i = Size::ZERO;
1687    let mut line_start = Size::ZERO;
1688
1689    let ptr_size = tcx.data_layout.pointer_size();
1690
1691    let mut ascii = String::new();
1692
1693    let oversized_ptr = |target: &mut String, width| {
1694        if target.len() > width {
1695            write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();
1696        }
1697    };
1698
1699    while i < alloc.size() {
1700        // The line start already has a space. While we could remove that space from the line start
1701        // printing and unconditionally print a space here, that would cause the single-line case
1702        // to have a single space before it, which looks weird.
1703        if i != line_start {
1704            write!(w, " ")?;
1705        }
1706        if let Some(prov) = alloc.provenance().get_ptr(i) {
1707            // Memory with provenance must be defined
1708            assert!(alloc.init_mask().is_range_initialized(alloc_range(i, ptr_size)).is_ok());
1709            let j = i.bytes_usize();
1710            let offset = alloc
1711                .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
1712            let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
1713            let offset = Size::from_bytes(offset);
1714            let provenance_width = |bytes| bytes * 3;
1715            let ptr = Pointer::new(prov, offset);
1716            let mut target = format!("{ptr:?}");
1717            if target.len() > provenance_width(ptr_size.bytes_usize() - 1) {
1718                // This is too long, try to save some space.
1719                target = format!("{ptr:#?}");
1720            }
1721            if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {
1722                // This branch handles the situation where a provenance starts in the current line
1723                // but ends in the next one.
1724                let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);
1725                let overflow = ptr_size - remainder;
1726                let remainder_width = provenance_width(remainder.bytes_usize()) - 2;
1727                let overflow_width = provenance_width(overflow.bytes_usize() - 1) + 1;
1728                ascii.push('╾'); // HEAVY LEFT AND LIGHT RIGHT
1729                for _ in 1..remainder.bytes() {
1730                    ascii.push('─'); // LIGHT HORIZONTAL
1731                }
1732                if overflow_width > remainder_width && overflow_width >= target.len() {
1733                    // The case where the provenance fits into the part in the next line
1734                    write!(w, "╾{0:─^1$}", "", remainder_width)?;
1735                    line_start =
1736                        write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
1737                    ascii.clear();
1738                    write!(w, "{target:─^overflow_width$}╼")?;
1739                } else {
1740                    oversized_ptr(&mut target, remainder_width);
1741                    write!(w, "╾{target:─^remainder_width$}")?;
1742                    line_start =
1743                        write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
1744                    write!(w, "{0:─^1$}╼", "", overflow_width)?;
1745                    ascii.clear();
1746                }
1747                for _ in 0..overflow.bytes() - 1 {
1748                    ascii.push('─');
1749                }
1750                ascii.push('╼'); // LIGHT LEFT AND HEAVY RIGHT
1751                i += ptr_size;
1752                continue;
1753            } else {
1754                // This branch handles a provenance that starts and ends in the current line.
1755                let provenance_width = provenance_width(ptr_size.bytes_usize() - 1);
1756                oversized_ptr(&mut target, provenance_width);
1757                ascii.push('╾');
1758                write!(w, "╾{target:─^provenance_width$}╼")?;
1759                for _ in 0..ptr_size.bytes() - 2 {
1760                    ascii.push('─');
1761                }
1762                ascii.push('╼');
1763                i += ptr_size;
1764            }
1765        } else if let Some((prov, idx)) = alloc.provenance().get_byte(i, &tcx) {
1766            // Memory with provenance must be defined
1767            assert!(
1768                alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok()
1769            );
1770            ascii.push('━'); // HEAVY HORIZONTAL
1771            // We have two characters to display this, which is obviously not enough.
1772            // Format is similar to "oversized" above.
1773            let j = i.bytes_usize();
1774            let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
1775            write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼")?;
1776            i += Size::from_bytes(1);
1777        } else if alloc
1778            .init_mask()
1779            .is_range_initialized(alloc_range(i, Size::from_bytes(1)))
1780            .is_ok()
1781        {
1782            let j = i.bytes_usize();
1783
1784            // Checked definedness (and thus range) and provenance. This access also doesn't
1785            // influence interpreter execution but is only for debugging.
1786            let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
1787            write!(w, "{c:02x}")?;
1788            if c.is_ascii_control() || c >= 0x80 {
1789                ascii.push('.');
1790            } else {
1791                ascii.push(char::from(c));
1792            }
1793            i += Size::from_bytes(1);
1794        } else {
1795            write!(w, "__")?;
1796            ascii.push('░');
1797            i += Size::from_bytes(1);
1798        }
1799        // Print a new line header if the next line still has some bytes to print.
1800        if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size() {
1801            line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
1802            ascii.clear();
1803        }
1804    }
1805    write_allocation_endline(w, &ascii)?;
1806
1807    Ok(())
1808}
1809
1810///////////////////////////////////////////////////////////////////////////
1811// Constants
1812
1813fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {
1814    write!(fmt, "b\"{}\"", byte_str.escape_ascii())
1815}
1816
1817fn comma_sep<'tcx>(
1818    tcx: TyCtxt<'tcx>,
1819    fmt: &mut Formatter<'_>,
1820    elems: Vec<(ConstValue, Ty<'tcx>)>,
1821) -> fmt::Result {
1822    let mut first = true;
1823    for (ct, ty) in elems {
1824        if !first {
1825            fmt.write_str(", ")?;
1826        }
1827        pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
1828        first = false;
1829    }
1830    Ok(())
1831}
1832
1833fn pretty_print_const_value_tcx<'tcx>(
1834    tcx: TyCtxt<'tcx>,
1835    ct: ConstValue,
1836    ty: Ty<'tcx>,
1837    fmt: &mut Formatter<'_>,
1838) -> fmt::Result {
1839    use crate::ty::print::PrettyPrinter;
1840
1841    if tcx.sess.verbose_internals() {
1842        fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;
1843        return Ok(());
1844    }
1845
1846    let u8_type = tcx.types.u8;
1847    match (ct, ty.kind()) {
1848        // Byte/string slices, printed as (byte) string literals.
1849        (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
1850            if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
1851                fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
1852                return Ok(());
1853            }
1854        }
1855        (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
1856            if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
1857                pretty_print_byte_str(fmt, data)?;
1858                return Ok(());
1859            }
1860        }
1861        (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
1862            let n = n.try_to_target_usize(tcx).unwrap();
1863            let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
1864            // cast is ok because we already checked for pointer size (32 or 64 bit) above
1865            let range = AllocRange { start: offset, size: Size::from_bytes(n) };
1866            let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
1867            fmt.write_str("*")?;
1868            pretty_print_byte_str(fmt, byte_str)?;
1869            return Ok(());
1870        }
1871        // Aggregates, printed as array/tuple/struct/variant construction syntax.
1872        //
1873        // NB: the `has_non_region_param` check ensures that we can use
1874        // the `destructure_const` query with an empty `ty::ParamEnv` without
1875        // introducing ICEs (e.g. via `layout_of`) from missing bounds.
1876        // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
1877        // to be able to destructure the tuple into `(0u8, *mut T)`
1878        (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
1879            let ct = tcx.lift(ct).unwrap();
1880            let ty = tcx.lift(ty).unwrap();
1881            if let Some(contents) = tcx.try_destructure_mir_constant_for_user_output(ct, ty) {
1882                let fields: Vec<(ConstValue, Ty<'_>)> = contents.fields.to_vec();
1883                match *ty.kind() {
1884                    ty::Array(..) => {
1885                        fmt.write_str("[")?;
1886                        comma_sep(tcx, fmt, fields)?;
1887                        fmt.write_str("]")?;
1888                    }
1889                    ty::Tuple(..) => {
1890                        fmt.write_str("(")?;
1891                        comma_sep(tcx, fmt, fields)?;
1892                        if contents.fields.len() == 1 {
1893                            fmt.write_str(",")?;
1894                        }
1895                        fmt.write_str(")")?;
1896                    }
1897                    ty::Adt(def, _) if def.variants().is_empty() => {
1898                        fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
1899                    }
1900                    ty::Adt(def, args) => {
1901                        let variant_idx = contents
1902                            .variant
1903                            .expect("destructed mir constant of adt without variant idx");
1904                        let variant_def = &def.variant(variant_idx);
1905                        let args = tcx.lift(args).unwrap();
1906                        let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
1907                        p.print_alloc_ids = true;
1908                        p.pretty_print_value_path(variant_def.def_id, args)?;
1909                        fmt.write_str(&p.into_buffer())?;
1910
1911                        match variant_def.ctor_kind() {
1912                            Some(CtorKind::Const) => {}
1913                            Some(CtorKind::Fn) => {
1914                                fmt.write_str("(")?;
1915                                comma_sep(tcx, fmt, fields)?;
1916                                fmt.write_str(")")?;
1917                            }
1918                            None => {
1919                                fmt.write_str(" {{ ")?;
1920                                let mut first = true;
1921                                for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields)
1922                                {
1923                                    if !first {
1924                                        fmt.write_str(", ")?;
1925                                    }
1926                                    write!(fmt, "{}: ", field_def.name)?;
1927                                    pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
1928                                    first = false;
1929                                }
1930                                fmt.write_str(" }}")?;
1931                            }
1932                        }
1933                    }
1934                    _ => unreachable!(),
1935                }
1936                return Ok(());
1937            }
1938        }
1939        (ConstValue::Scalar(scalar), _) => {
1940            let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
1941            p.print_alloc_ids = true;
1942            let ty = tcx.lift(ty).unwrap();
1943            p.pretty_print_const_scalar(scalar, ty)?;
1944            fmt.write_str(&p.into_buffer())?;
1945            return Ok(());
1946        }
1947        (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
1948            let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
1949            p.print_alloc_ids = true;
1950            p.pretty_print_value_path(*d, s)?;
1951            fmt.write_str(&p.into_buffer())?;
1952            return Ok(());
1953        }
1954        // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
1955        // their fields instead of just dumping the memory.
1956        _ => {}
1957    }
1958    // Fall back to debug pretty printing for invalid constants.
1959    write!(fmt, "{ct:?}: {ty}")
1960}
1961
1962pub(crate) fn pretty_print_const_value<'tcx>(
1963    ct: ConstValue,
1964    ty: Ty<'tcx>,
1965    fmt: &mut Formatter<'_>,
1966) -> fmt::Result {
1967    ty::tls::with(|tcx| {
1968        let ct = tcx.lift(ct).unwrap();
1969        let ty = tcx.lift(ty).unwrap();
1970        pretty_print_const_value_tcx(tcx, ct, ty, fmt)
1971    })
1972}
1973
1974///////////////////////////////////////////////////////////////////////////
1975// Miscellaneous
1976
1977/// Calc converted u64 decimal into hex and return its length in chars.
1978///
1979/// ```ignore (cannot-test-private-function)
1980/// assert_eq!(1, hex_number_length(0));
1981/// assert_eq!(1, hex_number_length(1));
1982/// assert_eq!(2, hex_number_length(16));
1983/// ```
1984fn hex_number_length(x: u64) -> usize {
1985    if x == 0 {
1986        return 1;
1987    }
1988    let mut length = 0;
1989    let mut x_left = x;
1990    while x_left > 0 {
1991        x_left /= 16;
1992        length += 1;
1993    }
1994    length
1995}