1use std::{
4 fmt::{Debug, Display, Write},
5 mem,
6};
7
8use hir_def::{
9 HasModule, VariantId,
10 expr_store::ExpressionStore,
11 hir::BindingId,
12 signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature},
13};
14use hir_expand::{Lookup, name::Name};
15use la_arena::ArenaMap;
16use rustc_type_ir::inherent::IntoKind;
17
18use crate::{
19 InferBodyId,
20 db::{HirDatabase, InternedClosureId},
21 display::{ClosureStyle, DisplayTarget, HirDisplay},
22 mir::{PlaceElem, PlaceTy, ProjectionElem, StatementKind, TerminatorKind},
23 next_solver::{DbInterner, TyKind, infer::DbInternerInferExt},
24};
25
26use super::{
27 AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, MutBorrowKind, Operand, OperandKind,
28 Place, Rvalue, UnOp,
29};
30
31macro_rules! w {
32 ($dst:expr, $($arg:tt)*) => {
33 { let _ = write!($dst, $($arg)*); }
34 };
35}
36
37macro_rules! wln {
38 ($dst:expr) => {
39 { let _ = writeln!($dst); }
40 };
41 ($dst:expr, $($arg:tt)*) => {
42 { let _ = writeln!($dst, $($arg)*); }
43 };
44}
45
46impl MirBody<'_> {
47 pub fn pretty_print(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
48 let hir_body = ExpressionStore::of(db, self.owner.expression_store_owner(db));
49 let mut ctx = MirPrettyCtx::new(self, hir_body, db, display_target);
50 ctx.for_body(|this| match ctx.body.owner {
51 InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::FunctionId(id)) => {
52 let data = FunctionSignature::of(db, id);
53 w!(this, "fn {}() ", data.name.display(db, this.display_target.edition));
54 }
55 InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::StaticId(id)) => {
56 let data = StaticSignature::of(db, id);
57 w!(this, "static {}: _ = ", data.name.display(db, this.display_target.edition));
58 }
59 InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::ConstId(id)) => {
60 let data = ConstSignature::of(db, id);
61 w!(
62 this,
63 "const {}: _ = ",
64 data.name
65 .as_ref()
66 .unwrap_or(&Name::missing())
67 .display(db, this.display_target.edition)
68 );
69 }
70 InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::VariantId(id)) => {
71 let loc = id.lookup(db);
72 let edition = this.display_target.edition;
73 w!(
74 this,
75 "enum {}::{} = ",
76 EnumSignature::of(db, loc.parent).name.display(db, edition),
77 loc.parent
78 .enum_variants(db)
79 .variant_name_by_id(id)
80 .unwrap()
81 .display(db, edition),
82 )
83 }
84 InferBodyId::AnonConstId(_) => w!(this, "{{const}}"),
85 });
86 ctx.result
87 }
88
89 pub fn dbg(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> impl Debug {
92 struct StringDbg(String);
93 impl Debug for StringDbg {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 f.write_str(&self.0)
96 }
97 }
98 StringDbg(self.pretty_print(db, display_target))
99 }
100}
101
102struct MirPrettyCtx<'a, 'db> {
103 body: &'a MirBody<'db>,
104 hir_body: &'a ExpressionStore,
105 db: &'db dyn HirDatabase,
106 result: String,
107 indent: String,
108 local_to_binding: ArenaMap<LocalId, BindingId>,
109 display_target: DisplayTarget,
110}
111
112impl Write for MirPrettyCtx<'_, '_> {
113 fn write_str(&mut self, s: &str) -> std::fmt::Result {
114 let mut it = s.split('\n'); self.write(it.next().unwrap_or_default());
116 for line in it {
117 self.write_line();
118 self.write(line);
119 }
120 Ok(())
121 }
122}
123
124enum LocalName {
125 Unknown(LocalId),
126 Binding(Name, LocalId),
127}
128
129impl<'db> HirDisplay<'db> for LocalName {
130 fn hir_fmt(
131 &self,
132 f: &mut crate::display::HirFormatter<'_, 'db>,
133 ) -> Result<(), crate::display::HirDisplayError> {
134 match self {
135 LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())),
136 LocalName::Binding(n, l) => {
137 write!(f, "{}_{}", n.display(f.db, f.edition()), u32::from(l.into_raw()))
138 }
139 }
140 }
141}
142
143impl<'a, 'db> MirPrettyCtx<'a, 'db> {
144 fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_, 'db>)) {
145 name(self);
146 self.with_block(|this| {
147 this.locals();
148 wln!(this);
149 this.blocks();
150 });
151 for &closure in &self.body.closures {
152 self.for_closure(closure);
153 }
154 }
155
156 fn for_closure(&mut self, closure: InternedClosureId<'db>) {
157 let body = match self.db.mir_body_for_closure(closure) {
158 Ok(it) => it,
159 Err(e) => {
160 wln!(self, "// error in {closure:?}: {e:?}");
161 return;
162 }
163 };
164 let result = mem::take(&mut self.result);
165 let indent = mem::take(&mut self.indent);
166 let mut ctx = MirPrettyCtx {
167 body,
168 local_to_binding: body.local_to_binding_map(),
169 result,
170 indent,
171 ..*self
172 };
173 ctx.for_body(|this| wln!(this, "// Closure: {:?}", closure));
174 self.result = ctx.result;
175 self.indent = ctx.indent;
176 }
177
178 fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_, 'db>)) {
179 self.indent += " ";
180 wln!(self, "{{");
181 f(self);
182 for _ in 0..4 {
183 self.result.pop();
184 self.indent.pop();
185 }
186 wln!(self, "}}");
187 }
188
189 fn new(
190 body: &'a MirBody<'db>,
191 hir_body: &'a ExpressionStore,
192 db: &'db dyn HirDatabase,
193 display_target: DisplayTarget,
194 ) -> Self {
195 let local_to_binding = body.local_to_binding_map();
196 MirPrettyCtx {
197 body,
198 db,
199 result: String::new(),
200 indent: String::new(),
201 local_to_binding,
202 hir_body,
203 display_target,
204 }
205 }
206
207 fn write_line(&mut self) {
208 self.result.push('\n');
209 self.result += &self.indent;
210 }
211
212 fn write(&mut self, line: &str) {
213 self.result += line;
214 }
215
216 fn locals(&mut self) {
217 for (id, local) in self.body.locals.iter() {
218 wln!(
219 self,
220 "let {}: {};",
221 self.local_name(id).display_test(self.db, self.display_target),
222 self.hir_display(&local.ty.as_ref())
223 );
224 }
225 }
226
227 fn local_name(&self, local: LocalId) -> LocalName {
228 match self.local_to_binding.get(local) {
229 Some(b) => LocalName::Binding(self.hir_body[*b].name.clone(), local),
230 None => LocalName::Unknown(local),
231 }
232 }
233
234 fn basic_block_id(&self, basic_block_id: BasicBlockId) -> String {
235 format!("'bb{}", u32::from(basic_block_id.into_raw()))
236 }
237
238 fn blocks(&mut self) {
239 for (id, block) in self.body.basic_blocks.iter() {
240 wln!(self);
241 w!(self, "{}: ", self.basic_block_id(id));
242 self.with_block(|this| {
243 for statement in &block.statements {
244 match &statement.kind {
245 StatementKind::Assign(l, r) => {
246 this.place(l);
247 w!(this, " = ");
248 this.rvalue(r);
249 wln!(this, ";");
250 }
251 StatementKind::StorageDead(p) => {
252 wln!(
253 this,
254 "StorageDead({})",
255 this.local_name(*p).display_test(this.db, this.display_target)
256 );
257 }
258 StatementKind::StorageLive(p) => {
259 wln!(
260 this,
261 "StorageLive({})",
262 this.local_name(*p).display_test(this.db, this.display_target)
263 );
264 }
265 StatementKind::Deinit(p) => {
266 w!(this, "Deinit(");
267 this.place(p);
268 wln!(this, ");");
269 }
270 StatementKind::FakeRead(p) => {
271 w!(this, "FakeRead(");
272 this.place(p);
273 wln!(this, ");");
274 }
275 StatementKind::Nop => wln!(this, "Nop;"),
276 }
277 }
278 match &block.terminator {
279 Some(terminator) => match &terminator.kind {
280 TerminatorKind::Goto { target } => {
281 wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
282 }
283 TerminatorKind::SwitchInt { discr, targets } => {
284 w!(this, "switch ");
285 this.operand(discr);
286 w!(this, " ");
287 this.with_block(|this| {
288 for (c, b) in targets.iter() {
289 wln!(this, "{c} => {},", this.basic_block_id(b));
290 }
291 wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
292 });
293 }
294 TerminatorKind::Call { func, args, destination, target, .. } => {
295 w!(this, "Call ");
296 this.with_block(|this| {
297 w!(this, "func: ");
298 this.operand(func);
299 wln!(this, ",");
300 w!(this, "args: [");
301 this.operand_list(args);
302 wln!(this, "],");
303 w!(this, "destination: ");
304 this.place(destination);
305 wln!(this, ",");
306 w!(this, "target: ");
307 match target {
308 Some(t) => w!(this, "{}", this.basic_block_id(*t)),
309 None => w!(this, "<unreachable>"),
310 }
311 wln!(this, ",");
312 });
313 }
314 _ => wln!(this, "{:?};", terminator),
315 },
316 None => wln!(this, "<no-terminator>;"),
317 }
318 })
319 }
320 }
321
322 fn place(&mut self, p: &Place) {
323 fn f<'db>(this: &mut MirPrettyCtx<'_, 'db>, local: LocalId, projections: &[PlaceElem]) {
324 let Some((last, head)) = projections.split_last() else {
325 w!(this, "{}", this.local_name(local).display_test(this.db, this.display_target));
327 return;
328 };
329 match last {
330 ProjectionElem::Deref => {
331 w!(this, "(*");
332 f(this, local, head);
333 w!(this, ")");
334 }
335 ProjectionElem::Downcast(variant_id) => match variant_id {
336 hir_def::VariantId::EnumVariantId(e) => {
337 w!(this, "(");
338 f(this, local, head);
339 let loc = e.lookup(this.db);
340 w!(this, " as {})", loc.name.display(this.db, this.display_target.edition),);
341 }
342 _ => {
343 f(this, local, head);
344 w!(this, ".{:?}", last);
345 }
346 },
347 ProjectionElem::Field(field) => {
348 f(this, local, head);
349
350 let infcx = DbInterner::new_with(this.db, this.body.owner.krate(this.db))
352 .infer_ctxt()
353 .build(rustc_type_ir::TypingMode::PostAnalysis);
354 let env = this.db.trait_environment(this.body.owner.generic_def(this.db));
355 let place_ty = PlaceTy::from_ty(this.body.locals[local].ty.as_ref())
356 .multi_projection_ty(&infcx, env, projections);
357 if let Some(variant_id) = place_ty.variant_id {
358 let variant_fields = variant_id.fields(this.db);
359 w!(
360 this,
361 ".{}",
362 variant_fields.fields()[field.to_local_field_id()]
363 .name
364 .display(this.db, this.display_target.edition)
365 );
366 } else {
367 match place_ty.ty.kind() {
368 TyKind::Adt(adt_def, _) if !adt_def.is_enum() => {
369 let variant_id =
370 VariantId::from_non_enum(adt_def.def_id()).unwrap();
371 let fields = variant_id.fields(this.db);
372 w!(
373 this,
374 ".{}",
375 fields.fields()[field.to_local_field_id()]
376 .name
377 .display(this.db, this.display_target.edition)
378 );
379 }
380 TyKind::Tuple(_) | TyKind::Closure(..) => w!(this, ".{}", field.0),
381 _ => {
382 w!(this, ".{:?}", last);
383 }
384 }
385 };
386 }
387 ProjectionElem::Index(l) => {
388 f(this, local, head);
389 w!(
390 this,
391 "[{}]",
392 this.local_name(*l).display_test(this.db, this.display_target)
393 );
394 }
395 it => {
396 f(this, local, head);
397 w!(this, ".{:?}", it);
398 }
399 }
400 }
401 f(self, p.local, p.projection.lookup());
402 }
403
404 fn operand(&mut self, r: &Operand) {
405 match &r.kind {
406 OperandKind::Copy(p) | OperandKind::Move(p) => {
407 self.place(p);
410 }
411 OperandKind::Constant { konst, .. } => {
412 w!(self, "Const({})", self.hir_display(&konst.as_ref()))
413 }
414 OperandKind::Static(s) => w!(self, "Static({:?})", s),
415 OperandKind::Allocation { allocation } => {
416 w!(self, "Allocation({})", self.hir_display(&allocation.as_ref()))
417 }
418 }
419 }
420
421 fn rvalue(&mut self, r: &Rvalue) {
422 match r {
423 Rvalue::Use(op) => self.operand(op),
424 Rvalue::Ref(r, p) => {
425 match r {
426 BorrowKind::Shared => w!(self, "&"),
427 BorrowKind::Shallow => w!(self, "&shallow "),
428 BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => w!(self, "&uniq "),
429 BorrowKind::Mut {
430 kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
431 } => w!(self, "&mut "),
432 }
433 self.place(p);
434 }
435 Rvalue::Aggregate(AggregateKind::Tuple(_), it) => {
436 w!(self, "(");
437 self.operand_list(it);
438 w!(self, ")");
439 }
440 Rvalue::Aggregate(AggregateKind::Array(_), it) => {
441 w!(self, "[");
442 self.operand_list(it);
443 w!(self, "]");
444 }
445 Rvalue::Repeat(op, len) => {
446 w!(self, "[");
447 self.operand(op);
448 w!(self, "; {}]", len.as_ref().display_test(self.db, self.display_target));
449 }
450 Rvalue::Aggregate(AggregateKind::Adt(_, _), it) => {
451 w!(self, "Adt(");
452 self.operand_list(it);
453 w!(self, ")");
454 }
455 Rvalue::Aggregate(AggregateKind::Closure(_), it) => {
456 w!(self, "Closure(");
457 self.operand_list(it);
458 w!(self, ")");
459 }
460 Rvalue::Aggregate(AggregateKind::Union(_, _), it) => {
461 w!(self, "Union(");
462 self.operand_list(it);
463 w!(self, ")");
464 }
465 Rvalue::Len(p) => {
466 w!(self, "Len(");
467 self.place(p);
468 w!(self, ")");
469 }
470 Rvalue::Cast(ck, op, ty) => {
471 w!(self, "Cast({ck:?}, ");
472 self.operand(op);
473 w!(self, ", {})", self.hir_display(&ty.as_ref()));
474 }
475 Rvalue::CheckedBinaryOp(b, o1, o2) => {
476 self.operand(o1);
477 w!(self, " {b} ");
478 self.operand(o2);
479 }
480 Rvalue::UnaryOp(u, o) => {
481 let u = match u {
482 UnOp::Not => "!",
483 UnOp::Neg => "-",
484 };
485 w!(self, "{u} ");
486 self.operand(o);
487 }
488 Rvalue::Discriminant(p) => {
489 w!(self, "Discriminant(");
490 self.place(p);
491 w!(self, ")");
492 }
493 Rvalue::ShallowInitBoxWithAlloc(_) => w!(self, "ShallowInitBoxWithAlloc"),
494 Rvalue::ShallowInitBox(op, _) => {
495 w!(self, "ShallowInitBox(");
496 self.operand(op);
497 w!(self, ")");
498 }
499 Rvalue::CopyForDeref(p) => {
500 w!(self, "CopyForDeref(");
501 self.place(p);
502 w!(self, ")");
503 }
504 Rvalue::ThreadLocalRef(n)
505 | Rvalue::AddressOf(n)
506 | Rvalue::BinaryOp(n)
507 | Rvalue::NullaryOp(n) => match *n {},
508 }
509 }
510
511 fn operand_list(&mut self, it: &[Operand]) {
512 let mut it = it.iter();
513 if let Some(first) = it.next() {
514 self.operand(first);
515 for op in it {
516 w!(self, ", ");
517 self.operand(op);
518 }
519 }
520 }
521
522 fn hir_display<'b, T: HirDisplay<'db>>(&self, ty: &'b T) -> impl Display + use<'a, 'b, 'db, T>
523 where
524 'db: 'b,
525 {
526 ty.display_test(self.db, self.display_target)
527 .with_closure_style(ClosureStyle::ClosureWithSubst)
528 }
529}