Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/pretty.rs')
| -rw-r--r-- | crates/hir-ty/src/mir/pretty.rs | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs new file mode 100644 index 0000000000..ffc08b7e34 --- /dev/null +++ b/crates/hir-ty/src/mir/pretty.rs @@ -0,0 +1,348 @@ +//! A pretty-printer for MIR. + +use std::fmt::{Display, Write}; + +use hir_def::{body::Body, expr::BindingId}; +use hir_expand::name::Name; +use la_arena::ArenaMap; + +use crate::{ + db::HirDatabase, + display::HirDisplay, + mir::{PlaceElem, ProjectionElem, StatementKind, Terminator}, +}; + +use super::{ + AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp, +}; + +impl MirBody { + pub fn pretty_print(&self, db: &dyn HirDatabase) -> String { + let hir_body = db.body(self.owner); + let mut ctx = MirPrettyCtx::new(self, &hir_body, db); + ctx.for_body(); + ctx.result + } +} + +struct MirPrettyCtx<'a> { + body: &'a MirBody, + hir_body: &'a Body, + db: &'a dyn HirDatabase, + result: String, + ident: String, + local_to_binding: ArenaMap<LocalId, BindingId>, +} + +macro_rules! w { + ($dst:expr, $($arg:tt)*) => { + { let _ = write!($dst, $($arg)*); } + }; +} + +macro_rules! wln { + ($dst:expr) => { + { let _ = writeln!($dst); } + }; + ($dst:expr, $($arg:tt)*) => { + { let _ = writeln!($dst, $($arg)*); } + }; +} + +impl Write for MirPrettyCtx<'_> { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + let mut it = s.split('\n'); // note: `.lines()` is wrong here + self.write(it.next().unwrap_or_default()); + for line in it { + self.write_line(); + self.write(line); + } + Ok(()) + } +} + +enum LocalName { + Unknown(LocalId), + Binding(Name, LocalId), +} + +impl Display for LocalName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())), + LocalName::Binding(n, l) => write!(f, "{n}_{}", u32::from(l.into_raw())), + } + } +} + +impl<'a> MirPrettyCtx<'a> { + fn for_body(&mut self) { + self.with_block(|this| { + this.locals(); + wln!(this); + this.blocks(); + }); + } + + fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) { + self.ident += " "; + wln!(self, "{{"); + f(self); + for _ in 0..4 { + self.result.pop(); + self.ident.pop(); + } + wln!(self, "}}"); + } + + fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self { + let local_to_binding = body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(); + MirPrettyCtx { + body, + db, + result: String::new(), + ident: String::new(), + local_to_binding, + hir_body, + } + } + + fn write_line(&mut self) { + self.result.push('\n'); + self.result += &self.ident; + } + + fn write(&mut self, line: &str) { + self.result += line; + } + + fn locals(&mut self) { + for (id, local) in self.body.locals.iter() { + wln!(self, "let {}: {};", self.local_name(id), local.ty.display(self.db)); + } + } + + fn local_name(&self, local: LocalId) -> LocalName { + match self.local_to_binding.get(local) { + Some(b) => LocalName::Binding(self.hir_body.bindings[*b].name.clone(), local), + None => LocalName::Unknown(local), + } + } + + fn basic_block_id(&self, basic_block_id: BasicBlockId) -> String { + format!("'bb{}", u32::from(basic_block_id.into_raw())) + } + + fn blocks(&mut self) { + for (id, block) in self.body.basic_blocks.iter() { + wln!(self); + w!(self, "{}: ", self.basic_block_id(id)); + self.with_block(|this| { + for statement in &block.statements { + match &statement.kind { + StatementKind::Assign(l, r) => { + this.place(l); + w!(this, " = "); + this.rvalue(r); + wln!(this, ";"); + } + StatementKind::StorageDead(p) => { + wln!(this, "StorageDead({})", this.local_name(*p)); + } + StatementKind::StorageLive(p) => { + wln!(this, "StorageLive({})", this.local_name(*p)); + } + StatementKind::Deinit(p) => { + w!(this, "Deinit("); + this.place(p); + wln!(this, ");"); + } + StatementKind::Nop => wln!(this, "Nop;"), + } + } + match &block.terminator { + Some(terminator) => match terminator { + Terminator::Goto { target } => { + wln!(this, "goto 'bb{};", u32::from(target.into_raw())) + } + Terminator::SwitchInt { discr, targets } => { + w!(this, "switch "); + this.operand(discr); + w!(this, " "); + this.with_block(|this| { + for (c, b) in targets.iter() { + wln!(this, "{c} => {},", this.basic_block_id(b)); + } + wln!(this, "_ => {},", this.basic_block_id(targets.otherwise())); + }); + } + Terminator::Call { func, args, destination, target, .. } => { + w!(this, "Call "); + this.with_block(|this| { + w!(this, "func: "); + this.operand(func); + wln!(this, ","); + w!(this, "args: ["); + this.operand_list(args); + wln!(this, "],"); + w!(this, "destination: "); + this.place(destination); + wln!(this, ","); + w!(this, "target: "); + match target { + Some(t) => w!(this, "{}", this.basic_block_id(*t)), + None => w!(this, "<unreachable>"), + } + wln!(this, ","); + }); + } + _ => wln!(this, "{:?};", terminator), + }, + None => wln!(this, "<no-terminator>;"), + } + }) + } + } + + fn place(&mut self, p: &Place) { + fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) { + let Some((last, head)) = projections.split_last() else { + // no projection + w!(this, "{}", this.local_name(local)); + return; + }; + match last { + ProjectionElem::Deref => { + w!(this, "(*"); + f(this, local, head); + w!(this, ")"); + } + ProjectionElem::Field(field) => { + let variant_data = field.parent.variant_data(this.db.upcast()); + let name = &variant_data.fields()[field.local_id].name; + match field.parent { + hir_def::VariantId::EnumVariantId(e) => { + w!(this, "("); + f(this, local, head); + let variant_name = + &this.db.enum_data(e.parent).variants[e.local_id].name; + w!(this, " as {}).{}", variant_name, name); + } + hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => { + f(this, local, head); + w!(this, ".{name}"); + } + } + } + ProjectionElem::TupleField(x) => { + f(this, local, head); + w!(this, ".{}", x); + } + ProjectionElem::Index(l) => { + f(this, local, head); + w!(this, "[{}]", this.local_name(*l)); + } + x => { + f(this, local, head); + w!(this, ".{:?}", x); + } + } + } + f(self, p.local, &p.projection); + } + + fn operand(&mut self, r: &Operand) { + match r { + Operand::Copy(p) | Operand::Move(p) => { + // MIR at the time of writing doesn't have difference between move and copy, so we show them + // equally. Feel free to change it. + self.place(p); + } + Operand::Constant(c) => w!(self, "Const({})", c.display(self.db)), + } + } + + fn rvalue(&mut self, r: &Rvalue) { + match r { + Rvalue::Use(op) => self.operand(op), + Rvalue::Ref(r, p) => { + match r { + BorrowKind::Shared => w!(self, "&"), + BorrowKind::Shallow => w!(self, "&shallow "), + BorrowKind::Unique => w!(self, "&uniq "), + BorrowKind::Mut { .. } => w!(self, "&mut "), + } + self.place(p); + } + Rvalue::Aggregate(AggregateKind::Tuple(_), x) => { + w!(self, "("); + self.operand_list(x); + w!(self, ")"); + } + Rvalue::Aggregate(AggregateKind::Array(_), x) => { + w!(self, "["); + self.operand_list(x); + w!(self, "]"); + } + Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => { + w!(self, "Adt("); + self.operand_list(x); + w!(self, ")"); + } + Rvalue::Aggregate(AggregateKind::Union(_, _), x) => { + w!(self, "Union("); + self.operand_list(x); + w!(self, ")"); + } + Rvalue::Len(p) => { + w!(self, "Len("); + self.place(p); + w!(self, ")"); + } + Rvalue::Cast(ck, op, ty) => { + w!(self, "Discriminant({ck:?}"); + self.operand(op); + w!(self, "{})", ty.display(self.db)); + } + Rvalue::CheckedBinaryOp(b, o1, o2) => { + self.operand(o1); + w!(self, " {b} "); + self.operand(o2); + } + Rvalue::UnaryOp(u, o) => { + let u = match u { + UnOp::Not => "!", + UnOp::Neg => "-", + }; + w!(self, "{u} "); + self.operand(o); + } + Rvalue::Discriminant(p) => { + w!(self, "Discriminant("); + self.place(p); + w!(self, ")"); + } + Rvalue::ShallowInitBox(op, _) => { + w!(self, "ShallowInitBox("); + self.operand(op); + w!(self, ")"); + } + Rvalue::CopyForDeref(p) => { + w!(self, "CopyForDeref("); + self.place(p); + w!(self, ")"); + } + } + } + + fn operand_list(&mut self, x: &[Operand]) { + let mut it = x.iter(); + if let Some(first) = it.next() { + self.operand(first); + for op in it { + w!(self, ", "); + self.operand(op); + } + } + } +} |