rustc_public/mir/
pretty.rs

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