Skip to main content

liquid_fixpoint/
constraint.rs

1use std::{collections::HashSet, hash::Hash};
2
3use derive_where::derive_where;
4use itertools::Itertools;
5use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
6
7use crate::{ConstDecl, ThyFunc, Types};
8
9#[derive_where(Hash, Clone, Debug)]
10pub struct Bind<T: Types> {
11    pub name: T::Var,
12    pub sort: Sort<T>,
13    pub preds: Vec<Pred<T>>,
14}
15
16#[derive_where(Hash, Clone, Debug)]
17pub enum Constraint<T: Types> {
18    Pred(Pred<T>, #[derive_where(skip)] Option<T::Tag>),
19    Conj(Vec<Self>),
20    ForAll(Bind<T>, Box<Self>),
21}
22
23impl<T: Types> Constraint<T> {
24    pub const TRUE: Self = Self::Pred(Pred::TRUE, None);
25
26    pub fn foralls(bindings: Vec<Bind<T>>, c: Self) -> Self {
27        bindings
28            .into_iter()
29            .rev()
30            .fold(c, |c, bind| Constraint::ForAll(bind, Box::new(c)))
31    }
32
33    pub fn conj(mut cstrs: Vec<Self>) -> Self {
34        if cstrs.len() == 1 { cstrs.remove(0) } else { Self::Conj(cstrs) }
35    }
36
37    /// Returns true if the constraint has at least one concrete RHS ("head") predicates.
38    /// If `!c.is_concrete` then `c` is trivially satisfiable and we can avoid calling fixpoint.
39    /// Returns the number of concrete, non-trivial head predicates in the constraint.
40    pub fn concrete_head_count(&self) -> usize {
41        fn go<T: Types>(c: &Constraint<T>, count: &mut usize) {
42            match c {
43                Constraint::Conj(cs) => cs.iter().for_each(|c| go(c, count)),
44                Constraint::ForAll(_, c) => go(c, count),
45                Constraint::Pred(head, _) => {
46                    if head.is_concrete() && !head.is_trivially_true() {
47                        *count += 1;
48                    }
49                }
50            }
51        }
52        let mut count = 0;
53        go(self, &mut count);
54        count
55    }
56
57    /// Flattens a single constraint into a list of individual constraints which
58    /// may be checked for satisfiability.
59    ///
60    /// NOTE: Assumes and requires that all binder names are unique, i.e.
61    /// there is no shadowing (it is OK to have multiple binders of the same
62    /// name so long as they never are used, e.g. UNDERSCORE).
63    pub fn flatten<F1>(&self, is_underscore: F1) -> Vec<FlatConstraint<T>>
64    where
65        F1: Copy + Fn(&T::Var) -> bool,
66    {
67        match self {
68            Constraint::Pred(pred, tag) => {
69                vec![FlatConstraint {
70                    binders: vec![],
71                    assumptions: Default::default(),
72                    head: pred.clone(),
73                    tag: tag.clone(),
74                }]
75            }
76            Constraint::Conj(constrs) => {
77                constrs
78                    .iter()
79                    .flat_map(|constr| constr.flatten(is_underscore))
80                    .collect_vec()
81            }
82            Constraint::ForAll(bind, constr) => {
83                let mut flat_constrs = constr.flatten(is_underscore);
84                for constr in &mut flat_constrs {
85                    if !is_underscore(&bind.name) {
86                        constr.binders.push((bind.name.clone(), bind.sort.clone()));
87                    }
88                    for pred in &bind.preds {
89                        constr.add_assumption(pred.clone());
90                    }
91                    // if is_invariant(&bind.name) {
92                    //     constr.add_invariant(bind.pred.clone());
93                    // } else {
94                    //     constr.add_assumption(bind.pred.clone());
95                    // }
96                }
97                flat_constrs
98            }
99        }
100    }
101}
102
103pub type WKVarAndConstrs<T> = (WKVar<T>, FlatConstraint<T>, Vec<FlatConstraint<T>>);
104pub type VarSorts<T> = (<T as Types>::Var, Sort<T>);
105
106#[derive_where(Clone, Debug)]
107pub struct FlatConstraint<T: Types> {
108    /// All of the binders that come before the head.
109    ///
110    /// NOTE: There should be no duplicates among the binders which are used (so
111    /// e.g. UNDERSCORE appearing multiple times is OK).
112    pub binders: Vec<(T::Var, Sort<T>)>,
113    /// All of the assumptions (i.e. a flattened conjunction of predicates from
114    /// all of the binders)
115    pub assumptions: FxIndexSet<Pred<T>>,
116    pub head: Pred<T>,
117    #[derive_where(skip)]
118    pub tag: Option<T::Tag>,
119}
120
121impl<T: Types> FlatConstraint<T> {
122    /// Removes any binder corresponding to a given var. Returns the new
123    /// constraint along with the removed binders and their sorts.
124    ///
125    /// NOTE: Assumes that all binders are unique and therefore there are no
126    /// name clashes.
127    pub fn remove_binders(&self, vars: &HashSet<T::Var>) -> (Vec<ConstDecl<T>>, FlatConstraint<T>) {
128        let mut new_binders = self.binders.clone();
129        let removed_binders = new_binders
130            .extract_if(.., |(var, _sort)| vars.contains(var))
131            .map(|(var, sort)| ConstDecl { name: var, sort, comment: None })
132            .collect_vec();
133        let new_constraint = FlatConstraint {
134            binders: new_binders,
135            assumptions: self.assumptions.clone(),
136            head: self.head.clone(),
137            tag: self.tag.clone(),
138        };
139        (removed_binders, new_constraint)
140    }
141
142    pub fn add_assumption(&mut self, assumption: Pred<T>) {
143        match assumption {
144            a @ Pred::KVar(_, _) => {
145                self.assumptions.insert(a);
146            }
147            Pred::Expr(e) => {
148                self.assumptions
149                    .extend(e.as_conjunction().into_iter().map(|e| Pred::Expr(e)));
150            }
151        }
152    }
153
154    pub fn preconditions(&self) -> FxIndexSet<Pred<T>> {
155        // If we add invariants, this becomes the union of both invariatns and
156        // assumptions
157        self.assumptions.clone()
158    }
159
160    /// Each element of the output is a wkvar, the constraint it corresponds to,
161    /// and the other constraints which must also hold.
162    ///
163    /// We essentially "decompose" each assumption which contains a wkvar in
164    /// order to transform the constraint into one in which that wkvar is
165    /// exposed.
166    ///
167    /// These decompositions are of two forms.
168    /// 1. Hoisting existentials to the top level.
169    /// 2. Splitting disjuncts using the below property. When we split a
170    ///    disjunct, we generate other constraints. A more sophisticated
171    ///    analysis might try and solve multiple constraints simultaneously; if
172    ///    we were to do this, we would probably want to change how this
173    ///    algorithm works to avoid redundancy, e.g. by instead of generating
174    ///    ((wkvar, constr, other_constrs) outputs generating (all_constrs)
175    ///    outputs and letting consumers choose which wkvars to solve in each
176    ///    constr.
177    ///
178    /// ```
179    /// (p || q) => c
180    /// ==
181    /// (p => c) && (q => c)
182    /// ```
183    pub fn wkvars_and_constrs(&self) -> Vec<WKVarAndConstrs<T>> {
184        let mut wkvars_and_constraints = self
185            .assumptions
186            .iter()
187            .flat_map(|assumption| {
188                assumption
189                    .wkvars_in_conj()
190                    .into_iter()
191                    .map(|wkvar| (wkvar, self.clone(), vec![]))
192            })
193            .collect_vec();
194        // Frontier elements:
195        //   (current assumption, current constraint, other constraints)
196        let mut frontier: Vec<_> = self
197            .assumptions
198            .iter()
199            .enumerate()
200            .filter_map(|(i, assumption)| {
201                if let Pred::Expr(assumption_expr) = assumption
202                    && assumption_expr.has_wkvar_reachable_by_split()
203                {
204                    let mut flat_constraint = self.clone();
205                    flat_constraint.assumptions.shift_remove_index(i);
206                    Some((assumption_expr.clone(), flat_constraint, vec![]))
207                } else {
208                    None
209                }
210            })
211            .collect_vec();
212        while let Some((e, mut constr, other_constrs)) = frontier.pop() {
213            match e {
214                // Base case: we reached a wkvar.
215                //   Record it alongside the constraint it is associated with
216                //   and the other constraints.
217                //
218                // NOTE: we don't need wkvars_in_conj because we explicitly
219                //       handle the And.
220                Expr::WKVar(wkvar) => {
221                    // NOTE: Technically this isn't necessary, so we don't add
222                    // the wkvar back to the assumptions. But for completeness
223                    // we might want to.
224                    //
225                    // constr.add_assumption(Pred::Expr(e));
226                    wkvars_and_constraints.push((wkvar, constr, other_constrs));
227                }
228                // In the And case, we add to the frontier each expression that
229                // is a wkvar or can reach a wkvar; whenever we do so, we also
230                // take all of the other expressions in the And and put them
231                // into the current constr's assumptions.
232                //
233                // We do this kind of annoying filter rather than just adding
234                // everything because it prevents us from cloning a bunch.
235                Expr::And(conjs) => {
236                    for (i, new_assumption) in conjs.iter().enumerate() {
237                        if !(matches!(new_assumption, Expr::WKVar(..))
238                            || new_assumption.has_wkvar_reachable_by_split())
239                        {
240                            continue;
241                        }
242                        let mut new_constr = constr.clone();
243                        for (j, other_new_assumption) in conjs.iter().enumerate() {
244                            if i == j {
245                                continue;
246                            }
247                            new_constr.add_assumption(Pred::Expr(other_new_assumption.clone()));
248                        }
249                        frontier.push((new_assumption.clone(), new_constr, other_constrs.clone()));
250                    }
251                }
252                // Hoist the exists and add it to the frontier.
253                //
254                // We assume that there is a wkvar reachable here, so we don't check.
255                Expr::Quantifier(Quantifier::Exists, _, _) => {
256                    let (new_vars, hoisted_e) = e.hoist_exists();
257                    constr.binders.extend(new_vars);
258                    frontier.push((hoisted_e, constr, other_constrs));
259                }
260                // This is where things get kind of complicated.
261                //
262                // Morally speaking what we are doing is translating the constraint
263                //     (disjunct1 || disjunct2 || ...) => constr
264                // into multiple constraints
265                //     disjunct1 => constr
266                //     disjunct2 => constr
267                //     ...
268                // but our frontier looks like
269                //     (current assumption, constr without that assumption, other constrs we're no longer looking at)
270                // So if there are N disjuncts, we'll push N items to the frontier:
271                //     (disjunct1, constr, (disjunct2 => constr) + (disjunct3 => constr) + ... + other_constrs)
272                // Except the same optimization applies where we won't consider a case where the disjunct
273                // has no wkvars in it.
274                Expr::Or(disjuncts) => {
275                    for (i, disjunct) in disjuncts.iter().enumerate() {
276                        if !(matches!(disjunct, Expr::WKVar(..))
277                            || disjunct.has_wkvar_reachable_by_split())
278                        {
279                            continue;
280                        }
281                        let mut new_other_constrs = other_constrs.clone();
282                        for (j, other_disjunct) in disjuncts.iter().enumerate() {
283                            if i == j {
284                                continue;
285                            }
286                            let mut new_other_constr = constr.clone();
287                            new_other_constr.add_assumption(Pred::Expr(other_disjunct.clone()));
288                            new_other_constrs.push(new_other_constr);
289                        }
290                        frontier.push((disjunct.clone(), constr.clone(), new_other_constrs));
291                    }
292                }
293                _ => {}
294            }
295        }
296        wkvars_and_constraints
297    }
298}
299
300#[derive_where(Hash, Clone, Debug)]
301pub struct DataDecl<T: Types> {
302    pub name: T::Sort,
303    pub vars: usize,
304    pub ctors: Vec<DataCtor<T>>,
305}
306
307impl<T: Types> DataDecl<T> {
308    pub fn deps(&self, acc: &mut Vec<T::Sort>) {
309        for ctor in &self.ctors {
310            for field in &ctor.fields {
311                field.sort.deps(acc);
312            }
313        }
314    }
315}
316
317#[derive_where(Hash, Clone, Debug)]
318pub struct SortDecl<T: Types> {
319    pub name: T::Sort,
320    pub vars: usize,
321}
322
323#[derive_where(Hash, Clone, Debug)]
324pub struct DataCtor<T: Types> {
325    pub name: T::Var,
326    pub fields: Vec<DataField<T>>,
327}
328
329#[derive_where(Hash, Clone, Debug)]
330pub struct DataField<T: Types> {
331    pub name: T::Var,
332    pub sort: Sort<T>,
333}
334
335#[derive_where(PartialEq, Eq, Hash, Clone, Debug)]
336pub enum Sort<T: Types> {
337    Int,
338    Bool,
339    Real,
340    Str,
341    BitVec(Box<Sort<T>>),
342    BvSize(u32),
343    Var(usize),
344    Func(Box<[Self; 2]>),
345    Abs(usize, Box<Self>),
346    App(SortCtor<T>, Vec<Self>),
347}
348
349impl<T: Types> Sort<T> {
350    pub fn deps(&self, acc: &mut Vec<T::Sort>) {
351        match self {
352            Sort::App(SortCtor::Data(dt_name), args) => {
353                acc.push(dt_name.clone());
354                for arg in args {
355                    arg.deps(acc);
356                }
357            }
358            Sort::Func(input_and_output) => {
359                let [input, output] = &**input_and_output;
360                input.deps(acc);
361                output.deps(acc);
362            }
363            Sort::Abs(_, sort) => {
364                sort.deps(acc);
365            }
366            _ => {}
367        }
368    }
369
370    pub fn mk_func<I>(params: usize, inputs: I, output: Sort<T>) -> Sort<T>
371    where
372        I: IntoIterator<Item = Sort<T>>,
373        I::IntoIter: DoubleEndedIterator,
374    {
375        let sort = inputs
376            .into_iter()
377            .rev()
378            .fold(output, |output, input| Sort::Func(Box::new([input, output])));
379
380        (0..params)
381            .rev()
382            .fold(sort, |sort, i| Sort::Abs(i, Box::new(sort)))
383    }
384
385    pub(crate) fn peel_out_abs(&self) -> (usize, &Sort<T>) {
386        let mut n = 0;
387        let mut curr = self;
388        while let Sort::Abs(i, sort) = curr {
389            assert_eq!(*i, n);
390            n += 1;
391            curr = sort;
392        }
393        (n, curr)
394    }
395
396    fn free_var_sorts_to_int_help(&mut self, bound: &mut HashSet<usize>) {
397        match self {
398            Sort::Int
399            | Sort::Real
400            | Sort::Bool
401            | Sort::Str
402            | Sort::BvSize(..)
403            | Sort::BitVec(..) => {}
404            Sort::Abs(var, inner) => {
405                bound.insert(*var);
406                inner.free_var_sorts_to_int_help(bound);
407                bound.remove(var);
408            }
409            Sort::App(_, args) => {
410                for arg in args {
411                    arg.free_var_sorts_to_int_help(bound);
412                }
413            }
414            Sort::Func(inner) => {
415                let [arg, out] = &mut **inner;
416                arg.free_var_sorts_to_int_help(bound);
417                out.free_var_sorts_to_int_help(bound);
418            }
419            Sort::Var(v) => {
420                if !bound.contains(v) {
421                    *self = Sort::Int;
422                }
423            }
424        }
425    }
426
427    pub(crate) fn free_var_sorts_to_int(&mut self) {
428        let mut bound = HashSet::new();
429        self.free_var_sorts_to_int_help(&mut bound);
430    }
431}
432
433#[derive_where(Hash, Debug)]
434pub struct FunSort<T: Types> {
435    pub params: usize,
436    pub inputs: Vec<Sort<T>>,
437    pub output: Sort<T>,
438}
439
440impl<T: Types> FunSort<T> {
441    pub fn deps(&self, acc: &mut Vec<T::Sort>) {
442        for sort in &self.inputs {
443            sort.deps(acc);
444        }
445        self.output.deps(acc);
446    }
447
448    pub fn into_sort(self) -> Sort<T> {
449        Sort::mk_func(self.params, self.inputs, self.output)
450    }
451}
452
453#[derive_where(PartialEq, Eq, Hash, Clone, Debug)]
454pub enum SortCtor<T: Types> {
455    Set,
456    Map,
457    Data(T::Sort),
458}
459
460#[derive_where(PartialEq, Eq, Hash, Clone, Debug)]
461pub enum Pred<T: Types> {
462    KVar(T::KVar, Vec<Expr<T>>),
463    Expr(Expr<T>),
464}
465
466impl<T: Types> Pred<T> {
467    pub const TRUE: Self = Pred::Expr(Expr::TRUE);
468
469    pub fn is_trivially_true(&self) -> bool {
470        match self {
471            Pred::Expr(e) => e.is_trivially_true(),
472            Pred::KVar(..) => false,
473        }
474    }
475
476    pub fn is_concrete(&self) -> bool {
477        matches!(self, Pred::Expr(_))
478    }
479
480    // Give all the weak kvars that appear as part of a top-level conjunction.
481    // This is a syntactic analysis.
482    pub fn wkvars_in_conj(&self) -> Vec<WKVar<T>> {
483        match self {
484            Pred::Expr(e) => e.wkvars_in_conj(),
485            Pred::KVar(..) => vec![],
486        }
487    }
488
489    /// Remove all wkvars, replacing them with true.
490    pub fn strip_wkvars(&self) -> Self {
491        match self {
492            Pred::Expr(e) => Pred::Expr(e.strip_wkvars()),
493            Pred::KVar(..) => self.clone(),
494        }
495    }
496}
497
498#[derive(Hash, Debug, Copy, Clone, PartialEq, Eq)]
499pub enum BinRel {
500    Eq,
501    Ne,
502    Gt,
503    Ge,
504    Lt,
505    Le,
506}
507
508impl BinRel {
509    pub const INEQUALITIES: [BinRel; 4] = [BinRel::Gt, BinRel::Ge, BinRel::Lt, BinRel::Le];
510}
511
512#[derive_where(PartialEq, Eq, Hash, Debug, Clone)]
513pub struct WKVar<T: Types> {
514    pub wkvid: T::Var,
515    pub args: Vec<Expr<T>>,
516}
517
518#[derive_where(PartialEq, Eq, Hash, Clone, Debug)]
519pub enum Expr<T: Types> {
520    Constant(Constant<T>),
521    Var(T::Var),
522    App(Box<Self>, Option<Vec<Sort<T>>>, Vec<Self>, Option<Sort<T>>),
523    Neg(Box<Self>),
524    BinaryOp(BinOp, Box<[Self; 2]>),
525    IfThenElse(Box<[Self; 3]>),
526    And(Vec<Self>),
527    Or(Vec<Self>),
528    Not(Box<Self>),
529    Imp(Box<[Self; 2]>),
530    Iff(Box<[Self; 2]>),
531    Atom(BinRel, Box<[Self; 2]>),
532    Let(T::Var, Box<[Self; 2]>),
533    ThyFunc(ThyFunc),
534    IsCtor(T::Var, Box<Self>),
535    Quantifier(Quantifier, Vec<(T::Var, Sort<T>)>, Box<Self>),
536    /// NOTE: WKVars are for internal use and we serialize them as UIFs using
537    /// the var argument. We also don't emit WKVars that are in head position
538    /// when translating to fixpoint.
539    ///
540    /// In an ideal world fixpoint would deal with weak kvars itself.
541    WKVar(WKVar<T>),
542}
543
544impl<T: Types> From<Constant<T>> for Expr<T> {
545    fn from(v: Constant<T>) -> Self {
546        Self::Constant(v)
547    }
548}
549
550impl<T: Types> Expr<T> {
551    pub const FALSE: Self = Expr::Constant(Constant::Boolean(false));
552    pub const TRUE: Self = Expr::Constant(Constant::Boolean(true));
553
554    pub const fn int(val: u128) -> Expr<T> {
555        Expr::Constant(Constant::Numeral(val))
556    }
557
558    pub fn eq(self, other: Self) -> Self {
559        Expr::Atom(BinRel::Eq, Box::new([self, other]))
560    }
561
562    pub fn and(mut exprs: Vec<Self>) -> Self {
563        if exprs.len() == 1 { exprs.remove(0) } else { Self::And(exprs) }
564    }
565
566    pub fn var_sorts_to_int(&mut self) {
567        match self {
568            Expr::Constant(_) | Expr::ThyFunc(_) | Expr::Var(_) => {}
569            Expr::App(func, sort_args, args, out_sort) => {
570                func.var_sorts_to_int();
571                for arg in args {
572                    arg.var_sorts_to_int();
573                }
574                if let Some(sort_args) = sort_args {
575                    for sort_arg in sort_args {
576                        sort_arg.free_var_sorts_to_int();
577                    }
578                }
579                if let Some(out_sort) = out_sort {
580                    out_sort.free_var_sorts_to_int();
581                }
582            }
583            Expr::Neg(e) | Expr::Not(e) => {
584                e.var_sorts_to_int();
585            }
586            Expr::BinaryOp(_, exprs)
587            | Expr::Imp(exprs)
588            | Expr::Iff(exprs)
589            | Expr::Atom(_, exprs) => {
590                let [e1, e2] = &mut **exprs;
591                e1.var_sorts_to_int();
592                e2.var_sorts_to_int();
593            }
594            Expr::IfThenElse(exprs) => {
595                let [p, e1, e2] = &mut **exprs;
596                p.var_sorts_to_int();
597                e1.var_sorts_to_int();
598                e2.var_sorts_to_int();
599            }
600            Expr::And(exprs) | Expr::Or(exprs) => {
601                for e in exprs {
602                    e.var_sorts_to_int();
603                }
604            }
605            Expr::Let(_, exprs) => {
606                let [var_e, body_e] = &mut **exprs;
607                var_e.var_sorts_to_int();
608                body_e.var_sorts_to_int();
609            }
610            Expr::IsCtor(_v, expr) => {
611                expr.var_sorts_to_int();
612            }
613            Expr::Quantifier(_, binder, expr) => {
614                for (_, sort) in binder {
615                    sort.free_var_sorts_to_int();
616                }
617                expr.var_sorts_to_int();
618            }
619            Expr::WKVar(_) => {
620                unimplemented!()
621            }
622        }
623    }
624
625    pub fn free_vars(&self) -> FxIndexSet<T::Var> {
626        let mut vars = FxIndexSet::default();
627        match self {
628            Expr::Constant(_) | Expr::ThyFunc(_) => {}
629            Expr::Var(x) => {
630                vars.insert(x.clone());
631            }
632            Expr::App(func, _sort_args, args, _out_sort) => {
633                vars.extend(func.free_vars());
634                for arg in args {
635                    vars.extend(arg.free_vars());
636                }
637            }
638            Expr::Neg(e) | Expr::Not(e) => {
639                vars = e.free_vars();
640            }
641            Expr::BinaryOp(_, exprs)
642            | Expr::Imp(exprs)
643            | Expr::Iff(exprs)
644            | Expr::Atom(_, exprs) => {
645                let [e1, e2] = &**exprs;
646                vars.extend(e1.free_vars());
647                vars.extend(e2.free_vars());
648            }
649            Expr::IfThenElse(exprs) => {
650                let [p, e1, e2] = &**exprs;
651                vars.extend(p.free_vars());
652                vars.extend(e1.free_vars());
653                vars.extend(e2.free_vars());
654            }
655            Expr::And(exprs) | Expr::Or(exprs) => {
656                for e in exprs {
657                    vars.extend(e.free_vars());
658                }
659            }
660            Expr::Let(name, exprs) => {
661                // Fixpoint only support one binder per let expressions, but it parses a singleton
662                // list of binders to be forward-compatible
663                let [var_e, body_e] = &**exprs;
664                vars.extend(var_e.free_vars());
665                let mut body_vars = body_e.free_vars();
666                body_vars.swap_remove(name);
667                vars.extend(body_vars);
668            }
669            Expr::IsCtor(_v, expr) => {
670                // NOTE: (ck) I'm pretty sure this isn't a binder so I'm not going to
671                // bother with `v`.
672                vars.extend(expr.free_vars());
673            }
674            Expr::Quantifier(_, binder, expr) => {
675                let mut inner = expr.free_vars();
676                for (var, _sort) in binder {
677                    inner.swap_remove(var);
678                }
679                vars.extend(inner);
680            }
681            Expr::WKVar(WKVar { wkvid: _, args }) => {
682                for e in args {
683                    vars.extend(e.free_vars());
684                }
685            }
686        };
687        vars
688    }
689
690    pub fn is_trivially_true(&self) -> bool {
691        matches!(self, Expr::Constant(Constant::Boolean(true)))
692    }
693
694    pub fn substitute(&self, subst: &FxIndexMap<T::Var, Self>) -> Self {
695        match self {
696            Expr::Var(v) => subst.get(v).cloned().unwrap_or_else(|| self.clone()),
697            Expr::Constant(_) | Expr::ThyFunc(_) => self.clone(),
698            Expr::App(expr, sort_args, exprs, out_sort) => {
699                Expr::App(
700                    Box::new(expr.substitute(subst)),
701                    sort_args.clone(),
702                    exprs.iter().map(|e| e.substitute(subst)).collect_vec(),
703                    out_sort.clone(),
704                )
705            }
706            Expr::Neg(expr) => Expr::Neg(Box::new(expr.substitute(subst))),
707            Expr::BinaryOp(bin_op, args) => {
708                Expr::BinaryOp(
709                    *bin_op,
710                    Box::new([args[0].substitute(subst), args[1].substitute(subst)]),
711                )
712            }
713            Expr::IfThenElse(args) => {
714                Expr::IfThenElse(Box::new([
715                    args[0].substitute(subst),
716                    args[1].substitute(subst),
717                    args[2].substitute(subst),
718                ]))
719            }
720            Expr::And(exprs) => Expr::And(exprs.iter().map(|e| e.substitute(subst)).collect_vec()),
721            Expr::Or(exprs) => Expr::Or(exprs.iter().map(|e| e.substitute(subst)).collect_vec()),
722            Expr::Not(expr) => Expr::Not(Box::new(expr.substitute(subst))),
723            Expr::Imp(args) => {
724                Expr::Imp(Box::new([args[0].substitute(subst), args[1].substitute(subst)]))
725            }
726            Expr::Iff(args) => {
727                Expr::Iff(Box::new([args[0].substitute(subst), args[1].substitute(subst)]))
728            }
729            Expr::Atom(bin_rel, args) => {
730                Expr::Atom(
731                    *bin_rel,
732                    Box::new([args[0].substitute(subst), args[1].substitute(subst)]),
733                )
734            }
735            Expr::Let(var, args) => {
736                Expr::Let(
737                    var.clone(),
738                    Box::new([args[0].substitute(subst), args[1].substitute(subst)]),
739                )
740            }
741            Expr::IsCtor(var, expr) => Expr::IsCtor(var.clone(), Box::new(expr.substitute(subst))),
742            Expr::Quantifier(q, sorts, expr) => {
743                Expr::Quantifier(*q, sorts.clone(), Box::new(expr.substitute(subst)))
744            }
745            Expr::WKVar(WKVar { wkvid, args }) => {
746                Expr::WKVar(WKVar {
747                    wkvid: wkvid.clone(),
748                    args: args.iter().map(|e| e.substitute(subst)).collect_vec(),
749                })
750            }
751        }
752    }
753
754    // Give all the weak kvars that appear as part of a top-level conjunction.
755    // This is a syntactic analysis.
756    pub fn wkvars_in_conj(&self) -> Vec<WKVar<T>> {
757        match self {
758            Expr::WKVar(wkvar) => vec![wkvar.clone()],
759            Expr::And(conj) => conj.iter().flat_map(|e| e.wkvars_in_conj()).collect(),
760            _ => vec![],
761        }
762    }
763
764    pub fn uncurry(&self) -> Self {
765        match self {
766            Expr::App(head, sort_args, args, out_sort) => {
767                let uncurried_head = head.uncurry();
768                let uncurried_args = args.iter().map(Expr::uncurry).collect_vec();
769                match uncurried_head {
770                    Expr::App(head_head, head_sort_args, mut head_args, _) => {
771                        head_args.extend(uncurried_args);
772                        let sort_args = match (head_sort_args, sort_args) {
773                            (Some(mut head_sort_args), Some(sort_args)) => {
774                                head_sort_args.extend(sort_args.clone());
775                                Some(head_sort_args)
776                            }
777                            _ => None,
778                        };
779                        Expr::App(head_head, sort_args, head_args, out_sort.clone())
780                    }
781                    Expr::WKVar(WKVar { wkvid, args: mut wkvar_args }) => {
782                        wkvar_args.extend(uncurried_args);
783                        Expr::WKVar(WKVar { wkvid, args: wkvar_args })
784                    }
785                    head => {
786                        Expr::App(
787                            Box::new(head),
788                            sort_args.clone(),
789                            uncurried_args,
790                            out_sort.clone(),
791                        )
792                    }
793                }
794            }
795            Expr::Constant(_) | Expr::Var(_) | Expr::ThyFunc(_) => self.clone(),
796            Expr::Neg(expr) => Expr::Neg(Box::new(expr.uncurry())),
797            Expr::BinaryOp(bin_op, args) => {
798                Expr::BinaryOp(*bin_op, Box::new([args[0].uncurry(), args[1].uncurry()]))
799            }
800            Expr::IfThenElse(args) => {
801                Expr::IfThenElse(Box::new([
802                    args[0].uncurry(),
803                    args[1].uncurry(),
804                    args[2].uncurry(),
805                ]))
806            }
807            Expr::And(exprs) => Expr::And(exprs.iter().map(Expr::uncurry).collect_vec()),
808            Expr::Or(exprs) => Expr::Or(exprs.iter().map(Expr::uncurry).collect_vec()),
809            Expr::Not(expr) => Expr::Not(Box::new(expr.uncurry())),
810            Expr::Imp(args) => Expr::Imp(Box::new([args[0].uncurry(), args[1].uncurry()])),
811            Expr::Iff(args) => Expr::Iff(Box::new([args[0].uncurry(), args[1].uncurry()])),
812            Expr::Atom(bin_rel, args) => {
813                Expr::Atom(*bin_rel, Box::new([args[0].uncurry(), args[1].uncurry()]))
814            }
815            Expr::Let(var, args) => {
816                Expr::Let(var.clone(), Box::new([args[0].uncurry(), args[1].uncurry()]))
817            }
818            Expr::IsCtor(var, expr) => Expr::IsCtor(var.clone(), Box::new(expr.uncurry())),
819            Expr::Quantifier(q, sorts, expr) => {
820                Expr::Quantifier(*q, sorts.clone(), Box::new(expr.uncurry()))
821            }
822            Expr::WKVar(WKVar { wkvid, args }) => {
823                Expr::WKVar(WKVar {
824                    wkvid: wkvid.clone(),
825                    args: args.iter().map(|e| e.uncurry()).collect_vec(),
826                })
827            }
828        }
829    }
830
831    pub fn has_wkvar_reachable_by_split(&self) -> bool {
832        if !matches!(self, Expr::Quantifier(Quantifier::Exists, ..) | Expr::Or(..) | Expr::And(..))
833        {
834            return false;
835        }
836        match self {
837            Expr::Or(exprs) => {
838                exprs.iter().any(|expr| {
839                    matches!(expr, Expr::WKVar(..)) || expr.has_wkvar_reachable_by_split()
840                })
841            }
842            Expr::Quantifier(Quantifier::Exists, _sorts, expr) => {
843                !expr.wkvars_in_conj().is_empty() || expr.has_wkvar_reachable_by_split()
844            }
845            Expr::And(exprs) => exprs.iter().any(Expr::has_wkvar_reachable_by_split),
846            _ => false,
847        }
848    }
849
850    pub fn hoist_exists(&self) -> (Vec<VarSorts<T>>, Expr<T>) {
851        match self {
852            Expr::Quantifier(Quantifier::Exists, var_sorts, inner_e) => {
853                let mut vars = var_sorts.clone();
854                let (new_vars, hoisted_inner) = inner_e.hoist_exists();
855                vars.extend(new_vars);
856                (vars, hoisted_inner)
857            }
858            Expr::And(exprs) => {
859                let mut vars = vec![];
860                let expr = Expr::And(
861                    exprs
862                        .iter()
863                        .flat_map(|expr| {
864                            let (new_vars, hoisted_expr) = expr.hoist_exists();
865                            vars.extend(new_vars);
866                            match hoisted_expr {
867                                Expr::And(exprs) => exprs,
868                                expr => vec![expr],
869                            }
870                        })
871                        .collect(),
872                );
873                (vars, expr)
874            }
875            _ => (vec![], self.clone()),
876        }
877    }
878
879    pub fn as_conjunction(self) -> Vec<Self> {
880        match self {
881            Expr::And(exprs) => exprs,
882            expr => vec![expr],
883        }
884    }
885
886    pub fn strip_wkvars(&self) -> Self {
887        match self {
888            Expr::Constant(_) | Expr::Var(_) | Expr::ThyFunc(_) => self.clone(),
889            Expr::WKVar(..) => Expr::TRUE,
890            Expr::App(head, sort_args, args, out_sort) => {
891                Expr::App(
892                    Box::new(head.strip_wkvars()),
893                    sort_args.clone(),
894                    args.iter().map(Expr::strip_wkvars).collect(),
895                    out_sort.clone(),
896                )
897            }
898            Expr::Neg(expr) => Expr::Neg(Box::new(expr.strip_wkvars())),
899            Expr::BinaryOp(bin_op, args) => {
900                Expr::BinaryOp(*bin_op, Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
901            }
902            Expr::IfThenElse(args) => {
903                Expr::IfThenElse(Box::new([
904                    args[0].strip_wkvars(),
905                    args[1].strip_wkvars(),
906                    args[2].strip_wkvars(),
907                ]))
908            }
909            Expr::And(exprs) => Expr::And(exprs.iter().map(Expr::strip_wkvars).collect_vec()),
910            Expr::Or(exprs) => Expr::Or(exprs.iter().map(Expr::strip_wkvars).collect_vec()),
911            Expr::Not(expr) => Expr::Not(Box::new(expr.strip_wkvars())),
912            Expr::Imp(args) => {
913                Expr::Imp(Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
914            }
915            Expr::Iff(args) => {
916                Expr::Iff(Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
917            }
918            Expr::Atom(bin_rel, args) => {
919                Expr::Atom(*bin_rel, Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
920            }
921            Expr::Let(var, args) => {
922                Expr::Let(var.clone(), Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
923            }
924            Expr::IsCtor(var, expr) => Expr::IsCtor(var.clone(), Box::new(expr.strip_wkvars())),
925            Expr::Quantifier(q, sorts, expr) => {
926                Expr::Quantifier(*q, sorts.clone(), Box::new(expr.strip_wkvars()))
927            }
928        }
929    }
930
931    pub fn total_num_disjuncts(&self) -> usize {
932        match self {
933            Expr::Or(disjuncts) => {
934                disjuncts.len()
935                    + disjuncts
936                        .iter()
937                        .map(Expr::total_num_disjuncts)
938                        .sum::<usize>()
939            }
940            Expr::And(conjuncts) => conjuncts.iter().map(Expr::total_num_disjuncts).sum(),
941            _ => 0,
942        }
943    }
944}
945
946#[derive_where(PartialEq, Eq, Hash, Clone, Debug)]
947pub enum Constant<T: Types> {
948    Numeral(u128),
949    Real(T::Real),
950    Boolean(bool),
951    String(T::String),
952    BitVec(u128, u32),
953}
954
955#[derive_where(Debug, Clone, Hash)]
956pub struct Qualifier<T: Types> {
957    pub name: String,
958    pub args: Vec<QualParam<T>>,
959    pub body: Expr<T>,
960}
961
962#[derive_where(Debug, Clone, Hash)]
963pub struct QualParam<T: Types> {
964    pub name: T::Var,
965    pub sort: Sort<T>,
966    pub is_wildcard: bool,
967}
968
969impl<T: Types> QualParam<T> {
970    pub fn new(name: T::Var, sort: Sort<T>) -> Self {
971        Self { name, sort, is_wildcard: false }
972    }
973}
974
975#[derive(Clone, Copy, PartialEq, Eq, Hash)]
976pub enum BinOp {
977    Add,
978    Sub,
979    Mul,
980    Div,
981    Mod,
982}
983
984#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
985pub enum Quantifier {
986    Exists,
987    Forall,
988}