1use std::fmt::Debug;
3use std::io::Write;
4use std::{fmt, io, iter};
5
6use fmt::{Display, Formatter};
7
8use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind};
9use crate::mir::{
10 Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents,
11};
12use crate::ty::{AdtKind, AssocKind, MirConst, Ty, TyConst};
13use crate::{Body, CrateDef, IndexedVal, Mutability, with};
14
15impl Display for Ty {
16 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
17 with(|ctx| write!(f, "{}", ctx.ty_pretty(*self)))
18 }
19}
20
21impl Display for AssocKind {
22 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
23 match self {
24 AssocKind::Fn { has_self: true, .. } => write!(f, "method"),
25 AssocKind::Fn { has_self: false, .. } => write!(f, "associated function"),
26 AssocKind::Const { .. } => write!(f, "associated const"),
27 AssocKind::Type { .. } => write!(f, "associated type"),
28 }
29 }
30}
31
32impl Debug for Place {
33 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
34 with(|ctx| write!(f, "{}", ctx.place_pretty(self)))
35 }
36}
37
38pub(crate) fn function_body<W: Write>(writer: &mut W, body: &Body, name: &str) -> io::Result<()> {
39 write!(writer, "fn {name}(")?;
40 let mut sep = "";
41 for (index, local) in body.arg_locals().iter().enumerate() {
42 write!(writer, "{}_{}: {}", sep, index + 1, local.ty)?;
43 sep = ", ";
44 }
45 write!(writer, ")")?;
46
47 let return_local = body.ret_local();
48 writeln!(writer, " -> {} {{", return_local.ty)?;
49
50 body.locals().iter().enumerate().try_for_each(|(index, local)| -> io::Result<()> {
51 if index == 0 || index > body.arg_count {
52 writeln!(writer, " let {}_{}: {};", pretty_mut(local.mutability), index, local.ty)
53 } else {
54 Ok(())
55 }
56 })?;
57
58 body.var_debug_info.iter().try_for_each(|info| {
59 let content = match &info.value {
60 VarDebugInfoContents::Place(place) => {
61 format!("{place:?}")
62 }
63 VarDebugInfoContents::Const(constant) => pretty_mir_const(&constant.const_),
64 };
65 writeln!(writer, " debug {} => {};", info.name, content)
66 })?;
67
68 body.blocks
69 .iter()
70 .enumerate()
71 .map(|(index, block)| -> io::Result<()> {
72 writeln!(writer, " bb{index}: {{")?;
73 let _ = block
74 .statements
75 .iter()
76 .map(|statement| -> io::Result<()> {
77 pretty_statement(writer, &statement.kind)?;
78 Ok(())
79 })
80 .collect::<Vec<_>>();
81 pretty_terminator(writer, &block.terminator.kind)?;
82 writeln!(writer, " }}").unwrap();
83 Ok(())
84 })
85 .collect::<Result<Vec<_>, _>>()?;
86 writeln!(writer, "}}")?;
87 Ok(())
88}
89
90fn pretty_statement<W: Write>(writer: &mut W, statement: &StatementKind) -> io::Result<()> {
91 const INDENT: &str = " ";
92 match statement {
93 StatementKind::Assign(place, rval) => {
94 write!(writer, "{INDENT}{place:?} = ")?;
95 pretty_rvalue(writer, rval)?;
96 writeln!(writer, ";")
97 }
98 StatementKind::FakeRead(cause, place) => {
100 writeln!(writer, "{INDENT}FakeRead({cause:?}, {place:?});")
101 }
102 StatementKind::SetDiscriminant { place, variant_index } => {
103 writeln!(writer, "{INDENT}discriminant({place:?}) = {};", variant_index.to_index())
104 }
105 StatementKind::StorageLive(local) => {
106 writeln!(writer, "{INDENT}StorageLive(_{local});")
107 }
108 StatementKind::StorageDead(local) => {
109 writeln!(writer, "{INDENT}StorageDead(_{local});")
110 }
111 StatementKind::Retag(kind, place) => writeln!(writer, "Retag({kind:?}, {place:?});"),
112 StatementKind::PlaceMention(place) => {
113 writeln!(writer, "{INDENT}PlaceMention({place:?};")
114 }
115 StatementKind::ConstEvalCounter => {
116 writeln!(writer, "{INDENT}ConstEvalCounter;")
117 }
118 StatementKind::Nop => writeln!(writer, "{INDENT}nop;"),
119 StatementKind::AscribeUserType { .. }
120 | StatementKind::Coverage(_)
121 | StatementKind::Intrinsic(_) => {
122 writeln!(writer, "{INDENT}{statement:?};")
124 }
125 }
126}
127
128fn pretty_terminator<W: Write>(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> {
129 pretty_terminator_head(writer, terminator)?;
130 let successors = terminator.successors();
131 let successor_count = successors.len();
132 let labels = pretty_successor_labels(terminator);
133
134 let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_)));
135 let fmt_unwind = |w: &mut W| -> io::Result<()> {
136 write!(w, "unwind ")?;
137 match terminator.unwind() {
138 None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
139 Some(UnwindAction::Continue) => write!(w, "continue"),
140 Some(UnwindAction::Unreachable) => write!(w, "unreachable"),
141 Some(UnwindAction::Terminate) => write!(w, "terminate"),
142 }
143 };
144
145 match (successor_count, show_unwind) {
146 (0, false) => {}
147 (0, true) => {
148 write!(writer, " -> ")?;
149 fmt_unwind(writer)?;
150 }
151 (1, false) => write!(writer, " -> bb{:?}", successors[0])?,
152 _ => {
153 write!(writer, " -> [")?;
154 for (i, target) in successors.iter().enumerate() {
155 if i > 0 {
156 write!(writer, ", ")?;
157 }
158 write!(writer, "{}: bb{:?}", labels[i], target)?;
159 }
160 if show_unwind {
161 write!(writer, ", ")?;
162 fmt_unwind(writer)?;
163 }
164 write!(writer, "]")?;
165 }
166 };
167
168 writeln!(writer, ";")
169}
170
171fn pretty_terminator_head<W: Write>(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> {
172 use self::TerminatorKind::*;
173 const INDENT: &str = " ";
174 match terminator {
175 Goto { .. } => write!(writer, "{INDENT}goto"),
176 SwitchInt { discr, .. } => {
177 write!(writer, "{INDENT}switchInt({})", pretty_operand(discr))
178 }
179 Resume => write!(writer, "{INDENT}resume"),
180 Abort => write!(writer, "{INDENT}abort"),
181 Return => write!(writer, "{INDENT}return"),
182 Unreachable => write!(writer, "{INDENT}unreachable"),
183 Drop { place, .. } => write!(writer, "{INDENT}drop({place:?})"),
184 Call { func, args, destination, .. } => {
185 write!(writer, "{INDENT}{:?} = {}(", destination, pretty_operand(func))?;
186 let mut args_iter = args.iter();
187 args_iter.next().map_or(Ok(()), |arg| write!(writer, "{}", pretty_operand(arg)))?;
188 args_iter.try_for_each(|arg| write!(writer, ", {}", pretty_operand(arg)))?;
189 write!(writer, ")")
190 }
191 Assert { cond, expected, msg, target: _, unwind: _ } => {
192 write!(writer, "{INDENT}assert(")?;
193 if !expected {
194 write!(writer, "!")?;
195 }
196 write!(writer, "{}, ", pretty_operand(cond))?;
197 pretty_assert_message(writer, msg)?;
198 write!(writer, ")")
199 }
200 InlineAsm { .. } => write!(writer, "{INDENT}InlineAsm"),
201 }
202}
203
204fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> {
205 use self::TerminatorKind::*;
206 match terminator {
207 Call { target: None, unwind: UnwindAction::Cleanup(_), .. }
208 | InlineAsm { destination: None, .. } => vec!["unwind".into()],
209 Resume | Abort | Return | Unreachable | Call { target: None, unwind: _, .. } => vec![],
210 Goto { .. } => vec!["".to_string()],
211 SwitchInt { targets, .. } => targets
212 .branches()
213 .map(|(val, _target)| format!("{val}"))
214 .chain(iter::once("otherwise".into()))
215 .collect(),
216 Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
217 Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
218 vec!["return".into(), "unwind".into()]
219 }
220 Drop { unwind: _, .. } | Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
221 Assert { unwind: UnwindAction::Cleanup(_), .. } => {
222 vec!["success".into(), "unwind".into()]
223 }
224 Assert { unwind: _, .. } => vec!["success".into()],
225 InlineAsm { destination: Some(_), .. } => vec!["goto".into(), "unwind".into()],
226 }
227}
228
229fn pretty_assert_message<W: Write>(writer: &mut W, msg: &AssertMessage) -> io::Result<()> {
230 match msg {
231 AssertMessage::BoundsCheck { len, index } => {
232 let pretty_len = pretty_operand(len);
233 let pretty_index = pretty_operand(index);
234 write!(
235 writer,
236 "\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}"
237 )
238 }
239 AssertMessage::Overflow(BinOp::Add, l, r) => {
240 let pretty_l = pretty_operand(l);
241 let pretty_r = pretty_operand(r);
242 write!(
243 writer,
244 "\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
245 )
246 }
247 AssertMessage::Overflow(BinOp::Sub, l, r) => {
248 let pretty_l = pretty_operand(l);
249 let pretty_r = pretty_operand(r);
250 write!(
251 writer,
252 "\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
253 )
254 }
255 AssertMessage::Overflow(BinOp::Mul, l, r) => {
256 let pretty_l = pretty_operand(l);
257 let pretty_r = pretty_operand(r);
258 write!(
259 writer,
260 "\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
261 )
262 }
263 AssertMessage::Overflow(BinOp::Div, l, r) => {
264 let pretty_l = pretty_operand(l);
265 let pretty_r = pretty_operand(r);
266 write!(
267 writer,
268 "\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
269 )
270 }
271 AssertMessage::Overflow(BinOp::Rem, l, r) => {
272 let pretty_l = pretty_operand(l);
273 let pretty_r = pretty_operand(r);
274 write!(
275 writer,
276 "\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}"
277 )
278 }
279 AssertMessage::Overflow(BinOp::Shr, _, r) => {
280 let pretty_r = pretty_operand(r);
281 write!(writer, "\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}")
282 }
283 AssertMessage::Overflow(BinOp::Shl, _, r) => {
284 let pretty_r = pretty_operand(r);
285 write!(writer, "\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}")
286 }
287 AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op),
288 AssertMessage::OverflowNeg(op) => {
289 let pretty_op = pretty_operand(op);
290 write!(writer, "\"attempt to negate `{{}}`, which would overflow\", {pretty_op}")
291 }
292 AssertMessage::DivisionByZero(op) => {
293 let pretty_op = pretty_operand(op);
294 write!(writer, "\"attempt to divide `{{}}` by zero\", {pretty_op}")
295 }
296 AssertMessage::RemainderByZero(op) => {
297 let pretty_op = pretty_operand(op);
298 write!(
299 writer,
300 "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}"
301 )
302 }
303 AssertMessage::MisalignedPointerDereference { required, found } => {
304 let pretty_required = pretty_operand(required);
305 let pretty_found = pretty_operand(found);
306 write!(
307 writer,
308 "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}"
309 )
310 }
311 AssertMessage::NullPointerDereference => {
312 write!(writer, "\"null pointer dereference occurred\"")
313 }
314 AssertMessage::InvalidEnumConstruction(op) => {
315 let pretty_op = pretty_operand(op);
316 write!(writer, "\"trying to construct an enum from an invalid value {{}}\",{pretty_op}")
317 }
318 AssertMessage::ResumedAfterReturn(_)
319 | AssertMessage::ResumedAfterPanic(_)
320 | AssertMessage::ResumedAfterDrop(_) => {
321 write!(writer, "{}", msg.description().unwrap())
322 }
323 }
324}
325
326fn pretty_operand(operand: &Operand) -> String {
327 match operand {
328 Operand::Copy(copy) => {
329 format!("{copy:?}")
330 }
331 Operand::Move(mv) => {
332 format!("move {mv:?}")
333 }
334 Operand::Constant(cnst) => pretty_mir_const(&cnst.const_),
335 }
336}
337
338fn pretty_mir_const(literal: &MirConst) -> String {
339 with(|cx| cx.mir_const_pretty(literal))
340}
341
342fn pretty_ty_const(ct: &TyConst) -> String {
343 with(|cx| cx.ty_const_pretty(ct.id))
344}
345
346fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> {
347 match rval {
348 Rvalue::AddressOf(mutability, place) => {
349 write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place)
350 }
351 Rvalue::Aggregate(aggregate_kind, operands) => {
352 pretty_aggregate(writer, aggregate_kind, operands)
354 }
355 Rvalue::BinaryOp(bin, op1, op2) => {
356 write!(writer, "{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2))
357 }
358 Rvalue::Cast(_, op, ty) => {
359 write!(writer, "{} as {}", pretty_operand(op), ty)
360 }
361 Rvalue::CheckedBinaryOp(bin, op1, op2) => {
362 write!(writer, "Checked{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2))
363 }
364 Rvalue::CopyForDeref(deref) => {
365 write!(writer, "CopyForDeref({deref:?})")
366 }
367 Rvalue::Discriminant(place) => {
368 write!(writer, "discriminant({place:?})")
369 }
370 Rvalue::Len(len) => {
371 write!(writer, "len({len:?})")
372 }
373 Rvalue::Ref(_, borrowkind, place) => {
374 let kind = match borrowkind {
375 BorrowKind::Shared => "&",
376 BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ",
377 BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ",
378 BorrowKind::Mut { .. } => "&mut ",
379 };
380 write!(writer, "{kind}{place:?}")
381 }
382 Rvalue::Repeat(op, cnst) => {
383 write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst))
384 }
385 Rvalue::ShallowInitBox(_, _) => Ok(()),
386 Rvalue::ThreadLocalRef(item) => {
387 write!(writer, "thread_local_ref{item:?}")
388 }
389 Rvalue::NullaryOp(nul, ty) => {
390 write!(writer, "{nul:?}::<{ty}>() \" \"")
391 }
392 Rvalue::UnaryOp(un, op) => {
393 write!(writer, "{:?}({})", un, pretty_operand(op))
394 }
395 Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)),
396 }
397}
398
399fn pretty_aggregate<W: Write>(
400 writer: &mut W,
401 aggregate_kind: &AggregateKind,
402 operands: &Vec<Operand>,
403) -> io::Result<()> {
404 let suffix = match aggregate_kind {
405 AggregateKind::Array(_) => {
406 write!(writer, "[")?;
407 "]"
408 }
409 AggregateKind::Tuple => {
410 write!(writer, "(")?;
411 ")"
412 }
413 AggregateKind::Adt(def, var, _, _, _) => {
414 if def.kind() == AdtKind::Enum {
415 write!(writer, "{}::{}", def.name(), def.variant(*var).unwrap().name())?;
416 } else {
417 write!(writer, "{}", def.variant(*var).unwrap().name())?;
418 }
419 if operands.is_empty() {
420 return Ok(());
421 }
422 write!(writer, "(")?;
424 ")"
425 }
426 AggregateKind::Closure(def, _) => {
427 write!(writer, "{{closure@{}}}(", def.span().diagnostic())?;
428 ")"
429 }
430 AggregateKind::Coroutine(def, _) => {
431 write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?;
432 ")"
433 }
434 AggregateKind::CoroutineClosure(def, _) => {
435 write!(writer, "{{coroutine-closure@{}}}(", def.span().diagnostic())?;
436 ")"
437 }
438 AggregateKind::RawPtr(ty, mutability) => {
439 write!(
440 writer,
441 "*{} {ty} from (",
442 if *mutability == Mutability::Mut { "mut" } else { "const" }
443 )?;
444 ")"
445 }
446 };
447 let mut separator = "";
448 for op in operands {
449 write!(writer, "{}{}", separator, pretty_operand(op))?;
450 separator = ", ";
451 }
452 write!(writer, "{suffix}")
453}
454
455fn pretty_mut(mutability: Mutability) -> &'static str {
456 match mutability {
457 Mutability::Not => " ",
458 Mutability::Mut => "mut ",
459 }
460}
461
462fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str {
463 match kind {
464 RawPtrKind::Const => "const",
465 RawPtrKind::Mut => "mut",
466 RawPtrKind::FakeForPtrMetadata => "const (fake)",
467 }
468}