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, Clone)]
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    pub fn to_sort(&self) -> Sort<T> {
453        Sort::mk_func(self.params, self.inputs.clone(), self.output.clone())
454    }
455}
456
457#[derive_where(PartialEq, Eq, Hash, Clone, Debug)]
458pub enum SortCtor<T: Types> {
459    Set,
460    Map,
461    Data(T::Sort),
462}
463
464#[derive_where(PartialEq, Eq, Hash, Clone, Debug)]
465pub enum Pred<T: Types> {
466    KVar(T::KVar, Vec<Expr<T>>),
467    Expr(Expr<T>),
468}
469
470impl<T: Types> Pred<T> {
471    pub const TRUE: Self = Pred::Expr(Expr::TRUE);
472
473    pub fn is_trivially_true(&self) -> bool {
474        match self {
475            Pred::Expr(e) => e.is_trivially_true(),
476            Pred::KVar(..) => false,
477        }
478    }
479
480    pub fn is_concrete(&self) -> bool {
481        matches!(self, Pred::Expr(_))
482    }
483
484    // Give all the weak kvars that appear as part of a top-level conjunction.
485    // This is a syntactic analysis.
486    pub fn wkvars_in_conj(&self) -> Vec<WKVar<T>> {
487        match self {
488            Pred::Expr(e) => e.wkvars_in_conj(),
489            Pred::KVar(..) => vec![],
490        }
491    }
492
493    /// Remove all wkvars, replacing them with true.
494    pub fn strip_wkvars(&self) -> Self {
495        match self {
496            Pred::Expr(e) => Pred::Expr(e.strip_wkvars()),
497            Pred::KVar(..) => self.clone(),
498        }
499    }
500}
501
502#[derive(Hash, Debug, Copy, Clone, PartialEq, Eq)]
503pub enum BinRel {
504    Eq,
505    Ne,
506    Gt,
507    Ge,
508    Lt,
509    Le,
510}
511
512impl BinRel {
513    pub const INEQUALITIES: [BinRel; 4] = [BinRel::Gt, BinRel::Ge, BinRel::Lt, BinRel::Le];
514}
515
516#[derive_where(PartialEq, Eq, Hash, Debug, Clone)]
517pub struct WKVar<T: Types> {
518    pub wkvid: T::Var,
519    pub args: Vec<Expr<T>>,
520}
521
522#[derive_where(PartialEq, Eq, Hash, Clone, Debug)]
523pub enum Expr<T: Types> {
524    Constant(Constant<T>),
525    Var(T::Var),
526    App(Box<Self>, Option<Vec<Sort<T>>>, Vec<Self>, Option<Sort<T>>),
527    Neg(Box<Self>),
528    BinaryOp(BinOp, Box<[Self; 2]>),
529    IfThenElse(Box<[Self; 3]>),
530    And(Vec<Self>),
531    Or(Vec<Self>),
532    Not(Box<Self>),
533    Imp(Box<[Self; 2]>),
534    Iff(Box<[Self; 2]>),
535    Atom(BinRel, Box<[Self; 2]>),
536    Let(T::Var, Box<[Self; 2]>),
537    ThyFunc(ThyFunc),
538    IsCtor(T::Var, Box<Self>),
539    Quantifier(Quantifier, Vec<(T::Var, Sort<T>)>, Box<Self>),
540    /// NOTE: WKVars are for internal use and we serialize them as UIFs using
541    /// the var argument. We also don't emit WKVars that are in head position
542    /// when translating to fixpoint.
543    ///
544    /// In an ideal world fixpoint would deal with weak kvars itself.
545    WKVar(WKVar<T>),
546}
547
548impl<T: Types> From<Constant<T>> for Expr<T> {
549    fn from(v: Constant<T>) -> Self {
550        Self::Constant(v)
551    }
552}
553
554impl<T: Types> Expr<T> {
555    pub const FALSE: Self = Expr::Constant(Constant::Boolean(false));
556    pub const TRUE: Self = Expr::Constant(Constant::Boolean(true));
557
558    pub const fn int(val: u128) -> Expr<T> {
559        Expr::Constant(Constant::Numeral(val))
560    }
561
562    pub fn eq(self, other: Self) -> Self {
563        Expr::Atom(BinRel::Eq, Box::new([self, other]))
564    }
565
566    pub fn and(mut exprs: Vec<Self>) -> Self {
567        if exprs.len() == 1 { exprs.remove(0) } else { Self::And(exprs) }
568    }
569
570    pub fn var_sorts_to_int(&mut self) {
571        match self {
572            Expr::Constant(_) | Expr::ThyFunc(_) | Expr::Var(_) => {}
573            Expr::App(func, sort_args, args, out_sort) => {
574                func.var_sorts_to_int();
575                for arg in args {
576                    arg.var_sorts_to_int();
577                }
578                if let Some(sort_args) = sort_args {
579                    for sort_arg in sort_args {
580                        sort_arg.free_var_sorts_to_int();
581                    }
582                }
583                if let Some(out_sort) = out_sort {
584                    out_sort.free_var_sorts_to_int();
585                }
586            }
587            Expr::Neg(e) | Expr::Not(e) => {
588                e.var_sorts_to_int();
589            }
590            Expr::BinaryOp(_, exprs)
591            | Expr::Imp(exprs)
592            | Expr::Iff(exprs)
593            | Expr::Atom(_, exprs) => {
594                let [e1, e2] = &mut **exprs;
595                e1.var_sorts_to_int();
596                e2.var_sorts_to_int();
597            }
598            Expr::IfThenElse(exprs) => {
599                let [p, e1, e2] = &mut **exprs;
600                p.var_sorts_to_int();
601                e1.var_sorts_to_int();
602                e2.var_sorts_to_int();
603            }
604            Expr::And(exprs) | Expr::Or(exprs) => {
605                for e in exprs {
606                    e.var_sorts_to_int();
607                }
608            }
609            Expr::Let(_, exprs) => {
610                let [var_e, body_e] = &mut **exprs;
611                var_e.var_sorts_to_int();
612                body_e.var_sorts_to_int();
613            }
614            Expr::IsCtor(_v, expr) => {
615                expr.var_sorts_to_int();
616            }
617            Expr::Quantifier(_, binder, expr) => {
618                for (_, sort) in binder {
619                    sort.free_var_sorts_to_int();
620                }
621                expr.var_sorts_to_int();
622            }
623            Expr::WKVar(_) => {
624                unimplemented!()
625            }
626        }
627    }
628
629    pub fn free_vars(&self) -> FxIndexSet<T::Var> {
630        let mut vars = FxIndexSet::default();
631        match self {
632            Expr::Constant(_) | Expr::ThyFunc(_) => {}
633            Expr::Var(x) => {
634                vars.insert(x.clone());
635            }
636            Expr::App(func, _sort_args, args, _out_sort) => {
637                vars.extend(func.free_vars());
638                for arg in args {
639                    vars.extend(arg.free_vars());
640                }
641            }
642            Expr::Neg(e) | Expr::Not(e) => {
643                vars = e.free_vars();
644            }
645            Expr::BinaryOp(_, exprs)
646            | Expr::Imp(exprs)
647            | Expr::Iff(exprs)
648            | Expr::Atom(_, exprs) => {
649                let [e1, e2] = &**exprs;
650                vars.extend(e1.free_vars());
651                vars.extend(e2.free_vars());
652            }
653            Expr::IfThenElse(exprs) => {
654                let [p, e1, e2] = &**exprs;
655                vars.extend(p.free_vars());
656                vars.extend(e1.free_vars());
657                vars.extend(e2.free_vars());
658            }
659            Expr::And(exprs) | Expr::Or(exprs) => {
660                for e in exprs {
661                    vars.extend(e.free_vars());
662                }
663            }
664            Expr::Let(name, exprs) => {
665                // Fixpoint only support one binder per let expressions, but it parses a singleton
666                // list of binders to be forward-compatible
667                let [var_e, body_e] = &**exprs;
668                vars.extend(var_e.free_vars());
669                let mut body_vars = body_e.free_vars();
670                body_vars.swap_remove(name);
671                vars.extend(body_vars);
672            }
673            Expr::IsCtor(_v, expr) => {
674                // NOTE: (ck) I'm pretty sure this isn't a binder so I'm not going to
675                // bother with `v`.
676                vars.extend(expr.free_vars());
677            }
678            Expr::Quantifier(_, binder, expr) => {
679                let mut inner = expr.free_vars();
680                for (var, _sort) in binder {
681                    inner.swap_remove(var);
682                }
683                vars.extend(inner);
684            }
685            Expr::WKVar(WKVar { wkvid: _, args }) => {
686                for e in args {
687                    vars.extend(e.free_vars());
688                }
689            }
690        };
691        vars
692    }
693
694    pub fn is_trivially_true(&self) -> bool {
695        matches!(self, Expr::Constant(Constant::Boolean(true)))
696    }
697
698    pub fn substitute(&self, subst: &FxIndexMap<T::Var, Self>) -> Self {
699        match self {
700            Expr::Var(v) => subst.get(v).cloned().unwrap_or_else(|| self.clone()),
701            Expr::Constant(_) | Expr::ThyFunc(_) => self.clone(),
702            Expr::App(expr, sort_args, exprs, out_sort) => {
703                Expr::App(
704                    Box::new(expr.substitute(subst)),
705                    sort_args.clone(),
706                    exprs.iter().map(|e| e.substitute(subst)).collect_vec(),
707                    out_sort.clone(),
708                )
709            }
710            Expr::Neg(expr) => Expr::Neg(Box::new(expr.substitute(subst))),
711            Expr::BinaryOp(bin_op, args) => {
712                Expr::BinaryOp(
713                    *bin_op,
714                    Box::new([args[0].substitute(subst), args[1].substitute(subst)]),
715                )
716            }
717            Expr::IfThenElse(args) => {
718                Expr::IfThenElse(Box::new([
719                    args[0].substitute(subst),
720                    args[1].substitute(subst),
721                    args[2].substitute(subst),
722                ]))
723            }
724            Expr::And(exprs) => Expr::And(exprs.iter().map(|e| e.substitute(subst)).collect_vec()),
725            Expr::Or(exprs) => Expr::Or(exprs.iter().map(|e| e.substitute(subst)).collect_vec()),
726            Expr::Not(expr) => Expr::Not(Box::new(expr.substitute(subst))),
727            Expr::Imp(args) => {
728                Expr::Imp(Box::new([args[0].substitute(subst), args[1].substitute(subst)]))
729            }
730            Expr::Iff(args) => {
731                Expr::Iff(Box::new([args[0].substitute(subst), args[1].substitute(subst)]))
732            }
733            Expr::Atom(bin_rel, args) => {
734                Expr::Atom(
735                    *bin_rel,
736                    Box::new([args[0].substitute(subst), args[1].substitute(subst)]),
737                )
738            }
739            Expr::Let(var, args) => {
740                Expr::Let(
741                    var.clone(),
742                    Box::new([args[0].substitute(subst), args[1].substitute(subst)]),
743                )
744            }
745            Expr::IsCtor(var, expr) => Expr::IsCtor(var.clone(), Box::new(expr.substitute(subst))),
746            Expr::Quantifier(q, sorts, expr) => {
747                Expr::Quantifier(*q, sorts.clone(), Box::new(expr.substitute(subst)))
748            }
749            Expr::WKVar(WKVar { wkvid, args }) => {
750                Expr::WKVar(WKVar {
751                    wkvid: wkvid.clone(),
752                    args: args.iter().map(|e| e.substitute(subst)).collect_vec(),
753                })
754            }
755        }
756    }
757
758    // Give all the weak kvars that appear as part of a top-level conjunction.
759    // This is a syntactic analysis.
760    pub fn wkvars_in_conj(&self) -> Vec<WKVar<T>> {
761        match self {
762            Expr::WKVar(wkvar) => vec![wkvar.clone()],
763            Expr::And(conj) => conj.iter().flat_map(|e| e.wkvars_in_conj()).collect(),
764            _ => vec![],
765        }
766    }
767
768    pub fn uncurry(&self) -> Self {
769        match self {
770            Expr::App(head, sort_args, args, out_sort) => {
771                let uncurried_head = head.uncurry();
772                let uncurried_args = args.iter().map(Expr::uncurry).collect_vec();
773                match uncurried_head {
774                    Expr::App(head_head, head_sort_args, mut head_args, _) => {
775                        head_args.extend(uncurried_args);
776                        let sort_args = match (head_sort_args, sort_args) {
777                            (Some(mut head_sort_args), Some(sort_args)) => {
778                                head_sort_args.extend(sort_args.clone());
779                                Some(head_sort_args)
780                            }
781                            _ => None,
782                        };
783                        Expr::App(head_head, sort_args, head_args, out_sort.clone())
784                    }
785                    Expr::WKVar(WKVar { wkvid, args: mut wkvar_args }) => {
786                        wkvar_args.extend(uncurried_args);
787                        Expr::WKVar(WKVar { wkvid, args: wkvar_args })
788                    }
789                    head => {
790                        Expr::App(
791                            Box::new(head),
792                            sort_args.clone(),
793                            uncurried_args,
794                            out_sort.clone(),
795                        )
796                    }
797                }
798            }
799            Expr::Constant(_) | Expr::Var(_) | Expr::ThyFunc(_) => self.clone(),
800            Expr::Neg(expr) => Expr::Neg(Box::new(expr.uncurry())),
801            Expr::BinaryOp(bin_op, args) => {
802                Expr::BinaryOp(*bin_op, Box::new([args[0].uncurry(), args[1].uncurry()]))
803            }
804            Expr::IfThenElse(args) => {
805                Expr::IfThenElse(Box::new([
806                    args[0].uncurry(),
807                    args[1].uncurry(),
808                    args[2].uncurry(),
809                ]))
810            }
811            Expr::And(exprs) => Expr::And(exprs.iter().map(Expr::uncurry).collect_vec()),
812            Expr::Or(exprs) => Expr::Or(exprs.iter().map(Expr::uncurry).collect_vec()),
813            Expr::Not(expr) => Expr::Not(Box::new(expr.uncurry())),
814            Expr::Imp(args) => Expr::Imp(Box::new([args[0].uncurry(), args[1].uncurry()])),
815            Expr::Iff(args) => Expr::Iff(Box::new([args[0].uncurry(), args[1].uncurry()])),
816            Expr::Atom(bin_rel, args) => {
817                Expr::Atom(*bin_rel, Box::new([args[0].uncurry(), args[1].uncurry()]))
818            }
819            Expr::Let(var, args) => {
820                Expr::Let(var.clone(), Box::new([args[0].uncurry(), args[1].uncurry()]))
821            }
822            Expr::IsCtor(var, expr) => Expr::IsCtor(var.clone(), Box::new(expr.uncurry())),
823            Expr::Quantifier(q, sorts, expr) => {
824                Expr::Quantifier(*q, sorts.clone(), Box::new(expr.uncurry()))
825            }
826            Expr::WKVar(WKVar { wkvid, args }) => {
827                Expr::WKVar(WKVar {
828                    wkvid: wkvid.clone(),
829                    args: args.iter().map(|e| e.uncurry()).collect_vec(),
830                })
831            }
832        }
833    }
834
835    pub fn has_wkvar_reachable_by_split(&self) -> bool {
836        if !matches!(self, Expr::Quantifier(Quantifier::Exists, ..) | Expr::Or(..) | Expr::And(..))
837        {
838            return false;
839        }
840        match self {
841            Expr::Or(exprs) => {
842                exprs.iter().any(|expr| {
843                    matches!(expr, Expr::WKVar(..)) || expr.has_wkvar_reachable_by_split()
844                })
845            }
846            Expr::Quantifier(Quantifier::Exists, _sorts, expr) => {
847                !expr.wkvars_in_conj().is_empty() || expr.has_wkvar_reachable_by_split()
848            }
849            Expr::And(exprs) => exprs.iter().any(Expr::has_wkvar_reachable_by_split),
850            _ => false,
851        }
852    }
853
854    pub fn hoist_exists(&self) -> (Vec<VarSorts<T>>, Expr<T>) {
855        match self {
856            Expr::Quantifier(Quantifier::Exists, var_sorts, inner_e) => {
857                let mut vars = var_sorts.clone();
858                let (new_vars, hoisted_inner) = inner_e.hoist_exists();
859                vars.extend(new_vars);
860                (vars, hoisted_inner)
861            }
862            Expr::And(exprs) => {
863                let mut vars = vec![];
864                let expr = Expr::And(
865                    exprs
866                        .iter()
867                        .flat_map(|expr| {
868                            let (new_vars, hoisted_expr) = expr.hoist_exists();
869                            vars.extend(new_vars);
870                            match hoisted_expr {
871                                Expr::And(exprs) => exprs,
872                                expr => vec![expr],
873                            }
874                        })
875                        .collect(),
876                );
877                (vars, expr)
878            }
879            _ => (vec![], self.clone()),
880        }
881    }
882
883    pub fn as_conjunction(self) -> Vec<Self> {
884        match self {
885            Expr::And(exprs) => exprs,
886            expr => vec![expr],
887        }
888    }
889
890    pub fn strip_wkvars(&self) -> Self {
891        match self {
892            Expr::Constant(_) | Expr::Var(_) | Expr::ThyFunc(_) => self.clone(),
893            Expr::WKVar(..) => Expr::TRUE,
894            Expr::App(head, sort_args, args, out_sort) => {
895                Expr::App(
896                    Box::new(head.strip_wkvars()),
897                    sort_args.clone(),
898                    args.iter().map(Expr::strip_wkvars).collect(),
899                    out_sort.clone(),
900                )
901            }
902            Expr::Neg(expr) => Expr::Neg(Box::new(expr.strip_wkvars())),
903            Expr::BinaryOp(bin_op, args) => {
904                Expr::BinaryOp(*bin_op, Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
905            }
906            Expr::IfThenElse(args) => {
907                Expr::IfThenElse(Box::new([
908                    args[0].strip_wkvars(),
909                    args[1].strip_wkvars(),
910                    args[2].strip_wkvars(),
911                ]))
912            }
913            Expr::And(exprs) => Expr::And(exprs.iter().map(Expr::strip_wkvars).collect_vec()),
914            Expr::Or(exprs) => Expr::Or(exprs.iter().map(Expr::strip_wkvars).collect_vec()),
915            Expr::Not(expr) => Expr::Not(Box::new(expr.strip_wkvars())),
916            Expr::Imp(args) => {
917                Expr::Imp(Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
918            }
919            Expr::Iff(args) => {
920                Expr::Iff(Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
921            }
922            Expr::Atom(bin_rel, args) => {
923                Expr::Atom(*bin_rel, Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
924            }
925            Expr::Let(var, args) => {
926                Expr::Let(var.clone(), Box::new([args[0].strip_wkvars(), args[1].strip_wkvars()]))
927            }
928            Expr::IsCtor(var, expr) => Expr::IsCtor(var.clone(), Box::new(expr.strip_wkvars())),
929            Expr::Quantifier(q, sorts, expr) => {
930                Expr::Quantifier(*q, sorts.clone(), Box::new(expr.strip_wkvars()))
931            }
932        }
933    }
934
935    pub fn total_num_disjuncts(&self) -> usize {
936        match self {
937            Expr::Or(disjuncts) => {
938                disjuncts.len()
939                    + disjuncts
940                        .iter()
941                        .map(Expr::total_num_disjuncts)
942                        .sum::<usize>()
943            }
944            Expr::And(conjuncts) => conjuncts.iter().map(Expr::total_num_disjuncts).sum(),
945            _ => 0,
946        }
947    }
948}
949
950#[derive_where(PartialEq, Eq, Hash, Clone, Debug)]
951pub enum Constant<T: Types> {
952    Numeral(u128),
953    Real(T::Real),
954    Boolean(bool),
955    String(T::String),
956    BitVec(u128, u32),
957}
958
959#[derive_where(Debug, Clone, Hash)]
960pub struct Qualifier<T: Types> {
961    pub name: String,
962    pub args: Vec<QualParam<T>>,
963    pub body: Expr<T>,
964}
965
966/// A fact that is asserted to the SMT solver, universally quantified over `args`.
967#[derive_where(Debug, Clone, Hash)]
968pub struct Axiom<T: Types> {
969    pub name: String,
970    pub args: Vec<(T::Var, Sort<T>)>,
971    pub body: Expr<T>,
972    /// The terms of the (multi-)pattern used by the SMT solver to instantiate the axiom, i.e.,
973    /// the axiom is instantiated when *all* of them are matched.
974    pub patterns: Vec<Expr<T>>,
975}
976
977#[derive_where(Debug, Clone, Hash)]
978pub struct QualParam<T: Types> {
979    pub name: T::Var,
980    pub sort: Sort<T>,
981    pub is_wildcard: bool,
982}
983
984impl<T: Types> QualParam<T> {
985    pub fn new(name: T::Var, sort: Sort<T>) -> Self {
986        Self { name, sort, is_wildcard: false }
987    }
988}
989
990#[derive(Clone, Copy, PartialEq, Eq, Hash)]
991pub enum BinOp {
992    Add,
993    Sub,
994    Mul,
995    Div,
996    Mod,
997}
998
999#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
1000pub enum Quantifier {
1001    Exists,
1002    Forall,
1003}