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 { 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        // FIXME: Add rest of the statements
100        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            // FIX-ME: Make them pretty.
125            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            // FIXME: Add pretty_aggregate function that returns a pretty string
351            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            // FIXME: Change this once we have CtorKind in StableMIR.
421            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}