flux_middle/rty/
expr.rs

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