1use std::convert::TryFrom;
2use std::ffi::{CStr, CString};
3use std::fmt;
4use std::os::raw::c_uint;
5use std::result::Result;
6use std::str::Utf8Error;
7use std::time::Duration;
8use z3_sys::*;
9
10use crate::{ApplyResult, Context, Goal, Params, Probe, Solver, Tactic};
11
12impl ApplyResult {
13 unsafe fn wrap(ctx: &Context, z3_apply_result: Z3_apply_result) -> ApplyResult {
14 unsafe {
15 Z3_apply_result_inc_ref(ctx.z3_ctx.0, z3_apply_result);
16 }
17 ApplyResult {
18 ctx: ctx.clone(),
19 z3_apply_result,
20 }
21 }
22
23 pub fn list_subgoals(self) -> impl Iterator<Item = Goal> {
24 let num_subgoals =
25 unsafe { Z3_apply_result_get_num_subgoals(self.ctx.z3_ctx.0, self.z3_apply_result) };
26 (0..num_subgoals).map(move |i| unsafe {
27 Goal::wrap(
28 &self.ctx,
29 Z3_apply_result_get_subgoal(self.ctx.z3_ctx.0, self.z3_apply_result, i).unwrap(),
30 )
31 })
32 }
33}
34
35impl Drop for ApplyResult {
36 fn drop(&mut self) {
37 unsafe {
38 Z3_apply_result_dec_ref(self.ctx.z3_ctx.0, self.z3_apply_result);
39 }
40 }
41}
42
43impl Tactic {
44 pub fn list_all() -> Vec<Result<String, Utf8Error>> {
54 let ctx = &Context::thread_local();
55 let p = unsafe { Z3_get_num_tactics(ctx.z3_ctx.0) };
56 (0..p)
57 .map(move |n| {
58 let t = unsafe { Z3_get_tactic_name(ctx.z3_ctx.0, n) };
59 unsafe { CStr::from_ptr(t) }.to_str().map(String::from)
60 })
61 .collect()
62 }
63
64 unsafe fn wrap(ctx: &Context, z3_tactic: Z3_tactic) -> Tactic {
65 unsafe {
66 Z3_tactic_inc_ref(ctx.z3_ctx.0, z3_tactic);
67 }
68 Tactic {
69 ctx: ctx.clone(),
70 z3_tactic,
71 }
72 }
73
74 pub fn new(name: &str) -> Tactic {
87 let ctx = &Context::thread_local();
88 let tactic_name = CString::new(name).unwrap();
89
90 unsafe {
91 let tactic = Z3_mk_tactic(ctx.z3_ctx.0, tactic_name.as_ptr())
92 .unwrap_or_else(|| panic!("{name} is an invalid tactic"));
93 Self::wrap(ctx, tactic)
94 }
95 }
96
97 pub fn create_skip() -> Tactic {
99 let ctx = &Context::thread_local();
100 unsafe { Self::wrap(ctx, Z3_tactic_skip(ctx.z3_ctx.0).unwrap()) }
101 }
102
103 pub fn create_fail() -> Tactic {
105 let ctx = &Context::thread_local();
106 unsafe { Self::wrap(ctx, Z3_tactic_fail(ctx.z3_ctx.0).unwrap()) }
107 }
108
109 pub fn repeat(t: &Tactic, max: u32) -> Tactic {
112 let ctx = &Context::thread_local();
113 unsafe {
114 Self::wrap(
115 ctx,
116 Z3_tactic_repeat(ctx.z3_ctx.0, t.z3_tactic, max).unwrap(),
117 )
118 }
119 }
120
121 pub fn try_for(&self, timeout: Duration) -> Tactic {
124 let timeout_ms = c_uint::try_from(timeout.as_millis()).unwrap_or(c_uint::MAX);
125 unsafe {
126 Self::wrap(
127 &self.ctx,
128 Z3_tactic_try_for(self.ctx.z3_ctx.0, self.z3_tactic, timeout_ms).unwrap(),
129 )
130 }
131 }
132
133 pub fn and_then(&self, then_tactic: &Tactic) -> Tactic {
136 unsafe {
137 Self::wrap(
138 &self.ctx,
139 Z3_tactic_and_then(self.ctx.z3_ctx.0, self.z3_tactic, then_tactic.z3_tactic)
140 .unwrap(),
141 )
142 }
143 }
144
145 pub fn or_else(&self, else_tactic: &Tactic) -> Tactic {
148 unsafe {
149 Self::wrap(
150 &self.ctx,
151 Z3_tactic_or_else(self.ctx.z3_ctx.0, self.z3_tactic, else_tactic.z3_tactic)
152 .unwrap(),
153 )
154 }
155 }
156
157 pub fn probe_or_else(&self, p: &Probe, t: &Tactic) -> Tactic {
160 unsafe {
161 Self::wrap(
162 &self.ctx,
163 Z3_tactic_cond(self.ctx.z3_ctx.0, p.z3_probe, self.z3_tactic, t.z3_tactic).unwrap(),
164 )
165 }
166 }
167
168 pub fn when(&self, p: &Probe) -> Tactic {
171 unsafe {
172 Self::wrap(
173 &self.ctx,
174 Z3_tactic_when(self.ctx.z3_ctx.0, p.z3_probe, self.z3_tactic).unwrap(),
175 )
176 }
177 }
178
179 pub fn cond(p: &Probe, t1: &Tactic, t2: &Tactic) -> Tactic {
182 assert_eq!(p.ctx, t1.ctx);
183 assert_eq!(t1.ctx, t2.ctx);
184 unsafe {
185 Self::wrap(
186 &p.ctx,
187 Z3_tactic_cond(p.ctx.z3_ctx.0, p.z3_probe, t1.z3_tactic, t2.z3_tactic).unwrap(),
188 )
189 }
190 }
191
192 pub fn fail_if(p: &Probe) -> Tactic {
194 unsafe {
195 Self::wrap(
196 &p.ctx,
197 Z3_tactic_fail_if(p.ctx.z3_ctx.0, p.z3_probe).unwrap(),
198 )
199 }
200 }
201
202 pub fn apply(&self, goal: &Goal, params: Option<&Params>) -> Result<ApplyResult, String> {
206 unsafe {
207 let z3_apply_result = match params {
208 None => Z3_tactic_apply(self.ctx.z3_ctx.0, self.z3_tactic, goal.z3_goal),
209 Some(params) => Z3_tactic_apply_ex(
210 self.ctx.z3_ctx.0,
211 self.z3_tactic,
212 goal.z3_goal,
213 params.z3_params,
214 ),
215 };
216 if let Some(z3_apply_result) = z3_apply_result {
217 Ok(ApplyResult::wrap(&self.ctx, z3_apply_result))
218 } else {
219 let code = Z3_get_error_code(self.ctx.z3_ctx.0);
220 let msg = Z3_get_error_msg(self.ctx.z3_ctx.0, code);
221 Err(String::from(CStr::from_ptr(msg).to_str().unwrap_or(
222 "Couldn't retrieve error message from z3: got invalid UTF-8",
223 )))
224 }
225 }
226 }
227
228 pub fn solver(&self) -> Solver {
246 unsafe {
247 Solver::wrap(
248 &self.ctx,
249 Z3_mk_solver_from_tactic(self.ctx.z3_ctx.0, self.z3_tactic).unwrap(),
250 )
251 }
252 }
253}
254
255impl fmt::Display for Tactic {
256 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
257 let p = unsafe { Z3_tactic_get_help(self.ctx.z3_ctx.0, self.z3_tactic) };
258 if p.is_null() {
259 return Result::Err(fmt::Error);
260 }
261 match unsafe { CStr::from_ptr(p) }.to_str() {
262 Ok(s) => write!(f, "{s}"),
263 Err(_) => Result::Err(fmt::Error),
264 }
265 }
266}
267
268impl fmt::Debug for Tactic {
269 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
270 <Self as fmt::Display>::fmt(self, f)
271 }
272}
273
274impl Drop for Tactic {
275 fn drop(&mut self) {
276 unsafe {
277 Z3_tactic_dec_ref(self.ctx.z3_ctx.0, self.z3_tactic);
278 }
279 }
280}