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};
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, AssocKind, MirConst, Ty, TyConst};
13use crate::{Body, CrateDef, IndexedVal, 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 Display for AssocKind {
22    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
23        match self {
24            AssocKind::Fn { has_self: true, .. } => write!(f, "method"),
25            AssocKind::Fn { has_self: false, .. } => write!(f, "associated function"),
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::InvalidEnumConstruction(op) => {
316            let pretty_op = pretty_operand(op);
317            write!(writer, "\"trying to construct an enum from an invalid value {{}}\",{pretty_op}")
318        }
319        AssertMessage::ResumedAfterReturn(_)
320        | AssertMessage::ResumedAfterPanic(_)
321        | AssertMessage::ResumedAfterDrop(_) => {
322            write!(writer, "{}", msg.description().unwrap())
323        }
324    }
325}
326
327fn pretty_operand(operand: &Operand) -> String {
328    match operand {
329        Operand::Copy(copy) => {
330            format!("{copy:?}")
331        }
332        Operand::Move(mv) => {
333            format!("move {mv:?}")
334        }
335        Operand::Constant(cnst) => pretty_mir_const(&cnst.const_),
336    }
337}
338
339fn pretty_mir_const(literal: &MirConst) -> String {
340    with(|cx| cx.mir_const_pretty(literal))
341}
342
343fn pretty_ty_const(ct: &TyConst) -> String {
344    with(|cx| cx.ty_const_pretty(ct.id))
345}
346
347fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> {
348    match rval {
349        Rvalue::AddressOf(mutability, place) => {
350            write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place)
351        }
352        Rvalue::Aggregate(aggregate_kind, operands) => {
353            // FIXME: Add pretty_aggregate function that returns a pretty string
354            pretty_aggregate(writer, aggregate_kind, operands)
355        }
356        Rvalue::BinaryOp(bin, op1, op2) => {
357            write!(writer, "{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2))
358        }
359        Rvalue::Cast(_, op, ty) => {
360            write!(writer, "{} as {}", pretty_operand(op), ty)
361        }
362        Rvalue::CheckedBinaryOp(bin, op1, op2) => {
363            write!(writer, "Checked{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2))
364        }
365        Rvalue::CopyForDeref(deref) => {
366            write!(writer, "CopyForDeref({deref:?})")
367        }
368        Rvalue::Discriminant(place) => {
369            write!(writer, "discriminant({place:?})")
370        }
371        Rvalue::Len(len) => {
372            write!(writer, "len({len:?})")
373        }
374        Rvalue::Ref(_, borrowkind, place) => {
375            let kind = match borrowkind {
376                BorrowKind::Shared => "&",
377                BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ",
378                BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ",
379                BorrowKind::Mut { .. } => "&mut ",
380            };
381            write!(writer, "{kind}{place:?}")
382        }
383        Rvalue::Repeat(op, cnst) => {
384            write!(writer, "[{}; {}]", pretty_operand(op), pretty_ty_const(cnst))
385        }
386        Rvalue::ShallowInitBox(_, _) => Ok(()),
387        Rvalue::ThreadLocalRef(item) => {
388            write!(writer, "thread_local_ref{item:?}")
389        }
390        Rvalue::NullaryOp(nul, ty) => {
391            write!(writer, "{nul:?}::<{ty}>() \" \"")
392        }
393        Rvalue::UnaryOp(un, op) => {
394            write!(writer, "{:?}({})", un, pretty_operand(op))
395        }
396        Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)),
397    }
398}
399
400fn pretty_aggregate<W: Write>(
401    writer: &mut W,
402    aggregate_kind: &AggregateKind,
403    operands: &Vec<Operand>,
404) -> io::Result<()> {
405    let suffix = match aggregate_kind {
406        AggregateKind::Array(_) => {
407            write!(writer, "[")?;
408            "]"
409        }
410        AggregateKind::Tuple => {
411            write!(writer, "(")?;
412            ")"
413        }
414        AggregateKind::Adt(def, var, _, _, _) => {
415            if def.kind() == AdtKind::Enum {
416                write!(writer, "{}::{}", def.name(), def.variant(*var).unwrap().name())?;
417            } else {
418                write!(writer, "{}", def.variant(*var).unwrap().name())?;
419            }
420            if operands.is_empty() {
421                return Ok(());
422            }
423            // FIXME: Change this once we have CtorKind in StableMIR.
424            write!(writer, "(")?;
425            ")"
426        }
427        AggregateKind::Closure(def, _) => {
428            write!(writer, "{{closure@{}}}(", def.span().diagnostic())?;
429            ")"
430        }
431        AggregateKind::Coroutine(def, _, _) => {
432            write!(writer, "{{coroutine@{}}}(", def.span().diagnostic())?;
433            ")"
434        }
435        AggregateKind::CoroutineClosure(def, _) => {
436            write!(writer, "{{coroutine-closure@{}}}(", def.span().diagnostic())?;
437            ")"
438        }
439        AggregateKind::RawPtr(ty, mutability) => {
440            write!(
441                writer,
442                "*{} {ty} from (",
443                if *mutability == Mutability::Mut { "mut" } else { "const" }
444            )?;
445            ")"
446        }
447    };
448    let mut separator = "";
449    for op in operands {
450        write!(writer, "{}{}", separator, pretty_operand(op))?;
451        separator = ", ";
452    }
453    write!(writer, "{suffix}")
454}
455
456fn pretty_mut(mutability: Mutability) -> &'static str {
457    match mutability {
458        Mutability::Not => " ",
459        Mutability::Mut => "mut ",
460    }
461}
462
463fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str {
464    match kind {
465        RawPtrKind::Const => "const",
466        RawPtrKind::Mut => "mut",
467        RawPtrKind::FakeForPtrMetadata => "const (fake)",
468    }
469}