Skip to main content

formally_smt/
solver.rs

1//
2// ::formally - the open-source formal methods toolchain
3//
4// Copyright (c) 2025 Nicola Gigante
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23//
24
25use crate::formally;
26use formally::{
27    smt::{backends::Backend, logics::Logic, *},
28    support::*,
29};
30
31use std::{
32    cell::RefCell,
33    collections::HashSet,
34    fmt::{Debug, Formatter},
35    rc::Rc,
36    sync::Arc,
37};
38
39/// Configuration for SMT solvers.
40///
41/// This is a simple struct holding some parameters used to instantiate SMT solvers. [Config]
42/// instances are usually constructed from the default one by changing the desired parameters and
43/// then passed to [Solver::new()] and related constructors. Fields are public and can be set
44/// arbitrarily but a builder method for each parameter is also provided, for convenience.
45///
46/// Example:
47/// ```
48/// # mod formally {
49/// #     pub extern crate formally_smt as smt;
50/// #     pub extern crate formally_support as support;
51/// # }
52/// # use formally::{smt::{*, backends::z3::Z3}, support::*};
53/// # fn main() -> Result<()> {
54/// let config =
55///     Config::default()
56///         .logic("LIA")
57///         .produce_models(true);
58///
59/// let solver = Solver::new(&config)?;
60/// // ...
61/// # Ok(())
62/// # }
63/// ```
64#[derive(Debug, Clone, Default)]
65pub struct Config {
66    /// The name of the logic to instantiate the solver for.
67    ///
68    /// A [None] value is the default, which means a logic is not selected and the solver will
69    /// accept symbols from all the known theories, as if "ALL" had been specified (such as
70    /// with the `(set-logic ALL)` command in SMT-LIBv2).
71    ///
72    /// Note that the `"ALL"` string itself has no special meaning, but is looked up as any other
73    /// logic name (and probably not found).
74    pub logic: Option<Identifier<'static>>,
75    /// Whether the solver has to activate the machinery for generating models for satisfiable
76    /// instances.
77    pub produce_models: bool,
78}
79
80impl Config {
81    /// Create a new [Config] object with default values.
82    pub fn new() -> Config {
83        Config::default()
84    }
85
86    /// Set the `logic` fied.
87    pub fn logic<'a>(self, logic: impl Into<Identifier<'a>>) -> Config {
88        Config {
89            logic: Some(logic.into().into_owned()),
90            ..self
91        }
92    }
93
94    /// Set the `produce_models` fied.
95    pub fn produce_models(self, produce_models: bool) -> Config {
96        Config {
97            produce_models,
98            ..self
99        }
100    }
101}
102
103/// Wrap two [Scope] objects for declared/defined functions and sorts.
104///
105/// SMT-LIBv2 specifies two different namespaces for function and sort symbols in a module.
106/// It is therefore common, especially in backends, to keep around two `Scope<Function>` objects
107/// keeping track of the two namespaces. [Env] is a small utility to keep two scopes together and
108/// perform lookups in the right one as needed.
109#[derive(Default, Clone)]
110pub struct Env {
111    pub functions: Scope<Function>,
112    pub sorts: Scope<Function>,
113}
114
115impl Env {
116    /// Creates an [Env] with empty scopes.
117    pub fn new() -> Self {
118        Env::default()
119    }
120}
121
122/// Select which scope to use in [Env::lookup()] and [Resolve::resolve()].
123#[derive(Clone, Copy, Hash, PartialEq, Eq)]
124pub enum Role {
125    /// Lookup/resolve a function.
126    Function,
127    /// Lookup/resolve a sort.
128    Sort,
129}
130
131impl Env {
132    /// Calls [lookup()](Scope::lookup) on the functions or the sorts scope depending on `role`.
133    ///
134    /// If `role` is `Role::Function`, the call goes to the functions scope. Otherwise
135    /// (`Role::Sort`), to the sorts scope.
136    pub fn lookup<'i>(&self, name: Identifier<'i>, role: Role) -> LookupSet<'_, 'i, Function> {
137        match role {
138            Role::Function => self.functions.lookup(name),
139            Role::Sort => self.sorts.lookup(name),
140        }
141    }
142
143    /// Creates a new [Env] from a parent one using the two scopes of the parent as parents of each
144    /// single scope.
145    pub fn with_parent(self, parent: Env) -> Env {
146        Env {
147            functions: self.functions.with_parent(parent.functions),
148            sorts: self.sorts.with_parent(parent.sorts),
149        }
150    }
151}
152
153/// Type that manages the backend's "term manager" (or "context") and subterm sharing.
154///
155/// Most SMT APIs separate the "term manager" (also called "context"), responsible for managing
156/// subterm sharing and the creation and lifetime management of term objects, from the "solver",
157/// which is responsible for the actual reasoning.
158///
159/// [formally::smt] does the same, where [TermManager] manages subterm sharing (through an
160/// underlying instance of [TermPool]) and a handle to the underlying backend's term manager (e.g.
161/// [TermManager](cvc5_sys::TermManager) in cvc5 or [Z3_context](z3_sys::Z3_context) in Z3).
162///
163/// The handle to the underlying backend's manager performs the conversion from [Term] to the
164/// internal representation of terms of the backend (e.g. [Z3_ast](z3_sys::Z3_ast) in Z3).
165///
166/// A term manager is mainly used by giving a reference to it to one or more [Solver] instances.
167/// Solvers built on the same manager can use the same terms which will be converted to the
168/// underlying backend's representation only once, saving time.
169///
170/// Example:
171/// ```
172/// # mod formally {
173/// #    pub extern crate formally_smt as smt;
174/// #    pub extern crate formally_support as support;
175/// # }
176///  use formally::{smt::{*, backends::z3::Z3}};
177/// # use formally::support::*;
178///
179/// # use std::rc::Rc;
180/// #
181/// # fn main() -> Result<()> {
182///  let manager = Rc::new(TermManager::with_backend(Z3)?);
183///  let config = Config::new();
184///  let mut slv1 = Solver::with_manager(&config, manager.clone())?;
185///  let mut slv2 = Solver::with_manager(&config, manager)?;
186///
187///  let x1 = slv1.declare(Declaration::constant("x", sort!(Int)))?;
188///  let x2 = slv2.declare(Declaration::constant("x", sort!(Int)))?;
189///
190///  // Equivalent declarations/definitions result in the same `Declared`/`Defined` objects
191///  assert_eq!(x1, x2);
192///
193///  let t1 = slv1.lookup(term!(* x x), Role::Function)?;
194///  let t2 = slv2.lookup(term!(* x x), Role::Function)?;
195///
196///  // Equivalent terms result into the same [Term] objects
197///  assert_eq!(t1, t2);
198///
199/// #   Ok(())
200/// # }
201/// ```
202///
203/// Since most SMT APIs are not threadsafe, [TermManager] is not [Send] nor [Sync] and must
204/// therefore be accessed and used by a single thread only. However, one can construct it using
205/// [TermManager::with_pool()] and passing a [DashPool] instance, which is a concurrent [TermPool]
206/// that allows to at least share the same terms among different threads.
207#[derive(Clone)]
208pub struct TermManager {
209    backend_manager: Rc<dyn backends::Manager>,
210    pool: Rc<dyn TermPool>,
211    decls: RefCell<HashSet<Arc<Declaration<Sort, Sort>>>>,
212    defs: RefCell<HashSet<Arc<Definition<Sort, Sort, Term>>>>,
213}
214
215impl TermManager {
216    fn decl(&self, decl: Declaration<Sort, Sort>) -> Declared {
217        if let Some(decl) = self.decls.borrow().get(&decl) {
218            return Declared(Nominal(decl.clone()));
219        }
220
221        let arc = Arc::new(decl);
222        self.decls.borrow_mut().insert(arc.clone());
223
224        Declared(Nominal(arc))
225    }
226
227    fn def(&self, def: Definition<Sort, Sort, Term>) -> Defined {
228        if let Some(def) = self.defs.borrow().get(&def) {
229            return Defined(Nominal(def.clone()));
230        }
231
232        let arc = Arc::new(def);
233        self.defs.borrow_mut().insert(arc.clone());
234
235        Defined(Nominal(arc))
236    }
237}
238
239impl Debug for TermManager {
240    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
241        write!(f, "TermManager {{ }}")
242    }
243}
244
245impl TermManager {
246    /// Construct a [TermManager] with the default backend (if any is available).
247    pub fn new() -> Result<TermManager> {
248        TermManager::with_backend(backends::Register::default_backend()?)
249    }
250
251    /// Construct a [TermManager] over a given [Backend] looked up by name, if it exists.
252    pub fn with_backend_name<'a>(backend: impl Into<Identifier<'a>>) -> Result<TermManager> {
253        let backend = backends::Register::backend(backend)?;
254
255        TermManager::with_backend(backend)
256    }
257
258    /// Construct a [TermManager] over a given [Backend].
259    pub fn with_backend(backend: impl Backend) -> Result<TermManager> {
260        Ok(TermManager {
261            backend_manager: Rc::from(backend.manager()?),
262            pool: Rc::new(HashPool::new()),
263            decls: RefCell::default(),
264            defs: RefCell::default(),
265        })
266    }
267
268    /// Construct a [TermManager] over a given [Backend] and [TermPool].
269    pub fn with_pool(backend: impl Backend, pool: Rc<dyn TermPool>) -> Result<TermManager> {
270        Ok(TermManager {
271            backend_manager: Rc::from(backend.manager()?),
272            pool,
273            decls: RefCell::default(),
274            defs: RefCell::default(),
275        })
276    }
277
278    /// Return a reference to the underlying [TermPool].
279    pub fn pool(&self) -> &dyn TermPool {
280        &*self.pool
281    }
282}
283
284/// Main interface to SMT solvers.
285///
286/// The [Solver] type is at the core of [formally::smt], providing high-level access to backend
287/// solvers.
288///
289/// One can create a [Solver] in three ways, all accepting a [Config] reference to configure the
290/// solver:
291/// - [Solver::new()] creates a solver with a default backend and a dedicated [TermManager].
292/// - [Solver::with_backend()] creates a solver with a given backend and a dedicated [TermManager].
293/// - [Solver::with_manager()] creates a solver over the given [TermManager].
294///
295/// [Solver] is responsible to implement name lookup on terms used in definitions and assertions.
296/// A term such as `term!(> p q)` contains three unresolved symbols, `">"`, `"p"`, and `"q"`.
297/// Without name resolution the term would be unusable by the backends.
298///
299/// [Solver] implements [Stack] to provide the usual incremental interface of most SAT/SMT solvers.
300pub struct Solver {
301    backend_solver: Box<dyn backends::Solver>,
302    env: Env,
303    manager: Rc<TermManager>,
304}
305
306impl Solver {
307    /// Create a [Solver] over a given [TermManager].
308    ///
309    /// Use this constructor to share the same [TermManager] among different solvers.
310    pub fn with_manager(config: &Config, manager: impl Into<Rc<TermManager>>) -> Result<Solver> {
311        let manager = manager.into();
312        let backend_solver = manager
313            .backend_manager
314            .backend()
315            .solver(config, manager.backend_manager.clone())?;
316        Ok(Solver {
317            env: Env::new().with_parent(backend_solver.logic().theory().env()),
318            backend_solver,
319            manager,
320        })
321    }
322
323    /// Create a [Solver] over a given [Backend].
324    ///
325    /// Backends provided by this crate are defined in [formally::smt::backend](backends).
326    pub fn with_backend(config: &Config, backend: impl Backend) -> Result<Solver> {
327        Solver::with_manager(config, TermManager::with_backend(backend)?)
328    }
329
330    /// Create a [Solver] over the given [Backend] specified by name, if it exists.
331    ///
332    /// Backends provided by this crate are defined in [formally::smt::backend](backends).
333    pub fn with_backend_name<'a>(
334        config: &Config,
335        backend: impl Into<Identifier<'a>>,
336    ) -> Result<Solver> {
337        Solver::with_manager(config, TermManager::with_backend_name(backend)?)
338    }
339
340    /// Create a [Solver] with a default backend and a dedicated [TermManager].
341    pub fn new(config: &Config) -> Result<Solver> {
342        Solver::with_manager(config, TermManager::new()?)
343    }
344
345    /// Get the currently selected [Logic].
346    pub fn logic(&self) -> &dyn Logic {
347        self.backend_solver.logic()
348    }
349
350    /// Set a new configuration for the solver.
351    ///
352    /// Note that the [logic](Config::logic) field is ignored when changing configuration after
353    /// construction.
354    pub fn config(&self, config: &Config) -> Result<()> {
355        Ok(self.backend_solver.config(config)?)
356    }
357
358    /// Get the [Env] object holding the current scopes for functions and sorts declared and defined
359    /// in the solver.
360    pub fn env(&self) -> &Env {
361        &self.env
362    }
363
364    /// Return a reference to the [TermPool] of the underlying [TermManager].
365    pub fn pool(&self) -> &dyn TermPool {
366        self.manager.pool()
367    }
368
369    /// Convert a [ToTerm] object into a [Term] including name resolution and type checking.
370    ///
371    /// This method uniques a [ToTerm] object into a [Term] using the [TermPool] of the underlying
372    /// [TermManager] and then performs name resolution and type checking on the term.
373    ///
374    /// The resulting term is guaranteed to be well typed and fully resolved.
375    ///
376    /// Since terms can describe sorts as well, and SMT-LIBv2 defines two different namespaces for
377    /// sorts and functions, the [Role] argument is needed to tell whether the term has to be
378    /// interpreted as a sort or as a value term.
379    pub fn lookup<T: ToTerm>(&self, term: T, role: Role) -> Result<Term> {
380        let interned = term.into_term_in(self.manager.pool());
381        let resolved = interned.resolve(self.env(), self.manager.pool(), role)?;
382        resolved.type_check()?;
383
384        Ok(resolved)
385    }
386
387    /// Convert a [ToSort] object into a [Sort] including name resolution and type checking.
388    ///
389    /// This method uniques a [ToSort] object into a [Sort] and then performs name resolution and
390    /// type checking on the sort.
391    ///
392    /// The resulting sort is guaranteed to be well-formed and fully resolved.
393    pub fn lookup_sort<S: ToSort>(&self, sort: &S) -> Result<Sort> {
394        let resolved = sort.resolve(self.env(), self.manager.pool(), Role::Sort)?;
395        resolved.type_check()?;
396
397        Ok(resolved.try_into()?)
398    }
399
400    /// Create a fully resolved [Variable] by looking up its sort.
401    ///
402    /// This convenience method is equivalent to constructing a new [Variable] instance by
403    /// calling [Solver::lookup_sort()] on the variable's sort.
404    ///
405    /// This method is needed to obtain a fully resolved [Variable] that can be used without further
406    /// name resolution, but it is *not* needed when normally using variables to define functions
407    /// that then refer to them by name.
408    ///
409    /// Example:
410    /// ```
411    /// # mod formally {
412    /// #    pub extern crate formally_smt as smt;
413    /// #    pub extern crate formally_support as support;
414    /// # }
415    ///  use formally::smt::*;
416    /// # use formally::support::*;
417    ///
418    /// # fn main() -> Result<()> {
419    ///  let config = Config::default();
420    ///  let mut solver = Solver::new(&config)?;
421    ///
422    ///  // we construct the variable directly.
423    ///  let x = Variable::new("x", sort!(Int));
424    ///
425    ///  // here the variable `x` is moved and `lookup_var()` is called internally.
426    ///  let f = solver.define(Definition::function("f", [x], sort!(Int), term!(* x 2)));
427    ///
428    ///  // here the variable is #expanded in the term so we need to do name resolution earlier
429    ///  let y = solver.lookup_var(&Variable::new("y", sort!(Int)))?;
430    ///  let g = solver.define(Definition::function("g", [y.clone()], sort!(Int), term!(* #y 2)));
431    ///
432    ///  solver.require(term!(distinct (f 21) (g 21) 42))?;
433    ///
434    ///  assert_eq!(solver.check()?, Answer::No);
435    ///
436    /// #    Ok(())
437    /// # }
438    /// ```
439    pub fn lookup_var<S: ToSort>(&self, variable: &Variable<S>) -> Result<Variable> {
440        Ok(
441            Variable::new(variable.name().clone(), self.lookup_sort(variable.sort())?)
442                .over(variable.span()),
443        )
444    }
445
446    /// Create a fully resolved [Binding] by looking up its sort and term.
447    ///
448    /// This convenience method is useful to infer automatically the sort of the binding from its
449    /// defining term. It is designed to be used in pair with [Binding::new()].
450    pub fn lookup_binding<T: ToTerm>(&self, binding: Binding<Infer, T>) -> Result<Binding> {
451        let def = self.lookup(&binding.def, Role::Function)?;
452        let sort = Sort::of(&def)?;
453
454        let variable =
455            Variable::new(binding.variable.name().clone(), sort).over(binding.variable.span());
456
457        Ok(Binding {
458            variable,
459            def,
460            span: binding.span,
461        })
462    }
463
464    /// Declare a function (or a constant, or a sort).
465    ///
466    /// As explained in the [overview](formally::smt), [declare()](Solver::declare) registers a
467    /// [Declaration] value in the suitable scope and returns a [Declared] object that immutably
468    /// points to the registered [Declaration] object. The registered [Declaration] object is
469    /// not equal to the one passed as argument, in general, because name
470    /// resolution occurs.
471    ///
472    /// Remember that constants are seen as functions with no arguments, and sorts as constants of
473    /// the special sort [Sort::sort()]. See also [Declaration::function()],
474    /// [Declaration::constant()], and [Declaration::sort()] for details.
475    pub fn declare<R: ToSort, B: ToSort>(&mut self, decl: Declaration<R, B>) -> Result<Declared> {
476        let mut domain = Vec::with_capacity(decl.domain.len());
477        for d in &decl.domain {
478            domain.push(self.lookup_sort(d)?);
479        }
480
481        let decl = Declaration {
482            name: decl.name,
483            domain,
484            range: self.lookup_sort(&decl.range)?,
485            span: decl.span,
486        };
487
488        let decl = self.manager.decl(decl);
489        self.backend_solver
490            .logic()
491            .check_function(&decl.clone().into())?;
492
493        if decl.range == Sort::sort() {
494            self.env.sorts.add(&decl.name, decl.clone().into());
495        } else {
496            self.env.functions.add(&decl.name, decl.clone().into());
497        }
498        self.backend_solver.declare(decl.clone())?;
499
500        Ok(decl)
501    }
502
503    /// Define a function (or a constant, or a sort).
504    ///
505    /// As explained in the [overview](formally::smt), [define()](Solver::define) registers a
506    /// [Definition] value in the suitable scope and returns a [Defined] object that immutably
507    /// points to the registered [Definition] object. The registered [Definition] object is not
508    /// equal to the one passed as argument, in general, because name
509    /// resolution occurs.
510    ///
511    /// Remember that constants are seen as functions with no arguments, and sorts as constants of
512    /// the special sort [Sort::sort()]. See also [Definition::function()],
513    /// [Definition::constant()], and [Definition::sort()] for details.
514    ///
515    /// See also the documentation of [Solver::lookup_var()] to know when name lookup is needed to
516    /// be done manually on the variables used in a definition.
517    pub fn define<V: ToSort, R: ToSort, B: ToTerm>(
518        &mut self,
519        def: Definition<V, R, B>,
520    ) -> Result<Defined> {
521        let mut nested = Env::new().with_parent(self.env().clone());
522        let mut domain = Vec::with_capacity(def.domain.len());
523        for var in &def.domain {
524            let var = self.lookup_var(var)?;
525            domain.push(var.clone());
526            nested
527                .functions
528                .add(var.name(), Function::Variable(var.clone()));
529        }
530
531        let range = self.lookup_sort(&def.range)?;
532        let body = def.body.into_term_in(self.manager.pool()).resolve(
533            &nested,
534            self.manager.pool(),
535            Role::Function,
536        )?;
537        let bodysort = body.type_check()?;
538
539        if range == Sort::sort() {
540            if bodysort != Sort::sort() {
541                error!(
542                    body.span(),
543                    "expected sort in definition of `{}`, found `{}`", def.name, bodysort
544                );
545                return Err(DiagnosticEmitted);
546            }
547        } else {
548            if bodysort != range {
549                error!(
550                    body.span(),
551                    "expected term of sort `{}` in definition of `{}`, found `{}`",
552                    range,
553                    def.name,
554                    bodysort
555                );
556                return Err(DiagnosticEmitted);
557            }
558        }
559
560        let def = Definition {
561            name: def.name,
562            domain,
563            range,
564            body,
565            span: None,
566        };
567
568        let def = self.manager.def(def);
569        self.backend_solver
570            .logic()
571            .check_function(&def.clone().into())?;
572
573        if def.range == Sort::sort() {
574            self.env.sorts.add(&def.name, def.clone().into());
575        } else {
576            self.env.functions.add(&def.name, def.clone().into());
577        }
578        self.backend_solver.define(def.clone())?;
579
580        Ok(def)
581    }
582
583    /// Assert the given term to the current assertion stack
584    ///
585    /// The term undergoes [name resolution](Resolve::resolve()) and must be well-typed and be of
586    /// sort [Core::Bool()](theories::Core::Bool()).
587    pub fn require<T: ToTerm>(&mut self, term: T) -> Result<()> {
588        let term = self.lookup(term, Role::Function)?;
589        self.backend_solver.logic().check_term(&term)?;
590        let sort = Sort::of(&term)?;
591
592        if sort != theories::Core::Bool() {
593            error!(term.span(), "can only assert Boolean terms");
594            note!(term.span(), "asserted term is of sort `{}`", sort);
595            return Err(DiagnosticEmitted);
596        }
597
598        self.backend_solver.require(&term)?;
599
600        Ok(())
601    }
602
603    /// Check the satisfiability of the current assertions.
604    ///
605    /// The return value distinguishes between errors occurred during the solving process (`Err(_)`)
606    /// and the case where no error occurred but the solver gave up on finding an answer
607    /// (`Ok(Answer::Unknown)`).
608    pub fn check(&mut self) -> Result<Answer> {
609        match self.backend_solver.check()? {
610            Some(true) => Ok(Answer::Yes),
611            Some(false) => Ok(Answer::No),
612            None => Ok(Answer::Unknown),
613        }
614    }
615
616    /// Get a model of the current assertions, if they were found to be satisfiable.
617    ///
618    /// The return value distinguishes between errors occurred while extracting the model from the
619    /// backend (`Err(_)`), and the case where no error occurred but there is no model available
620    /// (`Ok(None)`), for example because the last call to [check()](Solver::check()) did not
621    /// return `Ok(Answer::Yes)` or because model production was turned off in the solver's
622    /// [Config].
623    ///
624    /// Note that the representation of model values is still incomplete and only Boolean and
625    /// numerical values can be currently extracted.
626    pub fn model(&self) -> Result<Option<Model<'_>>> {
627        match self.backend_solver.model()? {
628            Some(provider) => Ok(Some(Model {
629                solver: self,
630                provider,
631            })),
632            None => Ok(None),
633        }
634    }
635}
636
637impl Stack for Solver {
638    /// Push a new frame in the assertions stack.
639    ///
640    /// The assertion stack includes asserted terms and declared/defined entities.
641    /// Currently, the `:global-declarations` option of SMT-LIBv2 is not supported.
642    fn push(&mut self) -> Result<()> {
643        Ok(self.backend_solver.push()?)
644    }
645
646    /// Pops a frame from the assertions stack, doing nothing if there is no frame to remove.
647    ///
648    /// The assertion stack includes asserted terms and declared/defined entities.
649    /// Currently, the `:global-declarations` option of SMT-LIBv2 is not supported.
650    fn pop_n(&mut self, n: usize) -> Result<()> {
651        Ok(self.backend_solver.pop_n(n)?)
652    }
653}
654
655/// A trait for types that can provide model values.
656pub trait ModelProvider {
657    fn value(&self, term: &Term, pool: &dyn TermPool) -> Option<Term>;
658}
659
660/// The answer to a call to [Solver::check()].
661#[derive(Clone, Copy, Default, PartialEq, Eq)]
662pub enum Answer {
663    /// The assertions are satisfiable.
664    Yes,
665    /// The assertions are unsatisfiable.
666    No,
667    /// A definite answer could not be found.
668    #[default]
669    Unknown,
670}
671
672impl Debug for Answer {
673    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
674        match self {
675            Answer::Yes => write!(f, "sat"),
676            Answer::No => write!(f, "unsat"),
677            Answer::Unknown => write!(f, "unknown"),
678        }
679    }
680}
681
682/// A model to the current set of assertions of a [Solver].
683pub struct Model<'s> {
684    solver: &'s Solver,
685    provider: Box<dyn 's + ModelProvider>,
686}
687
688impl Model<'_> {
689    /// Get the value of a [ToTerm] object (after name lookup and type checking) in the model.
690    pub fn value(&self, term: impl ToTerm) -> Result<Option<Term>> {
691        Ok(self.provider.value(
692            &self.solver.lookup(term, Role::Function)?,
693            self.solver.pool(),
694        ))
695    }
696}