Skip to main content

z3/
context.rs

1use log::debug;
2use std::cell::RefCell;
3use std::clone::Clone;
4use std::ffi::CString;
5use std::rc::Rc;
6use z3_sys::*;
7
8use crate::{Config, ContextHandle};
9
10/// A wrapper around [`Z3_context`] that enforces proper dropping behavior.
11/// All high-level code should instead use [`Context`]
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub(crate) struct ContextInternal(pub(crate) Z3_context);
14
15impl Drop for ContextInternal {
16    fn drop(&mut self) {
17        unsafe { Z3_del_context(self.0) };
18    }
19}
20
21/// Manager of all other Z3 objects, global configuration options, etc.
22///
23/// An application may use multiple Z3 contexts. Objects created in one context
24/// cannot be used in another one. However, several objects may be "translated" from
25/// one context to another.
26///
27/// While it is not safe to access Z3 objects from multiple threads, this library includes
28/// a safe structured abstraction for usage of Z3 objects across threads.
29/// See [`Synchronized`](crate::Synchronized).
30///
31/// z3.rs uses an implicit thread-local context by default, which is used in all operations.
32/// Most users will not need to create their own contexts or interact with contexts directly.
33///
34/// To use a context with a customized configuration (e.g. setting timeouts),
35/// use [`with_z3_config`](crate::with_z3_config).
36///
37/// Advanced users (e.g. those performing FFI with other Z3 bindings) can unsafely create their
38/// own contexts using [`Context::from_raw`], and then use them with [`with_z3_context`](crate::with_z3_context).
39///
40/// # See also:
41///
42/// - [`Config`]
43/// - [`with_z3_config`](crate::with_z3_config)
44/// - [`with_z3_context`](crate::with_z3_context)
45/// - [`Synchronized`](crate::Synchronized)
46/// - [`Translate`](crate::Translate)
47#[derive(PartialEq, Eq, Debug, Clone)]
48pub struct Context {
49    pub(crate) z3_ctx: Rc<ContextInternal>,
50}
51impl Context {
52    /// Returns a handle to the default thread-local [`Context`].
53    ///
54    /// This [`Context`] is used in all z3 operations.
55    /// Custom [`Context`]s are supported through [`with_z3_config`](crate::with_z3_config),
56    /// which allow for running a closure inside an environment with the provided [`Context`]
57    ///
58    /// # See also:
59    /// - [`with_z3_config`](crate::with_z3_config)
60    pub fn thread_local() -> Context {
61        DEFAULT_CONTEXT.with(|f| f.borrow().clone())
62    }
63
64    /// _Replaces_ the thread-local [`Context`] with a new one created from the given [`Config`].
65    /// This is useful if you want to use a specific configuration, but also want
66    /// to use the default thread-local context. Note that calling this function will
67    /// _replace_ the existing thread-local context. All existing [`Ast`]s and other Z3 objects
68    /// that were created using the previous thread-local context will still be valid, but cannot
69    /// be used with the new context without translation. You will generally want to call this
70    /// before you create any [`Ast`]s or other Z3 objects to avoid confusion. Also note that
71    /// since the [`Context`] is thread-local, this will only affect the current thread.
72    ///
73    /// # See also:
74    /// /// - [`Context::thread_local()`]
75    pub(crate) fn set_thread_local(ctx: &Context) {
76        DEFAULT_CONTEXT.with(|f| {
77            *f.borrow_mut() = ctx.clone();
78        });
79    }
80
81    /// Creates a new Z3 Context using the given configuration.
82    pub(crate) fn new(cfg: &Config) -> Context {
83        Context {
84            z3_ctx: unsafe {
85                let p = Z3_mk_context_rc(cfg.z3_cfg).unwrap();
86                debug!("new context {p:p}");
87                Z3_set_error_handler(p, None);
88                Rc::new(ContextInternal(p))
89            },
90        }
91    }
92
93    /// Construct a [`Context`] from a raw [`Z3_context`] pointer. This is mostly useful for
94    /// consumers who want to interoperate with Z3 contexts created through other means,
95    /// such as the C API or other bindings such as Python.
96    ///
97    /// # Safety
98    ///
99    /// The caller must ensure the pointer is valid and not already managed elsewhere.
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// use z3::ast::Bool;
105    /// use z3_sys::{Z3_mk_config, Z3_del_config, Z3_mk_context_rc};
106    /// use z3::Context;
107    ///
108    /// // Create a raw Z3_config using the low-level API
109    /// let cfg = unsafe { Z3_mk_config() }.unwrap();
110    /// let raw_ctx = unsafe { Z3_mk_context_rc(cfg) }.unwrap();
111    /// let ctx = unsafe { Context::from_raw(raw_ctx) };
112    /// // Use `ctx` as usual...
113    /// unsafe { Z3_del_config(cfg) };
114    /// let b = Bool::from_bool(true);
115    /// assert_eq!(b.as_bool(), Some(true));
116    /// ```
117    pub unsafe fn from_raw(z3_ctx: Z3_context) -> Context {
118        debug!("from_raw context {z3_ctx:p}");
119        Context {
120            z3_ctx: Rc::new(ContextInternal(z3_ctx)),
121        }
122    }
123
124    pub fn get_z3_context(&self) -> Z3_context {
125        self.z3_ctx.0
126    }
127
128    /// Interrupt a solver performing a satisfiability test, a tactic processing a goal, or simplify functions.
129    pub fn interrupt(&self) {
130        self.handle().interrupt();
131    }
132
133    /// Obtain a handle that can be used to interrupt computation from another thread.
134    ///
135    /// # See also:
136    ///
137    /// - [`ContextHandle`]
138    /// - [`ContextHandle::interrupt()`]
139    pub fn handle(&self) -> ContextHandle<'_> {
140        ContextHandle { ctx: self }
141    }
142
143    /// Update a global parameter.
144    ///
145    /// # See also
146    ///
147    /// - [`Context::update_bool_param_value()`]
148    pub fn update_param_value(&mut self, k: &str, v: &str) {
149        let ks = CString::new(k).unwrap();
150        let vs = CString::new(v).unwrap();
151        unsafe { Z3_update_param_value(self.z3_ctx.0, ks.as_ptr(), vs.as_ptr()) };
152    }
153
154    /// Update a global parameter.
155    ///
156    /// This is a helper function.
157    ///
158    /// # See also
159    ///
160    /// - [`Context::update_param_value()`]
161    pub fn update_bool_param_value(&mut self, k: &str, v: bool) {
162        self.update_param_value(k, if v { "true" } else { "false" });
163    }
164}
165
166impl ContextHandle<'_> {
167    /// Interrupt a solver performing a satisfiability test, a tactic processing a goal, or simplify functions.
168    pub fn interrupt(&self) {
169        unsafe {
170            Z3_interrupt(self.ctx.z3_ctx.0);
171        }
172    }
173}
174
175unsafe impl Sync for ContextHandle<'_> {}
176unsafe impl Send for ContextHandle<'_> {}
177
178thread_local! {
179    static DEFAULT_CONTEXT: RefCell<Context> = RefCell::new(Context::new(&Config::new()));
180}