Skip to main content

flux_middle/rty/
expr.rs

1use std::{fmt, hash::Hash, iter, ops::ControlFlow, sync::OnceLock};
2
3use flux_arc_interner::{Interned, List, impl_internable, impl_slice_internable};
4use flux_common::{bug, dbg::as_subscript};
5use flux_macros::{TypeFoldable, TypeVisitable};
6use flux_rustc_bridge::{
7    ToRustc,
8    const_eval::{scalar_to_bits, scalar_to_int, scalar_to_uint},
9    ty::{Const, ConstKind, ValTree, VariantIdx},
10};
11use flux_syntax::symbols::sym;
12use itertools::Itertools;
13use liquid_fixpoint::ThyFunc;
14use rustc_abi::{FIRST_VARIANT, FieldIdx};
15use rustc_data_structures::{snapshot_map::SnapshotMap, unord::UnordMap};
16use rustc_hir::def_id::DefId;
17use rustc_index::newtype_index;
18use rustc_macros::{Decodable, Encodable, TyDecodable, TyEncodable};
19use rustc_middle::{
20    mir::Local,
21    ty::{ParamConst, ScalarInt, TyCtxt},
22};
23use rustc_span::{Span, Symbol};
24use rustc_type_ir::{BoundVar, DebruijnIndex, INNERMOST};
25
26use super::{
27    BaseTy, Binder, BoundReftKind, BoundVariableKinds, FuncSort, GenericArgs, GenericArgsExt as _,
28    IntTy, Sort, UintTy,
29};
30use crate::{
31    big_int::BigInt,
32    def_id::FluxDefId,
33    fhir::{self},
34    global_env::GlobalEnv,
35    pretty::*,
36    queries::QueryResult,
37    rty::{
38        BoundVariableKind, SortArg, SortCtor, SubsetTyCtor,
39        fold::{
40            TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
41            TypeVisitor,
42        },
43    },
44};
45
46/// A lambda abstraction with an elaborated output sort. We need the output sort of lambdas for
47/// encoding into fixpoint
48#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
49pub struct Lambda {
50    body: Binder<Expr>,
51    output: Sort,
52}
53
54impl Lambda {
55    pub fn bind_with_vars(body: Expr, inputs: BoundVariableKinds, output: Sort) -> Self {
56        debug_assert!(inputs.iter().all(BoundVariableKind::is_refine));
57        Self { body: Binder::bind_with_vars(body, inputs), output }
58    }
59
60    pub fn bind_with_fsort(body: Expr, fsort: FuncSort) -> Self {
61        Self { body: Binder::bind_with_sorts(body, fsort.inputs()), output: fsort.output().clone() }
62    }
63
64    pub fn apply(&self, args: &[Expr]) -> Expr {
65        self.body.replace_bound_refts(args)
66    }
67
68    pub fn vars(&self) -> &BoundVariableKinds {
69        self.body.vars()
70    }
71
72    pub fn output(&self) -> Sort {
73        self.output.clone()
74    }
75
76    pub fn fsort(&self) -> FuncSort {
77        let inputs_and_output = self
78            .vars()
79            .iter()
80            .map(|kind| kind.expect_sort().clone())
81            .chain(iter::once(self.output()))
82            .collect();
83        FuncSort { inputs_and_output }
84    }
85}
86
87#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
88pub struct AliasReft {
89    /// Id of the associated refinement in the trait
90    pub assoc_id: FluxDefId,
91    pub args: GenericArgs,
92}
93
94impl AliasReft {
95    pub fn self_ty(&self) -> SubsetTyCtor {
96        self.args[0].expect_base().clone()
97    }
98
99    pub fn to_rustc_trait_ref<'tcx>(&self, tcx: TyCtxt<'tcx>) -> rustc_middle::ty::TraitRef<'tcx> {
100        let trait_def_id = self.assoc_id.parent();
101        let args = self
102            .args
103            .to_rustc(tcx)
104            .truncate_to(tcx, tcx.generics_of(trait_def_id));
105        rustc_middle::ty::TraitRef::new(tcx, trait_def_id, args)
106    }
107}
108
109#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
110pub struct Expr {
111    kind: Interned<ExprKind>,
112    espan: Option<ESpan>,
113}
114
115impl Expr {
116    pub fn at_opt(self, espan: Option<ESpan>) -> Expr {
117        Expr { kind: self.kind, espan }
118    }
119
120    pub fn at(self, espan: ESpan) -> Expr {
121        self.at_opt(Some(espan))
122    }
123
124    pub fn at_base(self, base: ESpan) -> Expr {
125        if let Some(espan) = self.espan { self.at(espan.with_base(base)) } else { self }
126    }
127
128    pub fn span(&self) -> Option<ESpan> {
129        self.espan
130    }
131
132    pub fn tt() -> Expr {
133        static TRUE: OnceLock<Expr> = OnceLock::new();
134        TRUE.get_or_init(|| ExprKind::Constant(Constant::Bool(true)).intern())
135            .clone()
136    }
137
138    pub fn ff() -> Expr {
139        static FALSE: OnceLock<Expr> = OnceLock::new();
140        FALSE
141            .get_or_init(|| ExprKind::Constant(Constant::Bool(false)).intern())
142            .clone()
143    }
144
145    pub fn and_from_iter(exprs: impl IntoIterator<Item = Expr>) -> Expr {
146        exprs
147            .into_iter()
148            .reduce(|acc, e| Expr::binary_op(BinOp::And, acc, e))
149            .unwrap_or_else(Expr::tt)
150    }
151
152    pub fn or_from_iter(exprs: impl IntoIterator<Item = Expr>) -> Expr {
153        exprs
154            .into_iter()
155            .reduce(|acc, e| Expr::binary_op(BinOp::Or, acc, e))
156            .unwrap_or_else(Expr::ff)
157    }
158
159    pub fn and(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
160        Expr::and_from_iter([e1.into(), e2.into()])
161    }
162
163    pub fn or(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
164        Expr::or_from_iter([e1.into(), e2.into()])
165    }
166
167    pub fn zero() -> Expr {
168        static ZERO: OnceLock<Expr> = OnceLock::new();
169        ZERO.get_or_init(|| ExprKind::Constant(Constant::ZERO).intern())
170            .clone()
171    }
172
173    pub fn int_max(int_ty: IntTy) -> Expr {
174        let bit_width: u64 = int_ty
175            .bit_width()
176            .unwrap_or(flux_config::pointer_width().bits());
177        Expr::constant(Constant::int_max(bit_width.try_into().unwrap()))
178    }
179
180    pub fn int_min(int_ty: IntTy) -> Expr {
181        let bit_width: u64 = int_ty
182            .bit_width()
183            .unwrap_or(flux_config::pointer_width().bits());
184        Expr::constant(Constant::int_min(bit_width.try_into().unwrap()))
185    }
186
187    pub fn uint_max(uint_ty: UintTy) -> Expr {
188        let bit_width: u64 = uint_ty
189            .bit_width()
190            .unwrap_or(flux_config::pointer_width().bits());
191        Expr::constant(Constant::uint_max(bit_width.try_into().unwrap()))
192    }
193
194    pub fn nu() -> Expr {
195        Expr::bvar(INNERMOST, BoundVar::ZERO, BoundReftKind::Anon)
196    }
197
198    pub fn is_nu(&self) -> bool {
199        if let ExprKind::Var(Var::Bound(INNERMOST, var)) = self.kind()
200            && var.var == BoundVar::ZERO
201        {
202            true
203        } else {
204            false
205        }
206    }
207
208    pub fn unit() -> Expr {
209        Expr::tuple(List::empty())
210    }
211
212    pub fn var(var: Var) -> Expr {
213        ExprKind::Var(var).intern()
214    }
215
216    pub fn fvar(name: Name) -> Expr {
217        Var::Free(name).to_expr()
218    }
219
220    pub fn evar(evid: EVid) -> Expr {
221        Var::EVar(evid).to_expr()
222    }
223
224    pub fn bvar(debruijn: DebruijnIndex, var: BoundVar, kind: BoundReftKind) -> Expr {
225        Var::Bound(debruijn, BoundReft { var, kind }).to_expr()
226    }
227
228    pub fn early_param(index: u32, name: Symbol) -> Expr {
229        Var::EarlyParam(EarlyReftParam { index, name }).to_expr()
230    }
231
232    pub fn local(local: Local) -> Expr {
233        ExprKind::Local(local).intern()
234    }
235
236    pub fn constant(c: Constant) -> Expr {
237        ExprKind::Constant(c).intern()
238    }
239
240    pub fn const_def_id(c: DefId) -> Expr {
241        ExprKind::ConstDefId(c).intern()
242    }
243
244    pub fn const_generic(param: ParamConst) -> Expr {
245        ExprKind::Var(Var::ConstGeneric(param)).intern()
246    }
247
248    pub fn tuple(flds: List<Expr>) -> Expr {
249        ExprKind::Tuple(flds).intern()
250    }
251
252    pub fn ctor_struct(def_id: DefId, flds: List<Expr>) -> Expr {
253        ExprKind::Ctor(Ctor::Struct(def_id), flds).intern()
254    }
255
256    pub fn ctor_raw_ptr(flds: List<Expr>) -> Expr {
257        ExprKind::Ctor(Ctor::RawPtr, flds).intern()
258    }
259
260    pub fn ctor_enum(def_id: DefId, idx: VariantIdx) -> Expr {
261        ExprKind::Ctor(Ctor::Enum(def_id, idx), List::empty()).intern()
262    }
263
264    pub fn ctor(ctor: Ctor, flds: List<Expr>) -> Expr {
265        ExprKind::Ctor(ctor, flds).intern()
266    }
267
268    pub fn is_ctor(def_id: DefId, variant_idx: VariantIdx, idx: impl Into<Expr>) -> Expr {
269        ExprKind::IsCtor(def_id, variant_idx, idx.into()).intern()
270    }
271
272    pub fn from_bits(bty: &BaseTy, bits: u128) -> Expr {
273        // FIXME: We are assuming the higher bits are not set. check this assumption
274        match bty {
275            BaseTy::Int(_) => {
276                let bits = bits as i128;
277                ExprKind::Constant(Constant::from(bits)).intern()
278            }
279            BaseTy::Uint(_) => ExprKind::Constant(Constant::from(bits)).intern(),
280            BaseTy::Bool => ExprKind::Constant(Constant::Bool(bits != 0)).intern(),
281            BaseTy::Char => {
282                let c = char::from_u32(bits.try_into().unwrap()).unwrap();
283                ExprKind::Constant(Constant::Char(c)).intern()
284            }
285            _ => bug!(),
286        }
287    }
288
289    pub fn ite(p: impl Into<Expr>, e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
290        ExprKind::IfThenElse(p.into(), e1.into(), e2.into()).intern()
291    }
292
293    fn empty() -> Expr {
294        let func = Self::global_func(SpecFuncKind::Thy(ThyFunc::SetEmpty));
295        Expr::app(func, List::empty(), List::from_arr([Expr::zero()]))
296    }
297
298    fn singleton(elem: Expr) -> Expr {
299        let func = Self::global_func(SpecFuncKind::Thy(ThyFunc::SetSng));
300        Expr::app(func, List::empty(), List::from_arr([elem]))
301    }
302
303    fn union(expr1: Expr, expr2: Expr) -> Expr {
304        let func = Self::global_func(SpecFuncKind::Thy(ThyFunc::SetCup));
305        Expr::app(func, List::empty(), List::from_arr([expr1, expr2]))
306    }
307
308    pub fn set(elems: List<Expr>) -> Expr {
309        let mut expr = Expr::empty();
310        for elem in &elems {
311            expr = Self::union(expr, Self::singleton(elem.clone()));
312        }
313        expr
314    }
315
316    pub fn abs(lam: Lambda) -> Expr {
317        ExprKind::Abs(lam).intern()
318    }
319
320    pub fn let_(init: Expr, body: Binder<Expr>) -> Expr {
321        ExprKind::Let(init, body).intern()
322    }
323
324    pub fn quant(kind: fhir::QuantKind, dom: QuantDom, body: Binder<Expr>) -> Expr {
325        ExprKind::Quant(kind, dom, body).intern()
326    }
327
328    pub fn hole(kind: HoleKind) -> Expr {
329        ExprKind::Hole(kind).intern()
330    }
331
332    pub fn kvar(kvar: KVar) -> Expr {
333        ExprKind::KVar(kvar).intern()
334    }
335
336    pub fn wkvar(wkvar: WKVar) -> Expr {
337        ExprKind::WKVar(wkvar).intern()
338    }
339
340    pub fn alias(alias: AliasReft, args: List<Expr>) -> Expr {
341        ExprKind::Alias(alias, args).intern()
342    }
343
344    pub fn forall(expr: Binder<Expr>) -> Expr {
345        ExprKind::Quant(fhir::QuantKind::Forall, QuantDom::Unbounded, expr).intern()
346    }
347
348    pub fn exists(expr: Binder<Expr>) -> Expr {
349        ExprKind::Quant(fhir::QuantKind::Exists, QuantDom::Unbounded, expr).intern()
350    }
351
352    pub fn binary_op(op: BinOp, e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
353        ExprKind::BinaryOp(op, e1.into(), e2.into()).intern()
354    }
355
356    pub fn prim_val(op: BinOp, e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
357        Expr::app(InternalFuncKind::Val(op), List::empty(), List::from_arr([e1.into(), e2.into()]))
358    }
359
360    pub fn prim_rel(op: BinOp, e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
361        Expr::app(InternalFuncKind::Rel(op), List::empty(), List::from_arr([e1.into(), e2.into()]))
362    }
363
364    pub fn unit_struct(def_id: DefId) -> Expr {
365        Expr::ctor_struct(def_id, List::empty())
366    }
367
368    pub fn cast(from: Sort, to: Sort, idx: Expr) -> Expr {
369        Expr::app(
370            InternalFuncKind::Cast,
371            List::from_arr([SortArg::Sort(from), SortArg::Sort(to)]),
372            List::from_arr([idx]),
373        )
374    }
375
376    pub fn app(func: impl Into<Expr>, sort_args: List<SortArg>, args: List<Expr>) -> Expr {
377        ExprKind::App(func.into(), sort_args, args).intern()
378    }
379
380    pub fn global_func(kind: SpecFuncKind) -> Expr {
381        ExprKind::GlobalFunc(kind).intern()
382    }
383
384    pub fn internal_func(kind: InternalFuncKind) -> Expr {
385        ExprKind::InternalFunc(kind).intern()
386    }
387
388    pub fn eq(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
389        ExprKind::BinaryOp(BinOp::Eq, e1.into(), e2.into()).intern()
390    }
391
392    pub fn unary_op(op: UnOp, e: impl Into<Expr>) -> Expr {
393        ExprKind::UnaryOp(op, e.into()).intern()
394    }
395
396    pub fn ne(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
397        ExprKind::BinaryOp(BinOp::Ne, e1.into(), e2.into()).intern()
398    }
399
400    pub fn ge(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
401        ExprKind::BinaryOp(BinOp::Ge(Sort::Int), e1.into(), e2.into()).intern()
402    }
403
404    pub fn gt(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
405        ExprKind::BinaryOp(BinOp::Gt(Sort::Int), e1.into(), e2.into()).intern()
406    }
407
408    pub fn lt(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
409        ExprKind::BinaryOp(BinOp::Lt(Sort::Int), e1.into(), e2.into()).intern()
410    }
411
412    pub fn le(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
413        ExprKind::BinaryOp(BinOp::Le(Sort::Int), e1.into(), e2.into()).intern()
414    }
415
416    pub fn implies(e1: impl Into<Expr>, e2: impl Into<Expr>) -> Expr {
417        ExprKind::BinaryOp(BinOp::Imp, e1.into(), e2.into()).intern()
418    }
419
420    pub fn field_proj(e: impl Into<Expr>, proj: FieldProj) -> Expr {
421        ExprKind::FieldProj(e.into(), proj).intern()
422    }
423
424    pub fn field_projs(e: impl Into<Expr>, projs: &[FieldProj]) -> Expr {
425        projs.iter().copied().fold(e.into(), Expr::field_proj)
426    }
427
428    pub fn path_proj(base: Expr, field: FieldIdx) -> Expr {
429        ExprKind::PathProj(base, field).intern()
430    }
431
432    pub fn not(&self) -> Expr {
433        ExprKind::UnaryOp(UnOp::Not, self.clone()).intern()
434    }
435
436    pub fn neg(&self) -> Expr {
437        ExprKind::UnaryOp(UnOp::Neg, self.clone()).intern()
438    }
439
440    pub fn kind(&self) -> &ExprKind {
441        &self.kind
442    }
443
444    /// An expression is an *atom* if it is "self-delimiting", i.e., it has a clear boundary
445    /// when printed. This is used to avoid unnecessary parenthesis when pretty printing.
446    pub fn is_atom(&self) -> bool {
447        !matches!(self.kind(), ExprKind::Abs(..) | ExprKind::BinaryOp(..) | ExprKind::Quant(..))
448    }
449
450    /// Simple syntactic check to see if the expression is a trivially true predicate. This is used
451    /// mostly for filtering predicates when pretty printing but also to simplify types in general.
452    pub fn is_trivially_true(&self) -> bool {
453        self.is_true()
454            || matches!(self.kind(), ExprKind::BinaryOp(BinOp::Eq | BinOp::Iff | BinOp::Imp, e1, e2) if e1.erase_spans() == e2.erase_spans())
455    }
456
457    /// Simple syntactic check to see if the expression is a trivially false predicate.
458    pub fn is_trivially_false(&self) -> bool {
459        self.is_false()
460    }
461
462    /// Whether the expression is *literally* the constant `true`.
463    fn is_true(&self) -> bool {
464        matches!(self.kind(), ExprKind::Constant(Constant::Bool(true)))
465    }
466
467    /// Whether the expression is *literally* the constant `false`.
468    fn is_false(&self) -> bool {
469        matches!(self.kind(), ExprKind::Constant(Constant::Bool(false)))
470    }
471
472    pub fn from_const(tcx: TyCtxt, c: &Const) -> Expr {
473        match &c.kind {
474            ConstKind::Param(param_const) => Expr::const_generic(*param_const),
475            ConstKind::Value(ty, ValTree::Leaf(scalar)) => {
476                Expr::constant(Constant::from_scalar_int(tcx, *scalar, ty).unwrap())
477            }
478            ConstKind::Value(_ty, ValTree::Branch(_)) => {
479                bug!("todo: ValTree::Branch {c:?}")
480            }
481            // We should have normalized away the unevaluated constants
482            ConstKind::Unevaluated(_) => bug!("unexpected `ConstKind::Unevaluated`"),
483
484            ConstKind::Infer(_) => bug!("unexpected `ConstKind::Infer`"),
485        }
486    }
487
488    fn const_op(op: &BinOp, c1: &Constant, c2: &Constant) -> Option<Constant> {
489        match op {
490            BinOp::Iff => c1.iff(c2),
491            BinOp::Imp => c1.imp(c2),
492            BinOp::Or => c1.or(c2),
493            BinOp::And => c1.and(c2),
494            BinOp::Gt(Sort::Int) => c1.gt(c2),
495            BinOp::Ge(Sort::Int) => c1.ge(c2),
496            BinOp::Lt(Sort::Int) => c2.gt(c1),
497            BinOp::Le(Sort::Int) => c2.ge(c1),
498            BinOp::Eq => Some(c1.eq(c2)),
499            BinOp::Ne => Some(c1.ne(c2)),
500            _ => None,
501        }
502    }
503
504    /// Simplify the expression by removing double negations, short-circuiting boolean connectives and
505    /// doing constant folding. Note that we also have [`TypeFoldable::normalize`] which applies beta
506    /// reductions for tuples and abstractions.
507    ///
508    /// Additionally replaces any occurrences of elements in assumed_preds with True.
509    pub fn simplify(&self, assumed_preds: &SnapshotMap<Expr, ()>) -> Expr {
510        struct Simplify<'a> {
511            assumed_preds: &'a SnapshotMap<Expr, ()>,
512        }
513
514        impl TypeFolder for Simplify<'_> {
515            fn fold_expr(&mut self, expr: &Expr) -> Expr {
516                if self.assumed_preds.get(&expr.erase_spans()).is_some() {
517                    return Expr::tt();
518                }
519                let span = expr.span();
520                match expr.kind() {
521                    ExprKind::BinaryOp(op, e1, e2) => {
522                        let e1 = e1.fold_with(self);
523                        let e2 = e2.fold_with(self);
524                        match (op, e1.kind(), e2.kind()) {
525                            (BinOp::And, ExprKind::Constant(Constant::Bool(false)), _) => {
526                                Expr::constant(Constant::Bool(false)).at_opt(e1.span())
527                            }
528                            (BinOp::And, _, ExprKind::Constant(Constant::Bool(false))) => {
529                                Expr::constant(Constant::Bool(false)).at_opt(e2.span())
530                            }
531                            (BinOp::And, ExprKind::Constant(Constant::Bool(true)), _) => e2,
532                            (BinOp::And, _, ExprKind::Constant(Constant::Bool(true))) => e1,
533                            (op, ExprKind::Constant(c1), ExprKind::Constant(c2)) => {
534                                if let Some(c) = Expr::const_op(op, c1, c2) {
535                                    Expr::constant(c).at_opt(span.or(e2.span()))
536                                } else {
537                                    Expr::binary_op(op.clone(), e1, e2).at_opt(span)
538                                }
539                            }
540                            _ => Expr::binary_op(op.clone(), e1, e2).at_opt(span),
541                        }
542                    }
543                    ExprKind::UnaryOp(UnOp::Not, e) => {
544                        let e = e.fold_with(self);
545                        match e.kind() {
546                            ExprKind::Constant(Constant::Bool(b)) => {
547                                Expr::constant(Constant::Bool(!b))
548                            }
549                            ExprKind::UnaryOp(UnOp::Not, e) => e.clone(),
550                            ExprKind::BinaryOp(BinOp::Eq, e1, e2) => {
551                                Expr::binary_op(BinOp::Ne, e1, e2).at_opt(span)
552                            }
553                            _ => Expr::unary_op(UnOp::Not, e).at_opt(span),
554                        }
555                    }
556                    ExprKind::IfThenElse(p, e1, e2) => {
557                        let p = p.fold_with(self);
558                        if p.is_trivially_true() {
559                            e1.fold_with(self).at_opt(span)
560                        } else if p.is_trivially_false() {
561                            e2.fold_with(self).at_opt(span)
562                        } else {
563                            Expr::ite(p, e1.fold_with(self), e2.fold_with(self)).at_opt(span)
564                        }
565                    }
566                    _ => expr.super_fold_with(self),
567                }
568            }
569        }
570        self.fold_with(&mut Simplify { assumed_preds })
571    }
572
573    pub fn to_loc(&self) -> Option<Loc> {
574        match self.kind() {
575            ExprKind::Local(local) => Some(Loc::Local(*local)),
576            ExprKind::Var(var) => Some(Loc::Var(*var)),
577            _ => None,
578        }
579    }
580
581    pub fn to_path(&self) -> Option<Path> {
582        let mut expr = self;
583        let mut proj = vec![];
584        while let ExprKind::PathProj(e, field) = expr.kind() {
585            proj.push(*field);
586            expr = e;
587        }
588        proj.reverse();
589        Some(Path::new(expr.to_loc()?, proj))
590    }
591
592    /// Whether this is an aggregate expression with no fields.
593    pub fn is_unit(&self) -> bool {
594        matches!(self.kind(), ExprKind::Tuple(flds) if flds.is_empty())
595            || matches!(self.kind(), ExprKind::Ctor(Ctor::Struct(_), flds) if flds.is_empty())
596    }
597
598    pub fn eta_expand_abs(&self, inputs: &BoundVariableKinds, output: Sort) -> Lambda {
599        let args = (0..inputs.len())
600            .map(|idx| Expr::bvar(INNERMOST, BoundVar::from_usize(idx), BoundReftKind::Anon))
601            .collect();
602        let body = Expr::app(self, List::empty(), args);
603        Lambda::bind_with_vars(body, inputs.clone(), output)
604    }
605
606    /// Applies a field projection to an expression and optimistically try to beta reduce it
607    pub fn proj_and_reduce(&self, proj: FieldProj) -> Expr {
608        match self.kind() {
609            ExprKind::Tuple(flds) | ExprKind::Ctor(Ctor::Struct(_) | Ctor::RawPtr, flds) => {
610                flds[proj.field_idx() as usize].clone()
611            }
612            _ => Expr::field_proj(self.clone(), proj),
613        }
614    }
615
616    pub fn visit_conj<'a>(&'a self, mut f: impl FnMut(&'a Expr)) {
617        fn go<'a>(e: &'a Expr, f: &mut impl FnMut(&'a Expr)) {
618            if let ExprKind::BinaryOp(BinOp::And, e1, e2) = e.kind() {
619                go(e1, f);
620                go(e2, f);
621            } else {
622                f(e);
623            }
624        }
625        go(self, &mut f);
626    }
627
628    pub fn flatten_conjs(&self) -> Vec<&Expr> {
629        let mut vec = vec![];
630        self.visit_conj(|e| vec.push(e));
631        vec
632    }
633
634    pub fn has_evars(&self) -> bool {
635        struct HasEvars;
636
637        impl TypeVisitor for HasEvars {
638            type BreakTy = ();
639            fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<Self::BreakTy> {
640                if let ExprKind::Var(Var::EVar(_)) = expr.kind() {
641                    ControlFlow::Break(())
642                } else {
643                    expr.super_visit_with(self)
644                }
645            }
646        }
647
648        self.visit_with(&mut HasEvars).is_break()
649    }
650
651    pub fn erase_spans(&self) -> Expr {
652        struct SpanEraser;
653        impl TypeFolder for SpanEraser {
654            fn fold_expr(&mut self, e: &Expr) -> Expr {
655                e.super_fold_with(self).at_opt(None)
656            }
657        }
658        self.fold_with(&mut SpanEraser)
659    }
660
661    /// Replaces [`BoundReftKind::Named(..)`] in [`Var::Bound(..)`] with
662    /// [`BoundReftKind::Anon`]. This is to ensure that expr equality works
663    /// properly --- the names are just annotations that do not change the
664    /// expressions themselves.
665    ///
666    /// If you think you need this function, you may be looking for
667    /// [`Expr::erase_metadata()`].
668    pub fn erase_bound_reft_kind(&self) -> Expr {
669        struct BoundReftKindEraser;
670        impl TypeFolder for BoundReftKindEraser {
671            fn fold_expr(&mut self, expr: &Expr) -> Expr {
672                if let ExprKind::Var(Var::Bound(
673                    db_index,
674                    BoundReft { var, kind: BoundReftKind::Named(_) },
675                )) = expr.kind()
676                {
677                    Expr::bvar(*db_index, *var, BoundReftKind::Anon)
678                } else {
679                    expr.super_fold_with(self)
680                }
681            }
682        }
683        self.fold_with(&mut BoundReftKindEraser)
684    }
685
686    pub fn erase_metadata(&self) -> Expr {
687        self.erase_spans().erase_bound_reft_kind()
688    }
689
690    /// Binary relations are lifted to tuples and ADTs, so e.g.
691    ///
692    ///     (1, false) > (0, true)
693    ///
694    /// is treated as
695    ///
696    ///     1 > 0 && false > true
697    ///
698    /// This "expands" the binary relations on tuples and ADTs to the second
699    /// form where their elements are individually compared.
700    ///
701    /// Only some binary relations are lifted in this manner, see
702    /// `bin_op_to_fixpoint` in `fixpoint_encoding.rs`.
703    pub fn expand_bin_rels(&self) -> Expr {
704        struct BinRelExpander;
705        impl BinRelExpander {
706            fn expand_bin_rel(&mut self, sort: &Sort, rel: &BinOp, e1: &Expr, e2: &Expr) -> Expr {
707                match sort {
708                    Sort::Tuple(sorts) => {
709                        let arity = sorts.len();
710                        self.apply_bin_rel_rec(sorts, rel, e1, e2, |field| {
711                            FieldProj::Tuple { arity, field }
712                        })
713                    }
714                    Sort::App(SortCtor::Adt(sort_def), args)
715                        if let Some(variant) = sort_def.opt_struct_variant() =>
716                    {
717                        let def_id = sort_def.did();
718                        let sorts = variant.field_sorts(args);
719                        self.apply_bin_rel_rec(&sorts, rel, e1, e2, |field| {
720                            FieldProj::Adt { def_id, field }
721                        })
722                    }
723                    _ => {
724                        Expr::binary_op(
725                            rel.clone(),
726                            e1.super_fold_with(self),
727                            e2.super_fold_with(self),
728                        )
729                    }
730                }
731            }
732            /// Apply binary relation recursively over aggregate expressions
733            fn apply_bin_rel_rec(
734                &mut self,
735                sorts: &[Sort],
736                rel: &BinOp,
737                e1: &Expr,
738                e2: &Expr,
739                mk_proj: impl Fn(u32) -> FieldProj,
740            ) -> Expr {
741                Expr::and_from_iter(sorts.iter().enumerate().map(|(idx, s)| {
742                    let proj = mk_proj(idx as u32);
743                    let e1 = e1.proj_and_reduce(proj);
744                    let e2 = e2.proj_and_reduce(proj);
745                    self.expand_bin_rel(s, rel, &e1, &e2)
746                }))
747            }
748        }
749        impl TypeFolder for BinRelExpander {
750            fn fold_expr(&mut self, expr: &Expr) -> Expr {
751                if let ExprKind::BinaryOp(
752                    rel @ (BinOp::Le(sort) | BinOp::Lt(sort) | BinOp::Ge(sort) | BinOp::Gt(sort)),
753                    e1,
754                    e2,
755                ) = expr.kind()
756                {
757                    self.expand_bin_rel(sort, rel, e1, e2)
758                } else {
759                    expr.super_fold_with(self)
760                }
761            }
762        }
763
764        let mut expander = BinRelExpander {};
765        self.fold_with(&mut expander)
766    }
767
768    /// This is really dumb but we sometimes have the following:
769    ///     Vec { k.0 }
770    /// And we want to really have it rendered as
771    ///     k
772    /// So what we do is look for Tuple or Ctor exprs whose subfields are all
773    /// projections of the same expr, and reduce them if so. Note that we
774    /// **also** require that these are in the same order.
775    pub fn eta_reduce_projs(&self) -> Self {
776        struct EtaReducer;
777        impl TypeFolder for EtaReducer {
778            fn fold_expr(&mut self, expr: &Expr) -> Expr {
779                match expr.kind() {
780                    ExprKind::Tuple(subexprs) => {
781                        let new_subexprs = subexprs
782                            .iter()
783                            .map(|subexpr| subexpr.fold_with(self))
784                            .collect_vec();
785                        if let Some(ExprKind::FieldProj(
786                            e_inner,
787                            FieldProj::Tuple { arity: _, field: 0 },
788                        )) = subexprs.first().map(|e| e.kind())
789                            && new_subexprs[1..].iter().zip(1..).all(|(other_e, i)| {
790                                if let ExprKind::FieldProj(
791                                    other_e_inner,
792                                    FieldProj::Tuple { arity: _, field },
793                                ) = other_e.kind()
794                                {
795                                    field == &i && &e_inner.erase_metadata() == other_e_inner
796                                } else {
797                                    false
798                                }
799                            })
800                        {
801                            e_inner.clone()
802                        } else {
803                            expr.clone()
804                        }
805                    }
806                    ExprKind::Ctor(_ctor, subexprs) => {
807                        let new_subexprs = subexprs
808                            .iter()
809                            .map(|subexpr| subexpr.fold_with(self))
810                            .collect_vec();
811                        if let Some(ExprKind::FieldProj(
812                            e_inner,
813                            FieldProj::Adt { def_id: _, field: 0 },
814                        )) = subexprs.first().map(|e| e.kind())
815                            && new_subexprs[1..].iter().zip(1..).all(|(other_e, i)| {
816                                if let ExprKind::FieldProj(
817                                    other_e_inner,
818                                    FieldProj::Adt { def_id: _, field },
819                                ) = other_e.kind()
820                                {
821                                    field == &i && &e_inner.erase_metadata() == other_e_inner
822                                } else {
823                                    false
824                                }
825                            })
826                        {
827                            e_inner.clone()
828                        } else {
829                            expr.clone()
830                        }
831                    }
832                    _ => expr.super_fold_with(self),
833                }
834            }
835        }
836        self.fold_with(&mut EtaReducer)
837    }
838}
839
840#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
841pub struct ESpan {
842    /// The top-level span information
843    pub span: Span,
844    /// The span for the (base) call-site for def-expanded spans
845    pub base: Option<Span>,
846}
847
848impl ESpan {
849    pub fn new(span: Span) -> Self {
850        Self { span, base: None }
851    }
852
853    pub fn with_base(&self, espan: ESpan) -> Self {
854        Self { span: self.span, base: Some(espan.span) }
855    }
856}
857
858#[derive(
859    Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, TypeFoldable, TypeVisitable,
860)]
861pub enum BinOp {
862    Iff,
863    Imp,
864    Or,
865    And,
866    Eq,
867    Ne,
868    Gt(Sort),
869    Ge(Sort),
870    Lt(Sort),
871    Le(Sort),
872    Add(Sort),
873    Sub(Sort),
874    Mul(Sort),
875    Div(Sort),
876    Mod(Sort),
877    BitAnd(Sort),
878    BitOr(Sort),
879    BitXor(Sort),
880    BitShl(Sort),
881    BitShr(Sort),
882}
883
884#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Debug, Decodable)]
885pub enum UnOp {
886    Not,
887    Neg,
888}
889
890#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, TyEncodable, TyDecodable)]
891pub enum Ctor {
892    /// for indices represented as `struct` in the refinement logic (e.g. using `refined_by` annotations)
893    Struct(DefId),
894    /// for indices represented as  `enum` in the refinement logic (e.g. using `reflected` annotations)
895    Enum(DefId, VariantIdx),
896    /// for the builtin indices of raw pointers
897    RawPtr,
898}
899
900impl Ctor {
901    fn is_enum(&self) -> bool {
902        matches!(self, Self::Enum(..))
903    }
904
905    pub fn def_id_and_variant(&self) -> Option<(DefId, VariantIdx)> {
906        match self {
907            Self::Struct(def_id) => Some((*def_id, FIRST_VARIANT)),
908            Self::Enum(def_id, variant_idx) => Some((*def_id, *variant_idx)),
909            Self::RawPtr => None,
910        }
911    }
912}
913
914/// # Primitive Properties
915/// Given a primop `op` with signature `(t1,...,tn) -> t`
916/// We define a refined type for `op` expressed as a `RuleMatcher`
917///
918/// ```text
919/// op :: (x1: t1, ..., xn: tn) -> { t[op_val[op](x1,...,xn)] | op_rel[x1,...,xn] }
920/// ```
921/// That is, using two *uninterpreted functions* `op_val` and `op_rel` that respectively denote
922/// 1. The _value_ of the primop, and
923/// 2. Some invariant _relation_ that holds for the primop.
924///
925/// The latter can be extended by the user via a `property` definition, which allows us
926/// to customize primops like `<<` with extra "facts" or lemmas. See `tests/tests/pos/surface/primops00.rs` for an example.
927#[derive(Debug, Clone, TyEncodable, TyDecodable, PartialEq, Eq, Hash)]
928pub enum InternalFuncKind {
929    /// UIF representing the value of a primop
930    Val(BinOp),
931    /// UIF representing the relationship of a primop
932    Rel(BinOp),
933    // Conversions betweeen Sorts
934    Cast,
935}
936
937#[derive(Debug, Clone, TyEncodable, TyDecodable, PartialEq, Eq, Hash)]
938pub enum SpecFuncKind {
939    /// Theory symbols *interpreted* by the SMT solver
940    Thy(liquid_fixpoint::ThyFunc),
941    /// User-defined function. This can be either a function with a body or a UIF.
942    Def(FluxDefId),
943}
944
945#[derive(
946    Clone, PartialEq, Eq, Hash, TyEncodable, Debug, TyDecodable, TypeVisitable, TypeFoldable,
947)]
948pub enum QuantDom {
949    Bounded { start: usize, end: usize },
950    Unbounded,
951}
952
953#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, Debug, TyDecodable)]
954pub enum ExprKind {
955    Var(Var),
956    Local(Local),
957    Constant(Constant),
958    /// A rust constant. This can be either `DefKind::Const` or `DefKind::AssocConst`
959    ConstDefId(DefId),
960    BinaryOp(BinOp, Expr, Expr),
961    GlobalFunc(SpecFuncKind),
962    InternalFunc(InternalFuncKind),
963    UnaryOp(UnOp, Expr),
964    FieldProj(Expr, FieldProj),
965    /// A variant used in the logic to represent a variant of an ADT as a pair of the `DefId` and variant-index
966    Ctor(Ctor, List<Expr>),
967    Tuple(List<Expr>),
968    PathProj(Expr, FieldIdx),
969    IfThenElse(Expr, Expr, Expr),
970    KVar(KVar),
971    WKVar(WKVar),
972    Alias(AliasReft, List<Expr>),
973    Let(Expr, Binder<Expr>),
974    /// Function application. The syntax allows arbitrary expressions in function position, but in
975    /// practice we are restricted by what's possible to encode in fixpoint. In a nutshell, we need
976    /// to make sure that expressions that can't be encoded are eliminated before we generate the
977    /// fixpoint constraint. Most notably, lambda abstractions have to be fully applied before
978    /// encoding into fixpoint (except when they appear as an index at the top-level).
979    App(Expr, List<SortArg>, List<Expr>),
980    /// Lambda abstractions. They are purely syntactic and we don't encode them in the logic. As such,
981    /// they have some syntactic restrictions that we must carefully maintain:
982    ///
983    /// 1. They can appear as an index at the top level.
984    /// 2. We can only substitute an abstraction for a variable in function position (or as an index).
985    ///    More generally, we need to partially evaluate expressions such that all abstractions in
986    ///    non-index position are eliminated before encoding into fixpoint. Right now, the
987    ///    implementation only evaluates abstractions that are immediately applied to arguments,
988    ///    thus the restriction.
989    Abs(Lambda),
990
991    /// Bounded quantifiers `exists i in 0..4 { pred(i) }` and `forall i in 0..4 { pred(i) }`.
992    Quant(fhir::QuantKind, QuantDom, Binder<Expr>),
993    /// A hole is an expression that must be inferred either *semantically* by generating a kvar or
994    /// *syntactically* by generating an evar. Whether a hole can be inferred semantically or
995    /// syntactically depends on the position it appears: only holes appearing in predicate position
996    /// can be inferred with a kvar (provided it satisfies the fixpoint horn constraints) and only
997    /// holes used as an index (a position that fully determines their value) can be inferred with
998    /// an evar.
999    ///
1000    /// Holes are implicitly defined in a scope, i.e., their solution could mention free and bound
1001    /// variables in this scope. This must be considered when generating an inference variables for
1002    /// them (either evar or kvar). In fact, the main reason we have holes is that we want to
1003    /// decouple the places where we generate holes (where we don't want to worry about the scope),
1004    /// and the places where we generate inference variable for them (where we do need to worry
1005    /// about the scope).
1006    Hole(HoleKind),
1007    /// Is the expression constructed from constructor of the given DefId (which should be `reflected` Enum)
1008    IsCtor(DefId, VariantIdx, Expr),
1009}
1010
1011impl ExprKind {
1012    fn intern(self) -> Expr {
1013        Expr { kind: Interned::new(self), espan: None }
1014    }
1015}
1016
1017#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
1018pub enum AggregateKind {
1019    Tuple(usize),
1020    Adt(DefId),
1021}
1022
1023#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
1024pub enum FieldProj {
1025    Tuple { arity: usize, field: u32 },
1026    Adt { def_id: DefId, field: u32 },
1027    RawPtr { field: RawPtrField },
1028}
1029
1030#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
1031pub enum RawPtrField {
1032    Base,
1033    Addr,
1034    Size,
1035}
1036
1037struct RawPtrFieldData {
1038    field: RawPtrField,
1039    index: u32,
1040    name: &'static str,
1041    sort: Sort,
1042}
1043
1044impl FieldProj {
1045    pub fn arity(&self, genv: GlobalEnv) -> QueryResult<usize> {
1046        match self {
1047            FieldProj::Tuple { arity, .. } => Ok(*arity),
1048            FieldProj::Adt { def_id, .. } => {
1049                Ok(genv.adt_sort_def_of(*def_id)?.struct_variant().fields())
1050            }
1051            FieldProj::RawPtr { .. } => Ok(RawPtrField::arity()),
1052        }
1053    }
1054
1055    pub fn field_idx(&self) -> u32 {
1056        match self {
1057            FieldProj::Tuple { field, .. } | FieldProj::Adt { field, .. } => *field,
1058            FieldProj::RawPtr { field } => field.index(),
1059        }
1060    }
1061}
1062
1063impl RawPtrField {
1064    const DATA: [RawPtrFieldData; 3] = [
1065        RawPtrFieldData { field: Self::Base, index: 0, name: "base", sort: Sort::Int },
1066        RawPtrFieldData { field: Self::Addr, index: 1, name: "addr", sort: Sort::Int },
1067        RawPtrFieldData { field: Self::Size, index: 2, name: "size", sort: Sort::Int },
1068    ];
1069
1070    pub fn iter() -> impl ExactSizeIterator<Item = Self> {
1071        Self::DATA.iter().map(|data| data.field)
1072    }
1073
1074    pub fn arity() -> usize {
1075        Self::DATA.len()
1076    }
1077
1078    fn data(self) -> &'static RawPtrFieldData {
1079        Self::DATA.iter().find(|data| data.field == self).unwrap()
1080    }
1081
1082    pub fn from_name(name: Symbol) -> Option<Self> {
1083        Self::DATA
1084            .iter()
1085            .find(|data| data.name == name.as_str())
1086            .map(|data| data.field)
1087    }
1088
1089    pub fn from_index(index: u32) -> Option<Self> {
1090        Self::DATA
1091            .iter()
1092            .find(|data| data.index == index)
1093            .map(|data| data.field)
1094    }
1095
1096    pub fn sort(self) -> Sort {
1097        self.data().sort.clone()
1098    }
1099
1100    pub fn index(self) -> u32 {
1101        self.data().index
1102    }
1103
1104    pub fn name(self) -> &'static str {
1105        self.data().name
1106    }
1107
1108    pub fn symbol(self) -> Symbol {
1109        match self {
1110            Self::Base => sym::base,
1111            Self::Addr => sym::addr,
1112            Self::Size => rustc_span::sym::size,
1113        }
1114    }
1115}
1116
1117/// The position where a [hole] appears. This determines how it will be inferred. This is related
1118/// to, but not the same as, an [`InferMode`].
1119///
1120/// [`InferMode`]: super::InferMode
1121/// [hole]: ExprKind::Hole
1122#[derive(
1123    Debug, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable,
1124)]
1125pub enum HoleKind {
1126    /// A hole in predicate position (e.g., the predicate in a [`TyKind::Constr`]). It will be
1127    /// inferred by generating a kvar.
1128    ///
1129    /// [`TyKind::Constr`]: super::TyKind::Constr
1130    Pred,
1131    /// A hole used as a refinement argument or index. It will be inferred by generating an evar.
1132    /// The expression filling the hole must have the provided sort.
1133    ///
1134    /// NOTE(nilehmann) we used to require the `Sort` for generating the evar because we needed it
1135    /// to eta-expand aggregate sorts. We've since removed this behavior but I'm keeping it here
1136    /// just in case. We could remove in case it becomes too problematic.
1137    Expr(Sort),
1138}
1139
1140/// In theory a kvar is just an unknown predicate that can use some variables in scope. In practice,
1141/// fixpoint makes a difference between the first and the rest of the arguments, the first one being
1142/// the kvar's *self argument*. Fixpoint will only instantiate qualifiers that use the self argument.
1143/// Flux generalizes the self argument to be a list. We call the rest of the arguments the *scope*.
1144#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
1145pub struct KVar {
1146    pub kvid: KVid,
1147    /// The number of arguments consider to be *self arguments*.
1148    pub self_args: usize,
1149    /// The list of *all* arguments with the self arguments at the beginning, i.e., the
1150    /// list of self arguments followed by the scope.
1151    pub args: List<Expr>,
1152}
1153
1154#[derive(
1155    Debug, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable,
1156)]
1157pub struct WKVid {
1158    /// Weak KVars right now are always associated with a function definition.
1159    /// Weak KVars can in theory be put wherever we can validly add a refinement
1160    pub parent_fn: DefId,
1161    /// There's no reason why this has to be KVid, and in principle it may be
1162    /// better served as just a number or a new index unto itself.
1163    pub id: KVid,
1164}
1165
1166impl WKVid {
1167    pub fn new(parent_fn: DefId, id: KVid) -> Self {
1168        Self { parent_fn, id }
1169    }
1170}
1171
1172/// A weak kvar is like a kvar with the exception that it infers the weakest
1173/// condition necessary instead of the strongest condition. Due to the way we
1174/// generate these kvars (on fn_sigs, rather than during constraint generation),
1175/// they also in theory are global.
1176#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
1177pub struct WKVar {
1178    pub wkvid: WKVid,
1179    /// Analagous to KVar self arguments except we require that instantiations
1180    /// use *at least one* of the self_args (unless there are none, in which
1181    /// case there is no such requirement).
1182    ///
1183    /// This is mostly relevant for weak kvars corresponding to function
1184    /// *outputs*. Consider the signature
1185    ///
1186    ///     foo: fn(x: usize) -> bool{b: $wk1(b, x)}
1187    ///            requires $wk0(x)
1188    ///
1189    /// In this case self_args would be 1 (corresponding to the bool `b`).
1190    /// Consider now the snippet
1191    ///
1192    ///     let b = foo(x);
1193    ///     assert(x > 0);
1194    ///
1195    /// Suppose the assertion fails. Without the self_args requirement, we
1196    /// could validly instantiate
1197    ///
1198    ///     $wk1(b, x) := x > 0
1199    ///
1200    /// But this is somewhat nonsensical because only in exceptional cases
1201    /// does it make sense for `foo` to somehow "add" information to its
1202    /// **argument** (`x`).
1203    ///
1204    /// By checking for the presence of **at least one** self arg, we
1205    /// ensure that the self argument is "used"; fixpoint does a similar check.
1206    ///
1207    /// This is a syntactic underapproximation and doesn't preclude things like
1208    ///
1209    ///     $wk1(b, x) := (b => true) && (x > 0)
1210    ///
1211    /// But we could conceive of a more sophisticated check using the self_args.
1212    pub self_args: usize,
1213    /// All arguments with self arguments at the beginning.
1214    pub args: List<Expr>,
1215}
1216
1217#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, Decodable)]
1218pub struct EarlyReftParam {
1219    pub index: u32,
1220    pub name: Symbol,
1221}
1222
1223#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, Decodable, Debug)]
1224pub struct BoundReft {
1225    pub var: BoundVar,
1226    pub kind: BoundReftKind,
1227}
1228
1229#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
1230pub enum Var {
1231    Free(Name),
1232    Bound(DebruijnIndex, BoundReft),
1233    EarlyParam(EarlyReftParam),
1234    EVar(EVid),
1235    ConstGeneric(ParamConst),
1236}
1237
1238#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
1239pub struct Path {
1240    pub loc: Loc,
1241    projection: List<FieldIdx>,
1242}
1243
1244#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
1245pub enum Loc {
1246    Local(Local),
1247    Var(Var),
1248}
1249
1250newtype_index! {
1251    /// *E*xistential *v*ariable *id*
1252    #[debug_format = "?{}e"]
1253    #[orderable]
1254    #[encodable]
1255    pub struct EVid {}
1256}
1257
1258newtype_index! {
1259    #[debug_format = "$k{}"]
1260    #[encodable]
1261    pub struct KVid {}
1262}
1263
1264newtype_index! {
1265    #[debug_format = "a{}"]
1266    #[orderable]
1267    #[encodable]
1268    pub struct Name {}
1269}
1270
1271#[derive(Copy, Debug, Clone)]
1272pub enum NameProvenance {
1273    Unknown,
1274    UnfoldBoundReft(BoundReftKind),
1275}
1276
1277impl NameProvenance {
1278    pub fn opt_symbol(&self) -> Option<Symbol> {
1279        match &self {
1280            NameProvenance::UnfoldBoundReft(BoundReftKind::Named(name)) => Some(*name),
1281            _ => None,
1282        }
1283    }
1284}
1285
1286#[derive(PartialEq, Eq, Hash, Debug)]
1287pub enum PrettyVar<V> {
1288    Local(V),
1289    Param(EarlyReftParam),
1290}
1291
1292impl<V: Copy + Into<usize>> PrettyVar<V> {
1293    pub fn as_subscript(&self) -> String {
1294        let idx = match self {
1295            PrettyVar::Local(v) => (*v).into(),
1296            PrettyVar::Param(p) => p.index as usize,
1297        };
1298        as_subscript(idx)
1299    }
1300}
1301
1302pub struct PrettyMap<V: Eq + Hash> {
1303    map: UnordMap<PrettyVar<V>, String>,
1304    count: UnordMap<Symbol, usize>,
1305}
1306
1307impl<V: Eq + Hash + Copy + Into<usize>> PrettyMap<V> {
1308    pub fn new() -> Self {
1309        PrettyMap { map: UnordMap::default(), count: UnordMap::default() }
1310    }
1311
1312    pub fn set(&mut self, var: PrettyVar<V>, prefix: Option<Symbol>) -> String {
1313        // if already defined, return it
1314        if let Some(symbol) = self.map.get(&var) {
1315            return symbol.clone();
1316        }
1317        // else define it, and stash
1318        let symbol = if let Some(prefix) = prefix {
1319            let index = self.count.entry(prefix).or_insert(0);
1320            let symbol = format!("{}{}", prefix, as_subscript(*index));
1321            *index += 1;
1322            symbol
1323        } else {
1324            format!("a'{}", var.as_subscript())
1325        };
1326        self.map.insert(var, symbol.clone());
1327        symbol
1328    }
1329
1330    pub fn get(&self, key: &PrettyVar<V>) -> String {
1331        match self.map.get(key) {
1332            Some(s) => s.clone(),
1333            None => format!("a'{}", key.as_subscript()),
1334        }
1335    }
1336}
1337
1338impl KVar {
1339    pub fn new(kvid: KVid, self_args: usize, args: Vec<Expr>) -> Self {
1340        KVar { kvid, self_args, args: List::from_vec(args) }
1341    }
1342
1343    fn self_args(&self) -> &[Expr] {
1344        &self.args[..self.self_args]
1345    }
1346
1347    fn scope(&self) -> &[Expr] {
1348        &self.args[self.self_args..]
1349    }
1350}
1351
1352impl WKVar {
1353    fn self_args(&self) -> &[Expr] {
1354        &self.args[..self.self_args]
1355    }
1356
1357    fn scope(&self) -> &[Expr] {
1358        &self.args[self.self_args..]
1359    }
1360}
1361
1362impl Var {
1363    pub fn to_expr(&self) -> Expr {
1364        Expr::var(*self)
1365    }
1366
1367    pub fn shift_in(&self, amount: u32) -> Self {
1368        match self {
1369            Var::Bound(idx, breft) => Var::Bound(idx.shifted_in(amount), *breft),
1370            _ => *self,
1371        }
1372    }
1373
1374    pub fn shift_out(&self, amount: u32) -> Self {
1375        match self {
1376            Var::Bound(idx, breft) => Var::Bound(idx.shifted_out(amount), *breft),
1377            _ => *self,
1378        }
1379    }
1380}
1381
1382impl Path {
1383    pub fn new(loc: Loc, projection: impl Into<List<FieldIdx>>) -> Path {
1384        Path { loc, projection: projection.into() }
1385    }
1386
1387    pub fn projection(&self) -> &[FieldIdx] {
1388        &self.projection[..]
1389    }
1390
1391    pub fn to_expr(&self) -> Expr {
1392        self.projection
1393            .iter()
1394            .fold(self.loc.to_expr(), |e, f| Expr::path_proj(e, *f))
1395    }
1396
1397    pub fn to_loc(&self) -> Option<Loc> {
1398        if self.projection.is_empty() { Some(self.loc) } else { None }
1399    }
1400}
1401
1402impl Loc {
1403    pub fn to_expr(&self) -> Expr {
1404        match self {
1405            Loc::Local(local) => Expr::local(*local),
1406            Loc::Var(var) => Expr::var(*var),
1407        }
1408    }
1409}
1410
1411macro_rules! impl_ops {
1412    ($($op:ident: $method:ident),*) => {$(
1413        impl<Rhs> std::ops::$op<Rhs> for Expr
1414        where
1415            Rhs: Into<Expr>,
1416        {
1417            type Output = Expr;
1418
1419            fn $method(self, rhs: Rhs) -> Self::Output {
1420                let sort = crate::rty::Sort::Int;
1421                Expr::binary_op(BinOp::$op(sort), self, rhs)
1422            }
1423        }
1424
1425        impl<Rhs> std::ops::$op<Rhs> for &Expr
1426        where
1427            Rhs: Into<Expr>,
1428        {
1429            type Output = Expr;
1430
1431            fn $method(self, rhs: Rhs) -> Self::Output {
1432                let sort = crate::rty::Sort::Int;
1433                Expr::binary_op(BinOp::$op(sort), self, rhs)
1434            }
1435        }
1436    )*};
1437}
1438impl_ops!(Add: add, Sub: sub, Mul: mul, Div: div);
1439
1440impl From<i32> for Expr {
1441    fn from(value: i32) -> Self {
1442        Expr::constant(Constant::from(value))
1443    }
1444}
1445
1446impl From<&Expr> for Expr {
1447    fn from(e: &Expr) -> Self {
1448        e.clone()
1449    }
1450}
1451
1452impl From<Path> for Expr {
1453    fn from(path: Path) -> Self {
1454        path.to_expr()
1455    }
1456}
1457
1458impl From<Name> for Expr {
1459    fn from(name: Name) -> Self {
1460        Expr::fvar(name)
1461    }
1462}
1463
1464impl From<Var> for Expr {
1465    fn from(var: Var) -> Self {
1466        Expr::var(var)
1467    }
1468}
1469
1470impl From<SpecFuncKind> for Expr {
1471    fn from(kind: SpecFuncKind) -> Self {
1472        Expr::global_func(kind)
1473    }
1474}
1475
1476impl From<InternalFuncKind> for Expr {
1477    fn from(kind: InternalFuncKind) -> Self {
1478        Expr::internal_func(kind)
1479    }
1480}
1481
1482impl From<Loc> for Path {
1483    fn from(loc: Loc) -> Self {
1484        Path::new(loc, vec![])
1485    }
1486}
1487
1488impl From<Name> for Loc {
1489    fn from(name: Name) -> Self {
1490        Loc::Var(Var::Free(name))
1491    }
1492}
1493
1494impl From<Local> for Loc {
1495    fn from(local: Local) -> Self {
1496        Loc::Local(local)
1497    }
1498}
1499
1500#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable)]
1501pub struct Real(pub Symbol);
1502
1503#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable)]
1504pub enum Constant {
1505    Int(BigInt),
1506    Real(Real),
1507    Bool(bool),
1508    Str(Symbol),
1509    Char(char),
1510    BitVec(u128, u32),
1511}
1512
1513impl Constant {
1514    pub const ZERO: Constant = Constant::Int(BigInt::ZERO);
1515    pub const ONE: Constant = Constant::Int(BigInt::ONE);
1516    pub const TRUE: Constant = Constant::Bool(true);
1517
1518    fn to_bool(self) -> Option<bool> {
1519        match self {
1520            Constant::Bool(b) => Some(b),
1521            _ => None,
1522        }
1523    }
1524
1525    fn to_int(self) -> Option<BigInt> {
1526        match self {
1527            Constant::Int(n) => Some(n),
1528            _ => None,
1529        }
1530    }
1531
1532    pub fn iff(&self, other: &Constant) -> Option<Constant> {
1533        let b1 = self.to_bool()?;
1534        let b2 = other.to_bool()?;
1535        Some(Constant::Bool(b1 == b2))
1536    }
1537
1538    pub fn imp(&self, other: &Constant) -> Option<Constant> {
1539        let b1 = self.to_bool()?;
1540        let b2 = other.to_bool()?;
1541        Some(Constant::Bool(!b1 || b2))
1542    }
1543
1544    pub fn or(&self, other: &Constant) -> Option<Constant> {
1545        let b1 = self.to_bool()?;
1546        let b2 = other.to_bool()?;
1547        Some(Constant::Bool(b1 || b2))
1548    }
1549
1550    pub fn and(&self, other: &Constant) -> Option<Constant> {
1551        let b1 = self.to_bool()?;
1552        let b2 = other.to_bool()?;
1553        Some(Constant::Bool(b1 && b2))
1554    }
1555
1556    pub fn eq(&self, other: &Constant) -> Constant {
1557        Constant::Bool(*self == *other)
1558    }
1559
1560    pub fn ne(&self, other: &Constant) -> Constant {
1561        Constant::Bool(*self != *other)
1562    }
1563
1564    pub fn gt(&self, other: &Constant) -> Option<Constant> {
1565        let n1 = self.to_int()?;
1566        let n2 = other.to_int()?;
1567        Some(Constant::Bool(n1 > n2))
1568    }
1569
1570    pub fn ge(&self, other: &Constant) -> Option<Constant> {
1571        let n1 = self.to_int()?;
1572        let n2 = other.to_int()?;
1573        Some(Constant::Bool(n1 >= n2))
1574    }
1575
1576    pub fn from_scalar_int<'tcx, T>(tcx: TyCtxt<'tcx>, scalar: ScalarInt, t: &T) -> Option<Self>
1577    where
1578        T: ToRustc<'tcx, T = rustc_middle::ty::Ty<'tcx>>,
1579    {
1580        use rustc_middle::ty::TyKind;
1581        let ty = t.to_rustc(tcx);
1582        match ty.kind() {
1583            TyKind::Int(int_ty) => Some(Constant::from(scalar_to_int(tcx, scalar, *int_ty))),
1584            TyKind::Uint(uint_ty) => Some(Constant::from(scalar_to_uint(tcx, scalar, *uint_ty))),
1585            TyKind::Bool => {
1586                let b = scalar_to_bits(tcx, scalar, ty)?;
1587                Some(Constant::Bool(b != 0))
1588            }
1589            TyKind::Char => {
1590                let b = scalar_to_bits(tcx, scalar, ty)?;
1591                Some(Constant::Char(char::from_u32(b as u32)?))
1592            }
1593            _ => bug!(),
1594        }
1595    }
1596
1597    /// See [`BigInt::int_min`]
1598    pub fn int_min(bit_width: u32) -> Constant {
1599        Constant::Int(BigInt::int_min(bit_width))
1600    }
1601
1602    /// See [`BigInt::int_max`]
1603    pub fn int_max(bit_width: u32) -> Constant {
1604        Constant::Int(BigInt::int_max(bit_width))
1605    }
1606
1607    /// See [`BigInt::uint_max`]
1608    pub fn uint_max(bit_width: u32) -> Constant {
1609        Constant::Int(BigInt::uint_max(bit_width))
1610    }
1611}
1612
1613impl From<i32> for Constant {
1614    fn from(c: i32) -> Self {
1615        Constant::Int(c.into())
1616    }
1617}
1618
1619impl From<usize> for Constant {
1620    fn from(u: usize) -> Self {
1621        Constant::Int(u.into())
1622    }
1623}
1624
1625impl From<u32> for Constant {
1626    fn from(c: u32) -> Self {
1627        Constant::Int(c.into())
1628    }
1629}
1630
1631impl From<u64> for Constant {
1632    fn from(c: u64) -> Self {
1633        Constant::Int(c.into())
1634    }
1635}
1636
1637impl From<u128> for Constant {
1638    fn from(c: u128) -> Self {
1639        Constant::Int(c.into())
1640    }
1641}
1642
1643impl From<i128> for Constant {
1644    fn from(c: i128) -> Self {
1645        Constant::Int(c.into())
1646    }
1647}
1648
1649impl From<bool> for Constant {
1650    fn from(b: bool) -> Self {
1651        Constant::Bool(b)
1652    }
1653}
1654
1655impl From<Symbol> for Constant {
1656    fn from(s: Symbol) -> Self {
1657        Constant::Str(s)
1658    }
1659}
1660
1661impl From<char> for Constant {
1662    fn from(c: char) -> Self {
1663        Constant::Char(c)
1664    }
1665}
1666
1667impl_internable!(ExprKind);
1668impl_slice_internable!(Expr, KVar);
1669
1670#[derive(Debug)]
1671pub struct FieldBind<T> {
1672    pub name: Symbol,
1673    pub value: T,
1674}
1675
1676impl<T: Pretty> Pretty for FieldBind<T> {
1677    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1678        w!(cx, f, "{}: {:?}", ^self.name, &self.value)
1679    }
1680}
1681
1682pub(crate) mod pretty {
1683
1684    use flux_rustc_bridge::def_id_to_string;
1685
1686    use super::*;
1687    use crate::name_of_thy_func;
1688
1689    #[derive(PartialEq, Eq, PartialOrd, Ord)]
1690    enum Precedence {
1691        Iff,
1692        Imp,
1693        Or,
1694        And,
1695        Cmp,
1696        Bitvec,
1697        AddSub,
1698        MulDiv,
1699    }
1700
1701    impl BinOp {
1702        fn precedence(&self) -> Precedence {
1703            match self {
1704                BinOp::Iff => Precedence::Iff,
1705                BinOp::Imp => Precedence::Imp,
1706                BinOp::Or => Precedence::Or,
1707                BinOp::And => Precedence::And,
1708                BinOp::Eq
1709                | BinOp::Ne
1710                | BinOp::Gt(_)
1711                | BinOp::Lt(_)
1712                | BinOp::Ge(_)
1713                | BinOp::Le(_) => Precedence::Cmp,
1714                BinOp::Add(_) | BinOp::Sub(_) => Precedence::AddSub,
1715                BinOp::Mul(_) | BinOp::Div(_) | BinOp::Mod(_) => Precedence::MulDiv,
1716                BinOp::BitAnd(_)
1717                | BinOp::BitOr(_)
1718                | BinOp::BitShl(_)
1719                | BinOp::BitShr(_)
1720                | BinOp::BitXor(_) => Precedence::Bitvec,
1721            }
1722        }
1723    }
1724
1725    impl Precedence {
1726        pub fn is_associative(&self) -> bool {
1727            !matches!(self, Precedence::Imp | Precedence::Cmp)
1728        }
1729    }
1730
1731    pub fn should_parenthesize(op: &BinOp, child: &Expr) -> bool {
1732        if let ExprKind::BinaryOp(child_op, ..) = child.kind() {
1733            child_op.precedence() < op.precedence()
1734                || (child_op.precedence() == op.precedence() && !op.precedence().is_associative())
1735        } else {
1736            false
1737        }
1738    }
1739
1740    impl Pretty for Ctor {
1741        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1742            match self {
1743                Ctor::Struct(def_id) => {
1744                    w!(cx, f, "{:?}", def_id)
1745                }
1746                Ctor::Enum(def_id, variant_idx) => {
1747                    w!(cx, f, "{:?}::{:?}", def_id, ^variant_idx)
1748                }
1749                Ctor::RawPtr => w!(cx, f, "ptr"),
1750            }
1751        }
1752    }
1753
1754    impl Pretty for fhir::QuantKind {
1755        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1756            match self {
1757                fhir::QuantKind::Exists => w!(cx, f, "∃"),
1758                fhir::QuantKind::Forall => w!(cx, f, "∀"),
1759            }
1760        }
1761    }
1762
1763    impl Pretty for InternalFuncKind {
1764        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1765            match self {
1766                InternalFuncKind::Val(op) => w!(cx, f, "[{:?}]", op),
1767                InternalFuncKind::Rel(op) => w!(cx, f, "[{:?}]?", op),
1768                InternalFuncKind::Cast => w!(cx, f, "cast"),
1769            }
1770        }
1771    }
1772
1773    impl Pretty for QuantDom {
1774        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1775            match self {
1776                QuantDom::Bounded { start, end } => w!(cx, f, "in {} .. {}", ^start, ^end),
1777                QuantDom::Unbounded => Ok(()),
1778            }
1779        }
1780    }
1781
1782    impl Pretty for Expr {
1783        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1784            let e = if cx.simplify_exprs {
1785                self.simplify(&SnapshotMap::default())
1786            } else {
1787                self.clone()
1788            };
1789            match e.kind() {
1790                ExprKind::Var(var) => w!(cx, f, "{:?}", var),
1791                ExprKind::Local(local) => w!(cx, f, "{:?}", ^local),
1792                ExprKind::ConstDefId(did) => w!(cx, f, "{}", ^def_id_to_string(*did)),
1793                ExprKind::Constant(c) => w!(cx, f, "{:?}", c),
1794
1795                ExprKind::BinaryOp(op, e1, e2) => {
1796                    if should_parenthesize(op, e1) {
1797                        w!(cx, f, "({:?})", e1)?;
1798                    } else {
1799                        w!(cx, f, "{:?}", e1)?;
1800                    }
1801                    if matches!(op, BinOp::Div(_)) {
1802                        w!(cx, f, "{:?}", op)?;
1803                    } else {
1804                        w!(cx, f, " {:?} ", op)?;
1805                    }
1806                    if should_parenthesize(op, e2) {
1807                        w!(cx, f, "({:?})", e2)?;
1808                    } else {
1809                        w!(cx, f, "{:?}", e2)?;
1810                    }
1811                    Ok(())
1812                }
1813                ExprKind::UnaryOp(op, e) => {
1814                    if e.is_atom() {
1815                        w!(cx, f, "{:?}{:?}", op, e)
1816                    } else {
1817                        w!(cx, f, "{:?}({:?})", op, e)
1818                    }
1819                }
1820                ExprKind::FieldProj(e, proj) if let ExprKind::Ctor(_, _) = e.kind() => {
1821                    // special case to avoid printing `{n:12}.n` as `12.n` but instead, just print `12`
1822                    // TODO: maintain an invariant that `FieldProj` never has a Ctor as first argument (as always reduced)
1823                    w!(cx, f, "{:?}", e.proj_and_reduce(*proj))
1824                }
1825                ExprKind::FieldProj(e, proj) => {
1826                    if e.is_atom() {
1827                        w!(cx, f, "{:?}.{}", e, ^fmt_field_proj(cx, *proj))
1828                    } else {
1829                        w!(cx, f, "({:?}).{}", e, ^fmt_field_proj(cx, *proj))
1830                    }
1831                }
1832                ExprKind::Tuple(flds) => {
1833                    if let [e] = &flds[..] {
1834                        w!(cx, f, "({:?},)", e)
1835                    } else {
1836                        w!(cx, f, "({:?})", join!(", ", flds))
1837                    }
1838                }
1839                ExprKind::IsCtor(def_id, variant_idx, idx) => {
1840                    w!(cx, f, "({:?} is {:?}::{:?})", idx, def_id, ^variant_idx)
1841                }
1842                ExprKind::Ctor(ctor, flds) => {
1843                    if matches!(ctor, Ctor::RawPtr) {
1844                        let fields = iter::zip(RawPtrField::iter(), flds)
1845                            .map(|(field, value)| {
1846                                FieldBind { name: field.symbol(), value: value.clone() }
1847                            })
1848                            .collect_vec();
1849                        return w!(cx, f, "ptr {{ {:?} }}", join!(", ", fields));
1850                    }
1851                    let Some((def_id, variant_idx)) = ctor.def_id_and_variant() else {
1852                        unreachable!()
1853                    };
1854                    if let Some(adt_sort_def) = cx.adt_sort_def_of(def_id) {
1855                        let variant = adt_sort_def.variant(variant_idx).field_names();
1856                        let fields = iter::zip(variant, flds)
1857                            .map(|(name, value)| FieldBind { name: *name, value: value.clone() })
1858                            .collect_vec();
1859                        match ctor {
1860                            Ctor::Struct(_) => {
1861                                w!(cx, f, "{:?} {{ {:?} }}", def_id, join!(", ", fields))
1862                            }
1863                            Ctor::Enum(_, idx) => {
1864                                if fields.is_empty() {
1865                                    w!(cx, f, "{:?}::{:?}", def_id, ^idx.index())
1866                                } else {
1867                                    w!(cx, f, "{:?}::{:?}({:?})", def_id, ^idx.index(), join!(", ", fields))
1868                                }
1869                            }
1870                            Ctor::RawPtr => unreachable!(),
1871                        }
1872                    } else {
1873                        match ctor {
1874                            Ctor::Struct(_) => {
1875                                w!(cx, f, "{:?} {{ {:?} }}", def_id, join!(", ", flds))
1876                            }
1877                            Ctor::Enum(_, idx) => {
1878                                w!(cx, f, "{:?}::{:?} {{ {:?} }}", def_id, ^idx, join!(", ", flds))
1879                            }
1880                            Ctor::RawPtr => unreachable!(),
1881                        }
1882                    }
1883                }
1884                ExprKind::PathProj(e, field) => {
1885                    if e.is_atom() {
1886                        w!(cx, f, "{:?}.{:?}", e, field)
1887                    } else {
1888                        w!(cx, f, "({:?}).{:?}", e, field)
1889                    }
1890                }
1891                ExprKind::App(func, _, args) => {
1892                    w!(cx, f, "{:?}({})",
1893                        parens!(func, !func.is_atom()),
1894                        ^args
1895                            .iter()
1896                            .format_with(", ", |arg, f| f(&format_args_cx!(cx, "{:?}", arg)))
1897                    )
1898                }
1899                ExprKind::IfThenElse(p, e1, e2) => {
1900                    w!(cx, f, "if {:?} {{ {:?} }} else {{ {:?} }}", p, e1, e2)
1901                }
1902                ExprKind::Hole(_) => {
1903                    w!(cx, f, "*")
1904                }
1905                ExprKind::KVar(kvar) => {
1906                    w!(cx, f, "{:?}", kvar)
1907                }
1908                ExprKind::WKVar(wkvar) => {
1909                    w!(cx, f, "{:?}", wkvar)
1910                }
1911                ExprKind::Alias(alias, args) => {
1912                    w!(cx, f, "{:?}({:?})", alias, join!(", ", args))
1913                }
1914                ExprKind::Abs(lam) => {
1915                    w!(cx, f, "{:?}", lam)
1916                }
1917                ExprKind::GlobalFunc(SpecFuncKind::Def(did)) => {
1918                    w!(cx, f, "{}", ^did.name())
1919                }
1920                ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => {
1921                    if let Some(name) = name_of_thy_func(*itf) {
1922                        w!(cx, f, "{}", ^name)
1923                    } else {
1924                        w!(cx, f, "<error>")
1925                    }
1926                }
1927                ExprKind::InternalFunc(func) => {
1928                    w!(cx, f, "{:?}", func)
1929                }
1930                ExprKind::Quant(kind, dom, body) => {
1931                    let vars = body.vars();
1932                    cx.with_bound_vars(vars, || {
1933                        w!(cx, f, "{:?} {:?} {{ {:?} }}", kind, dom, body.skip_binder_ref())
1934                    })
1935                }
1936                ExprKind::Let(init, body) => {
1937                    let vars = body.vars();
1938                    cx.with_bound_vars(vars, || {
1939                        cx.fmt_bound_vars(false, "(let ", vars, " = ", f)?;
1940                        w!(cx, f, "{:?} in {:?})", init, body.skip_binder_ref())
1941                    })
1942                }
1943            }
1944        }
1945    }
1946
1947    fn fmt_field_proj(cx: &PrettyCx, proj: FieldProj) -> String {
1948        if let FieldProj::Adt { def_id, field } = proj
1949            && let Some(adt_sort_def) = cx.adt_sort_def_of(def_id)
1950        {
1951            format!("{}", adt_sort_def.struct_variant().field_names()[field as usize])
1952        } else if let FieldProj::RawPtr { field } = proj {
1953            field.name().to_string()
1954        } else {
1955            format!("{}", proj.field_idx())
1956        }
1957    }
1958
1959    impl Pretty for Constant {
1960        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1961            match self {
1962                Constant::Int(i) => w!(cx, f, "{i}"),
1963                Constant::BitVec(i, sz) => w!(cx, f, "bv({i}, {sz})"),
1964                Constant::Real(r) => w!(cx, f, "{}", ^r.0),
1965                Constant::Bool(b) => w!(cx, f, "{b}"),
1966                Constant::Str(sym) => w!(cx, f, "\"{sym}\""),
1967                Constant::Char(c) => w!(cx, f, "\'{c}\'"),
1968            }
1969        }
1970    }
1971
1972    impl Pretty for AliasReft {
1973        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1974            w!(cx, f, "<({:?}) as {:?}", &self.args[0], self.assoc_id.parent())?;
1975            let args = &self.args[1..];
1976            if !args.is_empty() {
1977                w!(cx, f, "<{:?}>", join!(", ", args))?;
1978            }
1979            w!(cx, f, ">::{}", ^self.assoc_id.name())
1980        }
1981    }
1982
1983    impl Pretty for Lambda {
1984        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1985            let vars = self.body.vars();
1986            // TODO: remove redundant vars; see Ty
1987            // let redundant_bvars = self.body.redundant_bvars().into_iter().collect();
1988            cx.with_bound_vars(vars, || {
1989                cx.fmt_bound_vars(false, "λ", vars, ". ", f)?;
1990                w!(cx, f, "{:?}", self.body.as_ref().skip_binder())
1991            })
1992        }
1993    }
1994
1995    impl Pretty for Var {
1996        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1997            match self {
1998                Var::Bound(debruijn, var) => cx.fmt_bound_reft(*debruijn, *var, f),
1999                Var::EarlyParam(var) => w!(cx, f, "{}", ^var.name),
2000                Var::Free(name) => w!(cx, f, "{:?}", ^name),
2001                Var::EVar(evar) => w!(cx, f, "{:?}", ^evar),
2002                Var::ConstGeneric(param) => w!(cx, f, "{}", ^param.name),
2003            }
2004        }
2005    }
2006
2007    impl Pretty for KVar {
2008        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2009            w!(cx, f, "{:?}", ^self.kvid)?;
2010            match cx.kvar_args {
2011                KVarArgs::All => {
2012                    w!(
2013                        cx,
2014                        f,
2015                        "({:?})[{:?}]",
2016                        join!(", ", self.self_args()),
2017                        join!(", ", self.scope())
2018                    )?;
2019                }
2020                KVarArgs::SelfOnly => w!(cx, f, "({:?})", join!(", ", self.self_args()))?,
2021                KVarArgs::Hide => {}
2022            }
2023            Ok(())
2024        }
2025    }
2026
2027    impl Pretty for WKVar {
2028        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2029            // Since we reuse KVId, we need to make the serialization custom.
2030            // Also we will not serialize the parameters for now.
2031            w!(cx, f, "$wk{}_{}", ^self.wkvid.id.index(), ^cx.tcx().def_path_str(self.wkvid.parent_fn))?;
2032            w!(cx, f, "({:?})[{:?}]", join!(", ", self.self_args()), join!(", ", self.scope()))?;
2033            Ok(())
2034        }
2035    }
2036
2037    impl Pretty for Path {
2038        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2039            w!(cx, f, "{:?}", &self.loc)?;
2040            for field in &self.projection {
2041                w!(cx, f, ".{}", ^u32::from(*field))?;
2042            }
2043            Ok(())
2044        }
2045    }
2046
2047    impl Pretty for Loc {
2048        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2049            match self {
2050                Loc::Local(local) => w!(cx, f, "{:?}", ^local),
2051                Loc::Var(var) => w!(cx, f, "{:?}", var),
2052            }
2053        }
2054    }
2055
2056    impl Pretty for BinOp {
2057        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2058            match self {
2059                BinOp::Iff => w!(cx, f, "<=>"),
2060                BinOp::Imp => w!(cx, f, "=>"),
2061                BinOp::Or => w!(cx, f, "||"),
2062                BinOp::And => w!(cx, f, "&&"),
2063                BinOp::Eq => w!(cx, f, "=="),
2064                BinOp::Ne => w!(cx, f, "!="),
2065                BinOp::Gt(_) => w!(cx, f, ">"),
2066                BinOp::Ge(_) => w!(cx, f, ">="),
2067                BinOp::Lt(_) => w!(cx, f, "<"),
2068                BinOp::Le(_) => w!(cx, f, "<="),
2069                BinOp::Add(_) => w!(cx, f, "+"),
2070                BinOp::Sub(_) => w!(cx, f, "-"),
2071                BinOp::Mul(_) => w!(cx, f, "*"),
2072                BinOp::Div(_) => w!(cx, f, "/"),
2073                BinOp::Mod(_) => w!(cx, f, "mod"),
2074                BinOp::BitAnd(_) => w!(cx, f, "&"),
2075                BinOp::BitOr(_) => w!(cx, f, "|"),
2076                BinOp::BitXor(_) => w!(cx, f, "^"),
2077                BinOp::BitShl(_) => w!(cx, f, "<<"),
2078                BinOp::BitShr(_) => w!(cx, f, ">>"),
2079            }
2080        }
2081    }
2082
2083    impl Pretty for UnOp {
2084        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2085            match self {
2086                UnOp::Not => w!(cx, f, "!"),
2087                UnOp::Neg => w!(cx, f, "-"),
2088            }
2089        }
2090    }
2091
2092    impl_debug_with_default_cx!(Expr, Loc, Path, Var, KVar, WKVar, Lambda, AliasReft);
2093
2094    impl PrettyNested for Lambda {
2095        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
2096            // TODO: remove redundant vars; see Ty
2097            cx.nested_with_bound_vars("λ", self.body.vars(), None, |prefix| {
2098                let expr_d = self.body.skip_binder_ref().fmt_nested(cx)?;
2099                let text = format!("{}{}", prefix, expr_d.text);
2100                Ok(NestedString { text, children: expr_d.children, key: None })
2101            })
2102        }
2103    }
2104
2105    pub fn aggregate_nested(
2106        cx: &PrettyCx,
2107        ctor: &Ctor,
2108        flds: &[Expr],
2109        is_named: bool,
2110    ) -> Result<NestedString, fmt::Error> {
2111        let mut text =
2112            if is_named && ctor.is_enum() { format_cx!(cx, "{:?}", ctor) } else { "".to_string() };
2113        if flds.is_empty() {
2114            // No fields, no index
2115            Ok(NestedString { text, children: None, key: None })
2116        } else if flds.len() == 1 {
2117            // Single field, inline index
2118            text += &flds[0].fmt_nested(cx)?.text;
2119            Ok(NestedString { text, children: None, key: None })
2120        } else {
2121            let keys = if let Some((def_id, variant_idx)) = ctor.def_id_and_variant()
2122                && let Some(adt_sort_def) = cx.adt_sort_def_of(def_id)
2123            {
2124                adt_sort_def
2125                    .variant(variant_idx)
2126                    .field_names()
2127                    .iter()
2128                    .map(|name| format!("{name}"))
2129                    .collect_vec()
2130            } else if matches!(ctor, Ctor::RawPtr) {
2131                RawPtrField::iter()
2132                    .map(|field| field.name().to_string())
2133                    .collect_vec()
2134            } else {
2135                (0..flds.len()).map(|i| format!("arg{i}")).collect_vec()
2136            };
2137            // Multiple fields, nested index
2138            text += "{..}";
2139            let mut children = vec![];
2140            for (key, fld) in iter::zip(keys, flds) {
2141                let fld_d = fld.fmt_nested(cx)?;
2142                children.push(NestedString { key: Some(key), ..fld_d });
2143            }
2144            Ok(NestedString { text, children: Some(children), key: None })
2145        }
2146    }
2147
2148    impl PrettyNested for Name {
2149        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
2150            let text = cx.pretty_var_env.get(&PrettyVar::Local(*self));
2151            Ok(NestedString { text, key: None, children: None })
2152        }
2153    }
2154
2155    impl PrettyNested for Expr {
2156        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
2157            let e = if cx.simplify_exprs {
2158                self.simplify(&SnapshotMap::default())
2159            } else {
2160                self.clone()
2161            };
2162            match e.kind() {
2163                ExprKind::Var(Var::Free(name)) => name.fmt_nested(cx),
2164                ExprKind::Var(..)
2165                | ExprKind::Local(..)
2166                | ExprKind::Constant(..)
2167                | ExprKind::ConstDefId(..)
2168                | ExprKind::Hole(..)
2169                | ExprKind::GlobalFunc(..)
2170                | ExprKind::InternalFunc(..) => debug_nested(cx, &e),
2171                ExprKind::WKVar(..) => debug_nested(cx, &e),
2172                ExprKind::KVar(kvar) => {
2173                    let kv = format!("{:?}", kvar.kvid);
2174                    let mut strs = vec![kv];
2175                    for arg in &kvar.args {
2176                        strs.push(arg.fmt_nested(cx)?.text);
2177                    }
2178                    let text = format!("##[{}]##", strs.join("##"));
2179                    Ok(NestedString { text, children: None, key: None })
2180                }
2181                ExprKind::IfThenElse(p, e1, e2) => {
2182                    let p_d = p.fmt_nested(cx)?;
2183                    let e1_d = e1.fmt_nested(cx)?;
2184                    let e2_d = e2.fmt_nested(cx)?;
2185                    let text = format!("(if {} then {} else {})", p_d.text, e1_d.text, e2_d.text);
2186                    let children = float_children(vec![p_d.children, e1_d.children, e2_d.children]);
2187                    Ok(NestedString { text, children, key: None })
2188                }
2189                ExprKind::BinaryOp(op, e1, e2) => {
2190                    let e1_d = e1.fmt_nested(cx)?;
2191                    let e2_d = e2.fmt_nested(cx)?;
2192                    let e1_text = if should_parenthesize(op, e1) {
2193                        format!("({})", e1_d.text)
2194                    } else {
2195                        e1_d.text
2196                    };
2197                    let e2_text = if should_parenthesize(op, e2) {
2198                        format!("({})", e2_d.text)
2199                    } else {
2200                        e2_d.text
2201                    };
2202                    let op_d = debug_nested(cx, op)?;
2203                    let op_text = if matches!(op, BinOp::Div(_)) {
2204                        op_d.text
2205                    } else {
2206                        format!(" {} ", op_d.text)
2207                    };
2208                    let text = format!("{e1_text}{op_text}{e2_text}");
2209                    let children = float_children(vec![e1_d.children, e2_d.children]);
2210                    Ok(NestedString { text, children, key: None })
2211                }
2212                ExprKind::UnaryOp(op, e) => {
2213                    let e_d = e.fmt_nested(cx)?;
2214                    let op_d = debug_nested(cx, op)?;
2215                    let text = if e.is_atom() {
2216                        format!("{}{}", op_d.text, e_d.text)
2217                    } else {
2218                        format!("{}({})", op_d.text, e_d.text)
2219                    };
2220                    Ok(NestedString { text, children: e_d.children, key: None })
2221                }
2222                ExprKind::FieldProj(e, proj) if let ExprKind::Ctor(_, _) = e.kind() => {
2223                    // special case to avoid printing `{n:12}.n` as `12.n` but instead, just print `12`
2224                    // TODO: maintain an invariant that `FieldProj` never has a Ctor as first argument (as always reduced)
2225                    e.proj_and_reduce(*proj).fmt_nested(cx)
2226                }
2227                ExprKind::FieldProj(e, proj) => {
2228                    let e_d = e.fmt_nested(cx)?;
2229                    let text = if e.is_atom() {
2230                        format!("{}.{}", e_d.text, fmt_field_proj(cx, *proj))
2231                    } else {
2232                        format!("({}).{}", e_d.text, fmt_field_proj(cx, *proj))
2233                    };
2234                    Ok(NestedString { text, children: e_d.children, key: None })
2235                }
2236                ExprKind::Tuple(flds) => {
2237                    let mut texts = vec![];
2238                    let mut kidss = vec![];
2239                    for e in flds {
2240                        let e_d = e.fmt_nested(cx)?;
2241                        texts.push(e_d.text);
2242                        kidss.push(e_d.children);
2243                    }
2244                    let text = if let [e] = &texts[..] {
2245                        format!("({e},)")
2246                    } else {
2247                        format!("({})", texts.join(", "))
2248                    };
2249                    let children = float_children(kidss);
2250                    Ok(NestedString { text, children, key: None })
2251                }
2252                ExprKind::Ctor(ctor, flds) => aggregate_nested(cx, ctor, flds, true),
2253                ExprKind::IsCtor(def_id, variant_idx, idx) => {
2254                    let text = format!("is::{:?}::{:?}( {:?} )", def_id, variant_idx, idx);
2255                    Ok(NestedString { text, children: None, key: None })
2256                }
2257                ExprKind::PathProj(e, field) => {
2258                    let e_d = e.fmt_nested(cx)?;
2259                    let text = if e.is_atom() {
2260                        format!("{}.{:?}", e_d.text, field)
2261                    } else {
2262                        format!("({}).{:?}", e_d.text, field)
2263                    };
2264                    Ok(NestedString { text, children: e_d.children, key: None })
2265                }
2266                ExprKind::Alias(alias, args) => {
2267                    let mut texts = vec![];
2268                    let mut kidss = vec![];
2269                    for arg in args {
2270                        let arg_d = arg.fmt_nested(cx)?;
2271                        texts.push(arg_d.text);
2272                        kidss.push(arg_d.children);
2273                    }
2274                    let text = format_cx!(cx, "{:?}({:?})", alias, texts.join(", "));
2275                    let children = float_children(kidss);
2276                    Ok(NestedString { text, children, key: None })
2277                }
2278                ExprKind::App(func, _, args) => {
2279                    let func_d = func.fmt_nested(cx)?;
2280                    let mut texts = vec![];
2281                    let mut kidss = vec![func_d.children];
2282                    for arg in args {
2283                        let arg_d = arg.fmt_nested(cx)?;
2284                        texts.push(arg_d.text);
2285                        kidss.push(arg_d.children);
2286                    }
2287                    let text = if func.is_atom() {
2288                        format!("{}({})", func_d.text, texts.join(", "))
2289                    } else {
2290                        format!("({})({})", func_d.text, texts.join(", "))
2291                    };
2292                    let children = float_children(kidss);
2293                    Ok(NestedString { text, children, key: None })
2294                }
2295                ExprKind::Abs(lambda) => lambda.fmt_nested(cx),
2296                ExprKind::Let(init, body) => {
2297                    // FIXME this is very wrong!
2298                    cx.nested_with_bound_vars("let", body.vars(), None, |prefix| {
2299                        let body = body.skip_binder_ref().fmt_nested(cx)?;
2300                        let text = format!("{:?} {}{}", init, prefix, body.text);
2301                        Ok(NestedString { text, children: body.children, key: None })
2302                    })
2303                }
2304                ExprKind::Quant(kind, dom, body) => {
2305                    let left = match kind {
2306                        fhir::QuantKind::Forall => "∀",
2307                        fhir::QuantKind::Exists => "∃",
2308                    };
2309                    let right = Some(format!(" {:?}", dom));
2310
2311                    cx.nested_with_bound_vars(left, body.vars(), right, |all_str| {
2312                        let expr_d = body.as_ref().skip_binder().fmt_nested(cx)?;
2313                        let text = format!("{}{}", all_str, expr_d.text);
2314                        Ok(NestedString { text, children: expr_d.children, key: None })
2315                    })
2316                }
2317            }
2318        }
2319    }
2320}
2321
2322impl Expr {
2323    /// Applies transformations to simplify and humanize canonical Z3 outputs.
2324    pub fn prettify(&self) -> Self {
2325        self.fold_constants()
2326            .push_negations()
2327            .simplify_arithmetic()
2328            .simplify_bounds()
2329    }
2330
2331    /// Evaluates expressions with constant operands recursively.
2332    pub fn fold_constants(&self) -> Self {
2333        let span = self.span();
2334        match self.kind() {
2335            ExprKind::UnaryOp(op, a) => {
2336                let a_fold = a.fold_constants();
2337
2338                if let ExprKind::Constant(c) = a_fold.kind() {
2339                    match (op, c) {
2340                        (UnOp::Not, Constant::Bool(b)) => {
2341                            return Expr::constant(Constant::Bool(!b)).at_opt(span);
2342                        }
2343                        (UnOp::Neg, Constant::Int(n)) => {
2344                            return Expr::constant(Constant::Int(n.neg())).at_opt(span);
2345                        }
2346                        _ => {}
2347                    }
2348                }
2349                Expr::unary_op(*op, a_fold).at_opt(span)
2350            }
2351
2352            ExprKind::BinaryOp(op, a, b) => {
2353                let a_fold = a.fold_constants();
2354                let b_fold = b.fold_constants();
2355
2356                if let (ExprKind::Constant(c1), ExprKind::Constant(c2)) =
2357                    (a_fold.kind(), b_fold.kind())
2358                {
2359                    // 1. Leverage existing const_op logic (handles Bools, Eq, Ne, and Int comparisons)
2360                    if let Some(c3) = Expr::const_op(op, c1, c2) {
2361                        return Expr::constant(c3).at_opt(span);
2362                    }
2363
2364                    // 2. Handle integer arithmetic
2365                    if let (Constant::Int(n1), Constant::Int(n2)) = (c1, c2) {
2366                        let result = match op {
2367                            BinOp::Add(_) => n1.checked_add(n2),
2368                            BinOp::Sub(_) => n1.checked_sub(n2),
2369                            BinOp::Mul(_) => n1.checked_mul(n2),
2370                            BinOp::Div(_) => n1.checked_div(n2),
2371                            BinOp::Mod(_) => n1.checked_rem(n2),
2372                            _ => None,
2373                        };
2374
2375                        if let Some(res) = result {
2376                            return Expr::constant(Constant::Int(res)).at_opt(span);
2377                        }
2378                    }
2379                }
2380
2381                Expr::binary_op(op.clone(), a_fold, b_fold).at_opt(span)
2382            }
2383
2384            ExprKind::IfThenElse(p, e1, e2) => {
2385                let p_fold = p.fold_constants();
2386
2387                // Short-circuit the ITE if the predicate is a constant boolean
2388                if let ExprKind::Constant(Constant::Bool(b)) = p_fold.kind() {
2389                    if *b {
2390                        return e1.fold_constants().at_opt(span);
2391                    } else {
2392                        return e2.fold_constants().at_opt(span);
2393                    }
2394                }
2395
2396                Expr::ite(p_fold, e1.fold_constants(), e2.fold_constants()).at_opt(span)
2397            }
2398
2399            // Other variants (Tuple, App, FieldProj, etc.) recursively fall through
2400            // untouched to avoid making assumptions about how your inner types construct.
2401            _ => self.clone(),
2402        }
2403    }
2404
2405    /// Pass 1: Eliminate `Not` over inequalities and boolean operations.
2406    pub fn push_negations(&self) -> Self {
2407        let span = self.span();
2408        match self.kind() {
2409            ExprKind::UnaryOp(UnOp::Not, inner) => {
2410                match inner.kind() {
2411                    // Double negation
2412                    ExprKind::UnaryOp(UnOp::Not, a) => a.push_negations().at_opt(span),
2413
2414                    // Flipped Inequalities
2415                    ExprKind::BinaryOp(BinOp::Le(s), a, b) => {
2416                        Expr::binary_op(
2417                            BinOp::Gt(s.clone()),
2418                            a.push_negations(),
2419                            b.push_negations(),
2420                        )
2421                        .at_opt(span)
2422                    }
2423                    ExprKind::BinaryOp(BinOp::Lt(s), a, b) => {
2424                        Expr::binary_op(
2425                            BinOp::Ge(s.clone()),
2426                            a.push_negations(),
2427                            b.push_negations(),
2428                        )
2429                        .at_opt(span)
2430                    }
2431                    ExprKind::BinaryOp(BinOp::Ge(s), a, b) => {
2432                        Expr::binary_op(
2433                            BinOp::Lt(s.clone()),
2434                            a.push_negations(),
2435                            b.push_negations(),
2436                        )
2437                        .at_opt(span)
2438                    }
2439                    ExprKind::BinaryOp(BinOp::Gt(s), a, b) => {
2440                        Expr::binary_op(
2441                            BinOp::Le(s.clone()),
2442                            a.push_negations(),
2443                            b.push_negations(),
2444                        )
2445                        .at_opt(span)
2446                    }
2447
2448                    // Equalities
2449                    ExprKind::BinaryOp(BinOp::Eq, a, b) => {
2450                        Expr::binary_op(BinOp::Ne, a.push_negations(), b.push_negations())
2451                            .at_opt(span)
2452                    }
2453                    ExprKind::BinaryOp(BinOp::Ne, a, b) => {
2454                        Expr::binary_op(BinOp::Eq, a.push_negations(), b.push_negations())
2455                            .at_opt(span)
2456                    }
2457
2458                    // De Morgan's
2459                    ExprKind::BinaryOp(BinOp::Or, a, b) => {
2460                        Expr::binary_op(
2461                            BinOp::And,
2462                            a.not().push_negations(),
2463                            b.not().push_negations(),
2464                        )
2465                        .at_opt(span)
2466                    }
2467                    ExprKind::BinaryOp(BinOp::And, a, b) => {
2468                        Expr::binary_op(
2469                            BinOp::Or,
2470                            a.not().push_negations(),
2471                            b.not().push_negations(),
2472                        )
2473                        .at_opt(span)
2474                    }
2475
2476                    // Otherwise just wrap and stop
2477                    _ => Expr::unary_op(UnOp::Not, inner.push_negations()).at_opt(span),
2478                }
2479            }
2480            ExprKind::BinaryOp(op, a, b) => {
2481                Expr::binary_op(op.clone(), a.push_negations(), b.push_negations()).at_opt(span)
2482            }
2483            ExprKind::UnaryOp(op, a) => Expr::unary_op(*op, a.push_negations()).at_opt(span),
2484            ExprKind::IfThenElse(p, e1, e2) => {
2485                Expr::ite(p.push_negations(), e1.push_negations(), e2.push_negations()).at_opt(span)
2486            }
2487            _ => self.clone(),
2488        }
2489    }
2490
2491    /// Pass 2: Clean up canonicalized arithmetic (e.g. `b0 * -1 + b1` -> `b1 - b0`)
2492    pub fn simplify_arithmetic(&self) -> Self {
2493        let span = self.span();
2494        match self.kind() {
2495            ExprKind::BinaryOp(BinOp::Mul(s), a, b) => {
2496                let a_simp = a.simplify_arithmetic();
2497                let b_simp = b.simplify_arithmetic();
2498
2499                let is_minus_one = |e: &Expr| matches!(e.kind(), ExprKind::Constant(c) if *c == Constant::from(-1));
2500
2501                if is_minus_one(&b_simp) {
2502                    return a_simp.neg().at_opt(span);
2503                }
2504                if is_minus_one(&a_simp) {
2505                    return b_simp.neg().at_opt(span);
2506                }
2507
2508                Expr::binary_op(BinOp::Mul(s.clone()), a_simp, b_simp).at_opt(span)
2509            }
2510            ExprKind::BinaryOp(BinOp::Add(s), a, b) => {
2511                let a_simp = a.simplify_arithmetic();
2512                let b_simp = b.simplify_arithmetic();
2513
2514                if let ExprKind::UnaryOp(UnOp::Neg, x) = a_simp.kind() {
2515                    return Expr::binary_op(BinOp::Sub(s.clone()), b_simp, x.clone()).at_opt(span);
2516                }
2517                if let ExprKind::UnaryOp(UnOp::Neg, y) = b_simp.kind() {
2518                    return Expr::binary_op(BinOp::Sub(s.clone()), a_simp, y.clone()).at_opt(span);
2519                }
2520
2521                Expr::binary_op(BinOp::Add(s.clone()), a_simp, b_simp).at_opt(span)
2522            }
2523            ExprKind::BinaryOp(op, a, b) => {
2524                Expr::binary_op(op.clone(), a.simplify_arithmetic(), b.simplify_arithmetic())
2525                    .at_opt(span)
2526            }
2527            ExprKind::UnaryOp(op, a) => Expr::unary_op(*op, a.simplify_arithmetic()).at_opt(span),
2528            ExprKind::IfThenElse(p, e1, e2) => {
2529                Expr::ite(
2530                    p.simplify_arithmetic(),
2531                    e1.simplify_arithmetic(),
2532                    e2.simplify_arithmetic(),
2533                )
2534                .at_opt(span)
2535            }
2536            _ => self.clone(),
2537        }
2538    }
2539
2540    /// Pass 3: Integer shifts and inequality rearrangement
2541    pub fn simplify_bounds(&self) -> Self {
2542        let span = self.span();
2543        match self.kind() {
2544            ExprKind::BinaryOp(op, a, b) => {
2545                let a_simp = a.simplify_bounds();
2546                let b_simp = b.simplify_bounds();
2547
2548                let is_minus_one = |e: &Expr| matches!(e.kind(), ExprKind::Constant(c) if *c == Constant::from(-1));
2549                let is_zero =
2550                    |e: &Expr| matches!(e.kind(), ExprKind::Constant(c) if *c == Constant::from(0));
2551
2552                // Important: this logic only applies if the operators are specifically typed for integers
2553                match op {
2554                    BinOp::Gt(Sort::Int) => {
2555                        // X > -1  ==>  X >= 0
2556                        if is_minus_one(&b_simp) {
2557                            return Expr::binary_op(BinOp::Ge(Sort::Int), a_simp, Expr::zero())
2558                                .at_opt(span)
2559                                .simplify_bounds();
2560                        }
2561                        // X - Y > 0  ==>  X > Y
2562                        if is_zero(&b_simp)
2563                            && let ExprKind::BinaryOp(BinOp::Sub(s_sub), x, y) = a_simp.kind()
2564                        {
2565                            return Expr::binary_op(BinOp::Gt(s_sub.clone()), x.clone(), y.clone())
2566                                .at_opt(span);
2567                        }
2568                    }
2569                    BinOp::Ge(Sort::Int) => {
2570                        // X - Y >= 0  ==>  X >= Y
2571                        if is_zero(&b_simp)
2572                            && let ExprKind::BinaryOp(BinOp::Sub(s_sub), x, y) = a_simp.kind()
2573                        {
2574                            return Expr::binary_op(BinOp::Ge(s_sub.clone()), x.clone(), y.clone())
2575                                .at_opt(span);
2576                        }
2577                    }
2578                    BinOp::Lt(Sort::Int) => {
2579                        // X - Y < 0  ==>  X < Y
2580                        if is_zero(&b_simp)
2581                            && let ExprKind::BinaryOp(BinOp::Sub(s_sub), x, y) = a_simp.kind()
2582                        {
2583                            return Expr::binary_op(BinOp::Lt(s_sub.clone()), x.clone(), y.clone())
2584                                .at_opt(span);
2585                        }
2586                    }
2587                    BinOp::Le(Sort::Int) => {
2588                        // X <= -1  ==>  X < 0
2589                        if is_minus_one(&b_simp) {
2590                            return Expr::binary_op(BinOp::Lt(Sort::Int), a_simp, Expr::zero())
2591                                .at_opt(span)
2592                                .simplify_bounds();
2593                        }
2594                        // X - Y <= 0  ==>  X <= Y
2595                        if is_zero(&b_simp)
2596                            && let ExprKind::BinaryOp(BinOp::Sub(s_sub), x, y) = a_simp.kind()
2597                        {
2598                            return Expr::binary_op(BinOp::Le(s_sub.clone()), x.clone(), y.clone())
2599                                .at_opt(span);
2600                        }
2601                    }
2602                    _ => {}
2603                }
2604
2605                Expr::binary_op(op.clone(), a_simp, b_simp).at_opt(span)
2606            }
2607            ExprKind::UnaryOp(op, a) => Expr::unary_op(*op, a.simplify_bounds()).at_opt(span),
2608            ExprKind::IfThenElse(p, e1, e2) => {
2609                Expr::ite(p.simplify_bounds(), e1.simplify_bounds(), e2.simplify_bounds())
2610                    .at_opt(span)
2611            }
2612            _ => self.clone(),
2613        }
2614    }
2615}