Skip to main content

charon_lib/pretty/
fmt_with_ctx.rs

1//! Utilities for pretty-printing (u)llbc.
2use crate::{
3    common::{TAB_INCR, repeat_except_first},
4    formatter::*,
5    gast,
6    ids::IndexVec,
7    llbc_ast::{self as llbc, *},
8    transform::utils::GenericsSource,
9    ullbc_ast::{self as ullbc, *},
10};
11use either::Either;
12use itertools::Itertools;
13use std::{
14    borrow::Cow,
15    fmt::{self, Debug, Display},
16};
17
18pub struct WithCtx<'a, C, T: ?Sized> {
19    val: &'a T,
20    ctx: &'a C,
21}
22
23impl<'a, C, T: ?Sized> Display for WithCtx<'a, C, T>
24where
25    T: FmtWithCtx<C>,
26{
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        self.val.fmt_with_ctx(self.ctx, f)
29    }
30}
31
32/// Format the AST type as a string.
33pub trait FmtWithCtx<C> {
34    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result;
35
36    /// Returns a struct that implements `Display`. This allows the following:
37    /// ```text
38    ///     println!("{}", self.with_ctx(ctx));
39    /// ```
40    fn with_ctx<'a>(&'a self, ctx: &'a C) -> WithCtx<'a, C, Self> {
41        WithCtx { val: self, ctx }
42    }
43
44    fn to_string_with_ctx(&self, ctx: &C) -> String {
45        self.with_ctx(ctx).to_string()
46    }
47}
48
49macro_rules! impl_display_via_ctx {
50    ($ty:ty) => {
51        impl Display for $ty {
52            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53                self.fmt_with_ctx(&FmtCtx::new(), f)
54            }
55        }
56    };
57}
58macro_rules! impl_debug_via_display {
59    ($ty:ty) => {
60        impl Debug for $ty {
61            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62                <_ as Display>::fmt(self, f)
63            }
64        }
65    };
66}
67
68fn fmt_where_clauses<'a, I>(clauses: I, indent: &'a str) -> impl Display + 'a
69where
70    I: IntoIterator,
71    I::Item: Display,
72{
73    let clauses = clauses
74        .into_iter()
75        .map(|clause| clause.to_string())
76        .collect_vec();
77    std::fmt::from_fn(move |f| {
78        if !clauses.is_empty() {
79            write!(f, "\n{indent}where")?;
80            for (i, clause) in clauses.iter().enumerate() {
81                let sep = if i + 1 == clauses.len() { ";" } else { "," };
82                write!(f, "\n{indent}{TAB_INCR}{clause}{sep}")?;
83            }
84        }
85        Ok(())
86    })
87}
88
89//------- Impls, sorted by name --------
90
91impl<C: AstFormatter> FmtWithCtx<C> for AbortKind {
92    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        match self {
94            AbortKind::Panic(name) => {
95                write!(f, "panic")?;
96                if let Some(name) = name {
97                    write!(f, "({})", name.with_ctx(ctx))?;
98                }
99                Ok(())
100            }
101            AbortKind::UndefinedBehavior => write!(f, "undefined_behavior"),
102            AbortKind::UnwindTerminate => write!(f, "unwind_terminate"),
103        }
104    }
105}
106
107impl<C: AstFormatter> FmtWithCtx<C> for Abi {
108    fn fmt_with_ctx(&self, _ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        write!(f, "{}", self.rust_name())
110    }
111}
112
113impl<C: AstFormatter> FmtWithCtx<C> for BuiltinAssertKind {
114    fn fmt_with_ctx(&self, _ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        match self {
116            BuiltinAssertKind::BoundsCheck { .. } => write!(f, "bounds_check"),
117            BuiltinAssertKind::Overflow(..) => write!(f, "overflow"),
118            BuiltinAssertKind::OverflowNeg(..) => write!(f, "overflow_neg"),
119            BuiltinAssertKind::DivisionByZero(..) => write!(f, "division_by_zero"),
120            BuiltinAssertKind::RemainderByZero(..) => write!(f, "remainder_by_zero"),
121            BuiltinAssertKind::MisalignedPointerDereference { .. } => {
122                write!(f, "misaligned_pointer_dereference")
123            }
124            BuiltinAssertKind::NullPointerDereference => write!(f, "null_pointer_dereference"),
125            BuiltinAssertKind::InvalidEnumConstruction(..) => {
126                write!(f, "invalid_enum_construction")
127            }
128            BuiltinAssertKind::ResumedAfterReturn => write!(f, "resumed_after_return"),
129            BuiltinAssertKind::ResumedAfterDrop => write!(f, "resumed_after_drop"),
130            BuiltinAssertKind::ResumedAfterPanic => write!(f, "resumed_after_panic"),
131        }
132    }
133}
134
135impl<C: AstFormatter> FmtWithCtx<C> for ItemId {
136    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        match ctx.get_crate() {
138            None => write!(f, "{self}"),
139            Some(translated) => translated.item_short_name(*self).fmt_with_ctx(ctx, f),
140        }
141    }
142}
143
144impl Display for ItemId {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
146        let s = match self {
147            ItemId::Type(x) => x.to_pretty_string(),
148            ItemId::Fun(x) => x.to_pretty_string(),
149            ItemId::Global(x) => x.to_pretty_string(),
150            ItemId::TraitDecl(x) => x.to_pretty_string(),
151            ItemId::TraitImpl(x) => x.to_pretty_string(),
152        };
153        f.write_str(&s)
154    }
155}
156
157impl<C: AstFormatter> FmtWithCtx<C> for ItemRef<'_> {
158    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        match self {
160            ItemRef::Type(d) => write!(f, "{}", d.with_ctx(ctx)),
161            ItemRef::Fun(d) => write!(f, "{}", d.with_ctx(ctx)),
162            ItemRef::Global(d) => write!(f, "{}", d.with_ctx(ctx)),
163            ItemRef::TraitDecl(d) => write!(f, "{}", d.with_ctx(ctx)),
164            ItemRef::TraitImpl(d) => write!(f, "{}", d.with_ctx(ctx)),
165        }
166    }
167}
168
169impl<C: AstFormatter> FmtWithCtx<C> for Assert {
170    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        write!(
172            f,
173            "assert({} == {})",
174            self.cond.with_ctx(ctx),
175            self.expected,
176        )?;
177        if let Some(check_kind) = &self.check_kind {
178            write!(f, " ({})", check_kind.with_ctx(ctx))?;
179        }
180        Ok(())
181    }
182}
183
184impl<T> Binder<T> {
185    /// Format the parameters and contents of this binder and returns the resulting strings.
186    fn fmt_split<'a, C>(&'a self, ctx: &'a C) -> (String, String)
187    where
188        C: AstFormatter,
189        T: FmtWithCtx<C::Reborrow<'a>>,
190    {
191        self.fmt_split_with(ctx, |ctx, x| x.to_string_with_ctx(ctx))
192    }
193    /// Format the parameters and contents of this binder and returns the resulting strings.
194    fn fmt_split_with<'a, C>(
195        &'a self,
196        ctx: &'a C,
197        fmt_inner: impl FnOnce(&C::Reborrow<'a>, &T) -> String,
198    ) -> (String, String)
199    where
200        C: AstFormatter,
201    {
202        let ctx = &ctx.push_binder(Cow::Borrowed(&self.params));
203        (
204            self.params.fmt_with_ctx_single_line(ctx),
205            fmt_inner(ctx, &self.skip_binder),
206        )
207    }
208}
209
210impl Display for OverflowMode {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
212        match self {
213            OverflowMode::Panic => write!(f, "panic"),
214            OverflowMode::Wrap => write!(f, "wrap"),
215            OverflowMode::UB => write!(f, "ub"),
216        }
217    }
218}
219
220impl Display for BinOp {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
222        match self {
223            BinOp::BitXor => write!(f, "^"),
224            BinOp::BitAnd => write!(f, "&"),
225            BinOp::BitOr => write!(f, "|"),
226            BinOp::Eq => write!(f, "=="),
227            BinOp::Lt => write!(f, "<"),
228            BinOp::Le => write!(f, "<="),
229            BinOp::Ne => write!(f, "!="),
230            BinOp::Ge => write!(f, ">="),
231            BinOp::Gt => write!(f, ">"),
232            BinOp::Add(mode) => write!(f, "{}.+", mode),
233            BinOp::Sub(mode) => write!(f, "{}.-", mode),
234            BinOp::Mul(mode) => write!(f, "{}.*", mode),
235            BinOp::Div(mode) => write!(f, "{}./", mode),
236            BinOp::Rem(mode) => write!(f, "{}.%", mode),
237            BinOp::AddChecked => write!(f, "checked.+"),
238            BinOp::SubChecked => write!(f, "checked.-"),
239            BinOp::MulChecked => write!(f, "checked.*"),
240            BinOp::Shl(mode) => write!(f, "{}.<<", mode),
241            BinOp::Shr(mode) => write!(f, "{}.>>", mode),
242            BinOp::Cmp => write!(f, "cmp"),
243            BinOp::Offset => write!(f, "offset"),
244        }
245    }
246}
247
248impl<C: AstFormatter> FmtWithCtx<C> for llbc::Block {
249    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        for st in &self.statements {
251            writeln!(f, "{}", st.with_ctx(ctx))?;
252        }
253        Ok(())
254    }
255}
256
257impl<C: AstFormatter> FmtWithCtx<C> for ullbc::BlockData {
258    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259        for statement in &self.statements {
260            writeln!(f, "{};", statement.with_ctx(ctx))?;
261        }
262        write!(f, "{};", self.terminator.with_ctx(ctx))?;
263        Ok(())
264    }
265}
266
267impl<C: AstFormatter> FmtWithCtx<C> for gast::Body {
268    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        let tab = ctx.indent();
270        write!(f, "\n{tab}")?;
271        match self {
272            Body::Unstructured(body) => {
273                let body = body.with_ctx(ctx);
274                write!(f, "{{\n{body}{tab}}}")
275            }
276            Body::Structured(body) => {
277                let body = body.with_ctx(ctx);
278                write!(f, "{{\n{body}{tab}}}")
279            }
280            Body::Extern(name) => write!(f, "= <extern:{name}>"),
281            Body::Intrinsic { name, .. } => write!(f, "= <intrinsic:{name}>"),
282            Body::Opaque => write!(f, "= <opaque>"),
283            Body::Missing => write!(f, "= <missing>"),
284            Body::Error(error) => write!(f, "= error(\"{}\")", error.msg),
285            Body::TargetDispatch(targets) => {
286                writeln!(f, "= target_dispatch {{")?;
287                for (target, fun) in targets {
288                    let fun = fun.with_ctx(ctx);
289                    writeln!(f, "{tab}{TAB_INCR}{target} => {fun},")?;
290                }
291                write!(f, "{tab}}}")
292            }
293        }
294    }
295}
296
297impl Display for BorrowKind {
298    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
299        // Reuse the derived `Debug` impl to get the variant name.
300        write!(f, "{self:?}")
301    }
302}
303
304impl Display for BuiltinFunId {
305    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
306        let name = match *self {
307            BuiltinFunId::BoxNew => "BoxNew",
308            BuiltinFunId::ArrayToSliceShared => "ArrayToSliceShared",
309            BuiltinFunId::ArrayToSliceMut => "ArrayToSliceMut",
310            BuiltinFunId::ArrayRepeat => "ArrayRepeat",
311            BuiltinFunId::Index(BuiltinIndexOp {
312                is_array,
313                mutability,
314                is_range,
315            }) => {
316                let ty = if is_array { "Array" } else { "Slice" };
317                let op = if is_range { "SubSlice" } else { "Index" };
318                let mutability = mutability.variant_name();
319                &format!("{ty}{op}{mutability}")
320            }
321            BuiltinFunId::PtrFromParts(mutability) => {
322                let mutability = mutability.variant_name();
323                &format!("PtrFromParts{mutability}")
324            }
325        };
326        f.write_str(name)
327    }
328}
329
330impl<C: AstFormatter> FmtWithCtx<C> for Call {
331    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332        let dest = self.dest.with_ctx(ctx);
333        let func = self.func.with_ctx(ctx);
334        let args = self.args.iter().map(|x| x.with_ctx(ctx)).format(", ");
335        write!(f, "{dest} = {func}({args})")
336    }
337}
338
339impl<C: AstFormatter> FmtWithCtx<C> for UnsizingMetadata {
340    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341        match self {
342            UnsizingMetadata::Length(len) => write!(f, "{}", len.with_ctx(ctx)),
343            UnsizingMetadata::VTable(_, vtable) => {
344                write!(f, "{}", vtable.with_ctx(ctx))
345            }
346            UnsizingMetadata::VTableUpcast(fields) => {
347                write!(f, " at [")?;
348                let fields = fields.iter().map(|x| format!("{}", x.index())).format(", ");
349                write!(f, "{fields}]")
350            }
351            UnsizingMetadata::Unknown => {
352                write!(f, "?")
353            }
354        }
355    }
356}
357
358impl<C: AstFormatter> FmtWithCtx<C> for CastKind {
359    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        match self {
361            CastKind::Scalar(src, tgt) => write!(f, "cast<{src}, {tgt}>"),
362            CastKind::FnPtr(src, tgt) | CastKind::RawPtr(src, tgt) => {
363                write!(f, "cast<{}, {}>", src.with_ctx(ctx), tgt.with_ctx(ctx))
364            }
365            CastKind::Unsize(src, tgt, meta) => write!(
366                f,
367                "unsize_cast<{}, {}, {}>",
368                src.with_ctx(ctx),
369                tgt.with_ctx(ctx),
370                meta.with_ctx(ctx)
371            ),
372            CastKind::Transmute(src, tgt) => {
373                write!(f, "transmute<{}, {}>", src.with_ctx(ctx), tgt.with_ctx(ctx))
374            }
375            CastKind::Concretize(ty, ty1) => {
376                write!(f, "concretize<{}, {}>", ty.with_ctx(ctx), ty1.with_ctx(ctx))
377            }
378        }
379    }
380}
381
382impl<C: AstFormatter> FmtWithCtx<C> for ClauseDbVar {
383    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384        ctx.format_bound_var(f, *self, "TraitClause", |_| None)
385    }
386}
387
388impl<C: AstFormatter> FmtWithCtx<C> for ConstGenericDbVar {
389    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390        ctx.format_bound_var(f, *self, "@ConstGeneric", |v| Some(v.name.clone()))
391    }
392}
393
394impl<C: AstFormatter> FmtWithCtx<C> for ConstGenericParam {
395    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396        write!(f, "const {} : {}", self.name, self.ty.with_ctx(ctx))
397    }
398}
399
400impl Display for DeBruijnId {
401    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
402        write!(f, "{}", self.index)
403    }
404}
405
406impl<Id: Display> Display for DeBruijnVar<Id> {
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        match self {
409            Self::Bound(dbid, varid) => write!(f, "Bound({dbid}, {varid})"),
410            Self::Free(varid) => write!(f, "{varid}"),
411        }
412    }
413}
414
415impl<C: AstFormatter> FmtWithCtx<C> for DeclarationGroup {
416    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417        use DeclarationGroup::*;
418        match self {
419            Type(g) => write!(f, "Type decls group: {}", g.with_ctx(ctx)),
420            Fun(g) => write!(f, "Fun decls group: {}", g.with_ctx(ctx)),
421            Global(g) => write!(f, "Global decls group: {}", g.with_ctx(ctx)),
422            TraitDecl(g) => write!(f, "Trait decls group: {}", g.with_ctx(ctx)),
423            TraitImpl(g) => write!(f, "Trait impls group: {}", g.with_ctx(ctx)),
424            Mixed(g) => write!(f, "Mixed group: {}", g.with_ctx(ctx)),
425        }
426    }
427}
428
429impl<C: AstFormatter> FmtWithCtx<C> for DynPredicate {
430    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431        let params = &self.binder.params;
432        let ctx = &ctx.push_binder(Cow::Borrowed(params));
433        let GenericParams {
434            regions,
435            types,
436            const_generics,
437            trait_clauses,
438            regions_outlive,
439            types_outlive,
440            trait_type_constraints,
441        } = params;
442        assert!(regions.is_empty());
443        assert!(const_generics.is_empty());
444        assert!(regions_outlive.is_empty());
445        assert_eq!(types.len(), 1);
446
447        // Format the clauses with their assoc types, e.g. `Iterator<Item = ...>`.
448        let mut cstrs_per_clause: IndexVec<TraitClauseId, Vec<String>> =
449            trait_clauses.map_ref(|_| vec![]);
450        for cstr in trait_type_constraints {
451            let mut tgt_clause = None;
452            let (_, cstr) = cstr.fmt_split_with(ctx, |ctx, cstr| {
453                let mut path = vec![];
454                let mut tref = &cstr.trait_ref;
455                loop {
456                    match &tref.kind {
457                        TraitRefKind::ParentClause(parent_trait_ref, clause_id) => {
458                            path.push(*clause_id);
459                            tref = parent_trait_ref;
460                        }
461                        &TraitRefKind::Clause(DeBruijnVar::Bound(_, clause_id)) => {
462                            tgt_clause = Some(clause_id);
463                            break;
464                        }
465                        _ => unreachable!(),
466                    }
467                }
468                let ty = cstr.ty.with_ctx(ctx);
469                let path_fmt = path.iter().map(|id| id.format_as_implied()).format("::");
470                std::fmt::from_fn(|f| {
471                    write!(f, "{path_fmt}")?;
472                    if !path.is_empty() {
473                        write!(f, "::")?;
474                    }
475                    ctx.format_assoc_type_name(f, cstr.trait_ref.trait_id(), cstr.type_id)?;
476                    write!(f, " = {ty}")?;
477                    Ok(())
478                })
479                .to_string()
480            });
481            if let Some(cstrs) = cstrs_per_clause.get_mut(tgt_clause.unwrap()) {
482                cstrs.push(cstr);
483            }
484        }
485        let trait_clauses = trait_clauses.iter().map(|clause| {
486            let cstrs = &cstrs_per_clause[clause.clause_id];
487            clause.trait_.fmt_as_for_with(ctx, |ctx, pred| {
488                let (_, pred) = pred.split_self();
489                let trait_id = pred.id.with_ctx(ctx);
490                let generics = if pred.generics.has_explicits() || !cstrs.is_empty() {
491                    let xs = pred
492                        .generics
493                        .fmt_explicits(ctx)
494                        .map(Either::Left)
495                        .chain(cstrs.iter().map(Either::Right))
496                        .format(", ");
497                    format!("<{}>", xs)
498                } else {
499                    String::new()
500                };
501                format!("{trait_id}{generics}")
502            })
503        });
504
505        let types_outlive = types_outlive
506            .iter()
507            .filter(|x| !x.skip_binder.1.is_erased())
508            .map(|x| {
509                x.fmt_as_for_with(ctx, |ctx, types_outlive| {
510                    types_outlive.1.to_string_with_ctx(ctx)
511                })
512            });
513        let clauses = trait_clauses.chain(types_outlive).format(" + ");
514        write!(f, "{clauses}")
515    }
516}
517
518impl_display_via_ctx!(Field);
519impl<C: AstFormatter> FmtWithCtx<C> for Field {
520    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
521        if let Some(name) = &self.name {
522            write!(f, "{}: ", name)?
523        }
524        write!(f, "{}", self.ty.with_ctx(ctx))
525    }
526}
527
528impl Display for FileName {
529    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
530        match self {
531            FileName::Virtual(path_buf) | FileName::Local(path_buf) => {
532                write!(f, "{}", path_buf.display())
533            }
534            FileName::NotReal(name) => write!(f, "{}", name),
535        }
536    }
537}
538
539impl Display for FloatTy {
540    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
541        match self {
542            FloatTy::F16 => write!(f, "f16"),
543            FloatTy::F32 => write!(f, "f32"),
544            FloatTy::F64 => write!(f, "f64"),
545            FloatTy::F128 => write!(f, "f128"),
546        }
547    }
548}
549
550impl Display for FloatValue {
551    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
552        let v = &self.value;
553        let ty = self.ty;
554        write!(f, "{v}{ty}")
555    }
556}
557
558impl<C: AstFormatter> FmtWithCtx<C> for FnOperand {
559    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
560        match self {
561            FnOperand::Regular(func) => write!(f, "{}", func.with_ctx(ctx)),
562            FnOperand::Dynamic(op) => write!(f, "({})", op.with_ctx(ctx)),
563        }
564    }
565}
566
567impl<C: AstFormatter> FmtWithCtx<C> for FnPtr {
568    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569        match self.kind.as_ref() {
570            FnPtrKind::Fun(FunId::Regular(def_id)) => write!(f, "{}", def_id.with_ctx(ctx))?,
571            FnPtrKind::Fun(FunId::Builtin(builtin)) => write!(f, "@{}", builtin)?,
572            FnPtrKind::Trait(trait_ref, method_id) => {
573                write!(f, "{}::", trait_ref.with_ctx(ctx))?;
574                ctx.format_method_name(f, trait_ref.trait_id(), *method_id)?;
575            }
576        };
577        write!(f, "{}", self.generics.with_ctx(ctx))
578    }
579}
580
581impl<C: AstFormatter> FmtWithCtx<C> for FunDecl {
582    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583        let mut keyword = String::new();
584        if self.signature.is_unsafe {
585            keyword.push_str("unsafe ");
586        }
587        if !self.signature.abi.is_rust() {
588            keyword.push_str(&format!("extern \"{}\" ", self.signature.abi.with_ctx(ctx)));
589        }
590        keyword.push_str("fn");
591        self.item_meta
592            .fmt_item_intro(f, ctx, &keyword, self.def_id)?;
593
594        // Update the context
595        let ctx = &ctx.set_generics(&self.generics);
596
597        // Generic parameters
598        let (params, preds) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
599        write!(f, "{params}")?;
600
601        // Arguments
602        let n_args = self.signature.inputs.len();
603        let args_of_locals = |l: &Locals| {
604            let ctx = ctx.set_locals(l);
605            l.locals
606                .iter()
607                .skip(1)
608                .take(n_args)
609                .map(|l| format!("{}", l.index.with_ctx(&ctx)))
610                .collect::<Vec<String>>()
611        };
612
613        let arg_names = match &self.body {
614            Body::Unstructured(body) => args_of_locals(&body.locals),
615            Body::Structured(body) => args_of_locals(&body.locals),
616            Body::Intrinsic { arg_names, .. } => arg_names
617                .iter()
618                .enumerate()
619                .map(|(i, name)| {
620                    let id = LocalId::new(i + 1);
621                    match name {
622                        Some(name) => format!("{name}_{id}"),
623                        None => format!("_{id}"),
624                    }
625                })
626                .collect(),
627            Body::Error(..)
628            | Body::Extern(..)
629            | Body::Missing
630            | Body::Opaque
631            | Body::TargetDispatch(..) => (0..n_args)
632                .map(|i| format!("{}", LocalId::new(i + 1).with_ctx(ctx)))
633                .collect(),
634        };
635        let mut args: Vec<String> = Vec::new();
636        for (ty, name) in self.signature.inputs.iter().zip(arg_names) {
637            args.push(format!("{}: {}", name, ty.with_ctx(ctx)));
638        }
639        let args = args.join(", ");
640        if self.signature.is_variadic {
641            if args.is_empty() {
642                write!(f, "(...)")?;
643            } else {
644                write!(f, "({args}, ...)")?;
645            }
646        } else {
647            write!(f, "({args})")?;
648        }
649
650        // Return type
651        if !self.signature.output.is_unit() {
652            write!(f, " -> {}", self.signature.output.with_ctx(ctx))?;
653        };
654        write!(f, "{preds}")?;
655        write!(f, "{}", self.body.with_ctx(ctx))?;
656
657        Ok(())
658    }
659}
660
661impl<C: AstFormatter> FmtWithCtx<C> for FunDeclId {
662    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
663        ItemId::from(*self).fmt_with_ctx(ctx, f)
664    }
665}
666
667impl<C: AstFormatter> FmtWithCtx<C> for FunDeclRef {
668    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
669        let id = self.id.with_ctx(ctx);
670        let generics = self.generics.with_ctx(ctx);
671        write!(f, "{id}{generics}")
672    }
673}
674
675impl<C: AstFormatter> FmtWithCtx<C> for RegionBinder<FunSig> {
676    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
677        // Update the bound regions
678        let ctx = &ctx.push_bound_regions(&self.regions);
679        let FunSig {
680            is_unsafe,
681            abi,
682            is_variadic,
683            inputs,
684            output,
685        } = &self.skip_binder;
686
687        if *is_unsafe {
688            write!(f, "unsafe ")?;
689        }
690
691        if !abi.is_rust() {
692            write!(f, "extern \"{}\" ", abi.with_ctx(ctx))?;
693        }
694
695        write!(f, "fn")?;
696        if !self.regions.is_empty() {
697            write!(
698                f,
699                "<{}>",
700                self.regions.iter().map(|r| r.with_ctx(ctx)).format(", ")
701            )?;
702        }
703        let is_empty = inputs.is_empty();
704        let inputs = inputs.iter().map(|x| x.with_ctx(ctx)).format(", ");
705        if *is_variadic {
706            if is_empty {
707                write!(f, "(...)")?;
708            } else {
709                write!(f, "({inputs}, ...)")?;
710            }
711        } else {
712            write!(f, "({inputs})")?;
713        }
714        if !output.is_unit() {
715            let output = output.with_ctx(ctx);
716            write!(f, " -> {output}")?;
717        }
718        Ok(())
719    }
720}
721
722impl<Id: Copy, C: AstFormatter> FmtWithCtx<C> for GDeclarationGroup<Id>
723where
724    Id: FmtWithCtx<C>,
725{
726    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
727        use GDeclarationGroup::*;
728        match self {
729            NonRec(id) => write!(f, "Non rec: {}", id.with_ctx(ctx)),
730            Rec(ids) => {
731                let ids = ids.iter().map(|id| id.with_ctx(ctx)).format(", ");
732                write!(f, "Rec: {}", ids)
733            }
734        }
735    }
736}
737
738impl GenericArgs {
739    pub(crate) fn fmt_explicits<'a, C: AstFormatter>(
740        &'a self,
741        ctx: &'a C,
742    ) -> impl Iterator<Item = impl Display + 'a> {
743        let regions = self.regions.iter().map(|x| x.with_ctx(ctx));
744        let types = self.types.iter().map(|x| x.with_ctx(ctx));
745        let const_generics = self.const_generics.iter().map(|x| x.with_ctx(ctx));
746        regions.map(Either::Left).chain(
747            types
748                .map(Either::Left)
749                .chain(const_generics.map(Either::Right))
750                .map(Either::Right),
751        )
752    }
753
754    pub(crate) fn fmt_implicits<'a, C: AstFormatter>(
755        &'a self,
756        ctx: &'a C,
757    ) -> impl Iterator<Item = impl Display + 'a> {
758        self.trait_refs.iter().map(|x| x.with_ctx(ctx))
759    }
760}
761
762impl_display_via_ctx!(GenericArgs);
763impl_debug_via_display!(GenericArgs);
764impl<C: AstFormatter> FmtWithCtx<C> for GenericArgs {
765    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
766        if self.has_explicits() {
767            write!(f, "<{}>", self.fmt_explicits(ctx).format(", "))?;
768        }
769        if self.has_implicits() {
770            write!(f, "[{}]", self.fmt_implicits(ctx).format(", "))?;
771        }
772        Ok(())
773    }
774}
775
776impl GenericParams {
777    fn formatted_params<'a, C>(&'a self, ctx: &'a C) -> impl Iterator<Item = impl Display + 'a>
778    where
779        C: AstFormatter,
780    {
781        let regions = self.regions.iter().map(|x| x.with_ctx(ctx));
782        let types = self.types.iter().map(|x| x.with_ctx(ctx));
783        let const_generics = self.const_generics.iter().map(|x| x.with_ctx(ctx));
784        regions.map(Either::Left).chain(
785            types
786                .map(Either::Left)
787                .chain(const_generics.map(Either::Right))
788                .map(Either::Right),
789        )
790    }
791
792    fn formatted_clauses<'a, C>(&'a self, ctx: &'a C) -> impl Iterator<Item = impl Display + 'a>
793    where
794        C: AstFormatter,
795    {
796        let trait_clauses = self.trait_clauses.iter().map(|x| x.to_string_with_ctx(ctx));
797        let types_outlive = self
798            .types_outlive
799            .iter()
800            .enumerate()
801            .map(|(i, x)| format!("TypeOutlives{i}: {}", x.fmt_as_for(ctx)));
802        let regions_outlive = self
803            .regions_outlive
804            .iter()
805            .enumerate()
806            .map(|(i, x)| format!("RegionOutlives{i}: {}", x.fmt_as_for(ctx)));
807        let type_constraints = self
808            .trait_type_constraints
809            .iter_enumerated()
810            .map(|(i, x)| format!("TypeConstraint{i}: {}", x.fmt_as_for(ctx)));
811        trait_clauses.map(Either::Left).chain(
812            types_outlive
813                .chain(regions_outlive)
814                .chain(type_constraints)
815                .map(Either::Right),
816        )
817    }
818
819    pub fn fmt_with_ctx_with_trait_clauses<C>(&self, ctx: &C) -> (String, String)
820    where
821        C: AstFormatter,
822    {
823        let tab = ctx.indent();
824        let params = if self.has_explicits() {
825            let params = self.formatted_params(ctx).format(", ");
826            format!("<{}>", params)
827        } else {
828            String::new()
829        };
830        let clauses = if self.has_predicates() {
831            let clauses = self
832                .formatted_clauses(ctx)
833                .map(|x| format!("\n{tab}{TAB_INCR}{x},"))
834                .format("");
835            format!("\n{tab}where{clauses}")
836        } else {
837            String::new()
838        };
839        (params, clauses)
840    }
841
842    pub fn fmt_with_ctx_single_line<C>(&self, ctx: &C) -> String
843    where
844        C: AstFormatter,
845    {
846        if self.is_empty() {
847            String::new()
848        } else {
849            let params = self
850                .formatted_params(ctx)
851                .map(Either::Left)
852                .chain(self.formatted_clauses(ctx).map(Either::Right))
853                .format(", ");
854            format!("<{}>", params)
855        }
856    }
857}
858
859impl_debug_via_display!(GenericParams);
860impl Display for GenericParams {
861    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
862        write!(f, "{}", self.fmt_with_ctx_single_line(&FmtCtx::new()))
863    }
864}
865
866impl<C: AstFormatter> FmtWithCtx<C> for GenericsSource {
867    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
868        match self {
869            GenericsSource::Item(id) => write!(f, "{}", id.with_ctx(ctx)),
870            GenericsSource::Method(id, name) => write!(f, "{}::{name}", id.with_ctx(ctx)),
871            GenericsSource::TraitType(id, name) => {
872                write!(f, "{}::", id.with_ctx(ctx))?;
873                ctx.format_assoc_type_name(f, *id, *name)
874            }
875            GenericsSource::Builtin => write!(f, "<builtin>"),
876            GenericsSource::Other => write!(f, "<unknown>"),
877        }
878    }
879}
880
881impl<T> GExprBody<T> {
882    fn fmt_with_ctx_and_callback<C: AstFormatter>(
883        &self,
884        ctx: &C,
885        f: &mut fmt::Formatter<'_>,
886        fmt_body: impl FnOnce(
887            &mut fmt::Formatter<'_>,
888            &<<C as AstFormatter>::Reborrow<'_> as AstFormatter>::Reborrow<'_>,
889            &T,
890        ) -> fmt::Result,
891    ) -> fmt::Result {
892        // Update the context
893        let ctx = &ctx.set_locals(&self.locals);
894        let ctx = &ctx.increase_indent();
895        let tab = ctx.indent();
896
897        // Format the local variables
898        for v in &self.locals.locals {
899            write!(f, "{tab}")?;
900            write!(f, "let {}: {};", v.index.with_ctx(ctx), v.ty.with_ctx(ctx))?;
901
902            write!(f, " // ")?;
903            if v.index.is_zero() {
904                write!(f, "return")?;
905            } else if self.locals.is_return_or_arg(v.index) {
906                write!(f, "arg #{}", v.index.index())?
907            } else {
908                match &v.name {
909                    Some(_) => write!(f, "local")?,
910                    None => write!(f, "anonymous local")?,
911                }
912            }
913            writeln!(f)?;
914        }
915
916        fmt_body(f, ctx, &self.body)?;
917
918        Ok(())
919    }
920}
921
922impl<C: AstFormatter> FmtWithCtx<C> for GExprBody<llbc_ast::Block> {
923    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
924        // Inference fails when this is a closure.
925        fn fmt_body<C: AstFormatter>(
926            f: &mut fmt::Formatter<'_>,
927            ctx: &<<C as AstFormatter>::Reborrow<'_> as AstFormatter>::Reborrow<'_>,
928            body: &Block,
929        ) -> Result<(), fmt::Error> {
930            writeln!(f)?;
931            body.fmt_with_ctx(ctx, f)?;
932            Ok(())
933        }
934        self.fmt_with_ctx_and_callback(ctx, f, fmt_body::<C>)
935    }
936}
937impl<C: AstFormatter> FmtWithCtx<C> for GExprBody<ullbc_ast::BodyContents> {
938    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
939        // Inference fails when this is a closure.
940        fn fmt_body<C: AstFormatter>(
941            f: &mut fmt::Formatter<'_>,
942            ctx: &<<C as AstFormatter>::Reborrow<'_> as AstFormatter>::Reborrow<'_>,
943            body: &IndexVec<BlockId, BlockData>,
944        ) -> Result<(), fmt::Error> {
945            let tab = ctx.indent();
946            let ctx = &ctx.increase_indent();
947            for (bid, block) in body.iter_enumerated() {
948                writeln!(f)?;
949                writeln!(f, "{tab}bb{}: {{", bid.index())?;
950                writeln!(f, "{}", block.with_ctx(ctx))?;
951                writeln!(f, "{tab}}}")?;
952            }
953            Ok(())
954        }
955        self.fmt_with_ctx_and_callback(ctx, f, fmt_body::<C>)
956    }
957}
958
959impl<C> FmtWithCtx<C> for GlobalDecl
960where
961    C: AstFormatter,
962{
963    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
964        let keyword = match self.global_kind {
965            GlobalKind::Static => "static",
966            GlobalKind::ThreadLocal => "thread_local",
967            GlobalKind::AnonConst | GlobalKind::NamedConst => "const",
968        };
969        self.item_meta
970            .fmt_item_intro(f, ctx, keyword, self.def_id)?;
971
972        // Update the context with the generics
973        let ctx = &ctx.set_generics(&self.generics);
974
975        // Translate the parameters and the trait clauses
976        let (params, preds) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
977
978        // Type
979        let ty = self.ty.with_ctx(ctx);
980        write!(f, "{params}: {ty}")?;
981
982        // Predicates
983        write!(f, "{preds}")?;
984        if self.generics.has_predicates() {
985            writeln!(f)?;
986        }
987        write!(f, " ")?;
988
989        // Value
990        let value = self.value.with_ctx(ctx);
991        write!(f, "= {value}")?;
992
993        Ok(())
994    }
995}
996
997impl<C: AstFormatter> FmtWithCtx<C> for GlobalDeclId {
998    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
999        ItemId::from(*self).fmt_with_ctx(ctx, f)
1000    }
1001}
1002
1003impl<C: AstFormatter> FmtWithCtx<C> for GlobalDeclRef {
1004    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1005        let id = self.id.with_ctx(ctx);
1006        let generics = self.generics.with_ctx(ctx);
1007        write!(f, "{id}{generics}")
1008    }
1009}
1010
1011impl<C: AstFormatter> FmtWithCtx<C> for ImplElem {
1012    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1013        write!(f, "{{")?;
1014        match self {
1015            ImplElem::Ty(bound_ty) => {
1016                // Just printing the generics (not the predicates)
1017                let ctx = ctx.set_generics(&bound_ty.params);
1018                bound_ty.skip_binder.fmt_with_ctx(&ctx, f)?
1019            }
1020            ImplElem::Trait(impl_id) => {
1021                match ctx.get_crate().and_then(|tr| tr.trait_impls.get(*impl_id)) {
1022                    None => write!(f, "impl#{impl_id}")?,
1023                    Some(timpl) => {
1024                        // We need to put the first type parameter aside: it is the type for which
1025                        // we implement the trait.
1026                        let ctx = &ctx.set_generics(&timpl.generics);
1027                        let mut impl_trait = timpl.impl_trait.clone();
1028                        match impl_trait
1029                            .generics
1030                            .types
1031                            .remove_and_shift_ids(TypeVarId::ZERO)
1032                        {
1033                            Some(self_ty) => {
1034                                let self_ty = self_ty.with_ctx(ctx);
1035                                let impl_trait = impl_trait.with_ctx(ctx);
1036                                write!(f, "impl {impl_trait} for {self_ty}")?;
1037                            }
1038                            // TODO(mono): A monomorphized trait doesn't take arguments.
1039                            None => {
1040                                let impl_trait = impl_trait.with_ctx(ctx);
1041                                write!(f, "impl {impl_trait}")?;
1042                            }
1043                        }
1044                    }
1045                }
1046            }
1047        }
1048        write!(f, "}}")
1049    }
1050}
1051
1052impl Display for IntTy {
1053    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1054        match self {
1055            IntTy::Isize => write!(f, "isize"),
1056            IntTy::I8 => write!(f, "i8"),
1057            IntTy::I16 => write!(f, "i16"),
1058            IntTy::I32 => write!(f, "i32"),
1059            IntTy::I64 => write!(f, "i64"),
1060            IntTy::I128 => write!(f, "i128"),
1061        }
1062    }
1063}
1064
1065impl Display for UIntTy {
1066    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1067        match self {
1068            UIntTy::Usize => write!(f, "usize"),
1069            UIntTy::U8 => write!(f, "u8"),
1070            UIntTy::U16 => write!(f, "u16"),
1071            UIntTy::U32 => write!(f, "u32"),
1072            UIntTy::U64 => write!(f, "u64"),
1073            UIntTy::U128 => write!(f, "u128"),
1074        }
1075    }
1076}
1077
1078impl Display for IntegerTy {
1079    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1080        match self {
1081            IntegerTy::Signed(int_ty) => write!(f, "{int_ty}"),
1082            IntegerTy::Unsigned(uint_ty) => write!(f, "{uint_ty}"),
1083        }
1084    }
1085}
1086
1087fn trait_impl_short_name<C: AstFormatter>(ctx: &C, impl_id: TraitImplId) -> Option<&Name> {
1088    ctx.get_crate()
1089        .and_then(|tr| tr.short_names.get(&ItemId::TraitImpl(impl_id)))
1090        .filter(|name| matches!(name.name.first(), Some(PathElem::Ident(..))))
1091}
1092
1093impl ItemMeta {
1094    /// Format the start of an item definition, up to the name.
1095    pub fn fmt_item_intro<C: AstFormatter>(
1096        &self,
1097        f: &mut fmt::Formatter<'_>,
1098        ctx: &C,
1099        keyword: &str,
1100        id: impl Into<ItemId>,
1101    ) -> fmt::Result {
1102        let tab = ctx.indent();
1103        let id = id.into();
1104        let mut name = &self.name;
1105        let mut name_is_full = true;
1106        if let Some(tr) = ctx.get_crate()
1107            && let Some(short_name) = tr.short_names.get(&id)
1108        {
1109            name = short_name;
1110            name_is_full = false;
1111        } else if self
1112            .name
1113            .name
1114            .iter()
1115            .filter_map(|ne| ne.as_impl()?.as_trait())
1116            .any(|impl_id| trait_impl_short_name(ctx, *impl_id).is_some())
1117        {
1118            name_is_full = false;
1119        };
1120        if !name_is_full {
1121            writeln!(f, "// Full name: {}", self.name.full_name(ctx))?;
1122        }
1123
1124        if let Some(id) = &self.lang_item {
1125            writeln!(f, "{tab}#[lang_item(\"{id}\")]")?;
1126        }
1127        write!(f, "{tab}")?;
1128        if self.attr_info.public {
1129            write!(f, "pub ")?;
1130        }
1131        write!(f, "{keyword} {}", name.with_ctx(ctx))
1132    }
1133}
1134
1135impl Display for Literal {
1136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1137        match self {
1138            Literal::Scalar(v) => write!(f, "{v}"),
1139            Literal::Float(v) => write!(f, "{v}"),
1140            Literal::Bool(v) => write!(f, "{v}"),
1141            Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
1142            Literal::Str(v) => write!(f, "\"{}\"", v.replace("\\", "\\\\").replace("\n", "\\n")),
1143            Literal::ByteStr(v) => write!(f, "{v:?}"),
1144        }
1145    }
1146}
1147
1148impl Display for LiteralTy {
1149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1150        match self {
1151            LiteralTy::Int(ty) => write!(f, "{ty}"),
1152            LiteralTy::UInt(ty) => write!(f, "{ty}"),
1153            LiteralTy::Float(ty) => write!(f, "{ty}"),
1154            LiteralTy::Char => write!(f, "char"),
1155            LiteralTy::Bool => write!(f, "bool"),
1156        }
1157    }
1158}
1159
1160impl Display for Loc {
1161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1162        write!(f, "{}:{}", self.line, self.col)
1163    }
1164}
1165
1166impl Display for Local {
1167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1168        // We display both the variable name and its id because some
1169        // variables may have the same name (in different scopes)
1170        if let Some(name) = &self.name {
1171            write!(f, "{name}")?
1172        }
1173        write!(f, "_{}", self.index)?;
1174        Ok(())
1175    }
1176}
1177
1178impl<C: AstFormatter> FmtWithCtx<C> for LocalId {
1179    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1180        ctx.format_local_id(f, *self)
1181    }
1182}
1183
1184impl<C: AstFormatter> FmtWithCtx<C> for Name {
1185    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1186        // Reset generics to avoid names being displayed differently depending on the current
1187        // binding level.
1188        let ctx = &ctx.no_generics();
1189        let name = self.name.iter().map(|x| x.with_ctx(ctx)).format("::");
1190        write!(f, "{}", name)
1191    }
1192}
1193
1194impl Name {
1195    /// Print the full name, which is different from printing a `Name` since that will use the
1196    /// short name for impls.
1197    fn full_name<'a, C: AstFormatter + 'a>(&'a self, ctx: &'a C) -> impl Display + 'a {
1198        std::fmt::from_fn(move |f| {
1199            let ctx = &ctx.no_generics();
1200            let name = self
1201                .name
1202                .iter()
1203                .map(|elem| match elem {
1204                    PathElem::Impl(impl_elem) => Either::Left(impl_elem.with_ctx(ctx)),
1205                    _ => Either::Right(elem.with_ctx(ctx)),
1206                })
1207                .format("::");
1208            write!(f, "{name}")
1209        })
1210    }
1211}
1212
1213impl<C: AstFormatter> FmtWithCtx<C> for NullOp {
1214    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1215        let op = match self {
1216            NullOp::SizeOf => "size_of",
1217            NullOp::AlignOf => "align_of",
1218            &NullOp::OffsetOf(ref ty, variant, field) => {
1219                let tid = *ty.id.as_adt().expect("found offset_of of a non-adt type");
1220                write!(f, "offset_of({}.", ty.with_ctx(ctx))?;
1221                if let Some(variant) = variant {
1222                    ctx.format_enum_variant_name(f, tid, variant)?;
1223                    write!(f, ".")?;
1224                }
1225                ctx.format_field_name(f, tid, variant, field)?;
1226                write!(f, ")")?;
1227                return Ok(());
1228            }
1229            NullOp::UbChecks => "ub_checks",
1230            NullOp::OverflowChecks => "overflow_checks",
1231            NullOp::ContractChecks => "contract_checks",
1232        };
1233        write!(f, "{op}")
1234    }
1235}
1236
1237impl_display_via_ctx!(Operand);
1238impl<C: AstFormatter> FmtWithCtx<C> for Operand {
1239    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1240        match self {
1241            Operand::Copy(p) => write!(f, "copy {}", p.with_ctx(ctx)),
1242            Operand::Move(p) => write!(f, "move {}", p.with_ctx(ctx)),
1243            Operand::Const(c) => write!(f, "const {}", c.with_ctx(ctx)),
1244        }
1245    }
1246}
1247
1248impl<C: AstFormatter, T, U> FmtWithCtx<C> for OutlivesPred<T, U>
1249where
1250    T: FmtWithCtx<C>,
1251    U: FmtWithCtx<C>,
1252{
1253    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1254        write!(f, "{}: {}", self.0.with_ctx(ctx), self.1.with_ctx(ctx))
1255    }
1256}
1257
1258impl<C: AstFormatter> FmtWithCtx<C> for PathElem {
1259    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1260        match self {
1261            PathElem::Ident(s, d) => {
1262                write!(f, "{s}")?;
1263                if !d.is_zero() {
1264                    write!(f, "#{}", d)?;
1265                }
1266                Ok(())
1267            }
1268            PathElem::Impl(impl_elem) => {
1269                if let ImplElem::Trait(impl_id) = impl_elem
1270                    && let Some(short_name) = trait_impl_short_name(ctx, *impl_id)
1271                {
1272                    return write!(f, "{}", short_name.with_ctx(ctx));
1273                }
1274                write!(f, "{}", impl_elem.with_ctx(ctx))
1275            }
1276            PathElem::Instantiated(binder) => {
1277                // Anonymize all parameters.
1278                let underscore = "_".to_string();
1279                let params = GenericParams {
1280                    regions: binder.params.regions.map_ref(|x| RegionParam {
1281                        name: Some(underscore.clone()),
1282                        ..*x
1283                    }),
1284                    types: binder.params.types.map_ref(|x| TypeParam {
1285                        name: underscore.clone(),
1286                        ..*x
1287                    }),
1288                    const_generics: binder.params.const_generics.map_ref(|x| ConstGenericParam {
1289                        name: underscore.clone(),
1290                        ty: x.ty.clone(),
1291                        index: x.index,
1292                    }),
1293                    trait_clauses: binder.params.trait_clauses.clone(),
1294                    ..GenericParams::empty()
1295                };
1296                let ctx = &ctx.push_binder(Cow::Owned(params));
1297                write!(
1298                    f,
1299                    "<{}>",
1300                    binder.skip_binder.fmt_explicits(ctx).format(", ")
1301                )
1302            }
1303            PathElem::Target(target) => write!(f, "{target}"),
1304        }
1305    }
1306}
1307
1308impl_display_via_ctx!(Place);
1309impl<C: AstFormatter> FmtWithCtx<C> for Place {
1310    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1311        match &self.kind {
1312            PlaceKind::Local(var_id) => write!(f, "{}", var_id.with_ctx(ctx)),
1313            PlaceKind::Global(global_ref) => global_ref.fmt_with_ctx(ctx, f),
1314            PlaceKind::Projection(subplace, projection) => {
1315                let sub = subplace.with_ctx(ctx);
1316                match projection {
1317                    ProjectionElem::Deref => {
1318                        write!(f, "(*{sub})")
1319                    }
1320                    ProjectionElem::Field(proj_kind, field_id) => match proj_kind {
1321                        FieldProjKind::Adt(adt_id, opt_variant_id) => {
1322                            write!(f, "({sub}")?;
1323                            if let Some(variant_id) = opt_variant_id {
1324                                write!(f, " as variant ")?;
1325                                ctx.format_enum_variant(f, *adt_id, *variant_id)?;
1326                            }
1327                            write!(f, ").")?;
1328                            ctx.format_field_name(f, *adt_id, *opt_variant_id, *field_id)?;
1329                            Ok(())
1330                        }
1331                        FieldProjKind::Tuple(_) => {
1332                            write!(f, "{sub}.{field_id}")
1333                        }
1334                    },
1335                    ProjectionElem::PtrMetadata => {
1336                        write!(f, "{sub}.metadata")
1337                    }
1338                    ProjectionElem::Index {
1339                        offset,
1340                        from_end: true,
1341                        ..
1342                    } => write!(f, "{sub}[-{}]", offset.with_ctx(ctx)),
1343                    ProjectionElem::Index {
1344                        offset,
1345                        from_end: false,
1346                        ..
1347                    } => write!(f, "{sub}[{}]", offset.with_ctx(ctx)),
1348                    ProjectionElem::Subslice {
1349                        from,
1350                        to,
1351                        from_end: true,
1352                        ..
1353                    } => write!(f, "{sub}[{}..-{}]", from.with_ctx(ctx), to.with_ctx(ctx)),
1354                    ProjectionElem::Subslice {
1355                        from,
1356                        to,
1357                        from_end: false,
1358                        ..
1359                    } => write!(f, "{sub}[{}..{}]", from.with_ctx(ctx), to.with_ctx(ctx)),
1360                }
1361            }
1362        }
1363    }
1364}
1365
1366impl<C: AstFormatter> FmtWithCtx<C> for PolyTraitDeclRef {
1367    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1368        write!(f, "{}", self.fmt_as_for(ctx))
1369    }
1370}
1371
1372impl PolyTraitDeclRef {
1373    fn fmt_trait_proof<'a, C: AstFormatter + 'a>(
1374        &'a self,
1375        id: TraitClauseId,
1376        value: Option<&'a TraitRef>,
1377        ctx: &'a C,
1378    ) -> impl Display + 'a {
1379        std::fmt::from_fn(move |f| {
1380            write!(
1381                f,
1382                "proof {}: {}",
1383                id.format_as_implied(),
1384                self.format_as_pred(ctx)
1385            )?;
1386            if let Some(value) = value {
1387                write!(f, " = {}", value.with_ctx(ctx))?;
1388            }
1389            Ok(())
1390        })
1391    }
1392
1393    fn format_as_pred<'a, C: AstFormatter + 'a>(&'a self, ctx: &'a C) -> impl Display + 'a {
1394        std::fmt::from_fn(move |f| {
1395            let ctx = &ctx.push_bound_regions(&self.regions);
1396            if !self.regions.is_empty() {
1397                let regions = self.regions.iter().map(|r| r.with_ctx(ctx));
1398                write!(f, "for<{}> ", regions.format(", "))?;
1399            }
1400            write!(f, "({})", self.skip_binder.format_as_pred(ctx))
1401        })
1402    }
1403}
1404
1405impl Display for RawAttribute {
1406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1407        write!(f, "{}", self.path)?;
1408        if let Some(args) = &self.args {
1409            write!(f, "({args})")?;
1410        }
1411        Ok(())
1412    }
1413}
1414
1415impl<C: AstFormatter> FmtWithCtx<C> for Byte {
1416    fn fmt_with_ctx(&self, _ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1417        match self {
1418            Byte::Value(x) => write!(f, "{:#4x}", x),
1419            Byte::Uninit => write!(f, "--"),
1420            Byte::Provenance(p, ofs) => write!(f, "{:?}[{}]", p, ofs),
1421        }
1422    }
1423}
1424
1425impl_display_via_ctx!(ConstantExpr);
1426impl<C: AstFormatter> FmtWithCtx<C> for ConstantExpr {
1427    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1428        match &self.kind {
1429            ConstantExprKind::Literal(c) => write!(f, "{}", c),
1430            ConstantExprKind::Adt(variant_id, values) => {
1431                let values = values.iter().map(|v| v.with_ctx(ctx));
1432                match self.ty.as_adt() {
1433                    Some(ty_ref) => match ty_ref.id {
1434                        TypeId::Tuple => {
1435                            let trailing_comma = if values.len() == 1 { "," } else { "" };
1436                            let values = values.format(", ");
1437                            write!(f, "({values}{trailing_comma})")
1438                        }
1439                        TypeId::Builtin(BuiltinTy::Box) => {
1440                            let values = values.format(", ");
1441                            write!(f, "Box({values})")
1442                        }
1443                        TypeId::Builtin(BuiltinTy::Str) => {
1444                            let values = values.format(", ");
1445                            write!(f, "[{values}]")
1446                        }
1447                        TypeId::Adt(ty_id) => {
1448                            match variant_id {
1449                                None => ty_id.fmt_with_ctx(ctx, f)?,
1450                                Some(variant_id) => {
1451                                    ctx.format_enum_variant(f, ty_id, *variant_id)?
1452                                }
1453                            }
1454                            write!(f, " {{ ")?;
1455                            for (comma, (i, val)) in
1456                                repeat_except_first(", ").zip(values.enumerate())
1457                            {
1458                                write!(f, "{}", comma.unwrap_or_default())?;
1459                                let field_id = FieldId::new(i);
1460                                ctx.format_field_name(f, ty_id, *variant_id, field_id)?;
1461                                write!(f, ": {}", val)?;
1462                            }
1463                            write!(f, " }}")
1464                        }
1465                    },
1466                    None => {
1467                        let values = values.format(", ");
1468                        write!(f, "ConstAdt [{values}]")
1469                    }
1470                }
1471            }
1472            ConstantExprKind::Array(values) => {
1473                let values = values.iter().map(|v| v.with_ctx(ctx)).format(", ");
1474                write!(f, "[{}]", values)
1475            }
1476            ConstantExprKind::Global(global_ref) => {
1477                write!(f, "{}", global_ref.with_ctx(ctx))
1478            }
1479            ConstantExprKind::TraitConst(trait_ref, const_id) => {
1480                write!(f, "{}::", trait_ref.with_ctx(ctx),)?;
1481                ctx.format_assoc_const_name(f, trait_ref.trait_id(), *const_id)?;
1482                Ok(())
1483            }
1484            ConstantExprKind::VTableRef(trait_ref) => {
1485                write!(f, "&vtable_of({})", trait_ref.with_ctx(ctx),)
1486            }
1487            ConstantExprKind::Ref(cv, meta) => {
1488                if let Some(meta) = meta {
1489                    write!(
1490                        f,
1491                        "&{} with_metadata({})",
1492                        cv.with_ctx(ctx),
1493                        meta.with_ctx(ctx)
1494                    )
1495                } else {
1496                    write!(f, "&{}", cv.with_ctx(ctx))
1497                }
1498            }
1499            ConstantExprKind::Ptr(rk, cv, meta) => {
1500                let rk = match rk {
1501                    RefKind::Mut => "&raw mut",
1502                    RefKind::Shared => "&raw const",
1503                };
1504                if let Some(meta) = meta {
1505                    write!(
1506                        f,
1507                        "{} {} with_metadata({})",
1508                        rk,
1509                        cv.with_ctx(ctx),
1510                        meta.with_ctx(ctx)
1511                    )
1512                } else {
1513                    write!(f, "{} {}", rk, cv.with_ctx(ctx))
1514                }
1515            }
1516            ConstantExprKind::Var(id) => write!(f, "{}", id.with_ctx(ctx)),
1517            ConstantExprKind::Call(fp, args) => {
1518                let args = args.iter().map(|arg| arg.with_ctx(ctx)).format(", ");
1519                write!(f, "{}({args})", fp.with_ctx(ctx))
1520            }
1521            ConstantExprKind::FnDef(fp) => {
1522                write!(f, "{}", fp.with_ctx(ctx))
1523            }
1524            ConstantExprKind::FnPtr(fp) => {
1525                write!(f, "fnptr({})", fp.with_ctx(ctx))
1526            }
1527            ConstantExprKind::TypeId(ty) => {
1528                write!(f, "TypeId({})", ty.with_ctx(ctx))
1529            }
1530            ConstantExprKind::PtrNoProvenance(v) => write!(f, "no-provenance {v}"),
1531            ConstantExprKind::RawMemory(bytes) => {
1532                let bytes = bytes.iter().map(|v| v.with_ctx(ctx)).format(", ");
1533                write!(f, "RawMemory({})", bytes)
1534            }
1535            ConstantExprKind::Opaque(cause) => write!(f, "Opaque({cause})"),
1536        }
1537    }
1538}
1539
1540impl<C: AstFormatter> FmtWithCtx<C> for Region {
1541    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1542        match self {
1543            Region::Static => write!(f, "'static"),
1544            Region::Var(var) => write!(f, "{}", var.with_ctx(ctx)),
1545            Region::Body(id) => write!(f, "'{}", id),
1546            Region::Erased => write!(f, "'_"),
1547        }
1548    }
1549}
1550
1551impl<T> RegionBinder<T> {
1552    /// Format the parameters and contents of this binder and returns the resulting strings.
1553    fn fmt_split<'a, C>(&'a self, ctx: &'a C) -> (String, String)
1554    where
1555        C: AstFormatter,
1556        T: FmtWithCtx<C::Reborrow<'a>>,
1557    {
1558        self.fmt_split_with(ctx, |ctx, x| x.to_string_with_ctx(ctx))
1559    }
1560    /// Format the parameters and contents of this binder and returns the resulting strings.
1561    fn fmt_split_with<'a, C>(
1562        &'a self,
1563        ctx: &'a C,
1564        fmt_inner: impl FnOnce(&C::Reborrow<'a>, &T) -> String,
1565    ) -> (String, String)
1566    where
1567        C: AstFormatter,
1568    {
1569        let ctx = &ctx.push_bound_regions(&self.regions);
1570        (
1571            self.regions
1572                .iter()
1573                .map(|r| r.with_ctx(ctx))
1574                .format(", ")
1575                .to_string(),
1576            fmt_inner(ctx, &self.skip_binder),
1577        )
1578    }
1579
1580    /// Formats the binder as `for<params> value`.
1581    fn fmt_as_for<'a, C>(&'a self, ctx: &'a C) -> String
1582    where
1583        C: AstFormatter,
1584        T: FmtWithCtx<C::Reborrow<'a>>,
1585    {
1586        self.fmt_as_for_with(ctx, |ctx, x| x.to_string_with_ctx(ctx))
1587    }
1588    /// Formats the binder as `for<params> value`.
1589    fn fmt_as_for_with<'a, C>(
1590        &'a self,
1591        ctx: &'a C,
1592        fmt_inner: impl FnOnce(&C::Reborrow<'a>, &T) -> String,
1593    ) -> String
1594    where
1595        C: AstFormatter,
1596        T: FmtWithCtx<C::Reborrow<'a>>,
1597    {
1598        let (regions, value) = self.fmt_split_with(ctx, fmt_inner);
1599        let regions = if regions.is_empty() {
1600            "".to_string()
1601        } else {
1602            format!("for<{regions}> ",)
1603        };
1604        format!("{regions}{value}",)
1605    }
1606}
1607
1608impl<C: AstFormatter> FmtWithCtx<C> for RegionDbVar {
1609    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1610        ctx.format_bound_var(f, *self, "'_", |v| {
1611            v.name.as_ref().map(|name| name.to_string())
1612        })
1613    }
1614}
1615
1616impl<C: AstFormatter> FmtWithCtx<C> for RegionParam {
1617    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1618        if self.mutability.is_mutable() {
1619            write!(f, "mut ")?;
1620        }
1621        match &self.name {
1622            Some(name) => write!(f, "{name}"),
1623            None => {
1624                write!(f, "'_{}", self.index)?;
1625                if let Some(d @ 1..) = ctx.binder_depth().checked_sub(1) {
1626                    write!(f, "_{d}")?;
1627                }
1628                Ok(())
1629            }
1630        }
1631    }
1632}
1633
1634impl_display_via_ctx!(Rvalue);
1635impl<C: AstFormatter> FmtWithCtx<C> for Rvalue {
1636    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1637        match self {
1638            Rvalue::Use(x, _) => write!(f, "{}", x.with_ctx(ctx)),
1639            Rvalue::Ref {
1640                place,
1641                kind: borrow_kind,
1642                ptr_metadata,
1643            } => {
1644                let borrow_kind = match borrow_kind {
1645                    BorrowKind::Shared => "&",
1646                    BorrowKind::Mut => "&mut ",
1647                    BorrowKind::TwoPhaseMut => "&two-phase-mut ",
1648                    BorrowKind::UniqueImmutable => "&uniq ",
1649                    BorrowKind::Shallow => "&shallow ",
1650                };
1651                if ptr_metadata.ty().is_unit() {
1652                    // Hide unit metadata
1653                    write!(f, "{borrow_kind}{}", place.with_ctx(ctx))?;
1654                } else {
1655                    write!(
1656                        f,
1657                        "{borrow_kind}{} with_metadata({})",
1658                        place.with_ctx(ctx),
1659                        ptr_metadata.with_ctx(ctx)
1660                    )?;
1661                }
1662                Ok(())
1663            }
1664            Rvalue::RawPtr {
1665                place,
1666                kind: mutability,
1667                ptr_metadata,
1668            } => {
1669                let ptr_kind = match mutability {
1670                    RefKind::Shared => "&raw const ",
1671                    RefKind::Mut => "&raw mut ",
1672                };
1673                if ptr_metadata.ty().is_unit() {
1674                    // Hide unit metadata
1675                    write!(f, "{ptr_kind}{}", place.with_ctx(ctx))?;
1676                } else {
1677                    write!(
1678                        f,
1679                        "{ptr_kind}{} with_metadata({})",
1680                        place.with_ctx(ctx),
1681                        ptr_metadata.with_ctx(ctx)
1682                    )?;
1683                }
1684                Ok(())
1685            }
1686
1687            Rvalue::BinaryOp(binop, x, y) => {
1688                write!(f, "{} {} {}", x.with_ctx(ctx), binop, y.with_ctx(ctx))
1689            }
1690            Rvalue::UnaryOp(unop, x) => {
1691                write!(f, "{}({})", unop.with_ctx(ctx), x.with_ctx(ctx))
1692            }
1693            Rvalue::NullaryOp(op, ty) => {
1694                write!(f, "{}<{}>", op.with_ctx(ctx), ty.with_ctx(ctx))
1695            }
1696            Rvalue::Discriminant(p) => {
1697                write!(f, "@discriminant({})", p.with_ctx(ctx),)
1698            }
1699            Rvalue::Aggregate(kind, ops) => {
1700                let ops_s = ops.iter().map(|op| op.with_ctx(ctx)).format(", ");
1701                match kind {
1702                    AggregateKind::Adt(ty_ref, variant_id, field_id) => {
1703                        match ty_ref.id {
1704                            TypeId::Tuple => {
1705                                let trailing_comma = if ops.len() == 1 { "," } else { "" };
1706                                write!(f, "({ops_s}{trailing_comma})")
1707                            }
1708                            TypeId::Builtin(BuiltinTy::Box) => write!(f, "Box({})", ops_s),
1709                            TypeId::Builtin(BuiltinTy::Str) => {
1710                                write!(f, "[{}]", ops_s)
1711                            }
1712                            TypeId::Adt(ty_id) => {
1713                                match variant_id {
1714                                    None => ty_id.fmt_with_ctx(ctx, f)?,
1715                                    Some(variant_id) => {
1716                                        ctx.format_enum_variant(f, ty_id, *variant_id)?
1717                                    }
1718                                }
1719                                write!(f, " {{ ")?;
1720                                for (comma, (i, op)) in
1721                                    repeat_except_first(", ").zip(ops.iter().enumerate())
1722                                {
1723                                    write!(f, "{}", comma.unwrap_or_default())?;
1724                                    let field_id = match *field_id {
1725                                        None => FieldId::new(i),
1726                                        Some(field_id) => {
1727                                            assert_eq!(i, 0); // there should be only one operand
1728                                            field_id
1729                                        }
1730                                    };
1731                                    ctx.format_field_name(f, ty_id, *variant_id, field_id)?;
1732                                    write!(f, ": {}", op.with_ctx(ctx))?;
1733                                }
1734                                write!(f, " }}")
1735                            }
1736                        }
1737                    }
1738                    AggregateKind::Array(..) => {
1739                        write!(f, "[{}]", ops_s)
1740                    }
1741                    AggregateKind::RawPtr(_, rmut) => {
1742                        let mutability = match rmut {
1743                            RefKind::Shared => "const",
1744                            RefKind::Mut => "mut ",
1745                        };
1746                        write!(f, "*{} ({})", mutability, ops_s)
1747                    }
1748                }
1749            }
1750            Rvalue::Len(place, ..) => write!(f, "len({})", place.with_ctx(ctx)),
1751            Rvalue::Repeat(op, _ty, cg) => {
1752                write!(f, "[{}; {}]", op.with_ctx(ctx), cg.with_ctx(ctx))
1753            }
1754        }
1755    }
1756}
1757
1758impl Display for ScalarValue {
1759    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1760        match self {
1761            ScalarValue::Signed(ty, v) => write!(f, "{v}{ty}"),
1762            ScalarValue::Unsigned(ty, v) => write!(f, "{v}{ty}"),
1763        }
1764    }
1765}
1766
1767impl<C: AstFormatter> FmtWithCtx<C> for ullbc::Statement {
1768    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1769        let tab = ctx.indent();
1770        use ullbc::StatementKind;
1771        for line in &self.comments_before {
1772            writeln!(f, "{tab}// {line}")?;
1773        }
1774        match &self.kind {
1775            StatementKind::Assign(place, rvalue) => {
1776                write!(f, "{tab}{} = {}", place.with_ctx(ctx), rvalue.with_ctx(ctx),)
1777            }
1778            StatementKind::SetDiscriminant(place, variant_id) => write!(
1779                f,
1780                "{tab}@discriminant({}) = {}",
1781                place.with_ctx(ctx),
1782                variant_id
1783            ),
1784            StatementKind::CopyNonOverlapping(cno) => {
1785                write!(
1786                    f,
1787                    "{}copy_nonoverlapping({}, {}, {})",
1788                    tab,
1789                    cno.src.with_ctx(ctx),
1790                    cno.dst.with_ctx(ctx),
1791                    cno.count.with_ctx(ctx),
1792                )
1793            }
1794            StatementKind::StorageLive(var_id) => {
1795                write!(f, "{tab}storage_live({})", var_id.with_ctx(ctx))
1796            }
1797            StatementKind::StorageDead(var_id) => {
1798                write!(f, "{tab}storage_dead({})", var_id.with_ctx(ctx))
1799            }
1800            StatementKind::PlaceMention(place) => {
1801                write!(f, "{tab}_ = {}", place.with_ctx(ctx))
1802            }
1803            StatementKind::Assert { assert, on_failure } => {
1804                write!(
1805                    f,
1806                    "{tab}{} else {}",
1807                    assert.with_ctx(ctx),
1808                    on_failure.with_ctx(ctx)
1809                )
1810            }
1811            StatementKind::Nop => write!(f, "{tab}nop"),
1812        }
1813    }
1814}
1815
1816impl<C: AstFormatter> FmtWithCtx<C> for llbc::Statement {
1817    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1818        let tab = ctx.indent();
1819        use llbc::StatementKind;
1820        for line in &self.comments_before {
1821            writeln!(f, "{tab}// {line}")?;
1822        }
1823        write!(f, "{tab}")?;
1824        match &self.kind {
1825            StatementKind::Assign(place, rvalue) => {
1826                write!(f, "{} = {}", place.with_ctx(ctx), rvalue.with_ctx(ctx),)
1827            }
1828            StatementKind::SetDiscriminant(place, variant_id) => {
1829                write!(f, "@discriminant({}) = {}", place.with_ctx(ctx), variant_id)
1830            }
1831            StatementKind::CopyNonOverlapping(cno) => {
1832                write!(
1833                    f,
1834                    "copy_nonoverlapping({}, {}, {})",
1835                    cno.src.with_ctx(ctx),
1836                    cno.dst.with_ctx(ctx),
1837                    cno.count.with_ctx(ctx),
1838                )
1839            }
1840            StatementKind::StorageLive(var_id) => {
1841                write!(f, "storage_live({})", var_id.with_ctx(ctx))
1842            }
1843            StatementKind::StorageDead(var_id) => {
1844                write!(f, "storage_dead({})", var_id.with_ctx(ctx))
1845            }
1846            StatementKind::PlaceMention(place) => {
1847                write!(f, "_ = {}", place.with_ctx(ctx))
1848            }
1849            StatementKind::Drop(place, tref, kind) => {
1850                let kind = match kind {
1851                    DropKind::Precise => "drop",
1852                    DropKind::Conditional => "conditional_drop",
1853                };
1854                write!(f, "{kind}[{}] {}", tref.with_ctx(ctx), place.with_ctx(ctx),)
1855            }
1856            StatementKind::Assert { assert, on_failure } => {
1857                write!(
1858                    f,
1859                    "{} else {}",
1860                    assert.with_ctx(ctx),
1861                    on_failure.with_ctx(ctx)
1862                )
1863            }
1864            StatementKind::InlineAsm { asm, targets } => {
1865                write!(f, "asm!({asm:?})")?;
1866                if let [target] = targets.as_slice() {
1867                    for statement in &target.statements {
1868                        write!(f, "\n{}", statement.with_ctx(ctx))?;
1869                    }
1870                    return Ok(());
1871                }
1872                if !targets.is_empty() {
1873                    write!(f, " {{")?;
1874                    let ctx1 = &ctx.increase_indent();
1875                    let tab1 = ctx1.indent();
1876                    let ctx2 = &ctx1.increase_indent();
1877                    for (i, target) in targets.iter().enumerate() {
1878                        write!(
1879                            f,
1880                            "\n{tab1}target {i} => {{\n{}{tab1}}}",
1881                            target.with_ctx(ctx2)
1882                        )?;
1883                    }
1884                    write!(f, "\n{tab}}}")?;
1885                }
1886                Ok(())
1887            }
1888            StatementKind::Call(call) => {
1889                write!(f, "{}", call.with_ctx(ctx))
1890            }
1891            StatementKind::Abort(kind) => {
1892                write!(f, "{}", kind.with_ctx(ctx))
1893            }
1894            StatementKind::Return => write!(f, "return"),
1895            StatementKind::Break(index) => write!(f, "break {index}"),
1896            StatementKind::Continue(index) => write!(f, "continue {index}"),
1897            StatementKind::Nop => write!(f, "nop"),
1898            StatementKind::Switch(switch) => match switch {
1899                Switch::If(discr, true_st, false_st) => {
1900                    let ctx = &ctx.increase_indent();
1901                    write!(
1902                        f,
1903                        "if {} {{\n{}{tab}}} else {{\n{}{tab}}}",
1904                        discr.with_ctx(ctx),
1905                        true_st.with_ctx(ctx),
1906                        false_st.with_ctx(ctx),
1907                    )
1908                }
1909                Switch::SwitchInt(discr, _ty, maps, otherwise) => {
1910                    writeln!(f, "switch {} {{", discr.with_ctx(ctx))?;
1911                    let ctx1 = &ctx.increase_indent();
1912                    let inner_tab1 = ctx1.indent();
1913                    let ctx2 = &ctx1.increase_indent();
1914                    for (pvl, st) in maps {
1915                        // Note that there may be several pattern values
1916                        let pvl = pvl.iter().format(" | ");
1917                        writeln!(
1918                            f,
1919                            "{inner_tab1}{} => {{\n{}{inner_tab1}}},",
1920                            pvl,
1921                            st.with_ctx(ctx2),
1922                        )?;
1923                    }
1924                    writeln!(
1925                        f,
1926                        "{inner_tab1}_ => {{\n{}{inner_tab1}}},",
1927                        otherwise.with_ctx(ctx2),
1928                    )?;
1929                    write!(f, "{tab}}}")
1930                }
1931                Switch::Match(discr, maps, otherwise) => {
1932                    writeln!(f, "match {} {{", discr.with_ctx(ctx))?;
1933                    let ctx1 = &ctx.increase_indent();
1934                    let inner_tab1 = ctx1.indent();
1935                    let ctx2 = &ctx1.increase_indent();
1936                    let discr_type: Option<TypeDeclId> = discr
1937                        .ty
1938                        .kind()
1939                        .as_adt()
1940                        .and_then(|tref| tref.id.as_adt())
1941                        .copied();
1942                    for (cases, st) in maps {
1943                        write!(f, "{inner_tab1}",)?;
1944                        // Note that there may be several pattern values
1945                        for (bar, v) in repeat_except_first(" | ").zip(cases.iter()) {
1946                            write!(f, "{}", bar.unwrap_or_default())?;
1947                            match discr_type {
1948                                Some(type_id) => ctx.format_enum_variant(f, type_id, *v)?,
1949                                None => write!(f, "{}", v.to_pretty_string())?,
1950                            }
1951                        }
1952                        writeln!(f, " => {{\n{}{inner_tab1}}},", st.with_ctx(ctx2),)?;
1953                    }
1954                    if let Some(otherwise) = otherwise {
1955                        writeln!(
1956                            f,
1957                            "{inner_tab1}_ => {{\n{}{inner_tab1}}},",
1958                            otherwise.with_ctx(ctx2),
1959                        )?;
1960                    }
1961                    write!(f, "{tab}}}")
1962                }
1963            },
1964            StatementKind::Loop(body) => {
1965                let ctx = &ctx.increase_indent();
1966                write!(f, "loop {{\n{}{tab}}}", body.with_ctx(ctx))
1967            }
1968            StatementKind::Error(s) => write!(f, "@ERROR({})", s),
1969        }
1970    }
1971}
1972
1973impl<C: AstFormatter> FmtWithCtx<C> for Terminator {
1974    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1975        let tab = ctx.indent();
1976        for line in &self.comments_before {
1977            writeln!(f, "{tab}// {line}")?;
1978        }
1979        write!(f, "{tab}")?;
1980        match &self.kind {
1981            TerminatorKind::Goto { target } => write!(f, "goto bb{target}"),
1982            TerminatorKind::Switch { discr, targets } => match targets {
1983                SwitchTargets::If(true_block, false_block) => write!(
1984                    f,
1985                    "if {} -> bb{} else -> bb{}",
1986                    discr.with_ctx(ctx),
1987                    true_block,
1988                    false_block
1989                ),
1990                SwitchTargets::SwitchInt(_ty, maps, otherwise) => {
1991                    let maps = maps
1992                        .iter()
1993                        .map(|(v, bid)| format!("{}: bb{}", v, bid))
1994                        .chain([format!("otherwise: bb{otherwise}")])
1995                        .format(", ");
1996                    write!(f, "switch {} -> {}", discr.with_ctx(ctx), maps)
1997                }
1998            },
1999            TerminatorKind::Call {
2000                call,
2001                target,
2002                on_unwind,
2003            } => {
2004                let call = call.with_ctx(ctx);
2005                write!(f, "{call} -> bb{target} (unwind: bb{on_unwind})",)
2006            }
2007            TerminatorKind::Drop {
2008                kind,
2009                place,
2010                fn_ptr,
2011                target,
2012                on_unwind,
2013            } => {
2014                let kind = match kind {
2015                    DropKind::Precise => "drop",
2016                    DropKind::Conditional => "conditional_drop",
2017                };
2018                write!(
2019                    f,
2020                    "{kind}[{}] {} -> bb{target} (unwind: bb{on_unwind})",
2021                    fn_ptr.with_ctx(ctx),
2022                    place.with_ctx(ctx),
2023                )
2024            }
2025            TerminatorKind::Assert {
2026                assert,
2027                target,
2028                on_unwind,
2029            } => {
2030                write!(
2031                    f,
2032                    "assert {} -> bb{target} (unwind: bb{on_unwind})",
2033                    assert.with_ctx(ctx),
2034                )
2035            }
2036            TerminatorKind::InlineAsm {
2037                asm,
2038                targets,
2039                on_unwind,
2040            } => {
2041                let targets = targets
2042                    .iter()
2043                    .enumerate()
2044                    .map(|(i, target)| format!("target {i}: bb{target}"))
2045                    .chain([format!("unwind: bb{on_unwind}")])
2046                    .format(", ");
2047                write!(f, "asm!({asm:?}) -> {targets}")
2048            }
2049            TerminatorKind::Abort(kind) => write!(f, "{}", kind.with_ctx(ctx)),
2050            TerminatorKind::Return => write!(f, "return"),
2051            TerminatorKind::UnwindResume => write!(f, "unwind_continue"),
2052        }
2053    }
2054}
2055
2056impl<C: AstFormatter> FmtWithCtx<C> for TraitParam {
2057    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2058        write!(f, "{}", self.clause_id.format_as_required())?;
2059        if let Some(d @ 1..) = ctx.binder_depth().checked_sub(1) {
2060            write!(f, "_{d}")?;
2061        }
2062        write!(f, ": {}", self.trait_.format_as_pred(ctx))
2063    }
2064}
2065
2066impl TraitClauseId {
2067    pub(crate) fn format_as_implied(self) -> impl Display {
2068        std::fmt::from_fn(move |f| write!(f, "ImpliedClause{self}"))
2069    }
2070
2071    pub(crate) fn format_as_required(self) -> impl Display {
2072        std::fmt::from_fn(move |f| write!(f, "TraitClause{self}"))
2073    }
2074}
2075
2076impl<C: AstFormatter> FmtWithCtx<C> for TraitDecl {
2077    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2078        // Update the context
2079        let ctx = &ctx.set_generics(&self.generics);
2080
2081        self.item_meta
2082            .fmt_item_intro(f, ctx, "trait", self.def_id)?;
2083
2084        let (generics, clauses) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
2085        write!(f, "{generics}{clauses}")?;
2086
2087        let any_item = !self.implied_clauses.is_empty()
2088            || !self.consts.is_empty()
2089            || !self.types.is_empty()
2090            || !self.methods.is_empty();
2091        if any_item {
2092            write!(f, "\n{{\n")?;
2093            for c in &self.implied_clauses {
2094                writeln!(
2095                    f,
2096                    "{TAB_INCR}{}",
2097                    c.trait_.fmt_trait_proof(c.clause_id, None, ctx)
2098                )?;
2099            }
2100            for assoc_const in &self.consts {
2101                let name = &assoc_const.name;
2102                let ty = assoc_const.ty.with_ctx(ctx);
2103                writeln!(f, "{TAB_INCR}const {name} : {ty}")?;
2104            }
2105            for assoc_ty in &self.types {
2106                let name = assoc_ty.name();
2107                let ctx = &ctx.push_binder(Cow::Borrowed(&assoc_ty.params));
2108                let clauses = assoc_ty
2109                    .params
2110                    .formatted_clauses(ctx)
2111                    .map(|x| x.to_string())
2112                    .chain(assoc_ty.skip_binder.implied_clauses.iter().map(|clause| {
2113                        clause
2114                            .trait_
2115                            .fmt_trait_proof(clause.clause_id, None, ctx)
2116                            .to_string()
2117                    }));
2118                let params = if assoc_ty.params.has_explicits() {
2119                    format!("<{}>", assoc_ty.params.formatted_params(ctx).format(", "))
2120                } else {
2121                    String::new()
2122                };
2123                write!(f, "{TAB_INCR}type {name}{params}")?;
2124                if let Some(default) = &assoc_ty.skip_binder.default {
2125                    write!(f, " = {}", default.value.with_ctx(ctx))?;
2126                }
2127                write!(f, "{}", fmt_where_clauses(clauses, TAB_INCR))?;
2128                writeln!(f)?;
2129            }
2130            for method in self.methods() {
2131                let name = method.name();
2132                let (params, method) =
2133                    method.fmt_split_with(ctx, |ctx, method| match &method.default {
2134                        Some(fn_ref) => format!(" = {}", fn_ref.to_string_with_ctx(ctx)),
2135                        None => format!(";"),
2136                    });
2137                writeln!(f, "{TAB_INCR}fn {name}{params}{method}")?;
2138            }
2139            if let Some(vtb_ref) = &self.vtable {
2140                writeln!(f, "{TAB_INCR}vtable: {}", vtb_ref.with_ctx(ctx))?;
2141            } else {
2142                writeln!(f, "{TAB_INCR}non-dyn-compatible")?;
2143            }
2144            write!(f, "}}")?;
2145        }
2146        Ok(())
2147    }
2148}
2149
2150impl<C: AstFormatter> FmtWithCtx<C> for TraitDeclId {
2151    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2152        ItemId::from(*self).fmt_with_ctx(ctx, f)
2153    }
2154}
2155
2156impl<C: AstFormatter> FmtWithCtx<C> for TraitDeclRef {
2157    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2158        let trait_id = self.id.with_ctx(ctx);
2159        let generics = self.generics.with_ctx(ctx);
2160        write!(f, "{trait_id}{generics}")
2161    }
2162}
2163
2164impl TraitDeclRef {
2165    /// Split off the `Self` type. The returned `TraitDeclRef` has incorrect generics. The returned
2166    /// `Self` is `None` for monomorphized traits.
2167    pub fn split_self(&self) -> (Option<Ty>, Self) {
2168        let mut pred = self.clone();
2169        let self_ty = pred.generics.types.remove_and_shift_ids(TypeVarId::ZERO);
2170        (self_ty, pred)
2171    }
2172
2173    fn format_as_pred<'a, C: AstFormatter + 'a>(&'a self, ctx: &'a C) -> impl Display + 'a {
2174        std::fmt::from_fn(move |f| {
2175            let (self_ty, pred) = self.split_self();
2176            match self_ty {
2177                Some(self_ty) => write!(f, "{}: {}", self_ty.with_ctx(ctx), pred.with_ctx(ctx)),
2178                // Monomorphized traits don't have self types.
2179                None => write!(f, "{}", pred.with_ctx(ctx)),
2180            }
2181        })
2182    }
2183
2184    fn format_as_impl<'a, C: AstFormatter>(&'a self, ctx: &'a C) -> impl Display + 'a {
2185        std::fmt::from_fn(move |f| {
2186            let (self_ty, pred) = self.split_self();
2187            match self_ty {
2188                Some(self_ty) => write!(f, "{} for {}", pred.with_ctx(ctx), self_ty.with_ctx(ctx)),
2189                // Monomorphized traits don't have self types.
2190                None => write!(f, "{}", pred.with_ctx(ctx)),
2191            }
2192        })
2193    }
2194}
2195
2196impl<C: AstFormatter> FmtWithCtx<C> for TraitImpl {
2197    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2198        let trait_id = self.impl_trait.id;
2199        writeln!(f, "// Full name: {}", self.item_meta.name.full_name(ctx))?;
2200
2201        // Update the context
2202        let ctx = &ctx.set_generics(&self.generics);
2203
2204        let (generics, clauses) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
2205        let impl_trait = self.impl_trait.format_as_impl(ctx);
2206        write!(f, "impl{generics}")?;
2207        if let Some(short_name) = trait_impl_short_name(ctx, self.def_id) {
2208            write!(f, " \"{}\"", short_name.with_ctx(ctx))?;
2209        }
2210        write!(f, " {impl_trait}{clauses}",)?;
2211
2212        let newline = if clauses.is_empty() {
2213            " ".to_string()
2214        } else {
2215            "\n".to_string()
2216        };
2217        writeln!(f, "{newline}{{")?;
2218
2219        let any_item = !self.implied_trait_refs.is_empty()
2220            || !self.consts.is_empty()
2221            || !self.types.is_empty()
2222            || !self.methods.is_empty();
2223        if any_item {
2224            for (id, trait_ref) in self.implied_trait_refs.iter_enumerated() {
2225                writeln!(
2226                    f,
2227                    "{TAB_INCR}{}",
2228                    trait_ref
2229                        .trait_decl_ref
2230                        .fmt_trait_proof(id, Some(trait_ref), ctx)
2231                )?;
2232            }
2233            for (const_id, global) in self.consts.iter_enumerated() {
2234                write!(f, "{TAB_INCR}const ")?;
2235                ctx.format_assoc_const_name(f, trait_id, const_id)?;
2236                writeln!(f, " = {}", global.with_ctx(ctx))?;
2237            }
2238            for (type_id, assoc_ty) in self.types.iter_enumerated() {
2239                let ctx = &ctx.push_binder(Cow::Borrowed(&assoc_ty.params));
2240                let params = if assoc_ty.params.has_explicits() {
2241                    format!("<{}>", assoc_ty.params.formatted_params(ctx).format(", "))
2242                } else {
2243                    String::new()
2244                };
2245                let ty = assoc_ty.skip_binder.value.with_ctx(ctx);
2246                let clauses = assoc_ty
2247                    .params
2248                    .formatted_clauses(ctx)
2249                    .map(|x| x.to_string())
2250                    .chain(
2251                        assoc_ty
2252                            .skip_binder
2253                            .implied_trait_refs
2254                            .iter_enumerated()
2255                            .map(|(id, trait_ref)| {
2256                                trait_ref
2257                                    .trait_decl_ref
2258                                    .fmt_trait_proof(id, Some(trait_ref), ctx)
2259                                    .to_string()
2260                            }),
2261                    );
2262                write!(f, "{TAB_INCR}type ")?;
2263                ctx.format_assoc_type_name(f, trait_id, type_id)?;
2264                write!(f, "{params} = {ty}")?;
2265                write!(f, "{}", fmt_where_clauses(clauses, TAB_INCR))?;
2266                writeln!(f)?;
2267            }
2268            for (method_id, bound_fn) in self.methods.iter_enumerated() {
2269                let (params, fn_ref) = bound_fn.fmt_split(ctx);
2270                write!(f, "{TAB_INCR}fn ")?;
2271                ctx.format_method_name(f, trait_id, method_id)?;
2272                writeln!(f, "{params} = {fn_ref}")?;
2273            }
2274        }
2275        if let Some(vtb_ref) = &self.vtable {
2276            writeln!(f, "{TAB_INCR}vtable: {}", vtb_ref.with_ctx(ctx))?;
2277        } else {
2278            writeln!(f, "{TAB_INCR}non-dyn-compatible")?;
2279        }
2280        write!(f, "}}")?;
2281        Ok(())
2282    }
2283}
2284
2285impl<C: AstFormatter> FmtWithCtx<C> for TraitImplId {
2286    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2287        ItemId::from(*self).fmt_with_ctx(ctx, f)
2288    }
2289}
2290
2291impl<C: AstFormatter> FmtWithCtx<C> for TraitImplRef {
2292    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2293        let id = self.id.with_ctx(ctx);
2294        let generics = self.generics.with_ctx(ctx);
2295        write!(f, "{id}{generics}")
2296    }
2297}
2298
2299impl Display for TraitItemName {
2300    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
2301        write!(f, "{}", self.0)
2302    }
2303}
2304
2305impl<C: AstFormatter> FmtWithCtx<C> for TraitRef {
2306    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2307        match &self.kind {
2308            TraitRefKind::SelfId => write!(f, "Self"),
2309            TraitRefKind::ParentClause(sub, clause_id) => {
2310                let sub = sub.with_ctx(ctx);
2311                write!(f, "{sub}::{}", clause_id.format_as_implied())
2312            }
2313            TraitRefKind::ItemClause(sub, type_id, clause_id) => {
2314                write!(f, "{}::", sub.with_ctx(ctx))?;
2315                ctx.format_assoc_type_name(f, sub.trait_id(), *type_id)?;
2316                write!(f, "::{}", clause_id.format_as_implied())
2317            }
2318            TraitRefKind::TraitImpl(impl_ref) => {
2319                write!(f, "{}", impl_ref.with_ctx(ctx))
2320            }
2321            TraitRefKind::Clause(id) => write!(f, "{}", id.with_ctx(ctx)),
2322            TraitRefKind::BuiltinOrAuto { types, .. } => {
2323                let bound_ctx = &ctx.push_bound_regions(&self.trait_decl_ref.regions);
2324                let impl_trait = self.trait_decl_ref.skip_binder.format_as_impl(bound_ctx);
2325                write!(f, "{{built_in impl {impl_trait}")?;
2326                if !types.is_empty() {
2327                    let trait_id = self.trait_decl_ref.skip_binder.id;
2328                    let types = types
2329                        .iter_indexed()
2330                        .map(|(type_id, assoc_ty)| {
2331                            std::fmt::from_fn(move |f| {
2332                                ctx.format_assoc_type_name(f, trait_id, type_id)?;
2333                                let ty = assoc_ty.value.with_ctx(ctx);
2334                                write!(f, "  = {ty}")
2335                            })
2336                        })
2337                        .join(", ");
2338                    write!(f, " where {types}")?;
2339                }
2340                write!(f, "}}")?;
2341                Ok(())
2342            }
2343            TraitRefKind::Dyn => write!(f, "{}", self.trait_decl_ref.with_ctx(ctx)),
2344            TraitRefKind::Unknown(msg) => write!(f, "UNKNOWN({msg})"),
2345        }
2346    }
2347}
2348
2349impl<C: AstFormatter> FmtWithCtx<C> for TraitTypeConstraint {
2350    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2351        let trait_ref = self.trait_ref.with_ctx(ctx);
2352        let ty = self.ty.with_ctx(ctx);
2353        write!(f, "{trait_ref}::")?;
2354        ctx.format_assoc_type_name(f, self.trait_ref.trait_id(), self.type_id)?;
2355        write!(f, " = {ty}")
2356    }
2357}
2358
2359impl<C: AstFormatter> FmtWithCtx<C> for Ty {
2360    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2361        match self.kind() {
2362            TyKind::Adt(tref) => match tref.id {
2363                TypeId::Tuple => {
2364                    let generics = tref.generics.fmt_explicits(ctx).format(", ");
2365                    let trailing_comma = if tref.generics.types.len() == 1 {
2366                        ","
2367                    } else {
2368                        ""
2369                    };
2370                    write!(f, "({generics}{trailing_comma})")
2371                }
2372                _ => write!(f, "{}", tref.with_ctx(ctx)),
2373            },
2374            TyKind::TypeVar(id) => write!(f, "{}", id.with_ctx(ctx)),
2375            TyKind::Literal(kind) => write!(f, "{kind}"),
2376            TyKind::Never => write!(f, "!"),
2377            TyKind::Pattern(ty, pat) => write!(f, "{} is {}", ty.with_ctx(ctx), pat.with_ctx(ctx)),
2378            TyKind::Ref(r, ty, kind) => {
2379                write!(f, "&{} ", r.with_ctx(ctx))?;
2380                if let RefKind::Mut = kind {
2381                    write!(f, "mut ")?;
2382                }
2383                write!(f, "{}", ty.with_ctx(ctx))
2384            }
2385            TyKind::RawPtr(ty, kind) => {
2386                write!(f, "*")?;
2387                match kind {
2388                    RefKind::Shared => write!(f, "const")?,
2389                    RefKind::Mut => write!(f, "mut")?,
2390                }
2391                write!(f, " {}", ty.with_ctx(ctx))
2392            }
2393            TyKind::Array(ty, len) => {
2394                write!(f, "[{}; {}]", ty.with_ctx(ctx), len.with_ctx(ctx))
2395            }
2396            TyKind::Slice(ty) => {
2397                write!(f, "[{}]", ty.with_ctx(ctx))
2398            }
2399            TyKind::TraitType(trait_ref, type_id, generics) => {
2400                write!(f, "{}::", trait_ref.with_ctx(ctx))?;
2401                ctx.format_assoc_type_name(f, trait_ref.trait_id(), *type_id)?;
2402                write!(f, "{}", generics.with_ctx(ctx))
2403            }
2404            TyKind::DynTrait(pred) => {
2405                write!(f, "(dyn {})", pred.with_ctx(ctx))
2406            }
2407            TyKind::FnPtr(io) => {
2408                write!(f, "{}", io.with_ctx(ctx))
2409            }
2410            TyKind::FnDef(binder) => {
2411                let (regions, value) = binder.fmt_split(ctx);
2412                if !regions.is_empty() {
2413                    write!(f, "for<{regions}> ",)?
2414                };
2415                write!(f, "{value}",)
2416            }
2417            TyKind::PtrMetadata(ty) => {
2418                write!(f, "PtrMetadata<{}>", ty.with_ctx(ctx))
2419            }
2420            TyKind::Error(msg) => write!(f, "type_error(\"{msg}\")"),
2421        }
2422    }
2423}
2424
2425impl<C: AstFormatter> FmtWithCtx<C> for TypePattern {
2426    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2427        match self {
2428            TypePattern::Range(start, end) => {
2429                write!(f, "{}..={}", start.with_ctx(ctx), end.with_ctx(ctx))
2430            }
2431            TypePattern::OrPattern(patterns) => {
2432                write!(
2433                    f,
2434                    "({})",
2435                    patterns.iter().map(|pat| pat.with_ctx(ctx)).format(" | ")
2436                )
2437            }
2438            TypePattern::NotNull => write!(f, "!null"),
2439        }
2440    }
2441}
2442
2443impl<C: AstFormatter> FmtWithCtx<C> for TypeDbVar {
2444    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2445        ctx.format_bound_var(f, *self, "@Type", |v| Some(v.name.clone()))
2446    }
2447}
2448
2449impl<C: AstFormatter> FmtWithCtx<C> for TypeDecl {
2450    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2451        let keyword = match &self.kind {
2452            TypeDeclKind::Struct(..) => "struct",
2453            TypeDeclKind::Union(..) => "union",
2454            TypeDeclKind::Enum(..) => "enum",
2455            TypeDeclKind::Alias(..) => "type",
2456            TypeDeclKind::Opaque | TypeDeclKind::Error(..) => "opaque type",
2457        };
2458        self.item_meta
2459            .fmt_item_intro(f, ctx, keyword, self.def_id)?;
2460
2461        let ctx = &ctx.set_generics(&self.generics);
2462        let (params, preds) = self.generics.fmt_with_ctx_with_trait_clauses(ctx);
2463        write!(f, "{params}{preds}")?;
2464
2465        let nl_or_space = if !self.generics.has_predicates() {
2466            " ".to_string()
2467        } else {
2468            "\n".to_string()
2469        };
2470        match &self.kind {
2471            TypeDeclKind::Struct(fields) => {
2472                write!(f, "{nl_or_space}{{")?;
2473                if !fields.is_empty() {
2474                    writeln!(f)?;
2475                    for field in fields {
2476                        writeln!(f, "  {},", field.with_ctx(ctx))?;
2477                    }
2478                }
2479                write!(f, "}}")
2480            }
2481            TypeDeclKind::Union(fields) => {
2482                write!(f, "{nl_or_space}{{")?;
2483                writeln!(f)?;
2484                for field in fields {
2485                    writeln!(f, "  {},", field.with_ctx(ctx))?;
2486                }
2487                write!(f, "}}")
2488            }
2489            TypeDeclKind::Enum(variants) => {
2490                write!(f, "{nl_or_space}{{")?;
2491                writeln!(f)?;
2492                for variant in variants {
2493                    writeln!(f, "  {},", variant.with_ctx(ctx))?;
2494                }
2495                write!(f, "}}")
2496            }
2497            TypeDeclKind::Alias(ty) => write!(f, " = {}", ty.with_ctx(ctx)),
2498            TypeDeclKind::Opaque => write!(f, ""),
2499            TypeDeclKind::Error(msg) => write!(f, " = ERROR({msg})"),
2500        }
2501    }
2502}
2503
2504impl<C: AstFormatter> FmtWithCtx<C> for TypeDeclId {
2505    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2506        ItemId::from(*self).fmt_with_ctx(ctx, f)
2507    }
2508}
2509
2510impl<C: AstFormatter> FmtWithCtx<C> for TypeDeclRef {
2511    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2512        let id = self.id.with_ctx(ctx);
2513        let generics = self.generics.with_ctx(ctx);
2514        write!(f, "{id}{generics}")
2515    }
2516}
2517
2518impl<C: AstFormatter> FmtWithCtx<C> for TypeId {
2519    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2520        match self {
2521            TypeId::Tuple => Ok(()),
2522            TypeId::Adt(def_id) => write!(f, "{}", def_id.with_ctx(ctx)),
2523            TypeId::Builtin(aty) => write!(f, "{}", aty.get_name().with_ctx(ctx)),
2524        }
2525    }
2526}
2527
2528impl<C: AstFormatter> FmtWithCtx<C> for TypeParam {
2529    fn fmt_with_ctx(&self, _ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2530        write!(f, "{}", self.name)
2531    }
2532}
2533
2534impl<C: AstFormatter> FmtWithCtx<C> for UnOp {
2535    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2536        match self {
2537            UnOp::Not => write!(f, "~"),
2538            UnOp::Neg(mode) => write!(f, "{}.-", mode),
2539            UnOp::Cast(kind) => write!(f, "{}", kind.with_ctx(ctx)),
2540        }
2541    }
2542}
2543
2544impl_display_via_ctx!(Variant);
2545impl<C: AstFormatter> FmtWithCtx<C> for Variant {
2546    fn fmt_with_ctx(&self, ctx: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2547        write!(f, "{}", self.name)?;
2548        if !self.fields.is_empty() {
2549            let fields = self.fields.iter().map(|f| f.with_ctx(ctx)).format(", ");
2550            write!(f, "({})", fields)?;
2551        }
2552        Ok(())
2553    }
2554}