compiler/rustc_middle/src/mir/pretty.rs RUST 2,018 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,018.
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::*;1819const INDENT: &str = "    ";20/// Alignment for lining up comments following MIR statements21pub(crate) const ALIGN: usize = 40;2223/// An indication of where we are in the control flow graph. Used for printing24/// 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,2930    /// We just finished dumping the control flow graph. This is right before EOF31    AfterCFG,3233    /// We are about to start dumping the given basic block.34    BeforeBlock(BasicBlock),3536    /// We are just about to dump the given statement or terminator.37    BeforeLocation(Location),3839    /// We just dumped the given statement or terminator.40    AfterLocation(Location),4142    /// We just dumped the terminator for a block but not the closing `}`.43    AfterTerminator(BasicBlock),44}4546/// Cosmetic options for pretty-printing the MIR contents, gathered from the CLI. Each pass can47/// 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}5354impl 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}6061/// 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 the63/// command line. Layered on top of `MirWriter`, which does the actual writing.64pub struct MirDumper<'a, 'tcx> {65    show_pass_num: bool,66    pass_name: &'static str,67    disambiguator: &'a dyn Display,68    writer: MirWriter<'a, 'tcx>,69}7071impl<'a, 'tcx> MirDumper<'a, 'tcx> {72    // If dumping should be performed (e.g. because it was requested on the73    // 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-op77    // - `writer.options`: default options derived from CLI flags78    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 below81            let node_path = ty::print::with_no_trimmed_paths!(82                ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()))83            );84            filters.split('|').any(|or_filter| {85                or_filter.split('&').all(|and_filter| {86                    let and_filter_trimmed = and_filter.trim();87                    and_filter_trimmed == "all"88                        || pass_name.contains(and_filter_trimmed)89                        || node_path.contains(and_filter_trimmed)90                })91            })92        } else {93            false94        };9596        dump_enabled.then_some(MirDumper {97            show_pass_num: false,98            pass_name,99            disambiguator: &0,100            writer: MirWriter::new(tcx),101        })102    }103104    pub fn tcx(&self) -> TyCtxt<'tcx> {105        self.writer.tcx106    }107108    #[must_use]109    pub fn set_show_pass_num(mut self) -> Self {110        self.show_pass_num = true;111        self112    }113114    #[must_use]115    pub fn set_disambiguator(mut self, disambiguator: &'a dyn Display) -> Self {116        self.disambiguator = disambiguator;117        self118    }119120    #[must_use]121    pub fn set_extra_data(122        mut self,123        extra_data: &'a dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,124    ) -> Self {125        self.writer.extra_data = extra_data;126        self127    }128129    #[must_use]130    pub fn set_options(mut self, options: PrettyPrintMirOptions) -> Self {131        self.writer.options = options;132        self133    }134135    /// If the session is properly configured, dumps a human-readable representation of the MIR136    /// (with default pretty-printing options) into:137    ///138    /// ```text139    /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>140    /// ```141    ///142    /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,143    /// where `<filter>` takes the following forms:144    ///145    /// - `all` -- dump MIR for all fns, all passes, all everything146    /// - a filter defined by a set of substrings combined with `&` and `|`147    ///   (`&` has higher precedence). At least one of the `|`-separated groups148    ///   must match; an `|`-separated group matches if all of its `&`-separated149    ///   substrings are matched.150    ///151    /// Example:152    ///153    /// - `nll` == match if `nll` appears in the name154    /// - `foo & nll` == match if `foo` and `nll` both appear in the name155    /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name156    ///   or `typeck` appears in the name.157    /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name158    ///   or `typeck` and `bar` both appear in the name.159    pub fn dump_mir(&self, body: &Body<'tcx>) {160        let _ = try {161            let mut file = self.create_dump_file("mir", body)?;162            self.dump_mir_to_writer(body, &mut file)?;163        };164165        if self.tcx().sess.opts.unstable_opts.dump_mir_graphviz {166            let _ = try {167                let mut file = self.create_dump_file("dot", body)?;168                write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?;169            };170        }171    }172173    // #41697 -- we use `with_forced_impl_filename_line()` because `def_path_str()` would otherwise174    // trigger `type_of`, and this can run while we are already attempting to evaluate `type_of`.175    pub fn dump_mir_to_writer(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {176        // see notes on #41697 above177        let def_path =178            ty::print::with_no_trimmed_paths!(ty::print::with_forced_impl_filename_line!(179                self.tcx().def_path_str(body.source.def_id())180            ));181        // ignore-tidy-odd-backticks the literal below is fine182        write!(w, "// MIR for `{def_path}")?;183        match body.source.promoted {184            None => write!(w, "`")?,185            Some(promoted) => write!(w, "::{promoted:?}`")?,186        }187        writeln!(w, " {} {}", self.disambiguator, self.pass_name)?;188        if let Some(ref layout) = body.coroutine_layout_raw() {189            writeln!(w, "/* coroutine_layout = {layout:#?} */")?;190        }191        writeln!(w)?;192        (self.writer.extra_data)(PassWhere::BeforeCFG, w)?;193        write_user_type_annotations(self.tcx(), body, w)?;194        self.writer.write_mir_fn(body, w)?;195        (self.writer.extra_data)(PassWhere::AfterCFG, w)196    }197198    /// Returns the path to the filename where we should dump a given MIR.199    /// Also used by other bits of code (e.g., NLL inference) that dump200    /// graphviz data or other things.201    fn dump_path(&self, extension: &str, body: &Body<'tcx>) -> PathBuf {202        let tcx = self.tcx();203        let source = body.source;204        let promotion_id = match source.promoted {205            Some(id) => format!("-{id:?}"),206            None => String::new(),207        };208209        let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {210            String::new()211        } else if self.show_pass_num {212            let (dialect_index, phase_index) = body.phase.index();213            format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count)214        } else {215            ".-------".to_string()216        };217218        let crate_name = tcx.crate_name(source.def_id().krate);219        let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();220        // All drop shims have the same DefId, so we have to add the type221        // to get unique file names.222        let shim_disambiguator = match source.instance {223            ty::InstanceKind::DropGlue(_, Some(ty)) => {224                // Unfortunately, pretty-printed types are not very filename-friendly.225                // We do some filtering.226                let mut s = ".".to_owned();227                s.extend(ty.to_string().chars().filter_map(|c| match c {228                    ' ' => None,229                    ':' | '<' | '>' => Some('_'),230                    c => Some(c),231                }));232                s233            }234            ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {235                let mut s = ".".to_owned();236                s.extend(ty.to_string().chars().filter_map(|c| match c {237                    ' ' => None,238                    ':' | '<' | '>' => Some('_'),239                    c => Some(c),240                }));241                s242            }243            ty::InstanceKind::AsyncDropGlue(_, ty) => {244                let ty::Coroutine(_, args) = ty.kind() else {245                    bug!();246                };247                let ty = args.first().unwrap().expect_ty();248                let mut s = ".".to_owned();249                s.extend(ty.to_string().chars().filter_map(|c| match c {250                    ' ' => None,251                    ':' | '<' | '>' => Some('_'),252                    c => Some(c),253                }));254                s255            }256            ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {257                let mut s = ".".to_owned();258                s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {259                    ' ' => None,260                    ':' | '<' | '>' => Some('_'),261                    c => Some(c),262                }));263                s.push('.');264                s.extend(impl_cor.to_string().chars().filter_map(|c| match c {265                    ' ' => None,266                    ':' | '<' | '>' => Some('_'),267                    c => Some(c),268                }));269                s270            }271            _ => String::new(),272        };273274        let mut file_path = PathBuf::new();275        file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));276277        let pass_name = self.pass_name;278        let disambiguator = self.disambiguator;279        let file_name = format!(280            "{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",281        );282283        file_path.push(&file_name);284285        file_path286    }287288    /// Attempts to open a file where we should dump a given MIR or other289    /// bit of MIR-related data. Used by `mir-dump`, but also by other290    /// bits of code (e.g., NLL inference) that dump graphviz data or291    /// other things, and hence takes the extension as an argument.292    pub fn create_dump_file(293        &self,294        extension: &str,295        body: &Body<'tcx>,296    ) -> io::Result<io::BufWriter<fs::File>> {297        let file_path = self.dump_path(extension, body);298        if let Some(parent) = file_path.parent() {299            fs::create_dir_all(parent).map_err(|e| {300                io::Error::new(301                    e.kind(),302                    format!("IO error creating MIR dump directory: {parent:?}; {e}"),303                )304            })?;305        }306        fs::File::create_buffered(&file_path).map_err(|e| {307            io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))308        })309    }310}311312///////////////////////////////////////////////////////////////////////////313// Whole MIR bodies314315/// Write out a human-readable textual representation for the given MIR, with the default316/// [PrettyPrintMirOptions].317pub fn write_mir_pretty<'tcx>(318    tcx: TyCtxt<'tcx>,319    single: Option<DefId>,320    w: &mut dyn io::Write,321) -> io::Result<()> {322    let writer = MirWriter::new(tcx);323324    writeln!(w, "// WARNING: This output format is intended for human consumers only")?;325    writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;326    writeln!(w, "// HINT: See also -Z dump-mir for MIR at specific points during compilation.")?;327328    let mut first = true;329    for def_id in dump_mir_def_ids(tcx, single) {330        if first {331            first = false;332        } else {333            // Put empty lines between all items334            writeln!(w)?;335        }336337        let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {338            writer.write_mir_fn(body, w)?;339340            for body in tcx.promoted_mir(def_id) {341                writeln!(w)?;342                writer.write_mir_fn(body, w)?;343            }344            Ok(())345        };346347        // For `const fn` we want to render both the optimized MIR and the MIR for ctfe.348        if tcx.is_const_fn(def_id) {349            render_body(w, tcx.optimized_mir(def_id))?;350            writeln!(w)?;351            writeln!(w, "// MIR FOR CTFE")?;352            // Do not use `render_body`, as that would render the promoteds again, but these353            // are shared between mir_for_ctfe and optimized_mir354            writer.write_mir_fn(tcx.mir_for_ctfe(def_id), w)?;355        } else {356            if let Some((val, ty)) = tcx.trivial_const(def_id) {357                ty::print::with_forced_impl_filename_line! {358                    // see notes on #41697 elsewhere359                    write!(w, "const {}", tcx.def_path_str(def_id))?360                }361                writeln!(w, ": {} = const {};", ty, Const::Val(val, ty))?;362            } else {363                let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));364                render_body(w, instance_mir)?;365            }366        }367    }368    Ok(())369}370371/// Does the writing of MIR to output, e.g. a file.372pub struct MirWriter<'a, 'tcx> {373    tcx: TyCtxt<'tcx>,374    extra_data: &'a dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,375    options: PrettyPrintMirOptions,376}377378impl<'a, 'tcx> MirWriter<'a, 'tcx> {379    pub fn new(tcx: TyCtxt<'tcx>) -> Self {380        MirWriter { tcx, extra_data: &|_, _| Ok(()), options: PrettyPrintMirOptions::from_cli(tcx) }381    }382383    /// Write out a human-readable textual representation for the given function.384    pub fn write_mir_fn(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {385        write_mir_intro(self.tcx, body, w, self.options)?;386        for block in body.basic_blocks.indices() {387            (self.extra_data)(PassWhere::BeforeBlock(block), w)?;388            self.write_basic_block(block, body, w)?;389            if block.index() + 1 != body.basic_blocks.len() {390                writeln!(w)?;391            }392        }393394        writeln!(w, "}}")?;395396        write_allocations(self.tcx, body, w)?;397398        Ok(())399    }400}401402/// Prints local variables in a scope tree.403fn write_scope_tree(404    tcx: TyCtxt<'_>,405    body: &Body<'_>,406    scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,407    w: &mut dyn io::Write,408    parent: SourceScope,409    depth: usize,410    options: PrettyPrintMirOptions,411) -> io::Result<()> {412    let indent = depth * INDENT.len();413414    // Local variable debuginfo.415    for var_debug_info in &body.var_debug_info {416        if var_debug_info.source_info.scope != parent {417            // Not declared in this scope.418            continue;419        }420421        let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);422423        if options.include_extra_comments {424            writeln!(425                w,426                "{0:1$} // in {2}",427                indented_debug_info,428                ALIGN,429                comment(tcx, var_debug_info.source_info),430            )?;431        } else {432            writeln!(w, "{indented_debug_info}")?;433        }434    }435436    // Local variable types.437    for (local, local_decl) in body.local_decls.iter_enumerated() {438        if (1..body.arg_count + 1).contains(&local.index()) {439            // Skip over argument locals, they're printed in the signature.440            continue;441        }442443        if local_decl.source_info.scope != parent {444            // Not declared in this scope.445            continue;446        }447448        let mut_str = local_decl.mutability.prefix_str();449450        let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(451            "{0:1$}let {2}{3:?}: {4}",452            INDENT, indent, mut_str, local, local_decl.ty453        ));454        if let Some(user_ty) = &local_decl.user_ty {455            for user_ty in user_ty.projections() {456                write!(indented_decl, " as {user_ty:?}").unwrap();457            }458        }459        indented_decl.push(';');460461        let local_name = if local == RETURN_PLACE { " return place" } else { "" };462463        if options.include_extra_comments {464            writeln!(465                w,466                "{0:1$} //{2} in {3}",467                indented_decl,468                ALIGN,469                local_name,470                comment(tcx, local_decl.source_info),471            )?;472        } else {473            writeln!(w, "{indented_decl}",)?;474        }475    }476477    let Some(children) = scope_tree.get(&parent) else {478        return Ok(());479    };480481    for &child in children {482        let child_data = &body.source_scopes[child];483        assert_eq!(child_data.parent_scope, Some(parent));484485        let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {486            (487                format!(488                    " (inlined {}{})",489                    if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },490                    callee491                ),492                Some(callsite_span),493            )494        } else {495            (String::new(), None)496        };497498        let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);499500        if options.include_extra_comments {501            if let Some(span) = span {502                writeln!(503                    w,504                    "{0:1$} // at {2}",505                    indented_header,506                    ALIGN,507                    tcx.sess.source_map().span_to_diagnostic_string(span),508                )?;509            } else {510                writeln!(w, "{indented_header}")?;511            }512        } else {513            writeln!(w, "{indented_header}")?;514        }515516        write_scope_tree(tcx, body, scope_tree, w, child, depth + 1, options)?;517        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;518    }519520    Ok(())521}522523impl Debug for VarDebugInfo<'_> {524    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {525        if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {526            pre_fmt_projection(&projection[..], fmt)?;527            write!(fmt, "({}: {})", self.name, ty)?;528            post_fmt_projection(&projection[..], fmt)?;529        } else {530            write!(fmt, "{}", self.name)?;531        }532533        write!(fmt, " => {:?}", self.value)534    }535}536537/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its538/// local variables (both user-defined bindings and compiler temporaries).539fn write_mir_intro<'tcx>(540    tcx: TyCtxt<'tcx>,541    body: &Body<'_>,542    w: &mut dyn io::Write,543    options: PrettyPrintMirOptions,544) -> io::Result<()> {545    write_mir_sig(tcx, body, w)?;546    writeln!(w, "{{")?;547548    // construct a scope tree and write it out549    let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();550    for (index, scope_data) in body.source_scopes.iter_enumerated() {551        if let Some(parent) = scope_data.parent_scope {552            scope_tree.entry(parent).or_default().push(index);553        } else {554            // Only the argument scope has no parent, because it's the root.555            assert_eq!(index, OUTERMOST_SOURCE_SCOPE);556        }557    }558559    write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1, options)?;560561    // Add an empty line before the first block is printed.562    writeln!(w)?;563564    if let Some(coverage_info_hi) = &body.coverage_info_hi {565        write_coverage_info_hi(coverage_info_hi, w)?;566    }567    if let Some(function_coverage_info) = &body.function_coverage_info {568        write_function_coverage_info(function_coverage_info, w)?;569    }570571    Ok(())572}573574fn write_coverage_info_hi(575    coverage_info_hi: &coverage::CoverageInfoHi,576    w: &mut dyn io::Write,577) -> io::Result<()> {578    let coverage::CoverageInfoHi { num_block_markers: _, branch_spans } = coverage_info_hi;579580    // Only add an extra trailing newline if we printed at least one thing.581    let mut did_print = false;582583    for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {584        writeln!(585            w,586            "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",587        )?;588        did_print = true;589    }590591    if did_print {592        writeln!(w)?;593    }594595    Ok(())596}597598fn write_function_coverage_info(599    function_coverage_info: &coverage::FunctionCoverageInfo,600    w: &mut dyn io::Write,601) -> io::Result<()> {602    let coverage::FunctionCoverageInfo { mappings, .. } = function_coverage_info;603604    for coverage::Mapping { kind, span } in mappings {605        writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?;606    }607    writeln!(w)?;608609    Ok(())610}611612fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io::Result<()> {613    use rustc_hir::def::DefKind;614615    trace!("write_mir_sig: {:?}", body.source.instance);616    let def_id = body.source.def_id();617    let kind = tcx.def_kind(def_id);618    let is_function = match kind {619        DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::SyntheticCoroutineBody => {620            true621        }622        _ => tcx.is_closure_like(def_id),623    };624    match (kind, body.source.promoted) {625        (_, Some(_)) => write!(w, "const ")?, // promoteds are the closest to consts626        (DefKind::Const { .. } | DefKind::AssocConst { .. }, _) => write!(w, "const ")?,627        (DefKind::Static { safety: _, mutability: hir::Mutability::Not, nested: false }, _) => {628            write!(w, "static ")?629        }630        (DefKind::Static { safety: _, mutability: hir::Mutability::Mut, nested: false }, _) => {631            write!(w, "static mut ")?632        }633        (_, _) if is_function => write!(w, "fn ")?,634        // things like anon const, not an item635        (DefKind::AnonConst | DefKind::InlineConst, _) => {}636        // `global_asm!` have fake bodies, which we may dump after mir-build637        (DefKind::GlobalAsm, _) => {}638        _ => bug!("Unexpected def kind {:?}", kind),639    }640641    ty::print::with_forced_impl_filename_line! {642        // see notes on #41697 elsewhere643        write!(w, "{}", tcx.def_path_str(def_id))?644    }645    if let Some(p) = body.source.promoted {646        write!(w, "::{p:?}")?;647    }648649    if body.source.promoted.is_none() && is_function {650        write!(w, "(")?;651652        // fn argument types.653        for (i, arg) in body.args_iter().enumerate() {654            if i != 0 {655                write!(w, ", ")?;656            }657            write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;658        }659660        write!(w, ") -> {}", body.return_ty())?;661    } else {662        assert_eq!(body.arg_count, 0);663        write!(w, ": {} =", body.return_ty())?;664    }665666    if let Some(yield_ty) = body.yield_ty() {667        writeln!(w)?;668        writeln!(w, "yields {yield_ty}")?;669    }670671    write!(w, " ")?;672    // Next thing that gets printed is the opening {673674    Ok(())675}676677fn write_user_type_annotations(678    tcx: TyCtxt<'_>,679    body: &Body<'_>,680    w: &mut dyn io::Write,681) -> io::Result<()> {682    if !body.user_type_annotations.is_empty() {683        writeln!(w, "| User Type Annotations")?;684    }685    for (index, annotation) in body.user_type_annotations.iter_enumerated() {686        writeln!(687            w,688            "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",689            index.index(),690            annotation.user_ty,691            tcx.sess.source_map().span_to_diagnostic_string(annotation.span),692            with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),693        )?;694    }695    if !body.user_type_annotations.is_empty() {696        writeln!(w, "|")?;697    }698    Ok(())699}700701pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {702    if let Some(i) = single {703        vec![i]704    } else {705        tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()706    }707}708709///////////////////////////////////////////////////////////////////////////710// Basic blocks and their parts (statements, terminators, ...)711712impl<'a, 'tcx> MirWriter<'a, 'tcx> {713    /// Write out a human-readable textual representation for the given basic block.714    fn write_basic_block(715        &self,716        block: BasicBlock,717        body: &Body<'tcx>,718        w: &mut dyn io::Write,719    ) -> io::Result<()> {720        let data = &body[block];721722        // Basic block label at the top.723        let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };724        writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;725726        // List of statements in the middle.727        let mut current_location = Location { block, statement_index: 0 };728        for statement in &data.statements {729            (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;730731            for debuginfo in statement.debuginfos.iter() {732                writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?;733            }734735            let indented_body = format!("{INDENT}{INDENT}{statement:?};");736            if self.options.include_extra_comments {737                writeln!(738                    w,739                    "{:A$} // {}{}",740                    indented_body,741                    if self.tcx.sess.verbose_internals() {742                        format!("{current_location:?}: ")743                    } else {744                        String::new()745                    },746                    comment(self.tcx, statement.source_info),747                    A = ALIGN,748                )?;749            } else {750                writeln!(w, "{indented_body}")?;751            }752753            write_extra(754                self.tcx,755                w,756                &|visitor| visitor.visit_statement(statement, current_location),757                self.options,758            )?;759760            (self.extra_data)(PassWhere::AfterLocation(current_location), w)?;761762            current_location.statement_index += 1;763        }764765        for debuginfo in data.after_last_stmt_debuginfos.iter() {766            writeln!(w, "{INDENT}{INDENT}// DBG: {debuginfo:?};")?;767        }768769        // Terminator at the bottom.770        (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;771        if data.terminator.is_some() {772            let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);773            if self.options.include_extra_comments {774                writeln!(775                    w,776                    "{:A$} // {}{}",777                    indented_terminator,778                    if self.tcx.sess.verbose_internals() {779                        format!("{current_location:?}: ")780                    } else {781                        String::new()782                    },783                    comment(self.tcx, data.terminator().source_info),784                    A = ALIGN,785                )?;786            } else {787                writeln!(w, "{indented_terminator}")?;788            }789790            write_extra(791                self.tcx,792                w,793                &|visitor| visitor.visit_terminator(data.terminator(), current_location),794                self.options,795            )?;796        }797798        (self.extra_data)(PassWhere::AfterLocation(current_location), w)?;799        (self.extra_data)(PassWhere::AfterTerminator(block), w)?;800801        writeln!(w, "{INDENT}}}")802    }803}804805impl Debug for StatementKind<'_> {806    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {807        use self::StatementKind::*;808        match *self {809            Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),810            FakeRead(box (ref cause, ref place)) => {811                write!(fmt, "FakeRead({cause:?}, {place:?})")812            }813            StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),814            StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),815            SetDiscriminant { ref place, variant_index } => {816                write!(fmt, "discriminant({place:?}) = {variant_index:?}")817            }818            PlaceMention(ref place) => {819                write!(fmt, "PlaceMention({place:?})")820            }821            AscribeUserType(box (ref place, ref c_ty), ref variance) => {822                write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")823            }824            Coverage(ref kind) => write!(fmt, "Coverage::{kind:?}"),825            Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),826            ConstEvalCounter => write!(fmt, "ConstEvalCounter"),827            Nop => write!(fmt, "nop"),828            BackwardIncompatibleDropHint { ref place, reason: _ } => {829                // For now, we don't record the reason because there is only one use case,830                // which is to report breaking change in drop order by Edition 2024831                write!(fmt, "BackwardIncompatibleDropHint({place:?})")832            }833        }834    }835}836impl Debug for Statement<'_> {837    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {838        self.kind.fmt(fmt)839    }840}841842impl Debug for StmtDebugInfo<'_> {843    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {844        match self {845            StmtDebugInfo::AssignRef(local, place) => {846                write!(fmt, "{local:?} = &{place:?}")847            }848            StmtDebugInfo::InvalidAssign(local) => {849                write!(fmt, "{local:?} = &?")850            }851        }852    }853}854855impl Display for NonDivergingIntrinsic<'_> {856    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {857        match self {858            Self::Assume(op) => write!(f, "assume({op:?})"),859            Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => {860                write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})")861            }862        }863    }864}865866impl<'tcx> Debug for TerminatorKind<'tcx> {867    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {868        self.fmt_head(fmt)?;869        let successor_count = self.successors().count();870        let labels = self.fmt_successor_labels();871        assert_eq!(successor_count, labels.len());872873        // `Cleanup` is already included in successors874        let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));875        let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {876            write!(fmt, "unwind ")?;877            match self.unwind() {878                // Not needed or included in successors879                None | Some(UnwindAction::Cleanup(_)) => unreachable!(),880                Some(UnwindAction::Continue) => write!(fmt, "continue"),881                Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),882                Some(UnwindAction::Terminate(reason)) => {883                    write!(fmt, "terminate({})", reason.as_short_str())884                }885            }886        };887888        match (successor_count, show_unwind) {889            (0, false) => Ok(()),890            (0, true) => {891                write!(fmt, " -> ")?;892                fmt_unwind(fmt)893            }894            (1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),895            _ => {896                write!(fmt, " -> [")?;897                for (i, target) in self.successors().enumerate() {898                    if i > 0 {899                        write!(fmt, ", ")?;900                    }901                    write!(fmt, "{}: {:?}", labels[i], target)?;902                }903                if show_unwind {904                    write!(fmt, ", ")?;905                    fmt_unwind(fmt)?;906                }907                write!(fmt, "]")908            }909        }910    }911}912impl Debug for Terminator<'_> {913    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {914        self.kind.fmt(fmt)915    }916}917918impl<'tcx> TerminatorKind<'tcx> {919    /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the920    /// successor basic block, if any. The only information not included is the list of possible921    /// successors, which may be rendered differently between the text and the graphviz format.922    pub fn fmt_head<W: fmt::Write>(&self, fmt: &mut W) -> fmt::Result {923        use self::TerminatorKind::*;924        match self {925            Goto { .. } => write!(fmt, "goto"),926            SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),927            Return => write!(fmt, "return"),928            CoroutineDrop => write!(fmt, "coroutine_drop"),929            UnwindResume => write!(fmt, "resume"),930            UnwindTerminate(reason) => {931                write!(fmt, "terminate({})", reason.as_short_str())932            }933            Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),934            Unreachable => write!(fmt, "unreachable"),935            Drop { place, async_fut: None, .. } => write!(fmt, "drop({place:?})"),936            Drop { place, async_fut: Some(async_fut), .. } => {937                write!(fmt, "async drop({place:?}; poll={async_fut:?})")938            }939            Call { func, args, destination, .. } => {940                write!(fmt, "{destination:?} = ")?;941                write!(fmt, "{func:?}(")?;942                for (index, arg) in args.iter().enumerate() {943                    if index > 0 {944                        write!(fmt, ", ")?;945                    }946                    write!(fmt, "{:?}", arg.node)?;947                }948                write!(fmt, ")")949            }950            TailCall { func, args, .. } => {951                write!(fmt, "tailcall {func:?}(")?;952                for (index, arg) in args.iter().enumerate() {953                    if index > 0 {954                        write!(fmt, ", ")?;955                    }956                    write!(fmt, "{:?}", arg.node)?;957                }958                write!(fmt, ")")959            }960            Assert { cond, expected, msg, .. } => {961                write!(fmt, "assert(")?;962                if !expected {963                    write!(fmt, "!")?;964                }965                write!(fmt, "{cond:?}, ")?;966                msg.fmt_assert_args(fmt)?;967                write!(fmt, ")")968            }969            FalseEdge { .. } => write!(fmt, "falseEdge"),970            FalseUnwind { .. } => write!(fmt, "falseUnwind"),971            InlineAsm { template, operands, options, .. } => {972                write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;973                for op in operands {974                    write!(fmt, ", ")?;975                    let print_late = |&late| if late { "late" } else { "" };976                    match op {977                        InlineAsmOperand::In { reg, value } => {978                            write!(fmt, "in({reg}) {value:?}")?;979                        }980                        InlineAsmOperand::Out { reg, late, place: Some(place) } => {981                            write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;982                        }983                        InlineAsmOperand::Out { reg, late, place: None } => {984                            write!(fmt, "{}out({}) _", print_late(late), reg)?;985                        }986                        InlineAsmOperand::InOut {987                            reg,988                            late,989                            in_value,990                            out_place: Some(out_place),991                        } => {992                            write!(993                                fmt,994                                "in{}out({}) {:?} => {:?}",995                                print_late(late),996                                reg,997                                in_value,998                                out_place999                            )?;1000                        }1001                        InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {1002                            write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;1003                        }1004                        InlineAsmOperand::Const { value } => {1005                            write!(fmt, "const {value:?}")?;1006                        }1007                        InlineAsmOperand::SymFn { value } => {1008                            write!(fmt, "sym_fn {value:?}")?;1009                        }1010                        InlineAsmOperand::SymStatic { def_id } => {1011                            write!(fmt, "sym_static {def_id:?}")?;1012                        }1013                        InlineAsmOperand::Label { target_index } => {1014                            write!(fmt, "label {target_index}")?;1015                        }1016                    }1017                }1018                write!(fmt, ", options({options:?}))")1019            }1020        }1021    }10221023    /// Returns the list of labels for the edges to the successor basic blocks.1024    pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {1025        use self::TerminatorKind::*;1026        match *self {1027            Return1028            | TailCall { .. }1029            | UnwindResume1030            | UnwindTerminate(_)1031            | Unreachable1032            | CoroutineDrop => vec![],1033            Goto { .. } => vec!["".into()],1034            SwitchInt { ref targets, .. } => targets1035                .values1036                .iter()1037                .map(|&u| Cow::Owned(u.to_string()))1038                .chain(iter::once("otherwise".into()))1039                .collect(),1040            Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {1041                vec!["return".into(), "unwind".into()]1042            }1043            Call { target: Some(_), unwind: _, .. } => vec!["return".into()],1044            Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],1045            Call { target: None, unwind: _, .. } => vec![],1046            Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],1047            Yield { drop: None, .. } => vec!["resume".into()],1048            Drop { unwind: UnwindAction::Cleanup(_), drop: Some(_), .. } => {1049                vec!["return".into(), "unwind".into(), "drop".into()]1050            }1051            Drop { unwind: UnwindAction::Cleanup(_), drop: None, .. } => {1052                vec!["return".into(), "unwind".into()]1053            }1054            Drop { unwind: _, drop: Some(_), .. } => vec!["return".into(), "drop".into()],1055            Drop { unwind: _, .. } => vec!["return".into()],1056            Assert { unwind: UnwindAction::Cleanup(_), .. } => {1057                vec!["success".into(), "unwind".into()]1058            }1059            Assert { unwind: _, .. } => vec!["success".into()],1060            FalseEdge { .. } => vec!["real".into(), "imaginary".into()],1061            FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {1062                vec!["real".into(), "unwind".into()]1063            }1064            FalseUnwind { unwind: _, .. } => vec!["real".into()],1065            InlineAsm { asm_macro, options, ref targets, unwind, .. } => {1066                let mut vec = Vec::with_capacity(targets.len() + 1);1067                if !asm_macro.diverges(options) {1068                    vec.push("return".into());1069                }1070                vec.resize(targets.len(), "label".into());10711072                if let UnwindAction::Cleanup(_) = unwind {1073                    vec.push("unwind".into());1074                }10751076                vec1077            }1078        }1079    }1080}10811082impl<'tcx> Debug for Rvalue<'tcx> {1083    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1084        use self::Rvalue::*;10851086        match *self {1087            Use(ref operand, with_retag) => {1088                // With retag is more common so we only print when it's without.1089                write!(fmt, "{}{operand:?}", if with_retag.no() { "no_retag " } else { "" })1090            }1091            Repeat(ref a, b) => {1092                write!(fmt, "[{a:?}; ")?;1093                pretty_print_const(b, fmt, false)?;1094                write!(fmt, "]")1095            }1096            Cast(ref kind, ref place, ref ty) => {1097                with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))1098            }1099            BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),1100            UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),1101            Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),1102            ThreadLocalRef(did) => ty::tls::with(|tcx| {1103                let muta = tcx.static_mutability(did).unwrap().prefix_str();1104                write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))1105            }),1106            Ref(region, borrow_kind, ref place) => {1107                let kind_str = match borrow_kind {1108                    BorrowKind::Shared => "",1109                    BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ",1110                    BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ",1111                    BorrowKind::Mut { .. } => "mut ",1112                };11131114                // When printing regions, add trailing space if necessary.1115                let print_region = ty::tls::with(|tcx| {1116                    tcx.sess.verbose_internals() || tcx.sess.opts.unstable_opts.identify_regions1117                });1118                let region = if print_region {1119                    let mut region = region.to_string();1120                    if !region.is_empty() {1121                        region.push(' ');1122                    }1123                    region1124                } else {1125                    // Do not even print 'static1126                    String::new()1127                };1128                write!(fmt, "&{region}{kind_str}{place:?}")1129            }11301131            CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),11321133            RawPtr(mutability, ref place) => {1134                write!(fmt, "&raw {mut_str} {place:?}", mut_str = mutability.ptr_str())1135            }11361137            Aggregate(ref kind, ref places) => {1138                let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {1139                    let mut tuple_fmt = fmt.debug_tuple(name);1140                    for place in places {1141                        tuple_fmt.field(place);1142                    }1143                    tuple_fmt.finish()1144                };11451146                match **kind {1147                    AggregateKind::Array(_) => write!(fmt, "{places:?}"),11481149                    AggregateKind::Tuple => {1150                        if places.is_empty() {1151                            write!(fmt, "()")1152                        } else {1153                            fmt_tuple(fmt, "")1154                        }1155                    }11561157                    AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {1158                        ty::tls::with(|tcx| {1159                            let variant_def = &tcx.adt_def(adt_did).variant(variant);1160                            let args = tcx.lift(args);1161                            let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |p| {1162                                p.print_def_path(variant_def.def_id, args)1163                            })?;11641165                            match variant_def.ctor_kind() {1166                                Some(CtorKind::Const) => fmt.write_str(&name),1167                                Some(CtorKind::Fn) => fmt_tuple(fmt, &name),1168                                None => {1169                                    let mut struct_fmt = fmt.debug_struct(&name);1170                                    for (field, place) in iter::zip(&variant_def.fields, places) {1171                                        struct_fmt.field(field.name.as_str(), place);1172                                    }1173                                    struct_fmt.finish()1174                                }1175                            }1176                        })1177                    }11781179                    AggregateKind::Closure(def_id, args)1180                    | AggregateKind::CoroutineClosure(def_id, args) => ty::tls::with(|tcx| {1181                        let name = if tcx.sess.opts.unstable_opts.span_free_formats {1182                            let args = tcx.lift(args);1183                            format!("{{closure@{}}}", tcx.def_path_str_with_args(def_id, args),)1184                        } else {1185                            let span = tcx.def_span(def_id);1186                            format!(1187                                "{{closure@{}}}",1188                                tcx.sess.source_map().span_to_diagnostic_string(span)1189                            )1190                        };1191                        let mut struct_fmt = fmt.debug_struct(&name);11921193                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places1194                        if let Some(def_id) = def_id.as_local()1195                            && let Some(upvars) = tcx.upvars_mentioned(def_id)1196                        {1197                            for (&var_id, place) in iter::zip(upvars.keys(), places) {1198                                let var_name = tcx.hir_name(var_id);1199                                struct_fmt.field(var_name.as_str(), place);1200                            }1201                        } else {1202                            for (index, place) in places.iter().enumerate() {1203                                struct_fmt.field(&format!("{index}"), place);1204                            }1205                        }12061207                        struct_fmt.finish()1208                    }),12091210                    AggregateKind::Coroutine(def_id, _) => ty::tls::with(|tcx| {1211                        let name = format!("{{coroutine@{:?}}}", tcx.def_span(def_id));1212                        let mut struct_fmt = fmt.debug_struct(&name);12131214                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places1215                        if let Some(def_id) = def_id.as_local()1216                            && let Some(upvars) = tcx.upvars_mentioned(def_id)1217                        {1218                            for (&var_id, place) in iter::zip(upvars.keys(), places) {1219                                let var_name = tcx.hir_name(var_id);1220                                struct_fmt.field(var_name.as_str(), place);1221                            }1222                        } else {1223                            for (index, place) in places.iter().enumerate() {1224                                struct_fmt.field(&format!("{index}"), place);1225                            }1226                        }12271228                        struct_fmt.finish()1229                    }),12301231                    AggregateKind::RawPtr(pointee_ty, mutability) => {1232                        let kind_str = match mutability {1233                            Mutability::Mut => "mut",1234                            Mutability::Not => "const",1235                        };1236                        with_no_trimmed_paths!(write!(fmt, "*{kind_str} {pointee_ty} from "))?;1237                        fmt_tuple(fmt, "")1238                    }1239                }1240            }12411242            WrapUnsafeBinder(ref op, ty) => {1243                with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})"))1244            }1245        }1246    }1247}12481249impl<'tcx> Debug for Operand<'tcx> {1250    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1251        use self::Operand::*;1252        match *self {1253            Constant(ref a) => write!(fmt, "{a:?}"),1254            Copy(ref place) => write!(fmt, "copy {place:?}"),1255            Move(ref place) => write!(fmt, "move {place:?}"),1256            RuntimeChecks(checks) => write!(fmt, "{checks:?}"),1257        }1258    }1259}12601261impl<'tcx> Debug for ConstOperand<'tcx> {1262    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1263        write!(fmt, "{self}")1264    }1265}12661267impl<'tcx> Display for ConstOperand<'tcx> {1268    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1269        match self.ty().kind() {1270            ty::FnDef(..) => {}1271            _ => write!(fmt, "const ")?,1272        }1273        Display::fmt(&self.const_, fmt)1274    }1275}12761277impl Debug for Place<'_> {1278    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1279        self.as_ref().fmt(fmt)1280    }1281}12821283impl Debug for PlaceRef<'_> {1284    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {1285        pre_fmt_projection(self.projection, fmt)?;1286        write!(fmt, "{:?}", self.local)?;1287        post_fmt_projection(self.projection, fmt)1288    }1289}12901291fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {1292    for &elem in projection.iter().rev() {1293        match elem {1294            ProjectionElem::OpaqueCast(_)1295            | ProjectionElem::Downcast(_, _)1296            | ProjectionElem::Field(_, _) => {1297                write!(fmt, "(")?;1298            }1299            ProjectionElem::Deref => {1300                write!(fmt, "(*")?;1301            }1302            ProjectionElem::Index(_)1303            | ProjectionElem::ConstantIndex { .. }1304            | ProjectionElem::Subslice { .. } => {}1305            ProjectionElem::UnwrapUnsafeBinder(_) => {1306                write!(fmt, "unwrap_binder!(")?;1307            }1308        }1309    }13101311    Ok(())1312}13131314fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {1315    for &elem in projection.iter() {1316        match elem {1317            ProjectionElem::OpaqueCast(ty) => {1318                write!(fmt, " as {ty})")?;1319            }1320            ProjectionElem::Downcast(Some(name), _index) => {1321                write!(fmt, " as {name})")?;1322            }1323            ProjectionElem::Downcast(None, index) => {1324                write!(fmt, " as variant#{index:?})")?;1325            }1326            ProjectionElem::Deref => {1327                write!(fmt, ")")?;1328            }1329            ProjectionElem::Field(field, ty) => {1330                with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);1331            }1332            ProjectionElem::Index(ref index) => {1333                write!(fmt, "[{index:?}]")?;1334            }1335            ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {1336                write!(fmt, "[{offset:?} of {min_length:?}]")?;1337            }1338            ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {1339                write!(fmt, "[-{offset:?} of {min_length:?}]")?;1340            }1341            ProjectionElem::Subslice { from, to: 0, from_end: true } => {1342                write!(fmt, "[{from:?}:]")?;1343            }1344            ProjectionElem::Subslice { from: 0, to, from_end: true } => {1345                write!(fmt, "[:-{to:?}]")?;1346            }1347            ProjectionElem::Subslice { from, to, from_end: true } => {1348                write!(fmt, "[{from:?}:-{to:?}]")?;1349            }1350            ProjectionElem::Subslice { from, to, from_end: false } => {1351                write!(fmt, "[{from:?}..{to:?}]")?;1352            }1353            ProjectionElem::UnwrapUnsafeBinder(ty) => {1354                write!(fmt, "; {ty})")?;1355            }1356        }1357    }13581359    Ok(())1360}13611362/// After we print the main statement, we sometimes dump extra1363/// information. There's often a lot of little things "nuzzled up" in1364/// a statement.1365fn write_extra<'tcx>(1366    tcx: TyCtxt<'tcx>,1367    write: &mut dyn io::Write,1368    visit_op: &dyn Fn(&mut ExtraComments<'tcx>),1369    options: PrettyPrintMirOptions,1370) -> io::Result<()> {1371    if options.include_extra_comments {1372        let mut extra_comments = ExtraComments { tcx, comments: vec![] };1373        visit_op(&mut extra_comments);1374        for comment in extra_comments.comments {1375            writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;1376        }1377    }1378    Ok(())1379}13801381struct ExtraComments<'tcx> {1382    tcx: TyCtxt<'tcx>,1383    comments: Vec<String>,1384}13851386impl<'tcx> ExtraComments<'tcx> {1387    fn push(&mut self, lines: &str) {1388        for line in lines.split('\n') {1389            self.comments.push(line.to_string());1390        }1391    }1392}13931394fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {1395    match *ty.kind() {1396        ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,1397        // Unit type1398        ty::Tuple(g_args) if g_args.is_empty() => false,1399        ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),1400        ty::Array(ty, _) => use_verbose(ty, fn_def),1401        ty::FnDef(..) => fn_def,1402        _ => true,1403    }1404}14051406impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {1407    fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _location: Location) {1408        let ConstOperand { span, user_ty, const_ } = constant;1409        if use_verbose(const_.ty(), true) {1410            self.push("mir::ConstOperand");1411            self.push(&format!(1412                "+ span: {}",1413                self.tcx.sess.source_map().span_to_diagnostic_string(*span)1414            ));1415            if let Some(user_ty) = user_ty {1416                self.push(&format!("+ user_ty: {user_ty:?}"));1417            }14181419            let fmt_val = |val: ConstValue, ty: Ty<'tcx>| {1420                let tcx = self.tcx;1421                rustc_data_structures::make_display(move |fmt| {1422                    pretty_print_const_value_tcx(tcx, val, ty, fmt)1423                })1424            };14251426            let fmt_valtree = |cv: &ty::Value<'tcx>| {1427                let mut p = FmtPrinter::new(self.tcx, Namespace::ValueNS);1428                p.pretty_print_const_valtree(*cv, /*print_ty*/ true).unwrap();1429                p.into_buffer()1430            };14311432            let val = match const_ {1433                Const::Ty(_, ct) => match ct.kind() {1434                    ty::ConstKind::Param(p) => format!("ty::Param({p})"),1435                    ty::ConstKind::Unevaluated(uv) => {1436                        format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)1437                    }1438                    ty::ConstKind::Value(cv) => {1439                        format!("ty::Valtree({})", fmt_valtree(&cv))1440                    }1441                    // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.1442                    ty::ConstKind::Error(_) => "Error".to_string(),1443                    // These variants shouldn't exist in the MIR.1444                    ty::ConstKind::Placeholder(_)1445                    | ty::ConstKind::Infer(_)1446                    | ty::ConstKind::Expr(_)1447                    | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", const_),1448                },1449                Const::Unevaluated(uv, _) => {1450                    format!(1451                        "Unevaluated({}, {:?}, {:?})",1452                        self.tcx.def_path_str(uv.def),1453                        uv.args,1454                        uv.promoted,1455                    )1456                }1457                Const::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)),1458            };14591460            // This reflects what `Const` looked liked before `val` was renamed1461            // as `kind`. We print it like this to avoid having to update1462            // expected output in a lot of tests.1463            self.push(&format!("+ const_: Const {{ ty: {}, val: {} }}", const_.ty(), val));1464        }1465    }14661467    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {1468        self.super_rvalue(rvalue, location);1469        if let Rvalue::Aggregate(kind, _) = rvalue {1470            match **kind {1471                AggregateKind::Closure(def_id, args) => {1472                    self.push("closure");1473                    self.push(&format!("+ def_id: {def_id:?}"));1474                    self.push(&format!("+ args: {args:#?}"));1475                }14761477                AggregateKind::Coroutine(def_id, args) => {1478                    self.push("coroutine");1479                    self.push(&format!("+ def_id: {def_id:?}"));1480                    self.push(&format!("+ args: {args:#?}"));1481                    self.push(&format!("+ kind: {:?}", self.tcx.coroutine_kind(def_id)));1482                }14831484                AggregateKind::Adt(_, _, _, Some(user_ty), _) => {1485                    self.push("adt");1486                    self.push(&format!("+ user_ty: {user_ty:?}"));1487                }14881489                _ => {}1490            }1491        }1492    }1493}14941495fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {1496    let location = tcx.sess.source_map().span_to_diagnostic_string(span);1497    format!("scope {} at {}", scope.index(), location,)1498}14991500///////////////////////////////////////////////////////////////////////////1501// Allocations15021503/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding1504/// allocations.1505pub fn write_allocations<'tcx>(1506    tcx: TyCtxt<'tcx>,1507    body: &Body<'_>,1508    w: &mut dyn io::Write,1509) -> io::Result<()> {1510    fn alloc_ids_from_alloc(1511        alloc: ConstAllocation<'_>,1512    ) -> impl DoubleEndedIterator<Item = AllocId> {1513        alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id())1514    }15151516    fn alloc_id_from_const_val(val: ConstValue) -> Option<AllocId> {1517        match val {1518            ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()),1519            ConstValue::Scalar(interpret::Scalar::Int { .. }) => None,1520            ConstValue::ZeroSized => None,1521            ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => {1522                // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.1523                // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.1524                Some(alloc_id)1525            }1526        }1527    }1528    struct CollectAllocIds(BTreeSet<AllocId>);15291530    impl<'tcx> Visitor<'tcx> for CollectAllocIds {1531        fn visit_const_operand(&mut self, c: &ConstOperand<'tcx>, _: Location) {1532            match c.const_ {1533                Const::Ty(_, _) | Const::Unevaluated(..) => {}1534                Const::Val(val, _) => {1535                    if let Some(id) = alloc_id_from_const_val(val) {1536                        self.0.insert(id);1537                    }1538                }1539            }1540        }1541    }15421543    let mut visitor = CollectAllocIds(Default::default());1544    visitor.visit_body(body);15451546    // `seen` contains all seen allocations, including the ones we have *not* printed yet.1547    // The protocol is to first `insert` into `seen`, and only if that returns `true`1548    // then push to `todo`.1549    let mut seen = visitor.0;1550    let mut todo: Vec<_> = seen.iter().copied().collect();1551    while let Some(id) = todo.pop() {1552        let mut write_allocation_track_relocs =1553            |w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {1554                // `.rev()` because we are popping them from the back of the `todo` vector.1555                for id in alloc_ids_from_alloc(alloc).rev() {1556                    if seen.insert(id) {1557                        todo.push(id);1558                    }1559                }1560                write!(w, "{}", display_allocation(tcx, alloc.inner()))1561            };1562        write!(w, "\n{id:?}")?;1563        match tcx.try_get_global_alloc(id) {1564            // This can't really happen unless there are bugs, but it doesn't cost us anything to1565            // gracefully handle it and allow buggy rustc to be debugged via allocation printing.1566            None => write!(w, " (deallocated)")?,1567            Some(GlobalAlloc::Function { instance, .. }) => write!(w, " (fn: {instance})")?,1568            Some(GlobalAlloc::VTable(ty, dyn_ty)) => {1569                write!(w, " (vtable: impl {dyn_ty} for {ty})")?1570            }1571            Some(GlobalAlloc::TypeId { ty }) => write!(w, " (typeid for {ty})")?,1572            Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {1573                write!(w, " (static: {}", tcx.def_path_str(did))?;1574                if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup)1575                    && body1576                        .source1577                        .def_id()1578                        .as_local()1579                        .is_some_and(|def_id| tcx.hir_body_const_context(def_id).is_some())1580                {1581                    // Statics may be cyclic and evaluating them too early1582                    // in the MIR pipeline may cause cycle errors even though1583                    // normal compilation is fine.1584                    write!(w, ")")?;1585                } else {1586                    match tcx.eval_static_initializer(did) {1587                        Ok(alloc) => {1588                            write!(w, ", ")?;1589                            write_allocation_track_relocs(w, alloc)?;1590                        }1591                        Err(_) => write!(w, ", error during initializer evaluation)")?,1592                    }1593                }1594            }1595            Some(GlobalAlloc::Static(did)) => {1596                write!(w, " (extern static: {})", tcx.def_path_str(did))?1597            }1598            Some(GlobalAlloc::Memory(alloc)) => {1599                write!(w, " (")?;1600                write_allocation_track_relocs(w, alloc)?1601            }1602        }1603        writeln!(w)?;1604    }1605    Ok(())1606}16071608/// Dumps the size and metadata and content of an allocation to the given writer.1609/// The expectation is that the caller first prints other relevant metadata, so the exact1610/// format of this function is (*without* leading or trailing newline):1611///1612/// ```text1613/// size: {}, align: {}) {1614///     <bytes>1615/// }1616/// ```1617///1618/// The byte format is similar to how hex editors print bytes. Each line starts with the address of1619/// the start of the line, followed by all bytes in hex format (space separated).1620/// If the allocation is small enough to fit into a single line, no start address is given.1621/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control1622/// characters or characters whose value is larger than 127) with a `.`1623/// This also prints provenance adequately.1624pub fn display_allocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(1625    tcx: TyCtxt<'tcx>,1626    alloc: &'a Allocation<Prov, Extra, Bytes>,1627) -> RenderAllocation<'a, 'tcx, Prov, Extra, Bytes> {1628    RenderAllocation { tcx, alloc }1629}16301631#[doc(hidden)]1632pub struct RenderAllocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> {1633    tcx: TyCtxt<'tcx>,1634    alloc: &'a Allocation<Prov, Extra, Bytes>,1635}16361637impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> std::fmt::Display1638    for RenderAllocation<'a, 'tcx, Prov, Extra, Bytes>1639{1640    fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {1641        let RenderAllocation { tcx, alloc } = *self;1642        write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?;1643        if alloc.size() == Size::ZERO {1644            // We are done.1645            return write!(w, " {{}}");1646        }1647        if tcx.sess.opts.unstable_opts.dump_mir_exclude_alloc_bytes {1648            return write!(w, " {{ .. }}");1649        }1650        // Write allocation bytes.1651        writeln!(w, " {{")?;1652        write_allocation_bytes(tcx, alloc, w, "    ")?;1653        write!(w, "}}")?;1654        Ok(())1655    }1656}16571658fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result {1659    for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {1660        write!(w, "   ")?;1661    }1662    writeln!(w, " │ {ascii}")1663}16641665/// Number of bytes to print per allocation hex dump line.1666const BYTES_PER_LINE: usize = 16;16671668/// Prints the line start address and returns the new line start address.1669fn write_allocation_newline(1670    w: &mut dyn std::fmt::Write,1671    mut line_start: Size,1672    ascii: &str,1673    pos_width: usize,1674    prefix: &str,1675) -> Result<Size, std::fmt::Error> {1676    write_allocation_endline(w, ascii)?;1677    line_start += Size::from_bytes(BYTES_PER_LINE);1678    write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;1679    Ok(line_start)1680}16811682/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there1683/// is only one line). Note that your prefix should contain a trailing space as the lines are1684/// printed directly after it.1685pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(1686    tcx: TyCtxt<'tcx>,1687    alloc: &Allocation<Prov, Extra, Bytes>,1688    w: &mut dyn std::fmt::Write,1689    prefix: &str,1690) -> std::fmt::Result {1691    let num_lines = alloc.size().bytes_usize().saturating_sub(BYTES_PER_LINE);1692    // Number of chars needed to represent all line numbers.1693    let pos_width = hex_number_length(alloc.size().bytes());16941695    if num_lines > 0 {1696        write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;1697    } else {1698        write!(w, "{prefix}")?;1699    }17001701    let mut i = Size::ZERO;1702    let mut line_start = Size::ZERO;17031704    let ptr_size = tcx.data_layout.pointer_size();17051706    let mut ascii = String::new();17071708    let oversized_ptr = |target: &mut String, width| {1709        if target.len() > width {1710            write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();1711        }1712    };17131714    while i < alloc.size() {1715        // The line start already has a space. While we could remove that space from the line start1716        // printing and unconditionally print a space here, that would cause the single-line case1717        // to have a single space before it, which looks weird.1718        if i != line_start {1719            write!(w, " ")?;1720        }1721        if let Some(prov) = alloc.provenance().get_ptr(i) {1722            // Memory with provenance must be defined1723            assert!(alloc.init_mask().is_range_initialized(alloc_range(i, ptr_size)).is_ok());1724            let j = i.bytes_usize();1725            let offset = alloc1726                .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());1727            let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();1728            let offset = Size::from_bytes(offset);1729            let provenance_width = |bytes| bytes * 3;1730            let ptr = Pointer::new(prov, offset);1731            let mut target = format!("{ptr:?}");1732            if target.len() > provenance_width(ptr_size.bytes_usize() - 1) {1733                // This is too long, try to save some space.1734                target = format!("{ptr:#?}");1735            }1736            if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {1737                // This branch handles the situation where a provenance starts in the current line1738                // but ends in the next one.1739                let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);1740                let overflow = ptr_size - remainder;1741                let remainder_width = provenance_width(remainder.bytes_usize()) - 2;1742                let overflow_width = provenance_width(overflow.bytes_usize() - 1) + 1;1743                ascii.push('╾'); // HEAVY LEFT AND LIGHT RIGHT1744                for _ in 1..remainder.bytes() {1745                    ascii.push('─'); // LIGHT HORIZONTAL1746                }1747                if overflow_width > remainder_width && overflow_width >= target.len() {1748                    // The case where the provenance fits into the part in the next line1749                    write!(w, "╾{0:─^1$}", "", remainder_width)?;1750                    line_start =1751                        write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;1752                    ascii.clear();1753                    write!(w, "{target:─^overflow_width$}╼")?;1754                } else {1755                    oversized_ptr(&mut target, remainder_width);1756                    write!(w, "╾{target:─^remainder_width$}")?;1757                    line_start =1758                        write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;1759                    write!(w, "{0:─^1$}╼", "", overflow_width)?;1760                    ascii.clear();1761                }1762                for _ in 0..overflow.bytes() - 1 {1763                    ascii.push('─');1764                }1765                ascii.push('╼'); // LIGHT LEFT AND HEAVY RIGHT1766                i += ptr_size;1767                continue;1768            } else {1769                // This branch handles a provenance that starts and ends in the current line.1770                let provenance_width = provenance_width(ptr_size.bytes_usize() - 1);1771                oversized_ptr(&mut target, provenance_width);1772                ascii.push('╾');1773                write!(w, "╾{target:─^provenance_width$}╼")?;1774                for _ in 0..ptr_size.bytes() - 2 {1775                    ascii.push('─');1776                }1777                ascii.push('╼');1778                i += ptr_size;1779            }1780        } else if let Some(frag) = alloc.provenance().get_byte(i, &tcx) {1781            // Memory with provenance must be defined1782            assert!(1783                alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok()1784            );1785            ascii.push('━'); // HEAVY HORIZONTAL1786            // We have two characters to display this, which is obviously not enough.1787            // Format is similar to "oversized" above.1788            let j = i.bytes_usize();1789            let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];1790            // FIXME: Find a way to print `frag.offset` that does not look terrible...1791            write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼", prov = frag.prov, idx = frag.idx)?;1792            i += Size::from_bytes(1);1793        } else if alloc1794            .init_mask()1795            .is_range_initialized(alloc_range(i, Size::from_bytes(1)))1796            .is_ok()1797        {1798            let j = i.bytes_usize();17991800            // Checked definedness (and thus range) and provenance. This access also doesn't1801            // influence interpreter execution but is only for debugging.1802            let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];1803            write!(w, "{c:02x}")?;1804            if c.is_ascii_control() || c >= 0x80 {1805                ascii.push('.');1806            } else {1807                ascii.push(char::from(c));1808            }1809            i += Size::from_bytes(1);1810        } else {1811            write!(w, "__")?;1812            ascii.push('░');1813            i += Size::from_bytes(1);1814        }1815        // Print a new line header if the next line still has some bytes to print.1816        if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size() {1817            line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;1818            ascii.clear();1819        }1820    }1821    write_allocation_endline(w, &ascii)?;18221823    Ok(())1824}18251826///////////////////////////////////////////////////////////////////////////1827// Constants18281829fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {1830    write!(fmt, "b\"{}\"", byte_str.escape_ascii())1831}18321833fn comma_sep<'tcx>(1834    tcx: TyCtxt<'tcx>,1835    fmt: &mut Formatter<'_>,1836    elems: Vec<(ConstValue, Ty<'tcx>)>,1837) -> fmt::Result {1838    let mut first = true;1839    for (ct, ty) in elems {1840        if !first {1841            fmt.write_str(", ")?;1842        }1843        pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;1844        first = false;1845    }1846    Ok(())1847}18481849fn pretty_print_const_value_tcx<'tcx>(1850    tcx: TyCtxt<'tcx>,1851    ct: ConstValue,1852    ty: Ty<'tcx>,1853    fmt: &mut Formatter<'_>,1854) -> fmt::Result {1855    use crate::ty::print::PrettyPrinter;18561857    if tcx.sess.verbose_internals() {1858        fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;1859        return Ok(());1860    }18611862    // Printing [MaybeUninit<u8>::uninit(); N] or any other aggregate where all fields are uninit1863    // becomes very verbose. This special case makes the dump terse and clear.1864    if ct.all_bytes_uninit(tcx) {1865        fmt.write_str("<uninit>")?;1866        return Ok(());1867    }18681869    let u8_type = tcx.types.u8;1870    match (ct, ty.kind()) {1871        // Byte/string slices, printed as (byte) string literals.1872        (_, ty::Ref(_, inner_ty, _)) if let ty::Str = inner_ty.kind() => {1873            if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {1874                fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;1875                return Ok(());1876            }1877        }1878        (_, ty::Ref(_, inner_ty, _))1879            if let ty::Slice(t) = inner_ty.kind()1880                && *t == u8_type =>1881        {1882            if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {1883                pretty_print_byte_str(fmt, data)?;1884                return Ok(());1885            }1886        }1887        (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {1888            let n = n.try_to_target_usize(tcx).unwrap();1889            let alloc = tcx.global_alloc(alloc_id).unwrap_memory();1890            // cast is ok because we already checked for pointer size (32 or 64 bit) above1891            let range = AllocRange { start: offset, size: Size::from_bytes(n) };1892            let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();1893            fmt.write_str("*")?;1894            pretty_print_byte_str(fmt, byte_str)?;1895            return Ok(());1896        }1897        // Aggregates, printed as array/tuple/struct/variant construction syntax.1898        //1899        // NB: the `has_non_region_param` check ensures that we can use1900        // the `try_destructure_mir_constant_for_user_output ` query with1901        // an empty `TypingEnv::fully_monomorphized` without1902        // introducing ICEs (e.g. via `layout_of`) from missing bounds.1903        // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`1904        // to be able to destructure the tuple into `(0u8, *mut T)`1905        (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {1906            if let Some(contents) = tcx.try_destructure_mir_constant_for_user_output(ct, ty) {1907                let fields: Vec<(ConstValue, Ty<'_>)> = contents.fields.to_vec();1908                match *ty.kind() {1909                    ty::Array(..) => {1910                        fmt.write_str("[")?;1911                        comma_sep(tcx, fmt, fields)?;1912                        fmt.write_str("]")?;1913                    }1914                    ty::Tuple(..) => {1915                        fmt.write_str("(")?;1916                        comma_sep(tcx, fmt, fields)?;1917                        if contents.fields.len() == 1 {1918                            fmt.write_str(",")?;1919                        }1920                        fmt.write_str(")")?;1921                    }1922                    ty::Adt(def, _) if def.variants().is_empty() => {1923                        fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;1924                    }1925                    ty::Adt(def, args) => {1926                        let variant_idx = contents1927                            .variant1928                            .expect("destructed mir constant of adt without variant idx");1929                        let variant_def = &def.variant(variant_idx);1930                        let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);1931                        p.print_alloc_ids = true;1932                        p.pretty_print_value_path(variant_def.def_id, args)?;1933                        fmt.write_str(&p.into_buffer())?;19341935                        match variant_def.ctor_kind() {1936                            Some(CtorKind::Const) => {}1937                            Some(CtorKind::Fn) => {1938                                fmt.write_str("(")?;1939                                comma_sep(tcx, fmt, fields)?;1940                                fmt.write_str(")")?;1941                            }1942                            None => {1943                                fmt.write_str(" {{ ")?;1944                                let mut first = true;1945                                for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields)1946                                {1947                                    if !first {1948                                        fmt.write_str(", ")?;1949                                    }1950                                    write!(fmt, "{}: ", field_def.name)?;1951                                    pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;1952                                    first = false;1953                                }1954                                fmt.write_str(" }}")?;1955                            }1956                        }1957                    }1958                    _ => unreachable!(),1959                }1960                return Ok(());1961            }1962        }1963        (ConstValue::Scalar(scalar), _) => {1964            let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);1965            p.print_alloc_ids = true;1966            p.pretty_print_const_scalar(scalar, ty)?;1967            fmt.write_str(&p.into_buffer())?;1968            return Ok(());1969        }1970        (ConstValue::ZeroSized, ty::FnDef(d, s)) => {1971            let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);1972            p.print_alloc_ids = true;1973            p.pretty_print_value_path(*d, s)?;1974            fmt.write_str(&p.into_buffer())?;1975            return Ok(());1976        }1977        // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading1978        // their fields instead of just dumping the memory.1979        _ => {}1980    }1981    // Fall back to debug pretty printing for invalid constants.1982    write!(fmt, "{ct:?}: {ty}")1983}19841985pub(crate) fn pretty_print_const_value<'tcx>(1986    ct: ConstValue,1987    ty: Ty<'tcx>,1988    fmt: &mut Formatter<'_>,1989) -> fmt::Result {1990    ty::tls::with(|tcx| {1991        let ty = tcx.lift(ty);1992        pretty_print_const_value_tcx(tcx, ct, ty, fmt)1993    })1994}19951996///////////////////////////////////////////////////////////////////////////1997// Miscellaneous19981999/// Calc converted u64 decimal into hex and return its length in chars.2000///

