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