Skip to main content

z3/
solver.rs

1use log::debug;
2use std::borrow::Borrow;
3use std::ffi::{CStr, CString};
4use std::fmt;
5use std::iter::FusedIterator;
6use z3_sys::*;
7
8use crate::ast::Bool;
9use crate::{
10    AstVector, Context, Model, Params, SatResult, Solver, Statistics, Symbol, Translate, ast,
11    ast::Ast,
12};
13use std::ops::AddAssign;
14
15impl Solver {
16    pub(crate) unsafe fn wrap(ctx: &Context, z3_slv: Z3_solver) -> Solver {
17        unsafe {
18            Z3_solver_inc_ref(ctx.z3_ctx.0, z3_slv);
19        }
20        Solver {
21            ctx: ctx.clone(),
22            z3_slv,
23        }
24    }
25
26    /// Create a new solver. This solver is a "combined solver"
27    /// that internally uses a non-incremental (`solver1`) and an
28    /// incremental solver (`solver2`). This combined solver changes
29    /// its behaviour based on how it is used and how its parameters are set.
30    ///
31    /// If the solver is used in a non incremental way (i.e. no calls to
32    /// [`Solver::push()`] or [`Solver::pop()`], and no calls to
33    /// [`Solver::assert()`] or [`Solver::assert_and_track()`] after checking
34    /// satisfiability without an intervening [`Solver::reset()`]) then `solver1`
35    /// will be used. This solver will apply Z3's "default" tactic.
36    ///
37    /// The "default" tactic will attempt to probe the logic used by the
38    /// assertions and will apply a specialized tactic if one is supported.
39    /// Otherwise the general `(and-then simplify smt)` tactic will be used.
40    ///
41    /// If the solver is used in an incremental way then the combined solver
42    /// will switch to using `solver2` (which behaves similarly to the general
43    /// "smt" tactic).
44    ///
45    /// Note however it is possible to set the `solver2_timeout`,
46    /// `solver2_unknown`, and `ignore_solver1` parameters of the combined
47    /// solver to change its behaviour.
48    pub fn new() -> Solver {
49        let ctx = &Context::thread_local();
50        unsafe { Self::wrap(ctx, Z3_mk_solver(ctx.z3_ctx.0).unwrap()) }
51    }
52
53    /// Parse an SMT-LIB2 string with assertions, soft constraints and optimization objectives.
54    /// Add the parsed constraints and objectives to the solver.
55    pub fn from_string<T: Into<Vec<u8>>>(&self, source_string: T) {
56        let source_cstring = CString::new(source_string).unwrap();
57        unsafe {
58            Z3_solver_from_string(self.ctx.z3_ctx.0, self.z3_slv, source_cstring.as_ptr());
59        }
60    }
61
62    /// Create a new solver customized for the given logic.
63    /// It returns `None` if the logic is unknown or unsupported.
64    pub fn new_for_logic<S: Into<Symbol>>(logic: S) -> Option<Solver> {
65        let ctx = &Context::thread_local();
66        unsafe {
67            let s = Z3_mk_solver_for_logic(ctx.z3_ctx.0, logic.into().as_z3_symbol())?;
68            Some(Self::wrap(ctx, s))
69        }
70    }
71
72    /// Get this solver's context.
73    pub fn get_context(&self) -> &Context {
74        &self.ctx
75    }
76
77    /// Assert a constraint into the solver.
78    ///
79    /// The functions [`Solver::check()`] and [`Solver::check_assumptions()`]
80    /// should be used to check whether the logical context is consistent
81    /// or not.
82    ///
83    /// ```rust
84    /// # use z3::{Config, Context, Solver, ast, SatResult, ast::Bool};
85    /// let mut solver = Solver::new();
86    ///
87    /// solver.assert(&Bool::from_bool(true));
88    /// solver += &Bool::from_bool(false);
89    /// solver += Bool::fresh_const("");
90    ///
91    /// assert_eq!(solver.check(), SatResult::Unsat);
92    /// ````
93    ///
94    /// # See also:
95    ///
96    /// - [`Solver::assert_and_track()`]
97    pub fn assert<T: Borrow<Bool>>(&self, ast: T) {
98        let ast = ast.borrow();
99        debug!("assert: {ast:?}");
100        unsafe { Z3_solver_assert(self.ctx.z3_ctx.0, self.z3_slv, ast.z3_ast) };
101    }
102
103    /// Assert a constraint `a` into the solver, and track it (in the
104    /// unsat) core using the Boolean constant `p`.
105    ///
106    /// This API is an alternative to
107    /// [`Solver::check_assumptions()`]
108    /// for extracting unsat cores. Both APIs can be used in the same solver.
109    /// The unsat core will contain a combination of the Boolean variables
110    /// provided using [`Solver::assert_and_track()`]
111    /// and the Boolean literals provided using
112    /// [`Solver::check_assumptions()`].
113    ///
114    /// # See also:
115    ///
116    /// - [`Solver::assert()`]
117    pub fn assert_and_track<T: Into<Bool>>(&self, ast: T, p: &Bool) {
118        let ast = ast.into();
119        debug!("assert_and_track: {ast:?}");
120        unsafe { Z3_solver_assert_and_track(self.ctx.z3_ctx.0, self.z3_slv, ast.z3_ast, p.z3_ast) };
121    }
122
123    /// Remove all assertions from the solver.
124    pub fn reset(&self) {
125        unsafe { Z3_solver_reset(self.ctx.z3_ctx.0, self.z3_slv) };
126    }
127
128    /// Check whether the assertions in a given solver are consistent or not.
129    ///
130    /// The function [`Solver::get_model()`]
131    /// retrieves a model if the assertions is satisfiable (i.e., the
132    /// result is [`SatResult::Sat`]) and [model construction is enabled].
133    /// Note that if the call returns `SatResult::Unknown`, Z3 does not
134    /// ensure that calls to [`Solver::get_model()`]
135    /// succeed and any models produced in this case are not guaranteed
136    /// to satisfy the assertions.
137    ///
138    /// The function [`Solver::get_proof()`]
139    /// retrieves a proof if [proof generation was enabled] when the context
140    /// was created, and the assertions are unsatisfiable (i.e., the result
141    /// is [`SatResult::Unsat`]).
142    ///
143    /// # See also:
144    ///
145    /// - [`Config::set_model_generation()`](crate::Config::set_model_generation)
146    /// - [`Config::set_proof_generation()`](crate::Config::set_proof_generation)
147    /// - [`Solver::check_assumptions()`]
148    ///
149    /// [model construction is enabled]: crate::Config::set_model_generation
150    /// [proof generation was enabled]: crate::Config::set_proof_generation
151    pub fn check(&self) -> SatResult {
152        match unsafe { Z3_solver_check(self.ctx.z3_ctx.0, self.z3_slv) } {
153            Z3_L_FALSE => SatResult::Unsat,
154            Z3_L_UNDEF => SatResult::Unknown,
155            Z3_L_TRUE => SatResult::Sat,
156            _ => unreachable!(),
157        }
158    }
159
160    /// Check whether the assertions in the given solver and
161    /// optional assumptions are consistent or not.
162    ///
163    /// The function [`Solver::get_unsat_core()`]
164    /// retrieves the subset of the assumptions used in the
165    /// unsatisfiability proof produced by Z3.
166    ///
167    /// # See also:
168    ///
169    /// - [`Solver::check()`]
170    pub fn check_assumptions(&self, assumptions: &[Bool]) -> SatResult {
171        let a: Vec<Z3_ast> = assumptions.iter().map(|a| a.z3_ast).collect();
172        match unsafe {
173            Z3_solver_check_assumptions(self.ctx.z3_ctx.0, self.z3_slv, a.len() as u32, a.as_ptr())
174        } {
175            Z3_L_FALSE => SatResult::Unsat,
176            Z3_L_UNDEF => SatResult::Unknown,
177            Z3_L_TRUE => SatResult::Sat,
178            _ => unreachable!(),
179        }
180    }
181
182    /// Return all assertions currently in the solver.
183    pub fn get_assertions(&self) -> Vec<Bool> {
184        let av = unsafe {
185            AstVector::wrap(
186                &self.ctx,
187                Z3_solver_get_assertions(self.ctx.z3_ctx.0, self.z3_slv).unwrap(),
188            )
189        };
190        av.try_into().expect("solver assertions are always Bool")
191    }
192
193    /// Return a subset of the assumptions provided to either the last
194    ///
195    /// * [`Solver::check_assumptions`] call, or
196    /// * sequence of [`Solver::assert_and_track`] calls followed
197    ///   by a [`Solver::check`] call.
198    ///
199    /// These are the assumptions Z3 used in the unsatisfiability proof.
200    /// Assumptions are available in Z3. They are used to extract unsatisfiable
201    /// cores.  They may be also used to "retract" assumptions. Note that,
202    /// assumptions are not really "soft constraints", but they can be used to
203    /// implement them.
204    ///
205    /// By default, the unsat core will not be minimized. Generation of a minimized
206    /// unsat core can be enabled via the `"sat.core.minimize"` and `"smt.core.minimize"`
207    /// settings for SAT and SMT cores respectively. Generation of minimized unsat cores
208    /// will be more expensive.
209    ///
210    /// # See also:
211    ///
212    /// - [`Solver::check_assumptions`]
213    /// - [`Solver::assert_and_track`]
214    pub fn get_unsat_core(&self) -> Vec<Bool> {
215        let Some(raw) = (unsafe { Z3_solver_get_unsat_core(self.ctx.z3_ctx.0, self.z3_slv) })
216        else {
217            return vec![];
218        };
219        let av = unsafe { AstVector::wrap(&self.ctx, raw) };
220        av.try_into().expect("unsat core contains only Bool")
221    }
222
223    /// Retrieve consequences from the solver given a set of assumptions.
224    pub fn get_consequences(&self, assumptions: &[Bool], variables: &[Bool]) -> Vec<Bool> {
225        let assumptions_vec = AstVector::from(assumptions);
226        let variables_vec = AstVector::from(variables);
227        let consequences_vec = AstVector::new();
228
229        unsafe {
230            Z3_solver_get_consequences(
231                self.ctx.z3_ctx.0,
232                self.z3_slv,
233                assumptions_vec.z3_ast_vector,
234                variables_vec.z3_ast_vector,
235                consequences_vec.z3_ast_vector,
236            );
237        }
238
239        consequences_vec
240            .try_into()
241            .expect("consequences are always Bool")
242    }
243
244    /// Create a backtracking point.
245    ///
246    /// The solver contains a stack of assertions.
247    ///
248    /// # See also:
249    ///
250    /// - [`Solver::pop()`]
251    pub fn push(&self) {
252        unsafe { Z3_solver_push(self.ctx.z3_ctx.0, self.z3_slv) };
253    }
254
255    /// Backtrack `n` backtracking points.
256    ///
257    /// # See also:
258    ///
259    /// - [`Solver::push()`]
260    pub fn pop(&self, n: u32) {
261        unsafe { Z3_solver_pop(self.ctx.z3_ctx.0, self.z3_slv, n) };
262    }
263
264    /// Retrieve the model for the last [`Solver::check()`]
265    /// or [`Solver::check_assumptions()`] if the
266    /// assertions is satisfiable (i.e., the result is
267    /// `SatResult::Sat`) and [model construction is enabled].
268    ///
269    /// It can also be used
270    /// if the result is `SatResult::Unknown`, but the returned model
271    /// is not guaranteed to satisfy quantified assertions.
272    ///
273    /// The error handler is invoked if a model is not available because
274    /// the commands above were not invoked for the given solver, or if
275    /// the result was [`SatResult::Unsat`].
276    ///
277    /// [model construction is enabled]: crate::Config::set_model_generation
278    pub fn get_model(&self) -> Option<Model> {
279        Model::of_solver(self)
280    }
281
282    /// Retrieve the proof for the last [`Solver::check()`]
283    /// or [`Solver::check_assumptions()`].
284    ///
285    /// The error handler is invoked if [proof generation is not enabled],
286    /// or if the commands above were not invoked for the given solver,
287    /// or if the result was different from [`SatResult::Unsat`].
288    ///
289    /// # See also:
290    ///
291    /// - [`Config::set_proof_generation()`](crate::Config::set_proof_generation)
292    ///
293    /// [proof generation is not enabled]: crate::Config::set_proof_generation
294    //
295    // This seems to actually return an Ast with kind `SortKind::Unknown`, which we don't
296    // have an Ast subtype for yet.
297    pub fn get_proof(&self) -> Option<impl Ast> {
298        let m = unsafe { Z3_solver_get_proof(self.ctx.z3_ctx.0, self.z3_slv) }?;
299        Some(unsafe { ast::Dynamic::wrap(&self.ctx, m) })
300    }
301
302    /// Return a brief justification for an "unknown" result (i.e.,
303    /// [`SatResult::Unknown`]) for the commands [`Solver::check()`]
304    /// and [`Solver::check_assumptions()`].
305    pub fn get_reason_unknown(&self) -> Option<String> {
306        let p = unsafe { Z3_solver_get_reason_unknown(self.ctx.z3_ctx.0, self.z3_slv) };
307        if p.is_null() {
308            return None;
309        }
310        unsafe { CStr::from_ptr(p) }
311            .to_str()
312            .ok()
313            .map(|s| s.to_string())
314    }
315
316    /// Set the current solver using the given parameters.
317    pub fn set_params(&self, params: &Params) {
318        unsafe { Z3_solver_set_params(self.ctx.z3_ctx.0, self.z3_slv, params.z3_params) };
319    }
320
321    /// Retrieve the statistics for the last [`Solver::check()`].
322    pub fn get_statistics(&self) -> Statistics {
323        unsafe {
324            Statistics::wrap(
325                &self.ctx,
326                Z3_solver_get_statistics(self.ctx.z3_ctx.0, self.z3_slv).unwrap(),
327            )
328        }
329    }
330
331    pub fn to_smt2(&self) -> String {
332        let name = CString::new("benchmark generated from rust API").unwrap();
333        let logic = CString::new("").unwrap();
334        let status = CString::new("unknown").unwrap();
335        let attributes = CString::new("").unwrap();
336        let assumptions = self.get_assertions();
337        let mut num_assumptions = assumptions.len() as u32;
338        let formula = if num_assumptions > 0 {
339            num_assumptions -= 1;
340            assumptions[num_assumptions as usize].z3_ast
341        } else {
342            Bool::from_bool(true).z3_ast
343        };
344        let z3_assumptions = assumptions.iter().map(|a| a.z3_ast).collect::<Vec<_>>();
345
346        let p = unsafe {
347            Z3_benchmark_to_smtlib_string(
348                self.ctx.z3_ctx.0,
349                name.as_ptr(),
350                logic.as_ptr(),
351                status.as_ptr(),
352                attributes.as_ptr(),
353                num_assumptions,
354                z3_assumptions.as_ptr(),
355                formula,
356            )
357        };
358        if p.is_null() {
359            return String::new();
360        }
361        unsafe { CStr::from_ptr(p) }
362            .to_str()
363            .ok()
364            .map(|s| s.to_string())
365            .unwrap_or_else(String::new)
366    }
367
368    /// Iterates over models for the given [`Solvable`] from the current state of a [`Solver`].
369    ///
370    /// The iterator terminates if the [`Solver`] returns `UNSAT` or `UNKNOWN`, as well as if model
371    /// generation fails. This iterator may also _never_ terminate as some problems have infinite
372    /// solutions. It is recommended to use [`Iterator::take`] if your problem has an unbounded number
373    /// of solutions.
374    ///
375    /// Note that, since this iterator is querying the solver, it's not guaranteed to be at all "fast":
376    /// every iteration requires querying the solver and constructing a model, which can take time. This
377    /// interface is merely here as a clean alternative to manually issuing [`Solver::check`] and [`Solver::get_model`]
378    /// calls.
379    ///
380    /// The [`Solver`] given to this method is [`Clone`]'d when producing the iterator: no change
381    /// is made in the solver passed to the function.
382    ///
383    /// # Examples
384    ///
385    /// This can be used to iterate over solutions to individual [`Ast`]s:
386    ///
387    /// ```
388    /// # use z3::Solver;
389    /// # use z3::ast::*;
390    ///  let s = Solver::new();
391    ///  let a = Int::new_const("a");
392    ///  s.assert(a.le(4));
393    ///  s.assert(a.ge(0));
394    ///  let solutions: Vec<_> = s.solutions(a, true).collect();
395    ///  let mut solutions: Vec<_> = solutions.into_iter().map(|a|a.as_u64().unwrap()).collect();
396    ///  solutions.sort();
397    ///  assert_eq!(vec![0,1,2,3,4], solutions);
398    /// ```
399    ///
400    /// As well as solutions to multiple [`Ast`]s, if passed through a [`Vec`], an [`array`] or a [`slice`]:
401    ///
402    /// ```
403    /// # use z3::Solver;
404    /// # use z3::ast::*;
405    ///  let s = Solver::new();
406    ///  let a = Int::new_const("a");
407    ///  s.assert(a.le(2));
408    ///  s.assert(a.ge(0));
409    ///  let solutions: Vec<_> = s.solutions(&[a.clone(), a+2], true).collect();
410    ///  // Doing all this to avoid relying on the order Z3 returns solutions.
411    ///  let mut solutions: Vec<Vec<_>> = solutions.into_iter().map(|a|a.into_iter().map(|b|b.as_u64().unwrap()).collect()).collect();
412    ///  solutions.sort_by(|a,b| a[0].cmp(&b[0]));
413    ///
414    ///  assert_eq!(vec![vec![0,2], vec![1,3], vec![2,4]], solutions);
415    /// ```
416    ///
417    /// It is also possible to pass in differing types of [`Ast`]s in a [`tuple`]. The traits to allow
418    /// this have been implemented for [`tuple`]s of arity 2 and 3. If you need more, it is suggested
419    /// to use a struct (see the next example):
420    ///
421    /// ```
422    /// # use z3::Solver;
423    /// # use z3::ast::*;
424    ///  let s = Solver::new();
425    ///  let a = Int::new_const("a");
426    ///  let b = BV::new_const("b", 8);
427    ///  s.assert(a.lt(1));
428    ///  s.assert(a.ge(0));
429    ///  s.assert(b.bvxor(0xff).to_int(false).eq(&a));
430    ///  let solutions: Vec<_> = s.solutions((a, b), true).collect();
431    ///  assert_eq!(solutions.len(), 1);
432    ///  let solution = &solutions[0];
433    ///  assert_eq!(solution.0, 0);
434    ///  assert_eq!(solution.1, 0xff);
435    /// ```
436    ///
437    /// Users can also implement [`Solvable`] on their types that wrap Z3 types to allow
438    /// iterating over their models with this API:
439    ///
440    /// ```
441    /// # use z3::ast::*;
442    /// # use z3::*;
443    /// #[derive(Clone)]
444    ///  struct MyStruct{
445    ///    pub a: Int,
446    ///    pub b: Int
447    ///  }
448    ///
449    ///  impl Solvable for MyStruct {
450    ///     type ModelInstance = Self;
451    ///     fn read_from_model(&self, model: &Model, model_completion: bool) -> Option<Self> {
452    ///         Some(
453    ///             Self{
454    ///                 a: model.eval(&self.a, model_completion).unwrap(),
455    ///                 b: model.eval(&self.b, model_completion).unwrap()
456    ///             }
457    ///         )
458    ///     }
459    ///
460    ///     fn generate_constraint(&self, model: &Self) -> Bool {
461    ///         Bool::or(&[self.a.eq(&model.a).not(), self.b.eq(&model.b).not()])
462    ///     }
463    ///  }
464    ///
465    ///  let s = Solver::new();
466    ///  let my_struct = MyStruct{
467    ///     a: Int::fresh_const("a"),
468    ///     b: Int::fresh_const("b")
469    ///  };
470    ///  // only valid model will be a = 0 and b = 4
471    ///  s.assert(my_struct.a.lt(1));
472    ///  s.assert(my_struct.a.ge(0));
473    ///  s.assert(my_struct.b.eq(&my_struct.a + 4));
474    ///
475    ///  let solutions: Vec<_> = s.solutions(&my_struct, true).collect();
476    ///  assert_eq!(solutions.len(), 1);
477    ///  assert_eq!(solutions[0].a, 0);
478    ///  assert_eq!(solutions[0].b, 4);
479    /// ```
480    pub fn solutions<T: Solvable>(
481        &self,
482        t: T,
483        model_completion: bool,
484    ) -> impl FusedIterator<Item = T::ModelInstance> {
485        SolverIterator {
486            solver: self.clone(),
487            ast: t,
488            model_completion,
489        }
490        .fuse()
491    }
492
493    /// Consume the current [`Solver`] and iterate over solutions to the given [`Solvable`].
494    ///
495    /// # Example
496    ///
497    /// ```
498    /// # use z3::Solver;
499    /// # use z3::ast::*;
500    ///  let s = Solver::new_for_logic("QF_BV").unwrap();
501    ///  let a = BV::new_const("a", 8);
502    ///  let b = BV::new_const("b", 8);
503    ///  s.assert(a.bvxor(0xff).eq(&b));
504    ///  s.assert(b.eq(0x25));
505    ///  let solutions: Vec<_> = s.into_solutions([a,b], true).collect();
506    ///  assert_eq!(solutions.len(), 1);
507    ///  let solution = &solutions[0];
508    ///  assert_eq!(solution[0], 0xda);
509    ///  assert_eq!(solution[1], 0x25);
510    ///```
511    /// # See also:
512    ///
513    /// - [`Solver::solutions`]
514    pub fn into_solutions<T: Solvable>(
515        self,
516        t: T,
517        model_completion: bool,
518    ) -> impl FusedIterator<Item = T::ModelInstance> {
519        SolverIterator {
520            solver: self,
521            ast: t,
522            model_completion,
523        }
524        .fuse()
525    }
526
527    /// Check the solver and, if satisfiable, return a single model instance for `t`.
528    ///
529    /// This is a convenience that combines `check()` + `get_model()` + `Solvable::read_from_model`.
530    /// If the check returns `SatResult::Sat` and model construction and extraction succeed, this
531    /// method returns `Some(instance)`. For any other result (`Unsat`, `Unknown`) or if model
532    /// construction/reading fails, it returns `None`.
533    ///
534    /// # Example
535    ///
536    /// ```
537    /// # use z3::Solver;
538    /// # use z3::ast::*;
539    ///  let s = Solver::new();
540    ///  let a = Int::new_const("a");
541    ///  s.assert(a.ge(0));
542    ///  s.assert(a.le(2));
543    ///  let concrete_a = s.check_and_get_model(a, true).unwrap();
544    ///  // `concrete_a` is an `Int` value extracted from the model
545    ///  let val = concrete_a.as_u64().unwrap();
546    ///  assert!(val <= 2);
547    /// ```
548    pub fn check_and_get_model<T: Solvable>(
549        self,
550        t: T,
551        model_completion: bool,
552    ) -> Option<T::ModelInstance> {
553        match self.check() {
554            SatResult::Sat => {
555                let model = self.get_model()?;
556                let instance = t.read_from_model(&model, model_completion)?;
557                Some(instance)
558            }
559            _ => None,
560        }
561    }
562}
563
564impl Default for Solver {
565    fn default() -> Self {
566        Self::new()
567    }
568}
569
570struct SolverIterator<T> {
571    solver: Solver,
572    ast: T,
573    model_completion: bool,
574}
575
576impl<T: Solvable> Iterator for SolverIterator<T> {
577    type Item = T::ModelInstance;
578
579    fn next(&mut self) -> Option<Self::Item> {
580        match self.solver.check() {
581            SatResult::Sat => {
582                let model = self.solver.get_model()?;
583                let instance = self.ast.read_from_model(&model, self.model_completion)?;
584                let counterexample = self.ast.generate_constraint(&instance);
585                self.solver.assert(counterexample);
586                Some(instance)
587            }
588            _ => None,
589        }
590    }
591}
592
593impl fmt::Display for Solver {
594    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
595        let p = unsafe { Z3_solver_to_string(self.ctx.z3_ctx.0, self.z3_slv) };
596        if p.is_null() {
597            return Result::Err(fmt::Error);
598        }
599        match unsafe { CStr::from_ptr(p) }.to_str() {
600            Ok(s) => write!(f, "{s}"),
601            Err(_) => Result::Err(fmt::Error),
602        }
603    }
604}
605
606impl fmt::Debug for Solver {
607    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
608        <Self as fmt::Display>::fmt(self, f)
609    }
610}
611
612impl Drop for Solver {
613    fn drop(&mut self) {
614        unsafe { Z3_solver_dec_ref(self.ctx.z3_ctx.0, self.z3_slv) };
615    }
616}
617
618/// Creates a new [`Solver`] with the same assertions, tactics, and parameters
619/// as the original
620impl Clone for Solver {
621    fn clone(self: &Solver) -> Self {
622        self.translate(&Context::thread_local())
623    }
624}
625
626unsafe impl Translate for Solver {
627    fn translate(&self, dest: &Context) -> Solver {
628        unsafe {
629            Solver::wrap(
630                dest,
631                Z3_solver_translate(self.ctx.z3_ctx.0, self.z3_slv, dest.z3_ctx.0).unwrap(),
632            )
633        }
634    }
635}
636
637impl AddAssign<&Bool> for Solver {
638    fn add_assign(&mut self, rhs: &Bool) {
639        self.assert(rhs);
640    }
641}
642
643impl AddAssign<Bool> for Solver {
644    fn add_assign(&mut self, rhs: Bool) {
645        self.assert(&rhs);
646    }
647}
648
649/// Indicates that a type can be evaluated from a [`Model`] and produce a [`Bool`] counterexample,
650/// allowing usage in the [`Solver::solutions`] iterator pattern.
651///
652/// Specifically, types implementing this trait:
653/// * Can read a Z3 [`Model`] in a structured way to produce an instance
654///   of the type with its inner [`Ast`]s constrained by the [`Model`]
655/// * Can generate a counter-example assertion from its internal [`Ast`]s
656///   to constrain a [`Solver`] (usually just a disjunction of "not-equal"s)
657pub trait Solvable: Sized + Clone {
658    /// The type to be extracted from the [`Solver`]'s Model.
659    ///
660    /// It will usually just be [`Self`] and must be [`Solvable`]. This is only an associated
661    /// type and not just hard-coded to [`Self`] to allow for passing both owned and borrowed
662    /// values into [`Solver::solutions`], and to allow implementing this trait for types like
663    /// `&[T]`.
664    type ModelInstance: Solvable;
665
666    /// Defines how to derive data derived from the implementing type (usually just a [`Self`])
667    /// and a given [`Model`].
668    ///
669    /// Usually this just invokes [`Model::eval`] on some [`Ast`]s and wraps
670    /// it up into the proper type.
671    fn read_from_model(&self, model: &Model, model_completion: bool)
672    -> Option<Self::ModelInstance>;
673
674    /// Produce a [`Bool`] assertion ruling out the given model from the valuation of `self`.
675    ///
676    /// This is used to advance the [`Solver`] in [`Solver::solutions`]. This is usually just a
677    /// disjunction (in case of multiple terms) of "not equal" assertions between `self` and `model`.
678    fn generate_constraint(&self, model: &Self::ModelInstance) -> Bool;
679}
680
681impl<T: Solvable> Solvable for &T {
682    type ModelInstance = T::ModelInstance;
683    fn read_from_model(
684        &self,
685        model: &Model,
686        model_completion: bool,
687    ) -> Option<Self::ModelInstance> {
688        (*self).read_from_model(model, model_completion)
689    }
690
691    fn generate_constraint(&self, model: &Self::ModelInstance) -> Bool {
692        (*self).generate_constraint(model)
693    }
694}
695
696impl<T: Solvable> Solvable for Vec<T> {
697    type ModelInstance = Vec<T::ModelInstance>;
698    fn read_from_model(
699        &self,
700        model: &Model,
701        model_completion: bool,
702    ) -> Option<Self::ModelInstance> {
703        self.iter()
704            .map(|x| x.read_from_model(model, model_completion))
705            .collect()
706    }
707
708    fn generate_constraint(&self, model: &Self::ModelInstance) -> Bool {
709        let bools: Vec<_> = self
710            .iter()
711            .zip(model)
712            .map(|(a, b)| a.generate_constraint(b))
713            .collect();
714        Bool::or(&bools)
715    }
716}
717
718impl<T: Solvable> Solvable for &[T] {
719    type ModelInstance = Vec<T::ModelInstance>;
720    fn read_from_model(
721        &self,
722        model: &Model,
723        model_completion: bool,
724    ) -> Option<Self::ModelInstance> {
725        self.iter()
726            .map(|x| x.read_from_model(model, model_completion))
727            .collect()
728    }
729
730    fn generate_constraint(&self, model: &Self::ModelInstance) -> Bool {
731        let bools: Vec<_> = self
732            .iter()
733            .zip(model)
734            .map(|(a, b)| a.generate_constraint(b))
735            .collect();
736        Bool::or(&bools)
737    }
738}
739
740impl<T: Solvable + Clone, const N: usize> Solvable for [T; N] {
741    type ModelInstance = [T::ModelInstance; N];
742    fn read_from_model(
743        &self,
744        model: &Model,
745        model_completion: bool,
746    ) -> Option<Self::ModelInstance> {
747        let v: Option<Vec<_>> = self
748            .iter()
749            .map(|x| x.read_from_model(model, model_completion))
750            .collect();
751        let v = v?;
752        let a: [T::ModelInstance; N] = v.try_into().ok()?;
753        Some(a)
754    }
755
756    fn generate_constraint(&self, model: &Self::ModelInstance) -> Bool {
757        let bools: Vec<_> = self
758            .iter()
759            .zip(model)
760            .map(|(a, b)| a.generate_constraint(b))
761            .collect();
762        Bool::or(&bools)
763    }
764}
765impl<A: Solvable, B: Solvable> Solvable for (A, B) {
766    type ModelInstance = (A::ModelInstance, B::ModelInstance);
767    fn read_from_model(
768        &self,
769        model: &Model,
770        model_completion: bool,
771    ) -> Option<Self::ModelInstance> {
772        let (a, b) = self;
773        Some((
774            a.read_from_model(model, model_completion)?,
775            b.read_from_model(model, model_completion)?,
776        ))
777    }
778
779    fn generate_constraint(&self, model: &Self::ModelInstance) -> Bool {
780        let (a1, b1) = self;
781        let (a2, b2) = model;
782        Bool::or(&[a1.generate_constraint(a2), b1.generate_constraint(b2)])
783    }
784}
785
786impl<A: Solvable, B: Solvable, C: Solvable> Solvable for (A, B, C) {
787    type ModelInstance = (A::ModelInstance, B::ModelInstance, C::ModelInstance);
788    fn read_from_model(
789        &self,
790        model: &Model,
791        model_completion: bool,
792    ) -> Option<Self::ModelInstance> {
793        let (a, b, c) = self;
794        Some((
795            a.read_from_model(model, model_completion)?,
796            b.read_from_model(model, model_completion)?,
797            c.read_from_model(model, model_completion)?,
798        ))
799    }
800
801    fn generate_constraint(&self, model: &Self::ModelInstance) -> Bool {
802        let (a1, b1, c1) = self;
803        let (a2, b2, c2) = model;
804        Bool::or(&[
805            a1.generate_constraint(a2),
806            b1.generate_constraint(b2),
807            c1.generate_constraint(c2),
808        ])
809    }
810}
811
812// todo: there may be a way to do this with a macro, but I can't figure it out, without needing
813// to bring in the `paste` crate. Since this is niche anyway, I'm just going to do these two and
814// we can add more later if needed.