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///