charon_lib/pretty/
fmt_with_ctx.rs

1//! Utilities for pretty-printing (u)llbc.
2use crate::{
3    common::{repeat_except_first, TAB_INCR},
4    formatter::*,
5    gast,
6    llbc_ast::{self as llbc, *},
7    reorder_decls::*,
8    ullbc_ast::{self as ullbc, *},
9};
10use either::Either;
11use itertools::Itertools;
12use std::{
13    borrow::Cow,
14    fmt::{self, Debug, Display},
15};
16
17pub struct WithCtx<'a, C, T: ?Sized> {
18    val: &'a T,
19    ctx: &'a C,
20}
21
22impl<'a, C, T: ?Sized> Display for WithCtx<'a, C, T>
23where
24    T: FmtWithCtx<C>,
25{
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        self.val.fmt_with_ctx(self.ctx, f)
28    }
29}
30
31/// Format the AST type as a string.
32pub trait FmtWithCtx<C> {
33    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result;
34
35    /// Returns a struct that implements `Display`. This allows the following:
36    /// ```text
37    ///     println!("{}", self.with_ctx(ctx));
38    /// ```
39    fn with_ctx<'a>(&'a self, ctx: &'a C) -> WithCtx<'a, C, Self> {
40        WithCtx { val: self, ctx }
41    }
42
43    fn to_string_with_ctx(&self, ctx: &C) -> String {
44        self.with_ctx(ctx).to_string()
45    }
46}
47
48macro_rules! impl_display_via_ctx {
49    ($ty:ty) => {
50        impl Display for $ty {
51            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52                self.fmt_with_ctx(&FmtCtx::new(), f)
53            }
54        }
55    };
56}
57macro_rules! impl_debug_via_display {
58    ($ty:ty) => {
59        impl Debug for $ty {
60            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61                <_ as Display>::fmt(self, f)
62            }
63        }
64    };
65}
66
67//------- Impls, sorted by name --------
68
69impl<C: AstFormatter> FmtWithCtx<C> for AbortKind {
70    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        match self {
72            AbortKind::Panic(name) => {
73                write!(f, "panic")?;
74                if let Some(name) = name {
75                    write!(f, "({})", name.with_ctx(ctx))?;
76                }
77                Ok(())
78            }
79            AbortKind::UndefinedBehavior => write!(f, "undefined_behavior"),
80            AbortKind::UnwindTerminate => write!(f, "unwind_terminate"),
81        }
82    }
83}
84
85impl<C: AstFormatter> FmtWithCtx<C> for AnyTransId {
86    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        match ctx
88            .get_crate()
89            .and_then(|translated| translated.item_short_name(*self))
90        {
91            None => write!(f, "{self}"),
92            Some(name) => name.fmt_with_ctx(ctx, f),
93        }
94    }
95}
96
97impl Display for AnyTransId {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
99        let s = match self {
100            AnyTransId::Type(x) => x.to_pretty_string(),
101            AnyTransId::Fun(x) => x.to_pretty_string(),
102            AnyTransId::Global(x) => x.to_pretty_string(),
103            AnyTransId::TraitDecl(x) => x.to_pretty_string(),
104            AnyTransId::TraitImpl(x) => x.to_pretty_string(),
105        };
106        f.write_str(&s)
107    }
108}
109
110impl<C: AstFormatter> FmtWithCtx<C> for AnyTransItem<'_> {
111    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        match self {
113            AnyTransItem::Type(d) => write!(f, "{}", d.with_ctx(ctx)),
114            AnyTransItem::Fun(d) => write!(f, "{}", d.with_ctx(ctx)),
115            AnyTransItem::Global(d) => write!(f, "{}", d.with_ctx(ctx)),
116            AnyTransItem::TraitDecl(d) => write!(f, "{}", d.with_ctx(ctx)),
117            AnyTransItem::TraitImpl(d) => write!(f, "{}", d.with_ctx(ctx)),
118        }
119    }
120}
121
122impl<C: AstFormatter> FmtWithCtx<C> for Assert {
123    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        write!(
125            f,
126            "assert({} == {})",
127            self.cond.with_ctx(ctx),
128            self.expected,
129        )
130    }
131}
132
133impl<T> Binder<T> {
134    /// Format the parameters and contents of this binder and returns the resulting strings. Note:
135    /// this assumes the binder fully replaces the existing generics.
136    fn fmt_split<'a, C>(&'a self, ctx: &'a C) -> (String, String)
137    where
138        C: AstFormatter,
139        T: FmtWithCtx<C::Reborrow<'a>>,
140    {
141        let ctx = &ctx.push_binder(Cow::Borrowed(&self.params));
142        (
143            self.params.fmt_with_ctx_single_line(ctx),
144            self.skip_binder.to_string_with_ctx(ctx),
145        )
146    }
147}
148
149impl Display for BinOp {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
151        match self {
152            BinOp::BitXor => write!(f, "^"),
153            BinOp::BitAnd => write!(f, "&"),
154            BinOp::BitOr => write!(f, "|"),
155            BinOp::Eq => write!(f, "=="),
156            BinOp::Lt => write!(f, "<"),
157            BinOp::Le => write!(f, "<="),
158            BinOp::Ne => write!(f, "!="),
159            BinOp::Ge => write!(f, ">="),
160            BinOp::Gt => write!(f, ">"),
161            BinOp::Div => write!(f, "/"),
162            BinOp::Rem => write!(f, "%"),
163            BinOp::Add => write!(f, "+"),
164            BinOp::Sub => write!(f, "-"),
165            BinOp::Mul => write!(f, "*"),
166            BinOp::WrappingAdd => write!(f, "wrapping.+"),
167            BinOp::WrappingSub => write!(f, "wrapping.-"),
168            BinOp::WrappingMul => write!(f, "wrapping.*"),
169            BinOp::CheckedAdd => write!(f, "checked.+"),
170            BinOp::CheckedSub => write!(f, "checked.-"),
171            BinOp::CheckedMul => write!(f, "checked.*"),
172            BinOp::Shl => write!(f, "<<"),
173            BinOp::Shr => write!(f, ">>"),
174            BinOp::Cmp => write!(f, "cmp"),
175            BinOp::Offset => write!(f, "offset"),
176        }
177    }
178}
179
180impl<C: AstFormatter> FmtWithCtx<C> for llbc::Block {
181    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        for st in &self.statements {
183            writeln!(f, "{}", st.with_ctx(ctx))?;
184        }
185        Ok(())
186    }
187}
188
189impl<C: AstFormatter> FmtWithCtx<C> for ullbc::BlockData {
190    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        for statement in &self.statements {
192            writeln!(f, "{};", statement.with_ctx(ctx))?;
193        }
194        write!(f, "{};", self.terminator.with_ctx(ctx))?;
195        Ok(())
196    }
197}
198
199impl<C: AstFormatter> FmtWithCtx<C> for gast::Body {
200    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        match self {
202            Body::Unstructured(b) => write!(f, "{}", b.with_ctx(ctx)),
203            Body::Structured(b) => write!(f, "{}", b.with_ctx(ctx)),
204        }
205    }
206}
207
208impl Display for BorrowKind {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
210        // Reuse the derived `Debug` impl to get the variant name.
211        write!(f, "{self:?}")
212    }
213}
214
215impl Display for BuiltinFunId {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
217        let name = match *self {
218            BuiltinFunId::BoxNew => "BoxNew",
219            BuiltinFunId::ArrayToSliceShared => "ArrayToSliceShared",
220            BuiltinFunId::ArrayToSliceMut => "ArrayToSliceMut",
221            BuiltinFunId::ArrayRepeat => "ArrayRepeat",
222            BuiltinFunId::Index(BuiltinIndexOp {
223                is_array,
224                mutability,
225                is_range,
226            }) => {
227                let ty = if is_array { "Array" } else { "Slice" };
228                let op = if is_range { "SubSlice" } else { "Index" };
229                let mutability = mutability.variant_name();
230                &format!("{ty}{op}{mutability}")
231            }
232            BuiltinFunId::PtrFromParts(mutability) => {
233                let mutability = mutability.variant_name();
234                &format!("PtrFromParts{mutability}")
235            }
236        };
237        f.write_str(name)
238    }
239}
240
241impl<C: AstFormatter> FmtWithCtx<C> for Call {
242    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        let dest = self.dest.with_ctx(ctx);
244        let func = self.func.with_ctx(ctx);
245        let args = self.args.iter().map(|x| x.with_ctx(ctx)).format(", ");
246        write!(f, "{dest} := {func}({args})")
247    }
248}
249
250impl<C: AstFormatter> FmtWithCtx<C> for CastKind {
251    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252        match self {
253            CastKind::Scalar(src, tgt) => write!(f, "cast<{src}, {tgt}>"),
254            CastKind::FnPtr(src, tgt) | CastKind::RawPtr(src, tgt) => {
255                write!(f, "cast<{}, {}>", src.with_ctx(ctx), tgt.with_ctx(ctx))
256            }
257            CastKind::Unsize(src, tgt) => {
258                write!(
259                    f,
260                    "unsize_cast<{}, {}>",
261                    src.with_ctx(ctx),
262                    tgt.with_ctx(ctx)
263                )
264            }
265            CastKind::Transmute(src, tgt) => {
266                write!(f, "transmute<{}, {}>", src.with_ctx(ctx), tgt.with_ctx(ctx))
267            }
268        }
269    }
270}
271
272impl<C: AstFormatter> FmtWithCtx<C> for ClauseDbVar {
273    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        ctx.format_bound_var(f, *self, "@TraitClause", |_| None)
275    }
276}
277
278impl_display_via_ctx!(ConstantExpr);
279impl<C: AstFormatter> FmtWithCtx<C> for ConstantExpr {
280    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281        write!(f, "{}", self.value.with_ctx(ctx))
282    }
283}
284
285impl<C: AstFormatter> FmtWithCtx<C> for ConstGeneric {
286    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        match self {
288            ConstGeneric::Var(id) => write!(f, "{}", id.with_ctx(ctx)),
289            ConstGeneric::Value(v) => write!(f, "{v}"),
290            ConstGeneric::Global(id) => write!(f, "{}", id.with_ctx(ctx)),
291        }
292    }
293}
294
295impl<C: AstFormatter> FmtWithCtx<C> for ConstGenericDbVar {
296    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297        ctx.format_bound_var(f, *self, "@ConstGeneric", |v| Some(v.to_string()))
298    }
299}
300
301impl_display_via_ctx!(ConstGenericVar);
302impl<C: AstFormatter> FmtWithCtx<C> for ConstGenericVar {
303    fn fmt_with_ctx(&self, _ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        write!(f, "const {} : {}", self.name, self.ty)
305    }
306}
307
308impl Display for DeBruijnId {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
310        write!(f, "{}", self.index)
311    }
312}
313
314impl<Id: Display> Display for DeBruijnVar<Id> {
315    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316        match self {
317            Self::Bound(dbid, varid) => write!(f, "{dbid}_{varid}"),
318            Self::Free(varid) => write!(f, "{varid}"),
319        }
320    }
321}
322
323impl<C: AstFormatter> FmtWithCtx<C> for DeclarationGroup {
324    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325        use DeclarationGroup::*;
326        match self {
327            Type(g) => write!(f, "Type decls group: {}", g.with_ctx(ctx)),
328            Fun(g) => write!(f, "Fun decls group: {}", g.with_ctx(ctx)),
329            Global(g) => write!(f, "Global decls group: {}", g.with_ctx(ctx)),
330            TraitDecl(g) => write!(f, "Trait decls group: {}", g.with_ctx(ctx)),
331            TraitImpl(g) => write!(f, "Trait impls group: {}", g.with_ctx(ctx)),
332            Mixed(g) => write!(f, "Mixed group: {}", g.with_ctx(ctx)),
333        }
334    }
335}
336
337impl<C: AstFormatter> FmtWithCtx<C> for ExistentialPredicate {
338    fn fmt_with_ctx(&self, _ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        write!(f, "exists(TODO)")
340    }
341}
342
343impl_display_via_ctx!(Field);
344impl<C: AstFormatter> FmtWithCtx<C> for Field {
345    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346        if let Some(name) = &self.name {
347            write!(f, "{}: ", name)?
348        }
349        write!(f, "{}", self.ty.with_ctx(ctx))
350    }
351}
352
353impl Display for FileName {
354    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
355        match self {
356            FileName::Virtual(path_buf) | FileName::Local(path_buf) => {
357                write!(f, "{}", path_buf.display())
358            }
359            FileName::NotReal(name) => write!(f, "{}", name),
360        }
361    }
362}
363
364impl Display for FloatTy {
365    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
366        match self {
367            FloatTy::F16 => write!(f, "f16"),
368            FloatTy::F32 => write!(f, "f32"),
369            FloatTy::F64 => write!(f, "f64"),
370            FloatTy::F128 => write!(f, "f128"),
371        }
372    }
373}
374
375impl Display for FloatValue {
376    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
377        let v = &self.value;
378        match self.ty {
379            FloatTy::F16 => write!(f, "{v} : f16"),
380            FloatTy::F32 => write!(f, "{v} : f32"),
381            FloatTy::F64 => write!(f, "{v} : f64"),
382            FloatTy::F128 => write!(f, "{v} : f128"),
383        }
384    }
385}
386
387impl<C: AstFormatter> FmtWithCtx<C> for FnOperand {
388    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389        match self {
390            FnOperand::Regular(func) => write!(f, "{}", func.with_ctx(ctx)),
391            FnOperand::Move(p) => write!(f, "(move {})", p.with_ctx(ctx)),
392        }
393    }
394}
395
396impl<C: AstFormatter> FmtWithCtx<C> for FnPtr {
397    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398        match self.func.as_ref() {
399            FunIdOrTraitMethodRef::Fun(FunId::Regular(def_id)) => {
400                write!(f, "{}", def_id.with_ctx(ctx))?
401            }
402            FunIdOrTraitMethodRef::Fun(FunId::Builtin(builtin)) => write!(f, "@{}", builtin)?,
403            FunIdOrTraitMethodRef::Trait(trait_ref, method_id, _) => {
404                write!(f, "{}::{}", trait_ref.with_ctx(ctx), &method_id.0)?
405            }
406        };
407        write!(f, "{}", self.generics.with_ctx(ctx))
408    }
409}
410
411impl<C: AstFormatter> FmtWithCtx<C> for FunDecl {
412    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413        let keyword = if self.signature.is_unsafe {
414            "unsafe fn"
415        } else {
416            "fn"
417        };
418        self.item_meta
419            .fmt_item_intro(f, ctx, keyword, self.def_id)?;
420
421        // Update the context
422        let ctx = &ctx.set_generics(&self.signature.generics);
423
424        // Generic parameters
425        let (params, preds) = self.signature.generics.fmt_with_ctx_with_trait_clauses(ctx);
426        write!(f, "{params}")?;
427
428        // Arguments
429        let mut args: Vec<String> = Vec::new();
430        for (i, ty) in self.signature.inputs.iter().enumerate() {
431            // The input variables start at index 1
432            // TODO: use the locals to get the variable names
433            let id = LocalId::new(i + 1);
434            args.push(format!("{}: {}", id.to_pretty_string(), ty.with_ctx(ctx)));
435        }
436        let args = args.join(", ");
437        write!(f, "({args})")?;
438
439        // Return type
440        if !self.signature.output.is_unit() {
441            write!(f, " -> {}", self.signature.output.with_ctx(ctx))?;
442        };
443        write!(f, "{preds}")?;
444
445        // Body
446        match &self.body {
447            Ok(body) => {
448                let tab = ctx.indent();
449                let body = body.with_ctx(ctx);
450                write!(f, "\n{tab}{{\n{body}{tab}}}")?;
451            }
452            Err(Opaque) => {}
453        }
454
455        Ok(())
456    }
457}
458
459impl<C: AstFormatter> FmtWithCtx<C> for FunDeclId {
460    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461        AnyTransId::from(*self).fmt_with_ctx(ctx, f)
462    }
463}
464
465impl<C: AstFormatter> FmtWithCtx<C> for FunDeclRef {
466    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467        let id = self.id.with_ctx(ctx);
468        let generics = self.generics.with_ctx(ctx);
469        write!(f, "{id}{generics}")
470    }
471}
472
473impl<C: AstFormatter> FmtWithCtx<C> for FunSig {
474    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475        let ctx = &ctx.set_generics(&self.generics);
476
477        // Unsafe keyword
478        if self.is_unsafe {
479            write!(f, "unsafe ")?;
480        }
481
482        // Generic parameters
483        let (params, clauses) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
484        write!(f, "fn{params}")?;
485
486        // Arguments
487        let args = self
488            .inputs
489            .iter()
490            .map(|ty| ty.with_ctx(ctx).to_string())
491            .format(", ");
492        write!(f, "({args})")?;
493
494        // Return type
495        if !self.output.is_unit() {
496            write!(f, " -> {}", self.output.with_ctx(ctx))?;
497        }
498
499        write!(f, "{clauses}")?;
500        Ok(())
501    }
502}
503
504impl<Id: Copy, C: AstFormatter> FmtWithCtx<C> for GDeclarationGroup<Id>
505where
506    Id: FmtWithCtx<C>,
507{
508    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
509        use GDeclarationGroup::*;
510        match self {
511            NonRec(id) => write!(f, "Non rec: {}", id.with_ctx(ctx)),
512            Rec(ids) => {
513                let ids = ids.iter().map(|id| id.with_ctx(ctx)).format(", ");
514                write!(f, "Rec: {}", ids)
515            }
516        }
517    }
518}
519
520impl GenericArgs {
521    pub(crate) fn fmt_explicits<'a, C: AstFormatter>(
522        &'a self,
523        ctx: &'a C,
524    ) -> impl Iterator<Item = impl Display + 'a> {
525        let regions = self.regions.iter().map(|x| x.with_ctx(ctx));
526        let types = self.types.iter().map(|x| x.with_ctx(ctx));
527        let const_generics = self.const_generics.iter().map(|x| x.with_ctx(ctx));
528        regions.map(Either::Left).chain(
529            types
530                .map(Either::Left)
531                .chain(const_generics.map(Either::Right))
532                .map(Either::Right),
533        )
534    }
535
536    pub(crate) fn fmt_implicits<'a, C: AstFormatter>(
537        &'a self,
538        ctx: &'a C,
539    ) -> impl Iterator<Item = impl Display + 'a> {
540        self.trait_refs.iter().map(|x| x.with_ctx(ctx))
541    }
542}
543
544impl_display_via_ctx!(GenericArgs);
545impl_debug_via_display!(GenericArgs);
546impl<C: AstFormatter> FmtWithCtx<C> for GenericArgs {
547    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
548        if self.has_explicits() {
549            write!(f, "<{}>", self.fmt_explicits(ctx).format(", "))?;
550        }
551        if self.has_implicits() {
552            write!(f, "[{}]", self.fmt_implicits(ctx).format(", "))?;
553        }
554        Ok(())
555    }
556}
557
558impl GenericParams {
559    fn formatted_params<'a, C>(&'a self, ctx: &'a C) -> impl Iterator<Item = impl Display + 'a>
560    where
561        C: AstFormatter,
562    {
563        let regions = self.regions.iter().map(|x| x.with_ctx(ctx));
564        let types = self.types.iter();
565        let const_generics = self.const_generics.iter();
566        regions.map(Either::Left).chain(
567            types
568                .map(Either::Left)
569                .chain(const_generics.map(Either::Right))
570                .map(Either::Right),
571        )
572    }
573
574    fn formatted_clauses<'a, C>(&'a self, ctx: &'a C) -> impl Iterator<Item = impl Display + 'a>
575    where
576        C: AstFormatter,
577    {
578        let trait_clauses = self.trait_clauses.iter().map(|x| x.to_string_with_ctx(ctx));
579        let types_outlive = self.types_outlive.iter().map(|x| x.fmt_as_for(ctx));
580        let regions_outlive = self.regions_outlive.iter().map(|x| x.fmt_as_for(ctx));
581        let type_constraints = self
582            .trait_type_constraints
583            .iter()
584            .map(|x| x.fmt_as_for(ctx));
585        trait_clauses.map(Either::Left).chain(
586            types_outlive
587                .chain(regions_outlive)
588                .chain(type_constraints)
589                .map(Either::Right),
590        )
591    }
592
593    pub fn fmt_with_ctx_with_trait_clauses<C>(&self, ctx: &C) -> (String, String)
594    where
595        C: AstFormatter,
596    {
597        let tab = ctx.indent();
598        let params = if self.has_explicits() {
599            let params = self.formatted_params(ctx).format(", ");
600            format!("<{}>", params)
601        } else {
602            String::new()
603        };
604        let clauses = if self.has_predicates() {
605            let clauses = self
606                .formatted_clauses(ctx)
607                .map(|x| format!("\n{tab}{TAB_INCR}{x},"))
608                .format("");
609            format!("\n{tab}where{clauses}")
610        } else {
611            String::new()
612        };
613        (params, clauses)
614    }
615
616    pub fn fmt_with_ctx_single_line<C>(&self, ctx: &C) -> String
617    where
618        C: AstFormatter,
619    {
620        if self.is_empty() {
621            String::new()
622        } else {
623            let params = self
624                .formatted_params(ctx)
625                .map(Either::Left)
626                .chain(self.formatted_clauses(ctx).map(Either::Right))
627                .format(", ");
628            format!("<{}>", params)
629        }
630    }
631}
632
633impl_debug_via_display!(GenericParams);
634impl Display for GenericParams {
635    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
636        write!(f, "{}", self.fmt_with_ctx_single_line(&FmtCtx::new()))
637    }
638}
639
640impl<C: AstFormatter> FmtWithCtx<C> for GenericsSource {
641    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
642        match self {
643            GenericsSource::Item(id) => write!(f, "{}", id.with_ctx(ctx)),
644            GenericsSource::Method(id, name) => write!(f, "{}::{name}", id.with_ctx(ctx)),
645            GenericsSource::Builtin => write!(f, "<builtin>"),
646            GenericsSource::Other => write!(f, "<unknown>"),
647        }
648    }
649}
650
651impl<T> GExprBody<T> {
652    fn fmt_with_ctx_and_callback<C: AstFormatter>(
653        &self,
654        ctx: &C,
655        f: &mut fmt::Formatter<'_>,
656        fmt_body: impl for<'a, 'b> FnOnce(
657            &mut fmt::Formatter<'_>,
658            &<C::Reborrow<'b> as AstFormatter>::Reborrow<'a>,
659            &'a T,
660        ) -> fmt::Result,
661    ) -> fmt::Result {
662        // Update the context
663        let ctx = &ctx.set_locals(&self.locals);
664        let ctx = &ctx.increase_indent();
665        let tab = ctx.indent();
666
667        // Format the local variables
668        for v in &self.locals.locals {
669            write!(f, "{tab}")?;
670            write!(f, "let {v}: {};", v.ty.with_ctx(ctx))?;
671
672            write!(f, " // ")?;
673            let index = v.index.index();
674            if index == 0 {
675                write!(f, "return")?;
676            } else if index <= self.locals.arg_count {
677                write!(f, "arg #{index}")?
678            } else {
679                match &v.name {
680                    Some(_) => write!(f, "local")?,
681                    None => write!(f, "anonymous local")?,
682                }
683            }
684            writeln!(f)?;
685        }
686
687        fmt_body(f, ctx, &self.body)?;
688
689        Ok(())
690    }
691}
692
693impl<C: AstFormatter> FmtWithCtx<C> for GExprBody<llbc_ast::Block> {
694    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695        self.fmt_with_ctx_and_callback(ctx, f, |f, ctx, body| {
696            writeln!(f)?;
697            body.fmt_with_ctx(ctx, f)?;
698            Ok(())
699        })
700    }
701}
702impl<C: AstFormatter> FmtWithCtx<C> for GExprBody<ullbc_ast::BodyContents> {
703    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
704        self.fmt_with_ctx_and_callback(ctx, f, |f, ctx, body| {
705            let tab = ctx.indent();
706            let ctx = &ctx.increase_indent();
707            for (bid, block) in body.iter_indexed_values() {
708                writeln!(f)?;
709                writeln!(f, "{tab}bb{}: {{", bid.index())?;
710                writeln!(f, "{}", block.with_ctx(ctx))?;
711                writeln!(f, "{tab}}}")?;
712            }
713            Ok(())
714        })
715    }
716}
717
718impl<C> FmtWithCtx<C> for GlobalDecl
719where
720    C: AstFormatter,
721{
722    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
723        let keyword = match self.global_kind {
724            GlobalKind::Static => "static",
725            GlobalKind::AnonConst | GlobalKind::NamedConst => "const",
726        };
727        self.item_meta
728            .fmt_item_intro(f, ctx, keyword, self.def_id)?;
729
730        // Update the context with the generics
731        let ctx = &ctx.set_generics(&self.generics);
732
733        // Translate the parameters and the trait clauses
734        let (params, preds) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
735
736        // Type
737        let ty = self.ty.with_ctx(ctx);
738        write!(f, "{params}: {ty}")?;
739
740        // Predicates
741        write!(f, "{preds}")?;
742        if self.generics.has_predicates() {
743            write!(f, "\n")?;
744        }
745        write!(f, " ")?;
746
747        // Decl name
748        let initializer = self.init.with_ctx(ctx);
749        write!(f, "= {initializer}()")?;
750
751        Ok(())
752    }
753}
754
755impl<C: AstFormatter> FmtWithCtx<C> for GlobalDeclId {
756    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
757        AnyTransId::from(*self).fmt_with_ctx(ctx, f)
758    }
759}
760
761impl<C: AstFormatter> FmtWithCtx<C> for GlobalDeclRef {
762    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
763        let id = self.id.with_ctx(ctx);
764        let generics = self.generics.with_ctx(ctx);
765        write!(f, "{id}{generics}")
766    }
767}
768
769impl<C: AstFormatter> FmtWithCtx<C> for ImplElem {
770    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
771        write!(f, "{{")?;
772        match self {
773            ImplElem::Ty(bound_ty) => {
774                // Just printing the generics (not the predicates)
775                let ctx = ctx.set_generics(&bound_ty.params);
776                bound_ty.skip_binder.fmt_with_ctx(&ctx, f)?
777            }
778            ImplElem::Trait(impl_id) => {
779                match ctx.get_crate().and_then(|tr| tr.trait_impls.get(*impl_id)) {
780                    None => write!(f, "impl#{impl_id}")?,
781                    Some(timpl) => {
782                        // We need to put the first type parameter aside: it is the type for which
783                        // we implement the trait.
784                        let ctx = &ctx.set_generics(&timpl.generics);
785                        let mut impl_trait = timpl.impl_trait.clone();
786                        let self_ty = impl_trait.generics.types.remove(TypeVarId::ZERO).unwrap();
787                        write!(
788                            f,
789                            "impl {} for {}",
790                            impl_trait.with_ctx(ctx),
791                            self_ty.with_ctx(ctx)
792                        )?;
793                    }
794                }
795            }
796        }
797        write!(f, "}}")
798    }
799}
800
801impl Display for IntegerTy {
802    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
803        match self {
804            IntegerTy::Isize => write!(f, "isize"),
805            IntegerTy::I8 => write!(f, "i8"),
806            IntegerTy::I16 => write!(f, "i16"),
807            IntegerTy::I32 => write!(f, "i32"),
808            IntegerTy::I64 => write!(f, "i64"),
809            IntegerTy::I128 => write!(f, "i128"),
810            IntegerTy::Usize => write!(f, "usize"),
811            IntegerTy::U8 => write!(f, "u8"),
812            IntegerTy::U16 => write!(f, "u16"),
813            IntegerTy::U32 => write!(f, "u32"),
814            IntegerTy::U64 => write!(f, "u64"),
815            IntegerTy::U128 => write!(f, "u128"),
816        }
817    }
818}
819
820impl ItemMeta {
821    /// Format the start of an item definition, up to the name.
822    pub fn fmt_item_intro<C: AstFormatter>(
823        &self,
824        f: &mut fmt::Formatter<'_>,
825        ctx: &C,
826        keyword: &str,
827        id: impl Into<AnyTransId>,
828    ) -> fmt::Result {
829        let tab = ctx.indent();
830        let full_name = self.name.with_ctx(ctx);
831        let name = if let Some(tr) = ctx.get_crate()
832            && let Some(short_name) = tr.short_names.get(&id.into())
833        {
834            writeln!(f, "// Full name: {full_name}")?;
835            short_name.with_ctx(ctx)
836        } else {
837            full_name
838        };
839        if let Some(id) = &self.lang_item {
840            writeln!(f, "{tab}#[lang_item(\"{id}\")]")?;
841        }
842        write!(f, "{tab}")?;
843        if self.attr_info.public {
844            write!(f, "pub ")?;
845        }
846        write!(f, "{keyword} {name}")
847    }
848}
849
850impl Display for Literal {
851    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
852        match self {
853            Literal::Scalar(v) => write!(f, "{v}"),
854            Literal::Float(v) => write!(f, "{v}"),
855            Literal::Bool(v) => write!(f, "{v}"),
856            Literal::Char(v) => write!(f, "{v}"),
857            Literal::Str(v) => write!(f, "\"{}\"", v.replace("\\", "\\\\").replace("\n", "\\n")),
858            Literal::ByteStr(v) => write!(f, "{v:?}"),
859        }
860    }
861}
862
863impl Display for LiteralTy {
864    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
865        match self {
866            LiteralTy::Integer(ty) => write!(f, "{ty}"),
867            LiteralTy::Float(ty) => write!(f, "{ty}"),
868            LiteralTy::Char => write!(f, "char"),
869            LiteralTy::Bool => write!(f, "bool"),
870        }
871    }
872}
873
874impl Display for Loc {
875    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
876        write!(f, "{}:{}", self.line, self.col)
877    }
878}
879
880impl Display for Local {
881    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
882        // We display both the variable name and its id because some
883        // variables may have the same name (in different scopes)
884        if let Some(name) = &self.name {
885            write!(f, "{name}")?
886        }
887        write!(f, "{}", self.index.to_pretty_string())?;
888        Ok(())
889    }
890}
891
892impl<C: AstFormatter> FmtWithCtx<C> for LocalId {
893    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
894        ctx.format_local_id(f, *self)
895    }
896}
897
898impl<C: AstFormatter> FmtWithCtx<C> for Name {
899    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
900        let name = self.name.iter().map(|x| x.with_ctx(ctx)).format("::");
901        write!(f, "{}", name)
902    }
903}
904
905impl<C: AstFormatter> FmtWithCtx<C> for NullOp {
906    fn fmt_with_ctx(&self, _ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
907        let op = match self {
908            NullOp::SizeOf => "size_of",
909            NullOp::AlignOf => "align_of",
910            NullOp::OffsetOf(_) => "offset_of(?)",
911            NullOp::UbChecks => "ub_checks",
912        };
913        write!(f, "{op}")
914    }
915}
916
917impl_display_via_ctx!(Operand);
918impl<C: AstFormatter> FmtWithCtx<C> for Operand {
919    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
920        match self {
921            Operand::Copy(p) => write!(f, "copy ({})", p.with_ctx(ctx)),
922            Operand::Move(p) => write!(f, "move ({})", p.with_ctx(ctx)),
923            Operand::Const(c) => write!(f, "const ({})", c.with_ctx(ctx)),
924        }
925    }
926}
927
928impl<C: AstFormatter, T, U> FmtWithCtx<C> for OutlivesPred<T, U>
929where
930    T: FmtWithCtx<C>,
931    U: FmtWithCtx<C>,
932{
933    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
934        write!(f, "{} : {}", self.0.with_ctx(ctx), self.1.with_ctx(ctx))
935    }
936}
937
938impl<C: AstFormatter> FmtWithCtx<C> for PathElem {
939    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
940        match self {
941            PathElem::Ident(s, d) => {
942                write!(f, "{s}")?;
943                if !d.is_zero() {
944                    write!(f, "#{}", d)?;
945                }
946                Ok(())
947            }
948            PathElem::Impl(impl_elem, d) => {
949                write!(f, "{}", impl_elem.with_ctx(ctx))?;
950                if !d.is_zero() {
951                    write!(f, "#{}", d)?;
952                }
953                Ok(())
954            }
955            PathElem::Monomorphized(args) => {
956                write!(f, "<{}>", args.fmt_explicits(ctx).format(", "))
957            }
958        }
959    }
960}
961
962impl_display_via_ctx!(Place);
963impl<C: AstFormatter> FmtWithCtx<C> for Place {
964    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
965        match &self.kind {
966            PlaceKind::Local(var_id) => write!(f, "{}", var_id.with_ctx(ctx)),
967            PlaceKind::Projection(subplace, projection) => {
968                let sub = subplace.with_ctx(ctx);
969                match projection {
970                    ProjectionElem::Deref => {
971                        write!(f, "*({sub})")
972                    }
973                    ProjectionElem::Field(proj_kind, field_id) => match proj_kind {
974                        FieldProjKind::Adt(adt_id, opt_variant_id) => {
975                            write!(f, "({sub}")?;
976                            if let Some(variant_id) = opt_variant_id {
977                                write!(f, " as variant @{variant_id}")?
978                            }
979                            write!(f, ").")?;
980                            ctx.format_field_name(f, *adt_id, *opt_variant_id, *field_id)?;
981                            Ok(())
982                        }
983                        FieldProjKind::Tuple(_) => {
984                            write!(f, "({sub}).{field_id}")
985                        }
986                    },
987                    ProjectionElem::Index {
988                        offset,
989                        from_end: true,
990                        ..
991                    } => write!(f, "({sub})[-{}]", offset.with_ctx(ctx)),
992                    ProjectionElem::Index {
993                        offset,
994                        from_end: false,
995                        ..
996                    } => write!(f, "({sub})[{}]", offset.with_ctx(ctx)),
997                    ProjectionElem::Subslice {
998                        from,
999                        to,
1000                        from_end: true,
1001                        ..
1002                    } => write!(f, "({sub})[{}..-{}]", from.with_ctx(ctx), to.with_ctx(ctx)),
1003                    ProjectionElem::Subslice {
1004                        from,
1005                        to,
1006                        from_end: false,
1007                        ..
1008                    } => write!(f, "({sub})[{}..{}]", from.with_ctx(ctx), to.with_ctx(ctx)),
1009                }
1010            }
1011        }
1012    }
1013}
1014
1015impl<C: AstFormatter> FmtWithCtx<C> for PolyTraitDeclRef {
1016    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1017        write!(f, "{}", self.fmt_as_for(ctx))
1018    }
1019}
1020
1021impl Display for RawAttribute {
1022    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1023        write!(f, "{}", self.path)?;
1024        if let Some(args) = &self.args {
1025            write!(f, "({args})")?;
1026        }
1027        Ok(())
1028    }
1029}
1030
1031impl<C: AstFormatter> FmtWithCtx<C> for RawConstantExpr {
1032    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1033        match self {
1034            RawConstantExpr::Literal(c) => write!(f, "{}", c.to_string()),
1035            RawConstantExpr::Adt(variant_id, values) => {
1036                // It is a bit annoying: in order to properly format the value,
1037                // we need the type (which contains the type def id).
1038                // Anyway, the printing utilities are mostly for debugging.
1039                let variant_id = match variant_id {
1040                    Some(id) => format!("Some({id})"),
1041                    None => "None".to_string(),
1042                };
1043                let values = values.iter().map(|v| v.with_ctx(ctx)).format(", ");
1044                write!(f, "ConstAdt {} [{}]", variant_id, values)
1045            }
1046            RawConstantExpr::Array(values) => {
1047                let values = values.iter().map(|v| v.with_ctx(ctx)).format(", ");
1048                write!(f, "[{}]", values)
1049            }
1050            RawConstantExpr::Global(global_ref) => {
1051                write!(f, "{}", global_ref.with_ctx(ctx))
1052            }
1053            RawConstantExpr::TraitConst(trait_ref, name) => {
1054                write!(f, "{}::{name}", trait_ref.with_ctx(ctx),)
1055            }
1056            RawConstantExpr::Ref(cv) => {
1057                write!(f, "&{}", cv.with_ctx(ctx))
1058            }
1059            RawConstantExpr::MutPtr(cv) => {
1060                write!(f, "&raw mut {}", cv.with_ctx(ctx))
1061            }
1062            RawConstantExpr::Var(id) => write!(f, "{}", id.with_ctx(ctx)),
1063            RawConstantExpr::FnPtr(fp) => {
1064                write!(f, "{}", fp.with_ctx(ctx))
1065            }
1066            RawConstantExpr::RawMemory(bytes) => write!(f, "RawMemory({bytes:?})"),
1067            RawConstantExpr::Opaque(cause) => write!(f, "Opaque({cause})"),
1068        }
1069    }
1070}
1071
1072impl<C: AstFormatter> FmtWithCtx<C> for Region {
1073    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1074        match self {
1075            Region::Static => write!(f, "'static"),
1076            Region::Var(var) => write!(f, "{}", var.with_ctx(ctx)),
1077            Region::Erased => write!(f, "'_"),
1078        }
1079    }
1080}
1081
1082impl<T> RegionBinder<T> {
1083    /// Format the parameters and contents of this binder and returns the resulting strings.
1084    fn fmt_split<'a, C>(&'a self, ctx: &'a C) -> (String, String)
1085    where
1086        C: AstFormatter,
1087        T: FmtWithCtx<C::Reborrow<'a>>,
1088    {
1089        let ctx = &ctx.push_bound_regions(&self.regions);
1090        (
1091            self.regions
1092                .iter()
1093                .map(|r| r.with_ctx(ctx))
1094                .format(", ")
1095                .to_string(),
1096            self.skip_binder.to_string_with_ctx(ctx),
1097        )
1098    }
1099
1100    /// Formats the binder as `for<params> value`.
1101    fn fmt_as_for<'a, C>(&'a self, ctx: &'a C) -> String
1102    where
1103        C: AstFormatter,
1104        T: FmtWithCtx<C::Reborrow<'a>>,
1105    {
1106        let (regions, value) = self.fmt_split(ctx);
1107        let regions = if regions.is_empty() {
1108            "".to_string()
1109        } else {
1110            format!("for<{regions}> ",)
1111        };
1112        format!("{regions}{value}",)
1113    }
1114}
1115
1116impl<C: AstFormatter> FmtWithCtx<C> for RegionDbVar {
1117    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1118        ctx.format_bound_var(f, *self, "'_", |v| {
1119            v.name.as_ref().map(|name| name.to_string())
1120        })
1121    }
1122}
1123
1124impl_display_via_ctx!(RegionVar);
1125impl<C: AstFormatter> FmtWithCtx<C> for RegionVar {
1126    fn fmt_with_ctx(&self, _ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1127        match &self.name {
1128            Some(name) => write!(f, "{name}"),
1129            None => write!(f, "'_{}", self.index),
1130        }
1131    }
1132}
1133
1134impl_display_via_ctx!(Rvalue);
1135impl<C: AstFormatter> FmtWithCtx<C> for Rvalue {
1136    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1137        match self {
1138            Rvalue::Use(x) => write!(f, "{}", x.with_ctx(ctx)),
1139            Rvalue::Ref(place, borrow_kind) => {
1140                let borrow_kind = match borrow_kind {
1141                    BorrowKind::Shared => "&",
1142                    BorrowKind::Mut => "&mut ",
1143                    BorrowKind::TwoPhaseMut => "&two-phase-mut ",
1144                    BorrowKind::UniqueImmutable => "&uniq ",
1145                    BorrowKind::Shallow => "&shallow ",
1146                };
1147                write!(f, "{borrow_kind}{}", place.with_ctx(ctx))
1148            }
1149            Rvalue::RawPtr(place, mutability) => {
1150                let ptr_kind = match mutability {
1151                    RefKind::Shared => "&raw const ",
1152                    RefKind::Mut => "&raw mut ",
1153                };
1154                write!(f, "{ptr_kind}{}", place.with_ctx(ctx))
1155            }
1156
1157            Rvalue::BinaryOp(binop, x, y) => {
1158                write!(f, "{} {} {}", x.with_ctx(ctx), binop, y.with_ctx(ctx))
1159            }
1160            Rvalue::UnaryOp(unop, x) => {
1161                write!(f, "{}({})", unop.with_ctx(ctx), x.with_ctx(ctx))
1162            }
1163            Rvalue::NullaryOp(op, ty) => {
1164                write!(f, "{}<{}>", op.with_ctx(ctx), ty.with_ctx(ctx))
1165            }
1166            Rvalue::Discriminant(p, _) => {
1167                write!(f, "@discriminant({})", p.with_ctx(ctx),)
1168            }
1169            Rvalue::Aggregate(kind, ops) => {
1170                let ops_s = ops.iter().map(|op| op.with_ctx(ctx)).format(", ");
1171                match kind {
1172                    AggregateKind::Adt(def_id, variant_id, field_id, _) => {
1173                        match def_id {
1174                            TypeId::Tuple => write!(f, "({})", ops_s),
1175                            TypeId::Builtin(_) => unreachable!(),
1176                            TypeId::Adt(def_id) => {
1177                                match variant_id {
1178                                    None => def_id.fmt_with_ctx(ctx, f)?,
1179                                    Some(variant_id) => {
1180                                        ctx.format_enum_variant(f, *def_id, *variant_id)?
1181                                    }
1182                                }
1183                                write!(f, " {{ ")?;
1184                                for (comma, (i, op)) in
1185                                    repeat_except_first(", ").zip(ops.iter().enumerate())
1186                                {
1187                                    write!(f, "{}", comma.unwrap_or_default())?;
1188                                    let field_id = match *field_id {
1189                                        None => FieldId::new(i),
1190                                        Some(field_id) => {
1191                                            assert_eq!(i, 0); // there should be only one operand
1192                                            field_id
1193                                        }
1194                                    };
1195                                    ctx.format_field_name(f, *def_id, *variant_id, field_id)?;
1196                                    write!(f, ": {}", op.with_ctx(ctx))?;
1197                                }
1198                                write!(f, " }}")
1199                            }
1200                        }
1201                    }
1202                    AggregateKind::Array(..) => {
1203                        write!(f, "[{}]", ops_s)
1204                    }
1205                    AggregateKind::RawPtr(_, rmut) => {
1206                        let mutability = match rmut {
1207                            RefKind::Shared => "const",
1208                            RefKind::Mut => "mut ",
1209                        };
1210                        write!(f, "*{} ({})", mutability, ops_s)
1211                    }
1212                }
1213            }
1214            Rvalue::Global(global_ref) => write!(f, "{}", global_ref.with_ctx(ctx)),
1215            Rvalue::GlobalRef(global_ref, RefKind::Shared) => {
1216                write!(f, "&{}", global_ref.with_ctx(ctx))
1217            }
1218            Rvalue::GlobalRef(global_ref, RefKind::Mut) => {
1219                write!(f, "&raw mut {}", global_ref.with_ctx(ctx))
1220            }
1221            Rvalue::Len(place, ..) => write!(f, "len({})", place.with_ctx(ctx)),
1222            Rvalue::Repeat(op, _ty, cg) => {
1223                write!(f, "[{}; {}]", op.with_ctx(ctx), cg.with_ctx(ctx))
1224            }
1225            Rvalue::ShallowInitBox(op, ty) => {
1226                write!(
1227                    f,
1228                    "shallow_init_box::<{}>({})",
1229                    ty.with_ctx(ctx),
1230                    op.with_ctx(ctx)
1231                )
1232            }
1233        }
1234    }
1235}
1236
1237impl Display for ScalarValue {
1238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1239        match self {
1240            ScalarValue::Isize(v) => write!(f, "{v} : isize"),
1241            ScalarValue::I8(v) => write!(f, "{v} : i8"),
1242            ScalarValue::I16(v) => write!(f, "{v} : i16"),
1243            ScalarValue::I32(v) => write!(f, "{v} : i32"),
1244            ScalarValue::I64(v) => write!(f, "{v} : i64"),
1245            ScalarValue::I128(v) => write!(f, "{v} : i128"),
1246            ScalarValue::Usize(v) => write!(f, "{v} : usize"),
1247            ScalarValue::U8(v) => write!(f, "{v} : u8"),
1248            ScalarValue::U16(v) => write!(f, "{v} : u16"),
1249            ScalarValue::U32(v) => write!(f, "{v} : u32"),
1250            ScalarValue::U64(v) => write!(f, "{v} : u64"),
1251            ScalarValue::U128(v) => write!(f, "{v} : u128"),
1252        }
1253    }
1254}
1255
1256impl<C: AstFormatter> FmtWithCtx<C> for ullbc::Statement {
1257    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1258        let tab = ctx.indent();
1259        use ullbc::RawStatement;
1260        for line in &self.comments_before {
1261            writeln!(f, "{tab}// {line}")?;
1262        }
1263        match &self.content {
1264            RawStatement::Assign(place, rvalue) => write!(
1265                f,
1266                "{tab}{} := {}",
1267                place.with_ctx(ctx),
1268                rvalue.with_ctx(ctx),
1269            ),
1270            RawStatement::SetDiscriminant(place, variant_id) => write!(
1271                f,
1272                "{tab}@discriminant({}) := {}",
1273                place.with_ctx(ctx),
1274                variant_id
1275            ),
1276            RawStatement::CopyNonOverlapping(box CopyNonOverlapping { src, dst, count }) => write!(
1277                f,
1278                "{}copy_nonoverlapping({}, {}, {})",
1279                tab,
1280                src.with_ctx(ctx),
1281                dst.with_ctx(ctx),
1282                count.with_ctx(ctx),
1283            ),
1284            RawStatement::StorageLive(var_id) => {
1285                write!(f, "{tab}storage_live({})", var_id.with_ctx(ctx))
1286            }
1287            RawStatement::StorageDead(var_id) => {
1288                write!(f, "{tab}storage_dead({})", var_id.with_ctx(ctx))
1289            }
1290            RawStatement::Deinit(place) => {
1291                write!(f, "{tab}deinit({})", place.with_ctx(ctx))
1292            }
1293            RawStatement::Drop(place) => {
1294                write!(f, "{tab}drop {}", place.with_ctx(ctx))
1295            }
1296            RawStatement::Assert(assert) => write!(f, "{tab}{}", assert.with_ctx(ctx)),
1297            RawStatement::Nop => write!(f, "{tab}nop"),
1298            RawStatement::Error(s) => write!(f, "{tab}@Error({})", s),
1299        }
1300    }
1301}
1302
1303impl<C: AstFormatter> FmtWithCtx<C> for llbc::Statement {
1304    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1305        let tab = ctx.indent();
1306        use llbc::RawStatement;
1307        for line in &self.comments_before {
1308            writeln!(f, "{tab}// {line}")?;
1309        }
1310        write!(f, "{tab}")?;
1311        match &self.content {
1312            RawStatement::Assign(place, rvalue) => {
1313                write!(f, "{} := {}", place.with_ctx(ctx), rvalue.with_ctx(ctx),)
1314            }
1315            RawStatement::SetDiscriminant(place, variant_id) => write!(
1316                f,
1317                "@discriminant({}) := {}",
1318                place.with_ctx(ctx),
1319                variant_id
1320            ),
1321            RawStatement::CopyNonOverlapping(box CopyNonOverlapping { src, dst, count }) => write!(
1322                f,
1323                "copy_nonoverlapping({}, {}, {})",
1324                src.with_ctx(ctx),
1325                dst.with_ctx(ctx),
1326                count.with_ctx(ctx),
1327            ),
1328            RawStatement::StorageLive(var_id) => {
1329                write!(f, "storage_live({})", var_id.with_ctx(ctx))
1330            }
1331            RawStatement::StorageDead(var_id) => {
1332                write!(f, "storage_dead({})", var_id.with_ctx(ctx))
1333            }
1334            RawStatement::Deinit(place) => {
1335                write!(f, "deinit({})", place.with_ctx(ctx))
1336            }
1337            RawStatement::Drop(place) => {
1338                write!(f, "drop {}", place.with_ctx(ctx))
1339            }
1340            RawStatement::Assert(assert) => {
1341                write!(f, "{}", assert.with_ctx(ctx),)
1342            }
1343            RawStatement::Call(call) => {
1344                write!(f, "{}", call.with_ctx(ctx))
1345            }
1346            RawStatement::Abort(kind) => {
1347                write!(f, "{}", kind.with_ctx(ctx))
1348            }
1349            RawStatement::Return => write!(f, "return"),
1350            RawStatement::Break(index) => write!(f, "break {index}"),
1351            RawStatement::Continue(index) => write!(f, "continue {index}"),
1352            RawStatement::Nop => write!(f, "nop"),
1353            RawStatement::Switch(switch) => match switch {
1354                Switch::If(discr, true_st, false_st) => {
1355                    let ctx = &ctx.increase_indent();
1356                    write!(
1357                        f,
1358                        "if {} {{\n{}{tab}}}\n{tab}else {{\n{}{tab}}}",
1359                        discr.with_ctx(ctx),
1360                        true_st.with_ctx(ctx),
1361                        false_st.with_ctx(ctx),
1362                    )
1363                }
1364                Switch::SwitchInt(discr, _ty, maps, otherwise) => {
1365                    writeln!(f, "switch {} {{", discr.with_ctx(ctx))?;
1366                    let ctx1 = &ctx.increase_indent();
1367                    let inner_tab1 = ctx1.indent();
1368                    let ctx2 = &ctx1.increase_indent();
1369                    for (pvl, st) in maps {
1370                        // Note that there may be several pattern values
1371                        let pvl = pvl.iter().format(" | ");
1372                        writeln!(
1373                            f,
1374                            "{inner_tab1}{} => {{\n{}{inner_tab1}}},",
1375                            pvl,
1376                            st.with_ctx(ctx2),
1377                        )?;
1378                    }
1379                    writeln!(
1380                        f,
1381                        "{inner_tab1}_ => {{\n{}{inner_tab1}}},",
1382                        otherwise.with_ctx(ctx2),
1383                    )?;
1384                    write!(f, "{tab}}}")
1385                }
1386                Switch::Match(discr, maps, otherwise) => {
1387                    writeln!(f, "match {} {{", discr.with_ctx(ctx))?;
1388                    let ctx1 = &ctx.increase_indent();
1389                    let inner_tab1 = ctx1.indent();
1390                    let ctx2 = &ctx1.increase_indent();
1391                    let discr_type: Option<TypeDeclId> = discr
1392                        .ty
1393                        .kind()
1394                        .as_adt()
1395                        .and_then(|(x, _)| x.as_adt())
1396                        .copied();
1397                    for (cases, st) in maps {
1398                        write!(f, "{inner_tab1}",)?;
1399                        // Note that there may be several pattern values
1400                        for (bar, v) in repeat_except_first(" | ").zip(cases.iter()) {
1401                            write!(f, "{}", bar.unwrap_or_default())?;
1402                            match discr_type {
1403                                Some(type_id) => ctx.format_enum_variant(f, type_id, *v)?,
1404                                None => write!(f, "{}", v.to_pretty_string())?,
1405                            }
1406                        }
1407                        writeln!(f, " => {{\n{}{inner_tab1}}},", st.with_ctx(ctx2),)?;
1408                    }
1409                    if let Some(otherwise) = otherwise {
1410                        writeln!(
1411                            f,
1412                            "{inner_tab1}_ => {{\n{}{inner_tab1}}},",
1413                            otherwise.with_ctx(ctx2),
1414                        )?;
1415                    }
1416                    write!(f, "{tab}}}")
1417                }
1418            },
1419            RawStatement::Loop(body) => {
1420                let ctx = &ctx.increase_indent();
1421                write!(f, "loop {{\n{}{tab}}}", body.with_ctx(ctx))
1422            }
1423            RawStatement::Error(s) => write!(f, "@ERROR({})", s),
1424        }
1425    }
1426}
1427
1428impl<C: AstFormatter> FmtWithCtx<C> for Terminator {
1429    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1430        let tab = ctx.indent();
1431        for line in &self.comments_before {
1432            writeln!(f, "{tab}// {line}")?;
1433        }
1434        write!(f, "{tab}")?;
1435        match &self.content {
1436            RawTerminator::Goto { target } => write!(f, "goto bb{target}"),
1437            RawTerminator::Switch { discr, targets } => match targets {
1438                SwitchTargets::If(true_block, false_block) => write!(
1439                    f,
1440                    "if {} -> bb{} else -> bb{}",
1441                    discr.with_ctx(ctx),
1442                    true_block,
1443                    false_block
1444                ),
1445                SwitchTargets::SwitchInt(_ty, maps, otherwise) => {
1446                    let maps = maps
1447                        .iter()
1448                        .map(|(v, bid)| format!("{}: bb{}", v.to_string(), bid))
1449                        .chain([format!("otherwise: bb{otherwise}")])
1450                        .format(", ");
1451                    write!(f, "switch {} -> {}", discr.with_ctx(ctx), maps)
1452                }
1453            },
1454            RawTerminator::Call {
1455                call,
1456                target,
1457                on_unwind,
1458            } => {
1459                let call = call.with_ctx(ctx);
1460                write!(f, "{call} -> bb{target} (unwind: bb{on_unwind})",)
1461            }
1462            RawTerminator::Abort(kind) => write!(f, "{}", kind.with_ctx(ctx)),
1463            RawTerminator::Return => write!(f, "return"),
1464            RawTerminator::UnwindResume => write!(f, "unwind_continue"),
1465        }
1466    }
1467}
1468
1469impl<C: AstFormatter> FmtWithCtx<C> for TraitClause {
1470    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1471        let clause_id = self.clause_id.to_pretty_string();
1472        let trait_ = self.trait_.with_ctx(ctx);
1473        write!(f, "[{clause_id}]: {trait_}")
1474    }
1475}
1476
1477impl<C: AstFormatter> FmtWithCtx<C> for TraitDecl {
1478    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1479        // Update the context
1480        let ctx = &ctx.set_generics(&self.generics);
1481
1482        self.item_meta
1483            .fmt_item_intro(f, ctx, "trait", self.def_id)?;
1484
1485        let (generics, clauses) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
1486        write!(f, "{generics}{clauses}")?;
1487
1488        let any_item = !self.parent_clauses.is_empty()
1489            || !self.type_clauses.is_empty()
1490            || !self.consts.is_empty()
1491            || !self.types.is_empty()
1492            || self.methods().count() > 0;
1493        if any_item {
1494            write!(f, "\n{{\n")?;
1495            for c in &self.parent_clauses {
1496                writeln!(
1497                    f,
1498                    "{TAB_INCR}parent_clause{} : {}",
1499                    c.clause_id,
1500                    c.with_ctx(ctx)
1501                )?;
1502            }
1503            for (name, clauses) in &self.type_clauses {
1504                for c in clauses {
1505                    writeln!(
1506                        f,
1507                        "{TAB_INCR}item_clause_{name}_{} : {}",
1508                        c.clause_id.to_string(),
1509                        c.with_ctx(ctx)
1510                    )?;
1511                }
1512            }
1513            for (name, ty) in &self.consts {
1514                let ty = ty.with_ctx(ctx);
1515                writeln!(f, "{TAB_INCR}const {name} : {ty}")?;
1516            }
1517            for name in &self.types {
1518                writeln!(f, "{TAB_INCR}type {name}")?;
1519            }
1520            for (name, bound_fn) in self.methods() {
1521                let (params, fn_ref) = bound_fn.fmt_split(ctx);
1522                writeln!(f, "{TAB_INCR}fn {name}{params} = {fn_ref}")?;
1523            }
1524            write!(f, "}}")?;
1525        }
1526        Ok(())
1527    }
1528}
1529
1530impl<C: AstFormatter> FmtWithCtx<C> for TraitDeclId {
1531    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1532        AnyTransId::from(*self).fmt_with_ctx(ctx, f)
1533    }
1534}
1535
1536impl<C: AstFormatter> FmtWithCtx<C> for TraitDeclRef {
1537    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1538        let trait_id = self.trait_id.with_ctx(ctx);
1539        let generics = self.generics.with_ctx(ctx);
1540        write!(f, "{trait_id}{generics}")
1541    }
1542}
1543
1544impl<C: AstFormatter> FmtWithCtx<C> for TraitImpl {
1545    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1546        let full_name = self.item_meta.name.with_ctx(ctx);
1547        writeln!(f, "// Full name: {full_name}")?;
1548
1549        // Update the context
1550        let ctx = &ctx.set_generics(&self.generics);
1551
1552        let (generics, clauses) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
1553        let mut impl_trait = self.impl_trait.clone();
1554        let self_ty = impl_trait.generics.types.remove(TypeVarId::ZERO).unwrap();
1555        let self_ty = self_ty.with_ctx(ctx);
1556        let impl_trait = impl_trait.with_ctx(ctx);
1557        write!(f, "impl{generics} {impl_trait} for {self_ty}{clauses}")?;
1558
1559        let newline = if clauses.is_empty() {
1560            " ".to_string()
1561        } else {
1562            "\n".to_string()
1563        };
1564        write!(f, "{newline}{{")?;
1565
1566        let any_item = !self.parent_trait_refs.is_empty()
1567            || !self.type_clauses.is_empty()
1568            || !self.consts.is_empty()
1569            || !self.types.is_empty()
1570            || self.methods().count() > 0;
1571        if any_item {
1572            writeln!(f)?;
1573            for (i, c) in self.parent_trait_refs.iter().enumerate() {
1574                let i = TraitClauseId::new(i);
1575                writeln!(f, "{TAB_INCR}parent_clause{i} = {}", c.with_ctx(ctx))?;
1576            }
1577            for (name, clauses) in &self.type_clauses {
1578                for (i, c) in clauses.iter().enumerate() {
1579                    let i = TraitClauseId::new(i);
1580                    writeln!(f, "{TAB_INCR}item_clause_{name}_{i} = {}", c.with_ctx(ctx))?;
1581                }
1582            }
1583            for (name, global) in &self.consts {
1584                writeln!(f, "{TAB_INCR}const {name} = {}", global.with_ctx(ctx))?;
1585            }
1586            for (name, ty) in &self.types {
1587                writeln!(f, "{TAB_INCR}type {name} = {}", ty.with_ctx(ctx))?;
1588            }
1589            for (name, bound_fn) in self.methods() {
1590                let (params, fn_ref) = bound_fn.fmt_split(ctx);
1591                writeln!(f, "{TAB_INCR}fn {name}{params} = {fn_ref}")?;
1592            }
1593        }
1594        write!(f, "}}")?;
1595        Ok(())
1596    }
1597}
1598
1599impl<C: AstFormatter> FmtWithCtx<C> for TraitImplId {
1600    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1601        AnyTransId::from(*self).fmt_with_ctx(ctx, f)
1602    }
1603}
1604
1605impl Display for TraitItemName {
1606    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1607        write!(f, "{}", self.0)
1608    }
1609}
1610
1611impl<C: AstFormatter> FmtWithCtx<C> for TraitRefKind {
1612    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1613        match self {
1614            TraitRefKind::SelfId => write!(f, "Self"),
1615            TraitRefKind::ParentClause(id, _decl_id, clause_id) => {
1616                let id = id.with_ctx(ctx);
1617                write!(f, "{id}::parent_clause{clause_id}")
1618            }
1619            TraitRefKind::ItemClause(id, _decl_id, type_name, clause_id) => {
1620                let id = id.with_ctx(ctx);
1621                // Using on purpose `to_pretty_string` instead of `with_ctx`: the clause is local
1622                // to the associated type, so it should not be referenced in the current context.
1623                let clause = clause_id.to_pretty_string();
1624                write!(f, "({id}::{type_name}::[{clause}])")
1625            }
1626            TraitRefKind::TraitImpl(id, args) => {
1627                let impl_ = id.with_ctx(ctx);
1628                let args = args.with_ctx(ctx);
1629                write!(f, "{impl_}{args}")
1630            }
1631            TraitRefKind::Clause(id) => write!(f, "{}", id.with_ctx(ctx)),
1632            TraitRefKind::BuiltinOrAuto {
1633                trait_decl_ref: tr,
1634                types,
1635                ..
1636            } => {
1637                write!(f, "{}", tr.with_ctx(ctx))?;
1638                if !types.is_empty() {
1639                    let types = types
1640                        .iter()
1641                        .map(|(name, ty)| {
1642                            let ty = ty.with_ctx(ctx);
1643                            format!("{name}  = {ty}")
1644                        })
1645                        .join(", ");
1646                    write!(f, " where {types}")?;
1647                }
1648                Ok(())
1649            }
1650            TraitRefKind::Dyn(tr) => write!(f, "{}", tr.with_ctx(ctx)),
1651            TraitRefKind::Unknown(msg) => write!(f, "UNKNOWN({msg})"),
1652        }
1653    }
1654}
1655
1656impl<C: AstFormatter> FmtWithCtx<C> for TraitRef {
1657    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1658        self.kind.fmt_with_ctx(ctx, f)
1659    }
1660}
1661
1662impl<C: AstFormatter> FmtWithCtx<C> for TraitTypeConstraint {
1663    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1664        let trait_ref = self.trait_ref.with_ctx(ctx);
1665        let ty = self.ty.with_ctx(ctx);
1666        write!(f, "{}::{} = {}", trait_ref, self.type_name, ty)
1667    }
1668}
1669
1670impl<C: AstFormatter> FmtWithCtx<C> for Ty {
1671    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1672        match self.kind() {
1673            TyKind::Adt(id, generics) => {
1674                let adt_ident = id.with_ctx(ctx);
1675                if id.is_tuple() {
1676                    assert!(generics.trait_refs.is_empty());
1677                    let generics = generics.fmt_explicits(ctx).format(", ");
1678                    write!(f, "({generics})")
1679                } else {
1680                    let generics = generics.with_ctx(ctx);
1681                    write!(f, "{adt_ident}{generics}")
1682                }
1683            }
1684            TyKind::TypeVar(id) => write!(f, "{}", id.with_ctx(ctx)),
1685            TyKind::Literal(kind) => write!(f, "{kind}"),
1686            TyKind::Never => write!(f, "!"),
1687            TyKind::Ref(r, ty, kind) => {
1688                write!(f, "&{} ", r.with_ctx(ctx))?;
1689                if let RefKind::Mut = kind {
1690                    write!(f, "mut ")?;
1691                }
1692                write!(f, "({})", ty.with_ctx(ctx))
1693            }
1694            TyKind::RawPtr(ty, kind) => {
1695                write!(f, "*")?;
1696                match kind {
1697                    RefKind::Shared => write!(f, "const")?,
1698                    RefKind::Mut => write!(f, "mut")?,
1699                }
1700                write!(f, " {}", ty.with_ctx(ctx))
1701            }
1702            TyKind::TraitType(trait_ref, name) => {
1703                write!(f, "{}::{name}", trait_ref.with_ctx(ctx),)
1704            }
1705            TyKind::DynTrait(pred) => write!(f, "dyn ({})", pred.with_ctx(ctx)),
1706            TyKind::Arrow(io) => {
1707                // Update the bound regions
1708                let ctx = &ctx.push_bound_regions(&io.regions);
1709
1710                write!(f, "fn")?;
1711                if !io.regions.is_empty() {
1712                    write!(
1713                        f,
1714                        "<{}>",
1715                        io.regions.iter().map(|r| r.with_ctx(ctx)).format(", ")
1716                    )?;
1717                }
1718                let (inputs, output) = &io.skip_binder;
1719                let inputs = inputs.iter().map(|x| x.with_ctx(ctx)).format(", ");
1720                write!(f, "({inputs})")?;
1721                if !output.is_unit() {
1722                    let output = output.with_ctx(ctx);
1723                    write!(f, " -> {output}")?;
1724                }
1725                Ok(())
1726            }
1727            TyKind::Error(msg) => write!(f, "type_error(\"{msg}\")"),
1728        }
1729    }
1730}
1731
1732impl<C: AstFormatter> FmtWithCtx<C> for TypeDbVar {
1733    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1734        ctx.format_bound_var(f, *self, "@Type", |v| Some(v.to_string()))
1735    }
1736}
1737
1738impl<C: AstFormatter> FmtWithCtx<C> for TypeDecl {
1739    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1740        let keyword = match &self.kind {
1741            TypeDeclKind::Struct(..) => "struct",
1742            TypeDeclKind::Union(..) => "union",
1743            TypeDeclKind::Enum(..) => "enum",
1744            TypeDeclKind::Alias(..) => "type",
1745            TypeDeclKind::Opaque | TypeDeclKind::Error(..) => "opaque type",
1746        };
1747        self.item_meta
1748            .fmt_item_intro(f, ctx, keyword, self.def_id)?;
1749
1750        let ctx = &ctx.set_generics(&self.generics);
1751        let (params, preds) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
1752        write!(f, "{params}{preds}")?;
1753
1754        let nl_or_space = if !self.generics.has_predicates() {
1755            " ".to_string()
1756        } else {
1757            "\n".to_string()
1758        };
1759        match &self.kind {
1760            TypeDeclKind::Struct(fields) => {
1761                write!(f, "{nl_or_space}{{")?;
1762                if !fields.is_empty() {
1763                    writeln!(f)?;
1764                    for field in fields {
1765                        writeln!(f, "  {},", field.with_ctx(ctx))?;
1766                    }
1767                }
1768                write!(f, "}}")
1769            }
1770            TypeDeclKind::Union(fields) => {
1771                write!(f, "{nl_or_space}{{")?;
1772                writeln!(f)?;
1773                for field in fields {
1774                    writeln!(f, "  {},", field.with_ctx(ctx))?;
1775                }
1776                write!(f, "}}")
1777            }
1778            TypeDeclKind::Enum(variants) => {
1779                write!(f, "{nl_or_space}{{")?;
1780                writeln!(f)?;
1781                for variant in variants {
1782                    writeln!(f, "  {},", variant.with_ctx(ctx))?;
1783                }
1784                write!(f, "}}")
1785            }
1786            TypeDeclKind::Alias(ty) => write!(f, " = {}", ty.with_ctx(ctx)),
1787            TypeDeclKind::Opaque => write!(f, ""),
1788            TypeDeclKind::Error(msg) => write!(f, " = ERROR({msg})"),
1789        }
1790    }
1791}
1792
1793impl<C: AstFormatter> FmtWithCtx<C> for TypeDeclId {
1794    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1795        AnyTransId::from(*self).fmt_with_ctx(ctx, f)
1796    }
1797}
1798
1799impl<C: AstFormatter> FmtWithCtx<C> for TypeId {
1800    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1801        match self {
1802            TypeId::Tuple => Ok(()),
1803            TypeId::Adt(def_id) => write!(f, "{}", def_id.with_ctx(ctx)),
1804            TypeId::Builtin(aty) => write!(f, "{}", aty.get_name().with_ctx(ctx)),
1805        }
1806    }
1807}
1808
1809impl_display_via_ctx!(TypeVar);
1810impl<C: AstFormatter> FmtWithCtx<C> for TypeVar {
1811    fn fmt_with_ctx(&self, _ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1812        write!(f, "{}", self.name)
1813    }
1814}
1815
1816impl<C: AstFormatter> FmtWithCtx<C> for UnOp {
1817    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1818        match self {
1819            UnOp::Not => write!(f, "~"),
1820            UnOp::Neg => write!(f, "-"),
1821            UnOp::PtrMetadata => write!(f, "ptr_metadata"),
1822            UnOp::Cast(kind) => write!(f, "{}", kind.with_ctx(ctx)),
1823            UnOp::ArrayToSlice(..) => write!(f, "array_to_slice"),
1824        }
1825    }
1826}
1827
1828impl_display_via_ctx!(Variant);
1829impl<C: AstFormatter> FmtWithCtx<C> for Variant {
1830    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1831        write!(f, "{}", self.name)?;
1832        if !self.fields.is_empty() {
1833            let fields = self.fields.iter().map(|f| f.with_ctx(ctx)).format(", ");
1834            write!(f, "({})", fields)?;
1835        }
1836        Ok(())
1837    }
1838}