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