flux_middle/rty/
expr.rs

1use std::{fmt, iter, ops::ControlFlow, sync::OnceLock};
2
3use flux_arc_interner::{Interned, List, impl_internable, impl_slice_internable};
4use flux_common::bug;
5use flux_macros::{TypeFoldable, TypeVisitable};
6use flux_rustc_bridge::{
7    ToRustc,
8    const_eval::{scalar_to_bits, scalar_to_int, scalar_to_uint},
9    ty::{Const, ConstKind, ValTree, VariantIdx},
10};
11use itertools::Itertools;
12use liquid_fixpoint::ThyFunc;
13use rustc_abi::{FIRST_VARIANT, FieldIdx};
14use rustc_data_structures::snapshot_map::SnapshotMap;
15use rustc_hir::def_id::DefId;
16use rustc_index::newtype_index;
17use rustc_macros::{Decodable, Encodable, TyDecodable, TyEncodable};
18use rustc_middle::{
19    mir::Local,
20    ty::{ParamConst, ScalarInt, TyCtxt},
21};
22use rustc_span::{Span, Symbol};
23use rustc_type_ir::{BoundVar, DebruijnIndex, INNERMOST};
24
25use super::{
26    BaseTy, Binder, BoundReftKind, BoundVariableKinds, FuncSort, GenericArgs, GenericArgsExt as _,
27    IntTy, Sort, UintTy,
28};
29use crate::{
30    big_int::BigInt,
31    def_id::FluxDefId,
32    fhir::{self},
33    global_env::GlobalEnv,
34    pretty::*,
35    queries::QueryResult,
36    rty::{
37        BoundVariableKind, SortArg,
38        fold::{
39            TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable as _,
40            TypeVisitor,
41        },
42    },
43};
44
45/// A lambda abstraction with an elaborated output sort. We need the output sort of lambdas for
46/// encoding into fixpoint
47#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
48pub struct Lambda {
49    body: Binder<Expr>,
50    output: Sort,
51}
52
53impl Lambda {
54    pub fn bind_with_vars(body: Expr, inputs: BoundVariableKinds, output: Sort) -> Self {
55        debug_assert!(inputs.iter().all(BoundVariableKind::is_refine));
56        Self { body: Binder::bind_with_vars(body, inputs), output }
57    }
58
59    pub fn bind_with_fsort(body: Expr, fsort: FuncSort) -> Self {
60        Self { body: Binder::bind_with_sorts(body, fsort.inputs()), output: fsort.output().clone() }
61    }
62
63    pub fn apply(&self, args: &[Expr]) -> Expr {
64        self.body.replace_bound_refts(args)
65    }
66
67    pub fn vars(&self) -> &BoundVariableKinds {
68        self.body.vars()
69    }
70
71    pub fn output(&self) -> Sort {
72        self.output.clone()
73    }
74
75    pub fn fsort(&self) -> FuncSort {
76        let inputs_and_output = self
77            .vars()
78            .iter()
79            .map(|kind| kind.expect_sort().clone())
80            .chain(iter::once(self.output()))
81            .collect();
82        FuncSort { inputs_and_output }
83    }
84}
85
86#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
87pub struct AliasReft {
88    /// Id of the associated refinement in the trait
89    pub assoc_id: FluxDefId,
90    pub args: GenericArgs,
91}
92
93impl AliasReft {
94    pub fn to_rustc_trait_ref<'tcx>(&self, tcx: TyCtxt<'tcx>) -> rustc_middle::ty::TraitRef<'tcx> {
95        let trait_def_id = self.assoc_id.parent();
96        let args = self
97            .args
98            .to_rustc(tcx)
99            .truncate_to(tcx, tcx.generics_of(trait_def_id));
100        rustc_middle::ty::TraitRef::new(tcx, trait_def_id, args)
101    }
102}
103
104#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
105pub struct Expr {
106    kind: Interned<ExprKind>,
107    espan: Option<ESpan>,
108}
109
110impl Expr {
111    pub fn at_opt(self, espan: Option<ESpan>) -> Expr {
112        Expr { kind: self.kind, espan }
113    }
114
115    pub fn at(self, espan: ESpan) -> Expr {
116        self.at_opt(Some(espan))
117    }
118
119    pub fn at_base(self, base: ESpan) -> Expr {
120        if let Some(espan) = self.espan { self.at(espan.with_base(base)) } else { self }
121    }
122
123    pub fn span(&self) -> Option<ESpan> {
124        self.espan
125    }
126
127    pub fn tt() -> Expr {
128        static TRUE: OnceLock<Expr> = OnceLock::new();
129        TRUE.get_or_init(|| ExprKind::Constant(Constant::Bool(true)).intern())
130            .clone()
131    }
132
133    pub fn ff() -> Expr {
134        static FALSE: OnceLock<Expr> = OnceLock::new();
135        FALSE
136            .get_or_init(|| ExprKind::Constant(Constant::Bool(false)).intern())
137            .clone()
138    }
139
140    pub fn and_from_iter(exprs: impl IntoIterator<Item = Expr>) -> Expr {
141        exprs
142            .into_iter()
143            .reduce(|acc, e| Expr::binary_op(BinOp::And, acc, e))
144            .unwrap_or_else(Expr::tt)
145    }
146
147    pub fn or_from_iter(exprs: impl IntoIterator<Item = Expr>) -> Expr {
148        exprs
149            .into_iter()
150            .reduce(|acc, e| Expr::binary_op(BinOp::Or, acc, e))
151            .unwrap_or_else(Expr::ff)
152    }
153
154    pub fn and(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
155        Expr::and_from_iter([e1.into(), e2.into()])
156    }
157
158    pub fn or(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
159        Expr::or_from_iter([e1.into(), e2.into()])
160    }
161
162    pub fn zero() -> Expr {
163        static ZERO: OnceLock<Expr> = OnceLock::new();
164        ZERO.get_or_init(|| ExprKind::Constant(Constant::ZERO).intern())
165            .clone()
166    }
167
168    pub fn int_max(int_ty: IntTy) -> Expr {
169        let bit_width: u64 = int_ty
170            .bit_width()
171            .unwrap_or(flux_config::pointer_width().bits());
172        Expr::constant(Constant::int_max(bit_width.try_into().unwrap()))
173    }
174
175    pub fn int_min(int_ty: IntTy) -> Expr {
176        let bit_width: u64 = int_ty
177            .bit_width()
178            .unwrap_or(flux_config::pointer_width().bits());
179        Expr::constant(Constant::int_min(bit_width.try_into().unwrap()))
180    }
181
182    pub fn uint_max(uint_ty: UintTy) -> Expr {
183        let bit_width: u64 = uint_ty
184            .bit_width()
185            .unwrap_or(flux_config::pointer_width().bits());
186        Expr::constant(Constant::uint_max(bit_width.try_into().unwrap()))
187    }
188
189    pub fn nu() -> Expr {
190        Expr::bvar(INNERMOST, BoundVar::ZERO, BoundReftKind::Anon)
191    }
192
193    pub fn is_nu(&self) -> bool {
194        if let ExprKind::Var(Var::Bound(INNERMOST, var)) = self.kind()
195            && var.var == BoundVar::ZERO
196        {
197            true
198        } else {
199            false
200        }
201    }
202
203    pub fn unit() -> Expr {
204        Expr::tuple(List::empty())
205    }
206
207    pub fn var(var: Var) -> Expr {
208        ExprKind::Var(var).intern()
209    }
210
211    pub fn fvar(name: Name) -> Expr {
212        Var::Free(name).to_expr()
213    }
214
215    pub fn evar(evid: EVid) -> Expr {
216        Var::EVar(evid).to_expr()
217    }
218
219    pub fn bvar(debruijn: DebruijnIndex, var: BoundVar, kind: BoundReftKind) -> Expr {
220        Var::Bound(debruijn, BoundReft { var, kind }).to_expr()
221    }
222
223    pub fn early_param(index: u32, name: Symbol) -> Expr {
224        Var::EarlyParam(EarlyReftParam { index, name }).to_expr()
225    }
226
227    pub fn local(local: Local) -> Expr {
228        ExprKind::Local(local).intern()
229    }
230
231    pub fn constant(c: Constant) -> Expr {
232        ExprKind::Constant(c).intern()
233    }
234
235    pub fn const_def_id(c: DefId) -> Expr {
236        ExprKind::ConstDefId(c).intern()
237    }
238
239    pub fn const_generic(param: ParamConst) -> Expr {
240        ExprKind::Var(Var::ConstGeneric(param)).intern()
241    }
242
243    pub fn tuple(flds: List<Expr>) -> Expr {
244        ExprKind::Tuple(flds).intern()
245    }
246
247    pub fn ctor_struct(def_id: DefId, flds: List<Expr>) -> Expr {
248        ExprKind::Ctor(Ctor::Struct(def_id), flds).intern()
249    }
250
251    pub fn ctor_enum(def_id: DefId, idx: VariantIdx) -> Expr {
252        ExprKind::Ctor(Ctor::Enum(def_id, idx), List::empty()).intern()
253    }
254
255    pub fn ctor(ctor: Ctor, flds: List<Expr>) -> Expr {
256        ExprKind::Ctor(ctor, flds).intern()
257    }
258
259    pub fn is_ctor(def_id: DefId, variant_idx: VariantIdx, idx: impl Into<Expr>) -> Expr {
260        ExprKind::IsCtor(def_id, variant_idx, idx.into()).intern()
261    }
262
263    pub fn from_bits(bty: &BaseTy, bits: u128) -> Expr {
264        // FIXME: We are assuming the higher bits are not set. check this assumption
265        match bty {
266            BaseTy::Int(_) => {
267                let bits = bits as i128;
268                ExprKind::Constant(Constant::from(bits)).intern()
269            }
270            BaseTy::Uint(_) => ExprKind::Constant(Constant::from(bits)).intern(),
271            BaseTy::Bool => ExprKind::Constant(Constant::Bool(bits != 0)).intern(),
272            BaseTy::Char => {
273                let c = char::from_u32(bits.try_into().unwrap()).unwrap();
274                ExprKind::Constant(Constant::Char(c)).intern()
275            }
276            _ => bug!(),
277        }
278    }
279
280    pub fn ite(p: impl Into<Expr>, e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
281        ExprKind::IfThenElse(p.into(), e1.into(), e2.into()).intern()
282    }
283
284    fn empty() -> Expr {
285        let func = Self::global_func(SpecFuncKind::Thy(ThyFunc::SetEmpty));
286        Expr::app(func, List::empty(), List::from_arr([Expr::zero()]))
287    }
288
289    fn singleton(elem: Expr) -> Expr {
290        let func = Self::global_func(SpecFuncKind::Thy(ThyFunc::SetSng));
291        Expr::app(func, List::empty(), List::from_arr([elem]))
292    }
293
294    fn union(expr1: Expr, expr2: Expr) -> Expr {
295        let func = Self::global_func(SpecFuncKind::Thy(ThyFunc::SetCup));
296        Expr::app(func, List::empty(), List::from_arr([expr1, expr2]))
297    }
298
299    pub fn set(elems: List<Expr>) -> Expr {
300        let mut expr = Expr::empty();
301        for elem in &elems {
302            expr = Self::union(expr, Self::singleton(elem.clone()));
303        }
304        expr
305    }
306
307    pub fn abs(lam: Lambda) -> Expr {
308        ExprKind::Abs(lam).intern()
309    }
310
311    pub fn let_(init: Expr, body: Binder<Expr>) -> Expr {
312        ExprKind::Let(init, body).intern()
313    }
314
315    pub fn bounded_quant(kind: fhir::QuantKind, rng: fhir::Range, body: Binder<Expr>) -> Expr {
316        ExprKind::BoundedQuant(kind, rng, body).intern()
317    }
318
319    pub fn hole(kind: HoleKind) -> Expr {
320        ExprKind::Hole(kind).intern()
321    }
322
323    pub fn kvar(kvar: KVar) -> Expr {
324        ExprKind::KVar(kvar).intern()
325    }
326
327    pub fn alias(alias: AliasReft, args: List<Expr>) -> Expr {
328        ExprKind::Alias(alias, args).intern()
329    }
330
331    pub fn forall(expr: Binder<Expr>) -> Expr {
332        ExprKind::ForAll(expr).intern()
333    }
334
335    pub fn exists(expr: Binder<Expr>) -> Expr {
336        ExprKind::Exists(expr).intern()
337    }
338
339    pub fn binary_op(op: BinOp, e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
340        ExprKind::BinaryOp(op, e1.into(), e2.into()).intern()
341    }
342
343    pub fn prim_val(op: BinOp, e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
344        Expr::app(InternalFuncKind::Val(op), List::empty(), List::from_arr([e1.into(), e2.into()]))
345    }
346
347    pub fn prim_rel(op: BinOp, e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
348        Expr::app(InternalFuncKind::Rel(op), List::empty(), List::from_arr([e1.into(), e2.into()]))
349    }
350
351    pub fn unit_struct(def_id: DefId) -> Expr {
352        Expr::ctor_struct(def_id, List::empty())
353    }
354
355    pub fn cast(from: Sort, to: Sort, idx: Expr) -> Expr {
356        Expr::app(
357            InternalFuncKind::Cast,
358            List::from_arr([SortArg::Sort(from), SortArg::Sort(to)]),
359            List::from_arr([idx]),
360        )
361    }
362
363    pub fn app(func: impl Into<Expr>, sort_args: List<SortArg>, args: List<Expr>) -> Expr {
364        ExprKind::App(func.into(), sort_args, args).intern()
365    }
366
367    pub fn global_func(kind: SpecFuncKind) -> Expr {
368        ExprKind::GlobalFunc(kind).intern()
369    }
370
371    pub fn internal_func(kind: InternalFuncKind) -> Expr {
372        ExprKind::InternalFunc(kind).intern()
373    }
374
375    pub fn eq(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
376        ExprKind::BinaryOp(BinOp::Eq, e1.into(), e2.into()).intern()
377    }
378
379    pub fn unary_op(op: UnOp, e: impl Into<Expr>) -> Expr {
380        ExprKind::UnaryOp(op, e.into()).intern()
381    }
382
383    pub fn ne(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
384        ExprKind::BinaryOp(BinOp::Ne, e1.into(), e2.into()).intern()
385    }
386
387    pub fn ge(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
388        ExprKind::BinaryOp(BinOp::Ge(Sort::Int), e1.into(), e2.into()).intern()
389    }
390
391    pub fn gt(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
392        ExprKind::BinaryOp(BinOp::Gt(Sort::Int), e1.into(), e2.into()).intern()
393    }
394
395    pub fn lt(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
396        ExprKind::BinaryOp(BinOp::Lt(Sort::Int), e1.into(), e2.into()).intern()
397    }
398
399    pub fn le(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
400        ExprKind::BinaryOp(BinOp::Le(Sort::Int), e1.into(), e2.into()).intern()
401    }
402
403    pub fn implies(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
404        ExprKind::BinaryOp(BinOp::Imp, e1.into(), e2.into()).intern()
405    }
406
407    pub fn field_proj(e: impl Into<Expr>, proj: FieldProj) -> Expr {
408        ExprKind::FieldProj(e.into(), proj).intern()
409    }
410
411    pub fn field_projs(e: impl Into<Expr>, projs: &[FieldProj]) -> Expr {
412        projs.iter().copied().fold(e.into(), Expr::field_proj)
413    }
414
415    pub fn path_proj(base: Expr, field: FieldIdx) -> Expr {
416        ExprKind::PathProj(base, field).intern()
417    }
418
419    pub fn not(&self) -> Expr {
420        ExprKind::UnaryOp(UnOp::Not, self.clone()).intern()
421    }
422
423    pub fn neg(&self) -> Expr {
424        ExprKind::UnaryOp(UnOp::Neg, self.clone()).intern()
425    }
426
427    pub fn kind(&self) -> &ExprKind {
428        &self.kind
429    }
430
431    /// An expression is an *atom* if it is "self-delimiting", i.e., it has a clear boundary
432    /// when printed. This is used to avoid unnecessary parenthesis when pretty printing.
433    pub fn is_atom(&self) -> bool {
434        !matches!(self.kind(), ExprKind::Abs(..) | ExprKind::BinaryOp(..) | ExprKind::ForAll(..))
435    }
436
437    /// Simple syntactic check to see if the expression is a trivially true predicate. This is used
438    /// mostly for filtering predicates when pretty printing but also to simplify types in general.
439    pub fn is_trivially_true(&self) -> bool {
440        self.is_true()
441            || matches!(self.kind(), ExprKind::BinaryOp(BinOp::Eq | BinOp::Iff | BinOp::Imp, e1, e2) if e1.erase_spans() == e2.erase_spans())
442    }
443
444    /// Simple syntactic check to see if the expression is a trivially false predicate.
445    pub fn is_trivially_false(&self) -> bool {
446        self.is_false()
447    }
448
449    /// Whether the expression is *literally* the constant `true`.
450    fn is_true(&self) -> bool {
451        matches!(self.kind(), ExprKind::Constant(Constant::Bool(true)))
452    }
453
454    /// Whether the expression is *literally* the constant `false`.
455    fn is_false(&self) -> bool {
456        matches!(self.kind(), ExprKind::Constant(Constant::Bool(false)))
457    }
458
459    pub fn from_const(tcx: TyCtxt, c: &Const) -> Expr {
460        match &c.kind {
461            ConstKind::Param(param_const) => Expr::const_generic(*param_const),
462            ConstKind::Value(ty, ValTree::Leaf(scalar)) => {
463                Expr::constant(Constant::from_scalar_int(tcx, *scalar, ty).unwrap())
464            }
465            ConstKind::Value(_ty, ValTree::Branch(_)) => {
466                bug!("todo: ValTree::Branch {c:?}")
467            }
468            // We should have normalized away the unevaluated constants
469            ConstKind::Unevaluated(_) => bug!("unexpected `ConstKind::Unevaluated`"),
470
471            ConstKind::Infer(_) => bug!("unexpected `ConstKind::Infer`"),
472        }
473    }
474
475    fn const_op(op: &BinOp, c1: &Constant, c2: &Constant) -> Option<Constant> {
476        match op {
477            BinOp::Iff => c1.iff(c2),
478            BinOp::Imp => c1.imp(c2),
479            BinOp::Or => c1.or(c2),
480            BinOp::And => c1.and(c2),
481            BinOp::Gt(Sort::Int) => c1.gt(c2),
482            BinOp::Ge(Sort::Int) => c1.ge(c2),
483            BinOp::Lt(Sort::Int) => c2.gt(c1),
484            BinOp::Le(Sort::Int) => c2.ge(c1),
485            BinOp::Eq => Some(c1.eq(c2)),
486            BinOp::Ne => Some(c1.ne(c2)),
487            _ => None,
488        }
489    }
490
491    /// Simplify the expression by removing double negations, short-circuiting boolean connectives and
492    /// doing constant folding. Note that we also have [`TypeFoldable::normalize`] which applies beta
493    /// reductions for tuples and abstractions.
494    ///
495    /// Additionally replaces any occurrences of elements in assumed_preds with True.
496    pub fn simplify(&self, assumed_preds: &SnapshotMap<Expr, ()>) -> Expr {
497        struct Simplify<'a> {
498            assumed_preds: &'a SnapshotMap<Expr, ()>,
499        }
500
501        impl TypeFolder for Simplify<'_> {
502            fn fold_expr(&mut self, expr: &Expr) -> Expr {
503                if self.assumed_preds.get(&expr.erase_spans()).is_some() {
504                    return Expr::tt();
505                }
506                let span = expr.span();
507                match expr.kind() {
508                    ExprKind::BinaryOp(op, e1, e2) => {
509                        let e1 = e1.fold_with(self);
510                        let e2 = e2.fold_with(self);
511                        match (op, e1.kind(), e2.kind()) {
512                            (BinOp::And, ExprKind::Constant(Constant::Bool(false)), _) => {
513                                Expr::constant(Constant::Bool(false)).at_opt(e1.span())
514                            }
515                            (BinOp::And, _, ExprKind::Constant(Constant::Bool(false))) => {
516                                Expr::constant(Constant::Bool(false)).at_opt(e2.span())
517                            }
518                            (BinOp::And, ExprKind::Constant(Constant::Bool(true)), _) => e2,
519                            (BinOp::And, _, ExprKind::Constant(Constant::Bool(true))) => e1,
520                            (op, ExprKind::Constant(c1), ExprKind::Constant(c2)) => {
521                                if let Some(c) = Expr::const_op(op, c1, c2) {
522                                    Expr::constant(c).at_opt(span.or(e2.span()))
523                                } else {
524                                    Expr::binary_op(op.clone(), e1, e2).at_opt(span)
525                                }
526                            }
527                            _ => Expr::binary_op(op.clone(), e1, e2).at_opt(span),
528                        }
529                    }
530                    ExprKind::UnaryOp(UnOp::Not, e) => {
531                        let e = e.fold_with(self);
532                        match e.kind() {
533                            ExprKind::Constant(Constant::Bool(b)) => {
534                                Expr::constant(Constant::Bool(!b))
535                            }
536                            ExprKind::UnaryOp(UnOp::Not, e) => e.clone(),
537                            ExprKind::BinaryOp(BinOp::Eq, e1, e2) => {
538                                Expr::binary_op(BinOp::Ne, e1, e2).at_opt(span)
539                            }
540                            _ => Expr::unary_op(UnOp::Not, e).at_opt(span),
541                        }
542                    }
543                    ExprKind::IfThenElse(p, e1, e2) => {
544                        let p = p.fold_with(self);
545                        if p.is_trivially_true() {
546                            e1.fold_with(self).at_opt(span)
547                        } else if p.is_trivially_false() {
548                            e2.fold_with(self).at_opt(span)
549                        } else {
550                            Expr::ite(p, e1.fold_with(self), e2.fold_with(self)).at_opt(span)
551                        }
552                    }
553                    _ => expr.super_fold_with(self),
554                }
555            }
556        }
557        self.fold_with(&mut Simplify { assumed_preds })
558    }
559
560    pub fn to_loc(&self) -> Option<Loc> {
561        match self.kind() {
562            ExprKind::Local(local) => Some(Loc::Local(*local)),
563            ExprKind::Var(var) => Some(Loc::Var(*var)),
564            _ => None,
565        }
566    }
567
568    pub fn to_path(&self) -> Option<Path> {
569        let mut expr = self;
570        let mut proj = vec![];
571        while let ExprKind::PathProj(e, field) = expr.kind() {
572            proj.push(*field);
573            expr = e;
574        }
575        proj.reverse();
576        Some(Path::new(expr.to_loc()?, proj))
577    }
578
579    /// Whether this is an aggregate expression with no fields.
580    pub fn is_unit(&self) -> bool {
581        matches!(self.kind(), ExprKind::Tuple(flds) if flds.is_empty())
582            || matches!(self.kind(), ExprKind::Ctor(Ctor::Struct(_), flds) if flds.is_empty())
583    }
584
585    pub fn eta_expand_abs(&self, inputs: &BoundVariableKinds, output: Sort) -> Lambda {
586        let args = (0..inputs.len())
587            .map(|idx| Expr::bvar(INNERMOST, BoundVar::from_usize(idx), BoundReftKind::Anon))
588            .collect();
589        let body = Expr::app(self, List::empty(), args);
590        Lambda::bind_with_vars(body, inputs.clone(), output)
591    }
592
593    /// Applies a field projection to an expression and optimistically try to beta reduce it
594    pub fn proj_and_reduce(&self, proj: FieldProj) -> Expr {
595        match self.kind() {
596            ExprKind::Tuple(flds) | ExprKind::Ctor(Ctor::Struct(_), flds) => {
597                flds[proj.field_idx() as usize].clone()
598            }
599            _ => Expr::field_proj(self.clone(), proj),
600        }
601    }
602
603    pub fn visit_conj<'a>(&'a self, mut f: impl FnMut(&'a Expr)) {
604        fn go<'a>(e: &'a Expr, f: &mut impl FnMut(&'a Expr)) {
605            if let ExprKind::BinaryOp(BinOp::And, e1, e2) = e.kind() {
606                go(e1, f);
607                go(e2, f);
608            } else {
609                f(e);
610            }
611        }
612        go(self, &mut f);
613    }
614
615    pub fn flatten_conjs(&self) -> Vec<&Expr> {
616        let mut vec = vec![];
617        self.visit_conj(|e| vec.push(e));
618        vec
619    }
620
621    pub fn has_evars(&self) -> bool {
622        struct HasEvars;
623
624        impl TypeVisitor for HasEvars {
625            type BreakTy = ();
626            fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<Self::BreakTy> {
627                if let ExprKind::Var(Var::EVar(_)) = expr.kind() {
628                    ControlFlow::Break(())
629                } else {
630                    expr.super_visit_with(self)
631                }
632            }
633        }
634
635        self.visit_with(&mut HasEvars).is_break()
636    }
637
638    pub fn erase_spans(&self) -> Expr {
639        struct SpanEraser;
640        impl TypeFolder for SpanEraser {
641            fn fold_expr(&mut self, e: &Expr) -> Expr {
642                e.super_fold_with(self).at_opt(None)
643            }
644        }
645        self.fold_with(&mut SpanEraser)
646    }
647}
648
649#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
650pub struct ESpan {
651    /// The top-level span information
652    pub span: Span,
653    /// The span for the (base) call-site for def-expanded spans
654    pub base: Option<Span>,
655}
656
657impl ESpan {
658    pub fn new(span: Span) -> Self {
659        Self { span, base: None }
660    }
661
662    pub fn with_base(&self, espan: ESpan) -> Self {
663        Self { span: self.span, base: Some(espan.span) }
664    }
665}
666
667#[derive(
668    Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, TypeFoldable, TypeVisitable,
669)]
670pub enum BinOp {
671    Iff,
672    Imp,
673    Or,
674    And,
675    Eq,
676    Ne,
677    Gt(Sort),
678    Ge(Sort),
679    Lt(Sort),
680    Le(Sort),
681    Add(Sort),
682    Sub(Sort),
683    Mul(Sort),
684    Div(Sort),
685    Mod(Sort),
686    BitAnd(Sort),
687    BitOr(Sort),
688    BitXor(Sort),
689    BitShl(Sort),
690    BitShr(Sort),
691}
692
693#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Debug, Decodable)]
694pub enum UnOp {
695    Not,
696    Neg,
697}
698
699#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, TyEncodable, TyDecodable)]
700pub enum Ctor {
701    /// for indices represented as `struct` in the refinement logic (e.g. using `refined_by` annotations)
702    Struct(DefId),
703    /// for indices represented as  `enum` in the refinement logic (e.g. using `reflected` annotations)
704    Enum(DefId, VariantIdx),
705}
706
707impl Ctor {
708    pub fn def_id(&self) -> DefId {
709        match self {
710            Self::Struct(def_id) | Self::Enum(def_id, _) => *def_id,
711        }
712    }
713
714    pub fn variant_idx(&self) -> VariantIdx {
715        match self {
716            Self::Struct(_) => FIRST_VARIANT,
717            Self::Enum(_, variant_idx) => *variant_idx,
718        }
719    }
720
721    fn is_enum(&self) -> bool {
722        matches!(self, Self::Enum(..))
723    }
724}
725
726/// # Primitive Properties
727/// Given a primop `op` with signature `(t1,...,tn) -> t`
728/// We define a refined type for `op` expressed as a `RuleMatcher`
729///
730/// ```text
731/// op :: (x1: t1, ..., xn: tn) -> { t[op_val[op](x1,...,xn)] | op_rel[x1,...,xn] }
732/// ```
733/// That is, using two *uninterpreted functions* `op_val` and `op_rel` that respectively denote
734/// 1. The _value_ of the primop, and
735/// 2. Some invariant _relation_ that holds for the primop.
736///
737/// The latter can be extended by the user via a `property` definition, which allows us
738/// to customize primops like `<<` with extra "facts" or lemmas. See `tests/tests/pos/surface/primops00.rs` for an example.
739#[derive(Debug, Clone, TyEncodable, TyDecodable, PartialEq, Eq, Hash)]
740pub enum InternalFuncKind {
741    /// UIF representing the value of a primop
742    Val(BinOp),
743    /// UIF representing the relationship of a primop
744    Rel(BinOp),
745    // Conversions betweeen Sorts
746    Cast,
747}
748
749#[derive(Debug, Clone, TyEncodable, TyDecodable, PartialEq, Eq, Hash)]
750pub enum SpecFuncKind {
751    /// Theory symbols *interpreted* by the SMT solver
752    Thy(liquid_fixpoint::ThyFunc),
753    /// User-defined uninterpreted functions with no definition
754    Uif(FluxDefId),
755    /// User-defined functions with a body definition
756    Def(FluxDefId),
757}
758
759#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, Debug, TyDecodable)]
760pub enum ExprKind {
761    Var(Var),
762    Local(Local),
763    Constant(Constant),
764    /// A rust constant. This can be either `DefKind::Const` or `DefKind::AssocConst`
765    ConstDefId(DefId),
766    BinaryOp(BinOp, Expr, Expr),
767    GlobalFunc(SpecFuncKind),
768    InternalFunc(InternalFuncKind),
769    UnaryOp(UnOp, Expr),
770    FieldProj(Expr, FieldProj),
771    /// A variant used in the logic to represent a variant of an ADT as a pair of the `DefId` and variant-index
772    Ctor(Ctor, List<Expr>),
773    Tuple(List<Expr>),
774    PathProj(Expr, FieldIdx),
775    IfThenElse(Expr, Expr, Expr),
776    KVar(KVar),
777    Alias(AliasReft, List<Expr>),
778    Let(Expr, Binder<Expr>),
779    /// Function application. The syntax allows arbitrary expressions in function position, but in
780    /// practice we are restricted by what's possible to encode in fixpoint. In a nutshell, we need
781    /// to make sure that expressions that can't be encoded are eliminated before we generate the
782    /// fixpoint constraint. Most notably, lambda abstractions have to be fully applied before
783    /// encoding into fixpoint (except when they appear as an index at the top-level).
784    App(Expr, List<SortArg>, List<Expr>),
785    /// Lambda abstractions. They are purely syntactic and we don't encode them in the logic. As such,
786    /// they have some syntactic restrictions that we must carefully maintain:
787    ///
788    /// 1. They can appear as an index at the top level.
789    /// 2. We can only substitute an abstraction for a variable in function position (or as an index).
790    ///    More generally, we need to partially evaluate expressions such that all abstractions in
791    ///    non-index position are eliminated before encoding into fixpoint. Right now, the
792    ///    implementation only evaluates abstractions that are immediately applied to arguments,
793    ///    thus the restriction.
794    Abs(Lambda),
795
796    /// Bounded quantifiers `exists i in 0..4 { pred(i) }` and `forall i in 0..4 { pred(i) }`.
797    BoundedQuant(fhir::QuantKind, fhir::Range, Binder<Expr>),
798    /// A hole is an expression that must be inferred either *semantically* by generating a kvar or
799    /// *syntactically* by generating an evar. Whether a hole can be inferred semantically or
800    /// syntactically depends on the position it appears: only holes appearing in predicate position
801    /// can be inferred with a kvar (provided it satisfies the fixpoint horn constraints) and only
802    /// holes used as an index (a position that fully determines their value) can be inferred with
803    /// an evar.
804    ///
805    /// Holes are implicitly defined in a scope, i.e., their solution could mention free and bound
806    /// variables in this scope. This must be considered when generating an inference variables for
807    /// them (either evar or kvar). In fact, the main reason we have holes is that we want to
808    /// decouple the places where we generate holes (where we don't want to worry about the scope),
809    /// and the places where we generate inference variable for them (where we do need to worry
810    /// about the scope).
811    Hole(HoleKind),
812    ForAll(Binder<Expr>),
813    /// Only for non-cuts solutions from fixpoint
814    Exists(Binder<Expr>),
815    /// Is the expression constructed from constructor of the given DefId (which should be `reflected` Enum)
816    IsCtor(DefId, VariantIdx, Expr),
817}
818
819impl ExprKind {
820    fn intern(self) -> Expr {
821        Expr { kind: Interned::new(self), espan: None }
822    }
823}
824
825#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
826pub enum AggregateKind {
827    Tuple(usize),
828    Adt(DefId),
829}
830
831#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
832pub enum FieldProj {
833    Tuple { arity: usize, field: u32 },
834    Adt { def_id: DefId, field: u32 },
835}
836
837impl FieldProj {
838    pub fn arity(&self, genv: GlobalEnv) -> QueryResult<usize> {
839        match self {
840            FieldProj::Tuple { arity, .. } => Ok(*arity),
841            FieldProj::Adt { def_id, .. } => {
842                Ok(genv.adt_sort_def_of(*def_id)?.struct_variant().fields())
843            }
844        }
845    }
846
847    pub fn field_idx(&self) -> u32 {
848        match self {
849            FieldProj::Tuple { field, .. } | FieldProj::Adt { field, .. } => *field,
850        }
851    }
852}
853
854/// The position where a [hole] appears. This determines how it will be inferred. This is related
855/// to, but not the same as, an [`InferMode`].
856///
857/// [`InferMode`]: super::InferMode
858/// [hole]: ExprKind::Hole
859#[derive(
860    Debug, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable,
861)]
862pub enum HoleKind {
863    /// A hole in predicate position (e.g., the predicate in a [`TyKind::Constr`]). It will be
864    /// inferred by generating a kvar.
865    ///
866    /// [`TyKind::Constr`]: super::TyKind::Constr
867    Pred,
868    /// A hole used as a refinement argument or index. It will be inferred by generating an evar.
869    /// The expression filling the hole must have the provided sort.
870    ///
871    /// NOTE(nilehmann) we used to require the `Sort` for generating the evar because we needed it
872    /// to eta-expand aggregate sorts. We've since removed this behavior but I'm keeping it here
873    /// just in case. We could remove in case it becomes too problematic.
874    Expr(Sort),
875}
876
877/// In theory a kvar is just an unknown predicate that can use some variables in scope. In practice,
878/// fixpoint makes a difference between the first and the rest of the arguments, the first one being
879/// the kvar's *self argument*. Fixpoint will only instantiate qualifiers that use the self argument.
880/// Flux generalizes the self argument to be a list. We call the rest of the arguments the *scope*.
881#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
882pub struct KVar {
883    pub kvid: KVid,
884    /// The number of arguments consider to be *self arguments*.
885    pub self_args: usize,
886    /// The list of *all* arguments with the self arguments at the beginning, i.e., the
887    /// list of self arguments followed by the scope.
888    pub args: List<Expr>,
889}
890
891#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, Decodable)]
892pub struct EarlyReftParam {
893    pub index: u32,
894    pub name: Symbol,
895}
896
897#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, Decodable, Debug)]
898pub struct BoundReft {
899    pub var: BoundVar,
900    pub kind: BoundReftKind,
901}
902
903#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
904pub enum Var {
905    Free(Name),
906    Bound(DebruijnIndex, BoundReft),
907    EarlyParam(EarlyReftParam),
908    EVar(EVid),
909    ConstGeneric(ParamConst),
910}
911
912#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
913pub struct Path {
914    pub loc: Loc,
915    projection: List<FieldIdx>,
916}
917
918#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
919pub enum Loc {
920    Local(Local),
921    Var(Var),
922}
923
924newtype_index! {
925    /// *E*xistential *v*ariable *id*
926    #[debug_format = "?{}e"]
927    #[orderable]
928    #[encodable]
929    pub struct EVid {}
930}
931
932newtype_index! {
933    #[debug_format = "$k{}"]
934    #[encodable]
935    pub struct KVid {}
936}
937
938newtype_index! {
939    #[debug_format = "a{}"]
940    #[orderable]
941    #[encodable]
942    pub struct Name {}
943}
944
945impl KVar {
946    pub fn new(kvid: KVid, self_args: usize, args: Vec<Expr>) -> Self {
947        KVar { kvid, self_args, args: List::from_vec(args) }
948    }
949
950    fn self_args(&self) -> &[Expr] {
951        &self.args[..self.self_args]
952    }
953
954    fn scope(&self) -> &[Expr] {
955        &self.args[self.self_args..]
956    }
957}
958
959impl Var {
960    pub fn to_expr(&self) -> Expr {
961        Expr::var(*self)
962    }
963}
964
965impl Path {
966    pub fn new(loc: Loc, projection: impl Into<List<FieldIdx>>) -> Path {
967        Path { loc, projection: projection.into() }
968    }
969
970    pub fn projection(&self) -> &[FieldIdx] {
971        &self.projection[..]
972    }
973
974    pub fn to_expr(&self) -> Expr {
975        self.projection
976            .iter()
977            .fold(self.loc.to_expr(), |e, f| Expr::path_proj(e, *f))
978    }
979
980    pub fn to_loc(&self) -> Option<Loc> {
981        if self.projection.is_empty() { Some(self.loc) } else { None }
982    }
983}
984
985impl Loc {
986    pub fn to_expr(&self) -> Expr {
987        match self {
988            Loc::Local(local) => Expr::local(*local),
989            Loc::Var(var) => Expr::var(*var),
990        }
991    }
992}
993
994macro_rules! impl_ops {
995    ($($op:ident: $method:ident),*) => {$(
996        impl<Rhs> std::ops::$op<Rhs> for Expr
997        where
998            Rhs: Into<Expr>,
999        {
1000            type Output = Expr;
1001
1002            fn $method(self, rhs: Rhs) -> Self::Output {
1003                let sort = crate::rty::Sort::Int;
1004                Expr::binary_op(BinOp::$op(sort), self, rhs)
1005            }
1006        }
1007
1008        impl<Rhs> std::ops::$op<Rhs> for &Expr
1009        where
1010            Rhs: Into<Expr>,
1011        {
1012            type Output = Expr;
1013
1014            fn $method(self, rhs: Rhs) -> Self::Output {
1015                let sort = crate::rty::Sort::Int;
1016                Expr::binary_op(BinOp::$op(sort), self, rhs)
1017            }
1018        }
1019    )*};
1020}
1021impl_ops!(Add: add, Sub: sub, Mul: mul, Div: div);
1022
1023impl From<i32> for Expr {
1024    fn from(value: i32) -> Self {
1025        Expr::constant(Constant::from(value))
1026    }
1027}
1028
1029impl From<&Expr> for Expr {
1030    fn from(e: &Expr) -> Self {
1031        e.clone()
1032    }
1033}
1034
1035impl From<Path> for Expr {
1036    fn from(path: Path) -> Self {
1037        path.to_expr()
1038    }
1039}
1040
1041impl From<Name> for Expr {
1042    fn from(name: Name) -> Self {
1043        Expr::fvar(name)
1044    }
1045}
1046
1047impl From<Var> for Expr {
1048    fn from(var: Var) -> Self {
1049        Expr::var(var)
1050    }
1051}
1052
1053impl From<SpecFuncKind> for Expr {
1054    fn from(kind: SpecFuncKind) -> Self {
1055        Expr::global_func(kind)
1056    }
1057}
1058
1059impl From<InternalFuncKind> for Expr {
1060    fn from(kind: InternalFuncKind) -> Self {
1061        Expr::internal_func(kind)
1062    }
1063}
1064
1065impl From<Loc> for Path {
1066    fn from(loc: Loc) -> Self {
1067        Path::new(loc, vec![])
1068    }
1069}
1070
1071impl From<Name> for Loc {
1072    fn from(name: Name) -> Self {
1073        Loc::Var(Var::Free(name))
1074    }
1075}
1076
1077impl From<Local> for Loc {
1078    fn from(local: Local) -> Self {
1079        Loc::Local(local)
1080    }
1081}
1082
1083#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable)]
1084pub struct Real(pub u128);
1085
1086#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable)]
1087pub enum Constant {
1088    Int(BigInt),
1089    Real(Real),
1090    Bool(bool),
1091    Str(Symbol),
1092    Char(char),
1093    BitVec(u128, u32),
1094}
1095
1096impl Constant {
1097    pub const ZERO: Constant = Constant::Int(BigInt::ZERO);
1098    pub const ONE: Constant = Constant::Int(BigInt::ONE);
1099    pub const TRUE: Constant = Constant::Bool(true);
1100
1101    fn to_bool(self) -> Option<bool> {
1102        match self {
1103            Constant::Bool(b) => Some(b),
1104            _ => None,
1105        }
1106    }
1107
1108    fn to_int(self) -> Option<BigInt> {
1109        match self {
1110            Constant::Int(n) => Some(n),
1111            _ => None,
1112        }
1113    }
1114
1115    pub fn iff(&self, other: &Constant) -> Option<Constant> {
1116        let b1 = self.to_bool()?;
1117        let b2 = other.to_bool()?;
1118        Some(Constant::Bool(b1 == b2))
1119    }
1120
1121    pub fn imp(&self, other: &Constant) -> Option<Constant> {
1122        let b1 = self.to_bool()?;
1123        let b2 = other.to_bool()?;
1124        Some(Constant::Bool(!b1 || b2))
1125    }
1126
1127    pub fn or(&self, other: &Constant) -> Option<Constant> {
1128        let b1 = self.to_bool()?;
1129        let b2 = other.to_bool()?;
1130        Some(Constant::Bool(b1 || b2))
1131    }
1132
1133    pub fn and(&self, other: &Constant) -> Option<Constant> {
1134        let b1 = self.to_bool()?;
1135        let b2 = other.to_bool()?;
1136        Some(Constant::Bool(b1 && b2))
1137    }
1138
1139    pub fn eq(&self, other: &Constant) -> Constant {
1140        Constant::Bool(*self == *other)
1141    }
1142
1143    pub fn ne(&self, other: &Constant) -> Constant {
1144        Constant::Bool(*self != *other)
1145    }
1146
1147    pub fn gt(&self, other: &Constant) -> Option<Constant> {
1148        let n1 = self.to_int()?;
1149        let n2 = other.to_int()?;
1150        Some(Constant::Bool(n1 > n2))
1151    }
1152
1153    pub fn ge(&self, other: &Constant) -> Option<Constant> {
1154        let n1 = self.to_int()?;
1155        let n2 = other.to_int()?;
1156        Some(Constant::Bool(n1 >= n2))
1157    }
1158
1159    pub fn from_scalar_int<'tcx, T>(tcx: TyCtxt<'tcx>, scalar: ScalarInt, t: &T) -> Option<Self>
1160    where
1161        T: ToRustc<'tcx, T = rustc_middle::ty::Ty<'tcx>>,
1162    {
1163        use rustc_middle::ty::TyKind;
1164        let ty = t.to_rustc(tcx);
1165        match ty.kind() {
1166            TyKind::Int(int_ty) => Some(Constant::from(scalar_to_int(tcx, scalar, *int_ty))),
1167            TyKind::Uint(uint_ty) => Some(Constant::from(scalar_to_uint(tcx, scalar, *uint_ty))),
1168            TyKind::Bool => {
1169                let b = scalar_to_bits(tcx, scalar, ty)?;
1170                Some(Constant::Bool(b != 0))
1171            }
1172            TyKind::Char => {
1173                let b = scalar_to_bits(tcx, scalar, ty)?;
1174                Some(Constant::Char(char::from_u32(b as u32)?))
1175            }
1176            _ => bug!(),
1177        }
1178    }
1179
1180    /// See [`BigInt::int_min`]
1181    pub fn int_min(bit_width: u32) -> Constant {
1182        Constant::Int(BigInt::int_min(bit_width))
1183    }
1184
1185    /// See [`BigInt::int_max`]
1186    pub fn int_max(bit_width: u32) -> Constant {
1187        Constant::Int(BigInt::int_max(bit_width))
1188    }
1189
1190    /// See [`BigInt::uint_max`]
1191    pub fn uint_max(bit_width: u32) -> Constant {
1192        Constant::Int(BigInt::uint_max(bit_width))
1193    }
1194}
1195
1196impl From<i32> for Constant {
1197    fn from(c: i32) -> Self {
1198        Constant::Int(c.into())
1199    }
1200}
1201
1202impl From<usize> for Constant {
1203    fn from(u: usize) -> Self {
1204        Constant::Int(u.into())
1205    }
1206}
1207
1208impl From<u32> for Constant {
1209    fn from(c: u32) -> Self {
1210        Constant::Int(c.into())
1211    }
1212}
1213
1214impl From<u128> for Constant {
1215    fn from(c: u128) -> Self {
1216        Constant::Int(c.into())
1217    }
1218}
1219
1220impl From<i128> for Constant {
1221    fn from(c: i128) -> Self {
1222        Constant::Int(c.into())
1223    }
1224}
1225
1226impl From<bool> for Constant {
1227    fn from(b: bool) -> Self {
1228        Constant::Bool(b)
1229    }
1230}
1231
1232impl From<Symbol> for Constant {
1233    fn from(s: Symbol) -> Self {
1234        Constant::Str(s)
1235    }
1236}
1237
1238impl From<char> for Constant {
1239    fn from(c: char) -> Self {
1240        Constant::Char(c)
1241    }
1242}
1243
1244impl_internable!(ExprKind);
1245impl_slice_internable!(Expr, KVar);
1246
1247#[derive(Debug)]
1248pub struct FieldBind<T> {
1249    pub name: Symbol,
1250    pub value: T,
1251}
1252
1253impl<T: Pretty> Pretty for FieldBind<T> {
1254    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1255        w!(cx, f, "{}: {:?}", ^self.name, &self.value)
1256    }
1257}
1258
1259pub(crate) mod pretty {
1260    use flux_rustc_bridge::def_id_to_string;
1261
1262    use super::*;
1263    use crate::{name_of_thy_func, rty::pretty::nested_with_bound_vars};
1264
1265    #[derive(PartialEq, Eq, PartialOrd, Ord)]
1266    enum Precedence {
1267        Iff,
1268        Imp,
1269        Or,
1270        And,
1271        Cmp,
1272        Bitvec,
1273        AddSub,
1274        MulDiv,
1275    }
1276
1277    impl BinOp {
1278        fn precedence(&self) -> Precedence {
1279            match self {
1280                BinOp::Iff => Precedence::Iff,
1281                BinOp::Imp => Precedence::Imp,
1282                BinOp::Or => Precedence::Or,
1283                BinOp::And => Precedence::And,
1284                BinOp::Eq
1285                | BinOp::Ne
1286                | BinOp::Gt(_)
1287                | BinOp::Lt(_)
1288                | BinOp::Ge(_)
1289                | BinOp::Le(_) => Precedence::Cmp,
1290                BinOp::Add(_) | BinOp::Sub(_) => Precedence::AddSub,
1291                BinOp::Mul(_) | BinOp::Div(_) | BinOp::Mod(_) => Precedence::MulDiv,
1292                BinOp::BitAnd(_)
1293                | BinOp::BitOr(_)
1294                | BinOp::BitShl(_)
1295                | BinOp::BitShr(_)
1296                | BinOp::BitXor(_) => Precedence::Bitvec,
1297            }
1298        }
1299    }
1300
1301    impl Precedence {
1302        pub fn is_associative(&self) -> bool {
1303            !matches!(self, Precedence::Imp | Precedence::Cmp)
1304        }
1305    }
1306
1307    pub fn should_parenthesize(op: &BinOp, child: &Expr) -> bool {
1308        if let ExprKind::BinaryOp(child_op, ..) = child.kind() {
1309            child_op.precedence() < op.precedence()
1310                || (child_op.precedence() == op.precedence() && !op.precedence().is_associative())
1311        } else {
1312            false
1313        }
1314    }
1315
1316    impl Pretty for Ctor {
1317        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1318            match self {
1319                Ctor::Struct(def_id) => {
1320                    w!(cx, f, "{:?}", def_id)
1321                }
1322                Ctor::Enum(def_id, variant_idx) => {
1323                    w!(cx, f, "{:?}::{:?}", def_id, ^variant_idx)
1324                }
1325            }
1326        }
1327    }
1328
1329    impl Pretty for fhir::QuantKind {
1330        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1331            match self {
1332                fhir::QuantKind::Exists => w!(cx, f, "∃"),
1333                fhir::QuantKind::Forall => w!(cx, f, "∀"),
1334            }
1335        }
1336    }
1337
1338    impl Pretty for InternalFuncKind {
1339        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1340            match self {
1341                InternalFuncKind::Val(op) => w!(cx, f, "[{:?}]", op),
1342                InternalFuncKind::Rel(op) => w!(cx, f, "[{:?}]?", op),
1343                InternalFuncKind::Cast => w!(cx, f, "cast"),
1344            }
1345        }
1346    }
1347
1348    impl Pretty for Expr {
1349        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1350            let e = if cx.simplify_exprs {
1351                self.simplify(&SnapshotMap::default())
1352            } else {
1353                self.clone()
1354            };
1355            match e.kind() {
1356                ExprKind::Var(var) => w!(cx, f, "{:?}", var),
1357                ExprKind::Local(local) => w!(cx, f, "{:?}", ^local),
1358                ExprKind::ConstDefId(did) => w!(cx, f, "{}", ^def_id_to_string(*did)),
1359                ExprKind::Constant(c) => w!(cx, f, "{:?}", c),
1360
1361                ExprKind::BinaryOp(op, e1, e2) => {
1362                    if should_parenthesize(op, e1) {
1363                        w!(cx, f, "({:?})", e1)?;
1364                    } else {
1365                        w!(cx, f, "{:?}", e1)?;
1366                    }
1367                    if matches!(op, BinOp::Div(_)) {
1368                        w!(cx, f, "{:?}", op)?;
1369                    } else {
1370                        w!(cx, f, " {:?} ", op)?;
1371                    }
1372                    if should_parenthesize(op, e2) {
1373                        w!(cx, f, "({:?})", e2)?;
1374                    } else {
1375                        w!(cx, f, "{:?}", e2)?;
1376                    }
1377                    Ok(())
1378                }
1379                ExprKind::UnaryOp(op, e) => {
1380                    if e.is_atom() {
1381                        w!(cx, f, "{:?}{:?}", op, e)
1382                    } else {
1383                        w!(cx, f, "{:?}({:?})", op, e)
1384                    }
1385                }
1386                ExprKind::FieldProj(e, proj) if let ExprKind::Ctor(_, _) = e.kind() => {
1387                    // special case to avoid printing `{n:12}.n` as `12.n` but instead, just print `12`
1388                    // TODO: maintain an invariant that `FieldProj` never has a Ctor as first argument (as always reduced)
1389                    w!(cx, f, "{:?}", e.proj_and_reduce(*proj))
1390                }
1391                ExprKind::FieldProj(e, proj) => {
1392                    if e.is_atom() {
1393                        w!(cx, f, "{:?}.{}", e, ^fmt_field_proj(cx, *proj))
1394                    } else {
1395                        w!(cx, f, "({:?}).{}", e, ^fmt_field_proj(cx, *proj))
1396                    }
1397                }
1398                ExprKind::Tuple(flds) => {
1399                    if let [e] = &flds[..] {
1400                        w!(cx, f, "({:?},)", e)
1401                    } else {
1402                        w!(cx, f, "({:?})", join!(", ", flds))
1403                    }
1404                }
1405                ExprKind::IsCtor(def_id, variant_idx, idx) => {
1406                    w!(cx, f, "({:?} is {:?}::{:?})", idx, def_id, ^variant_idx)
1407                }
1408                ExprKind::Ctor(ctor, flds) => {
1409                    let def_id = ctor.def_id();
1410                    if let Some(adt_sort_def) = cx.adt_sort_def_of(def_id) {
1411                        let variant = adt_sort_def.variant(ctor.variant_idx()).field_names();
1412                        let fields = iter::zip(variant, flds)
1413                            .map(|(name, value)| FieldBind { name: *name, value: value.clone() })
1414                            .collect_vec();
1415                        match ctor {
1416                            Ctor::Struct(_) => {
1417                                w!(cx, f, "{:?} {{ {:?} }}", def_id, join!(", ", fields))
1418                            }
1419                            Ctor::Enum(_, idx) => {
1420                                if fields.is_empty() {
1421                                    w!(cx, f, "{:?}::{:?}", def_id, ^idx.index())
1422                                } else {
1423                                    w!(cx, f, "{:?}::{:?}({:?})", def_id, ^idx.index(), join!(", ", fields))
1424                                }
1425                            }
1426                        }
1427                    } else {
1428                        match ctor {
1429                            Ctor::Struct(_) => {
1430                                w!(cx, f, "{:?} {{ {:?} }}", def_id, join!(", ", flds))
1431                            }
1432                            Ctor::Enum(_, idx) => {
1433                                w!(cx, f, "{:?}::{:?} {{ {:?} }}", def_id, ^idx, join!(", ", flds))
1434                            }
1435                        }
1436                    }
1437                }
1438                ExprKind::PathProj(e, field) => {
1439                    if e.is_atom() {
1440                        w!(cx, f, "{:?}.{:?}", e, field)
1441                    } else {
1442                        w!(cx, f, "({:?}).{:?}", e, field)
1443                    }
1444                }
1445                ExprKind::App(func, _, args) => {
1446                    w!(cx, f, "{:?}({})",
1447                        parens!(func, !func.is_atom()),
1448                        ^args
1449                            .iter()
1450                            .format_with(", ", |arg, f| f(&format_args_cx!(cx, "{:?}", arg)))
1451                    )
1452                }
1453                ExprKind::IfThenElse(p, e1, e2) => {
1454                    w!(cx, f, "if {:?} {{ {:?} }} else {{ {:?} }}", p, e1, e2)
1455                }
1456                ExprKind::Hole(_) => {
1457                    w!(cx, f, "*")
1458                }
1459                ExprKind::KVar(kvar) => {
1460                    w!(cx, f, "{:?}", kvar)
1461                }
1462                ExprKind::Alias(alias, args) => {
1463                    w!(cx, f, "{:?}({:?})", alias, join!(", ", args))
1464                }
1465                ExprKind::Abs(lam) => {
1466                    w!(cx, f, "{:?}", lam)
1467                }
1468                ExprKind::GlobalFunc(SpecFuncKind::Def(did) | SpecFuncKind::Uif(did)) => {
1469                    w!(cx, f, "{}", ^did.name())
1470                }
1471                ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => {
1472                    if let Some(name) = name_of_thy_func(*itf) {
1473                        w!(cx, f, "{}", ^name)
1474                    } else {
1475                        w!(cx, f, "<error>")
1476                    }
1477                }
1478                ExprKind::InternalFunc(func) => {
1479                    w!(cx, f, "{:?}", func)
1480                }
1481                ExprKind::ForAll(expr) => {
1482                    let vars = expr.vars();
1483                    cx.with_bound_vars(vars, || {
1484                        if !vars.is_empty() {
1485                            cx.fmt_bound_vars(false, "∀", vars, ". ", f)?;
1486                        }
1487                        w!(cx, f, "{:?}", expr.skip_binder_ref())
1488                    })
1489                }
1490                ExprKind::Exists(expr) => {
1491                    let vars = expr.vars();
1492                    cx.with_bound_vars(vars, || {
1493                        if !vars.is_empty() {
1494                            cx.fmt_bound_vars(false, "∃", vars, ". ", f)?;
1495                        }
1496                        w!(cx, f, "{:?}", expr.skip_binder_ref())
1497                    })
1498                }
1499                ExprKind::BoundedQuant(kind, rng, body) => {
1500                    let vars = body.vars();
1501                    cx.with_bound_vars(vars, || {
1502                        w!(
1503                            cx,
1504                            f,
1505                            "{:?} {}..{} {{ {:?} }}",
1506                            kind,
1507                            ^rng.start,
1508                            ^rng.end,
1509                            body.skip_binder_ref()
1510                        )
1511                    })
1512                }
1513                ExprKind::Let(init, body) => {
1514                    let vars = body.vars();
1515                    cx.with_bound_vars(vars, || {
1516                        cx.fmt_bound_vars(false, "(let ", vars, " = ", f)?;
1517                        w!(cx, f, "{:?} in {:?})", init, body.skip_binder_ref())
1518                    })
1519                }
1520            }
1521        }
1522    }
1523
1524    fn fmt_field_proj(cx: &PrettyCx, proj: FieldProj) -> String {
1525        if let FieldProj::Adt { def_id, field } = proj
1526            && let Some(adt_sort_def) = cx.adt_sort_def_of(def_id)
1527        {
1528            format!("{}", adt_sort_def.struct_variant().field_names()[field as usize])
1529        } else {
1530            format!("{}", proj.field_idx())
1531        }
1532    }
1533
1534    impl Pretty for Constant {
1535        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1536            match self {
1537                Constant::Int(i) => w!(cx, f, "{i}"),
1538                Constant::BitVec(i, sz) => w!(cx, f, "bv({i}, {sz})"),
1539                Constant::Real(r) => w!(cx, f, "{}.0", ^r.0),
1540                Constant::Bool(b) => w!(cx, f, "{b}"),
1541                Constant::Str(sym) => w!(cx, f, "\"{sym}\""),
1542                Constant::Char(c) => w!(cx, f, "\'{c}\'"),
1543            }
1544        }
1545    }
1546
1547    impl Pretty for AliasReft {
1548        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1549            w!(cx, f, "<({:?}) as {:?}", &self.args[0], self.assoc_id.parent())?;
1550            let args = &self.args[1..];
1551            if !args.is_empty() {
1552                w!(cx, f, "<{:?}>", join!(", ", args))?;
1553            }
1554            w!(cx, f, ">::{}", ^self.assoc_id.name())
1555        }
1556    }
1557
1558    impl Pretty for Lambda {
1559        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1560            let vars = self.body.vars();
1561            // TODO: remove redundant vars; see Ty
1562            // let redundant_bvars = self.body.redundant_bvars().into_iter().collect();
1563            cx.with_bound_vars(vars, || {
1564                cx.fmt_bound_vars(false, "λ", vars, ". ", f)?;
1565                w!(cx, f, "{:?}", self.body.as_ref().skip_binder())
1566            })
1567        }
1568    }
1569
1570    impl Pretty for Var {
1571        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1572            match self {
1573                Var::Bound(debruijn, var) => cx.fmt_bound_reft(*debruijn, *var, f),
1574                Var::EarlyParam(var) => w!(cx, f, "{}", ^var.name),
1575                Var::Free(name) => w!(cx, f, "{:?}", ^name),
1576                Var::EVar(evar) => w!(cx, f, "{:?}", ^evar),
1577                Var::ConstGeneric(param) => w!(cx, f, "{}", ^param.name),
1578            }
1579        }
1580    }
1581
1582    impl Pretty for KVar {
1583        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1584            w!(cx, f, "{:?}", ^self.kvid)?;
1585            match cx.kvar_args {
1586                KVarArgs::All => {
1587                    w!(
1588                        cx,
1589                        f,
1590                        "({:?})[{:?}]",
1591                        join!(", ", self.self_args()),
1592                        join!(", ", self.scope())
1593                    )?;
1594                }
1595                KVarArgs::SelfOnly => w!(cx, f, "({:?})", join!(", ", self.self_args()))?,
1596                KVarArgs::Hide => {}
1597            }
1598            Ok(())
1599        }
1600    }
1601
1602    impl Pretty for Path {
1603        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1604            w!(cx, f, "{:?}", &self.loc)?;
1605            for field in &self.projection {
1606                w!(cx, f, ".{}", ^u32::from(*field))?;
1607            }
1608            Ok(())
1609        }
1610    }
1611
1612    impl Pretty for Loc {
1613        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1614            match self {
1615                Loc::Local(local) => w!(cx, f, "{:?}", ^local),
1616                Loc::Var(var) => w!(cx, f, "{:?}", var),
1617            }
1618        }
1619    }
1620
1621    impl Pretty for BinOp {
1622        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1623            match self {
1624                BinOp::Iff => w!(cx, f, "⇔"),
1625                BinOp::Imp => w!(cx, f, "⇒"),
1626                BinOp::Or => w!(cx, f, "∨"),
1627                BinOp::And => w!(cx, f, "∧"),
1628                BinOp::Eq => w!(cx, f, "="),
1629                BinOp::Ne => w!(cx, f, "≠"),
1630                BinOp::Gt(_) => w!(cx, f, ">"),
1631                BinOp::Ge(_) => w!(cx, f, "≥"),
1632                BinOp::Lt(_) => w!(cx, f, "<"),
1633                BinOp::Le(_) => w!(cx, f, "≤"),
1634                BinOp::Add(_) => w!(cx, f, "+"),
1635                BinOp::Sub(_) => w!(cx, f, "-"),
1636                BinOp::Mul(_) => w!(cx, f, "*"),
1637                BinOp::Div(_) => w!(cx, f, "/"),
1638                BinOp::Mod(_) => w!(cx, f, "mod"),
1639                BinOp::BitAnd(_) => w!(cx, f, "&"),
1640                BinOp::BitOr(_) => w!(cx, f, "|"),
1641                BinOp::BitXor(_) => w!(cx, f, "^"),
1642                BinOp::BitShl(_) => w!(cx, f, "<<"),
1643                BinOp::BitShr(_) => w!(cx, f, ">>"),
1644            }
1645        }
1646    }
1647
1648    impl Pretty for UnOp {
1649        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1650            match self {
1651                UnOp::Not => w!(cx, f, "¬"),
1652                UnOp::Neg => w!(cx, f, "-"),
1653            }
1654        }
1655    }
1656
1657    impl_debug_with_default_cx!(Expr, Loc, Path, Var, KVar, Lambda, AliasReft);
1658
1659    impl PrettyNested for Lambda {
1660        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
1661            // TODO: remove redundant vars; see Ty
1662            nested_with_bound_vars(cx, "λ", self.body.vars(), None, |prefix| {
1663                let expr_d = self.body.skip_binder_ref().fmt_nested(cx)?;
1664                let text = format!("{}{}", prefix, expr_d.text);
1665                Ok(NestedString { text, children: expr_d.children, key: None })
1666            })
1667        }
1668    }
1669
1670    pub fn aggregate_nested(
1671        cx: &PrettyCx,
1672        ctor: &Ctor,
1673        flds: &[Expr],
1674        is_named: bool,
1675    ) -> Result<NestedString, fmt::Error> {
1676        let def_id = ctor.def_id();
1677        let mut text =
1678            if is_named && ctor.is_enum() { format_cx!(cx, "{:?}", ctor) } else { "".to_string() };
1679        if flds.is_empty() {
1680            // No fields, no index
1681            Ok(NestedString { text, children: None, key: None })
1682        } else if flds.len() == 1 {
1683            // Single field, inline index
1684            text += &format_cx!(cx, "{:?}", flds[0].clone());
1685            Ok(NestedString { text, children: None, key: None })
1686        } else {
1687            let keys = if let Some(adt_sort_def) = cx.adt_sort_def_of(def_id) {
1688                adt_sort_def
1689                    .variant(ctor.variant_idx())
1690                    .field_names()
1691                    .iter()
1692                    .map(|name| format!("{name}"))
1693                    .collect_vec()
1694            } else {
1695                (0..flds.len()).map(|i| format!("arg{i}")).collect_vec()
1696            };
1697            // Multiple fields, nested index
1698            text += "{..}";
1699            let mut children = vec![];
1700            for (key, fld) in iter::zip(keys, flds) {
1701                let fld_d = fld.fmt_nested(cx)?;
1702                children.push(NestedString { key: Some(key), ..fld_d });
1703            }
1704            Ok(NestedString { text, children: Some(children), key: None })
1705        }
1706    }
1707
1708    impl PrettyNested for Expr {
1709        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
1710            let e = if cx.simplify_exprs {
1711                self.simplify(&SnapshotMap::default())
1712            } else {
1713                self.clone()
1714            };
1715            match e.kind() {
1716                ExprKind::Var(..)
1717                | ExprKind::Local(..)
1718                | ExprKind::Constant(..)
1719                | ExprKind::ConstDefId(..)
1720                | ExprKind::Hole(..)
1721                | ExprKind::GlobalFunc(..)
1722                | ExprKind::InternalFunc(..)
1723                | ExprKind::KVar(..) => debug_nested(cx, &e),
1724
1725                ExprKind::IfThenElse(p, e1, e2) => {
1726                    let p_d = p.fmt_nested(cx)?;
1727                    let e1_d = e1.fmt_nested(cx)?;
1728                    let e2_d = e2.fmt_nested(cx)?;
1729                    let text = format!("(if {} then {} else {})", p_d.text, e1_d.text, e2_d.text);
1730                    let children = float_children(vec![p_d.children, e1_d.children, e2_d.children]);
1731                    Ok(NestedString { text, children, key: None })
1732                }
1733                ExprKind::BinaryOp(op, e1, e2) => {
1734                    let e1_d = e1.fmt_nested(cx)?;
1735                    let e2_d = e2.fmt_nested(cx)?;
1736                    let e1_text = if should_parenthesize(op, e1) {
1737                        format!("({})", e1_d.text)
1738                    } else {
1739                        e1_d.text
1740                    };
1741                    let e2_text = if should_parenthesize(op, e2) {
1742                        format!("({})", e2_d.text)
1743                    } else {
1744                        e2_d.text
1745                    };
1746                    let op_d = debug_nested(cx, op)?;
1747                    let op_text = if matches!(op, BinOp::Div(_)) {
1748                        op_d.text
1749                    } else {
1750                        format!(" {} ", op_d.text)
1751                    };
1752                    let text = format!("{e1_text}{op_text}{e2_text}");
1753                    let children = float_children(vec![e1_d.children, e2_d.children]);
1754                    Ok(NestedString { text, children, key: None })
1755                }
1756                ExprKind::UnaryOp(op, e) => {
1757                    let e_d = e.fmt_nested(cx)?;
1758                    let op_d = debug_nested(cx, op)?;
1759                    let text = if e.is_atom() {
1760                        format!("{}{}", op_d.text, e_d.text)
1761                    } else {
1762                        format!("{}({})", op_d.text, e_d.text)
1763                    };
1764                    Ok(NestedString { text, children: e_d.children, key: None })
1765                }
1766                ExprKind::FieldProj(e, proj) if let ExprKind::Ctor(_, _) = e.kind() => {
1767                    // special case to avoid printing `{n:12}.n` as `12.n` but instead, just print `12`
1768                    // TODO: maintain an invariant that `FieldProj` never has a Ctor as first argument (as always reduced)
1769                    e.proj_and_reduce(*proj).fmt_nested(cx)
1770                }
1771                ExprKind::FieldProj(e, proj) => {
1772                    let e_d = e.fmt_nested(cx)?;
1773                    let text = if e.is_atom() {
1774                        format!("{}.{}", e_d.text, fmt_field_proj(cx, *proj))
1775                    } else {
1776                        format!("({}).{}", e_d.text, fmt_field_proj(cx, *proj))
1777                    };
1778                    Ok(NestedString { text, children: e_d.children, key: None })
1779                }
1780                ExprKind::Tuple(flds) => {
1781                    let mut texts = vec![];
1782                    let mut kidss = vec![];
1783                    for e in flds {
1784                        let e_d = e.fmt_nested(cx)?;
1785                        texts.push(e_d.text);
1786                        kidss.push(e_d.children);
1787                    }
1788                    let text = if let [e] = &texts[..] {
1789                        format!("({e},)")
1790                    } else {
1791                        format!("({})", texts.join(", "))
1792                    };
1793                    let children = float_children(kidss);
1794                    Ok(NestedString { text, children, key: None })
1795                }
1796                ExprKind::Ctor(ctor, flds) => aggregate_nested(cx, ctor, flds, true),
1797                ExprKind::IsCtor(def_id, variant_idx, idx) => {
1798                    let text = format!("is::{:?}::{:?}( {:?} )", def_id, variant_idx, idx);
1799                    Ok(NestedString { text, children: None, key: None })
1800                }
1801                ExprKind::PathProj(e, field) => {
1802                    let e_d = e.fmt_nested(cx)?;
1803                    let text = if e.is_atom() {
1804                        format!("{}.{:?}", e_d.text, field)
1805                    } else {
1806                        format!("({}).{:?}", e_d.text, field)
1807                    };
1808                    Ok(NestedString { text, children: e_d.children, key: None })
1809                }
1810                ExprKind::Alias(alias, args) => {
1811                    let mut texts = vec![];
1812                    let mut kidss = vec![];
1813                    for arg in args {
1814                        let arg_d = arg.fmt_nested(cx)?;
1815                        texts.push(arg_d.text);
1816                        kidss.push(arg_d.children);
1817                    }
1818                    let text = format_cx!(cx, "{:?}({:?})", alias, texts.join(", "));
1819                    let children = float_children(kidss);
1820                    Ok(NestedString { text, children, key: None })
1821                }
1822                ExprKind::App(func, _, args) => {
1823                    let func_d = func.fmt_nested(cx)?;
1824                    let mut texts = vec![];
1825                    let mut kidss = vec![func_d.children];
1826                    for arg in args {
1827                        let arg_d = arg.fmt_nested(cx)?;
1828                        texts.push(arg_d.text);
1829                        kidss.push(arg_d.children);
1830                    }
1831                    let text = if func.is_atom() {
1832                        format!("{}({})", func_d.text, texts.join(", "))
1833                    } else {
1834                        format!("({})({})", func_d.text, texts.join(", "))
1835                    };
1836                    let children = float_children(kidss);
1837                    Ok(NestedString { text, children, key: None })
1838                }
1839                ExprKind::Abs(lambda) => lambda.fmt_nested(cx),
1840                ExprKind::Let(init, body) => {
1841                    // FIXME this is very wrong!
1842                    nested_with_bound_vars(cx, "let", body.vars(), None, |prefix| {
1843                        let body = body.skip_binder_ref().fmt_nested(cx)?;
1844                        let text = format!("{:?} {}{}", init, prefix, body.text);
1845                        Ok(NestedString { text, children: body.children, key: None })
1846                    })
1847                }
1848                ExprKind::BoundedQuant(kind, rng, body) => {
1849                    let left = match kind {
1850                        fhir::QuantKind::Forall => "∀",
1851                        fhir::QuantKind::Exists => "∃",
1852                    };
1853                    let right = Some(format!(" in {}..{}", rng.start, rng.end));
1854
1855                    nested_with_bound_vars(cx, left, body.vars(), right, |all_str| {
1856                        let expr_d = body.as_ref().skip_binder().fmt_nested(cx)?;
1857                        let text = format!("{}{}", all_str, expr_d.text);
1858                        Ok(NestedString { text, children: expr_d.children, key: None })
1859                    })
1860                }
1861                ExprKind::ForAll(expr) => {
1862                    nested_with_bound_vars(cx, "∀", expr.vars(), None, |all_str| {
1863                        let expr_d = expr.as_ref().skip_binder().fmt_nested(cx)?;
1864                        let text = format!("{}{}", all_str, expr_d.text);
1865                        Ok(NestedString { text, children: expr_d.children, key: None })
1866                    })
1867                }
1868                ExprKind::Exists(expr) => {
1869                    nested_with_bound_vars(cx, "∀", expr.vars(), None, |all_str| {
1870                        let expr_d = expr.as_ref().skip_binder().fmt_nested(cx)?;
1871                        let text = format!("{}{}", all_str, expr_d.text);
1872                        Ok(NestedString { text, children: expr_d.children, key: None })
1873                    })
1874                }
1875            }
1876        }
1877    }
1878}