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