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