1use std::collections::BTreeSet;
2use std::fmt::{Display, Write as _};
3use std::path::{Path, PathBuf};
4use std::{fs, io};
5
6use rustc_abi::Size;
7use rustc_ast::InlineAsmTemplatePiece;
8use tracing::trace;
9use ty::print::PrettyPrinter;
10
11use super::graphviz::write_mir_fn_graphviz;
12use crate::mir::interpret::{
13 AllocBytes, AllocId, Allocation, ConstAllocation, GlobalAlloc, Pointer, Provenance,
14 alloc_range, read_target_uint,
15};
16use crate::mir::visit::Visitor;
17use crate::mir::*;
18
19const INDENT: &str = " ";
20pub(crate) const ALIGN: usize = 40;
22
23#[derive(Clone, Copy)]
26pub enum PassWhere {
27 BeforeCFG,
29
30 AfterCFG,
32
33 BeforeBlock(BasicBlock),
35
36 BeforeLocation(Location),
38
39 AfterLocation(Location),
41
42 AfterTerminator(BasicBlock),
44}
45
46#[derive(Copy, Clone)]
49pub struct PrettyPrintMirOptions {
50 pub include_extra_comments: bool,
52}
53
54impl PrettyPrintMirOptions {
55 pub fn from_cli(tcx: TyCtxt<'_>) -> Self {
57 Self { include_extra_comments: tcx.sess.opts.unstable_opts.mir_include_spans.is_enabled() }
58 }
59}
60
61#[inline]
86pub fn dump_mir<'tcx, F>(
87 tcx: TyCtxt<'tcx>,
88 pass_num: bool,
89 pass_name: &str,
90 disambiguator: &dyn Display,
91 body: &Body<'tcx>,
92 extra_data: F,
93) where
94 F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
95{
96 dump_mir_with_options(
97 tcx,
98 pass_num,
99 pass_name,
100 disambiguator,
101 body,
102 extra_data,
103 PrettyPrintMirOptions::from_cli(tcx),
104 );
105}
106
107#[inline]
113pub fn dump_mir_with_options<'tcx, F>(
114 tcx: TyCtxt<'tcx>,
115 pass_num: bool,
116 pass_name: &str,
117 disambiguator: &dyn Display,
118 body: &Body<'tcx>,
119 extra_data: F,
120 options: PrettyPrintMirOptions,
121) where
122 F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
123{
124 if !dump_enabled(tcx, pass_name, body.source.def_id()) {
125 return;
126 }
127
128 dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data, options);
129}
130
131pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool {
132 let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir else {
133 return false;
134 };
135 let node_path = ty::print::with_forced_impl_filename_line!(tcx.def_path_str(def_id));
137 filters.split('|').any(|or_filter| {
138 or_filter.split('&').all(|and_filter| {
139 let and_filter_trimmed = and_filter.trim();
140 and_filter_trimmed == "all"
141 || pass_name.contains(and_filter_trimmed)
142 || node_path.contains(and_filter_trimmed)
143 })
144 })
145}
146
147pub fn dump_mir_to_writer<'tcx, F>(
159 tcx: TyCtxt<'tcx>,
160 pass_name: &str,
161 disambiguator: &dyn Display,
162 body: &Body<'tcx>,
163 w: &mut dyn io::Write,
164 mut extra_data: F,
165 options: PrettyPrintMirOptions,
166) -> io::Result<()>
167where
168 F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
169{
170 let def_path =
172 ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
173 write!(w, "// MIR for `{def_path}")?;
175 match body.source.promoted {
176 None => write!(w, "`")?,
177 Some(promoted) => write!(w, "::{promoted:?}`")?,
178 }
179 writeln!(w, " {disambiguator} {pass_name}")?;
180 if let Some(ref layout) = body.coroutine_layout_raw() {
181 writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
182 }
183 writeln!(w)?;
184 extra_data(PassWhere::BeforeCFG, w)?;
185 write_user_type_annotations(tcx, body, w)?;
186 write_mir_fn(tcx, body, &mut extra_data, w, options)?;
187 extra_data(PassWhere::AfterCFG, w)
188}
189
190fn dump_matched_mir_node<'tcx, F>(
191 tcx: TyCtxt<'tcx>,
192 pass_num: bool,
193 pass_name: &str,
194 disambiguator: &dyn Display,
195 body: &Body<'tcx>,
196 extra_data: F,
197 options: PrettyPrintMirOptions,
198) where
199 F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
200{
201 let _: io::Result<()> = try {
202 let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
203 dump_mir_to_writer(tcx, pass_name, disambiguator, body, &mut file, extra_data, options)?;
204 };
205
206 if tcx.sess.opts.unstable_opts.dump_mir_graphviz {
207 let _: io::Result<()> = try {
208 let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body)?;
209 write_mir_fn_graphviz(tcx, body, false, &mut file)?;
210 };
211 }
212}
213
214fn dump_path<'tcx>(
218 tcx: TyCtxt<'tcx>,
219 extension: &str,
220 pass_num: bool,
221 pass_name: &str,
222 disambiguator: &dyn Display,
223 body: &Body<'tcx>,
224) -> PathBuf {
225 let source = body.source;
226 let promotion_id = match source.promoted {
227 Some(id) => format!("-{id:?}"),
228 None => String::new(),
229 };
230
231 let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
232 String::new()
233 } else if pass_num {
234 let (dialect_index, phase_index) = body.phase.index();
235 format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count)
236 } else {
237 ".-------".to_string()
238 };
239
240 let crate_name = tcx.crate_name(source.def_id().krate);
241 let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
242 let shim_disambiguator = match source.instance {
245 ty::InstanceKind::DropGlue(_, Some(ty)) => {
246 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 s
255 }
256 ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
257 let mut s = ".".to_owned();
258 s.extend(ty.to_string().chars().filter_map(|c| match c {
259 ' ' => None,
260 ':' | '<' | '>' => Some('_'),
261 c => Some(c),
262 }));
263 s
264 }
265 ty::InstanceKind::AsyncDropGlue(_, ty) => {
266 let ty::Coroutine(_, args) = ty.kind() else {
267 bug!();
268 };
269 let ty = args.first().unwrap().expect_ty();
270 let mut s = ".".to_owned();
271 s.extend(ty.to_string().chars().filter_map(|c| match c {
272 ' ' => None,
273 ':' | '<' | '>' => Some('_'),
274 c => Some(c),
275 }));
276 s
277 }
278 ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {
279 let mut s = ".".to_owned();
280 s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {
281 ' ' => None,
282 ':' | '<' | '>' => Some('_'),
283 c => Some(c),
284 }));
285 s.push('.');
286 s.extend(impl_cor.to_string().chars().filter_map(|c| match c {
287 ' ' => None,
288 ':' | '<' | '>' => Some('_'),
289 c => Some(c),
290 }));
291 s
292 }
293 _ => String::new(),
294 };
295
296 let mut file_path = PathBuf::new();
297 file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
298
299 let file_name = format!(
300 "{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",
301 );
302
303 file_path.push(&file_name);
304
305 file_path
306}
307
308pub fn create_dump_file<'tcx>(
313 tcx: TyCtxt<'tcx>,
314 extension: &str,
315 pass_num: bool,
316 pass_name: &str,
317 disambiguator: &dyn Display,
318 body: &Body<'tcx>,
319) -> io::Result<io::BufWriter<fs::File>> {
320 let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, body);
321 if let Some(parent) = file_path.parent() {
322 fs::create_dir_all(parent).map_err(|e| {
323 io::Error::new(
324 e.kind(),
325 format!("IO error creating MIR dump directory: {parent:?}; {e}"),
326 )
327 })?;
328 }
329 fs::File::create_buffered(&file_path).map_err(|e| {
330 io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
331 })
332}
333
334pub fn write_mir_pretty<'tcx>(
340 tcx: TyCtxt<'tcx>,
341 single: Option<DefId>,
342 w: &mut dyn io::Write,
343) -> io::Result<()> {
344 let options = PrettyPrintMirOptions::from_cli(tcx);
345
346 writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
347 writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
348 writeln!(w, "// HINT: See also -Z dump-mir for MIR at specific points during compilation.")?;
349
350 let mut first = true;
351 for def_id in dump_mir_def_ids(tcx, single) {
352 if first {
353 first = false;
354 } else {
355 writeln!(w)?;
357 }
358
359 let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
360 write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
361
362 for body in tcx.promoted_mir(def_id) {
363 writeln!(w)?;
364 write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
365 }
366 Ok(())
367 };
368
369 if tcx.is_const_fn(def_id) {
371 render_body(w, tcx.optimized_mir(def_id))?;
372 writeln!(w)?;
373 writeln!(w, "// MIR FOR CTFE")?;
374 write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w, options)?;
377 } else {
378 let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
379 render_body(w, instance_mir)?;
380 }
381 }
382 Ok(())
383}
384
385pub fn write_mir_fn<'tcx, F>(
387 tcx: TyCtxt<'tcx>,
388 body: &Body<'tcx>,
389 extra_data: &mut F,
390 w: &mut dyn io::Write,
391 options: PrettyPrintMirOptions,
392) -> io::Result<()>
393where
394 F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
395{
396 write_mir_intro(tcx, body, w, options)?;
397 for block in body.basic_blocks.indices() {
398 extra_data(PassWhere::BeforeBlock(block), w)?;
399 write_basic_block(tcx, block, body, extra_data, w, options)?;
400 if block.index() + 1 != body.basic_blocks.len() {
401 writeln!(w)?;
402 }
403 }
404
405 writeln!(w, "}}")?;
406
407 write_allocations(tcx, body, w)?;
408
409 Ok(())
410}
411
412fn write_scope_tree(
414 tcx: TyCtxt<'_>,
415 body: &Body<'_>,
416 scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
417 w: &mut dyn io::Write,
418 parent: SourceScope,
419 depth: usize,
420 options: PrettyPrintMirOptions,
421) -> io::Result<()> {
422 let indent = depth * INDENT.len();
423
424 for var_debug_info in &body.var_debug_info {
426 if var_debug_info.source_info.scope != parent {
427 continue;
429 }
430
431 let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
432
433 if options.include_extra_comments {
434 writeln!(
435 w,
436 "{0:1$} // in {2}",
437 indented_debug_info,
438 ALIGN,
439 comment(tcx, var_debug_info.source_info),
440 )?;
441 } else {
442 writeln!(w, "{indented_debug_info}")?;
443 }
444 }
445
446 for (local, local_decl) in body.local_decls.iter_enumerated() {
448 if (1..body.arg_count + 1).contains(&local.index()) {
449 continue;
451 }
452
453 if local_decl.source_info.scope != parent {
454 continue;
456 }
457
458 let mut_str = local_decl.mutability.prefix_str();
459
460 let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(
461 "{0:1$}let {2}{3:?}: {4}",
462 INDENT, indent, mut_str, local, local_decl.ty
463 ));
464 if let Some(user_ty) = &local_decl.user_ty {
465 for user_ty in user_ty.projections() {
466 write!(indented_decl, " as {user_ty:?}").unwrap();
467 }
468 }
469 indented_decl.push(';');
470
471 let local_name = if local == RETURN_PLACE { " return place" } else { "" };
472
473 if options.include_extra_comments {
474 writeln!(
475 w,
476 "{0:1$} //{2} in {3}",
477 indented_decl,
478 ALIGN,
479 local_name,
480 comment(tcx, local_decl.source_info),
481 )?;
482 } else {
483 writeln!(w, "{indented_decl}",)?;
484 }
485 }
486
487 let Some(children) = scope_tree.get(&parent) else {
488 return Ok(());
489 };
490
491 for &child in children {
492 let child_data = &body.source_scopes[child];
493 assert_eq!(child_data.parent_scope, Some(parent));
494
495 let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
496 (
497 format!(
498 " (inlined {}{})",
499 if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
500 callee
501 ),
502 Some(callsite_span),
503 )
504 } else {
505 (String::new(), None)
506 };
507
508 let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
509
510 if options.include_extra_comments {
511 if let Some(span) = span {
512 writeln!(
513 w,
514 "{0:1$} // at {2}",
515 indented_header,
516 ALIGN,
517 tcx.sess.source_map().span_to_embeddable_string(span),
518 )?;
519 } else {
520 writeln!(w, "{indented_header}")?;
521 }
522 } else {
523 writeln!(w, "{indented_header}")?;
524 }
525
526 write_scope_tree(tcx, body, scope_tree, w, child, depth + 1, options)?;
527 writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
528 }
529
530 Ok(())
531}
532
533impl Debug for VarDebugInfo<'_> {
534 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
535 if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
536 pre_fmt_projection(&projection[..], fmt)?;
537 write!(fmt, "({}: {})", self.name, ty)?;
538 post_fmt_projection(&projection[..], fmt)?;
539 } else {
540 write!(fmt, "{}", self.name)?;
541 }
542
543 write!(fmt, " => {:?}", self.value)
544 }
545}
546
547fn write_mir_intro<'tcx>(
550 tcx: TyCtxt<'tcx>,
551 body: &Body<'_>,
552 w: &mut dyn io::Write,
553 options: PrettyPrintMirOptions,
554) -> io::Result<()> {
555 write_mir_sig(tcx, body, w)?;
556 writeln!(w, "{{")?;
557
558 let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
560 for (index, scope_data) in body.source_scopes.iter_enumerated() {
561 if let Some(parent) = scope_data.parent_scope {
562 scope_tree.entry(parent).or_default().push(index);
563 } else {
564 assert_eq!(index, OUTERMOST_SOURCE_SCOPE);
566 }
567 }
568
569 write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1, options)?;
570
571 writeln!(w)?;
573
574 if let Some(coverage_info_hi) = &body.coverage_info_hi {
575 write_coverage_info_hi(coverage_info_hi, w)?;
576 }
577 if let Some(function_coverage_info) = &body.function_coverage_info {
578 write_function_coverage_info(function_coverage_info, w)?;
579 }
580
581 Ok(())
582}
583
584fn write_coverage_info_hi(
585 coverage_info_hi: &coverage::CoverageInfoHi,
586 w: &mut dyn io::Write,
587) -> io::Result<()> {
588 let coverage::CoverageInfoHi { num_block_markers: _, branch_spans } = coverage_info_hi;
589
590 let mut did_print = false;
592
593 for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {
594 writeln!(
595 w,
596 "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
597 )?;
598 did_print = true;
599 }
600
601 if did_print {
602 writeln!(w)?;
603 }
604
605 Ok(())
606}
607
608fn write_function_coverage_info(
609 function_coverage_info: &coverage::FunctionCoverageInfo,
610 w: &mut dyn io::Write,
611) -> io::Result<()> {
612 let coverage::FunctionCoverageInfo { mappings, .. } = function_coverage_info;
613
614 for coverage::Mapping { kind, span } in mappings {
615 writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?;
616 }
617 writeln!(w)?;
618
619 Ok(())
620}
621
622fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io::Result<()> {
623 use rustc_hir::def::DefKind;
624
625 trace!("write_mir_sig: {:?}", body.source.instance);
626 let def_id = body.source.def_id();
627 let kind = tcx.def_kind(def_id);
628 let is_function = match kind {
629 DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::SyntheticCoroutineBody => {
630 true
631 }
632 _ => tcx.is_closure_like(def_id),
633 };
634 match (kind, body.source.promoted) {
635 (_, Some(_)) => write!(w, "const ")?, (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
637 (DefKind::Static { safety: _, mutability: hir::Mutability::Not, nested: false }, _) => {
638 write!(w, "static ")?
639 }
640 (DefKind::Static { safety: _, mutability: hir::Mutability::Mut, nested: false }, _) => {
641 write!(w, "static mut ")?
642 }
643 (_, _) if is_function => write!(w, "fn ")?,
644 (DefKind::AnonConst | DefKind::InlineConst, _) => {}
646 (DefKind::GlobalAsm, _) => {}
648 _ => bug!("Unexpected def kind {:?}", kind),
649 }
650
651 ty::print::with_forced_impl_filename_line! {
652 write!(w, "{}", tcx.def_path_str(def_id))?
654 }
655 if let Some(p) = body.source.promoted {
656 write!(w, "::{p:?}")?;
657 }
658
659 if body.source.promoted.is_none() && is_function {
660 write!(w, "(")?;
661
662 for (i, arg) in body.args_iter().enumerate() {
664 if i != 0 {
665 write!(w, ", ")?;
666 }
667 write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
668 }
669
670 write!(w, ") -> {}", body.return_ty())?;
671 } else {
672 assert_eq!(body.arg_count, 0);
673 write!(w, ": {} =", body.return_ty())?;
674 }
675
676 if let Some(yield_ty) = body.yield_ty() {
677 writeln!(w)?;
678 writeln!(w, "yields {yield_ty}")?;
679 }
680
681 write!(w, " ")?;
682 Ok(())
685}
686
687fn write_user_type_annotations(
688 tcx: TyCtxt<'_>,
689 body: &Body<'_>,
690 w: &mut dyn io::Write,
691) -> io::Result<()> {
692 if !body.user_type_annotations.is_empty() {
693 writeln!(w, "| User Type Annotations")?;
694 }
695 for (index, annotation) in body.user_type_annotations.iter_enumerated() {
696 writeln!(
697 w,
698 "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
699 index.index(),
700 annotation.user_ty,
701 tcx.sess.source_map().span_to_embeddable_string(annotation.span),
702 with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
703 )?;
704 }
705 if !body.user_type_annotations.is_empty() {
706 writeln!(w, "|")?;
707 }
708 Ok(())
709}
710
711pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
712 if let Some(i) = single {
713 vec![i]
714 } else {
715 tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
716 }
717}
718
719fn write_basic_block<'tcx, F>(
724 tcx: TyCtxt<'tcx>,
725 block: BasicBlock,
726 body: &Body<'tcx>,
727 extra_data: &mut F,
728 w: &mut dyn io::Write,
729 options: PrettyPrintMirOptions,
730) -> io::Result<()>
731where
732 F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
733{
734 let data = &body[block];
735
736 let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
738 writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
739
740 let mut current_location = Location { block, statement_index: 0 };
742 for statement in &data.statements {
743 extra_data(PassWhere::BeforeLocation(current_location), w)?;
744 let indented_body = format!("{INDENT}{INDENT}{statement:?};");
745 if options.include_extra_comments {
746 writeln!(
747 w,
748 "{:A$} // {}{}",
749 indented_body,
750 if tcx.sess.verbose_internals() {
751 format!("{current_location:?}: ")
752 } else {
753 String::new()
754 },
755 comment(tcx, statement.source_info),
756 A = ALIGN,
757 )?;
758 } else {
759 writeln!(w, "{indented_body}")?;
760 }
761
762 write_extra(
763 tcx,
764 w,
765 |visitor| {
766 visitor.visit_statement(statement, current_location);
767 },
768 options,
769 )?;
770
771 extra_data(PassWhere::AfterLocation(current_location), w)?;
772
773 current_location.statement_index += 1;
774 }
775
776 extra_data(PassWhere::BeforeLocation(current_location), w)?;
778 if data.terminator.is_some() {
779 let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
780 if options.include_extra_comments {
781 writeln!(
782 w,
783 "{:A$} // {}{}",
784 indented_terminator,
785 if tcx.sess.verbose_internals() {
786 format!("{current_location:?}: ")
787 } else {
788 String::new()
789 },
790 comment(tcx, data.terminator().source_info),
791 A = ALIGN,
792 )?;
793 } else {
794 writeln!(w, "{indented_terminator}")?;
795 }
796
797 write_extra(
798 tcx,
799 w,
800 |visitor| {
801 visitor.visit_terminator(data.terminator(), current_location);
802 },
803 options,
804 )?;
805 }
806
807 extra_data(PassWhere::AfterLocation(current_location), w)?;
808 extra_data(PassWhere::AfterTerminator(block), w)?;
809
810 writeln!(w, "{INDENT}}}")
811}
812
813impl Debug for Statement<'_> {
814 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
815 use self::StatementKind::*;
816 match self.kind {
817 Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),
818 FakeRead(box (ref cause, ref place)) => {
819 write!(fmt, "FakeRead({cause:?}, {place:?})")
820 }
821 Retag(ref kind, ref place) => write!(
822 fmt,
823 "Retag({}{:?})",
824 match kind {
825 RetagKind::FnEntry => "[fn entry] ",
826 RetagKind::TwoPhase => "[2phase] ",
827 RetagKind::Raw => "[raw] ",
828 RetagKind::Default => "",
829 },
830 place,
831 ),
832 StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),
833 StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),
834 SetDiscriminant { ref place, variant_index } => {
835 write!(fmt, "discriminant({place:?}) = {variant_index:?}")
836 }
837 Deinit(ref place) => write!(fmt, "Deinit({place:?})"),
838 PlaceMention(ref place) => {
839 write!(fmt, "PlaceMention({place:?})")
840 }
841 AscribeUserType(box (ref place, ref c_ty), ref variance) => {
842 write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
843 }
844 Coverage(ref kind) => write!(fmt, "Coverage::{kind:?}"),
845 Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
846 ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
847 Nop => write!(fmt, "nop"),
848 BackwardIncompatibleDropHint { ref place, reason: _ } => {
849 write!(fmt, "BackwardIncompatibleDropHint({place:?})")
852 }
853 }
854 }
855}
856
857impl Display for NonDivergingIntrinsic<'_> {
858 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
859 match self {
860 Self::Assume(op) => write!(f, "assume({op:?})"),
861 Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => {
862 write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})")
863 }
864 }
865 }
866}
867
868impl<'tcx> Debug for TerminatorKind<'tcx> {
869 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
870 self.fmt_head(fmt)?;
871 let successor_count = self.successors().count();
872 let labels = self.fmt_successor_labels();
873 assert_eq!(successor_count, labels.len());
874
875 let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
877 let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
878 write!(fmt, "unwind ")?;
879 match self.unwind() {
880 None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
882 Some(UnwindAction::Continue) => write!(fmt, "continue"),
883 Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
884 Some(UnwindAction::Terminate(reason)) => {
885 write!(fmt, "terminate({})", reason.as_short_str())
886 }
887 }
888 };
889
890 match (successor_count, show_unwind) {
891 (0, false) => Ok(()),
892 (0, true) => {
893 write!(fmt, " -> ")?;
894 fmt_unwind(fmt)
895 }
896 (1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
897 _ => {
898 write!(fmt, " -> [")?;
899 for (i, target) in self.successors().enumerate() {
900 if i > 0 {
901 write!(fmt, ", ")?;
902 }
903 write!(fmt, "{}: {:?}", labels[i], target)?;
904 }
905 if show_unwind {
906 write!(fmt, ", ")?;
907 fmt_unwind(fmt)?;
908 }
909 write!(fmt, "]")
910 }
911 }
912 }
913}
914
915impl<'tcx> TerminatorKind<'tcx> {
916 pub fn fmt_head<W: fmt::Write>(&self, fmt: &mut W) -> fmt::Result {
920 use self::TerminatorKind::*;
921 match self {
922 Goto { .. } => write!(fmt, "goto"),
923 SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
924 Return => write!(fmt, "return"),
925 CoroutineDrop => write!(fmt, "coroutine_drop"),
926 UnwindResume => write!(fmt, "resume"),
927 UnwindTerminate(reason) => {
928 write!(fmt, "terminate({})", reason.as_short_str())
929 }
930 Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
931 Unreachable => write!(fmt, "unreachable"),
932 Drop { place, .. } => write!(fmt, "drop({place:?})"),
933 Call { func, args, destination, .. } => {
934 write!(fmt, "{destination:?} = ")?;
935 write!(fmt, "{func:?}(")?;
936 for (index, arg) in args.iter().enumerate() {
937 if index > 0 {
938 write!(fmt, ", ")?;
939 }
940 write!(fmt, "{:?}", arg.node)?;
941 }
942 write!(fmt, ")")
943 }
944 TailCall { func, args, .. } => {
945 write!(fmt, "tailcall {func:?}(")?;
946 for (index, arg) in args.iter().enumerate() {
947 if index > 0 {
948 write!(fmt, ", ")?;
949 }
950 write!(fmt, "{:?}", arg.node)?;
951 }
952 write!(fmt, ")")
953 }
954 Assert { cond, expected, msg, .. } => {
955 write!(fmt, "assert(")?;
956 if !expected {
957 write!(fmt, "!")?;
958 }
959 write!(fmt, "{cond:?}, ")?;
960 msg.fmt_assert_args(fmt)?;
961 write!(fmt, ")")
962 }
963 FalseEdge { .. } => write!(fmt, "falseEdge"),
964 FalseUnwind { .. } => write!(fmt, "falseUnwind"),
965 InlineAsm { template, operands, options, .. } => {
966 write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
967 for op in operands {
968 write!(fmt, ", ")?;
969 let print_late = |&late| if late { "late" } else { "" };
970 match op {
971 InlineAsmOperand::In { reg, value } => {
972 write!(fmt, "in({reg}) {value:?}")?;
973 }
974 InlineAsmOperand::Out { reg, late, place: Some(place) } => {
975 write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
976 }
977 InlineAsmOperand::Out { reg, late, place: None } => {
978 write!(fmt, "{}out({}) _", print_late(late), reg)?;
979 }
980 InlineAsmOperand::InOut {
981 reg,
982 late,
983 in_value,
984 out_place: Some(out_place),
985 } => {
986 write!(
987 fmt,
988 "in{}out({}) {:?} => {:?}",
989 print_late(late),
990 reg,
991 in_value,
992 out_place
993 )?;
994 }
995 InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
996 write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
997 }
998 InlineAsmOperand::Const { value } => {
999 write!(fmt, "const {value:?}")?;
1000 }
1001 InlineAsmOperand::SymFn { value } => {
1002 write!(fmt, "sym_fn {value:?}")?;
1003 }
1004 InlineAsmOperand::SymStatic { def_id } => {
1005 write!(fmt, "sym_static {def_id:?}")?;
1006 }
1007 InlineAsmOperand::Label { target_index } => {
1008 write!(fmt, "label {target_index}")?;
1009 }
1010 }
1011 }
1012 write!(fmt, ", options({options:?}))")
1013 }
1014 }
1015 }
1016
1017 pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
1019 use self::TerminatorKind::*;
1020 match *self {
1021 Return
1022 | TailCall { .. }
1023 | UnwindResume
1024 | UnwindTerminate(_)
1025 | Unreachable
1026 | CoroutineDrop => vec![],
1027 Goto { .. } => vec!["".into()],
1028 SwitchInt { ref targets, .. } => targets
1029 .values
1030 .iter()
1031 .map(|&u| Cow::Owned(u.to_string()))
1032 .chain(iter::once("otherwise".into()))
1033 .collect(),
1034 Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
1035 vec!["return".into(), "unwind".into()]
1036 }
1037 Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
1038 Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
1039 Call { target: None, unwind: _, .. } => vec![],
1040 Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
1041 Yield { drop: None, .. } => vec!["resume".into()],
1042 Drop { unwind: UnwindAction::Cleanup(_), drop: Some(_), .. } => {
1043 vec!["return".into(), "unwind".into(), "drop".into()]
1044 }
1045 Drop { unwind: UnwindAction::Cleanup(_), drop: None, .. } => {
1046 vec!["return".into(), "unwind".into()]
1047 }
1048 Drop { unwind: _, drop: Some(_), .. } => vec!["return".into(), "drop".into()],
1049 Drop { unwind: _, .. } => vec!["return".into()],
1050 Assert { unwind: UnwindAction::Cleanup(_), .. } => {
1051 vec!["success".into(), "unwind".into()]
1052 }
1053 Assert { unwind: _, .. } => vec!["success".into()],
1054 FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
1055 FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {
1056 vec!["real".into(), "unwind".into()]
1057 }
1058 FalseUnwind { unwind: _, .. } => vec!["real".into()],
1059 InlineAsm { asm_macro, options, ref targets, unwind, .. } => {
1060 let mut vec = Vec::with_capacity(targets.len() + 1);
1061 if !asm_macro.diverges(options) {
1062 vec.push("return".into());
1063 }
1064 vec.resize(targets.len(), "label".into());
1065
1066 if let UnwindAction::Cleanup(_) = unwind {
1067 vec.push("unwind".into());
1068 }
1069
1070 vec
1071 }
1072 }
1073 }
1074}
1075
1076impl<'tcx> Debug for Rvalue<'tcx> {
1077 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1078 use self::Rvalue::*;
1079
1080 match *self {
1081 Use(ref place) => write!(fmt, "{place:?}"),
1082 Repeat(ref a, b) => {
1083 write!(fmt, "[{a:?}; ")?;
1084 pretty_print_const(b, fmt, false)?;
1085 write!(fmt, "]")
1086 }
1087 Len(ref a) => write!(fmt, "Len({a:?})"),
1088 Cast(ref kind, ref place, ref ty) => {
1089 with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
1090 }
1091 BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
1092 UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
1093 Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
1094 NullaryOp(ref op, ref t) => {
1095 let t = with_no_trimmed_paths!(format!("{}", t));
1096 match op {
1097 NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
1098 NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
1099 NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
1100 NullOp::UbChecks => write!(fmt, "UbChecks()"),
1101 NullOp::ContractChecks => write!(fmt, "ContractChecks()"),
1102 }
1103 }
1104 ThreadLocalRef(did) => ty::tls::with(|tcx| {
1105 let muta = tcx.static_mutability(did).unwrap().prefix_str();
1106 write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
1107 }),
1108 Ref(region, borrow_kind, ref place) => {
1109 let kind_str = match borrow_kind {
1110 BorrowKind::Shared => "",
1111 BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ",
1112 BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ",
1113 BorrowKind::Mut { .. } => "mut ",
1114 };
1115
1116 let print_region = ty::tls::with(|tcx| {
1118 tcx.sess.verbose_internals() || tcx.sess.opts.unstable_opts.identify_regions
1119 });
1120 let region = if print_region {
1121 let mut region = region.to_string();
1122 if !region.is_empty() {
1123 region.push(' ');
1124 }
1125 region
1126 } else {
1127 String::new()
1129 };
1130 write!(fmt, "&{region}{kind_str}{place:?}")
1131 }
1132
1133 CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
1134
1135 RawPtr(mutability, ref place) => {
1136 write!(fmt, "&raw {mut_str} {place:?}", mut_str = mutability.ptr_str())
1137 }
1138
1139 Aggregate(ref kind, ref places) => {
1140 let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
1141 let mut tuple_fmt = fmt.debug_tuple(name);
1142 for place in places {
1143 tuple_fmt.field(place);
1144 }
1145 tuple_fmt.finish()
1146 };
1147
1148 match **kind {
1149 AggregateKind::Array(_) => write!(fmt, "{places:?}"),
1150
1151 AggregateKind::Tuple => {
1152 if places.is_empty() {
1153 write!(fmt, "()")
1154 } else {
1155 fmt_tuple(fmt, "")
1156 }
1157 }
1158
1159 AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {
1160 ty::tls::with(|tcx| {
1161 let variant_def = &tcx.adt_def(adt_did).variant(variant);
1162 let args = tcx.lift(args).expect("could not lift for printing");
1163 let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |p| {
1164 p.print_def_path(variant_def.def_id, args)
1165 })?;
1166
1167 match variant_def.ctor_kind() {
1168 Some(CtorKind::Const) => fmt.write_str(&name),
1169 Some(CtorKind::Fn) => fmt_tuple(fmt, &name),
1170 None => {
1171 let mut struct_fmt = fmt.debug_struct(&name);
1172 for (field, place) in iter::zip(&variant_def.fields, places) {
1173 struct_fmt.field(field.name.as_str(), place);
1174 }
1175 struct_fmt.finish()
1176 }
1177 }
1178 })
1179 }
1180
1181 AggregateKind::Closure(def_id, args)
1182 | AggregateKind::CoroutineClosure(def_id, args) => ty::tls::with(|tcx| {
1183 let name = if tcx.sess.opts.unstable_opts.span_free_formats {
1184 let args = tcx.lift(args).unwrap();
1185 format!("{{closure@{}}}", tcx.def_path_str_with_args(def_id, args),)
1186 } else {
1187 let span = tcx.def_span(def_id);
1188 format!(
1189 "{{closure@{}}}",
1190 tcx.sess.source_map().span_to_diagnostic_string(span)
1191 )
1192 };
1193 let mut struct_fmt = fmt.debug_struct(&name);
1194
1195 if let Some(def_id) = def_id.as_local()
1197 && let Some(upvars) = tcx.upvars_mentioned(def_id)
1198 {
1199 for (&var_id, place) in iter::zip(upvars.keys(), places) {
1200 let var_name = tcx.hir_name(var_id);
1201 struct_fmt.field(var_name.as_str(), place);
1202 }
1203 } else {
1204 for (index, place) in places.iter().enumerate() {
1205 struct_fmt.field(&format!("{index}"), place);
1206 }
1207 }
1208
1209 struct_fmt.finish()
1210 }),
1211
1212 AggregateKind::Coroutine(def_id, _) => ty::tls::with(|tcx| {
1213 let name = format!("{{coroutine@{:?}}}", tcx.def_span(def_id));
1214 let mut struct_fmt = fmt.debug_struct(&name);
1215
1216 if let Some(def_id) = def_id.as_local()
1218 && let Some(upvars) = tcx.upvars_mentioned(def_id)
1219 {
1220 for (&var_id, place) in iter::zip(upvars.keys(), places) {
1221 let var_name = tcx.hir_name(var_id);
1222 struct_fmt.field(var_name.as_str(), place);
1223 }
1224 } else {
1225 for (index, place) in places.iter().enumerate() {
1226 struct_fmt.field(&format!("{index}"), place);
1227 }
1228 }
1229
1230 struct_fmt.finish()
1231 }),
1232
1233 AggregateKind::RawPtr(pointee_ty, mutability) => {
1234 let kind_str = match mutability {
1235 Mutability::Mut => "mut",
1236 Mutability::Not => "const",
1237 };
1238 with_no_trimmed_paths!(write!(fmt, "*{kind_str} {pointee_ty} from "))?;
1239 fmt_tuple(fmt, "")
1240 }
1241 }
1242 }
1243
1244 ShallowInitBox(ref place, ref ty) => {
1245 with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
1246 }
1247
1248 WrapUnsafeBinder(ref op, ty) => {
1249 with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})"))
1250 }
1251 }
1252 }
1253}
1254
1255impl<'tcx> Debug for Operand<'tcx> {
1256 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1257 use self::Operand::*;
1258 match *self {
1259 Constant(ref a) => write!(fmt, "{a:?}"),
1260 Copy(ref place) => write!(fmt, "copy {place:?}"),
1261 Move(ref place) => write!(fmt, "move {place:?}"),
1262 }
1263 }
1264}
1265
1266impl<'tcx> Debug for ConstOperand<'tcx> {
1267 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1268 write!(fmt, "{self}")
1269 }
1270}
1271
1272impl<'tcx> Display for ConstOperand<'tcx> {
1273 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1274 match self.ty().kind() {
1275 ty::FnDef(..) => {}
1276 _ => write!(fmt, "const ")?,
1277 }
1278 Display::fmt(&self.const_, fmt)
1279 }
1280}
1281
1282impl Debug for Place<'_> {
1283 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1284 self.as_ref().fmt(fmt)
1285 }
1286}
1287
1288impl Debug for PlaceRef<'_> {
1289 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1290 pre_fmt_projection(self.projection, fmt)?;
1291 write!(fmt, "{:?}", self.local)?;
1292 post_fmt_projection(self.projection, fmt)
1293 }
1294}
1295
1296fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
1297 for &elem in projection.iter().rev() {
1298 match elem {
1299 ProjectionElem::OpaqueCast(_)
1300 | ProjectionElem::Subtype(_)
1301 | ProjectionElem::Downcast(_, _)
1302 | ProjectionElem::Field(_, _) => {
1303 write!(fmt, "(")?;
1304 }
1305 ProjectionElem::Deref => {
1306 write!(fmt, "(*")?;
1307 }
1308 ProjectionElem::Index(_)
1309 | ProjectionElem::ConstantIndex { .. }
1310 | ProjectionElem::Subslice { .. } => {}
1311 ProjectionElem::UnwrapUnsafeBinder(_) => {
1312 write!(fmt, "unwrap_binder!(")?;
1313 }
1314 }
1315 }
1316
1317 Ok(())
1318}
1319
1320fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
1321 for &elem in projection.iter() {
1322 match elem {
1323 ProjectionElem::OpaqueCast(ty) => {
1324 write!(fmt, " as {ty})")?;
1325 }
1326 ProjectionElem::Subtype(ty) => {
1327 write!(fmt, " as subtype {ty})")?;
1328 }
1329 ProjectionElem::Downcast(Some(name), _index) => {
1330 write!(fmt, " as {name})")?;
1331 }
1332 ProjectionElem::Downcast(None, index) => {
1333 write!(fmt, " as variant#{index:?})")?;
1334 }
1335 ProjectionElem::Deref => {
1336 write!(fmt, ")")?;
1337 }
1338 ProjectionElem::Field(field, ty) => {
1339 with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
1340 }
1341 ProjectionElem::Index(ref index) => {
1342 write!(fmt, "[{index:?}]")?;
1343 }
1344 ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
1345 write!(fmt, "[{offset:?} of {min_length:?}]")?;
1346 }
1347 ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
1348 write!(fmt, "[-{offset:?} of {min_length:?}]")?;
1349 }
1350 ProjectionElem::Subslice { from, to: 0, from_end: true } => {
1351 write!(fmt, "[{from:?}:]")?;
1352 }
1353 ProjectionElem::Subslice { from: 0, to, from_end: true } => {
1354 write!(fmt, "[:-{to:?}]")?;
1355 }
1356 ProjectionElem::Subslice { from, to, from_end: true } => {
1357 write!(fmt, "[{from:?}:-{to:?}]")?;
1358 }
1359 ProjectionElem::Subslice { from, to, from_end: false } => {
1360 write!(fmt, "[{from:?}..{to:?}]")?;
1361 }
1362 ProjectionElem::UnwrapUnsafeBinder(ty) => {
1363 write!(fmt, "; {ty})")?;
1364 }
1365 }
1366 }
1367
1368 Ok(())
1369}
1370
1371fn write_extra<'tcx, F>(
1375 tcx: TyCtxt<'tcx>,
1376 write: &mut dyn io::Write,
1377 mut visit_op: F,
1378 options: PrettyPrintMirOptions,
1379) -> io::Result<()>
1380where
1381 F: FnMut(&mut ExtraComments<'tcx>),
1382{
1383 if options.include_extra_comments {
1384 let mut extra_comments = ExtraComments { tcx, comments: vec![] };
1385 visit_op(&mut extra_comments);
1386 for comment in extra_comments.comments {
1387 writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
1388 }
1389 }
1390 Ok(())
1391}
1392
1393struct ExtraComments<'tcx> {
1394 tcx: TyCtxt<'tcx>,
1395 comments: Vec<String>,
1396}
1397
1398impl<'tcx> ExtraComments<'tcx> {
1399 fn push(&mut self, lines: &str) {
1400 for line in lines.split('\n') {
1401 self.comments.push(line.to_string());
1402 }
1403 }
1404}
1405
1406fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
1407 match *ty.kind() {
1408 ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
1409 ty::Tuple(g_args) if g_args.is_empty() => false,
1411 ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),
1412 ty::Array(ty, _) => use_verbose(ty, fn_def),
1413 ty::FnDef(..) => fn_def,
1414 _ => true,
1415 }
1416}
1417
1418impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
1419 fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _location: Location) {
1420 let ConstOperand { span, user_ty, const_ } = constant;
1421 if use_verbose(const_.ty(), true) {
1422 self.push("mir::ConstOperand");
1423 self.push(&format!(
1424 "+ span: {}",
1425 self.tcx.sess.source_map().span_to_embeddable_string(*span)
1426 ));
1427 if let Some(user_ty) = user_ty {
1428 self.push(&format!("+ user_ty: {user_ty:?}"));
1429 }
1430
1431 let fmt_val = |val: ConstValue, ty: Ty<'tcx>| {
1432 let tcx = self.tcx;
1433 rustc_data_structures::make_display(move |fmt| {
1434 pretty_print_const_value_tcx(tcx, val, ty, fmt)
1435 })
1436 };
1437
1438 let fmt_valtree = |cv: &ty::Value<'tcx>| {
1439 let mut p = FmtPrinter::new(self.tcx, Namespace::ValueNS);
1440 p.pretty_print_const_valtree(*cv, true).unwrap();
1441 p.into_buffer()
1442 };
1443
1444 let val = match const_ {
1445 Const::Ty(_, ct) => match ct.kind() {
1446 ty::ConstKind::Param(p) => format!("ty::Param({p})"),
1447 ty::ConstKind::Unevaluated(uv) => {
1448 format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
1449 }
1450 ty::ConstKind::Value(cv) => {
1451 format!("ty::Valtree({})", fmt_valtree(&cv))
1452 }
1453 ty::ConstKind::Error(_) => "Error".to_string(),
1455 ty::ConstKind::Placeholder(_)
1457 | ty::ConstKind::Infer(_)
1458 | ty::ConstKind::Expr(_)
1459 | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", const_),
1460 },
1461 Const::Unevaluated(uv, _) => {
1462 format!(
1463 "Unevaluated({}, {:?}, {:?})",
1464 self.tcx.def_path_str(uv.def),
1465 uv.args,
1466 uv.promoted,
1467 )
1468 }
1469 Const::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)),
1470 };
1471
1472 self.push(&format!("+ const_: Const {{ ty: {}, val: {} }}", const_.ty(), val));
1476 }
1477 }
1478
1479 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1480 self.super_rvalue(rvalue, location);
1481 if let Rvalue::Aggregate(kind, _) = rvalue {
1482 match **kind {
1483 AggregateKind::Closure(def_id, args) => {
1484 self.push("closure");
1485 self.push(&format!("+ def_id: {def_id:?}"));
1486 self.push(&format!("+ args: {args:#?}"));
1487 }
1488
1489 AggregateKind::Coroutine(def_id, args) => {
1490 self.push("coroutine");
1491 self.push(&format!("+ def_id: {def_id:?}"));
1492 self.push(&format!("+ args: {args:#?}"));
1493 self.push(&format!("+ kind: {:?}", self.tcx.coroutine_kind(def_id)));
1494 }
1495
1496 AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
1497 self.push("adt");
1498 self.push(&format!("+ user_ty: {user_ty:?}"));
1499 }
1500
1501 _ => {}
1502 }
1503 }
1504 }
1505}
1506
1507fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
1508 let location = tcx.sess.source_map().span_to_embeddable_string(span);
1509 format!("scope {} at {}", scope.index(), location,)
1510}
1511
1512pub fn write_allocations<'tcx>(
1518 tcx: TyCtxt<'tcx>,
1519 body: &Body<'_>,
1520 w: &mut dyn io::Write,
1521) -> io::Result<()> {
1522 fn alloc_ids_from_alloc(
1523 alloc: ConstAllocation<'_>,
1524 ) -> impl DoubleEndedIterator<Item = AllocId> {
1525 alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id())
1526 }
1527
1528 fn alloc_id_from_const_val(val: ConstValue) -> Option<AllocId> {
1529 match val {
1530 ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()),
1531 ConstValue::Scalar(interpret::Scalar::Int { .. }) => None,
1532 ConstValue::ZeroSized => None,
1533 ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => {
1534 Some(alloc_id)
1537 }
1538 }
1539 }
1540 struct CollectAllocIds(BTreeSet<AllocId>);
1541
1542 impl<'tcx> Visitor<'tcx> for CollectAllocIds {
1543 fn visit_const_operand(&mut self, c: &ConstOperand<'tcx>, _: Location) {
1544 match c.const_ {
1545 Const::Ty(_, _) | Const::Unevaluated(..) => {}
1546 Const::Val(val, _) => {
1547 if let Some(id) = alloc_id_from_const_val(val) {
1548 self.0.insert(id);
1549 }
1550 }
1551 }
1552 }
1553 }
1554
1555 let mut visitor = CollectAllocIds(Default::default());
1556 visitor.visit_body(body);
1557
1558 let mut seen = visitor.0;
1562 let mut todo: Vec<_> = seen.iter().copied().collect();
1563 while let Some(id) = todo.pop() {
1564 let mut write_allocation_track_relocs =
1565 |w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
1566 for id in alloc_ids_from_alloc(alloc).rev() {
1568 if seen.insert(id) {
1569 todo.push(id);
1570 }
1571 }
1572 write!(w, "{}", display_allocation(tcx, alloc.inner()))
1573 };
1574 write!(w, "\n{id:?}")?;
1575 match tcx.try_get_global_alloc(id) {
1576 None => write!(w, " (deallocated)")?,
1579 Some(GlobalAlloc::Function { instance, .. }) => write!(w, " (fn: {instance})")?,
1580 Some(GlobalAlloc::VTable(ty, dyn_ty)) => {
1581 write!(w, " (vtable: impl {dyn_ty} for {ty})")?
1582 }
1583 Some(GlobalAlloc::TypeId { ty }) => write!(w, " (typeid for {ty})")?,
1584 Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
1585 write!(w, " (static: {}", tcx.def_path_str(did))?;
1586 if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup)
1587 && body
1588 .source
1589 .def_id()
1590 .as_local()
1591 .is_some_and(|def_id| tcx.hir_body_const_context(def_id).is_some())
1592 {
1593 write!(w, ")")?;
1597 } else {
1598 match tcx.eval_static_initializer(did) {
1599 Ok(alloc) => {
1600 write!(w, ", ")?;
1601 write_allocation_track_relocs(w, alloc)?;
1602 }
1603 Err(_) => write!(w, ", error during initializer evaluation)")?,
1604 }
1605 }
1606 }
1607 Some(GlobalAlloc::Static(did)) => {
1608 write!(w, " (extern static: {})", tcx.def_path_str(did))?
1609 }
1610 Some(GlobalAlloc::Memory(alloc)) => {
1611 write!(w, " (")?;
1612 write_allocation_track_relocs(w, alloc)?
1613 }
1614 }
1615 writeln!(w)?;
1616 }
1617 Ok(())
1618}
1619
1620pub fn display_allocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
1637 tcx: TyCtxt<'tcx>,
1638 alloc: &'a Allocation<Prov, Extra, Bytes>,
1639) -> RenderAllocation<'a, 'tcx, Prov, Extra, Bytes> {
1640 RenderAllocation { tcx, alloc }
1641}
1642
1643#[doc(hidden)]
1644pub struct RenderAllocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> {
1645 tcx: TyCtxt<'tcx>,
1646 alloc: &'a Allocation<Prov, Extra, Bytes>,
1647}
1648
1649impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> std::fmt::Display
1650 for RenderAllocation<'a, 'tcx, Prov, Extra, Bytes>
1651{
1652 fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1653 let RenderAllocation { tcx, alloc } = *self;
1654 write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?;
1655 if alloc.size() == Size::ZERO {
1656 return write!(w, " {{}}");
1658 }
1659 if tcx.sess.opts.unstable_opts.dump_mir_exclude_alloc_bytes {
1660 return write!(w, " {{ .. }}");
1661 }
1662 writeln!(w, " {{")?;
1664 write_allocation_bytes(tcx, alloc, w, " ")?;
1665 write!(w, "}}")?;
1666 Ok(())
1667 }
1668}
1669
1670fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result {
1671 for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {
1672 write!(w, " ")?;
1673 }
1674 writeln!(w, " │ {ascii}")
1675}
1676
1677const BYTES_PER_LINE: usize = 16;
1679
1680fn write_allocation_newline(
1682 w: &mut dyn std::fmt::Write,
1683 mut line_start: Size,
1684 ascii: &str,
1685 pos_width: usize,
1686 prefix: &str,
1687) -> Result<Size, std::fmt::Error> {
1688 write_allocation_endline(w, ascii)?;
1689 line_start += Size::from_bytes(BYTES_PER_LINE);
1690 write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;
1691 Ok(line_start)
1692}
1693
1694pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
1698 tcx: TyCtxt<'tcx>,
1699 alloc: &Allocation<Prov, Extra, Bytes>,
1700 w: &mut dyn std::fmt::Write,
1701 prefix: &str,
1702) -> std::fmt::Result {
1703 let num_lines = alloc.size().bytes_usize().saturating_sub(BYTES_PER_LINE);
1704 let pos_width = hex_number_length(alloc.size().bytes());
1706
1707 if num_lines > 0 {
1708 write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;
1709 } else {
1710 write!(w, "{prefix}")?;
1711 }
1712
1713 let mut i = Size::ZERO;
1714 let mut line_start = Size::ZERO;
1715
1716 let ptr_size = tcx.data_layout.pointer_size();
1717
1718 let mut ascii = String::new();
1719
1720 let oversized_ptr = |target: &mut String, width| {
1721 if target.len() > width {
1722 write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();
1723 }
1724 };
1725
1726 while i < alloc.size() {
1727 if i != line_start {
1731 write!(w, " ")?;
1732 }
1733 if let Some(prov) = alloc.provenance().get_ptr(i) {
1734 assert!(alloc.init_mask().is_range_initialized(alloc_range(i, ptr_size)).is_ok());
1736 let j = i.bytes_usize();
1737 let offset = alloc
1738 .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
1739 let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
1740 let offset = Size::from_bytes(offset);
1741 let provenance_width = |bytes| bytes * 3;
1742 let ptr = Pointer::new(prov, offset);
1743 let mut target = format!("{ptr:?}");
1744 if target.len() > provenance_width(ptr_size.bytes_usize() - 1) {
1745 target = format!("{ptr:#?}");
1747 }
1748 if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {
1749 let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);
1752 let overflow = ptr_size - remainder;
1753 let remainder_width = provenance_width(remainder.bytes_usize()) - 2;
1754 let overflow_width = provenance_width(overflow.bytes_usize() - 1) + 1;
1755 ascii.push('╾'); for _ in 1..remainder.bytes() {
1757 ascii.push('─'); }
1759 if overflow_width > remainder_width && overflow_width >= target.len() {
1760 write!(w, "╾{0:─^1$}", "", remainder_width)?;
1762 line_start =
1763 write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
1764 ascii.clear();
1765 write!(w, "{target:─^overflow_width$}╼")?;
1766 } else {
1767 oversized_ptr(&mut target, remainder_width);
1768 write!(w, "╾{target:─^remainder_width$}")?;
1769 line_start =
1770 write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
1771 write!(w, "{0:─^1$}╼", "", overflow_width)?;
1772 ascii.clear();
1773 }
1774 for _ in 0..overflow.bytes() - 1 {
1775 ascii.push('─');
1776 }
1777 ascii.push('╼'); i += ptr_size;
1779 continue;
1780 } else {
1781 let provenance_width = provenance_width(ptr_size.bytes_usize() - 1);
1783 oversized_ptr(&mut target, provenance_width);
1784 ascii.push('╾');
1785 write!(w, "╾{target:─^provenance_width$}╼")?;
1786 for _ in 0..ptr_size.bytes() - 2 {
1787 ascii.push('─');
1788 }
1789 ascii.push('╼');
1790 i += ptr_size;
1791 }
1792 } else if let Some((prov, idx)) = alloc.provenance().get_byte(i, &tcx) {
1793 assert!(
1795 alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok()
1796 );
1797 ascii.push('━'); let j = i.bytes_usize();
1801 let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
1802 write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼")?;
1803 i += Size::from_bytes(1);
1804 } else if alloc
1805 .init_mask()
1806 .is_range_initialized(alloc_range(i, Size::from_bytes(1)))
1807 .is_ok()
1808 {
1809 let j = i.bytes_usize();
1810
1811 let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
1814 write!(w, "{c:02x}")?;
1815 if c.is_ascii_control() || c >= 0x80 {
1816 ascii.push('.');
1817 } else {
1818 ascii.push(char::from(c));
1819 }
1820 i += Size::from_bytes(1);
1821 } else {
1822 write!(w, "__")?;
1823 ascii.push('░');
1824 i += Size::from_bytes(1);
1825 }
1826 if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size() {
1828 line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
1829 ascii.clear();
1830 }
1831 }
1832 write_allocation_endline(w, &ascii)?;
1833
1834 Ok(())
1835}
1836
1837fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {
1841 write!(fmt, "b\"{}\"", byte_str.escape_ascii())
1842}
1843
1844fn comma_sep<'tcx>(
1845 tcx: TyCtxt<'tcx>,
1846 fmt: &mut Formatter<'_>,
1847 elems: Vec<(ConstValue, Ty<'tcx>)>,
1848) -> fmt::Result {
1849 let mut first = true;
1850 for (ct, ty) in elems {
1851 if !first {
1852 fmt.write_str(", ")?;
1853 }
1854 pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
1855 first = false;
1856 }
1857 Ok(())
1858}
1859
1860fn pretty_print_const_value_tcx<'tcx>(
1861 tcx: TyCtxt<'tcx>,
1862 ct: ConstValue,
1863 ty: Ty<'tcx>,
1864 fmt: &mut Formatter<'_>,
1865) -> fmt::Result {
1866 use crate::ty::print::PrettyPrinter;
1867
1868 if tcx.sess.verbose_internals() {
1869 fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;
1870 return Ok(());
1871 }
1872
1873 let u8_type = tcx.types.u8;
1874 match (ct, ty.kind()) {
1875 (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
1877 if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
1878 fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
1879 return Ok(());
1880 }
1881 }
1882 (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
1883 if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
1884 pretty_print_byte_str(fmt, data)?;
1885 return Ok(());
1886 }
1887 }
1888 (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
1889 let n = n.try_to_target_usize(tcx).unwrap();
1890 let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
1891 let range = AllocRange { start: offset, size: Size::from_bytes(n) };
1893 let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
1894 fmt.write_str("*")?;
1895 pretty_print_byte_str(fmt, byte_str)?;
1896 return Ok(());
1897 }
1898 (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
1906 let ct = tcx.lift(ct).unwrap();
1907 let ty = tcx.lift(ty).unwrap();
1908 if let Some(contents) = tcx.try_destructure_mir_constant_for_user_output(ct, ty) {
1909 let fields: Vec<(ConstValue, Ty<'_>)> = contents.fields.to_vec();
1910 match *ty.kind() {
1911 ty::Array(..) => {
1912 fmt.write_str("[")?;
1913 comma_sep(tcx, fmt, fields)?;
1914 fmt.write_str("]")?;
1915 }
1916 ty::Tuple(..) => {
1917 fmt.write_str("(")?;
1918 comma_sep(tcx, fmt, fields)?;
1919 if contents.fields.len() == 1 {
1920 fmt.write_str(",")?;
1921 }
1922 fmt.write_str(")")?;
1923 }
1924 ty::Adt(def, _) if def.variants().is_empty() => {
1925 fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
1926 }
1927 ty::Adt(def, args) => {
1928 let variant_idx = contents
1929 .variant
1930 .expect("destructed mir constant of adt without variant idx");
1931 let variant_def = &def.variant(variant_idx);
1932 let args = tcx.lift(args).unwrap();
1933 let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
1934 p.print_alloc_ids = true;
1935 p.pretty_print_value_path(variant_def.def_id, args)?;
1936 fmt.write_str(&p.into_buffer())?;
1937
1938 match variant_def.ctor_kind() {
1939 Some(CtorKind::Const) => {}
1940 Some(CtorKind::Fn) => {
1941 fmt.write_str("(")?;
1942 comma_sep(tcx, fmt, fields)?;
1943 fmt.write_str(")")?;
1944 }
1945 None => {
1946 fmt.write_str(" {{ ")?;
1947 let mut first = true;
1948 for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields)
1949 {
1950 if !first {
1951 fmt.write_str(", ")?;
1952 }
1953 write!(fmt, "{}: ", field_def.name)?;
1954 pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
1955 first = false;
1956 }
1957 fmt.write_str(" }}")?;
1958 }
1959 }
1960 }
1961 _ => unreachable!(),
1962 }
1963 return Ok(());
1964 }
1965 }
1966 (ConstValue::Scalar(scalar), _) => {
1967 let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
1968 p.print_alloc_ids = true;
1969 let ty = tcx.lift(ty).unwrap();
1970 p.pretty_print_const_scalar(scalar, ty)?;
1971 fmt.write_str(&p.into_buffer())?;
1972 return Ok(());
1973 }
1974 (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
1975 let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
1976 p.print_alloc_ids = true;
1977 p.pretty_print_value_path(*d, s)?;
1978 fmt.write_str(&p.into_buffer())?;
1979 return Ok(());
1980 }
1981 _ => {}
1984 }
1985 write!(fmt, "{ct:?}: {ty}")
1987}
1988
1989pub(crate) fn pretty_print_const_value<'tcx>(
1990 ct: ConstValue,
1991 ty: Ty<'tcx>,
1992 fmt: &mut Formatter<'_>,
1993) -> fmt::Result {
1994 ty::tls::with(|tcx| {
1995 let ct = tcx.lift(ct).unwrap();
1996 let ty = tcx.lift(ty).unwrap();
1997 pretty_print_const_value_tcx(tcx, ct, ty, fmt)
1998 })
1999}
2000
2001fn hex_number_length(x: u64) -> usize {
2012 if x == 0 {
2013 return 1;
2014 }
2015 let mut length = 0;
2016 let mut x_left = x;
2017 while x_left > 0 {
2018 x_left /= 16;
2019 length += 1;
2020 }
2021 length
2022}