rustc_smir/stable_mir/mir/
pretty.rs

1//! Implement methods to pretty print stable MIR body.
2use 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        // FIXME: Add rest of the statements
99        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            // FIX-ME: Make them pretty.
124            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            // FIXME: Add pretty_aggregate function that returns a pretty string
348            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            // FIXME: Change this once we have CtorKind in StableMIR.
418            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}