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