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.