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::Alias(_) => bug!("unexpected `ConstKind::Alias`"),
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    /// Reduce a pointer to its address, for relational operators
617    pub fn reduce_ptr_addr(&self) -> Expr {
618        self.proj_and_reduce(FieldProj::RawPtr { field: RawPtrField::Addr })
619    }
620
621    pub fn visit_conj<'a>(&'a self, mut f: impl FnMut(&'a Expr)) {
622        fn go<'a>(e: &'a Expr, f: &mut impl FnMut(&'a Expr)) {
623            if let ExprKind::BinaryOp(BinOp::And, e1, e2) = e.kind() {
624                go(e1, f);
625                go(e2, f);
626            } else {
627                f(e);
628            }
629        }
630        go(self, &mut f);
631    }
632
633    pub fn flatten_conjs(&self) -> Vec<&Expr> {
634        let mut vec = vec![];
635        self.visit_conj(|e| vec.push(e));
636        vec
637    }
638
639    pub fn has_evars(&self) -> bool {
640        struct HasEvars;
641
642        impl TypeVisitor for HasEvars {
643            type BreakTy = ();
644            fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<Self::BreakTy> {
645                if let ExprKind::Var(Var::EVar(_)) = expr.kind() {
646                    ControlFlow::Break(())
647                } else {
648                    expr.super_visit_with(self)
649                }
650            }
651        }
652
653        self.visit_with(&mut HasEvars).is_break()
654    }
655
656    pub fn erase_spans(&self) -> Expr {
657        struct SpanEraser;
658        impl TypeFolder for SpanEraser {
659            fn fold_expr(&mut self, e: &Expr) -> Expr {
660                e.super_fold_with(self).at_opt(None)
661            }
662        }
663        self.fold_with(&mut SpanEraser)
664    }
665
666    /// Replaces [`BoundReftKind::Named(..)`] in [`Var::Bound(..)`] with
667    /// [`BoundReftKind::Anon`]. This is to ensure that expr equality works
668    /// properly --- the names are just annotations that do not change the
669    /// expressions themselves.
670    ///
671    /// If you think you need this function, you may be looking for
672    /// [`Expr::erase_metadata()`].
673    pub fn erase_bound_reft_kind(&self) -> Expr {
674        struct BoundReftKindEraser;
675        impl TypeFolder for BoundReftKindEraser {
676            fn fold_expr(&mut self, expr: &Expr) -> Expr {
677                if let ExprKind::Var(Var::Bound(
678                    db_index,
679                    BoundReft { var, kind: BoundReftKind::Named(_) },
680                )) = expr.kind()
681                {
682                    Expr::bvar(*db_index, *var, BoundReftKind::Anon)
683                } else {
684                    expr.super_fold_with(self)
685                }
686            }
687        }
688        self.fold_with(&mut BoundReftKindEraser)
689    }
690
691    pub fn erase_metadata(&self) -> Expr {
692        self.erase_spans().erase_bound_reft_kind()
693    }
694
695    /// Binary relations are lifted to tuples and ADTs, so e.g.
696    ///
697    ///     (1, false) > (0, true)
698    ///
699    /// is treated as
700    ///
701    ///     1 > 0 && false > true
702    ///
703    /// This "expands" the binary relations on tuples and ADTs to the second
704    /// form where their elements are individually compared.
705    ///
706    /// Only some binary relations are lifted in this manner, see
707    /// `bin_op_to_fixpoint` in `fixpoint_encoding.rs`.
708    pub fn expand_bin_rels(&self) -> Expr {
709        struct BinRelExpander;
710        impl BinRelExpander {
711            fn expand_bin_rel(&mut self, sort: &Sort, rel: &BinOp, e1: &Expr, e2: &Expr) -> Expr {
712                match sort {
713                    Sort::Tuple(sorts) => {
714                        let arity = sorts.len();
715                        self.apply_bin_rel_rec(sorts, rel, e1, e2, |field| {
716                            FieldProj::Tuple { arity, field }
717                        })
718                    }
719                    Sort::App(SortCtor::Adt(sort_def), args)
720                        if let Some(variant) = sort_def.opt_struct_variant() =>
721                    {
722                        let def_id = sort_def.did();
723                        let sorts = variant.field_sorts(args);
724                        self.apply_bin_rel_rec(&sorts, rel, e1, e2, |field| {
725                            FieldProj::Adt { def_id, field }
726                        })
727                    }
728                    _ => {
729                        Expr::binary_op(
730                            rel.clone(),
731                            e1.super_fold_with(self),
732                            e2.super_fold_with(self),
733                        )
734                    }
735                }
736            }
737            /// Apply binary relation recursively over aggregate expressions
738            fn apply_bin_rel_rec(
739                &mut self,
740                sorts: &[Sort],
741                rel: &BinOp,
742                e1: &Expr,
743                e2: &Expr,
744                mk_proj: impl Fn(u32) -> FieldProj,
745            ) -> Expr {
746                Expr::and_from_iter(sorts.iter().enumerate().map(|(idx, s)| {
747                    let proj = mk_proj(idx as u32);
748                    let e1 = e1.proj_and_reduce(proj);
749                    let e2 = e2.proj_and_reduce(proj);
750                    self.expand_bin_rel(s, rel, &e1, &e2)
751                }))
752            }
753        }
754        impl TypeFolder for BinRelExpander {
755            fn fold_expr(&mut self, expr: &Expr) -> Expr {
756                if let ExprKind::BinaryOp(
757                    rel @ (BinOp::Le(sort) | BinOp::Lt(sort) | BinOp::Ge(sort) | BinOp::Gt(sort)),
758                    e1,
759                    e2,
760                ) = expr.kind()
761                {
762                    self.expand_bin_rel(sort, rel, e1, e2)
763                } else {
764                    expr.super_fold_with(self)
765                }
766            }
767        }
768
769        let mut expander = BinRelExpander {};
770        self.fold_with(&mut expander)
771    }
772
773    /// This is really dumb but we sometimes have the following:
774    ///     Vec { k.0 }
775    /// And we want to really have it rendered as
776    ///     k
777    /// So what we do is look for Tuple or Ctor exprs whose subfields are all
778    /// projections of the same expr, and reduce them if so. Note that we
779    /// **also** require that these are in the same order.
780    pub fn eta_reduce_projs(&self) -> Self {
781        struct EtaReducer;
782        impl TypeFolder for EtaReducer {
783            fn fold_expr(&mut self, expr: &Expr) -> Expr {
784                match expr.kind() {
785                    ExprKind::Tuple(subexprs) => {
786                        let new_subexprs = subexprs
787                            .iter()
788                            .map(|subexpr| subexpr.fold_with(self))
789                            .collect_vec();
790                        if let Some(ExprKind::FieldProj(
791                            e_inner,
792                            FieldProj::Tuple { arity: _, field: 0 },
793                        )) = subexprs.first().map(|e| e.kind())
794                            && new_subexprs[1..].iter().zip(1..).all(|(other_e, i)| {
795                                if let ExprKind::FieldProj(
796                                    other_e_inner,
797                                    FieldProj::Tuple { arity: _, field },
798                                ) = other_e.kind()
799                                {
800                                    field == &i && &e_inner.erase_metadata() == other_e_inner
801                                } else {
802                                    false
803                                }
804                            })
805                        {
806                            e_inner.clone()
807                        } else {
808                            expr.clone()
809                        }
810                    }
811                    ExprKind::Ctor(_ctor, subexprs) => {
812                        let new_subexprs = subexprs
813                            .iter()
814                            .map(|subexpr| subexpr.fold_with(self))
815                            .collect_vec();
816                        if let Some(ExprKind::FieldProj(
817                            e_inner,
818                            FieldProj::Adt { def_id: _, field: 0 },
819                        )) = subexprs.first().map(|e| e.kind())
820                            && new_subexprs[1..].iter().zip(1..).all(|(other_e, i)| {
821                                if let ExprKind::FieldProj(
822                                    other_e_inner,
823                                    FieldProj::Adt { def_id: _, field },
824                                ) = other_e.kind()
825                                {
826                                    field == &i && &e_inner.erase_metadata() == other_e_inner
827                                } else {
828                                    false
829                                }
830                            })
831                        {
832                            e_inner.clone()
833                        } else {
834                            expr.clone()
835                        }
836                    }
837                    _ => expr.super_fold_with(self),
838                }
839            }
840        }
841        self.fold_with(&mut EtaReducer)
842    }
843}
844
845#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
846pub struct ESpan {
847    /// The top-level span information
848    pub span: Span,
849    /// The span for the (base) call-site for def-expanded spans
850    pub base: Option<Span>,
851}
852
853impl ESpan {
854    pub fn new(span: Span) -> Self {
855        Self { span, base: None }
856    }
857
858    pub fn with_base(&self, espan: ESpan) -> Self {
859        Self { span: self.span, base: Some(espan.span) }
860    }
861}
862
863#[derive(
864    Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, TypeFoldable, TypeVisitable,
865)]
866pub enum BinOp {
867    Iff,
868    Imp,
869    Or,
870    And,
871    Eq,
872    Ne,
873    Gt(Sort),
874    Ge(Sort),
875    Lt(Sort),
876    Le(Sort),
877    Add(Sort),
878    Sub(Sort),
879    Mul(Sort),
880    Div(Sort),
881    Mod(Sort),
882    BitAnd(Sort),
883    BitOr(Sort),
884    BitXor(Sort),
885    BitShl(Sort),
886    BitShr(Sort),
887}
888
889#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Debug, Decodable)]
890pub enum UnOp {
891    Not,
892    Neg,
893}
894
895#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, TyEncodable, TyDecodable)]
896pub enum Ctor {
897    /// for indices represented as `struct` in the refinement logic (e.g. using `refined_by` annotations)
898    Struct(DefId),
899    /// for indices represented as  `enum` in the refinement logic (e.g. using `reflected` annotations)
900    Enum(DefId, VariantIdx),
901    /// for the builtin indices of raw pointers
902    RawPtr,
903}
904
905impl Ctor {
906    fn is_enum(&self) -> bool {
907        matches!(self, Self::Enum(..))
908    }
909
910    pub fn def_id_and_variant(&self) -> Option<(DefId, VariantIdx)> {
911        match self {
912            Self::Struct(def_id) => Some((*def_id, FIRST_VARIANT)),
913            Self::Enum(def_id, variant_idx) => Some((*def_id, *variant_idx)),
914            Self::RawPtr => None,
915        }
916    }
917}
918
919/// # Primitive Properties
920/// Given a primop `op` with signature `(t1,...,tn) -> t`
921/// We define a refined type for `op` expressed as a `RuleMatcher`
922///
923/// ```text
924/// op :: (x1: t1, ..., xn: tn) -> { t[op_val[op](x1,...,xn)] | op_rel[x1,...,xn] }
925/// ```
926/// That is, using two *uninterpreted functions* `op_val` and `op_rel` that respectively denote
927/// 1. The _value_ of the primop, and
928/// 2. Some invariant _relation_ that holds for the primop.
929///
930/// The latter can be extended by the user via a `property` definition, which allows us
931/// to customize primops like `<<` with extra "facts" or lemmas. See `tests/tests/pos/surface/primops00.rs` for an example.
932#[derive(Debug, Clone, TyEncodable, TyDecodable, PartialEq, Eq, Hash)]
933pub enum InternalFuncKind {
934    /// UIF representing the value of a primop
935    Val(BinOp),
936    /// UIF representing the relationship of a primop
937    Rel(BinOp),
938    // Conversions betweeen Sorts
939    Cast,
940}
941
942#[derive(Debug, Clone, TyEncodable, TyDecodable, PartialEq, Eq, Hash)]
943pub enum SpecFuncKind {
944    /// Theory symbols *interpreted* by the SMT solver
945    Thy(liquid_fixpoint::ThyFunc),
946    /// User-defined function. This can be either a function with a body or a UIF.
947    Def(FluxDefId),
948}
949
950#[derive(
951    Clone, PartialEq, Eq, Hash, TyEncodable, Debug, TyDecodable, TypeVisitable, TypeFoldable,
952)]
953pub enum QuantDom {
954    Bounded { start: usize, end: usize },
955    Unbounded,
956}
957
958#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, Debug, TyDecodable)]
959pub enum ExprKind {
960    Var(Var),
961    Local(Local),
962    Constant(Constant),
963    /// A rust constant. This can be either `DefKind::Const` or `DefKind::AssocConst`
964    ConstDefId(DefId),
965    BinaryOp(BinOp, Expr, Expr),
966    GlobalFunc(SpecFuncKind),
967    InternalFunc(InternalFuncKind),
968    UnaryOp(UnOp, Expr),
969    FieldProj(Expr, FieldProj),
970    /// A variant used in the logic to represent a variant of an ADT as a pair of the `DefId` and variant-index
971    Ctor(Ctor, List<Expr>),
972    Tuple(List<Expr>),
973    PathProj(Expr, FieldIdx),
974    IfThenElse(Expr, Expr, Expr),
975    KVar(KVar),
976    WKVar(WKVar),
977    Alias(AliasReft, List<Expr>),
978    Let(Expr, Binder<Expr>),
979    /// Function application. The syntax allows arbitrary expressions in function position, but in
980    /// practice we are restricted by what's possible to encode in fixpoint. In a nutshell, we need
981    /// to make sure that expressions that can't be encoded are eliminated before we generate the
982    /// fixpoint constraint. Most notably, lambda abstractions have to be fully applied before
983    /// encoding into fixpoint (except when they appear as an index at the top-level).
984    App(Expr, List<SortArg>, List<Expr>),
985    /// Lambda abstractions. They are purely syntactic and we don't encode them in the logic. As such,
986    /// they have some syntactic restrictions that we must carefully maintain:
987    ///
988    /// 1. They can appear as an index at the top level.
989    /// 2. We can only substitute an abstraction for a variable in function position (or as an index).
990    ///    More generally, we need to partially evaluate expressions such that all abstractions in
991    ///    non-index position are eliminated before encoding into fixpoint. Right now, the
992    ///    implementation only evaluates abstractions that are immediately applied to arguments,
993    ///    thus the restriction.
994    Abs(Lambda),
995
996    /// Bounded quantifiers `exists i in 0..4 { pred(i) }` and `forall i in 0..4 { pred(i) }`.
997    Quant(fhir::QuantKind, QuantDom, Binder<Expr>),
998    /// A hole is an expression that must be inferred either *semantically* by generating a kvar or
999    /// *syntactically* by generating an evar. Whether a hole can be inferred semantically or
1000    /// syntactically depends on the position it appears: only holes appearing in predicate position
1001    /// can be inferred with a kvar (provided it satisfies the fixpoint horn constraints) and only
1002    /// holes used as an index (a position that fully determines their value) can be inferred with
1003    /// an evar.
1004    ///
1005    /// Holes are implicitly defined in a scope, i.e., their solution could mention free and bound
1006    /// variables in this scope. This must be considered when generating an inference variables for
1007    /// them (either evar or kvar). In fact, the main reason we have holes is that we want to
1008    /// decouple the places where we generate holes (where we don't want to worry about the scope),
1009    /// and the places where we generate inference variable for them (where we do need to worry
1010    /// about the scope).
1011    Hole(HoleKind),
1012    /// Is the expression constructed from constructor of the given DefId (which should be `reflected` Enum)
1013    IsCtor(DefId, VariantIdx, Expr),
1014}
1015
1016impl ExprKind {
1017    fn intern(self) -> Expr {
1018        Expr { kind: Interned::new(self), espan: None }
1019    }
1020}
1021
1022#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
1023pub enum AggregateKind {
1024    Tuple(usize),
1025    Adt(DefId),
1026}
1027
1028#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
1029pub enum FieldProj {
1030    Tuple { arity: usize, field: u32 },
1031    Adt { def_id: DefId, field: u32 },
1032    RawPtr { field: RawPtrField },
1033}
1034
1035#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug)]
1036pub enum RawPtrField {
1037    Base,
1038    Addr,
1039    Size,
1040}
1041
1042struct RawPtrFieldData {
1043    field: RawPtrField,
1044    index: u32,
1045    name: &'static str,
1046    sort: Sort,
1047}
1048
1049impl FieldProj {
1050    pub fn arity(&self, genv: GlobalEnv) -> QueryResult<usize> {
1051        match self {
1052            FieldProj::Tuple { arity, .. } => Ok(*arity),
1053            FieldProj::Adt { def_id, .. } => {
1054                Ok(genv.adt_sort_def_of(*def_id)?.struct_variant().fields())
1055            }
1056            FieldProj::RawPtr { .. } => Ok(RawPtrField::arity()),
1057        }
1058    }
1059
1060    pub fn field_idx(&self) -> u32 {
1061        match self {
1062            FieldProj::Tuple { field, .. } | FieldProj::Adt { field, .. } => *field,
1063            FieldProj::RawPtr { field } => field.index(),
1064        }
1065    }
1066}
1067
1068impl RawPtrField {
1069    const DATA: [RawPtrFieldData; 3] = [
1070        RawPtrFieldData { field: Self::Base, index: 0, name: "base", sort: Sort::Int },
1071        RawPtrFieldData { field: Self::Addr, index: 1, name: "addr", sort: Sort::Int },
1072        RawPtrFieldData { field: Self::Size, index: 2, name: "size", sort: Sort::Int },
1073    ];
1074
1075    pub fn iter() -> impl ExactSizeIterator<Item = Self> {
1076        Self::DATA.iter().map(|data| data.field)
1077    }
1078
1079    pub fn arity() -> usize {
1080        Self::DATA.len()
1081    }
1082
1083    fn data(self) -> &'static RawPtrFieldData {
1084        Self::DATA.iter().find(|data| data.field == self).unwrap()
1085    }
1086
1087    pub fn from_name(name: Symbol) -> Option<Self> {
1088        Self::DATA
1089            .iter()
1090            .find(|data| data.name == name.as_str())
1091            .map(|data| data.field)
1092    }
1093
1094    pub fn from_index(index: u32) -> Option<Self> {
1095        Self::DATA
1096            .iter()
1097            .find(|data| data.index == index)
1098            .map(|data| data.field)
1099    }
1100
1101    pub fn sort(self) -> Sort {
1102        self.data().sort.clone()
1103    }
1104
1105    pub fn index(self) -> u32 {
1106        self.data().index
1107    }
1108
1109    pub fn name(self) -> &'static str {
1110        self.data().name
1111    }
1112
1113    pub fn symbol(self) -> Symbol {
1114        match self {
1115            Self::Base => sym::base,
1116            Self::Addr => sym::addr,
1117            Self::Size => rustc_span::sym::size,
1118        }
1119    }
1120}
1121
1122/// The position where a [hole] appears. This determines how it will be inferred. This is related
1123/// to, but not the same as, an [`InferMode`].
1124///
1125/// [`InferMode`]: super::InferMode
1126/// [hole]: ExprKind::Hole
1127#[derive(
1128    Debug, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable,
1129)]
1130pub enum HoleKind {
1131    /// A hole in predicate position (e.g., the predicate in a [`TyKind::Constr`]). It will be
1132    /// inferred by generating a kvar.
1133    ///
1134    /// [`TyKind::Constr`]: super::TyKind::Constr
1135    Pred,
1136    /// A hole used as a refinement argument or index. It will be inferred by generating an evar.
1137    /// The expression filling the hole must have the provided sort.
1138    ///
1139    /// NOTE(nilehmann) we used to require the `Sort` for generating the evar because we needed it
1140    /// to eta-expand aggregate sorts. We've since removed this behavior but I'm keeping it here
1141    /// just in case. We could remove in case it becomes too problematic.
1142    Expr(Sort),
1143}
1144
1145/// In theory a kvar is just an unknown predicate that can use some variables in scope. In practice,
1146/// fixpoint makes a difference between the first and the rest of the arguments, the first one being
1147/// the kvar's *self argument*. Fixpoint will only instantiate qualifiers that use the self argument.
1148/// Flux generalizes the self argument to be a list. We call the rest of the arguments the *scope*.
1149#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
1150pub struct KVar {
1151    pub kvid: KVid,
1152    /// The number of arguments consider to be *self arguments*.
1153    pub self_args: usize,
1154    /// The list of *all* arguments with the self arguments at the beginning, i.e., the
1155    /// list of self arguments followed by the scope.
1156    pub args: List<Expr>,
1157}
1158
1159#[derive(
1160    Debug, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable,
1161)]
1162pub struct WKVid {
1163    /// Weak KVars right now are always associated with a function definition.
1164    /// Weak KVars can in theory be put wherever we can validly add a refinement
1165    pub parent_fn: DefId,
1166    /// There's no reason why this has to be KVid, and in principle it may be
1167    /// better served as just a number or a new index unto itself.
1168    pub id: KVid,
1169}
1170
1171impl WKVid {
1172    pub fn new(parent_fn: DefId, id: KVid) -> Self {
1173        Self { parent_fn, id }
1174    }
1175}
1176
1177/// A weak kvar is like a kvar with the exception that it infers the weakest
1178/// condition necessary instead of the strongest condition. Due to the way we
1179/// generate these kvars (on fn_sigs, rather than during constraint generation),
1180/// they also in theory are global.
1181#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
1182pub struct WKVar {
1183    pub wkvid: WKVid,
1184    /// Analagous to KVar self arguments except we require that instantiations
1185    /// use *at least one* of the self_args (unless there are none, in which
1186    /// case there is no such requirement).
1187    ///
1188    /// This is mostly relevant for weak kvars corresponding to function
1189    /// *outputs*. Consider the signature
1190    ///
1191    ///     foo: fn(x: usize) -> bool{b: $wk1(b, x)}
1192    ///            requires $wk0(x)
1193    ///
1194    /// In this case self_args would be 1 (corresponding to the bool `b`).
1195    /// Consider now the snippet
1196    ///
1197    ///     let b = foo(x);
1198    ///     assert(x > 0);
1199    ///
1200    /// Suppose the assertion fails. Without the self_args requirement, we
1201    /// could validly instantiate
1202    ///
1203    ///     $wk1(b, x) := x > 0
1204    ///
1205    /// But this is somewhat nonsensical because only in exceptional cases
1206    /// does it make sense for `foo` to somehow "add" information to its
1207    /// **argument** (`x`).
1208    ///
1209    /// By checking for the presence of **at least one** self arg, we
1210    /// ensure that the self argument is "used"; fixpoint does a similar check.
1211    ///
1212    /// This is a syntactic underapproximation and doesn't preclude things like
1213    ///
1214    ///     $wk1(b, x) := (b => true) && (x > 0)
1215    ///
1216    /// But we could conceive of a more sophisticated check using the self_args.
1217    pub self_args: usize,
1218    /// All arguments with self arguments at the beginning.
1219    pub args: List<Expr>,
1220}
1221
1222#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, Decodable)]
1223pub struct EarlyReftParam {
1224    pub index: u32,
1225    pub name: Symbol,
1226}
1227
1228#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, Decodable, Debug)]
1229pub struct BoundReft {
1230    pub var: BoundVar,
1231    pub kind: BoundReftKind,
1232}
1233
1234#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
1235pub enum Var {
1236    Free(Name),
1237    Bound(DebruijnIndex, BoundReft),
1238    EarlyParam(EarlyReftParam),
1239    EVar(EVid),
1240    ConstGeneric(ParamConst),
1241}
1242
1243#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
1244pub struct Path {
1245    pub loc: Loc,
1246    projection: List<FieldIdx>,
1247}
1248
1249#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)]
1250pub enum Loc {
1251    Local(Local),
1252    Var(Var),
1253}
1254
1255newtype_index! {
1256    /// *E*xistential *v*ariable *id*
1257    #[debug_format = "?{}e"]
1258    #[orderable]
1259    #[encodable]
1260    pub struct EVid {}
1261}
1262
1263newtype_index! {
1264    #[debug_format = "$k{}"]
1265    #[encodable]
1266    pub struct KVid {}
1267}
1268
1269newtype_index! {
1270    #[debug_format = "a{}"]
1271    #[orderable]
1272    #[encodable]
1273    pub struct Name {}
1274}
1275
1276#[derive(Copy, Debug, Clone)]
1277pub enum NameProvenance {
1278    Unknown,
1279    UnfoldBoundReft(BoundReftKind),
1280}
1281
1282impl NameProvenance {
1283    pub fn opt_symbol(&self) -> Option<Symbol> {
1284        match &self {
1285            NameProvenance::UnfoldBoundReft(BoundReftKind::Named(name)) => Some(*name),
1286            _ => None,
1287        }
1288    }
1289}
1290
1291#[derive(PartialEq, Eq, Hash, Debug)]
1292pub enum PrettyVar<V> {
1293    Local(V),
1294    Param(EarlyReftParam),
1295}
1296
1297impl<V: Copy + Into<usize>> PrettyVar<V> {
1298    pub fn as_subscript(&self) -> String {
1299        let idx = match self {
1300            PrettyVar::Local(v) => (*v).into(),
1301            PrettyVar::Param(p) => p.index as usize,
1302        };
1303        as_subscript(idx)
1304    }
1305}
1306
1307pub struct PrettyMap<V: Eq + Hash> {
1308    map: UnordMap<PrettyVar<V>, String>,
1309    count: UnordMap<Symbol, usize>,
1310}
1311
1312impl<V: Eq + Hash + Copy + Into<usize>> PrettyMap<V> {
1313    pub fn new() -> Self {
1314        PrettyMap { map: UnordMap::default(), count: UnordMap::default() }
1315    }
1316
1317    pub fn set(&mut self, var: PrettyVar<V>, prefix: Option<Symbol>) -> String {
1318        // if already defined, return it
1319        if let Some(symbol) = self.map.get(&var) {
1320            return symbol.clone();
1321        }
1322        // else define it, and stash
1323        let symbol = if let Some(prefix) = prefix {
1324            let index = self.count.entry(prefix).or_insert(0);
1325            let symbol = format!("{}{}", prefix, as_subscript(*index));
1326            *index += 1;
1327            symbol
1328        } else {
1329            format!("a'{}", var.as_subscript())
1330        };
1331        self.map.insert(var, symbol.clone());
1332        symbol
1333    }
1334
1335    pub fn get(&self, key: &PrettyVar<V>) -> String {
1336        match self.map.get(key) {
1337            Some(s) => s.clone(),
1338            None => format!("a'{}", key.as_subscript()),
1339        }
1340    }
1341}
1342
1343impl KVar {
1344    pub fn new(kvid: KVid, self_args: usize, args: Vec<Expr>) -> Self {
1345        KVar { kvid, self_args, args: List::from_vec(args) }
1346    }
1347
1348    fn self_args(&self) -> &[Expr] {
1349        &self.args[..self.self_args]
1350    }
1351
1352    fn scope(&self) -> &[Expr] {
1353        &self.args[self.self_args..]
1354    }
1355}
1356
1357impl WKVar {
1358    fn self_args(&self) -> &[Expr] {
1359        &self.args[..self.self_args]
1360    }
1361
1362    fn scope(&self) -> &[Expr] {
1363        &self.args[self.self_args..]
1364    }
1365}
1366
1367impl Var {
1368    pub fn to_expr(&self) -> Expr {
1369        Expr::var(*self)
1370    }
1371
1372    pub fn shift_in(&self, amount: u32) -> Self {
1373        match self {
1374            Var::Bound(idx, breft) => Var::Bound(idx.shifted_in(amount), *breft),
1375            _ => *self,
1376        }
1377    }
1378
1379    pub fn shift_out(&self, amount: u32) -> Self {
1380        match self {
1381            Var::Bound(idx, breft) => Var::Bound(idx.shifted_out(amount), *breft),
1382            _ => *self,
1383        }
1384    }
1385}
1386
1387impl Path {
1388    pub fn new(loc: Loc, projection: impl Into<List<FieldIdx>>) -> Path {
1389        Path { loc, projection: projection.into() }
1390    }
1391
1392    pub fn projection(&self) -> &[FieldIdx] {
1393        &self.projection[..]
1394    }
1395
1396    pub fn to_expr(&self) -> Expr {
1397        self.projection
1398            .iter()
1399            .fold(self.loc.to_expr(), |e, f| Expr::path_proj(e, *f))
1400    }
1401
1402    pub fn to_loc(&self) -> Option<Loc> {
1403        if self.projection.is_empty() { Some(self.loc) } else { None }
1404    }
1405}
1406
1407impl Loc {
1408    pub fn to_expr(&self) -> Expr {
1409        match self {
1410            Loc::Local(local) => Expr::local(*local),
1411            Loc::Var(var) => Expr::var(*var),
1412        }
1413    }
1414}
1415
1416macro_rules! impl_ops {
1417    ($($op:ident: $method:ident),*) => {$(
1418        impl<Rhs> std::ops::$op<Rhs> for Expr
1419        where
1420            Rhs: Into<Expr>,
1421        {
1422            type Output = Expr;
1423
1424            fn $method(self, rhs: Rhs) -> Self::Output {
1425                let sort = crate::rty::Sort::Int;
1426                Expr::binary_op(BinOp::$op(sort), self, rhs)
1427            }
1428        }
1429
1430        impl<Rhs> std::ops::$op<Rhs> for &Expr
1431        where
1432            Rhs: Into<Expr>,
1433        {
1434            type Output = Expr;
1435
1436            fn $method(self, rhs: Rhs) -> Self::Output {
1437                let sort = crate::rty::Sort::Int;
1438                Expr::binary_op(BinOp::$op(sort), self, rhs)
1439            }
1440        }
1441    )*};
1442}
1443impl_ops!(Add: add, Sub: sub, Mul: mul, Div: div);
1444
1445impl From<i32> for Expr {
1446    fn from(value: i32) -> Self {
1447        Expr::constant(Constant::from(value))
1448    }
1449}
1450
1451impl From<&Expr> for Expr {
1452    fn from(e: &Expr) -> Self {
1453        e.clone()
1454    }
1455}
1456
1457impl From<Path> for Expr {
1458    fn from(path: Path) -> Self {
1459        path.to_expr()
1460    }
1461}
1462
1463impl From<Name> for Expr {
1464    fn from(name: Name) -> Self {
1465        Expr::fvar(name)
1466    }
1467}
1468
1469impl From<Var> for Expr {
1470    fn from(var: Var) -> Self {
1471        Expr::var(var)
1472    }
1473}
1474
1475impl From<SpecFuncKind> for Expr {
1476    fn from(kind: SpecFuncKind) -> Self {
1477        Expr::global_func(kind)
1478    }
1479}
1480
1481impl From<InternalFuncKind> for Expr {
1482    fn from(kind: InternalFuncKind) -> Self {
1483        Expr::internal_func(kind)
1484    }
1485}
1486
1487impl From<Loc> for Path {
1488    fn from(loc: Loc) -> Self {
1489        Path::new(loc, vec![])
1490    }
1491}
1492
1493impl From<Name> for Loc {
1494    fn from(name: Name) -> Self {
1495        Loc::Var(Var::Free(name))
1496    }
1497}
1498
1499impl From<Local> for Loc {
1500    fn from(local: Local) -> Self {
1501        Loc::Local(local)
1502    }
1503}
1504
1505#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable)]
1506pub struct Real(pub Symbol);
1507
1508#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable)]
1509pub enum Constant {
1510    Int(BigInt),
1511    Real(Real),
1512    Bool(bool),
1513    Str(Symbol),
1514    Char(char),
1515    BitVec(u128, u32),
1516}
1517
1518impl Constant {
1519    pub const ZERO: Constant = Constant::Int(BigInt::ZERO);
1520    pub const ONE: Constant = Constant::Int(BigInt::ONE);
1521    pub const TRUE: Constant = Constant::Bool(true);
1522
1523    fn to_bool(self) -> Option<bool> {
1524        match self {
1525            Constant::Bool(b) => Some(b),
1526            _ => None,
1527        }
1528    }
1529
1530    fn to_int(self) -> Option<BigInt> {
1531        match self {
1532            Constant::Int(n) => Some(n),
1533            _ => None,
1534        }
1535    }
1536
1537    pub fn iff(&self, other: &Constant) -> Option<Constant> {
1538        let b1 = self.to_bool()?;
1539        let b2 = other.to_bool()?;
1540        Some(Constant::Bool(b1 == b2))
1541    }
1542
1543    pub fn imp(&self, other: &Constant) -> Option<Constant> {
1544        let b1 = self.to_bool()?;
1545        let b2 = other.to_bool()?;
1546        Some(Constant::Bool(!b1 || b2))
1547    }
1548
1549    pub fn or(&self, other: &Constant) -> Option<Constant> {
1550        let b1 = self.to_bool()?;
1551        let b2 = other.to_bool()?;
1552        Some(Constant::Bool(b1 || b2))
1553    }
1554
1555    pub fn and(&self, other: &Constant) -> Option<Constant> {
1556        let b1 = self.to_bool()?;
1557        let b2 = other.to_bool()?;
1558        Some(Constant::Bool(b1 && b2))
1559    }
1560
1561    pub fn eq(&self, other: &Constant) -> Constant {
1562        Constant::Bool(*self == *other)
1563    }
1564
1565    pub fn ne(&self, other: &Constant) -> Constant {
1566        Constant::Bool(*self != *other)
1567    }
1568
1569    pub fn gt(&self, other: &Constant) -> Option<Constant> {
1570        let n1 = self.to_int()?;
1571        let n2 = other.to_int()?;
1572        Some(Constant::Bool(n1 > n2))
1573    }
1574
1575    pub fn ge(&self, other: &Constant) -> Option<Constant> {
1576        let n1 = self.to_int()?;
1577        let n2 = other.to_int()?;
1578        Some(Constant::Bool(n1 >= n2))
1579    }
1580
1581    pub fn from_scalar_int<'tcx, T>(tcx: TyCtxt<'tcx>, scalar: ScalarInt, t: &T) -> Option<Self>
1582    where
1583        T: ToRustc<'tcx, T = rustc_middle::ty::Ty<'tcx>>,
1584    {
1585        use rustc_middle::ty::TyKind;
1586        let ty = t.to_rustc(tcx);
1587        match ty.kind() {
1588            TyKind::Int(int_ty) => Some(Constant::from(scalar_to_int(tcx, scalar, *int_ty))),
1589            TyKind::Uint(uint_ty) => Some(Constant::from(scalar_to_uint(tcx, scalar, *uint_ty))),
1590            TyKind::Bool => {
1591                let b = scalar_to_bits(tcx, scalar, ty);
1592                Some(Constant::Bool(b != 0))
1593            }
1594            TyKind::Char => {
1595                let b = scalar_to_bits(tcx, scalar, ty);
1596                Some(Constant::Char(char::from_u32(b as u32)?))
1597            }
1598            _ => bug!(),
1599        }
1600    }
1601
1602    /// See [`BigInt::int_min`]
1603    pub fn int_min(bit_width: u32) -> Constant {
1604        Constant::Int(BigInt::int_min(bit_width))
1605    }
1606
1607    /// See [`BigInt::int_max`]
1608    pub fn int_max(bit_width: u32) -> Constant {
1609        Constant::Int(BigInt::int_max(bit_width))
1610    }
1611
1612    /// See [`BigInt::uint_max`]
1613    pub fn uint_max(bit_width: u32) -> Constant {
1614        Constant::Int(BigInt::uint_max(bit_width))
1615    }
1616}
1617
1618impl From<i32> for Constant {
1619    fn from(c: i32) -> Self {
1620        Constant::Int(c.into())
1621    }
1622}
1623
1624impl From<usize> for Constant {
1625    fn from(u: usize) -> Self {
1626        Constant::Int(u.into())
1627    }
1628}
1629
1630impl From<u32> for Constant {
1631    fn from(c: u32) -> Self {
1632        Constant::Int(c.into())
1633    }
1634}
1635
1636impl From<u64> for Constant {
1637    fn from(c: u64) -> Self {
1638        Constant::Int(c.into())
1639    }
1640}
1641
1642impl From<u128> for Constant {
1643    fn from(c: u128) -> Self {
1644        Constant::Int(c.into())
1645    }
1646}
1647
1648impl From<i128> for Constant {
1649    fn from(c: i128) -> Self {
1650        Constant::Int(c.into())
1651    }
1652}
1653
1654impl From<bool> for Constant {
1655    fn from(b: bool) -> Self {
1656        Constant::Bool(b)
1657    }
1658}
1659
1660impl From<Symbol> for Constant {
1661    fn from(s: Symbol) -> Self {
1662        Constant::Str(s)
1663    }
1664}
1665
1666impl From<char> for Constant {
1667    fn from(c: char) -> Self {
1668        Constant::Char(c)
1669    }
1670}
1671
1672impl_internable!(ExprKind);
1673impl_slice_internable!(Expr, KVar);
1674
1675#[derive(Debug)]
1676pub struct FieldBind<T> {
1677    pub name: Symbol,
1678    pub value: T,
1679}
1680
1681impl<T: Pretty> Pretty for FieldBind<T> {
1682    fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1683        w!(cx, f, "{}: {:?}", ^self.name, &self.value)
1684    }
1685}
1686
1687pub(crate) mod pretty {
1688
1689    use flux_rustc_bridge::def_id_to_string;
1690
1691    use super::*;
1692    use crate::name_of_thy_func;
1693
1694    #[derive(PartialEq, Eq, PartialOrd, Ord)]
1695    enum Precedence {
1696        Iff,
1697        Imp,
1698        Or,
1699        And,
1700        Cmp,
1701        Bitvec,
1702        AddSub,
1703        MulDiv,
1704    }
1705
1706    impl BinOp {
1707        fn precedence(&self) -> Precedence {
1708            match self {
1709                BinOp::Iff => Precedence::Iff,
1710                BinOp::Imp => Precedence::Imp,
1711                BinOp::Or => Precedence::Or,
1712                BinOp::And => Precedence::And,
1713                BinOp::Eq
1714                | BinOp::Ne
1715                | BinOp::Gt(_)
1716                | BinOp::Lt(_)
1717                | BinOp::Ge(_)
1718                | BinOp::Le(_) => Precedence::Cmp,
1719                BinOp::Add(_) | BinOp::Sub(_) => Precedence::AddSub,
1720                BinOp::Mul(_) | BinOp::Div(_) | BinOp::Mod(_) => Precedence::MulDiv,
1721                BinOp::BitAnd(_)
1722                | BinOp::BitOr(_)
1723                | BinOp::BitShl(_)
1724                | BinOp::BitShr(_)
1725                | BinOp::BitXor(_) => Precedence::Bitvec,
1726            }
1727        }
1728    }
1729
1730    impl Precedence {
1731        pub fn is_associative(&self) -> bool {
1732            !matches!(self, Precedence::Imp | Precedence::Cmp)
1733        }
1734    }
1735
1736    pub fn should_parenthesize(op: &BinOp, child: &Expr) -> bool {
1737        if let ExprKind::BinaryOp(child_op, ..) = child.kind() {
1738            child_op.precedence() < op.precedence()
1739                || (child_op.precedence() == op.precedence() && !op.precedence().is_associative())
1740        } else {
1741            false
1742        }
1743    }
1744
1745    impl Pretty for Ctor {
1746        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1747            match self {
1748                Ctor::Struct(def_id) => {
1749                    w!(cx, f, "{:?}", def_id)
1750                }
1751                Ctor::Enum(def_id, variant_idx) => {
1752                    w!(cx, f, "{:?}::{:?}", def_id, ^variant_idx)
1753                }
1754                Ctor::RawPtr => w!(cx, f, "ptr"),
1755            }
1756        }
1757    }
1758
1759    impl Pretty for fhir::QuantKind {
1760        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1761            match self {
1762                fhir::QuantKind::Exists => w!(cx, f, "∃"),
1763                fhir::QuantKind::Forall => w!(cx, f, "∀"),
1764            }
1765        }
1766    }
1767
1768    impl Pretty for InternalFuncKind {
1769        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1770            match self {
1771                InternalFuncKind::Val(op) => w!(cx, f, "[{:?}]", op),
1772                InternalFuncKind::Rel(op) => w!(cx, f, "[{:?}]?", op),
1773                InternalFuncKind::Cast => w!(cx, f, "cast"),
1774            }
1775        }
1776    }
1777
1778    impl Pretty for QuantDom {
1779        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1780            match self {
1781                QuantDom::Bounded { start, end } => w!(cx, f, "in {} .. {}", ^start, ^end),
1782                QuantDom::Unbounded => Ok(()),
1783            }
1784        }
1785    }
1786
1787    impl Pretty for Expr {
1788        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1789            let e = if cx.simplify_exprs {
1790                self.simplify(&SnapshotMap::default())
1791            } else {
1792                self.clone()
1793            };
1794            match e.kind() {
1795                ExprKind::Var(var) => w!(cx, f, "{:?}", var),
1796                ExprKind::Local(local) => w!(cx, f, "{:?}", ^local),
1797                ExprKind::ConstDefId(did) => w!(cx, f, "{}", ^def_id_to_string(*did)),
1798                ExprKind::Constant(c) => w!(cx, f, "{:?}", c),
1799
1800                ExprKind::BinaryOp(op, e1, e2) => {
1801                    if should_parenthesize(op, e1) {
1802                        w!(cx, f, "({:?})", e1)?;
1803                    } else {
1804                        w!(cx, f, "{:?}", e1)?;
1805                    }
1806                    if matches!(op, BinOp::Div(_)) {
1807                        w!(cx, f, "{:?}", op)?;
1808                    } else {
1809                        w!(cx, f, " {:?} ", op)?;
1810                    }
1811                    if should_parenthesize(op, e2) {
1812                        w!(cx, f, "({:?})", e2)?;
1813                    } else {
1814                        w!(cx, f, "{:?}", e2)?;
1815                    }
1816                    Ok(())
1817                }
1818                ExprKind::UnaryOp(op, e) => {
1819                    if e.is_atom() {
1820                        w!(cx, f, "{:?}{:?}", op, e)
1821                    } else {
1822                        w!(cx, f, "{:?}({:?})", op, e)
1823                    }
1824                }
1825                ExprKind::FieldProj(e, proj) if let ExprKind::Ctor(_, _) = e.kind() => {
1826                    // special case to avoid printing `{n:12}.n` as `12.n` but instead, just print `12`
1827                    // TODO: maintain an invariant that `FieldProj` never has a Ctor as first argument (as always reduced)
1828                    w!(cx, f, "{:?}", e.proj_and_reduce(*proj))
1829                }
1830                ExprKind::FieldProj(e, proj) => {
1831                    if e.is_atom() {
1832                        w!(cx, f, "{:?}.{}", e, ^fmt_field_proj(cx, *proj))
1833                    } else {
1834                        w!(cx, f, "({:?}).{}", e, ^fmt_field_proj(cx, *proj))
1835                    }
1836                }
1837                ExprKind::Tuple(flds) => {
1838                    if let [e] = &flds[..] {
1839                        w!(cx, f, "({:?},)", e)
1840                    } else {
1841                        w!(cx, f, "({:?})", join!(", ", flds))
1842                    }
1843                }
1844                ExprKind::IsCtor(def_id, variant_idx, idx) => {
1845                    w!(cx, f, "({:?} is {:?}::{:?})", idx, def_id, ^variant_idx)
1846                }
1847                ExprKind::Ctor(ctor, flds) => {
1848                    if matches!(ctor, Ctor::RawPtr) {
1849                        let fields = iter::zip(RawPtrField::iter(), flds)
1850                            .map(|(field, value)| {
1851                                FieldBind { name: field.symbol(), value: value.clone() }
1852                            })
1853                            .collect_vec();
1854                        return w!(cx, f, "ptr {{ {:?} }}", join!(", ", fields));
1855                    }
1856                    let Some((def_id, variant_idx)) = ctor.def_id_and_variant() else {
1857                        unreachable!()
1858                    };
1859                    if let Some(adt_sort_def) = cx.adt_sort_def_of(def_id) {
1860                        let variant = adt_sort_def.variant(variant_idx).field_names();
1861                        let fields = iter::zip(variant, flds)
1862                            .map(|(name, value)| FieldBind { name: *name, value: value.clone() })
1863                            .collect_vec();
1864                        match ctor {
1865                            Ctor::Struct(_) => {
1866                                w!(cx, f, "{:?} {{ {:?} }}", def_id, join!(", ", fields))
1867                            }
1868                            Ctor::Enum(_, idx) => {
1869                                if fields.is_empty() {
1870                                    w!(cx, f, "{:?}::{:?}", def_id, ^idx.index())
1871                                } else {
1872                                    w!(cx, f, "{:?}::{:?}({:?})", def_id, ^idx.index(), join!(", ", fields))
1873                                }
1874                            }
1875                            Ctor::RawPtr => unreachable!(),
1876                        }
1877                    } else {
1878                        match ctor {
1879                            Ctor::Struct(_) => {
1880                                w!(cx, f, "{:?} {{ {:?} }}", def_id, join!(", ", flds))
1881                            }
1882                            Ctor::Enum(_, idx) => {
1883                                w!(cx, f, "{:?}::{:?} {{ {:?} }}", def_id, ^idx, join!(", ", flds))
1884                            }
1885                            Ctor::RawPtr => unreachable!(),
1886                        }
1887                    }
1888                }
1889                ExprKind::PathProj(e, field) => {
1890                    if e.is_atom() {
1891                        w!(cx, f, "{:?}.{:?}", e, field)
1892                    } else {
1893                        w!(cx, f, "({:?}).{:?}", e, field)
1894                    }
1895                }
1896                ExprKind::App(func, _, args) => {
1897                    w!(cx, f, "{:?}({})",
1898                        parens!(func, !func.is_atom()),
1899                        ^args
1900                            .iter()
1901                            .format_with(", ", |arg, f| f(&format_args_cx!(cx, "{:?}", arg)))
1902                    )
1903                }
1904                ExprKind::IfThenElse(p, e1, e2) => {
1905                    w!(cx, f, "if {:?} {{ {:?} }} else {{ {:?} }}", p, e1, e2)
1906                }
1907                ExprKind::Hole(_) => {
1908                    w!(cx, f, "*")
1909                }
1910                ExprKind::KVar(kvar) => {
1911                    w!(cx, f, "{:?}", kvar)
1912                }
1913                ExprKind::WKVar(wkvar) => {
1914                    w!(cx, f, "{:?}", wkvar)
1915                }
1916                ExprKind::Alias(alias, args) => {
1917                    w!(cx, f, "{:?}({:?})", alias, join!(", ", args))
1918                }
1919                ExprKind::Abs(lam) => {
1920                    w!(cx, f, "{:?}", lam)
1921                }
1922                ExprKind::GlobalFunc(SpecFuncKind::Def(did)) => {
1923                    w!(cx, f, "{}", ^did.name())
1924                }
1925                ExprKind::GlobalFunc(SpecFuncKind::Thy(itf)) => {
1926                    if let Some(name) = name_of_thy_func(*itf) {
1927                        w!(cx, f, "{}", ^name)
1928                    } else {
1929                        w!(cx, f, "<error>")
1930                    }
1931                }
1932                ExprKind::InternalFunc(func) => {
1933                    w!(cx, f, "{:?}", func)
1934                }
1935                ExprKind::Quant(kind, dom, body) => {
1936                    let vars = body.vars();
1937                    cx.with_bound_vars(vars, || {
1938                        w!(cx, f, "{:?} {:?} {{ {:?} }}", kind, dom, body.skip_binder_ref())
1939                    })
1940                }
1941                ExprKind::Let(init, body) => {
1942                    let vars = body.vars();
1943                    cx.with_bound_vars(vars, || {
1944                        cx.fmt_bound_vars(false, "(let ", vars, " = ", f)?;
1945                        w!(cx, f, "{:?} in {:?})", init, body.skip_binder_ref())
1946                    })
1947                }
1948            }
1949        }
1950    }
1951
1952    fn fmt_field_proj(cx: &PrettyCx, proj: FieldProj) -> String {
1953        if let FieldProj::Adt { def_id, field } = proj
1954            && let Some(adt_sort_def) = cx.adt_sort_def_of(def_id)
1955        {
1956            format!("{}", adt_sort_def.struct_variant().field_names()[field as usize])
1957        } else if let FieldProj::RawPtr { field } = proj {
1958            field.name().to_string()
1959        } else {
1960            format!("{}", proj.field_idx())
1961        }
1962    }
1963
1964    impl Pretty for Constant {
1965        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1966            match self {
1967                Constant::Int(i) => w!(cx, f, "{i}"),
1968                Constant::BitVec(i, sz) => w!(cx, f, "bv({i}, {sz})"),
1969                Constant::Real(r) => w!(cx, f, "{}", ^r.0),
1970                Constant::Bool(b) => w!(cx, f, "{b}"),
1971                Constant::Str(sym) => w!(cx, f, "\"{sym}\""),
1972                Constant::Char(c) => w!(cx, f, "\'{c}\'"),
1973            }
1974        }
1975    }
1976
1977    impl Pretty for AliasReft {
1978        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1979            w!(cx, f, "<({:?}) as {:?}", &self.args[0], self.assoc_id.parent())?;
1980            let args = &self.args[1..];
1981            if !args.is_empty() {
1982                w!(cx, f, "<{:?}>", join!(", ", args))?;
1983            }
1984            w!(cx, f, ">::{}", ^self.assoc_id.name())
1985        }
1986    }
1987
1988    impl Pretty for Lambda {
1989        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1990            let vars = self.body.vars();
1991            // TODO: remove redundant vars; see Ty
1992            // let redundant_bvars = self.body.redundant_bvars().into_iter().collect();
1993            cx.with_bound_vars(vars, || {
1994                cx.fmt_bound_vars(false, "λ", vars, ". ", f)?;
1995                w!(cx, f, "{:?}", self.body.as_ref().skip_binder())
1996            })
1997        }
1998    }
1999
2000    impl Pretty for Var {
2001        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2002            match self {
2003                Var::Bound(debruijn, var) => cx.fmt_bound_reft(*debruijn, *var, f),
2004                Var::EarlyParam(var) => w!(cx, f, "{}", ^var.name),
2005                Var::Free(name) => w!(cx, f, "{:?}", ^name),
2006                Var::EVar(evar) => w!(cx, f, "{:?}", ^evar),
2007                Var::ConstGeneric(param) => w!(cx, f, "{}", ^param.name),
2008            }
2009        }
2010    }
2011
2012    impl Pretty for KVar {
2013        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2014            w!(cx, f, "{:?}", ^self.kvid)?;
2015            match cx.kvar_args {
2016                KVarArgs::All => {
2017                    w!(
2018                        cx,
2019                        f,
2020                        "({:?})[{:?}]",
2021                        join!(", ", self.self_args()),
2022                        join!(", ", self.scope())
2023                    )?;
2024                }
2025                KVarArgs::SelfOnly => w!(cx, f, "({:?})", join!(", ", self.self_args()))?,
2026                KVarArgs::Hide => {}
2027            }
2028            Ok(())
2029        }
2030    }
2031
2032    impl Pretty for WKVar {
2033        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2034            // Since we reuse KVId, we need to make the serialization custom.
2035            // Also we will not serialize the parameters for now.
2036            w!(cx, f, "$wk{}_{}", ^self.wkvid.id.index(), ^cx.tcx().def_path_str(self.wkvid.parent_fn))?;
2037            w!(cx, f, "({:?})[{:?}]", join!(", ", self.self_args()), join!(", ", self.scope()))?;
2038            Ok(())
2039        }
2040    }
2041
2042    impl Pretty for Path {
2043        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2044            w!(cx, f, "{:?}", &self.loc)?;
2045            for field in &self.projection {
2046                w!(cx, f, ".{}", ^u32::from(*field))?;
2047            }
2048            Ok(())
2049        }
2050    }
2051
2052    impl Pretty for Loc {
2053        fn fmt(&self, cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2054            match self {
2055                Loc::Local(local) => w!(cx, f, "{:?}", ^local),
2056                Loc::Var(var) => w!(cx, f, "{:?}", var),
2057            }
2058        }
2059    }
2060
2061    impl Pretty for BinOp {
2062        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2063            match self {
2064                BinOp::Iff => w!(cx, f, "<=>"),
2065                BinOp::Imp => w!(cx, f, "=>"),
2066                BinOp::Or => w!(cx, f, "||"),
2067                BinOp::And => w!(cx, f, "&&"),
2068                BinOp::Eq => w!(cx, f, "=="),
2069                BinOp::Ne => w!(cx, f, "!="),
2070                BinOp::Gt(_) => w!(cx, f, ">"),
2071                BinOp::Ge(_) => w!(cx, f, ">="),
2072                BinOp::Lt(_) => w!(cx, f, "<"),
2073                BinOp::Le(_) => w!(cx, f, "<="),
2074                BinOp::Add(_) => w!(cx, f, "+"),
2075                BinOp::Sub(_) => w!(cx, f, "-"),
2076                BinOp::Mul(_) => w!(cx, f, "*"),
2077                BinOp::Div(_) => w!(cx, f, "/"),
2078                BinOp::Mod(_) => w!(cx, f, "mod"),
2079                BinOp::BitAnd(_) => w!(cx, f, "&"),
2080                BinOp::BitOr(_) => w!(cx, f, "|"),
2081                BinOp::BitXor(_) => w!(cx, f, "^"),
2082                BinOp::BitShl(_) => w!(cx, f, "<<"),
2083                BinOp::BitShr(_) => w!(cx, f, ">>"),
2084            }
2085        }
2086    }
2087
2088    impl Pretty for UnOp {
2089        fn fmt(&self, _cx: &PrettyCx, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2090            match self {
2091                UnOp::Not => w!(cx, f, "!"),
2092                UnOp::Neg => w!(cx, f, "-"),
2093            }
2094        }
2095    }
2096
2097    impl_debug_with_default_cx!(Expr, Loc, Path, Var, KVar, WKVar, Lambda, AliasReft);
2098
2099    impl PrettyNested for Lambda {
2100        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
2101            // TODO: remove redundant vars; see Ty
2102            cx.nested_with_bound_vars("λ", self.body.vars(), None, |prefix| {
2103                let expr_d = self.body.skip_binder_ref().fmt_nested(cx)?;
2104                let text = format!("{}{}", prefix, expr_d.text);
2105                Ok(NestedString { text, children: expr_d.children, key: None })
2106            })
2107        }
2108    }
2109
2110    pub fn aggregate_nested(
2111        cx: &PrettyCx,
2112        ctor: &Ctor,
2113        flds: &[Expr],
2114        is_named: bool,
2115    ) -> Result<NestedString, fmt::Error> {
2116        let mut text =
2117            if is_named && ctor.is_enum() { format_cx!(cx, "{:?}", ctor) } else { "".to_string() };
2118        if flds.is_empty() {
2119            // No fields, no index
2120            Ok(NestedString { text, children: None, key: None })
2121        } else if flds.len() == 1 {
2122            // Single field, inline index
2123            text += &flds[0].fmt_nested(cx)?.text;
2124            Ok(NestedString { text, children: None, key: None })
2125        } else {
2126            let keys = if let Some((def_id, variant_idx)) = ctor.def_id_and_variant()
2127                && let Some(adt_sort_def) = cx.adt_sort_def_of(def_id)
2128            {
2129                adt_sort_def
2130                    .variant(variant_idx)
2131                    .field_names()
2132                    .iter()
2133                    .map(|name| format!("{name}"))
2134                    .collect_vec()
2135            } else if matches!(ctor, Ctor::RawPtr) {
2136                RawPtrField::iter()
2137                    .map(|field| field.name().to_string())
2138                    .collect_vec()
2139            } else {
2140                (0..flds.len()).map(|i| format!("arg{i}")).collect_vec()
2141            };
2142            // Multiple fields, nested index
2143            text += "{..}";
2144            let mut children = vec![];
2145            for (key, fld) in iter::zip(keys, flds) {
2146                let fld_d = fld.fmt_nested(cx)?;
2147                children.push(NestedString { key: Some(key), ..fld_d });
2148            }
2149            Ok(NestedString { text, children: Some(children), key: None })
2150        }
2151    }
2152
2153    impl PrettyNested for Name {
2154        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
2155            let text = cx.pretty_var_env.get(&PrettyVar::Local(*self));
2156            Ok(NestedString { text, key: None, children: None })
2157        }
2158    }
2159
2160    impl PrettyNested for Expr {
2161        fn fmt_nested(&self, cx: &PrettyCx) -> Result<NestedString, fmt::Error> {
2162            let e = if cx.simplify_exprs {
2163                self.simplify(&SnapshotMap::default())
2164            } else {
2165                self.clone()
2166            };
2167            match e.kind() {
2168                ExprKind::Var(Var::Free(name)) => name.fmt_nested(cx),
2169                ExprKind::Var(..)
2170                | ExprKind::Local(..)
2171                | ExprKind::Constant(..)
2172                | ExprKind::ConstDefId(..)
2173                | ExprKind::Hole(..)
2174                | ExprKind::GlobalFunc(..)
2175                | ExprKind::InternalFunc(..) => debug_nested(cx, &e),
2176                ExprKind::WKVar(..) => debug_nested(cx, &e),
2177                ExprKind::KVar(kvar) => {
2178                    let kv = format!("{:?}", kvar.kvid);
2179                    let mut strs = vec![kv];
2180                    for arg in &kvar.args {
2181                        strs.push(arg.fmt_nested(cx)?.text);
2182                    }
2183                    let text = format!("##[{}]##", strs.join("##"));
2184                    Ok(NestedString { text, children: None, key: None })
2185                }
2186                ExprKind::IfThenElse(p, e1, e2) => {
2187                    let p_d = p.fmt_nested(cx)?;
2188                    let e1_d = e1.fmt_nested(cx)?;
2189                    let e2_d = e2.fmt_nested(cx)?;
2190                    let text = format!("(if {} then {} else {})", p_d.text, e1_d.text, e2_d.text);
2191                    let children = float_children(vec![p_d.children, e1_d.children, e2_d.children]);
2192                    Ok(NestedString { text, children, key: None })
2193                }
2194                ExprKind::BinaryOp(op, e1, e2) => {
2195                    let e1_d = e1.fmt_nested(cx)?;
2196                    let e2_d = e2.fmt_nested(cx)?;
2197                    let e1_text = if should_parenthesize(op, e1) {
2198                        format!("({})", e1_d.text)
2199                    } else {
2200                        e1_d.text
2201                    };
2202                    let e2_text = if should_parenthesize(op, e2) {
2203                        format!("({})", e2_d.text)
2204                    } else {
2205                        e2_d.text
2206                    };
2207                    let op_d = debug_nested(cx, op)?;
2208                    let op_text = if matches!(op, BinOp::Div(_)) {
2209                        op_d.text
2210                    } else {
2211                        format!(" {} ", op_d.text)
2212                    };
2213                    let text = format!("{e1_text}{op_text}{e2_text}");
2214                    let children = float_children(vec![e1_d.children, e2_d.children]);
2215                    Ok(NestedString { text, children, key: None })
2216                }
2217                ExprKind::UnaryOp(op, e) => {
2218                    let e_d = e.fmt_nested(cx)?;
2219                    let op_d = debug_nested(cx, op)?;
2220                    let text = if e.is_atom() {
2221                        format!("{}{}", op_d.text, e_d.text)
2222                    } else {
2223                        format!("{}({})", op_d.text, e_d.text)
2224                    };
2225                    Ok(NestedString { text, children: e_d.children, key: None })
2226                }
2227                ExprKind::FieldProj(e, proj) if let ExprKind::Ctor(_, _) = e.kind() => {
2228                    // special case to avoid printing `{n:12}.n` as `12.n` but instead, just print `12`
2229                    // TODO: maintain an invariant that `FieldProj` never has a Ctor as first argument (as always reduced)
2230                    e.proj_and_reduce(*proj).fmt_nested(cx)
2231                }
2232                ExprKind::FieldProj(e, proj) => {
2233                    let e_d = e.fmt_nested(cx)?;
2234                    let text = if e.is_atom() {
2235                        format!("{}.{}", e_d.text, fmt_field_proj(cx, *proj))
2236                    } else {
2237                        format!("({}).{}", e_d.text, fmt_field_proj(cx, *proj))
2238                    };
2239                    Ok(NestedString { text, children: e_d.children, key: None })
2240                }
2241                ExprKind::Tuple(flds) => {
2242                    let mut texts = vec![];
2243                    let mut kidss = vec![];
2244                    for e in flds {
2245                        let e_d = e.fmt_nested(cx)?;
2246                        texts.push(e_d.text);
2247                        kidss.push(e_d.children);
2248                    }
2249                    let text = if let [e] = &texts[..] {
2250                        format!("({e},)")
2251                    } else {
2252                        format!("({})", texts.join(", "))
2253                    };
2254                    let children = float_children(kidss);
2255                    Ok(NestedString { text, children, key: None })
2256                }
2257                ExprKind::Ctor(ctor, flds) => aggregate_nested(cx, ctor, flds, true),
2258                ExprKind::IsCtor(def_id, variant_idx, idx) => {
2259                    let text = format!("is::{:?}::{:?}( {:?} )", def_id, variant_idx, idx);
2260                    Ok(NestedString { text, children: None, key: None })
2261                }
2262                ExprKind::PathProj(e, field) => {
2263                    let e_d = e.fmt_nested(cx)?;
2264                    let text = if e.is_atom() {
2265                        format!("{}.{:?}", e_d.text, field)
2266                    } else {
2267                        format!("({}).{:?}", e_d.text, field)
2268                    };
2269                    Ok(NestedString { text, children: e_d.children, key: None })
2270                }
2271                ExprKind::Alias(alias, args) => {
2272                    let mut texts = vec![];
2273                    let mut kidss = vec![];
2274                    for arg in args {
2275                        let arg_d = arg.fmt_nested(cx)?;
2276                        texts.push(arg_d.text);
2277                        kidss.push(arg_d.children);
2278                    }
2279                    let text = format_cx!(cx, "{:?}({:?})", alias, texts.join(", "));
2280                    let children = float_children(kidss);
2281                    Ok(NestedString { text, children, key: None })
2282                }
2283                ExprKind::App(func, _, args) => {
2284                    let func_d = func.fmt_nested(cx)?;
2285                    let mut texts = vec![];
2286                    let mut kidss = vec![func_d.children];
2287                    for arg in args {
2288                        let arg_d = arg.fmt_nested(cx)?;
2289                        texts.push(arg_d.text);
2290                        kidss.push(arg_d.children);
2291                    }
2292                    let text = if func.is_atom() {
2293                        format!("{}({})", func_d.text, texts.join(", "))
2294                    } else {
2295                        format!("({})({})", func_d.text, texts.join(", "))
2296                    };
2297                    let children = float_children(kidss);
2298                    Ok(NestedString { text, children, key: None })
2299                }
2300                ExprKind::Abs(lambda) => lambda.fmt_nested(cx),
2301                ExprKind::Let(init, body) => {
2302                    // FIXME this is very wrong!
2303                    cx.nested_with_bound_vars("let", body.vars(), None, |prefix| {
2304                        let body = body.skip_binder_ref().fmt_nested(cx)?;
2305                        let text = format!("{:?} {}{}", init, prefix, body.text);
2306                        Ok(NestedString { text, children: body.children, key: None })
2307                    })
2308                }
2309                ExprKind::Quant(kind, dom, body) => {
2310                    let left = match kind {
2311                        fhir::QuantKind::Forall => "∀",
2312                        fhir::QuantKind::Exists => "∃",
2313                    };
2314                    let right = Some(format!(" {:?}", dom));
2315
2316                    cx.nested_with_bound_vars(left, body.vars(), right, |all_str| {
2317                        let expr_d = body.as_ref().skip_binder().fmt_nested(cx)?;
2318                        let text = format!("{}{}", all_str, expr_d.text);
2319                        Ok(NestedString { text, children: expr_d.children, key: None })
2320                    })
2321                }
2322            }
2323        }
2324    }
2325}
2326
2327impl Expr {
2328    /// Applies transformations to simplify and humanize canonical Z3 outputs.
2329    pub fn prettify(&self) -> Self {
2330        self.fold_constants()
2331            .push_negations()
2332            .simplify_arithmetic()
2333            .simplify_bounds()
2334    }
2335
2336    /// Evaluates expressions with constant operands recursively.
2337    pub fn fold_constants(&self) -> Self {
2338        let span = self.span();
2339        match self.kind() {
2340            ExprKind::UnaryOp(op, a) => {
2341                let a_fold = a.fold_constants();
2342
2343                if let ExprKind::Constant(c) = a_fold.kind() {
2344                    match (op, c) {
2345                        (UnOp::Not, Constant::Bool(b)) => {
2346                            return Expr::constant(Constant::Bool(!b)).at_opt(span);
2347                        }
2348                        (UnOp::Neg, Constant::Int(n)) => {
2349                            return Expr::constant(Constant::Int(n.neg())).at_opt(span);
2350                        }
2351                        _ => {}
2352                    }
2353                }
2354                Expr::unary_op(*op, a_fold).at_opt(span)
2355            }
2356
2357            ExprKind::BinaryOp(op, a, b) => {
2358                let a_fold = a.fold_constants();
2359                let b_fold = b.fold_constants();
2360
2361                if let (ExprKind::Constant(c1), ExprKind::Constant(c2)) =
2362                    (a_fold.kind(), b_fold.kind())
2363                {
2364                    // 1. Leverage existing const_op logic (handles Bools, Eq, Ne, and Int comparisons)
2365                    if let Some(c3) = Expr::const_op(op, c1, c2) {
2366                        return Expr::constant(c3).at_opt(span);
2367                    }
2368
2369                    // 2. Handle integer arithmetic
2370                    if let (Constant::Int(n1), Constant::Int(n2)) = (c1, c2) {
2371                        let result = match op {
2372                            BinOp::Add(_) => n1.checked_add(n2),
2373                            BinOp::Sub(_) => n1.checked_sub(n2),
2374                            BinOp::Mul(_) => n1.checked_mul(n2),
2375                            BinOp::Div(_) => n1.checked_div(n2),
2376                            BinOp::Mod(_) => n1.checked_rem(n2),
2377                            _ => None,
2378                        };
2379
2380                        if let Some(res) = result {
2381                            return Expr::constant(Constant::Int(res)).at_opt(span);
2382                        }
2383                    }
2384                }
2385
2386                Expr::binary_op(op.clone(), a_fold, b_fold).at_opt(span)
2387            }
2388
2389            ExprKind::IfThenElse(p, e1, e2) => {
2390                let p_fold = p.fold_constants();
2391
2392                // Short-circuit the ITE if the predicate is a constant boolean
2393                if let ExprKind::Constant(Constant::Bool(b)) = p_fold.kind() {
2394                    if *b {
2395                        return e1.fold_constants().at_opt(span);
2396                    } else {
2397                        return e2.fold_constants().at_opt(span);
2398                    }
2399                }
2400
2401                Expr::ite(p_fold, e1.fold_constants(), e2.fold_constants()).at_opt(span)
2402            }
2403
2404            // Other variants (Tuple, App, FieldProj, etc.) recursively fall through
2405            // untouched to avoid making assumptions about how your inner types construct.
2406            _ => self.clone(),
2407        }
2408    }
2409
2410    /// Pass 1: Eliminate `Not` over inequalities and boolean operations.
2411    pub fn push_negations(&self) -> Self {
2412        let span = self.span();
2413        match self.kind() {
2414            ExprKind::UnaryOp(UnOp::Not, inner) => {
2415                match inner.kind() {
2416                    // Double negation
2417                    ExprKind::UnaryOp(UnOp::Not, a) => a.push_negations().at_opt(span),
2418
2419                    // Flipped Inequalities
2420                    ExprKind::BinaryOp(BinOp::Le(s), a, b) => {
2421                        Expr::binary_op(
2422                            BinOp::Gt(s.clone()),
2423                            a.push_negations(),
2424                            b.push_negations(),
2425                        )
2426                        .at_opt(span)
2427                    }
2428                    ExprKind::BinaryOp(BinOp::Lt(s), a, b) => {
2429                        Expr::binary_op(
2430                            BinOp::Ge(s.clone()),
2431                            a.push_negations(),
2432                            b.push_negations(),
2433                        )
2434                        .at_opt(span)
2435                    }
2436                    ExprKind::BinaryOp(BinOp::Ge(s), a, b) => {
2437                        Expr::binary_op(
2438                            BinOp::Lt(s.clone()),
2439                            a.push_negations(),
2440                            b.push_negations(),
2441                        )
2442                        .at_opt(span)
2443                    }
2444                    ExprKind::BinaryOp(BinOp::Gt(s), a, b) => {
2445                        Expr::binary_op(
2446                            BinOp::Le(s.clone()),
2447                            a.push_negations(),
2448                            b.push_negations(),
2449                        )
2450                        .at_opt(span)
2451                    }
2452
2453                    // Equalities
2454                    ExprKind::BinaryOp(BinOp::Eq, a, b) => {
2455                        Expr::binary_op(BinOp::Ne, a.push_negations(), b.push_negations())
2456                            .at_opt(span)
2457                    }
2458                    ExprKind::BinaryOp(BinOp::Ne, a, b) => {
2459                        Expr::binary_op(BinOp::Eq, a.push_negations(), b.push_negations())
2460                            .at_opt(span)
2461                    }
2462
2463                    // De Morgan's
2464                    ExprKind::BinaryOp(BinOp::Or, a, b) => {
2465                        Expr::binary_op(
2466                            BinOp::And,
2467                            a.not().push_negations(),
2468                            b.not().push_negations(),
2469                        )
2470                        .at_opt(span)
2471                    }
2472                    ExprKind::BinaryOp(BinOp::And, a, b) => {
2473                        Expr::binary_op(
2474                            BinOp::Or,
2475                            a.not().push_negations(),
2476                            b.not().push_negations(),
2477                        )
2478                        .at_opt(span)
2479                    }
2480
2481                    // Otherwise just wrap and stop
2482                    _ => Expr::unary_op(UnOp::Not, inner.push_negations()).at_opt(span),
2483                }
2484            }
2485            ExprKind::BinaryOp(op, a, b) => {
2486                Expr::binary_op(op.clone(), a.push_negations(), b.push_negations()).at_opt(span)
2487            }
2488            ExprKind::UnaryOp(op, a) => Expr::unary_op(*op, a.push_negations()).at_opt(span),
2489            ExprKind::IfThenElse(p, e1, e2) => {
2490                Expr::ite(p.push_negations(), e1.push_negations(), e2.push_negations()).at_opt(span)
2491            }
2492            _ => self.clone(),
2493        }
2494    }
2495
2496    /// Pass 2: Clean up canonicalized arithmetic (e.g. `b0 * -1 + b1` -> `b1 - b0`)
2497    pub fn simplify_arithmetic(&self) -> Self {
2498        let span = self.span();
2499        match self.kind() {
2500            ExprKind::BinaryOp(BinOp::Mul(s), a, b) => {
2501                let a_simp = a.simplify_arithmetic();
2502                let b_simp = b.simplify_arithmetic();
2503
2504                let is_minus_one = |e: &Expr| matches!(e.kind(), ExprKind::Constant(c) if *c == Constant::from(-1));
2505
2506                if is_minus_one(&b_simp) {
2507                    return a_simp.neg().at_opt(span);
2508                }
2509                if is_minus_one(&a_simp) {
2510                    return b_simp.neg().at_opt(span);
2511                }
2512
2513                Expr::binary_op(BinOp::Mul(s.clone()), a_simp, b_simp).at_opt(span)
2514            }
2515            ExprKind::BinaryOp(BinOp::Add(s), a, b) => {
2516                let a_simp = a.simplify_arithmetic();
2517                let b_simp = b.simplify_arithmetic();
2518
2519                if let ExprKind::UnaryOp(UnOp::Neg, x) = a_simp.kind() {
2520                    return Expr::binary_op(BinOp::Sub(s.clone()), b_simp, x.clone()).at_opt(span);
2521                }
2522                if let ExprKind::UnaryOp(UnOp::Neg, y) = b_simp.kind() {
2523                    return Expr::binary_op(BinOp::Sub(s.clone()), a_simp, y.clone()).at_opt(span);
2524                }
2525
2526                Expr::binary_op(BinOp::Add(s.clone()), a_simp, b_simp).at_opt(span)
2527            }
2528            ExprKind::BinaryOp(op, a, b) => {
2529                Expr::binary_op(op.clone(), a.simplify_arithmetic(), b.simplify_arithmetic())
2530                    .at_opt(span)
2531            }
2532            ExprKind::UnaryOp(op, a) => Expr::unary_op(*op, a.simplify_arithmetic()).at_opt(span),
2533            ExprKind::IfThenElse(p, e1, e2) => {
2534                Expr::ite(
2535                    p.simplify_arithmetic(),
2536                    e1.simplify_arithmetic(),
2537                    e2.simplify_arithmetic(),
2538                )
2539                .at_opt(span)
2540            }
2541            _ => self.clone(),
2542        }
2543    }
2544
2545    /// Pass 3: Integer shifts and inequality rearrangement
2546    pub fn simplify_bounds(&self) -> Self {
2547        let span = self.span();
2548        match self.kind() {
2549            ExprKind::BinaryOp(op, a, b) => {
2550                let a_simp = a.simplify_bounds();
2551                let b_simp = b.simplify_bounds();
2552
2553                let is_minus_one = |e: &Expr| matches!(e.kind(), ExprKind::Constant(c) if *c == Constant::from(-1));
2554                let is_zero =
2555                    |e: &Expr| matches!(e.kind(), ExprKind::Constant(c) if *c == Constant::from(0));
2556
2557                // Important: this logic only applies if the operators are specifically typed for integers
2558                match op {
2559                    BinOp::Gt(Sort::Int) => {
2560                        // X > -1  ==>  X >= 0
2561                        if is_minus_one(&b_simp) {
2562                            return Expr::binary_op(BinOp::Ge(Sort::Int), a_simp, Expr::zero())
2563                                .at_opt(span)
2564                                .simplify_bounds();
2565                        }
2566                        // X - Y > 0  ==>  X > Y
2567                        if is_zero(&b_simp)
2568                            && let ExprKind::BinaryOp(BinOp::Sub(s_sub), x, y) = a_simp.kind()
2569                        {
2570                            return Expr::binary_op(BinOp::Gt(s_sub.clone()), x.clone(), y.clone())
2571                                .at_opt(span);
2572                        }
2573                    }
2574                    BinOp::Ge(Sort::Int) => {
2575                        // X - Y >= 0  ==>  X >= Y
2576                        if is_zero(&b_simp)
2577                            && let ExprKind::BinaryOp(BinOp::Sub(s_sub), x, y) = a_simp.kind()
2578                        {
2579                            return Expr::binary_op(BinOp::Ge(s_sub.clone()), x.clone(), y.clone())
2580                                .at_opt(span);
2581                        }
2582                    }
2583                    BinOp::Lt(Sort::Int) => {
2584                        // X - Y < 0  ==>  X < Y
2585                        if is_zero(&b_simp)
2586                            && let ExprKind::BinaryOp(BinOp::Sub(s_sub), x, y) = a_simp.kind()
2587                        {
2588                            return Expr::binary_op(BinOp::Lt(s_sub.clone()), x.clone(), y.clone())
2589                                .at_opt(span);
2590                        }
2591                    }
2592                    BinOp::Le(Sort::Int) => {
2593                        // X <= -1  ==>  X < 0
2594                        if is_minus_one(&b_simp) {
2595                            return Expr::binary_op(BinOp::Lt(Sort::Int), a_simp, Expr::zero())
2596                                .at_opt(span)
2597                                .simplify_bounds();
2598                        }
2599                        // X - Y <= 0  ==>  X <= Y
2600                        if is_zero(&b_simp)
2601                            && let ExprKind::BinaryOp(BinOp::Sub(s_sub), x, y) = a_simp.kind()
2602                        {
2603                            return Expr::binary_op(BinOp::Le(s_sub.clone()), x.clone(), y.clone())
2604                                .at_opt(span);
2605                        }
2606                    }
2607                    _ => {}
2608                }
2609
2610                Expr::binary_op(op.clone(), a_simp, b_simp).at_opt(span)
2611            }
2612            ExprKind::UnaryOp(op, a) => Expr::unary_op(*op, a.simplify_bounds()).at_opt(span),
2613            ExprKind::IfThenElse(p, e1, e2) => {
2614                Expr::ite(p.simplify_bounds(), e1.simplify_bounds(), e2.simplify_bounds())
2615                    .at_opt(span)
2616            }
2617            _ => self.clone(),
2618        }
2619    }
2620}