compiler/rustc_middle/src/mir/pretty.rs RUST 2,085 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,085.
1use std::collections::BTreeSet;2use std::fmt::{Display, Write as _};3use std::path::{Path, PathBuf};4use std::{fs, io};56use rustc_abi::Size;7use rustc_ast::InlineAsmTemplatePiece;8use tracing::trace;9use ty::print::PrettyPrinter;1011use 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::*;18use crate::ty::CoroutineArgsExt;1920const INDENT: &str = "    ";21/// Alignment for lining up comments following MIR statements22pub(crate) const ALIGN: usize = 40;2324/// An indication of where we are in the control flow graph. Used for printing25/// extra information in `dump_mir`26#[derive(Clone, Copy)]27pub enum PassWhere {28    /// We have not started dumping the control flow graph, but we are about to.29    BeforeCFG,3031    /// We just finished dumping the control flow graph. This is right before EOF32    AfterCFG,3334    /// We are about to start dumping the given basic block.35    BeforeBlock(BasicBlock),3637    /// We are just about to dump the given statement or terminator.38    BeforeLocation(Location),3940    /// We just dumped the given statement or terminator.41    AfterLocation(Location),4243    /// We just dumped the terminator for a block but not the closing `}`.44    AfterTerminator(BasicBlock),45}4647/// Cosmetic options for pretty-printing the MIR contents, gathered from the CLI. Each pass can48/// override these when dumping its own specific MIR information with `dump_mir`.49#[derive(Copy, Clone)]50pub struct PrettyPrintMirOptions {51    /// Whether to include extra comments, like span info. From `-Z mir-include-spans`.52    pub include_extra_comments: bool,53}5455impl PrettyPrintMirOptions {56    /// Create the default set of MIR pretty-printing options from the CLI flags.57    pub fn from_cli(tcx: TyCtxt<'_>) -> Self {58        Self { include_extra_comments: tcx.sess.opts.unstable_opts.mir_include_spans.is_enabled() }59    }60}6162/// Manages MIR dumping, which is MIR writing done to a file with a specific name. In particular,63/// it makes it impossible to dump MIR to one of these files when it hasn't been requested from the64/// command line. Layered on top of `MirWriter`, which does the actual writing.65pub struct MirDumper<'a, 'tcx> {66    show_pass_num: bool,67    pass_name: &'static str,68    disambiguator: &'a dyn Display,69    writer: MirWriter<'a, 'tcx>,70}7172impl<'a, 'tcx> MirDumper<'a, 'tcx> {73    // If dumping should be performed (e.g. because it was requested on the74    // CLI), returns a `MirDumper` with default values for the following fields:75    // - `show_pass_num`: `false`76    // - `disambiguator`: `&0`77    // - `writer.extra_data`: a no-op78    // - `writer.options`: default options derived from CLI flags79    pub fn new(tcx: TyCtxt<'tcx>, pass_name: &'static str, body: &Body<'tcx>) -> Option<Self> {80        let dump_enabled = if let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir {81            // see notes on #41697 below82            let node_path = ty::print::with_no_trimmed_paths!(83                ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()))84            );85            filters.split('|').any(|or_filter| {86                or_filter.split('&').all(|and_filter| {87                    let and_filter_trimmed = and_filter.trim();88                    and_filter_trimmed == "all"89                        || pass_name.contains(and_filter_trimmed)90                        || node_path.contains(and_filter_trimmed)91                })92            })93        } else {94            false95        };9697        dump_enabled.then_some(MirDumper {98            show_pass_num: false,99            pass_name,100            disambiguator: &0,101            writer: MirWriter::new(tcx),102        })103    }104105    pub fn tcx(&self) -> TyCtxt<'tcx> {106        self.writer.tcx107    }108109    #[must_use]110    pub fn set_show_pass_num(mut self) -> Self {111        self.show_pass_num = true;112        self113    }114115    #[must_use]116    pub fn set_disambiguator(mut self, disambiguator: &'a dyn Display) -> Self {117        self.disambiguator = disambiguator;118        self119    }120121    #[must_use]122    pub fn set_extra_data(123        mut self,124        extra_data: &'a dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,125    ) -> Self {126        self.writer.extra_data = extra_data;127        self128    }129130    #[must_use]131    pub fn set_options(mut self, options: PrettyPrintMirOptions) -> Self {132        self.writer.options = options;133        self134    }135136    /// If the session is properly configured, dumps a human-readable representation of the MIR137    /// (with default pretty-printing options) into:138    ///139    /// ```text140    /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>141    /// ```142    ///143    /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,144    /// where `<filter>` takes the following forms:145    ///146    /// - `all` -- dump MIR for all fns, all passes, all everything147    /// - a filter defined by a set of substrings combined with `&` and `|`148    ///   (`&` has higher precedence). At least one of the `|`-separated groups149    ///   must match; an `|`-separated group matches if all of its `&`-separated150    ///   substrings are matched.151    ///152    /// Example:153    ///154    /// - `nll` == match if `nll` appears in the name155    /// - `foo & nll` == match if `foo` and `nll` both appear in the name156    /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name157    ///   or `typeck` appears in the name.158    /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name159    ///   or `typeck` and `bar` both appear in the name.160    pub fn dump_mir(&self, body: &Body<'tcx>) {161        let _ = try {162            let mut file = self.create_dump_file("mir", body)?;163            self.dump_mir_to_writer(body, &mut file)?;164        };165166        if self.tcx().sess.opts.unstable_opts.dump_mir_graphviz {167            let _ = try {168                let mut file = self.create_dump_file("dot", body)?;169                write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?;170            };171        }172    }173174    // #41697 -- we use `with_forced_impl_filename_line()` because `def_path_str()` would otherwise175    // trigger `type_of`, and this can run while we are already attempting to evaluate `type_of`.176    pub fn dump_mir_to_writer(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {177        // see notes on #41697 above178        let def_path =179            ty::print::with_no_trimmed_paths!(ty::print::with_forced_impl_filename_line!(180                self.tcx().def_path_str(body.source.def_id())181            ));182        // ignore-tidy-odd-backticks the literal below is fine183        write!(w, "// MIR for `{def_path}")?;184        match body.source.promoted {185            None => write!(w, "`")?,186            Some(promoted) => write!(w, "::{promoted:?}`")?,187        }188        writeln!(w, " {} {}", self.disambiguator, self.pass_name)?;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    }195196    /// 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 dump198    /// 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        };206207        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        };215216        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 type219        // 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                s231            }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                s240            }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                s253            }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                s268            }269            _ => String::new(),270        };271272        let mut file_path = PathBuf::new();273        file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));274275        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        );280281        file_path.push(&file_name);282283        file_path284    }285286    /// Attempts to open a file where we should dump a given MIR or other287    /// bit of MIR-related data. Used by `mir-dump`, but also by other288    /// bits of code (e.g., NLL inference) that dump graphviz data or289    /// 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}309310///////////////////////////////////////////////////////////////////////////311// Whole MIR bodies312313/// Write out a human-readable textual representation of this crate's MIR,314/// with the default [`PrettyPrintMirOptions`].315pub fn write_mir_pretty<'tcx>(tcx: TyCtxt<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {316    let writer = MirWriter::new(tcx);317318    writeln!(w, "// WARNING: This output format is intended for human consumers only")?;319    writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;320    writeln!(w, "// HINT: See also -Z dump-mir for MIR at specific points during compilation.")?;321322    let mut first = true;323    for &def_id in tcx.mir_keys(()) {324        if first {325            first = false;326        } else {327            // Put empty lines between all items328            writeln!(w)?;329        }330331        let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {332            writer.write_mir_fn(body, w)?;333334            for body in tcx.promoted_mir(def_id) {335                writeln!(w)?;336                writer.write_mir_fn(body, w)?;337            }338            Ok(())339        };340341        // For `const fn` we want to render both the optimized MIR and the MIR for ctfe.342        if tcx.is_const_fn(def_id) {343            render_body(w, tcx.optimized_mir(def_id))?;344            writeln!(w)?;345            writeln!(w, "// MIR FOR CTFE")?;346            // Do not use `render_body`, as that would render the promoteds again, but these347            // are shared between mir_for_ctfe and optimized_mir348            writer.write_mir_fn(tcx.mir_for_ctfe(def_id), w)?;349        } else {350            if let Some((val, ty)) = tcx.trivial_const(def_id) {351                ty::print::with_forced_impl_filename_line! {352                    // see notes on #41697 elsewhere353                    write!(w, "const {}", tcx.def_path_str(def_id))?354                }355                writeln!(w, ": {} = const {};", ty, Const::Val(val, ty))?;356            } else {357                let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id.to_def_id()));358                render_body(w, instance_mir)?;359            }360        }361    }362    Ok(())363}364365/// Does the writing of MIR to output, e.g. a file.366pub struct MirWriter<'a, 'tcx> {367    tcx: TyCtxt<'tcx>,368    extra_data: &'a dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,369    options: PrettyPrintMirOptions,370}371372impl<'a, 'tcx> MirWriter<'a, 'tcx> {373    pub fn new(tcx: TyCtxt<'tcx>) -> Self {374        MirWriter { tcx, extra_data: &|_, _| Ok(()), options: PrettyPrintMirOptions::from_cli(tcx) }375    }376377    /// Write out a human-readable textual representation for the given function.378    pub fn write_mir_fn(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {379        write_mir_intro(self.tcx, body, w, self.options)?;380        for block in body.basic_blocks.indices() {381            (self.extra_data)(PassWhere::BeforeBlock(block), w)?;382            self.write_basic_block(block, body, w)?;383            if block.index() + 1 != body.basic_blocks.len() {384                writeln!(w)?;385            }386        }387388        writeln!(w, "}}")?;389390        write_allocations(self.tcx, body, w)?;391392        Ok(())393    }394}395396/// Prints local variables in a scope tree.397fn write_scope_tree(398    tcx: TyCtxt<'_>,399    body: &Body<'_>,400    scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,401    w: &mut dyn io::Write,402    parent: SourceScope,403    depth: usize,404    options: PrettyPrintMirOptions,405) -> io::Result<()> {406    let indent = depth * INDENT.len();407408    // Local variable debuginfo.409    for var_debug_info in &body.var_debug_info {410        if var_debug_info.source_info.scope != parent {411            // Not declared in this scope.412            continue;413        }414415        let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);416417        if options.include_extra_comments {418            writeln!(419                w,420                "{0:1$} // in {2}",421                indented_debug_info,422                ALIGN,423                comment(tcx, var_debug_info.source_info),424            )?;425        } else {426            writeln!(w, "{indented_debug_info}")?;427        }428    }429430    // Coroutine debuginfo.431    if let Some(layout) = body.coroutine_layout_raw() {432        for (field, field_decl) in layout.field_tys.iter_enumerated() {433            let source_info = field_decl.source_info;434            if let Some(name) = field_decl.debuginfo_name435                && source_info.scope == parent436            {437                let indented_debug_info =438                    format!("{0:1$}coroutine debug {2} => {3:?};", INDENT, indent, name, field);439440                if options.include_extra_comments {441                    writeln!(442                        w,443                        "{0:1$} // in {2}",444                        indented_debug_info,445                        ALIGN,446                        comment(tcx, source_info),447                    )?;448                } else {449                    writeln!(w, "{indented_debug_info}")?;450                }451            }452        }453    }454455    // Local variable types.456    for (local, local_decl) in body.local_decls.iter_enumerated() {457        if (1..body.arg_count + 1).contains(&local.index()) {458            // Skip over argument locals, they're printed in the signature.459            continue;460        }461462        if local_decl.source_info.scope != parent {463            // Not declared in this scope.464            continue;465        }466467        let mut_str = local_decl.mutability.prefix_str();468469        let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(470            "{0:1$}let {2}{3:?}: {4}",471            INDENT, indent, mut_str, local, local_decl.ty472        ));473        if let Some(user_ty) = &local_decl.user_ty {474            for user_ty in user_ty.projections() {475                write!(indented_decl, " as {user_ty:?}").unwrap();476            }477        }478        indented_decl.push(';');479480        let local_name = if local == RETURN_PLACE { " return place" } else { "" };481482        if options.include_extra_comments {483            writeln!(484                w,485                "{0:1$} //{2} in {3}",486                indented_decl,487                ALIGN,488                local_name,489                comment(tcx, local_decl.source_info),490            )?;491        } else {492            writeln!(w, "{indented_decl}",)?;493        }494    }495496    let Some(children) = scope_tree.get(&parent) else {497        return Ok(());498    };499500    for &child in children {501        let child_data = &body.source_scopes[child];502        assert_eq!(child_data.parent_scope, Some(parent));503504        let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {505            (506                format!(507                    " (inlined {}{})",508                    if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },509                    callee510                ),511                Some(callsite_span),512            )513        } else {514            (String::new(), None)515        };516517        let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);518519        if options.include_extra_comments {520            if let Some(span) = span {521                writeln!(522                    w,523                    "{0:1$} // at {2}",524                    indented_header,525                    ALIGN,526                    tcx.sess.source_map().span_to_diagnostic_string(span),527                )?;528            } else {529                writeln!(w, "{indented_header}")?;530            }531        } else {532            writeln!(w, "{indented_header}")?;533        }534535        write_scope_tree(tcx, body, scope_tree, w, child, depth + 1, options)?;536        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;537    }538539    Ok(())540}541542impl Debug for VarDebugInfo<'_> {543    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {544        if let Some(VarDebugInfoFragment { ty, ref projection }) = self.composite {545            pre_fmt_projection(&projection[..], fmt)?;546            write!(fmt, "({}: {})", self.name, ty)?;547            post_fmt_projection(&projection[..], fmt)?;548        } else {549            write!(fmt, "{}", self.name)?;550        }551552        write!(fmt, " => {:?}", self.value)553    }554}555556fn write_coroutine_layout<'tcx>(557    tcx: TyCtxt<'tcx>,558    layout: &CoroutineLayout<'_>,559    w: &mut dyn io::Write,560    options: PrettyPrintMirOptions,561) -> io::Result<()> {562    let CoroutineLayout { field_tys, variant_fields, variant_source_info, storage_conflicts } =563        layout;564565    writeln!(w, "{INDENT}coroutine layout {{")?;566567    for (field, CoroutineSavedTy { ty, source_info, ignore_for_traits, debuginfo_name: _ }) in568        field_tys.iter_enumerated()569    {570        let ignore_for_traits = if *ignore_for_traits { " (ignored for traits)" } else { "" };571        let indented_body = format!("{INDENT}{INDENT}field {field:?}: {ty}{ignore_for_traits};",);572        if options.include_extra_comments {573            writeln!(w, "{0:ALIGN$} // in {1}", indented_body, comment(tcx, *source_info))?;574        } else {575            writeln!(w, "{}", indented_body)?;576        }577    }578579    writeln!(w, "{INDENT}{INDENT}variant_fields = {{")?;580    for (variant, fields) in variant_fields.iter_enumerated() {581        let variant_name = ty::CoroutineArgs::variant_name(variant);582        let header = format!("{INDENT}{INDENT}{INDENT}{variant_name:9}({variant:?}): {fields:?},");583        if options.include_extra_comments {584            let source_info = variant_source_info[variant];585            writeln!(w, "{0:ALIGN$} // in {1}", header, comment(tcx, source_info))?;586        } else {587            writeln!(w, "{}", header)?;588        }589    }590    writeln!(w, "{INDENT}{INDENT}}}")?;591    writeln!(w, "{INDENT}{INDENT}storage_conflicts = {storage_conflicts:?}")?;592    writeln!(w, "{INDENT}}}")593}594595/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its596/// local variables (both user-defined bindings and compiler temporaries).597fn write_mir_intro<'tcx>(598    tcx: TyCtxt<'tcx>,599    body: &Body<'_>,600    w: &mut dyn io::Write,601    options: PrettyPrintMirOptions,602) -> io::Result<()> {603    write_mir_sig(tcx, body, w)?;604    writeln!(w, "{{")?;605606    if let Some(ref layout) = body.coroutine_layout_raw() {607        write_coroutine_layout(tcx, layout, w, options)?;608    }609610    // construct a scope tree and write it out611    let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();612    for (index, scope_data) in body.source_scopes.iter_enumerated() {613        if let Some(parent) = scope_data.parent_scope {614            scope_tree.entry(parent).or_default().push(index);615        } else {616            // Only the argument scope has no parent, because it's the root.617            assert_eq!(index, OUTERMOST_SOURCE_SCOPE);618        }619    }620621    write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1, options)?;622623    // Add an empty line before the first block is printed.624    writeln!(w)?;625626    if let Some(coverage_info_hi) = &body.coverage_info_hi {627        write_coverage_info_hi(coverage_info_hi, w)?;628    }629    if let Some(function_coverage_info) = &body.function_coverage_info {630        write_function_coverage_info(function_coverage_info, w)?;631    }632633    Ok(())634}635636fn write_coverage_info_hi(637    coverage_info_hi: &coverage::CoverageInfoHi,638    w: &mut dyn io::Write,639) -> io::Result<()> {640    let coverage::CoverageInfoHi { num_block_markers: _, branch_spans } = coverage_info_hi;641642    // Only add an extra trailing newline if we printed at least one thing.643    let mut did_print = false;644645    for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {646        writeln!(647            w,648            "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",649        )?;650        did_print = true;651    }652653    if did_print {654        writeln!(w)?;655    }656657    Ok(())658}659660fn write_function_coverage_info(661    function_coverage_info: &coverage::FunctionCoverageInfo,662    w: &mut dyn io::Write,663) -> io::Result<()> {664    let coverage::FunctionCoverageInfo { mappings, .. } = function_coverage_info;665666    for coverage::Mapping { kind, span } in mappings {667        writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?;668    }669    writeln!(w)?;670671    Ok(())672}673674fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io::Result<()> {675    use rustc_hir::def::DefKind;676677    trace!("write_mir_sig: {:?}", body.source.instance);678    let def_id = body.source.def_id();679    let kind = tcx.def_kind(def_id);680    let is_function = match kind {681        DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::SyntheticCoroutineBody => {682            true683        }684        _ => tcx.is_closure_like(def_id),685    };686    match (kind, body.source.promoted) {687        (_, Some(_)) => write!(w, "const ")?, // promoteds are the closest to consts688        (DefKind::Const { .. } | DefKind::AssocConst { .. }, _) => write!(w, "const ")?,689        (DefKind::Static { safety: _, mutability: hir::Mutability::Not, nested: false }, _) => {690            write!(w, "static ")?691        }692        (DefKind::Static { safety: _, mutability: hir::Mutability::Mut, nested: false }, _) => {693            write!(w, "static mut ")?694        }695        (_, _) if is_function => write!(w, "fn ")?,696        // things like anon const, not an item697        (DefKind::AnonConst | DefKind::InlineConst, _) => {}698        // `global_asm!` have fake bodies, which we may dump after mir-build699        (DefKind::GlobalAsm, _) => {}700        _ => bug!("Unexpected def kind {:?}", kind),701    }702703    ty::print::with_forced_impl_filename_line! {704        // see notes on #41697 elsewhere705        write!(w, "{}", tcx.def_path_str(def_id))?706    }707    if let Some(p) = body.source.promoted {708        write!(w, "::{p:?}")?;709    }710711    if body.source.promoted.is_none() && is_function {712        write!(w, "(")?;713714        // fn argument types.715        for (i, arg) in body.args_iter().enumerate() {716            if i != 0 {717                write!(w, ", ")?;718            }719            write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;720        }721722        write!(w, ") -> {}", body.return_ty())?;723    } else {724        assert_eq!(body.arg_count, 0);725        write!(w, ": {} =", body.return_ty())?;726    }727728    if let Some(yield_ty) = body.yield_ty() {729        writeln!(w)?;730        writeln!(w, "yields {yield_ty}")?;731    }732733    write!(w, " ")?;734    // Next thing that gets printed is the opening {735736    Ok(())737}738739fn write_user_type_annotations(740    tcx: TyCtxt<'_>,741    body: &Body<'_>,742    w: &mut dyn io::Write,743) -> io::Result<()> {744    if !body.user_type_annotations.is_empty() {745        writeln!(w, "| User Type Annotations")?;746    }747    for (index, annotation) in body.user_type_annotations.iter_enumerated() {748        writeln!(749            w,750            "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",751            index.index(),752            annotation.user_ty,753            tcx.sess.source_map().span_to_diagnostic_string(annotation.span),754            with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),755        )?;756    }757    if !body.user_type_annotations.is_empty() {758        writeln!(w, "|")?;759    }760    Ok(())761}762763///////////////////////////////////////////////////////////////////////////764// Basic blocks and their parts (statements, terminators, ...)765766impl<'a, 'tcx> MirWriter<'a, 'tcx> {767    /// Write out a human-readable textual representation for the given basic block.768    fn write_basic_block(769        &self,770        block: BasicBlock,771        body: &Body<'tcx>,772        w: &mut dyn io::Write,773    ) -> io::Result<()> {774        let data = &body[block];775776        // Basic block label at the top.777        let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };778        writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;779780        // List of statements in the middle.781        let mut current_location = Location { block, statement_index: 0 };782        for statement in &data.statements {783            (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;784785            for debuginfo in statement.debuginfos.iter() {786                writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?;787            }788789            let indented_body = format!("{INDENT}{INDENT}{statement:?};");790            if self.options.include_extra_comments {791                writeln!(792                    w,793                    "{:A$} // {}{}",794                    indented_body,795                    if self.tcx.sess.verbose_internals() {796                        format!("{current_location:?}: ")797                    } else {798                        String::new()799                    },800                    comment(self.tcx, statement.source_info),801                    A = ALIGN,802                )?;803            } else {804                writeln!(w, "{indented_body}")?;805            }806807            write_extra(808                self.tcx,809                w,810                &|visitor| visitor.visit_statement(statement, current_location),811                self.options,812            )?;813814            (self.extra_data)(PassWhere::AfterLocation(current_location), w)?;815816            current_location.statement_index += 1;817        }818819        for debuginfo in data.after_last_stmt_debuginfos.iter() {820            writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?;821        }822823        // Terminator at the bottom.824        (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;825        if data.terminator.is_some() {826            let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);827            if self.options.include_extra_comments {828                writeln!(829                    w,830                    "{:A$} // {}{}",831                    indented_terminator,832                    if self.tcx.sess.verbose_internals() {833                        format!("{current_location:?}: ")834                    } else {835                        String::new()836                    },837                    comment(self.tcx, data.terminator().source_info),838                    A = ALIGN,839                )?;840            } else {841                writeln!(w, "{indented_terminator}")?;842            }843844            write_extra(845                self.tcx,846                w,847                &|visitor| visitor.visit_terminator(data.terminator(), current_location),848                self.options,849            )?;850        }851852        (self.extra_data)(PassWhere::AfterLocation(current_location), w)?;853        (self.extra_data)(PassWhere::AfterTerminator(block), w)?;854855        writeln!(w, "{INDENT}}}")856    }857}858859impl Debug for StatementKind<'_> {860    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {861        use self::StatementKind::*;862        match *self {863            Assign((ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),864            FakeRead((ref cause, ref place)) => {865                write!(fmt, "FakeRead({cause:?}, {place:?})")866            }867            StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),868            StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),869            SetDiscriminant { ref place, variant_index } => {870                write!(fmt, "discriminant({place:?}) = {variant_index:?}")871            }872            PlaceMention(ref place) => {873                write!(fmt, "PlaceMention({place:?})")874            }875            AscribeUserType((ref place, ref c_ty), ref variance) => {876                write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")877            }878            Coverage(ref kind) => write!(fmt, "Coverage::{kind:?}"),879            Intrinsic(ref intrinsic) => write!(fmt, "{intrinsic}"),880            ConstEvalCounter => write!(fmt, "ConstEvalCounter"),881            Nop => write!(fmt, "nop"),882            BackwardIncompatibleDropHint { ref place, reason: _ } => {883                // For now, we don't record the reason because there is only one use case,884                // which is to report breaking change in drop order by Edition 2024885                write!(fmt, "BackwardIncompatibleDropHint({place:?})")886            }887        }888    }889}890impl Debug for Statement<'_> {891    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {892        self.kind.fmt(fmt)893    }894}895896impl Debug for StmtDebugInfo<'_> {897    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {898        match self {899            StmtDebugInfo::AssignRef(local, place) => {900                write!(fmt, "{local:?} = &{place:?}")901            }902            StmtDebugInfo::InvalidAssign(local) => {903                write!(fmt, "{local:?} = &?")904            }905        }906    }907}908909impl Display for NonDivergingIntrinsic<'_> {910    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {911        match self {912            Self::Assume(op) => write!(f, "assume({op:?})"),913            Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => {914                write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})")915            }916        }917    }918}919920impl<'tcx> Debug for TerminatorKind<'tcx> {921    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {922        self.fmt_head(fmt)?;923        let successor_count = self.successors().count();924        let labels = self.fmt_successor_labels();925        assert_eq!(successor_count, labels.len());926927        // `Cleanup` is already included in successors928        let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));929        let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {930            write!(fmt, "unwind ")?;931            match self.unwind() {932                // Not needed or included in successors933                None | Some(UnwindAction::Cleanup(_)) => unreachable!(),934                Some(UnwindAction::Continue) => write!(fmt, "continue"),935                Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),936                Some(UnwindAction::Terminate(reason)) => {937                    write!(fmt, "terminate({})", reason.as_short_str())938                }939            }940        };941942        match (successor_count, show_unwind) {943            (0, false) => Ok(()),944            (0, true) => {945                write!(fmt, " -> ")?;946                fmt_unwind(fmt)947            }948            (1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),949            _ => {950                write!(fmt, " -> [")?;951                for (i, target) in self.successors().enumerate() {952                    if i > 0 {953                        write!(fmt, ", ")?;954                    }955                    write!(fmt, "{}: {:?}", labels[i], target)?;956                }957                if show_unwind {958                    write!(fmt, ", ")?;959                    fmt_unwind(fmt)?;960                }961                write!(fmt, "]")962            }963        }964    }965}966impl Debug for Terminator<'_> {967    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {968        self.kind.fmt(fmt)969    }970}971972impl<'tcx> TerminatorKind<'tcx> {973    /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the974    /// successor basic block, if any. The only information not included is the list of possible975    /// successors, which may be rendered differently between the text and the graphviz format.976    pub fn fmt_head<W: fmt::Write>(&self, fmt: &mut W) -> fmt::Result {977        use self::TerminatorKind::*;978        match self {979            Goto { .. } => write!(fmt, "goto"),980            SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),981            Return => write!(fmt, "return"),982            CoroutineDrop => write!(fmt, "coroutine_drop"),983            UnwindResume => write!(fmt, "resume"),984            UnwindTerminate(reason) => {985                write!(fmt, "terminate({})", reason.as_short_str())986            }987            Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),988            Unreachable => write!(fmt, "unreachable"),989            Drop { place, .. } => write!(fmt, "drop({place:?})"),990            Call { func, args, destination, .. } => {991                write!(fmt, "{destination:?} = ")?;992                write!(fmt, "{func:?}(")?;993                for (index, arg) in args.iter().enumerate() {994                    if index > 0 {995                        write!(fmt, ", ")?;996                    }997                    write!(fmt, "{:?}", arg.node)?;998                }999                write!(fmt, ")")1000            }1001            TailCall { func, args, .. } => {1002                write!(fmt, "tailcall {func:?}(")?;1003                for (index, arg) in args.iter().enumerate() {1004                    if index > 0 {1005                        write!(fmt, ", ")?;1006                    }1007                    write!(fmt, "{:?}", arg.node)?;1008                }1009                write!(fmt, ")")1010            }1011            Assert { cond, expected, msg, .. } => {1012                write!(fmt, "assert(")?;1013                if !expected {1014                    write!(fmt, "!")?;1015                }1016                write!(fmt, "{cond:?}, ")?;1017                msg.fmt_assert_args(fmt)?;1018                write!(fmt, ")")1019            }1020            FalseEdge { .. } => write!(fmt, "falseEdge"),1021            FalseUnwind { .. } => write!(fmt, "falseUnwind"),1022            InlineAsm { template, operands, options, .. } => {1023                write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;1024                for op in operands {1025                    write!(fmt, ", ")?;1026                    let print_late = |&late| if late { "late" } else { "" };1027                    match op {1028                        InlineAsmOperand::In { reg, value } => {1029                            write!(fmt, "in({reg}) {value:?}")?;1030                        }1031                        InlineAsmOperand::Out { reg, late, place: Some(place) } => {1032                            write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;1033                        }1034                        InlineAsmOperand::Out { reg, late, place: None } => {1035                            write!(fmt, "{}out({}) _", print_late(late), reg)?;1036                        }1037                        InlineAsmOperand::InOut {1038                            reg,1039                            late,1040                            in_value,1041                            out_place: Some(out_place),1042                        } => {1043                            write!(1044                                fmt,1045                                "in{}out({}) {:?} => {:?}",1046                                print_late(late),1047                                reg,1048                                in_value,1049                                out_place1050                            )?;1051                        }1052                        InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {1053                            write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;1054                        }1055                        InlineAsmOperand::Const { value } => {1056                            write!(fmt, "const {value:?}")?;1057                        }1058                        InlineAsmOperand::SymFn { value } => {1059                            write!(fmt, "sym_fn {value:?}")?;1060                        }1061                        InlineAsmOperand::SymStatic { def_id } => {1062                            write!(fmt, "sym_static {def_id:?}")?;1063                        }1064                        InlineAsmOperand::Label { target_index } => {1065                            write!(fmt, "label {target_index}")?;1066                        }1067                    }1068                }1069                write!(fmt, ", options({options:?}))")1070            }1071        }1072    }10731074    /// Returns the list of labels for the edges to the successor basic blocks.1075    pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {1076        use self::TerminatorKind::*;1077        match *self {1078            Return1079            | TailCall { .. }1080            | UnwindResume1081            | UnwindTerminate(_)1082            | Unreachable1083            | CoroutineDrop => vec![],1084            Goto { .. } => vec!["".into()],1085            SwitchInt { ref targets, .. } => targets1086                .values1087                .iter()1088                .map(|&u| Cow::Owned(u.to_string()))1089                .chain(iter::once("otherwise".into()))1090                .collect(),1091            Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {1092                vec!["return".into(), "unwind".into()]1093            }1094            Call { target: Some(_), unwind: _, .. } => vec!["return".into()],1095            Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],1096            Call { target: None, unwind: _, .. } => vec![],1097            Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],1098            Yield { drop: None, .. } => vec!["resume".into()],1099            Drop { unwind: UnwindAction::Cleanup(_), drop: Some(_), .. } => {1100                vec!["return".into(), "unwind".into(), "drop".into()]1101            }1102            Drop { unwind: UnwindAction::Cleanup(_), drop: None, .. } => {1103                vec!["return".into(), "unwind".into()]1104            }1105            Drop { unwind: _, drop: Some(_), .. } => vec!["return".into(), "drop".into()],1106            Drop { unwind: _, .. } => vec!["return".into()],1107            Assert { unwind: UnwindAction::Cleanup(_), .. } => {1108                vec!["success".into(), "unwind".into()]1109            }1110            Assert { unwind: _, .. } => vec!["success".into()],1111            FalseEdge { .. } => vec!["real".into(), "imaginary".into()],1112            FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {1113                vec!["real".into(), "unwind".into()]1114            }1115            FalseUnwind { unwind: _, .. } => vec!["real".into()],1116            InlineAsm { asm_macro, options, ref targets, unwind, .. } => {1117                let mut vec = Vec::with_capacity(targets.len() + 1);1118                if !asm_macro.diverges(options) {1119                    vec.push("return".into());1120                }1121                vec.resize(targets.len(), "label".into());11221123                if let UnwindAction::Cleanup(_) = unwind {1124                    vec.push("unwind".into());1125                }11261127                vec1128            }1129        }1130    }1131}11321133impl<'tcx> Debug for Rvalue<'tcx> {1134    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1135        use self::Rvalue::*;11361137        match *self {1138            Use(ref operand, with_retag) => {1139                // With retag is more common so we only print when it's without.1140                write!(fmt, "{}{operand:?}", if with_retag.no() { "no_retag " } else { "" })1141            }1142            Repeat(ref a, b) => {1143                write!(fmt, "[{a:?}; ")?;1144                pretty_print_const(b, fmt, false)?;1145                write!(fmt, "]")1146            }1147            Cast(ref kind, ref place, ref ty) => {1148                with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))1149            }1150            BinaryOp(ref op, (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),1151            UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),1152            Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),1153            ThreadLocalRef(did) => ty::tls::with(|tcx| {1154                let muta = tcx.static_mutability(did).unwrap().prefix_str();1155                write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))1156            }),1157            Ref(region, borrow_kind, ref place) => {1158                let kind_str = match borrow_kind {1159                    BorrowKind::Shared => "",1160                    BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ",1161                    BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ",1162                    BorrowKind::Mut { .. } => "mut ",1163                };11641165                // When printing regions, add trailing space if necessary.1166                let print_region = ty::tls::with(|tcx| {1167                    tcx.sess.verbose_internals() || tcx.sess.opts.unstable_opts.identify_regions1168                });1169                let region = if print_region {1170                    let mut region = region.to_string();1171                    if !region.is_empty() {1172                        region.push(' ');1173                    }1174                    region1175                } else {1176                    // Do not even print 'static1177                    String::new()1178                };1179                write!(fmt, "&{region}{kind_str}{place:?}")1180            }11811182            Reborrow(target, mutability, ref place) => {1183                write!(1184                    fmt,1185                    "{target:?}({} {place:?})",1186                    if mutability.is_mut() { "reborrow" } else { "coerce shared" }1187                )1188            }11891190            CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),11911192            RawPtr(mutability, ref place) => {1193                write!(fmt, "&raw {mut_str} {place:?}", mut_str = mutability.ptr_str())1194            }11951196            Aggregate(ref kind, ref places) => {1197                let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {1198                    let mut tuple_fmt = fmt.debug_tuple(name);1199                    for place in places {1200                        tuple_fmt.field(place);1201                    }1202                    tuple_fmt.finish()1203                };12041205                match **kind {1206                    AggregateKind::Array(_) => write!(fmt, "{places:?}"),12071208                    AggregateKind::Tuple => {1209                        if places.is_empty() {1210                            write!(fmt, "()")1211                        } else {1212                            fmt_tuple(fmt, "")1213                        }1214                    }12151216                    AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {1217                        ty::tls::with(|tcx| {1218                            let variant_def = &tcx.adt_def(adt_did).variant(variant);1219                            let args = tcx.lift(args);1220                            let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |p| {1221                                p.print_def_path(variant_def.def_id, args)1222                            })?;12231224                            match variant_def.ctor_kind() {1225                                Some(CtorKind::Const) => fmt.write_str(&name),1226                                Some(CtorKind::Fn) => fmt_tuple(fmt, &name),1227                                None => {1228                                    let mut struct_fmt = fmt.debug_struct(&name);1229                                    for (field, place) in iter::zip(&variant_def.fields, places) {1230                                        struct_fmt.field(field.name.as_str(), place);1231                                    }1232                                    struct_fmt.finish()1233                                }1234                            }1235                        })1236                    }12371238                    AggregateKind::Closure(def_id, args)1239                    | AggregateKind::CoroutineClosure(def_id, args) => ty::tls::with(|tcx| {1240                        let name = if tcx.sess.opts.unstable_opts.span_free_formats {1241                            let args = tcx.lift(args);1242                            format!("{{closure@{}}}", tcx.def_path_str_with_args(def_id, args),)1243                        } else {1244                            let span = tcx.def_span(def_id);1245                            format!(1246                                "{{closure@{}}}",1247                                tcx.sess.source_map().span_to_diagnostic_string(span)1248                            )1249                        };1250                        let mut struct_fmt = fmt.debug_struct(&name);12511252                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places1253                        if let Some(def_id) = def_id.as_local()1254                            && let Some(upvars) = tcx.upvars_mentioned(def_id)1255                        {1256                            for (&var_id, place) in iter::zip(upvars.keys(), places) {1257                                let var_name = tcx.hir_name(var_id);1258                                struct_fmt.field(var_name.as_str(), place);1259                            }1260                        } else {1261                            for (index, place) in places.iter().enumerate() {1262                                struct_fmt.field(&format!("{index}"), place);1263                            }1264                        }12651266                        struct_fmt.finish()1267                    }),12681269                    AggregateKind::Coroutine(def_id, _) => ty::tls::with(|tcx| {1270                        let name = format!("{{coroutine@{:?}}}", tcx.def_span(def_id));1271                        let mut struct_fmt = fmt.debug_struct(&name);12721273                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places1274                        if let Some(def_id) = def_id.as_local()1275                            && let Some(upvars) = tcx.upvars_mentioned(def_id)1276                        {1277                            for (&var_id, place) in iter::zip(upvars.keys(), places) {1278                                let var_name = tcx.hir_name(var_id);1279                                struct_fmt.field(var_name.as_str(), place);1280                            }1281                        } else {1282                            for (index, place) in places.iter().enumerate() {1283                                struct_fmt.field(&format!("{index}"), place);1284                            }1285                        }12861287                        struct_fmt.finish()1288                    }),12891290                    AggregateKind::RawPtr(pointee_ty, mutability) => {1291                        let kind_str = match mutability {1292                            Mutability::Mut => "mut",1293                            Mutability::Not => "const",1294                        };1295                        with_no_trimmed_paths!(write!(fmt, "*{kind_str} {pointee_ty} from "))?;1296                        fmt_tuple(fmt, "")1297                    }1298                }1299            }13001301            WrapUnsafeBinder(ref op, ty) => {1302                with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})"))1303            }1304        }1305    }1306}13071308impl<'tcx> Debug for Operand<'tcx> {1309    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1310        use self::Operand::*;1311        match *self {1312            Constant(ref a) => write!(fmt, "{a:?}"),1313            Copy(ref place) => write!(fmt, "copy {place:?}"),1314            Move(ref place) => write!(fmt, "move {place:?}"),1315            RuntimeChecks(checks) => write!(fmt, "{checks:?}"),1316        }1317    }1318}13191320impl<'tcx> Debug for ConstOperand<'tcx> {1321    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1322        write!(fmt, "{self}")1323    }1324}13251326impl<'tcx> Display for ConstOperand<'tcx> {1327    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1328        match self.ty().kind() {1329            ty::FnDef(..) => {}1330            _ => write!(fmt, "const ")?,1331        }1332        Display::fmt(&self.const_, fmt)1333    }1334}13351336impl Debug for Place<'_> {1337    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1338        self.as_ref().fmt(fmt)1339    }1340}13411342impl Debug for PlaceRef<'_> {1343    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1344        pre_fmt_projection(self.projection, fmt)?;1345        write!(fmt, "{:?}", self.local)?;1346        post_fmt_projection(self.projection, fmt)1347    }1348}13491350fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {1351    for &elem in projection.iter().rev() {1352        match elem {1353            ProjectionElem::OpaqueCast(_)1354            | ProjectionElem::Downcast(_, _)1355            | ProjectionElem::Field(_, _) => {1356                write!(fmt, "(")?;1357            }1358            ProjectionElem::Deref => {1359                write!(fmt, "(*")?;1360            }1361            ProjectionElem::Index(_)1362            | ProjectionElem::ConstantIndex { .. }1363            | ProjectionElem::Subslice { .. } => {}1364            ProjectionElem::UnwrapUnsafeBinder(_) => {1365                write!(fmt, "unwrap_binder!(")?;1366            }1367        }1368    }13691370    Ok(())1371}13721373fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {1374    for &elem in projection.iter() {1375        match elem {1376            ProjectionElem::OpaqueCast(ty) => {1377                write!(fmt, " as {ty})")?;1378            }1379            ProjectionElem::Downcast(Some(name), _index) => {1380                write!(fmt, " as {name})")?;1381            }1382            ProjectionElem::Downcast(None, index) => {1383                write!(fmt, " as variant#{index:?})")?;1384            }1385            ProjectionElem::Deref => {1386                write!(fmt, ")")?;1387            }1388            ProjectionElem::Field(field, ty) => {1389                with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);1390            }1391            ProjectionElem::Index(ref index) => {1392                write!(fmt, "[{index:?}]")?;1393            }1394            ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {1395                write!(fmt, "[{offset:?} of {min_length:?}]")?;1396            }1397            ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {1398                write!(fmt, "[-{offset:?} of {min_length:?}]")?;1399            }1400            ProjectionElem::Subslice { from, to: 0, from_end: true } => {1401                write!(fmt, "[{from:?}:]")?;1402            }1403            ProjectionElem::Subslice { from: 0, to, from_end: true } => {1404                write!(fmt, "[:-{to:?}]")?;1405            }1406            ProjectionElem::Subslice { from, to, from_end: true } => {1407                write!(fmt, "[{from:?}:-{to:?}]")?;1408            }1409            ProjectionElem::Subslice { from, to, from_end: false } => {1410                write!(fmt, "[{from:?}..{to:?}]")?;1411            }1412            ProjectionElem::UnwrapUnsafeBinder(ty) => {1413                write!(fmt, "; {ty})")?;1414            }1415        }1416    }14171418    Ok(())1419}14201421/// After we print the main statement, we sometimes dump extra1422/// information. There's often a lot of little things "nuzzled up" in1423/// a statement.1424fn write_extra<'tcx>(1425    tcx: TyCtxt<'tcx>,1426    write: &mut dyn io::Write,1427    visit_op: &dyn Fn(&mut ExtraComments<'tcx>),1428    options: PrettyPrintMirOptions,1429) -> io::Result<()> {1430    if options.include_extra_comments {1431        let mut extra_comments = ExtraComments { tcx, comments: vec![] };1432        visit_op(&mut extra_comments);1433        for comment in extra_comments.comments {1434            writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;1435        }1436    }1437    Ok(())1438}14391440struct ExtraComments<'tcx> {1441    tcx: TyCtxt<'tcx>,1442    comments: Vec<String>,1443}14441445impl<'tcx> ExtraComments<'tcx> {1446    fn push(&mut self, lines: &str) {1447        for line in lines.split('\n') {1448            self.comments.push(line.to_string());1449        }1450    }1451}14521453fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {1454    match *ty.kind() {1455        ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,1456        // Unit type1457        ty::Tuple(g_args) if g_args.is_empty() => false,1458        ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),1459        ty::Array(ty, _) => use_verbose(ty, fn_def),1460        ty::FnDef(..) => fn_def,1461        _ => true,1462    }1463}14641465impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {1466    fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _location: Location) {1467        let ConstOperand { span, user_ty, const_ } = constant;1468        if use_verbose(const_.ty(), true) {1469            self.push("mir::ConstOperand");1470            self.push(&format!(1471                "+ span: {}",1472                self.tcx.sess.source_map().span_to_diagnostic_string(*span)1473            ));1474            if let Some(user_ty) = user_ty {1475                self.push(&format!("+ user_ty: {user_ty:?}"));1476            }14771478            let fmt_val = |val: ConstValue, ty: Ty<'tcx>| {1479                let tcx = self.tcx;1480                rustc_data_structures::make_display(move |fmt| {1481                    pretty_print_const_value_tcx(tcx, val, ty, fmt)1482                })1483            };14841485            let fmt_valtree = |cv: &ty::Value<'tcx>| {1486                let mut p = FmtPrinter::new(self.tcx, Namespace::ValueNS);1487                p.pretty_print_const_valtree(*cv, /*print_ty*/ true).unwrap();1488                p.into_buffer()1489            };14901491            let val = match const_ {1492                Const::Ty(_, ct) => match ct.kind() {1493                    ty::ConstKind::Param(p) => format!("ty::Param({p})"),1494                    ty::ConstKind::Unevaluated(uv) => {1495                        let kind = match uv.kind {1496                            ty::UnevaluatedConstKind::Projection { def_id }1497                            | ty::UnevaluatedConstKind::Inherent { def_id }1498                            | ty::UnevaluatedConstKind::Free { def_id }1499                            | ty::UnevaluatedConstKind::Anon { def_id } => {1500                                self.tcx.def_path_str(def_id)1501                            }1502                        };1503                        format!("ty::Unevaluated({}, {:?})", kind, uv.args)1504                    }1505                    ty::ConstKind::Value(cv) => {1506                        format!("ty::Valtree({})", fmt_valtree(&cv))1507                    }1508                    // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.1509                    ty::ConstKind::Error(_) => "Error".to_string(),1510                    // These variants shouldn't exist in the MIR.1511                    ty::ConstKind::Placeholder(_)1512                    | ty::ConstKind::Infer(_)1513                    | ty::ConstKind::Expr(_)1514                    | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", const_),1515                },1516                Const::Unevaluated(uv, _) => {1517                    format!(1518                        "Unevaluated({}, {:?}, {:?})",1519                        self.tcx.def_path_str(uv.def),1520                        uv.args,1521                        uv.promoted,1522                    )1523                }1524                Const::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)),1525            };15261527            // This reflects what `Const` looked liked before `val` was renamed1528            // as `kind`. We print it like this to avoid having to update1529            // expected output in a lot of tests.1530            self.push(&format!("+ const_: Const {{ ty: {}, val: {} }}", const_.ty(), val));1531        }1532    }15331534    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {1535        self.super_rvalue(rvalue, location);1536        if let Rvalue::Aggregate(kind, _) = rvalue {1537            match **kind {1538                AggregateKind::Closure(def_id, args) => {1539                    self.push("closure");1540                    self.push(&format!("+ def_id: {def_id:?}"));1541                    self.push(&format!("+ args: {args:#?}"));1542                }15431544                AggregateKind::Coroutine(def_id, args) => {1545                    self.push("coroutine");1546                    self.push(&format!("+ def_id: {def_id:?}"));1547                    self.push(&format!("+ args: {args:#?}"));1548                    self.push(&format!("+ kind: {:?}", self.tcx.coroutine_kind(def_id)));1549                }15501551                AggregateKind::Adt(_, _, _, Some(user_ty), _) => {1552                    self.push("adt");1553                    self.push(&format!("+ user_ty: {user_ty:?}"));1554                }15551556                _ => {}1557            }1558        }1559    }1560}15611562fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {1563    let location = tcx.sess.source_map().span_to_diagnostic_string(span);1564    format!("scope {} at {}", scope.index(), location,)1565}15661567///////////////////////////////////////////////////////////////////////////1568// Allocations15691570/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding1571/// allocations.1572pub fn write_allocations<'tcx>(1573    tcx: TyCtxt<'tcx>,1574    body: &Body<'_>,1575    w: &mut dyn io::Write,1576) -> io::Result<()> {1577    fn alloc_ids_from_alloc(1578        alloc: ConstAllocation<'_>,1579    ) -> impl DoubleEndedIterator<Item = AllocId> {1580        alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id())1581    }15821583    fn alloc_id_from_const_val(val: ConstValue) -> Option<AllocId> {1584        match val {1585            ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()),1586            ConstValue::Scalar(interpret::Scalar::Int { .. }) => None,1587            ConstValue::ZeroSized => None,1588            ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => {1589                // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.1590                // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.1591                Some(alloc_id)1592            }1593        }1594    }1595    struct CollectAllocIds(BTreeSet<AllocId>);15961597    impl<'tcx> Visitor<'tcx> for CollectAllocIds {1598        fn visit_const_operand(&mut self, c: &ConstOperand<'tcx>, _: Location) {1599            match c.const_ {1600                Const::Ty(_, _) | Const::Unevaluated(..) => {}1601                Const::Val(val, _) => {1602                    if let Some(id) = alloc_id_from_const_val(val) {1603                        self.0.insert(id);1604                    }1605                }1606            }1607        }1608    }16091610    let mut visitor = CollectAllocIds(Default::default());1611    visitor.visit_body(body);16121613    // `seen` contains all seen allocations, including the ones we have *not* printed yet.1614    // The protocol is to first `insert` into `seen`, and only if that returns `true`1615    // then push to `todo`.1616    let mut seen = visitor.0;1617    let mut todo: Vec<_> = seen.iter().copied().collect();1618    while let Some(id) = todo.pop() {1619        let mut write_allocation_track_relocs =1620            |w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {1621                // `.rev()` because we are popping them from the back of the `todo` vector.1622                for id in alloc_ids_from_alloc(alloc).rev() {1623                    if seen.insert(id) {1624                        todo.push(id);1625                    }1626                }1627                write!(w, "{}", display_allocation(tcx, alloc.inner()))1628            };1629        write!(w, "\n{id:?}")?;1630        match tcx.try_get_global_alloc(id) {1631            // This can't really happen unless there are bugs, but it doesn't cost us anything to1632            // gracefully handle it and allow buggy rustc to be debugged via allocation printing.1633            None => write!(w, " (deallocated)")?,1634            Some(GlobalAlloc::Function { instance, .. }) => write!(w, " (fn: {instance})")?,1635            Some(GlobalAlloc::VTable(ty, dyn_ty)) => {1636                write!(w, " (vtable: impl {dyn_ty} for {ty})")?1637            }1638            Some(GlobalAlloc::TypeId { ty }) => write!(w, " (typeid for {ty})")?,1639            Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {1640                write!(w, " (static: {}", tcx.def_path_str(did))?;1641                if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup)1642                    && body1643                        .source1644                        .def_id()1645                        .as_local()1646                        .is_some_and(|def_id| tcx.hir_body_const_context(def_id).is_some())1647                {1648                    // Statics may be cyclic and evaluating them too early1649                    // in the MIR pipeline may cause cycle errors even though1650                    // normal compilation is fine.1651                    write!(w, ")")?;1652                } else {1653                    match tcx.eval_static_initializer(did) {1654                        Ok(alloc) => {1655                            write!(w, ", ")?;1656                            write_allocation_track_relocs(w, alloc)?;1657                        }1658                        Err(_) => write!(w, ", error during initializer evaluation)")?,1659                    }1660                }1661            }1662            Some(GlobalAlloc::Static(did)) => {1663                write!(w, " (extern static: {})", tcx.def_path_str(did))?1664            }1665            Some(GlobalAlloc::Memory(alloc)) => {1666                write!(w, " (")?;1667                write_allocation_track_relocs(w, alloc)?1668            }1669        }1670        writeln!(w)?;1671    }1672    Ok(())1673}16741675/// Dumps the size and metadata and content of an allocation to the given writer.1676/// The expectation is that the caller first prints other relevant metadata, so the exact1677/// format of this function is (*without* leading or trailing newline):1678///1679/// ```text1680/// size: {}, align: {}) {1681///     <bytes>1682/// }1683/// ```1684///1685/// The byte format is similar to how hex editors print bytes. Each line starts with the address of1686/// the start of the line, followed by all bytes in hex format (space separated).1687/// If the allocation is small enough to fit into a single line, no start address is given.1688/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control1689/// characters or characters whose value is larger than 127) with a `.`1690/// This also prints provenance adequately.1691pub fn display_allocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(1692    tcx: TyCtxt<'tcx>,1693    alloc: &'a Allocation<Prov, Extra, Bytes>,1694) -> RenderAllocation<'a, 'tcx, Prov, Extra, Bytes> {1695    RenderAllocation { tcx, alloc }1696}16971698#[doc(hidden)]1699pub struct RenderAllocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> {1700    tcx: TyCtxt<'tcx>,1701    alloc: &'a Allocation<Prov, Extra, Bytes>,1702}17031704impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> std::fmt::Display1705    for RenderAllocation<'a, 'tcx, Prov, Extra, Bytes>1706{1707    fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {1708        let RenderAllocation { tcx, alloc } = *self;1709        write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?;1710        if alloc.size() == Size::ZERO {1711            // We are done.1712            return write!(w, " {{}}");1713        }1714        if tcx.sess.opts.unstable_opts.dump_mir_exclude_alloc_bytes {1715            return write!(w, " {{ .. }}");1716        }1717        // Write allocation bytes.1718        writeln!(w, " {{")?;1719        write_allocation_bytes(tcx, alloc, w, "    ")?;1720        write!(w, "}}")?;1721        Ok(())1722    }1723}17241725fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result {1726    for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {1727        write!(w, "   ")?;1728    }1729    writeln!(w, " │ {ascii}")1730}17311732/// Number of bytes to print per allocation hex dump line.1733const BYTES_PER_LINE: usize = 16;17341735/// Prints the line start address and returns the new line start address.1736fn write_allocation_newline(1737    w: &mut dyn std::fmt::Write,1738    mut line_start: Size,1739    ascii: &str,1740    pos_width: usize,1741    prefix: &str,1742) -> Result<Size, std::fmt::Error> {1743    write_allocation_endline(w, ascii)?;1744    line_start += Size::from_bytes(BYTES_PER_LINE);1745    write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;1746    Ok(line_start)1747}17481749/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there1750/// is only one line). Note that your prefix should contain a trailing space as the lines are1751/// printed directly after it.1752pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(1753    tcx: TyCtxt<'tcx>,1754    alloc: &Allocation<Prov, Extra, Bytes>,1755    w: &mut dyn std::fmt::Write,1756    prefix: &str,1757) -> std::fmt::Result {1758    let num_lines = alloc.size().bytes_usize().saturating_sub(BYTES_PER_LINE);1759    // Number of chars needed to represent all line numbers.1760    let pos_width = hex_number_length(alloc.size().bytes());17611762    if num_lines > 0 {1763        write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;1764    } else {1765        write!(w, "{prefix}")?;1766    }17671768    let mut i = Size::ZERO;1769    let mut line_start = Size::ZERO;17701771    let ptr_size = tcx.data_layout.pointer_size();17721773    let mut ascii = String::new();17741775    let oversized_ptr = |target: &mut String, width| {1776        if target.len() > width {1777            write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();1778        }1779    };17801781    while i < alloc.size() {1782        // The line start already has a space. While we could remove that space from the line start1783        // printing and unconditionally print a space here, that would cause the single-line case1784        // to have a single space before it, which looks weird.1785        if i != line_start {1786            write!(w, " ")?;1787        }1788        if let Some(prov) = alloc.provenance().get_ptr(i) {1789            // Memory with provenance must be defined1790            assert!(alloc.init_mask().is_range_initialized(alloc_range(i, ptr_size)).is_ok());1791            let j = i.bytes_usize();1792            let offset = alloc1793                .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());1794            let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();1795            let offset = Size::from_bytes(offset);1796            let provenance_width = |bytes| bytes * 3;1797            let ptr = Pointer::new(prov, offset);1798            let mut target = format!("{ptr:?}");1799            if target.len() > provenance_width(ptr_size.bytes_usize() - 1) {1800                // This is too long, try to save some space.1801                target = format!("{ptr:#?}");1802            }1803            if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {1804                // This branch handles the situation where a provenance starts in the current line1805                // but ends in the next one.1806                let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);1807                let overflow = ptr_size - remainder;1808                let remainder_width = provenance_width(remainder.bytes_usize()) - 2;1809                let overflow_width = provenance_width(overflow.bytes_usize() - 1) + 1;1810                ascii.push('╾'); // HEAVY LEFT AND LIGHT RIGHT1811                for _ in 1..remainder.bytes() {1812                    ascii.push('─'); // LIGHT HORIZONTAL1813                }1814                if overflow_width > remainder_width && overflow_width >= target.len() {1815                    // The case where the provenance fits into the part in the next line1816                    write!(w, "╾{0:─^1$}", "", remainder_width)?;1817                    line_start =1818                        write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;1819                    ascii.clear();1820                    write!(w, "{target:─^overflow_width$}╼")?;1821                } else {1822                    oversized_ptr(&mut target, remainder_width);1823                    write!(w, "╾{target:─^remainder_width$}")?;1824                    line_start =1825                        write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;1826                    write!(w, "{0:─^1$}╼", "", overflow_width)?;1827                    ascii.clear();1828                }1829                for _ in 0..overflow.bytes() - 1 {1830                    ascii.push('─');1831                }1832                ascii.push('╼'); // LIGHT LEFT AND HEAVY RIGHT1833                i += ptr_size;1834                continue;1835            } else {1836                // This branch handles a provenance that starts and ends in the current line.1837                let provenance_width = provenance_width(ptr_size.bytes_usize() - 1);1838                oversized_ptr(&mut target, provenance_width);1839                ascii.push('╾');1840                write!(w, "╾{target:─^provenance_width$}╼")?;1841                for _ in 0..ptr_size.bytes() - 2 {1842                    ascii.push('─');1843                }1844                ascii.push('╼');1845                i += ptr_size;1846            }1847        } else if let Some(frag) = alloc.provenance().get_byte(i, &tcx) {1848            // Memory with provenance must be defined1849            assert!(1850                alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok()1851            );1852            ascii.push('━'); // HEAVY HORIZONTAL1853            // We have two characters to display this, which is obviously not enough.1854            // Format is similar to "oversized" above.1855            let j = i.bytes_usize();1856            let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];1857            // FIXME: Find a way to print `frag.offset` that does not look terrible...1858            write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼", prov = frag.prov, idx = frag.idx)?;1859            i += Size::from_bytes(1);1860        } else if alloc1861            .init_mask()1862            .is_range_initialized(alloc_range(i, Size::from_bytes(1)))1863            .is_ok()1864        {1865            let j = i.bytes_usize();18661867            // Checked definedness (and thus range) and provenance. This access also doesn't1868            // influence interpreter execution but is only for debugging.1869            let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];1870            write!(w, "{c:02x}")?;1871            if c.is_ascii_control() || c >= 0x80 {1872                ascii.push('.');1873            } else {1874                ascii.push(char::from(c));1875            }1876            i += Size::from_bytes(1);1877        } else {1878            write!(w, "__")?;1879            ascii.push('░');1880            i += Size::from_bytes(1);1881        }1882        // Print a new line header if the next line still has some bytes to print.1883        if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size() {1884            line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;1885            ascii.clear();1886        }1887    }1888    write_allocation_endline(w, &ascii)?;18891890    Ok(())1891}18921893///////////////////////////////////////////////////////////////////////////1894// Constants18951896fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {1897    write!(fmt, "b\"{}\"", byte_str.escape_ascii())1898}18991900fn comma_sep<'tcx>(1901    tcx: TyCtxt<'tcx>,1902    fmt: &mut Formatter<'_>,1903    elems: Vec<(ConstValue, Ty<'tcx>)>,1904) -> fmt::Result {1905    let mut first = true;1906    for (ct, ty) in elems {1907        if !first {1908            fmt.write_str(", ")?;1909        }1910        pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;1911        first = false;1912    }1913    Ok(())1914}19151916fn pretty_print_const_value_tcx<'tcx>(1917    tcx: TyCtxt<'tcx>,1918    ct: ConstValue,1919    ty: Ty<'tcx>,1920    fmt: &mut Formatter<'_>,1921) -> fmt::Result {1922    use crate::ty::print::PrettyPrinter;19231924    if tcx.sess.verbose_internals() {1925        fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;1926        return Ok(());1927    }19281929    // Printing [MaybeUninit<u8>::uninit(); N] or any other aggregate where all fields are uninit1930    // becomes very verbose. This special case makes the dump terse and clear.1931    if ct.all_bytes_uninit(tcx) {1932        fmt.write_str("<uninit>")?;1933        return Ok(());1934    }19351936    let u8_type = tcx.types.u8;1937    match (ct, ty.kind()) {1938        // Byte/string slices, printed as (byte) string literals.1939        (_, ty::Ref(_, inner_ty, _)) if let ty::Str = inner_ty.kind() => {1940            if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {1941                fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;1942                return Ok(());1943            }1944        }1945        (_, ty::Ref(_, inner_ty, _))1946            if let ty::Slice(t) = inner_ty.kind()1947                && *t == u8_type =>1948        {1949            if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {1950                pretty_print_byte_str(fmt, data)?;1951                return Ok(());1952            }1953        }1954        (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {1955            let n = n.try_to_target_usize(tcx).unwrap();1956            let alloc = tcx.global_alloc(alloc_id).unwrap_memory();1957            // cast is ok because we already checked for pointer size (32 or 64 bit) above1958            let range = AllocRange { start: offset, size: Size::from_bytes(n) };1959            let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();1960            fmt.write_str("*")?;1961            pretty_print_byte_str(fmt, byte_str)?;1962            return Ok(());1963        }1964        // Aggregates, printed as array/tuple/struct/variant construction syntax.1965        //1966        // NB: the `has_non_region_param` check ensures that we can use1967        // the `try_destructure_mir_constant_for_user_output ` query with1968        // an empty `TypingEnv::fully_monomorphized` without1969        // introducing ICEs (e.g. via `layout_of`) from missing bounds.1970        // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`1971        // to be able to destructure the tuple into `(0u8, *mut T)`1972        (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {1973            if let Some(contents) = tcx.try_destructure_mir_constant_for_user_output(ct, ty) {1974                let fields: Vec<(ConstValue, Ty<'_>)> = contents.fields.to_vec();1975                match *ty.kind() {1976                    ty::Array(..) => {1977                        fmt.write_str("[")?;1978                        comma_sep(tcx, fmt, fields)?;1979                        fmt.write_str("]")?;1980                    }1981                    ty::Tuple(..) => {1982                        fmt.write_str("(")?;1983                        comma_sep(tcx, fmt, fields)?;1984                        if contents.fields.len() == 1 {1985                            fmt.write_str(",")?;1986                        }1987                        fmt.write_str(")")?;1988                    }1989                    ty::Adt(def, _) if def.variants().is_empty() => {1990                        fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;1991                    }1992                    ty::Adt(def, args) => {1993                        let variant_idx = contents1994                            .variant1995                            .expect("destructed mir constant of adt without variant idx");1996                        let variant_def = &def.variant(variant_idx);1997                        let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);1998                        p.print_alloc_ids = true;1999                        p.pretty_print_value_path(variant_def.def_id, args)?;2000                        fmt.write_str(&p.into_buffer())?;

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.