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 itertools::Itertools;
12use liquid_fixpoint::ThyFunc;
13use rustc_abi::{FIRST_VARIANT, FieldIdx};
14use rustc_data_structures::{fx::FxHashMap, 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 function. This can be either a function with a body or a UIF.
754    Def(FluxDefId),
755}
756
757#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, Debug, TyDecodable)]
758pub enum ExprKind {
759    Var(Var),
760    Local(Local),
761    Constant(Constant),
762    /// A rust constant. This can be either `DefKind::Const` or `DefKind::AssocConst`
763    ConstDefId(DefId),
764    BinaryOp(BinOp, Expr, Expr),
765    GlobalFunc(SpecFuncKind),
766    InternalFunc(InternalFuncKind),
767    UnaryOp(UnOp, Expr),
768    FieldProj(Expr, FieldProj),
769    /// A variant used in the logic to represent a variant of an ADT as a pair of the `DefId` and variant-index
770    Ctor(Ctor, List<Expr>),
771    Tuple(List<Expr>),
772    PathProj(Expr, FieldIdx),
773    IfThenElse(Expr, Expr, Expr),
774    KVar(KVar),
775    Alias(AliasReft, List<Expr>),
776    Let(Expr, Binder<Expr>),
777    /// Function application. The syntax allows arbitrary expressions in function position, but in
778    /// practice we are restricted by what's possible to encode in fixpoint. In a nutshell, we need
779    /// to make sure that expressions that can't be encoded are eliminated before we generate the
780    /// fixpoint constraint. Most notably, lambda abstractions have to be fully applied before
781    /// encoding into fixpoint (except when they appear as an index at the top-level).
782    App(Expr, List<SortArg>, List<Expr>),
783    /// Lambda abstractions. They are purely syntactic and we don't encode them in the logic. As such,
784    /// they have some syntactic restrictions that we must carefully maintain:
785    ///
786    /// 1. They can appear as an index at the top level.
787    /// 2. We can only substitute an abstraction for a variable in function position (or as an index).
788    ///    More generally, we need to partially evaluate expressions such that all abstractions in
789    ///    non-index position are eliminated before encoding into fixpoint. Right now, the
790    ///    implementation only evaluates abstractions that are immediately applied to arguments,
791    ///    thus the restriction.
792    Abs(Lambda),
793
794    /// Bounded quantifiers `exists i in 0..4 { pred(i) }` and `forall i in 0..4 { pred(i) }`.
795    BoundedQuant(fhir::QuantKind, fhir::Range, Binder<Expr>),
796    /// A hole is an expression that must be inferred either *semantically* by generating a kvar or
797    /// *syntactically* by generating an evar. Whether a hole can be inferred semantically or
798    /// syntactically depends on the position it appears: only holes appearing in predicate position
799    /// can be inferred with a kvar (provided it satisfies the fixpoint horn constraints) and only
800    /// holes used as an index (a position that fully determines their value) can be inferred with
801    /// an evar.
802    ///
803    /// Holes are implicitly defined in a scope, i.e., their solution could mention free and bound
804    /// variables in this scope. This must be considered when generating an inference variables for
805    /// them (either evar or kvar). In fact, the main reason we have holes is that we want to
806    /// decouple the places where we generate holes (where we don't want to worry about the scope),
807    /// and the places where we generate inference variable for them (where we do need to worry
808    /// about the scope).
809    Hole(HoleKind),
810    ForAll(Binder<Expr>),
811    /// Only for non-cuts solutions from fixpoint
812    Exists(Binder<Expr>),
813    /// Is the expression constructed from constructor of the given DefId (which should be `reflected` Enum)
814    IsCtor(DefId, VariantIdx, Expr),
815}
816
817impl ExprKind {
818    fn intern(self) -> Expr {
819        Expr { kind: Interned::new(self), espan: None }
820    }
821}
822
823#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
824pub enum AggregateKind {
825    Tuple(usize),
826    Adt(DefId),
827}
828
829#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
830pub enum FieldProj {
831    Tuple { arity: usize, field: u32 },
832    Adt { def_id: DefId, field: u32 },
833}
834
835impl FieldProj {
836    pub fn arity(&self, genv: GlobalEnv) -> QueryResult<usize> {
837        match self {
838            FieldProj::Tuple { arity, .. } => Ok(*arity),
839            FieldProj::Adt { def_id, .. } => {
840                Ok(genv.adt_sort_def_of(*def_id)?.struct_variant().fields())
841            }
842        }
843    }
844
845    pub fn field_idx(&self) -> u32 {
846        match self {
847            FieldProj::Tuple { field, .. } | FieldProj::Adt { field, .. } => *field,
848        }
849    }
850}
851
852/// The position where a [hole] appears. This determines how it will be inferred. This is related
853/// to, but not the same as, an [`InferMode`].
854///
855/// [`InferMode`]: super::InferMode
856/// [hole]: ExprKind::Hole
857#[derive(
858    Debug, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable,
859)]
860pub enum HoleKind {
861    /// A hole in predicate position (e.g., the predicate in a [`TyKind::Constr`]). It will be
862    /// inferred by generating a kvar.
863    ///
864    /// [`TyKind::Constr`]: super::TyKind::Constr
865    Pred,
866    /// A hole used as a refinement argument or index. It will be inferred by generating an evar.
867    /// The expression filling the hole must have the provided sort.
868    ///
869    /// NOTE(nilehmann) we used to require the `Sort` for generating the evar because we needed it
870    /// to eta-expand aggregate sorts. We've since removed this behavior but I'm keeping it here
871    /// just in case. We could remove in case it becomes too problematic.
872    Expr(Sort),
873}
874
875/// In theory a kvar is just an unknown predicate that can use some variables in scope. In practice,
876/// fixpoint makes a difference between the first and the rest of the arguments, the first one being
877/// the kvar's *self argument*. Fixpoint will only instantiate qualifiers that use the self argument.
878/// Flux generalizes the self argument to be a list. We call the rest of the arguments the *scope*.
879#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
880pub struct KVar {
881    pub kvid: KVid,
882    /// The number of arguments consider to be *self arguments*.
883    pub self_args: usize,
884    /// The list of *all* arguments with the self arguments at the beginning, i.e., the
885    /// list of self arguments followed by the scope.
886    pub args: List<Expr>,
887}
888
889#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, Decodable)]
890pub struct EarlyReftParam {
891    pub index: u32,
892    pub name: Symbol,
893}
894
895#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, Decodable, Debug)]
896pub struct BoundReft {
897    pub var: BoundVar,
898    pub kind: BoundReftKind,
899}
900
901#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
902pub enum Var {
903    Free(Name),
904    Bound(DebruijnIndex, BoundReft),
905    EarlyParam(EarlyReftParam),
906    EVar(EVid),
907    ConstGeneric(ParamConst),
908}
909
910#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
911pub struct Path {
912    pub loc: Loc,
913    projection: List<FieldIdx>,
914}
915
916#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
917pub enum Loc {
918    Local(Local),
919    Var(Var),
920}
921
922newtype_index! {
923    /// *E*xistential *v*ariable *id*
924    #[debug_format = "?{}e"]
925    #[orderable]
926    #[encodable]
927    pub struct EVid {}
928}
929
930newtype_index! {
931    #[debug_format = "$k{}"]
932    #[encodable]
933    pub struct KVid {}
934}
935
936newtype_index! {
937    #[debug_format = "a{}"]
938    #[orderable]
939    #[encodable]
940    pub struct Name {}
941}
942
943#[derive(Copy, Debug, Clone)]
944pub enum NameProvenance {
945    Unknown,
946    UnfoldBoundReft(BoundReftKind),
947}
948
949impl NameProvenance {
950    pub fn opt_symbol(&self) -> Option<Symbol> {
951        match &self {
952            NameProvenance::UnfoldBoundReft(BoundReftKind::Named(name)) => Some(*name),
953            _ => None,
954        }
955    }
956}
957
958#[derive(PartialEq, Eq, Hash, Debug)]
959pub enum PrettyVar<V> {
960    Local(V),
961    Param(EarlyReftParam),
962}
963
964impl<V: Copy + Into<usize>> PrettyVar<V> {
965    pub fn as_subscript(&self) -> String {
966        let idx = match self {
967            PrettyVar::Local(v) => (*v).into(),
968            PrettyVar::Param(p) => p.index as usize,
969        };
970        as_subscript(idx)
971    }
972}
973
974pub struct PrettyMap<V: Eq + Hash> {
975    map: FxHashMap<PrettyVar<V>, String>,
976    count: FxHashMap<Symbol, usize>,
977}
978
979impl<V: Eq + Hash + Copy + Into<usize>> PrettyMap<V> {
980    pub fn new() -> Self {
981        PrettyMap { map: FxHashMap::default(), count: FxHashMap::default() }
982    }
983
984    pub fn set(&mut self, var: PrettyVar<V>, prefix: Option<Symbol>) -> String {
985        // if already defined, return it
986        if let Some(symbol) = self.map.get(&var) {
987            return symbol.clone();
988        }
989        // else define it, and stash
990        let symbol = if let Some(prefix) = prefix {
991            let index = self.count.entry(prefix).or_insert(0);
992            let symbol = format!("{}{}", prefix, as_subscript(*index));
993            *index += 1;
994            symbol
995        } else {
996            format!("a'{}", var.as_subscript())
997        };
998        self.map.insert(var, symbol.clone());
999        symbol
1000    }
1001
1002    pub fn get(&self, key: &PrettyVar<V>) -> String {
1003        match self.map.get(key) {
1004            Some(s) => s.clone(),
1005            None => format!("a'{}", key.as_subscript()),
1006        }
1007    }
1008}
1009
1010impl KVar {
1011    pub fn new(kvid: KVid, self_args: usize, args: Vec<Expr>) -> Self {
1012        KVar { kvid, self_args, args: List::from_vec(args) }
1013    }
1014
1015    fn self_args(&self) -> &[Expr] {
1016        &self.args[..self.self_args]
1017    }
1018
1019    fn scope(&self) -> &[Expr] {
1020        &self.args[self.self_args..]
1021    }
1022}
1023
1024impl Var {
1025    pub fn to_expr(&self) -> Expr {
1026        Expr::var(*self)
1027    }
1028}
1029
1030impl Path {
1031    pub fn new(loc: Loc, projection: impl Into<List<FieldIdx>>) -> Path {
1032        Path { loc, projection: projection.into() }
1033    }
1034
1035    pub fn projection(&self) -> &[FieldIdx] {
1036        &self.projection[..]
1037    }
1038
1039    pub fn to_expr(&self) -> Expr {
1040        self.projection
1041            .iter()
1042            .fold(self.loc.to_expr(), |e, f| Expr::path_proj(e, *f))
1043    }
1044
1045    pub fn to_loc(&self) -> Option<Loc> {
1046        if self.projection.is_empty() { Some(self.loc) } else { None }
1047    }
1048}
1049
1050impl Loc {
1051    pub fn to_expr(&self) -> Expr {
1052        match self {
1053            Loc::Local(local) => Expr::local(*local),
1054            Loc::Var(var) => Expr::var(*var),
1055        }
1056    }
1057}
1058
1059macro_rules! impl_ops {
1060    ($($op:ident: $method:ident),*) => {$(
1061        impl<Rhs> std::ops::$op<Rhs> for Expr
1062        where
1063            Rhs: Into<Expr>,
1064        {
1065            type Output = Expr;
1066
1067            fn $method(self, rhs: Rhs) -> Self::Output {
1068                let sort = crate::rty::Sort::Int;
1069                Expr::binary_op(BinOp::$op(sort), self, rhs)
1070            }
1071        }
1072
1073        impl<Rhs> std::ops::$op<Rhs> for &Expr
1074        where
1075            Rhs: Into<Expr>,
1076        {
1077            type Output = Expr;
1078
1079            fn $method(self, rhs: Rhs) -> Self::Output {
1080                let sort = crate::rty::Sort::Int;
1081                Expr::binary_op(BinOp::$op(sort), self, rhs)
1082            }
1083        }
1084    )*};
1085}
1086impl_ops!(Add: add, Sub: sub, Mul: mul, Div: div);
1087
1088impl From<i32> for Expr {
1089    fn from(value: i32) -> Self {
1090        Expr::constant(Constant::from(value))
1091    }
1092}
1093
1094impl From<&Expr> for Expr {
1095    fn from(e: &Expr) -> Self {
1096        e.clone()
1097    }
1098}
1099
1100impl From<Path> for Expr {
1101    fn from(path: Path) -> Self {
1102        path.to_expr()
1103    }
1104}
1105
1106impl From<Name> for Expr {
1107    fn from(name: Name) -> Self {
1108        Expr::fvar(name)
1109    }
1110}
1111
1112impl From<Var> for Expr {
1113    fn from(var: Var) -> Self {
1114        Expr::var(var)
1115    }
1116}
1117
1118impl From<SpecFuncKind> for Expr {
1119    fn from(kind: SpecFuncKind) -> Self {
1120        Expr::global_func(kind)
1121    }
1122}
1123
1124impl From<InternalFuncKind> for Expr {
1125    fn from(kind: InternalFuncKind) -> Self {
1126        Expr::internal_func(kind)
1127    }
1128}
1129
1130impl From<Loc> for Path {
1131    fn from(loc: Loc) -> Self {
1132        Path::new(loc, vec![])
1133    }
1134}
1135
1136impl From<Name> for Loc {
1137    fn from(name: Name) -> Self {
1138        Loc::Var(Var::Free(name))
1139    }
1140}
1141
1142impl From<Local> for Loc {
1143    fn from(local: Local) -> Self {
1144        Loc::Local(local)
1145    }
1146}
1147
1148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable)]
1149pub struct Real(pub u128);
1150
1151#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable)]
1152pub enum Constant {
1153    Int(BigInt),
1154    Real(Real),
1155    Bool(bool),
1156    Str(Symbol),
1157    Char(char),
1158    BitVec(u128, u32),
1159}
1160
1161impl Constant {
1162    pub const ZERO: Constant = Constant::Int(BigInt::ZERO);
1163    pub const ONE: Constant = Constant::Int(BigInt::ONE);
1164    pub const TRUE: Constant = Constant::Bool(true);
1165
1166    fn to_bool(self) -> Option<bool> {
1167        match self {
1168            Constant::Bool(b) => Some(b),
1169            _ => None,
1170        }
1171    }
1172
1173    fn to_int(self) -> Option<BigInt> {
1174        match self {
1175            Constant::Int(n) => Some(n),
1176            _ => None,
1177        }
1178    }
1179
1180    pub fn iff(&self, other: &Constant) -> Option<Constant> {
1181        let b1 = self.to_bool()?;
1182        let b2 = other.to_bool()?;
1183        Some(Constant::Bool(b1 == b2))
1184    }
1185
1186    pub fn imp(&self, other: &Constant) -> Option<Constant> {
1187        let b1 = self.to_bool()?;
1188        let b2 = other.to_bool()?;
1189        Some(Constant::Bool(!b1 || b2))
1190    }
1191
1192    pub fn or(&self, other: &Constant) -> Option<Constant> {
1193        let b1 = self.to_bool()?;
1194        let b2 = other.to_bool()?;
1195        Some(Constant::Bool(b1 || b2))
1196    }
1197
1198    pub fn and(&self, other: &Constant) -> Option<Constant> {
1199        let b1 = self.to_bool()?;
1200        let b2 = other.to_bool()?;
1201        Some(Constant::Bool(b1 && b2))
1202    }
1203
1204    pub fn eq(&self, other: &Constant) -> Constant {
1205        Constant::Bool(*self == *other)
1206    }
1207
1208    pub fn ne(&self, other: &Constant) -> Constant {
1209        Constant::Bool(*self != *other)
1210    }
1211
1212    pub fn gt(&self, other: &Constant) -> Option<Constant> {
1213        let n1 = self.to_int()?;
1214        let n2 = other.to_int()?;
1215        Some(Constant::Bool(n1 > n2))
1216    }
1217
1218    pub fn ge(&self, other: &Constant) -> Option<Constant> {
1219        let n1 = self.to_int()?;
1220        let n2 = other.to_int()?;
1221        Some(Constant::Bool(n1 >= n2))
1222    }
1223
1224    pub fn from_scalar_int<'tcx, T>(tcx: TyCtxt<'tcx>, scalar: ScalarInt, t: &T) -> Option<Self>
1225    where
1226        T: ToRustc<'tcx, T = rustc_middle::ty::Ty<'tcx>>,
1227    {
1228        use rustc_middle::ty::TyKind;
1229        let ty = t.to_rustc(tcx);
1230        match ty.kind() {
1231            TyKind::Int(int_ty) => Some(Constant::from(scalar_to_int(tcx, scalar, *int_ty))),
1232            TyKind::Uint(uint_ty) => Some(Constant::from(scalar_to_uint(tcx, scalar, *uint_ty))),
1233            TyKind::Bool => {
1234                let b = scalar_to_bits(tcx, scalar, ty)?;
1235                Some(Constant::Bool(b != 0))
1236            }
1237            TyKind::Char => {
1238                let b = scalar_to_bits(tcx, scalar, ty)?;
1239                Some(Constant::Char(char::from_u32(b as u32)?))
1240            }
1241            _ => bug!(),
1242        }
1243    }
1244
1245    /// See [`BigInt::int_min`]
1246    pub fn int_min(bit_width: u32) -> Constant {
1247        Constant::Int(BigInt::int_min(bit_width))
1248    }
1249
1250    /// See [`BigInt::int_max`]
1251    pub fn int_max(bit_width: u32) -> Constant {
1252        Constant::Int(BigInt::int_max(bit_width))
1253    }
1254
1255    /// See [`BigInt::uint_max`]
1256    pub fn uint_max(bit_width: u32) -> Constant {
1257        Constant::Int(BigInt::uint_max(bit_width))
1258    }
1259}
1260
1261impl From<i32> for Constant {
1262    fn from(c: i32) -> Self {
1263        Constant::Int(c.into())
1264    }
1265}
1266
1267impl From<usize> for Constant {
1268    fn from(u: usize) -> Self {
1269        Constant::Int(u.into())
1270    }
1271}
1272
1273impl From<u32> for Constant {
1274    fn from(c: u32) -> Self {
1275        Constant::Int(c.into())
1276    }
1277}
1278
1279impl From<u64> for Constant {
1280    fn from(c: u64) -> Self {
1281        Constant::Int(c.into())
1282    }
1283}
1284
1285impl From<u128> for Constant {
1286    fn from(c: u128) -> Self {
1287        Constant::Int(c.into())
1288    }
1289}
1290
1291impl From<i128> for Constant {
1292    fn from(c: i128) -> Self {
1293        Constant::Int(c.into())
1294    }
1295}
1296
1297impl From<bool> for Constant {
1298    fn from(b: bool) -> Self {
1299        Constant::Bool(b)
1300    }
1301}
1302
1303impl From<Symbol> for Constant {
1304    fn from(s: Symbol) -> Self {
1305        Constant::Str(s)
1306    }
1307}
1308
1309impl From<char> for Constant {
1310    fn from(c: char) -> Self {
1311        Constant::Char(c)
1312    }
1313}
1314
1315impl_internable!(ExprKind);
1316impl_slice_internable!(Expr, KVar);
1317
1318#[derive(Debug)]
1319pub struct FieldBind<T> {
1320    pub name: Symbol,
1321    pub value: T,
1322}
1323
1324impl<T: Pretty> Pretty for FieldBind<T> {
1325    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1326        w!(cx, f, "{}: {:?}", ^self.name, &self.value)
1327    }
1328}
1329
1330pub(crate) mod pretty {
1331
1332    use flux_rustc_bridge::def_id_to_string;
1333
1334    use super::*;
1335    use crate::name_of_thy_func;
1336
1337    #[derive(PartialEq, Eq, PartialOrd, Ord)]
1338    enum Precedence {
1339        Iff,
1340        Imp,
1341        Or,
1342        And,
1343        Cmp,
1344        Bitvec,
1345        AddSub,
1346        MulDiv,
1347    }
1348
1349    impl BinOp {
1350        fn precedence(&self) -> Precedence {
1351            match self {
1352                BinOp::Iff => Precedence::Iff,
1353                BinOp::Imp => Precedence::Imp,
1354                BinOp::Or => Precedence::Or,
1355                BinOp::And => Precedence::And,
1356                BinOp::Eq
1357                | BinOp::Ne
1358                | BinOp::Gt(_)
1359                | BinOp::Lt(_)
1360                | BinOp::Ge(_)
1361                | BinOp::Le(_) => Precedence::Cmp,
1362                BinOp::Add(_) | BinOp::Sub(_) => Precedence::AddSub,
1363                BinOp::Mul(_) | BinOp::Div(_) | BinOp::Mod(_) => Precedence::MulDiv,
1364                BinOp::BitAnd(_)
1365                | BinOp::BitOr(_)
1366                | BinOp::BitShl(_)
1367                | BinOp::BitShr(_)
1368                | BinOp::BitXor(_) => Precedence::Bitvec,
1369            }
1370        }
1371    }
1372
1373    impl Precedence {
1374        pub fn is_associative(&self) -> bool {
1375            !matches!(self, Precedence::Imp | Precedence::Cmp)
1376        }
1377    }
1378
1379    pub fn should_parenthesize(op: &BinOp, child: &Expr) -> bool {
1380        if let ExprKind::BinaryOp(child_op, ..) = child.kind() {
1381            child_op.precedence() < op.precedence()
1382                || (child_op.precedence() == op.precedence() && !op.precedence().is_associative())
1383        } else {
1384            false
1385        }
1386    }
1387
1388    impl Pretty for Ctor {
1389        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1390            match self {
1391                Ctor::Struct(def_id) => {
1392                    w!(cx, f, "{:?}", def_id)
1393                }
1394                Ctor::Enum(def_id, variant_idx) => {
1395                    w!(cx, f, "{:?}::{:?}", def_id, ^variant_idx)
1396                }
1397            }
1398        }
1399    }
1400
1401    impl Pretty for fhir::QuantKind {
1402        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1403            match self {
1404                fhir::QuantKind::Exists => w!(cx, f, "∃"),
1405                fhir::QuantKind::Forall => w!(cx, f, "∀"),
1406            }
1407        }
1408    }
1409
1410    impl Pretty for InternalFuncKind {
1411        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1412            match self {
1413                InternalFuncKind::Val(op) => w!(cx, f, "[{:?}]", op),
1414                InternalFuncKind::Rel(op) => w!(cx, f, "[{:?}]?", op),
1415                InternalFuncKind::Cast => w!(cx, f, "cast"),
1416            }
1417        }
1418    }
1419
1420    impl Pretty for Expr {
1421        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1422            let e = if cx.simplify_exprs {
1423                self.simplify(&SnapshotMap::default())
1424            } else {
1425                self.clone()
1426            };
1427            match e.kind() {
1428                ExprKind::Var(var) => w!(cx, f, "{:?}", var),
1429                ExprKind::Local(local) => w!(cx, f, "{:?}", ^local),
1430                ExprKind::ConstDefId(did) => w!(cx, f, "{}", ^def_id_to_string(*did)),
1431                ExprKind::Constant(c) => w!(cx, f, "{:?}", c),
1432
1433                ExprKind::BinaryOp(op, e1, e2) => {
1434                    if should_parenthesize(op, e1) {
1435                        w!(cx, f, "({:?})", e1)?;
1436                    } else {
1437                        w!(cx, f, "{:?}", e1)?;
1438                    }
1439                    if matches!(op, BinOp::Div(_)) {
1440                        w!(cx, f, "{:?}", op)?;
1441                    } else {
1442                        w!(cx, f, " {:?} ", op)?;
1443                    }
1444                    if should_parenthesize(op, e2) {
1445                        w!(cx, f, "({:?})", e2)?;
1446                    } else {
1447                        w!(cx, f, "{:?}", e2)?;
1448                    }
1449                    Ok(())
1450                }
1451                ExprKind::UnaryOp(op, e) => {
1452                    if e.is_atom() {
1453                        w!(cx, f, "{:?}{:?}", op, e)
1454                    } else {
1455                        w!(cx, f, "{:?}({:?})", op, e)
1456                    }
1457                }
1458                ExprKind::FieldProj(e, proj) if let ExprKind::Ctor(_, _) = e.kind() => {
1459                    // special case to avoid printing `{n:12}.n` as `12.n` but instead, just print `12`
1460                    // TODO: maintain an invariant that `FieldProj` never has a Ctor as first argument (as always reduced)
1461                    w!(cx, f, "{:?}", e.proj_and_reduce(*proj))
1462                }
1463                ExprKind::FieldProj(e, proj) => {
1464                    if e.is_atom() {
1465                        w!(cx, f, "{:?}.{}", e, ^fmt_field_proj(cx, *proj))
1466                    } else {
1467                        w!(cx, f, "({:?}).{}", e, ^fmt_field_proj(cx, *proj))
1468                    }
1469                }
1470                ExprKind::Tuple(flds) => {
1471                    if let [e] = &flds[..] {
1472                        w!(cx, f, "({:?},)", e)
1473                    } else {
1474                        w!(cx, f, "({:?})", join!(", ", flds))
1475                    }
1476                }
1477                ExprKind::IsCtor(def_id, variant_idx, idx) => {
1478                    w!(cx, f, "({:?} is {:?}::{:?})", idx, def_id, ^variant_idx)
1479                }
1480                ExprKind::Ctor(ctor, flds) => {
1481                    let def_id = ctor.def_id();
1482                    if let Some(adt_sort_def) = cx.adt_sort_def_of(def_id) {
1483                        let variant = adt_sort_def.variant(ctor.variant_idx()).field_names();
1484                        let fields = iter::zip(variant, flds)
1485                            .map(|(name, value)| FieldBind { name: *name, value: value.clone() })
1486                            .collect_vec();
1487                        match ctor {
1488                            Ctor::Struct(_) => {
1489                                w!(cx, f, "{:?} {{ {:?} }}", def_id, join!(", ", fields))
1490                            }
1491                            Ctor::Enum(_, idx) => {
1492                                if fields.is_empty() {
1493                                    w!(cx, f, "{:?}::{:?}", def_id, ^idx.index())
1494                                } else {
1495                                    w!(cx, f, "{:?}::{:?}({:?})", def_id, ^idx.index(), join!(", ", fields))
1496                                }
1497                            }
1498                        }
1499                    } else {
1500                        match ctor {
1501                            Ctor::Struct(_) => {
1502                                w!(cx, f, "{:?} {{ {:?} }}", def_id, join!(", ", flds))
1503                            }
1504                            Ctor::Enum(_, idx) => {
1505                                w!(cx, f, "{:?}::{:?} {{ {:?} }}", def_id, ^idx, join!(", ", flds))
1506                            }
1507                        }
1508                    }
1509                }
1510                ExprKind::PathProj(e, field) => {
1511                    if e.is_atom() {
1512                        w!(cx, f, "{:?}.{:?}", e, field)
1513                    } else {
1514                        w!(cx, f, "({:?}).{:?}", e, field)
1515                    }
1516                }
1517                ExprKind::App(func, _, args) => {
1518                    w!(cx, f, "{:?}({})",
1519                        parens!(func, !func.is_atom()),
1520                        ^args
1521                            .iter()
1522                            .format_with(", ", |arg, f| f(&format_args_cx!(cx, "{:?}", arg)))
1523                    )
1524                }
1525                ExprKind::IfThenElse(p, e1, e2) => {
1526                    w!(cx, f, "if {:?} {{ {:?} }} else {{ {:?} }}", p, e1, e2)
1527                }
1528                ExprKind::Hole(_) => {
1529                    w!(cx, f, "*")
1530                }
1531                ExprKind::KVar(kvar) => {
1532                    w!(cx, f, "{:?}", kvar)
1533                }
1534                ExprKind::Alias(alias, args) => {
1535                    w!(cx, f, "{:?}({:?})", alias, join!(", ", args))
1536                }
1537                ExprKind::Abs(lam) => {
1538                    w!(cx, f, "{:?}", lam)
1539                }
1540                ExprKind::GlobalFunc(SpecFuncKind::Def(did)) => {
1541                    w!(cx, f, "{}", ^did.name())
1542                }
1543                ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => {
1544                    if let Some(name) = name_of_thy_func(*itf) {
1545                        w!(cx, f, "{}", ^name)
1546                    } else {
1547                        w!(cx, f, "<error>")
1548                    }
1549                }
1550                ExprKind::InternalFunc(func) => {
1551                    w!(cx, f, "{:?}", func)
1552                }
1553                ExprKind::ForAll(expr) => {
1554                    let vars = expr.vars();
1555                    cx.with_bound_vars(vars, || {
1556                        if !vars.is_empty() {
1557                            cx.fmt_bound_vars(false, "∀", vars, ". ", f)?;
1558                        }
1559                        w!(cx, f, "{:?}", expr.skip_binder_ref())
1560                    })
1561                }
1562                ExprKind::Exists(expr) => {
1563                    let vars = expr.vars();
1564                    cx.with_bound_vars(vars, || {
1565                        if !vars.is_empty() {
1566                            cx.fmt_bound_vars(false, "∃", vars, ". ", f)?;
1567                        }
1568                        w!(cx, f, "{:?}", expr.skip_binder_ref())
1569                    })
1570                }
1571                ExprKind::BoundedQuant(kind, rng, body) => {
1572                    let vars = body.vars();
1573                    cx.with_bound_vars(vars, || {
1574                        w!(
1575                            cx,
1576                            f,
1577                            "{:?} {}..{} {{ {:?} }}",
1578                            kind,
1579                            ^rng.start,
1580                            ^rng.end,
1581                            body.skip_binder_ref()
1582                        )
1583                    })
1584                }
1585                ExprKind::Let(init, body) => {
1586                    let vars = body.vars();
1587                    cx.with_bound_vars(vars, || {
1588                        cx.fmt_bound_vars(false, "(let ", vars, " = ", f)?;
1589                        w!(cx, f, "{:?} in {:?})", init, body.skip_binder_ref())
1590                    })
1591                }
1592            }
1593        }
1594    }
1595
1596    fn fmt_field_proj(cx: &PrettyCx, proj: FieldProj) -> String {
1597        if let FieldProj::Adt { def_id, field } = proj
1598            && let Some(adt_sort_def) = cx.adt_sort_def_of(def_id)
1599        {
1600            format!("{}", adt_sort_def.struct_variant().field_names()[field as usize])
1601        } else {
1602            format!("{}", proj.field_idx())
1603        }
1604    }
1605
1606    impl Pretty for Constant {
1607        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1608            match self {
1609                Constant::Int(i) => w!(cx, f, "{i}"),
1610                Constant::BitVec(i, sz) => w!(cx, f, "bv({i}, {sz})"),
1611                Constant::Real(r) => w!(cx, f, "{}.0", ^r.0),
1612                Constant::Bool(b) => w!(cx, f, "{b}"),
1613                Constant::Str(sym) => w!(cx, f, "\"{sym}\""),
1614                Constant::Char(c) => w!(cx, f, "\'{c}\'"),
1615            }
1616        }
1617    }
1618
1619    impl Pretty for AliasReft {
1620        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1621            w!(cx, f, "<({:?}) as {:?}", &self.args[0], self.assoc_id.parent())?;
1622            let args = &self.args[1..];
1623            if !args.is_empty() {
1624                w!(cx, f, "<{:?}>", join!(", ", args))?;
1625            }
1626            w!(cx, f, ">::{}", ^self.assoc_id.name())
1627        }
1628    }
1629
1630    impl Pretty for Lambda {
1631        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1632            let vars = self.body.vars();
1633            // TODO: remove redundant vars; see Ty
1634            // let redundant_bvars = self.body.redundant_bvars().into_iter().collect();
1635            cx.with_bound_vars(vars, || {
1636                cx.fmt_bound_vars(false, "λ", vars, ". ", f)?;
1637                w!(cx, f, "{:?}", self.body.as_ref().skip_binder())
1638            })
1639        }
1640    }
1641
1642    impl Pretty for Var {
1643        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1644            match self {
1645                Var::Bound(debruijn, var) => cx.fmt_bound_reft(*debruijn, *var, f),
1646                Var::EarlyParam(var) => w!(cx, f, "{}", ^var.name),
1647                Var::Free(name) => w!(cx, f, "{:?}", ^name),
1648                Var::EVar(evar) => w!(cx, f, "{:?}", ^evar),
1649                Var::ConstGeneric(param) => w!(cx, f, "{}", ^param.name),
1650            }
1651        }
1652    }
1653
1654    impl Pretty for KVar {
1655        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1656            w!(cx, f, "{:?}", ^self.kvid)?;
1657            match cx.kvar_args {
1658                KVarArgs::All => {
1659                    w!(
1660                        cx,
1661                        f,
1662                        "({:?})[{:?}]",
1663                        join!(", ", self.self_args()),
1664                        join!(", ", self.scope())
1665                    )?;
1666                }
1667                KVarArgs::SelfOnly => w!(cx, f, "({:?})", join!(", ", self.self_args()))?,
1668                KVarArgs::Hide => {}
1669            }
1670            Ok(())
1671        }
1672    }
1673
1674    impl Pretty for Path {
1675        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1676            w!(cx, f, "{:?}", &self.loc)?;
1677            for field in &self.projection {
1678                w!(cx, f, ".{}", ^u32::from(*field))?;
1679            }
1680            Ok(())
1681        }
1682    }
1683
1684    impl Pretty for Loc {
1685        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1686            match self {
1687                Loc::Local(local) => w!(cx, f, "{:?}", ^local),
1688                Loc::Var(var) => w!(cx, f, "{:?}", var),
1689            }
1690        }
1691    }
1692
1693    impl Pretty for BinOp {
1694        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1695            match self {
1696                BinOp::Iff => w!(cx, f, "⇔"),
1697                BinOp::Imp => w!(cx, f, "⇒"),
1698                BinOp::Or => w!(cx, f, "∨"),
1699                BinOp::And => w!(cx, f, "∧"),
1700                BinOp::Eq => w!(cx, f, "="),
1701                BinOp::Ne => w!(cx, f, "≠"),
1702                BinOp::Gt(_) => w!(cx, f, ">"),
1703                BinOp::Ge(_) => w!(cx, f, "≥"),
1704                BinOp::Lt(_) => w!(cx, f, "<"),
1705                BinOp::Le(_) => w!(cx, f, "≤"),
1706                BinOp::Add(_) => w!(cx, f, "+"),
1707                BinOp::Sub(_) => w!(cx, f, "-"),
1708                BinOp::Mul(_) => w!(cx, f, "*"),
1709                BinOp::Div(_) => w!(cx, f, "/"),
1710                BinOp::Mod(_) => w!(cx, f, "mod"),
1711                BinOp::BitAnd(_) => w!(cx, f, "&"),
1712                BinOp::BitOr(_) => w!(cx, f, "|"),
1713                BinOp::BitXor(_) => w!(cx, f, "^"),
1714                BinOp::BitShl(_) => w!(cx, f, "<<"),
1715                BinOp::BitShr(_) => w!(cx, f, ">>"),
1716            }
1717        }
1718    }
1719
1720    impl Pretty for UnOp {
1721        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1722            match self {
1723                UnOp::Not => w!(cx, f, "¬"),
1724                UnOp::Neg => w!(cx, f, "-"),
1725            }
1726        }
1727    }
1728
1729    impl_debug_with_default_cx!(Expr, Loc, Path, Var, KVar, Lambda, AliasReft);
1730
1731    impl PrettyNested for Lambda {
1732        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
1733            // TODO: remove redundant vars; see Ty
1734            cx.nested_with_bound_vars("λ", self.body.vars(), None, |prefix| {
1735                let expr_d = self.body.skip_binder_ref().fmt_nested(cx)?;
1736                let text = format!("{}{}", prefix, expr_d.text);
1737                Ok(NestedString { text, children: expr_d.children, key: None })
1738            })
1739        }
1740    }
1741
1742    pub fn aggregate_nested(
1743        cx: &PrettyCx,
1744        ctor: &Ctor,
1745        flds: &[Expr],
1746        is_named: bool,
1747    ) -> Result<NestedString, fmt::Error> {
1748        let def_id = ctor.def_id();
1749        let mut text =
1750            if is_named && ctor.is_enum() { format_cx!(cx, "{:?}", ctor) } else { "".to_string() };
1751        if flds.is_empty() {
1752            // No fields, no index
1753            Ok(NestedString { text, children: None, key: None })
1754        } else if flds.len() == 1 {
1755            // Single field, inline index
1756            text += &flds[0].fmt_nested(cx)?.text;
1757            Ok(NestedString { text, children: None, key: None })
1758        } else {
1759            let keys = if let Some(adt_sort_def) = cx.adt_sort_def_of(def_id) {
1760                adt_sort_def
1761                    .variant(ctor.variant_idx())
1762                    .field_names()
1763                    .iter()
1764                    .map(|name| format!("{name}"))
1765                    .collect_vec()
1766            } else {
1767                (0..flds.len()).map(|i| format!("arg{i}")).collect_vec()
1768            };
1769            // Multiple fields, nested index
1770            text += "{..}";
1771            let mut children = vec![];
1772            for (key, fld) in iter::zip(keys, flds) {
1773                let fld_d = fld.fmt_nested(cx)?;
1774                children.push(NestedString { key: Some(key), ..fld_d });
1775            }
1776            Ok(NestedString { text, children: Some(children), key: None })
1777        }
1778    }
1779
1780    impl PrettyNested for Name {
1781        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
1782            let text = cx.pretty_var_env.get(&PrettyVar::Local(*self));
1783            Ok(NestedString { text, key: None, children: None })
1784        }
1785    }
1786
1787    impl PrettyNested for Expr {
1788        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
1789            let e = if cx.simplify_exprs {
1790                self.simplify(&SnapshotMap::default())
1791            } else {
1792                self.clone()
1793            };
1794            match e.kind() {
1795                ExprKind::Var(Var::Free(name)) => name.fmt_nested(cx),
1796                ExprKind::Var(..)
1797                | ExprKind::Local(..)
1798                | ExprKind::Constant(..)
1799                | ExprKind::ConstDefId(..)
1800                | ExprKind::Hole(..)
1801                | ExprKind::GlobalFunc(..)
1802                | ExprKind::InternalFunc(..) => debug_nested(cx, &e),
1803                ExprKind::KVar(kvar) => {
1804                    let kv = format!("{:?}", kvar.kvid);
1805                    let mut strs = vec![kv];
1806                    for arg in &kvar.args {
1807                        strs.push(arg.fmt_nested(cx)?.text);
1808                    }
1809                    let text = format!("##[{}]##", strs.join("##"));
1810                    Ok(NestedString { text, children: None, key: None })
1811                }
1812                ExprKind::IfThenElse(p, e1, e2) => {
1813                    let p_d = p.fmt_nested(cx)?;
1814                    let e1_d = e1.fmt_nested(cx)?;
1815                    let e2_d = e2.fmt_nested(cx)?;
1816                    let text = format!("(if {} then {} else {})", p_d.text, e1_d.text, e2_d.text);
1817                    let children = float_children(vec![p_d.children, e1_d.children, e2_d.children]);
1818                    Ok(NestedString { text, children, key: None })
1819                }
1820                ExprKind::BinaryOp(op, e1, e2) => {
1821                    let e1_d = e1.fmt_nested(cx)?;
1822                    let e2_d = e2.fmt_nested(cx)?;
1823                    let e1_text = if should_parenthesize(op, e1) {
1824                        format!("({})", e1_d.text)
1825                    } else {
1826                        e1_d.text
1827                    };
1828                    let e2_text = if should_parenthesize(op, e2) {
1829                        format!("({})", e2_d.text)
1830                    } else {
1831                        e2_d.text
1832                    };
1833                    let op_d = debug_nested(cx, op)?;
1834                    let op_text = if matches!(op, BinOp::Div(_)) {
1835                        op_d.text
1836                    } else {
1837                        format!(" {} ", op_d.text)
1838                    };
1839                    let text = format!("{e1_text}{op_text}{e2_text}");
1840                    let children = float_children(vec![e1_d.children, e2_d.children]);
1841                    Ok(NestedString { text, children, key: None })
1842                }
1843                ExprKind::UnaryOp(op, e) => {
1844                    let e_d = e.fmt_nested(cx)?;
1845                    let op_d = debug_nested(cx, op)?;
1846                    let text = if e.is_atom() {
1847                        format!("{}{}", op_d.text, e_d.text)
1848                    } else {
1849                        format!("{}({})", op_d.text, e_d.text)
1850                    };
1851                    Ok(NestedString { text, children: e_d.children, key: None })
1852                }
1853                ExprKind::FieldProj(e, proj) if let ExprKind::Ctor(_, _) = e.kind() => {
1854                    // special case to avoid printing `{n:12}.n` as `12.n` but instead, just print `12`
1855                    // TODO: maintain an invariant that `FieldProj` never has a Ctor as first argument (as always reduced)
1856                    e.proj_and_reduce(*proj).fmt_nested(cx)
1857                }
1858                ExprKind::FieldProj(e, proj) => {
1859                    let e_d = e.fmt_nested(cx)?;
1860                    let text = if e.is_atom() {
1861                        format!("{}.{}", e_d.text, fmt_field_proj(cx, *proj))
1862                    } else {
1863                        format!("({}).{}", e_d.text, fmt_field_proj(cx, *proj))
1864                    };
1865                    Ok(NestedString { text, children: e_d.children, key: None })
1866                }
1867                ExprKind::Tuple(flds) => {
1868                    let mut texts = vec![];
1869                    let mut kidss = vec![];
1870                    for e in flds {
1871                        let e_d = e.fmt_nested(cx)?;
1872                        texts.push(e_d.text);
1873                        kidss.push(e_d.children);
1874                    }
1875                    let text = if let [e] = &texts[..] {
1876                        format!("({e},)")
1877                    } else {
1878                        format!("({})", texts.join(", "))
1879                    };
1880                    let children = float_children(kidss);
1881                    Ok(NestedString { text, children, key: None })
1882                }
1883                ExprKind::Ctor(ctor, flds) => aggregate_nested(cx, ctor, flds, true),
1884                ExprKind::IsCtor(def_id, variant_idx, idx) => {
1885                    let text = format!("is::{:?}::{:?}( {:?} )", def_id, variant_idx, idx);
1886                    Ok(NestedString { text, children: None, key: None })
1887                }
1888                ExprKind::PathProj(e, field) => {
1889                    let e_d = e.fmt_nested(cx)?;
1890                    let text = if e.is_atom() {
1891                        format!("{}.{:?}", e_d.text, field)
1892                    } else {
1893                        format!("({}).{:?}", e_d.text, field)
1894                    };
1895                    Ok(NestedString { text, children: e_d.children, key: None })
1896                }
1897                ExprKind::Alias(alias, args) => {
1898                    let mut texts = vec![];
1899                    let mut kidss = vec![];
1900                    for arg in args {
1901                        let arg_d = arg.fmt_nested(cx)?;
1902                        texts.push(arg_d.text);
1903                        kidss.push(arg_d.children);
1904                    }
1905                    let text = format_cx!(cx, "{:?}({:?})", alias, texts.join(", "));
1906                    let children = float_children(kidss);
1907                    Ok(NestedString { text, children, key: None })
1908                }
1909                ExprKind::App(func, _, args) => {
1910                    let func_d = func.fmt_nested(cx)?;
1911                    let mut texts = vec![];
1912                    let mut kidss = vec![func_d.children];
1913                    for arg in args {
1914                        let arg_d = arg.fmt_nested(cx)?;
1915                        texts.push(arg_d.text);
1916                        kidss.push(arg_d.children);
1917                    }
1918                    let text = if func.is_atom() {
1919                        format!("{}({})", func_d.text, texts.join(", "))
1920                    } else {
1921                        format!("({})({})", func_d.text, texts.join(", "))
1922                    };
1923                    let children = float_children(kidss);
1924                    Ok(NestedString { text, children, key: None })
1925                }
1926                ExprKind::Abs(lambda) => lambda.fmt_nested(cx),
1927                ExprKind::Let(init, body) => {
1928                    // FIXME this is very wrong!
1929                    cx.nested_with_bound_vars("let", body.vars(), None, |prefix| {
1930                        let body = body.skip_binder_ref().fmt_nested(cx)?;
1931                        let text = format!("{:?} {}{}", init, prefix, body.text);
1932                        Ok(NestedString { text, children: body.children, key: None })
1933                    })
1934                }
1935                ExprKind::BoundedQuant(kind, rng, body) => {
1936                    let left = match kind {
1937                        fhir::QuantKind::Forall => "∀",
1938                        fhir::QuantKind::Exists => "∃",
1939                    };
1940                    let right = Some(format!(" in {}..{}", rng.start, rng.end));
1941
1942                    cx.nested_with_bound_vars(left, body.vars(), right, |all_str| {
1943                        let expr_d = body.as_ref().skip_binder().fmt_nested(cx)?;
1944                        let text = format!("{}{}", all_str, expr_d.text);
1945                        Ok(NestedString { text, children: expr_d.children, key: None })
1946                    })
1947                }
1948                ExprKind::ForAll(expr) => {
1949                    cx.nested_with_bound_vars("∀", expr.vars(), None, |all_str| {
1950                        let expr_d = expr.as_ref().skip_binder().fmt_nested(cx)?;
1951                        let text = format!("{}{}", all_str, expr_d.text);
1952                        Ok(NestedString { text, children: expr_d.children, key: None })
1953                    })
1954                }
1955                ExprKind::Exists(expr) => {
1956                    cx.nested_with_bound_vars("∀", expr.vars(), None, |all_str| {
1957                        let expr_d = expr.as_ref().skip_binder().fmt_nested(cx)?;
1958                        let text = format!("{}{}", all_str, expr_d.text);
1959                        Ok(NestedString { text, children: expr_d.children, key: None })
1960                    })
1961                }
1962            }
1963        }
1964    }
1965}