Code quality findings 44

Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let ty = args.first().unwrap().expect_ty();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
write!(indented_decl, " as {user_ty:?}").unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let child_data = &body.source_scopes[child];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
pre_fmt_projection(&projection[..], fmt)?;
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
post_fmt_projection(&projection[..], fmt)?;
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let data = &body[block];
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
(1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
write!(fmt, "{}: {:?}", labels[i], target)?;
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let muta = tcx.static_mutability(did).unwrap().prefix_str();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
p.pretty_print_const_valtree(*cv, /*print_ty*/ true).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// Printing [MaybeUninit<u8>::uninit(); N] or any other aggregate where all fields are uninit
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let n = n.try_to_target_usize(tcx).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
.expect("destructed mir constant of adt without variant idx");
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use crate::mir::*;
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
indented_decl.push(';');
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
scope_tree.entry(parent).or_default().push(index);
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let is_function = match kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match (kind, body.source.promoted) {
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use self::StatementKind::*;
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match (successor_count, show_unwind) {
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use self::TerminatorKind::*;
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use self::TerminatorKind::*;
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use self::Rvalue::*;
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use self::Operand::*;
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match self.ty().kind() {
Performance Info: Calling .to_string() (especially on &str) allocates a new String. If done repeatedly in loops, consider alternatives like working with &str or using crates like `itoa`/`ryu` for number-to-string conversion.
info performance to-string-in-loop
self.comments.push(line.to_string());
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
self.comments.push(line.to_string());
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match *ty.kind() {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
todo.push(id);
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
ascii.push('╾'); // HEAVY LEFT AND LIGHT RIGHT
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
ascii.push('─'); // LIGHT HORIZONTAL
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
ascii.push('─');
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
ascii.push('╼'); // LIGHT LEFT AND HEAVY RIGHT
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
ascii.push('╾');
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
ascii.push('─');
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
ascii.push('╼');
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
// E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
// to be able to destructure the tuple into `(0u8, *mut T)`

Get this view in your editor

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