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