aws_smt_ir/
fold.rs

1//! Traits for transforming [`Term`]s into other terms.
2// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3// SPDX-License-Identifier: Apache-2.0
4use crate::{
5    Command, CoreOp, Ctx, FunctionDec, IConst, ICoreOp, IIndex, ILet, IMatch, IOp, IQuantifier,
6    ISort, ISymbol, IVar, Let, Logic, Quantifier, Sorted, Term, Void, IUF, UF,
7};
8pub use aws_smt_ir_derive::Fold;
9
10mod compose;
11pub use compose::{Compose, TryComposed};
12mod intra;
13pub use intra::IntraLogicFolder;
14mod inter;
15use crate::smt2parser::Numeral;
16pub use inter::InterLogicFolder;
17use smallvec::SmallVec;
18
19/// A `Folder<T>` is a type that can be used to transform SMT terms in logic `T` to some other type.
20/// For example, a folder could be used to partially evaluate a term or encode it into a simpler
21/// logic (e.g. bit-blasting bit-vectors). Useful folders will often preserve equisatisfiability,
22/// although this is not required.
23///
24/// **Note: When writing code generic over folders, be generic over `Folder<T, M>` instead of
25/// `Folder<T>`, as the latter only includes types that directly implement `Folder`. See
26/// [Emulating Specialization](#emulating-specialization) below for details.
27///
28/// [`IConst`], [`IVar`], [`CoreOp`], and [`IOp`] implement [`Fold`]; when [`Fold::fold_with`] is called
29/// with a folder, it will recursively fold all of the subexpressions contained within. For types
30/// that have corresponding hooks in [`Folder`] (e.g. [`Folder::fold_var`], [`Folder::fold_core_op`]),
31/// [`Fold::fold_with`] will call the hook, which should then recursively fold subexpressions -- this
32/// can be accomplished through a call to [`SuperFold::super_fold_with`].
33///
34/// # Emulating specialization
35///
36/// The [`IntraLogicFolder`] and [`InterLogicFolder`] traits are more specialized versions of
37/// [`Folder`], providing default implementations for more `fold_*` hooks where possible. These
38/// traits implement `Folder` through a blanket impl, which would produce conflicting trait
39/// impls because [specialization](https://rust-lang.github.io/rfcs/1210-impl-specialization.html)
40/// is not implemented. To work around this until some form of specialization lands, `Folder` has
41/// a parameter `M` that acts as a *marker* of which `Folder`-family trait was directly implemented.
42/// Types that directly implement `Folder` have a marker of `()`, while types that indirectly
43/// implement it through a more specialized trait use marker types corresponding to that trait.
44/// This makes the blanket impls produce distinct implementations `Folder<_, A>`, `Folder<_, B>`, etc.
45///
46/// # Examples
47///
48/// ### Partially evaluating `not` operations
49///
50/// ```
51/// # fn main() {
52/// use aws_smt_ir::fold::{Fold, Folder, SuperFold};
53/// use aws_smt_ir::{CoreOp, ICoreOp, IVar, IUF, IOp, Term, ILet, IMatch, Logic, Void, IConst, IQuantifier};
54///
55/// struct PartiallyEvaluateNot;
56///
57/// impl<T: Logic> Folder<T> for PartiallyEvaluateNot {
58///     type Output = Term<T>;
59///     type Error = ();
60///
61///     fn fold_const(&mut self, constant: IConst) -> Result<Self::Output, Self::Error> {
62///         Ok(constant.into())
63///     }
64///
65///     fn fold_var(&mut self, var: IVar<T::Var>) -> Result<Self::Output, Self::Error> {
66///         Ok(var.into())
67///     }
68///
69///     fn fold_core_op(&mut self, op: ICoreOp<T>) -> Result<Self::Output, Self::Error> {
70///         // Recursively fold arguments, then check if there's a `not` we can evaluate
71///         Ok(match op.super_fold_with(self)? {
72///             CoreOp::Not(Term::CoreOp(op)) if matches!(*op, CoreOp::True) => false.into(),
73///             CoreOp::Not(Term::CoreOp(op)) if matches!(*op, CoreOp::False) => true.into(),
74///             op => op.into(),
75///         })
76///     }
77///
78///     fn fold_theory_op(&mut self, op: IOp<T>) -> Result<Self::Output, Self::Error> {
79///         op.super_fold_with(self).map(Into::into)
80///     }
81///
82///     fn fold_uninterpreted_func(&mut self, uf: IUF<T>) -> Result<Self::Output, Self::Error> {
83///         uf.super_fold_with(self).map(Into::into)
84///     }
85///
86///     fn fold_let(&mut self, l: ILet<T>) -> Result<Self::Output, Self::Error> {
87///         l.super_fold_with(self).map(Into::into)
88///     }
89///
90///     fn fold_match(&mut self, m: IMatch<T>) -> Result<Self::Output, Self::Error> {
91///         m.super_fold_with(self).map(Into::into)
92///     }
93///
94///     fn fold_quantifier(&mut self, quantifier: IQuantifier<T>) -> Result<Self::Output, Self::Error> {
95///         quantifier.super_fold_with(self).map(Into::into)
96///     }
97/// }
98///
99/// let f1: Term = !Term::CoreOp(true.into());
100/// let f2 = f1.clone() & !Term::CoreOp(false.into());
101/// let mut folder = PartiallyEvaluateNot;
102/// assert_eq!(f1.fold_with(&mut folder), Ok(false.into()));
103/// assert_eq!(f2.fold_with(&mut folder), Ok(CoreOp::And([false.into(), true.into()].into()).into()));
104/// # }
105/// ```
106pub trait Folder<T: Logic, M = ()>: Compose {
107    /// Type produced by the transformation.
108    /// If `Output = Term<T>`, implement [`IntraLogicFolder`] instead.
109    /// If `Output = Term<U>` for some `U: Logic`, implement [`InterLogicFolder`] instead.
110    type Output;
111
112    /// Type of errors that the folder may run into.
113    type Error;
114
115    /// Gets the context tracked by the folder, if applicable.
116    /// Tracking context enables things like determining the sorts of expressions and which
117    /// variables are free/bound, but a folder that doesn't need that information can leave
118    /// this method defaulted, which will return `None`.
119    fn context(&self) -> Option<&Ctx> {
120        None
121    }
122
123    /// Gets a mutable reference to the context tracked by the folder, if applicable.
124    /// Tracking context enables things like determining the sorts of expressions and which
125    /// variables are free/bound, but a folder that doesn't need that information can leave
126    /// this method defaulted, which will return `None`.
127    fn context_mut(&mut self) -> Option<&mut Ctx> {
128        debug_assert!(
129            self.context().is_none(),
130            "context() and context_mut() should be implemented together"
131        );
132        None
133    }
134
135    /// Transforms a term, recursively transforming its contents.
136    /// By default, calls [`SuperFold::super_fold_with`].
137    fn fold_term(&mut self, term: Term<T>) -> Result<Self::Output, Self::Error> {
138        term.super_fold_with(self)
139    }
140
141    /// Transforms a constant.
142    fn fold_const(&mut self, constant: IConst) -> Result<Self::Output, Self::Error>;
143
144    /// Transforms a variable.
145    fn fold_var(&mut self, var: IVar<T::Var>) -> Result<Self::Output, Self::Error>;
146
147    /// Transforms an SMT-LIB Core operation, recursively folding its arguments.
148    fn fold_core_op(&mut self, op: ICoreOp<T>) -> Result<Self::Output, Self::Error>;
149
150    /// Transforms a non-core operation, recursively folding its arguments.
151    fn fold_theory_op(&mut self, op: IOp<T>) -> Result<Self::Output, Self::Error>;
152
153    /// Transforms an uninterpreted function, recursively folding its arguments.
154    fn fold_uninterpreted_func(&mut self, func: IUF<T>) -> Result<Self::Output, Self::Error>;
155
156    /// Transforms a let binder, recursively folding its arguments.
157    fn fold_let(&mut self, l: ILet<T>) -> Result<Self::Output, Self::Error>;
158
159    /// Transforms a match binder, recursively folding its arguments.
160    fn fold_match(&mut self, m: IMatch<T>) -> Result<Self::Output, Self::Error>;
161
162    /// Transforms a quantifier, recursively folding its arguments.
163    fn fold_quantifier(&mut self, quantifier: IQuantifier<T>) -> Result<Self::Output, Self::Error>;
164
165    /// Transforms a set-logic command.
166    /// By default, returns as-is.
167    fn fold_set_logic(&mut self, logic: ISymbol) -> Result<Command<Self::Output>, Self::Error> {
168        Ok(Command::SetLogic { symbol: logic })
169    }
170
171    /// Transforms an asserted term.
172    /// By default, calls [`Self::fold_term`].
173    fn fold_assert(&mut self, asserted: Term<T>) -> Result<Command<Self::Output>, Self::Error> {
174        self.fold_term(asserted)
175            .map(|term| Command::Assert { term })
176    }
177
178    /// Transforms a constant declaration.
179    /// By default, returns as-is.
180    fn fold_declare_const(
181        &mut self,
182        symbol: ISymbol,
183        sort: ISort,
184    ) -> Result<Command<Self::Output>, Self::Error> {
185        Ok(Command::DeclareConst { symbol, sort })
186    }
187
188    /// Transforms a function declaration.
189    /// By default, returns as-is.
190    fn fold_declare_fun(
191        &mut self,
192        symbol: ISymbol,
193        parameters: Vec<ISort>,
194        sort: ISort,
195    ) -> Result<Command<Self::Output>, Self::Error> {
196        Ok(Command::DeclareFun {
197            symbol,
198            parameters,
199            sort,
200        })
201    }
202
203    /// Transforms a sort declaration.
204    /// By default, returns as-is.
205    fn fold_declare_sort(
206        &mut self,
207        symbol: ISymbol,
208        arity: Numeral,
209    ) -> Result<Command<Self::Output>, Self::Error> {
210        Ok(Command::DeclareSort { symbol, arity })
211    }
212
213    /// Transforms a function definition.
214    /// By default, recursively transforms the function body with [`Self::fold_term`].
215    fn fold_define_fun(
216        &mut self,
217        sig: FunctionDec,
218        body: Term<T>,
219    ) -> Result<Command<Self::Output>, Self::Error> {
220        let term = body.fold_with(self)?;
221        Ok(Command::DefineFun { sig, term })
222    }
223
224    /// Transforms a recursive function definition.
225    /// By default, recursively transforms the function body with [`Self::fold_term`].
226    fn fold_define_fun_rec(
227        &mut self,
228        sig: FunctionDec,
229        body: Term<T>,
230    ) -> Result<Command<Self::Output>, Self::Error> {
231        let term = body.fold_with(self)?;
232        Ok(Command::DefineFunRec { sig, term })
233    }
234
235    /// Transforms a set of recursive function definition.
236    /// By default, recursively transforms each function body with [`Self::fold_term`].
237    fn fold_define_funs_rec(
238        &mut self,
239        funs: Vec<(FunctionDec, Term<T>)>,
240    ) -> Result<Command<Self::Output>, Self::Error> {
241        let funs = funs
242            .into_iter()
243            .map(|(sig, body)| Ok((sig, body.fold_with(self)?)))
244            .collect::<Result<_, _>>()?;
245        Ok(Command::DefineFunsRec { funs })
246    }
247
248    /// Transforms a sort definition.
249    /// By default, returns as-is.
250    fn fold_define_sort(
251        &mut self,
252        symbol: ISymbol,
253        parameters: Vec<ISymbol>,
254        sort: ISort,
255    ) -> Result<Command<Self::Output>, Self::Error> {
256        Ok(Command::DefineSort {
257            symbol,
258            parameters,
259            sort,
260        })
261    }
262
263    /// Transforms a `get-value` call.
264    /// By default, recursively transforms each term with [`Self::fold_term`].
265    fn fold_get_value(
266        &mut self,
267        terms: Vec<Term<T>>,
268    ) -> Result<Command<Self::Output>, Self::Error> {
269        let terms = terms.fold_with(self)?;
270        Ok(Command::GetValue { terms })
271    }
272}
273
274/// `Fold` implements the logic to recursively fold the contents of a type using a folder (see [`Folder`]).
275/// This should usually not be implemented manually; in particular, types used as operations
276/// (i.e. the `T::Op` of a `T: Logic`) should derive `Fold` using the [`Fold`] derive macro.
277///
278/// # Examples
279///
280/// ## Deriving `Fold` for a simple enum.
281///
282/// ```
283/// # fn main() {
284/// use aws_smt_ir::{fold::Fold, Term, Logic, Operation};
285///
286/// #[derive(Operation, Fold)]
287/// enum AddOrSubtract<Term> {
288///     Add(Vec<Term>),
289///     Subtract(Term, Term),
290/// }
291/// # }
292/// ```
293pub trait Fold<T: Logic, Out> {
294    /// Type output by the transformation.
295    type Output;
296
297    /// Transforms `self` using `folder`.
298    ///
299    /// If type inference is failing because it can't figure out which `T` to use, use
300    /// [`Logic::fold`] to resolve the ambiguity.
301    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
302    where
303        F: Folder<T, M, Output = Out>;
304}
305
306/// `SuperFold` implements the logic to recursively fold the contents of types that have hooks in
307/// [`Folder`] using a folder (see [`Folder`]).
308/// This should usually not be implemented manually; in particular, types used as operations
309/// (i.e. the `T::Op` of a `T: Logic`) should derive [`SuperFold`] using the [`Fold`] derive macro.
310pub trait SuperFold<T: Logic, Out> {
311    /// Type output by the transformation, e.g. `Foo<Out>` for an implementation for `Foo<U: Fold<T, Out>>`.
312    type Output;
313
314    /// Recursively transforms the contents of `self` using `folder.`
315    fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
316    where
317        F: Folder<T, M, Output = Out>;
318}
319
320impl<T: Logic, Out> Fold<T, Out> for Term<T> {
321    type Output = Out;
322    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
323    where
324        F: Folder<T, M, Output = Out>,
325    {
326        folder.fold_term(self)
327    }
328}
329
330impl<T: Logic, Out> Fold<T, Out> for &Term<T> {
331    type Output = Out;
332    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
333    where
334        F: Folder<T, M, Output = Out>,
335    {
336        folder.fold_term(self.clone())
337    }
338}
339
340impl<T: Logic, Out> SuperFold<T, Out> for Term<T> {
341    type Output = Out;
342
343    fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
344    where
345        F: Folder<T, M, Output = Out>,
346    {
347        match self {
348            Self::Constant(c) => c.fold_with(folder),
349            Self::Variable(v) => v.fold_with(folder),
350            Self::CoreOp(op) => op.fold_with(folder),
351            Self::OtherOp(op) => op.fold_with(folder),
352            Self::UF(uf) => uf.fold_with(folder),
353            Self::Let(l) => l.fold_with(folder),
354            Self::Match(m) => m.fold_with(folder),
355            Self::Quantifier(q) => q.fold_with(folder),
356        }
357    }
358}
359
360impl<T: Logic, Out> SuperFold<T, Out> for &Term<T> {
361    type Output = Out;
362
363    fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
364    where
365        F: Folder<T, M, Output = Out>,
366    {
367        self.clone().super_fold_with(folder)
368    }
369}
370
371impl<T: Logic, Out> Fold<T, Out> for IConst {
372    type Output = Out;
373    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
374    where
375        F: Folder<T, M, Output = Out>,
376    {
377        folder.fold_const(self)
378    }
379}
380
381impl<T: Logic, Out> Fold<T, Out> for IVar<T::Var> {
382    type Output = Out;
383    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
384    where
385        F: Folder<T, M, Output = Out>,
386    {
387        folder.fold_var(self)
388    }
389}
390
391// TODO: extend `Fold` derive to work for `CoreOp`
392impl<T: Logic, Out> Fold<T, Out> for ICoreOp<T> {
393    type Output = Out;
394    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
395    where
396        F: Folder<T, M, Output = Out>,
397    {
398        folder.fold_core_op(self)
399    }
400}
401
402// TODO: extend `Fold` derive to work for `CoreOp`
403impl<T: Logic, Inner: Fold<T, Out>, Out> SuperFold<T, Out> for CoreOp<Inner> {
404    type Output = CoreOp<Inner::Output>;
405    fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
406    where
407        F: Folder<T, M, Output = Out>,
408    {
409        use CoreOp::*;
410        const STACK_RED_ZONE: usize = 32 * 1024;
411        const NEW_STACK_SIZE: usize = 1024 * 1024;
412        let op = match self {
413            True => True,
414            False => False,
415            Not(t) => Not(t.fold_with(folder)?),
416            And(args) => And(stacker::maybe_grow(STACK_RED_ZONE, NEW_STACK_SIZE, || {
417                args.fold_with(folder)
418            })?),
419            Or(args) => Or(stacker::maybe_grow(STACK_RED_ZONE, NEW_STACK_SIZE, || {
420                args.fold_with(folder)
421            })?),
422            Xor(args) => Xor(args.fold_with(folder)?),
423            Imp(args) => Imp(args.fold_with(folder)?),
424            Eq(args) => Eq(args.fold_with(folder)?),
425            Distinct(args) => Distinct(args.fold_with(folder)?),
426            Ite(i, t, e) => Ite(
427                i.fold_with(folder)?,
428                t.fold_with(folder)?,
429                e.fold_with(folder)?,
430            ),
431        };
432        Ok(op)
433    }
434}
435
436impl<L: Logic, Out> Fold<L, Out> for IOp<L> {
437    type Output = Out;
438    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
439    where
440        F: Folder<L, M, Output = Out>,
441    {
442        folder.fold_theory_op(self)
443    }
444}
445
446impl<T: Logic, Out> Fold<T, Out> for IUF<T> {
447    type Output = Out;
448    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
449    where
450        F: Folder<T, M, Output = Out>,
451    {
452        folder.fold_uninterpreted_func(self)
453    }
454}
455
456impl<T: Logic, Inner, Out> SuperFold<T, Out> for UF<Inner>
457where
458    Inner: Fold<T, Out>,
459{
460    type Output = UF<Inner::Output>;
461    fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
462    where
463        F: Folder<T, M, Output = Out>,
464    {
465        let (func, args) = (self.func.fold_with(folder)?, self.args.fold_with(folder)?);
466        Ok(UF { func, args })
467    }
468}
469
470impl<T: Logic, Out> Fold<T, Out> for IQuantifier<T> {
471    type Output = Out;
472    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
473    where
474        F: Folder<T, M, Output = Out>,
475    {
476        folder.fold_quantifier(self)
477    }
478}
479
480impl<T: Logic, Inner, Out> SuperFold<T, Out> for Quantifier<Inner>
481where
482    Inner: Fold<T, Out>,
483{
484    type Output = Quantifier<Inner::Output>;
485    fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
486    where
487        F: Folder<T, M, Output = Out>,
488    {
489        use Quantifier::*;
490        Ok(match self {
491            Forall(vars, t) => Forall(vars.fold_with(folder)?, t.fold_with(folder)?),
492            Exists(vars, t) => Exists(vars.fold_with(folder)?, t.fold_with(folder)?),
493        })
494    }
495}
496
497impl<T: Logic, Out> Fold<T, Out> for ILet<T> {
498    type Output = Out;
499
500    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
501    where
502        F: Folder<T, M, Output = Out>,
503    {
504        let old_bindings = if let Some(ctx) = folder.context_mut() {
505            self.bindings
506                .iter()
507                .map(|(sym, t)| {
508                    let sort = t.sort(ctx).ok();
509                    let old_sort = ctx.local.bind_var(sym.clone(), sort);
510                    (sym.clone(), old_sort)
511                })
512                .collect()
513        } else {
514            vec![]
515        };
516        let res = folder.fold_let(self);
517        if let Some(ctx) = folder.context_mut() {
518            for (sym, old_sort) in old_bindings {
519                match old_sort {
520                    None => ctx.local.unbind_var(&sym),
521                    Some(sort) => {
522                        ctx.local.bind_var(sym, sort);
523                    }
524                }
525            }
526        }
527        res
528    }
529}
530
531impl<T: Logic, Inner: Fold<T, Out>, Out> SuperFold<T, Out> for Let<Inner> {
532    type Output = Let<Inner::Output>;
533
534    fn super_fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
535    where
536        F: Folder<T, M, Output = Out>,
537    {
538        Ok(Let {
539            bindings: self.bindings.fold_with(folder)?,
540            term: self.term.fold_with(folder)?,
541        })
542    }
543}
544
545impl<T: Logic, Out> Fold<T, Out> for Void {
546    type Output = Out;
547    fn fold_with<F: Folder<T, M>, M>(self, _folder: &mut F) -> Result<Self::Output, F::Error> {
548        match self {}
549    }
550}
551
552impl<T: Logic, Out> SuperFold<T, Out> for Void {
553    type Output = Void;
554    fn super_fold_with<F: Folder<T, M>, M>(
555        self,
556        _folder: &mut F,
557    ) -> Result<Self::Output, F::Error> {
558        match self {}
559    }
560}
561
562impl<T: Logic, Out> Fold<T, Out> for ISymbol {
563    type Output = Self;
564    fn fold_with<F, M>(self, _: &mut F) -> Result<Self::Output, F::Error>
565    where
566        F: Folder<T, M, Output = Out>,
567    {
568        Ok(self)
569    }
570}
571
572impl<T: Logic, Out> Fold<T, Out> for ISort {
573    type Output = Self;
574    fn fold_with<F, M>(self, _: &mut F) -> Result<Self::Output, F::Error>
575    where
576        F: Folder<T, M, Output = Out>,
577    {
578        Ok(self)
579    }
580}
581
582impl<T: Logic, Out, Inner: Fold<T, Out>> Fold<T, Out> for Vec<Inner> {
583    type Output = Vec<<Inner as Fold<T, Out>>::Output>;
584    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
585    where
586        F: Folder<T, M, Output = Out>,
587    {
588        self.into_iter().map(|t| t.fold_with(folder)).collect()
589    }
590}
591
592impl<T: Logic, Out, Inner: Fold<T, Out>, const N: usize> Fold<T, Out> for SmallVec<[Inner; N]>
593where
594    [Inner; N]: smallvec::Array<Item = Inner>,
595    [Inner::Output; N]: smallvec::Array<Item = Inner::Output>,
596{
597    type Output = SmallVec<[Inner::Output; N]>;
598    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
599    where
600        F: Folder<T, M, Output = Out>,
601    {
602        self.into_iter().map(|t| t.fold_with(folder)).collect()
603    }
604}
605
606impl<'a, T: Logic, Out, Inner, Output, const N: usize> Fold<T, Out> for &'a SmallVec<[Inner; N]>
607where
608    &'a Inner: Fold<T, Out, Output = Output>,
609    [Inner; N]: smallvec::Array<Item = Inner>,
610    [Output; N]: smallvec::Array<Item = Output>,
611{
612    type Output = SmallVec<[Output; N]>;
613    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
614    where
615        F: Folder<T, M, Output = Out>,
616    {
617        self.iter().map(|t| t.fold_with(folder)).collect()
618    }
619}
620
621impl<T: Logic, Out, Inner: Fold<T, Out>> Fold<T, Out> for Box<[Inner]> {
622    type Output = Box<[<Inner as Fold<T, Out>>::Output]>;
623    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
624    where
625        F: Folder<T, M, Output = Out>,
626    {
627        self.into_vec()
628            .into_iter()
629            .map(|t| t.fold_with(folder))
630            .collect()
631    }
632}
633
634impl<T: Logic, Out, const N: usize> Fold<T, Out> for [IIndex; N] {
635    type Output = Self;
636    fn fold_with<F, M>(self, _: &mut F) -> Result<Self::Output, F::Error>
637    where
638        F: Folder<T, M, Output = Out>,
639    {
640        Ok(self)
641    }
642}
643
644impl<T: Logic, Out, Inner, Output> Fold<T, Out> for &[Inner]
645where
646    for<'a> &'a Inner: Fold<T, Out, Output = Output>,
647{
648    type Output = Vec<Output>;
649    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
650    where
651        F: Folder<T, M, Output = Out>,
652    {
653        self.iter().map(|t| t.fold_with(folder)).collect()
654    }
655}
656
657impl<T: Logic, Out, Inner, Output> Fold<T, Out> for &Vec<Inner>
658where
659    for<'a> &'a Inner: Fold<T, Out, Output = Output>,
660{
661    type Output = Vec<Output>;
662    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
663    where
664        F: Folder<T, M, Output = Out>,
665    {
666        self.iter().map(|t| t.fold_with(folder)).collect()
667    }
668}
669
670macro_rules! impl_fold_tuple {
671    ($($x:ident),*) => {
672        impl<T: Logic, Out, $($x),*> Fold<T, Out> for ($($x),*)
673        where
674            $($x: Fold<T, Out>),*
675        {
676            type Output = ($($x::Output),*);
677            fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
678            where
679                F: Folder<T, M, Output = Out>,
680            {
681                #[allow(non_snake_case)]
682                let ($($x),*) = self;
683                Ok(($($x.fold_with(folder)?),*))
684            }
685        }
686    };
687}
688
689impl_fold_tuple!(A, B);
690impl_fold_tuple!(A, B, C);
691impl_fold_tuple!(A, B, C, D);
692impl_fold_tuple!(A, B, C, D, E);
693
694impl<L: Logic, Out> Fold<L, Out> for Command<Term<L>> {
695    type Output = Command<Out>;
696
697    fn fold_with<F, M>(self, folder: &mut F) -> Result<Self::Output, F::Error>
698    where
699        F: Folder<L, M, Output = Out>,
700    {
701        use crate::smt2parser::concrete::Command::*;
702        if let Some(ctx) = folder.context_mut() {
703            ctx.process(&self);
704        }
705        let command = match self {
706            Assert { term } => folder.fold_assert(term)?,
707            CheckSat => CheckSat,
708            CheckSatAssuming { literals } => CheckSatAssuming { literals },
709            DeclareConst { symbol, sort } => folder.fold_declare_const(symbol, sort)?,
710            DeclareDatatype { symbol, datatype } => DeclareDatatype { symbol, datatype },
711            DeclareDatatypes { datatypes } => DeclareDatatypes { datatypes },
712            DeclareFun {
713                symbol,
714                parameters,
715                sort,
716            } => folder.fold_declare_fun(symbol, parameters, sort)?,
717            DeclareSort { symbol, arity } => folder.fold_declare_sort(symbol, arity)?,
718            DefineFun { sig, term } => folder.fold_define_fun(sig, term)?,
719            DefineFunRec { sig, term } => folder.fold_define_fun_rec(sig, term)?,
720            DefineFunsRec { funs } => folder.fold_define_funs_rec(funs)?,
721            DefineSort {
722                symbol,
723                parameters,
724                sort,
725            } => folder.fold_define_sort(symbol, parameters, sort)?,
726            Echo { message } => Echo { message },
727            Exit => Exit,
728            GetAssertions => GetAssertions,
729            GetAssignment => GetAssignment,
730            GetInfo { flag } => GetInfo { flag },
731            GetModel => GetModel,
732            GetOption { keyword } => GetOption { keyword },
733            GetProof => GetProof,
734            GetUnsatAssumptions => GetUnsatAssumptions,
735            GetUnsatCore => GetUnsatCore,
736            GetValue { terms } => folder.fold_get_value(terms)?,
737            Pop { level } => Pop { level },
738            Push { level } => Push { level },
739            Reset => Reset,
740            ResetAssertions => ResetAssertions,
741            SetInfo { keyword, value } => SetInfo { keyword, value },
742            SetLogic { symbol } => folder.fold_set_logic(symbol)?,
743            SetOption { keyword, value } => SetOption { keyword, value },
744        };
745        if let Some(ctx) = folder.context_mut() {
746            ctx.process(&command);
747        }
748        Ok(command)
749    }
750}