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)]
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 format!(".{:03}-{:03}", body.phase.phase_index(), body.pass_count)
235 } else {
236 ".-------".to_string()
237 };
238
239 let crate_name = tcx.crate_name(source.def_id().krate);
240 let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
241 let shim_disambiguator = match source.instance {
244 ty::InstanceKind::DropGlue(_, Some(ty)) => {
245 let mut s = ".".to_owned();
248 s.extend(ty.to_string().chars().filter_map(|c| match c {
249 ' ' => None,
250 ':' | '<' | '>' => Some('_'),
251 c => Some(c),
252 }));
253 s
254 }
255 ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) => {
256 let mut s = ".".to_owned();
259 s.extend(ty.to_string().chars().filter_map(|c| match c {
260 ' ' => None,
261 ':' | '<' | '>' => Some('_'),
262 c => Some(c),
263 }));
264 s
265 }
266 _ => String::new(),
267 };
268
269 let mut file_path = PathBuf::new();
270 file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
271
272 let file_name = format!(
273 "{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",
274 );
275
276 file_path.push(&file_name);
277
278 file_path
279}
280
281pub fn create_dump_file<'tcx>(
286 tcx: TyCtxt<'tcx>,
287 extension: &str,
288 pass_num: bool,
289 pass_name: &str,
290 disambiguator: &dyn Display,
291 body: &Body<'tcx>,
292) -> io::Result<io::BufWriter<fs::File>> {
293 let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, body);
294 if let Some(parent) = file_path.parent() {
295 fs::create_dir_all(parent).map_err(|e| {
296 io::Error::new(
297 e.kind(),
298 format!("IO error creating MIR dump directory: {parent:?}; {e}"),
299 )
300 })?;
301 }
302 fs::File::create_buffered(&file_path).map_err(|e| {
303 io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
304 })
305}
306
307pub fn write_mir_pretty<'tcx>(
313 tcx: TyCtxt<'tcx>,
314 single: Option<DefId>,
315 w: &mut dyn io::Write,
316) -> io::Result<()> {
317 let options = PrettyPrintMirOptions::from_cli(tcx);
318
319 writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
320 writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
321
322 let mut first = true;
323 for def_id in dump_mir_def_ids(tcx, single) {
324 if first {
325 first = false;
326 } else {
327 writeln!(w)?;
329 }
330
331 let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
332 write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
333
334 for body in tcx.promoted_mir(def_id) {
335 writeln!(w)?;
336 write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
337 }
338 Ok(())
339 };
340
341 if tcx.is_const_fn(def_id) {
343 render_body(w, tcx.optimized_mir(def_id))?;
344 writeln!(w)?;
345 writeln!(w, "// MIR FOR CTFE")?;
346 write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w, options)?;
349 } else {
350 let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
351 render_body(w, instance_mir)?;
352 }
353 }
354 Ok(())
355}
356
357pub fn write_mir_fn<'tcx, F>(
359 tcx: TyCtxt<'tcx>,
360 body: &Body<'tcx>,
361 extra_data: &mut F,
362 w: &mut dyn io::Write,
363 options: PrettyPrintMirOptions,
364) -> io::Result<()>
365where
366 F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
367{
368 write_mir_intro(tcx, body, w, options)?;
369 for block in body.basic_blocks.indices() {
370 extra_data(PassWhere::BeforeBlock(block), w)?;
371 write_basic_block(tcx, block, body, extra_data, w, options)?;
372 if block.index() + 1 != body.basic_blocks.len() {
373 writeln!(w)?;
374 }
375 }
376
377 writeln!(w, "}}")?;
378
379 write_allocations(tcx, body, w)?;
380
381 Ok(())
382}
383
384fn write_scope_tree(
386 tcx: TyCtxt<'_>,
387 body: &Body<'_>,
388 scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
389 w: &mut dyn io::Write,
390 parent: SourceScope,
391 depth: usize,
392 options: PrettyPrintMirOptions,
393) -> io::Result<()> {
394 let indent = depth * INDENT.len();
395
396 for var_debug_info in &body.var_debug_info {
398 if var_debug_info.source_info.scope != parent {
399 continue;
401 }
402
403 let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
404
405 if options.include_extra_comments {
406 writeln!(
407 w,
408 "{0:1$} // in {2}",
409 indented_debug_info,
410 ALIGN,
411 comment(tcx, var_debug_info.source_info),
412 )?;
413 } else {
414 writeln!(w, "{indented_debug_info}")?;
415 }
416 }
417
418 for (local, local_decl) in body.local_decls.iter_enumerated() {
420 if (1..body.arg_count + 1).contains(&local.index()) {
421 continue;
423 }
424
425 if local_decl.source_info.scope != parent {
426 continue;
428 }
429
430 let mut_str = local_decl.mutability.prefix_str();
431
432 let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(
433 "{0:1$}let {2}{3:?}: {4}",
434 INDENT, indent, mut_str, local, local_decl.ty
435 ));
436 if let Some(user_ty) = &local_decl.user_ty {
437 for user_ty in user_ty.projections() {
438 write!(indented_decl, " as {user_ty:?}").unwrap();
439 }
440 }
441 indented_decl.push(';');
442
443 let local_name = if local == RETURN_PLACE { " return place" } else { "" };
444
445 if options.include_extra_comments {
446 writeln!(
447 w,
448 "{0:1$} //{2} in {3}",
449 indented_decl,
450 ALIGN,
451 local_name,
452 comment(tcx, local_decl.source_info),
453 )?;
454 } else {
455 writeln!(w, "{indented_decl}",)?;
456 }
457 }
458
459 let Some(children) = scope_tree.get(&parent) else {
460 return Ok(());
461 };
462
463 for &child in children {
464 let child_data = &body.source_scopes[child];
465 assert_eq!(child_data.parent_scope, Some(parent));
466
467 let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
468 (
469 format!(
470 " (inlined {}{})",
471 if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
472 callee
473 ),
474 Some(callsite_span),
475 )
476 } else {
477 (String::new(), None)
478 };
479
480 let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
481
482 if options.include_extra_comments {
483 if let Some(span) = span {
484 writeln!(
485 w,
486 "{0:1$} // at {2}",
487 indented_header,
488 ALIGN,
489 tcx.sess.source_map().span_to_embeddable_string(span),
490 )?;
491 } else {
492 writeln!(w, "{indented_header}")?;
493 }
494 } else {
495 writeln!(w, "{indented_header}")?;
496 }
497
498 write_scope_tree(tcx, body, scope_tree, w, child, depth + 1, options)?;
499 writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
500 }
501
502 Ok(())
503}
504
505impl Debug for VarDebugInfo<'_> {
506 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
507 if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
508 pre_fmt_projection(&projection[..], fmt)?;
509 write!(fmt, "({}: {})", self.name, ty)?;
510 post_fmt_projection(&projection[..], fmt)?;
511 } else {
512 write!(fmt, "{}", self.name)?;
513 }
514
515 write!(fmt, " => {:?}", self.value)
516 }
517}
518
519fn write_mir_intro<'tcx>(
522 tcx: TyCtxt<'tcx>,
523 body: &Body<'_>,
524 w: &mut dyn io::Write,
525 options: PrettyPrintMirOptions,
526) -> io::Result<()> {
527 write_mir_sig(tcx, body, w)?;
528 writeln!(w, "{{")?;
529
530 let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
532 for (index, scope_data) in body.source_scopes.iter().enumerate() {
533 if let Some(parent) = scope_data.parent_scope {
534 scope_tree.entry(parent).or_default().push(SourceScope::new(index));
535 } else {
536 assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
538 }
539 }
540
541 write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1, options)?;
542
543 writeln!(w)?;
545
546 if let Some(coverage_info_hi) = &body.coverage_info_hi {
547 write_coverage_info_hi(coverage_info_hi, w)?;
548 }
549 if let Some(function_coverage_info) = &body.function_coverage_info {
550 write_function_coverage_info(function_coverage_info, w)?;
551 }
552
553 Ok(())
554}
555
556fn write_coverage_info_hi(
557 coverage_info_hi: &coverage::CoverageInfoHi,
558 w: &mut dyn io::Write,
559) -> io::Result<()> {
560 let coverage::CoverageInfoHi {
561 num_block_markers: _,
562 branch_spans,
563 mcdc_degraded_branch_spans,
564 mcdc_spans,
565 } = coverage_info_hi;
566
567 let mut did_print = false;
569
570 for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {
571 writeln!(
572 w,
573 "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
574 )?;
575 did_print = true;
576 }
577
578 for coverage::MCDCBranchSpan { span, true_marker, false_marker, .. } in
579 mcdc_degraded_branch_spans
580 {
581 writeln!(
582 w,
583 "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
584 )?;
585 did_print = true;
586 }
587
588 for (
589 coverage::MCDCDecisionSpan { span, end_markers, decision_depth, num_conditions: _ },
590 conditions,
591 ) in mcdc_spans
592 {
593 let num_conditions = conditions.len();
594 writeln!(
595 w,
596 "{INDENT}coverage mcdc decision {{ num_conditions: {num_conditions:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
597 )?;
598 for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in
599 conditions
600 {
601 writeln!(
602 w,
603 "{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
604 condition_info.condition_id
605 )?;
606 }
607 did_print = true;
608 }
609
610 if did_print {
611 writeln!(w)?;
612 }
613
614 Ok(())
615}
616
617fn write_function_coverage_info(
618 function_coverage_info: &coverage::FunctionCoverageInfo,
619 w: &mut dyn io::Write,
620) -> io::Result<()> {
621 let coverage::FunctionCoverageInfo { body_span, mappings, .. } = function_coverage_info;
622
623 writeln!(w, "{INDENT}coverage body span: {body_span:?}")?;
624 for coverage::Mapping { kind, span } in mappings {
625 writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?;
626 }
627 writeln!(w)?;
628
629 Ok(())
630}
631
632fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io::Result<()> {
633 use rustc_hir::def::DefKind;
634
635 trace!("write_mir_sig: {:?}", body.source.instance);
636 let def_id = body.source.def_id();
637 let kind = tcx.def_kind(def_id);
638 let is_function = match kind {
639 DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::SyntheticCoroutineBody => {
640 true
641 }
642 _ => tcx.is_closure_like(def_id),
643 };
644 match (kind, body.source.promoted) {
645 (_, Some(_)) => write!(w, "const ")?, (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
647 (DefKind::Static { safety: _, mutability: hir::Mutability::Not, nested: false }, _) => {
648 write!(w, "static ")?
649 }
650 (DefKind::Static { safety: _, mutability: hir::Mutability::Mut, nested: false }, _) => {
651 write!(w, "static mut ")?
652 }
653 (_, _) if is_function => write!(w, "fn ")?,
654 (DefKind::AnonConst | DefKind::InlineConst, _) => {} _ => bug!("Unexpected def kind {:?}", kind),
656 }
657
658 ty::print::with_forced_impl_filename_line! {
659 write!(w, "{}", tcx.def_path_str(def_id))?
661 }
662 if let Some(p) = body.source.promoted {
663 write!(w, "::{p:?}")?;
664 }
665
666 if body.source.promoted.is_none() && is_function {
667 write!(w, "(")?;
668
669 for (i, arg) in body.args_iter().enumerate() {
671 if i != 0 {
672 write!(w, ", ")?;
673 }
674 write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
675 }
676
677 write!(w, ") -> {}", body.return_ty())?;
678 } else {
679 assert_eq!(body.arg_count, 0);
680 write!(w, ": {} =", body.return_ty())?;
681 }
682
683 if let Some(yield_ty) = body.yield_ty() {
684 writeln!(w)?;
685 writeln!(w, "yields {yield_ty}")?;
686 }
687
688 write!(w, " ")?;
689 Ok(())
692}
693
694fn write_user_type_annotations(
695 tcx: TyCtxt<'_>,
696 body: &Body<'_>,
697 w: &mut dyn io::Write,
698) -> io::Result<()> {
699 if !body.user_type_annotations.is_empty() {
700 writeln!(w, "| User Type Annotations")?;
701 }
702 for (index, annotation) in body.user_type_annotations.iter_enumerated() {
703 writeln!(
704 w,
705 "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
706 index.index(),
707 annotation.user_ty,
708 tcx.sess.source_map().span_to_embeddable_string(annotation.span),
709 with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
710 )?;
711 }
712 if !body.user_type_annotations.is_empty() {
713 writeln!(w, "|")?;
714 }
715 Ok(())
716}
717
718pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
719 if let Some(i) = single {
720 vec![i]
721 } else {
722 tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
723 }
724}
725
726fn write_basic_block<'tcx, F>(
731 tcx: TyCtxt<'tcx>,
732 block: BasicBlock,
733 body: &Body<'tcx>,
734 extra_data: &mut F,
735 w: &mut dyn io::Write,
736 options: PrettyPrintMirOptions,
737) -> io::Result<()>
738where
739 F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
740{
741 let data = &body[block];
742
743 let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
745 writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
746
747 let mut current_location = Location { block, statement_index: 0 };
749 for statement in &data.statements {
750 extra_data(PassWhere::BeforeLocation(current_location), w)?;
751 let indented_body = format!("{INDENT}{INDENT}{statement:?};");
752 if options.include_extra_comments {
753 writeln!(
754 w,
755 "{:A$} // {}{}",
756 indented_body,
757 if tcx.sess.verbose_internals() {
758 format!("{current_location:?}: ")
759 } else {
760 String::new()
761 },
762 comment(tcx, statement.source_info),
763 A = ALIGN,
764 )?;
765 } else {
766 writeln!(w, "{indented_body}")?;
767 }
768
769 write_extra(
770 tcx,
771 w,
772 |visitor| {
773 visitor.visit_statement(statement, current_location);
774 },
775 options,
776 )?;
777
778 extra_data(PassWhere::AfterLocation(current_location), w)?;
779
780 current_location.statement_index += 1;
781 }
782
783 extra_data(PassWhere::BeforeLocation(current_location), w)?;
785 if data.terminator.is_some() {
786 let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
787 if options.include_extra_comments {
788 writeln!(
789 w,
790 "{:A$} // {}{}",
791 indented_terminator,
792 if tcx.sess.verbose_internals() {
793 format!("{current_location:?}: ")
794 } else {
795 String::new()
796 },
797 comment(tcx, data.terminator().source_info),
798 A = ALIGN,
799 )?;
800 } else {
801 writeln!(w, "{indented_terminator}")?;
802 }
803
804 write_extra(
805 tcx,
806 w,
807 |visitor| {
808 visitor.visit_terminator(data.terminator(), current_location);
809 },
810 options,
811 )?;
812 }
813
814 extra_data(PassWhere::AfterLocation(current_location), w)?;
815 extra_data(PassWhere::AfterTerminator(block), w)?;
816
817 writeln!(w, "{INDENT}}}")
818}
819
820impl Debug for Statement<'_> {
821 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
822 use self::StatementKind::*;
823 match self.kind {
824 Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),
825 FakeRead(box (ref cause, ref place)) => {
826 write!(fmt, "FakeRead({cause:?}, {place:?})")
827 }
828 Retag(ref kind, ref place) => write!(
829 fmt,
830 "Retag({}{:?})",
831 match kind {
832 RetagKind::FnEntry => "[fn entry] ",
833 RetagKind::TwoPhase => "[2phase] ",
834 RetagKind::Raw => "[raw] ",
835 RetagKind::Default => "",
836 },
837 place,
838 ),
839 StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),
840 StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),
841 SetDiscriminant { ref place, variant_index } => {
842 write!(fmt, "discriminant({place:?}) = {variant_index:?}")
843 }
844 Deinit(ref place) => write!(fmt, "Deinit({place:?})"),
845 PlaceMention(ref place) => {
846 write!(fmt, "PlaceMention({place:?})")
847 }
848 AscribeUserType(box (ref place, ref c_ty), ref variance) => {
849 write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
850 }
851 Coverage(ref kind) => write!(fmt, "Coverage::{kind:?}"),
852 Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
853 ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
854 Nop => write!(fmt, "nop"),
855 BackwardIncompatibleDropHint { ref place, reason: _ } => {
856 write!(fmt, "backward incompatible drop({place:?})")
859 }
860 }
861 }
862}
863
864impl Display for NonDivergingIntrinsic<'_> {
865 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
866 match self {
867 Self::Assume(op) => write!(f, "assume({op:?})"),
868 Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => {
869 write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})")
870 }
871 }
872 }
873}
874
875impl<'tcx> Debug for TerminatorKind<'tcx> {
876 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
877 self.fmt_head(fmt)?;
878 let successor_count = self.successors().count();
879 let labels = self.fmt_successor_labels();
880 assert_eq!(successor_count, labels.len());
881
882 let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
884 let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
885 write!(fmt, "unwind ")?;
886 match self.unwind() {
887 None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
889 Some(UnwindAction::Continue) => write!(fmt, "continue"),
890 Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
891 Some(UnwindAction::Terminate(reason)) => {
892 write!(fmt, "terminate({})", reason.as_short_str())
893 }
894 }
895 };
896
897 match (successor_count, show_unwind) {
898 (0, false) => Ok(()),
899 (0, true) => {
900 write!(fmt, " -> ")?;
901 fmt_unwind(fmt)
902 }
903 (1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
904 _ => {
905 write!(fmt, " -> [")?;
906 for (i, target) in self.successors().enumerate() {
907 if i > 0 {
908 write!(fmt, ", ")?;
909 }
910 write!(fmt, "{}: {:?}", labels[i], target)?;
911 }
912 if show_unwind {
913 write!(fmt, ", ")?;
914 fmt_unwind(fmt)?;
915 }
916 write!(fmt, "]")
917 }
918 }
919 }
920}
921
922impl<'tcx> TerminatorKind<'tcx> {
923 pub fn fmt_head<W: fmt::Write>(&self, fmt: &mut W) -> fmt::Result {
927 use self::TerminatorKind::*;
928 match self {
929 Goto { .. } => write!(fmt, "goto"),
930 SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
931 Return => write!(fmt, "return"),
932 CoroutineDrop => write!(fmt, "coroutine_drop"),
933 UnwindResume => write!(fmt, "resume"),
934 UnwindTerminate(reason) => {
935 write!(fmt, "terminate({})", reason.as_short_str())
936 }
937 Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
938 Unreachable => write!(fmt, "unreachable"),
939 Drop { place, .. } => write!(fmt, "drop({place:?})"),
940 Call { func, args, destination, .. } => {
941 write!(fmt, "{destination:?} = ")?;
942 write!(fmt, "{func:?}(")?;
943 for (index, arg) in args.iter().map(|a| &a.node).enumerate() {
944 if index > 0 {
945 write!(fmt, ", ")?;
946 }
947 write!(fmt, "{arg:?}")?;
948 }
949 write!(fmt, ")")
950 }
951 TailCall { func, args, .. } => {
952 write!(fmt, "tailcall {func:?}(")?;
953 for (index, arg) in args.iter().enumerate() {
954 if index > 0 {
955 write!(fmt, ", ")?;
956 }
957 write!(fmt, "{:?}", arg)?;
958 }
959 write!(fmt, ")")
960 }
961 Assert { cond, expected, msg, .. } => {
962 write!(fmt, "assert(")?;
963 if !expected {
964 write!(fmt, "!")?;
965 }
966 write!(fmt, "{cond:?}, ")?;
967 msg.fmt_assert_args(fmt)?;
968 write!(fmt, ")")
969 }
970 FalseEdge { .. } => write!(fmt, "falseEdge"),
971 FalseUnwind { .. } => write!(fmt, "falseUnwind"),
972 InlineAsm { template, ref operands, options, .. } => {
973 write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
974 for op in operands {
975 write!(fmt, ", ")?;
976 let print_late = |&late| if late { "late" } else { "" };
977 match op {
978 InlineAsmOperand::In { reg, value } => {
979 write!(fmt, "in({reg}) {value:?}")?;
980 }
981 InlineAsmOperand::Out { reg, late, place: Some(place) } => {
982 write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
983 }
984 InlineAsmOperand::Out { reg, late, place: None } => {
985 write!(fmt, "{}out({}) _", print_late(late), reg)?;
986 }
987 InlineAsmOperand::InOut {
988 reg,
989 late,
990 in_value,
991 out_place: Some(out_place),
992 } => {
993 write!(
994 fmt,
995 "in{}out({}) {:?} => {:?}",
996 print_late(late),
997 reg,
998 in_value,
999 out_place
1000 )?;
1001 }
1002 InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
1003 write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
1004 }
1005 InlineAsmOperand::Const { value } => {
1006 write!(fmt, "const {value:?}")?;
1007 }
1008 InlineAsmOperand::SymFn { value } => {
1009 write!(fmt, "sym_fn {value:?}")?;
1010 }
1011 InlineAsmOperand::SymStatic { def_id } => {
1012 write!(fmt, "sym_static {def_id:?}")?;
1013 }
1014 InlineAsmOperand::Label { target_index } => {
1015 write!(fmt, "label {target_index}")?;
1016 }
1017 }
1018 }
1019 write!(fmt, ", options({options:?}))")
1020 }
1021 }
1022 }
1023
1024 pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
1026 use self::TerminatorKind::*;
1027 match *self {
1028 Return
1029 | TailCall { .. }
1030 | UnwindResume
1031 | UnwindTerminate(_)
1032 | Unreachable
1033 | CoroutineDrop => vec![],
1034 Goto { .. } => vec!["".into()],
1035 SwitchInt { ref targets, .. } => targets
1036 .values
1037 .iter()
1038 .map(|&u| Cow::Owned(u.to_string()))
1039 .chain(iter::once("otherwise".into()))
1040 .collect(),
1041 Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
1042 vec!["return".into(), "unwind".into()]
1043 }
1044 Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
1045 Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
1046 Call { target: None, unwind: _, .. } => vec![],
1047 Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
1048 Yield { drop: None, .. } => vec!["resume".into()],
1049 Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
1050 Drop { unwind: _, .. } => vec!["return".into()],
1051 Assert { unwind: UnwindAction::Cleanup(_), .. } => {
1052 vec!["success".into(), "unwind".into()]
1053 }
1054 Assert { unwind: _, .. } => vec!["success".into()],
1055 FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
1056 FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {
1057 vec!["real".into(), "unwind".into()]
1058 }
1059 FalseUnwind { unwind: _, .. } => vec!["real".into()],
1060 InlineAsm { asm_macro, options, ref targets, unwind, .. } => {
1061 let mut vec = Vec::with_capacity(targets.len() + 1);
1062 if !asm_macro.diverges(options) {
1063 vec.push("return".into());
1064 }
1065 vec.resize(targets.len(), "label".into());
1066
1067 if let UnwindAction::Cleanup(_) = unwind {
1068 vec.push("unwind".into());
1069 }
1070
1071 vec
1072 }
1073 }
1074 }
1075}
1076
1077impl<'tcx> Debug for Rvalue<'tcx> {
1078 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1079 use self::Rvalue::*;
1080
1081 match *self {
1082 Use(ref place) => write!(fmt, "{place:?}"),
1083 Repeat(ref a, b) => {
1084 write!(fmt, "[{a:?}; ")?;
1085 pretty_print_const(b, fmt, false)?;
1086 write!(fmt, "]")
1087 }
1088 Len(ref a) => write!(fmt, "Len({a:?})"),
1089 Cast(ref kind, ref place, ref ty) => {
1090 with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
1091 }
1092 BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
1093 UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
1094 Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
1095 NullaryOp(ref op, ref t) => {
1096 let t = with_no_trimmed_paths!(format!("{}", t));
1097 match op {
1098 NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
1099 NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
1100 NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
1101 NullOp::UbChecks => write!(fmt, "UbChecks()"),
1102 NullOp::ContractChecks => write!(fmt, "ContractChecks()"),
1103 }
1104 }
1105 ThreadLocalRef(did) => ty::tls::with(|tcx| {
1106 let muta = tcx.static_mutability(did).unwrap().prefix_str();
1107 write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
1108 }),
1109 Ref(region, borrow_kind, ref place) => {
1110 let kind_str = match borrow_kind {
1111 BorrowKind::Shared => "",
1112 BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ",
1113 BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ",
1114 BorrowKind::Mut { .. } => "mut ",
1115 };
1116
1117 let print_region = ty::tls::with(|tcx| {
1119 tcx.sess.verbose_internals() || tcx.sess.opts.unstable_opts.identify_regions
1120 });
1121 let region = if print_region {
1122 let mut region = region.to_string();
1123 if !region.is_empty() {
1124 region.push(' ');
1125 }
1126 region
1127 } else {
1128 String::new()
1130 };
1131 write!(fmt, "&{region}{kind_str}{place:?}")
1132 }
1133
1134 CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
1135
1136 RawPtr(mutability, ref place) => {
1137 write!(fmt, "&raw {mut_str} {place:?}", mut_str = mutability.ptr_str())
1138 }
1139
1140 Aggregate(ref kind, ref places) => {
1141 let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
1142 let mut tuple_fmt = fmt.debug_tuple(name);
1143 for place in places {
1144 tuple_fmt.field(place);
1145 }
1146 tuple_fmt.finish()
1147 };
1148
1149 match **kind {
1150 AggregateKind::Array(_) => write!(fmt, "{places:?}"),
1151
1152 AggregateKind::Tuple => {
1153 if places.is_empty() {
1154 write!(fmt, "()")
1155 } else {
1156 fmt_tuple(fmt, "")
1157 }
1158 }
1159
1160 AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {
1161 ty::tls::with(|tcx| {
1162 let variant_def = &tcx.adt_def(adt_did).variant(variant);
1163 let args = tcx.lift(args).expect("could not lift for printing");
1164 let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |cx| {
1165 cx.print_def_path(variant_def.def_id, args)
1166 })?;
1167
1168 match variant_def.ctor_kind() {
1169 Some(CtorKind::Const) => fmt.write_str(&name),
1170 Some(CtorKind::Fn) => fmt_tuple(fmt, &name),
1171 None => {
1172 let mut struct_fmt = fmt.debug_struct(&name);
1173 for (field, place) in iter::zip(&variant_def.fields, places) {
1174 struct_fmt.field(field.name.as_str(), place);
1175 }
1176 struct_fmt.finish()
1177 }
1178 }
1179 })
1180 }
1181
1182 AggregateKind::Closure(def_id, args)
1183 | AggregateKind::CoroutineClosure(def_id, args) => ty::tls::with(|tcx| {
1184 let name = if tcx.sess.opts.unstable_opts.span_free_formats {
1185 let args = tcx.lift(args).unwrap();
1186 format!("{{closure@{}}}", tcx.def_path_str_with_args(def_id, args),)
1187 } else {
1188 let span = tcx.def_span(def_id);
1189 format!(
1190 "{{closure@{}}}",
1191 tcx.sess.source_map().span_to_diagnostic_string(span)
1192 )
1193 };
1194 let mut struct_fmt = fmt.debug_struct(&name);
1195
1196 if let Some(def_id) = def_id.as_local()
1198 && let Some(upvars) = tcx.upvars_mentioned(def_id)
1199 {
1200 for (&var_id, place) in iter::zip(upvars.keys(), places) {
1201 let var_name = tcx.hir().name(var_id);
1202 struct_fmt.field(var_name.as_str(), place);
1203 }
1204 } else {
1205 for (index, place) in places.iter().enumerate() {
1206 struct_fmt.field(&format!("{index}"), place);
1207 }
1208 }
1209
1210 struct_fmt.finish()
1211 }),
1212
1213 AggregateKind::Coroutine(def_id, _) => ty::tls::with(|tcx| {
1214 let name = format!("{{coroutine@{:?}}}", tcx.def_span(def_id));
1215 let mut struct_fmt = fmt.debug_struct(&name);
1216
1217 if let Some(def_id) = def_id.as_local()
1219 && let Some(upvars) = tcx.upvars_mentioned(def_id)
1220 {
1221 for (&var_id, place) in iter::zip(upvars.keys(), places) {
1222 let var_name = tcx.hir().name(var_id);
1223 struct_fmt.field(var_name.as_str(), place);
1224 }
1225 } else {
1226 for (index, place) in places.iter().enumerate() {
1227 struct_fmt.field(&format!("{index}"), place);
1228 }
1229 }
1230
1231 struct_fmt.finish()
1232 }),
1233
1234 AggregateKind::RawPtr(pointee_ty, mutability) => {
1235 let kind_str = match mutability {
1236 Mutability::Mut => "mut",
1237 Mutability::Not => "const",
1238 };
1239 with_no_trimmed_paths!(write!(fmt, "*{kind_str} {pointee_ty} from "))?;
1240 fmt_tuple(fmt, "")
1241 }
1242 }
1243 }
1244
1245 ShallowInitBox(ref place, ref ty) => {
1246 with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
1247 }
1248
1249 WrapUnsafeBinder(ref op, ty) => {
1250 with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})"))
1251 }
1252 }
1253 }
1254}
1255
1256impl<'tcx> Debug for Operand<'tcx> {
1257 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1258 use self::Operand::*;
1259 match *self {
1260 Constant(ref a) => write!(fmt, "{a:?}"),
1261 Copy(ref place) => write!(fmt, "copy {place:?}"),
1262 Move(ref place) => write!(fmt, "move {place:?}"),
1263 }
1264 }
1265}
1266
1267impl<'tcx> Debug for ConstOperand<'tcx> {
1268 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1269 write!(fmt, "{self}")
1270 }
1271}
1272
1273impl<'tcx> Display for ConstOperand<'tcx> {
1274 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1275 match self.ty().kind() {
1276 ty::FnDef(..) => {}
1277 _ => write!(fmt, "const ")?,
1278 }
1279 Display::fmt(&self.const_, fmt)
1280 }
1281}
1282
1283impl Debug for Place<'_> {
1284 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1285 self.as_ref().fmt(fmt)
1286 }
1287}
1288
1289impl Debug for PlaceRef<'_> {
1290 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1291 pre_fmt_projection(self.projection, fmt)?;
1292 write!(fmt, "{:?}", self.local)?;
1293 post_fmt_projection(self.projection, fmt)
1294 }
1295}
1296
1297fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
1298 for &elem in projection.iter().rev() {
1299 match elem {
1300 ProjectionElem::OpaqueCast(_)
1301 | ProjectionElem::Subtype(_)
1302 | ProjectionElem::Downcast(_, _)
1303 | ProjectionElem::Field(_, _) => {
1304 write!(fmt, "(")?;
1305 }
1306 ProjectionElem::Deref => {
1307 write!(fmt, "(*")?;
1308 }
1309 ProjectionElem::Index(_)
1310 | ProjectionElem::ConstantIndex { .. }
1311 | ProjectionElem::Subslice { .. } => {}
1312 ProjectionElem::UnwrapUnsafeBinder(_) => {
1313 write!(fmt, "unwrap_binder!(")?;
1314 }
1315 }
1316 }
1317
1318 Ok(())
1319}
1320
1321fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
1322 for &elem in projection.iter() {
1323 match elem {
1324 ProjectionElem::OpaqueCast(ty) => {
1325 write!(fmt, " as {ty})")?;
1326 }
1327 ProjectionElem::Subtype(ty) => {
1328 write!(fmt, " as subtype {ty})")?;
1329 }
1330 ProjectionElem::Downcast(Some(name), _index) => {
1331 write!(fmt, " as {name})")?;
1332 }
1333 ProjectionElem::Downcast(None, index) => {
1334 write!(fmt, " as variant#{index:?})")?;
1335 }
1336 ProjectionElem::Deref => {
1337 write!(fmt, ")")?;
1338 }
1339 ProjectionElem::Field(field, ty) => {
1340 with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
1341 }
1342 ProjectionElem::Index(ref index) => {
1343 write!(fmt, "[{index:?}]")?;
1344 }
1345 ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
1346 write!(fmt, "[{offset:?} of {min_length:?}]")?;
1347 }
1348 ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
1349 write!(fmt, "[-{offset:?} of {min_length:?}]")?;
1350 }
1351 ProjectionElem::Subslice { from, to: 0, from_end: true } => {
1352 write!(fmt, "[{from:?}:]")?;
1353 }
1354 ProjectionElem::Subslice { from: 0, to, from_end: true } => {
1355 write!(fmt, "[:-{to:?}]")?;
1356 }
1357 ProjectionElem::Subslice { from, to, from_end: true } => {
1358 write!(fmt, "[{from:?}:-{to:?}]")?;
1359 }
1360 ProjectionElem::Subslice { from, to, from_end: false } => {
1361 write!(fmt, "[{from:?}..{to:?}]")?;
1362 }
1363 ProjectionElem::UnwrapUnsafeBinder(ty) => {
1364 write!(fmt, "; {ty})")?;
1365 }
1366 }
1367 }
1368
1369 Ok(())
1370}
1371
1372fn write_extra<'tcx, F>(
1376 tcx: TyCtxt<'tcx>,
1377 write: &mut dyn io::Write,
1378 mut visit_op: F,
1379 options: PrettyPrintMirOptions,
1380) -> io::Result<()>
1381where
1382 F: FnMut(&mut ExtraComments<'tcx>),
1383{
1384 if options.include_extra_comments {
1385 let mut extra_comments = ExtraComments { tcx, comments: vec![] };
1386 visit_op(&mut extra_comments);
1387 for comment in extra_comments.comments {
1388 writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
1389 }
1390 }
1391 Ok(())
1392}
1393
1394struct ExtraComments<'tcx> {
1395 tcx: TyCtxt<'tcx>,
1396 comments: Vec<String>,
1397}
1398
1399impl<'tcx> ExtraComments<'tcx> {
1400 fn push(&mut self, lines: &str) {
1401 for line in lines.split('\n') {
1402 self.comments.push(line.to_string());
1403 }
1404 }
1405}
1406
1407fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
1408 match *ty.kind() {
1409 ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
1410 ty::Tuple(g_args) if g_args.is_empty() => false,
1412 ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),
1413 ty::Array(ty, _) => use_verbose(ty, fn_def),
1414 ty::FnDef(..) => fn_def,
1415 _ => true,
1416 }
1417}
1418
1419impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
1420 fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _location: Location) {
1421 let ConstOperand { span, user_ty, const_ } = constant;
1422 if use_verbose(const_.ty(), true) {
1423 self.push("mir::ConstOperand");
1424 self.push(&format!(
1425 "+ span: {}",
1426 self.tcx.sess.source_map().span_to_embeddable_string(*span)
1427 ));
1428 if let Some(user_ty) = user_ty {
1429 self.push(&format!("+ user_ty: {user_ty:?}"));
1430 }
1431
1432 let fmt_val = |val: ConstValue<'tcx>, ty: Ty<'tcx>| {
1433 let tcx = self.tcx;
1434 rustc_data_structures::make_display(move |fmt| {
1435 pretty_print_const_value_tcx(tcx, val, ty, fmt)
1436 })
1437 };
1438
1439 let fmt_valtree = |cv: &ty::Value<'tcx>| {
1440 let mut cx = FmtPrinter::new(self.tcx, Namespace::ValueNS);
1441 cx.pretty_print_const_valtree(*cv, true).unwrap();
1442 cx.into_buffer()
1443 };
1444
1445 let val = match const_ {
1446 Const::Ty(_, ct) => match ct.kind() {
1447 ty::ConstKind::Param(p) => format!("ty::Param({p})"),
1448 ty::ConstKind::Unevaluated(uv) => {
1449 format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
1450 }
1451 ty::ConstKind::Value(cv) => {
1452 format!("ty::Valtree({})", fmt_valtree(&cv))
1453 }
1454 ty::ConstKind::Error(_) => "Error".to_string(),
1456 ty::ConstKind::Placeholder(_)
1458 | ty::ConstKind::Infer(_)
1459 | ty::ConstKind::Expr(_)
1460 | ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", const_),
1461 },
1462 Const::Unevaluated(uv, _) => {
1463 format!(
1464 "Unevaluated({}, {:?}, {:?})",
1465 self.tcx.def_path_str(uv.def),
1466 uv.args,
1467 uv.promoted,
1468 )
1469 }
1470 Const::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)),
1471 };
1472
1473 self.push(&format!("+ const_: Const {{ ty: {}, val: {} }}", const_.ty(), val));
1477 }
1478 }
1479
1480 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1481 self.super_rvalue(rvalue, location);
1482 if let Rvalue::Aggregate(kind, _) = rvalue {
1483 match **kind {
1484 AggregateKind::Closure(def_id, args) => {
1485 self.push("closure");
1486 self.push(&format!("+ def_id: {def_id:?}"));
1487 self.push(&format!("+ args: {args:#?}"));
1488 }
1489
1490 AggregateKind::Coroutine(def_id, args) => {
1491 self.push("coroutine");
1492 self.push(&format!("+ def_id: {def_id:?}"));
1493 self.push(&format!("+ args: {args:#?}"));
1494 self.push(&format!("+ kind: {:?}", self.tcx.coroutine_kind(def_id)));
1495 }
1496
1497 AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
1498 self.push("adt");
1499 self.push(&format!("+ user_ty: {user_ty:?}"));
1500 }
1501
1502 _ => {}
1503 }
1504 }
1505 }
1506}
1507
1508fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
1509 let location = tcx.sess.source_map().span_to_embeddable_string(span);
1510 format!("scope {} at {}", scope.index(), location,)
1511}
1512
1513pub fn write_allocations<'tcx>(
1519 tcx: TyCtxt<'tcx>,
1520 body: &Body<'_>,
1521 w: &mut dyn io::Write,
1522) -> io::Result<()> {
1523 fn alloc_ids_from_alloc(
1524 alloc: ConstAllocation<'_>,
1525 ) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
1526 alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id())
1527 }
1528
1529 fn alloc_id_from_const_val(val: ConstValue<'_>) -> Option<AllocId> {
1530 match val {
1531 ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()),
1532 ConstValue::Scalar(interpret::Scalar::Int { .. }) => None,
1533 ConstValue::ZeroSized => None,
1534 ConstValue::Slice { .. } => {
1535 None
1537 }
1538 ConstValue::Indirect { alloc_id, .. } => {
1539 Some(alloc_id)
1542 }
1543 }
1544 }
1545 struct CollectAllocIds(BTreeSet<AllocId>);
1546
1547 impl<'tcx> Visitor<'tcx> for CollectAllocIds {
1548 fn visit_const_operand(&mut self, c: &ConstOperand<'tcx>, _: Location) {
1549 match c.const_ {
1550 Const::Ty(_, _) | Const::Unevaluated(..) => {}
1551 Const::Val(val, _) => {
1552 if let Some(id) = alloc_id_from_const_val(val) {
1553 self.0.insert(id);
1554 }
1555 }
1556 }
1557 }
1558 }
1559
1560 let mut visitor = CollectAllocIds(Default::default());
1561 visitor.visit_body(body);
1562
1563 let mut seen = visitor.0;
1567 let mut todo: Vec<_> = seen.iter().copied().collect();
1568 while let Some(id) = todo.pop() {
1569 let mut write_allocation_track_relocs =
1570 |w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
1571 for id in alloc_ids_from_alloc(alloc).rev() {
1573 if seen.insert(id) {
1574 todo.push(id);
1575 }
1576 }
1577 write!(w, "{}", display_allocation(tcx, alloc.inner()))
1578 };
1579 write!(w, "\n{id:?}")?;
1580 match tcx.try_get_global_alloc(id) {
1581 None => write!(w, " (deallocated)")?,
1584 Some(GlobalAlloc::Function { instance, .. }) => write!(w, " (fn: {instance})")?,
1585 Some(GlobalAlloc::VTable(ty, dyn_ty)) => {
1586 write!(w, " (vtable: impl {dyn_ty} for {ty})")?
1587 }
1588 Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
1589 write!(w, " (static: {}", tcx.def_path_str(did))?;
1590 if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup)
1591 && tcx.hir().body_const_context(body.source.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) = alloc.provenance().get(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:#?} (1 ptr byte)╼")?;
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<'tcx>, 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<'tcx>,
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 cx = FmtPrinter::new(tcx, Namespace::ValueNS);
1934 cx.print_alloc_ids = true;
1935 cx.print_value_path(variant_def.def_id, args)?;
1936 fmt.write_str(&cx.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 cx = FmtPrinter::new(tcx, Namespace::ValueNS);
1968 cx.print_alloc_ids = true;
1969 let ty = tcx.lift(ty).unwrap();
1970 cx.pretty_print_const_scalar(scalar, ty)?;
1971 fmt.write_str(&cx.into_buffer())?;
1972 return Ok(());
1973 }
1974 (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
1975 let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
1976 cx.print_alloc_ids = true;
1977 cx.print_value_path(*d, s)?;
1978 fmt.write_str(&cx.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<'tcx>,
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}