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