├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── src ├── ast.rs ├── config.rs ├── context.rs ├── lib.rs ├── model.rs ├── optimize.rs ├── solver.rs ├── sort.rs └── symbol.rs └── tests ├── lib.rs └── semver_tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *~ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | sudo: false 7 | 8 | script: 9 | - cargo build 10 | - cargo test 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "z3" 3 | version = "0.2.0" 4 | authors = ["Graydon Hoare "] 5 | 6 | description = "High-level rust bindings for the Z3 SMT solver from Microsoft Research" 7 | license = "MIT" 8 | keywords = ["FFI", "SMT", "satisfiability", "solver"] 9 | readme = "README.md" 10 | homepage = "https://github.com/graydon/z3-rs" 11 | repository = "https://github.com/graydon/z3-rs.git" 12 | 13 | [dependencies] 14 | libc = "0.2" 15 | log = "0.3" 16 | lazy_static = "0.1" 17 | env_logger = "0.3" 18 | z3-sys = "~0.1.0" 19 | 20 | [dev-dependencies] 21 | semver = "~0.2.1" -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, 2016 Graydon Hoare 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | High-level rust bindings to the Z3 SMT solver 2 | 3 | See [https://github.com/Z3Prover/z3](https://github.com/Z3Prover/z3) for details on Z3. 4 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | use z3_sys::*; 2 | use Context; 3 | use Sort; 4 | use Symbol; 5 | use Ast; 6 | use Z3_MUTEX; 7 | use std::hash::{Hash, Hasher}; 8 | use std::cmp::{PartialEq, Eq}; 9 | use std::ffi::CString; 10 | 11 | macro_rules! unop { 12 | ( $f:ident, $z3fn:ident ) => { 13 | pub fn $f(&self) -> Ast<'ctx> { 14 | Ast::new(self.ctx, unsafe { 15 | let guard = Z3_MUTEX.lock().unwrap(); 16 | $z3fn(self.ctx.z3_ctx, self.z3_ast) 17 | }) 18 | } 19 | }; 20 | } 21 | 22 | macro_rules! binop { 23 | ( $f:ident, $z3fn:ident ) => { 24 | pub fn $f(&self, other: &Ast<'ctx>) -> Ast<'ctx> { 25 | Ast::new(self.ctx, unsafe { 26 | let guard = Z3_MUTEX.lock().unwrap(); 27 | $z3fn(self.ctx.z3_ctx, self.z3_ast, other.z3_ast) 28 | }) 29 | } 30 | }; 31 | } 32 | 33 | macro_rules! trinop { 34 | ( $f:ident, $z3fn:ident ) => { 35 | pub fn $f(&self, a: &Ast<'ctx>, b: &Ast<'ctx>) -> Ast<'ctx> { 36 | Ast::new(self.ctx, unsafe { 37 | let guard = Z3_MUTEX.lock().unwrap(); 38 | $z3fn(self.ctx.z3_ctx, self.z3_ast, a.z3_ast, b.z3_ast) 39 | }) 40 | } 41 | }; 42 | } 43 | 44 | macro_rules! varop { 45 | ( $f:ident, $z3fn:ident ) => { 46 | pub fn $f(&self, other: &[&Ast<'ctx>]) -> Ast<'ctx> { 47 | Ast::new(self.ctx, unsafe { 48 | let guard = Z3_MUTEX.lock().unwrap(); 49 | let mut tmp = vec![self.z3_ast]; 50 | for a in other { 51 | tmp.push(a.z3_ast) 52 | } 53 | assert!(tmp.len() <= 0xffffffff); 54 | $z3fn(self.ctx.z3_ctx, tmp.len() as u32, tmp.as_ptr()) 55 | }) 56 | } 57 | }; 58 | } 59 | 60 | impl<'ctx> Ast<'ctx> { 61 | 62 | pub fn new(ctx: &Context, ast: Z3_ast) -> Ast { 63 | assert!(!ast.is_null()); 64 | Ast { 65 | ctx: ctx, 66 | z3_ast: unsafe { 67 | debug!("new ast {:p}", ast); 68 | let guard = Z3_MUTEX.lock().unwrap(); 69 | Z3_inc_ref(ctx.z3_ctx, ast); 70 | ast 71 | } 72 | } 73 | } 74 | 75 | pub fn new_const(sym: &Symbol<'ctx>, 76 | sort: &Sort<'ctx>) -> Ast<'ctx> { 77 | Ast::new(sym.ctx, unsafe { 78 | let guard = Z3_MUTEX.lock().unwrap(); 79 | Z3_mk_const(sym.ctx.z3_ctx, sym.z3_sym, sort.z3_sort) 80 | }) 81 | } 82 | 83 | pub fn fresh_const(ctx: &'ctx Context, 84 | prefix: &str, 85 | sort: &Sort<'ctx>) -> Ast<'ctx> { 86 | Ast::new(ctx, unsafe { 87 | let pp = CString::new(prefix).unwrap(); 88 | let p = pp.as_ptr(); 89 | let guard = Z3_MUTEX.lock().unwrap(); 90 | Z3_mk_fresh_const(ctx.z3_ctx, p, sort.z3_sort) 91 | }) 92 | } 93 | 94 | pub fn from_bool(ctx: &'ctx Context, b: bool) -> Ast<'ctx> { 95 | Ast::new(ctx, unsafe { 96 | let guard = Z3_MUTEX.lock().unwrap(); 97 | if b { 98 | Z3_mk_true(ctx.z3_ctx) 99 | } else { 100 | Z3_mk_false(ctx.z3_ctx) 101 | } 102 | }) 103 | } 104 | 105 | pub fn from_i64(ctx: &'ctx Context, i: i64) -> Ast<'ctx> { 106 | Ast::new(ctx, unsafe { 107 | let sort = ctx.int_sort(); 108 | let guard = Z3_MUTEX.lock().unwrap(); 109 | Z3_mk_int64(ctx.z3_ctx, i, sort.z3_sort) 110 | }) 111 | } 112 | 113 | pub fn from_u64(ctx: &'ctx Context, u: u64) -> Ast<'ctx> { 114 | Ast::new(ctx, unsafe { 115 | let sort = ctx.int_sort(); 116 | let guard = Z3_MUTEX.lock().unwrap(); 117 | Z3_mk_unsigned_int64(ctx.z3_ctx, u, sort.z3_sort) 118 | }) 119 | } 120 | 121 | pub fn from_real(ctx: &'ctx Context, num: i32, den: i32) -> Ast<'ctx> { 122 | Ast::new(ctx, unsafe { 123 | let guard = Z3_MUTEX.lock().unwrap(); 124 | Z3_mk_real(ctx.z3_ctx, 125 | num as ::libc::c_int, 126 | den as ::libc::c_int) 127 | }) 128 | } 129 | 130 | pub fn as_bool(&self) -> Option { 131 | unsafe { 132 | let guard = Z3_MUTEX.lock().unwrap(); 133 | match Z3_get_bool_value(self.ctx.z3_ctx, self.z3_ast) { 134 | Z3_L_TRUE => Some(true), 135 | Z3_L_FALSE => Some(false), 136 | _ => None 137 | } 138 | } 139 | } 140 | 141 | pub fn as_i64(&self) -> Option { 142 | unsafe { 143 | let guard = Z3_MUTEX.lock().unwrap(); 144 | let mut tmp : ::libc::c_longlong = 0; 145 | if Z3_TRUE == Z3_get_numeral_int64(self.ctx.z3_ctx, 146 | self.z3_ast, &mut tmp) { 147 | Some(tmp) 148 | } else { 149 | None 150 | } 151 | } 152 | } 153 | 154 | pub fn as_u64(&self) -> Option { 155 | unsafe { 156 | let guard = Z3_MUTEX.lock().unwrap(); 157 | let mut tmp : ::libc::c_ulonglong = 0; 158 | if Z3_TRUE == Z3_get_numeral_uint64(self.ctx.z3_ctx, 159 | self.z3_ast, &mut tmp) { 160 | Some(tmp) 161 | } else { 162 | None 163 | } 164 | } 165 | } 166 | 167 | pub fn as_real(&self) -> Option<(i64,i64)> { 168 | unsafe { 169 | let guard = Z3_MUTEX.lock().unwrap(); 170 | let mut num : i64 = 0; 171 | let mut den : i64 = 0; 172 | if Z3_TRUE == Z3_get_numeral_small(self.ctx.z3_ctx, 173 | self.z3_ast, 174 | &mut num, &mut den) { 175 | Some((num,den)) 176 | } else { 177 | None 178 | } 179 | } 180 | } 181 | 182 | varop!(distinct, Z3_mk_distinct); 183 | 184 | // Boolean ops 185 | trinop!(ite, Z3_mk_ite); 186 | binop!(iff, Z3_mk_iff); 187 | binop!(implies, Z3_mk_implies); 188 | binop!(xor, Z3_mk_xor); 189 | varop!(and, Z3_mk_and); 190 | varop!(or, Z3_mk_or); 191 | varop!(add, Z3_mk_add); 192 | varop!(sub, Z3_mk_sub); 193 | varop!(mul, Z3_mk_mul); 194 | unop!(not, Z3_mk_not); 195 | 196 | // Numeric ops 197 | binop!(div, Z3_mk_div); 198 | binop!(rem, Z3_mk_rem); 199 | binop!(modulo, Z3_mk_mod); 200 | binop!(power, Z3_mk_power); 201 | unop!(minus, Z3_mk_unary_minus); 202 | binop!(lt, Z3_mk_lt); 203 | binop!(le, Z3_mk_le); 204 | binop!(_eq, Z3_mk_eq); 205 | binop!(ge, Z3_mk_ge); 206 | binop!(gt, Z3_mk_gt); 207 | unop!(int2real, Z3_mk_int2real); 208 | unop!(real2int, Z3_mk_real2int); 209 | unop!(is_int, Z3_mk_is_int); 210 | 211 | // Bitvector ops 212 | unop!(bvnot, Z3_mk_bvnot); 213 | unop!(bvneg, Z3_mk_bvneg); 214 | unop!(bvredand, Z3_mk_bvredand); 215 | unop!(bvredor, Z3_mk_bvredor); 216 | binop!(bvand, Z3_mk_bvand); 217 | binop!(bvor, Z3_mk_bvor); 218 | binop!(bvxor, Z3_mk_bvxor); 219 | binop!(bvnand, Z3_mk_bvnand); 220 | binop!(bvnor, Z3_mk_bvnor); 221 | binop!(bvxnor, Z3_mk_bvxnor); 222 | binop!(bvadd, Z3_mk_bvadd); 223 | binop!(bvsub, Z3_mk_bvsub); 224 | binop!(bvmul, Z3_mk_bvmul); 225 | binop!(bvudiv, Z3_mk_bvudiv); 226 | binop!(bvsdiv, Z3_mk_bvsdiv); 227 | binop!(bvurem, Z3_mk_bvurem); 228 | binop!(bvsrem, Z3_mk_bvsrem); 229 | binop!(bvsmod, Z3_mk_bvsmod); 230 | binop!(bvult, Z3_mk_bvult); 231 | binop!(bvslt, Z3_mk_bvslt); 232 | binop!(bvule, Z3_mk_bvule); 233 | binop!(bvsle, Z3_mk_bvsle); 234 | binop!(bvuge, Z3_mk_bvuge); 235 | binop!(bvsge, Z3_mk_bvsge); 236 | binop!(bvugt, Z3_mk_bvugt); 237 | binop!(bvsgt, Z3_mk_bvsgt); 238 | binop!(concat, Z3_mk_concat); 239 | binop!(bvshl, Z3_mk_bvshl); 240 | binop!(bvlshr, Z3_mk_bvlshr); 241 | binop!(bvashr, Z3_mk_bvashr); 242 | 243 | // Array ops 244 | binop!(select, Z3_mk_select); 245 | trinop!(store, Z3_mk_store); 246 | 247 | // Set ops 248 | binop!(set_add, Z3_mk_set_add); 249 | binop!(set_del, Z3_mk_set_del); 250 | varop!(set_union, Z3_mk_set_union); 251 | varop!(set_intersect, Z3_mk_set_intersect); 252 | binop!(set_member, Z3_mk_set_member); 253 | binop!(set_subset, Z3_mk_set_subset); 254 | unop!(set_complement, Z3_mk_set_complement); 255 | } 256 | 257 | impl<'ctx> Drop for Ast<'ctx> { 258 | fn drop(&mut self) { 259 | unsafe { 260 | debug!("drop ast {:p}", self.z3_ast); 261 | let guard = Z3_MUTEX.lock().unwrap(); 262 | Z3_dec_ref(self.ctx.z3_ctx, self.z3_ast); 263 | } 264 | } 265 | } 266 | 267 | impl<'ctx> Hash for Ast<'ctx> { 268 | fn hash(&self, state: &mut H) { 269 | unsafe { 270 | let u = Z3_get_ast_hash(self.ctx.z3_ctx, self.z3_ast); 271 | u.hash(state); 272 | } 273 | } 274 | } 275 | 276 | impl<'ctx> PartialEq> for Ast<'ctx> { 277 | fn eq(&self, other: &Ast<'ctx>) -> bool { 278 | unsafe { 279 | Z3_TRUE == Z3_is_eq_ast(self.ctx.z3_ctx, 280 | self.z3_ast, 281 | other.z3_ast) 282 | } 283 | } 284 | } 285 | 286 | impl<'ctx> Eq for Ast<'ctx> { } 287 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use z3_sys::*; 2 | use Config; 3 | use Z3_MUTEX; 4 | use std::ffi::CString; 5 | 6 | impl Config { 7 | pub fn new() -> Config { 8 | Config { 9 | kvs: Vec::new(), 10 | z3_cfg: unsafe { 11 | let guard = Z3_MUTEX.lock().unwrap(); 12 | let p = Z3_mk_config(); 13 | debug!("new config {:p}", p); 14 | p 15 | } 16 | } 17 | } 18 | pub fn set_param_value(&mut self, k: &str, v: &str) { 19 | let ks = CString::new(k).unwrap(); 20 | let vs = CString::new(v).unwrap(); 21 | self.kvs.push((ks, vs)); 22 | unsafe { 23 | let guard = Z3_MUTEX.lock().unwrap(); 24 | Z3_set_param_value(self.z3_cfg, 25 | self.kvs.last().unwrap().0.as_ptr(), 26 | self.kvs.last().unwrap().1.as_ptr()); 27 | } 28 | } 29 | 30 | pub fn set_bool_param_value(&mut self, k: &str, v: bool) { 31 | self.set_param_value(k, if v { "true" } else { "false" }); 32 | } 33 | 34 | // Helpers for common parameters 35 | pub fn set_proof_generation(&mut self, b: bool) 36 | { 37 | self.set_bool_param_value("proof", b); 38 | } 39 | 40 | pub fn set_model_generation(&mut self, b: bool) 41 | { 42 | self.set_bool_param_value("model", b); 43 | } 44 | 45 | pub fn set_debug_ref_count(&mut self, b: bool) 46 | { 47 | self.set_bool_param_value("debug_ref_count", b); 48 | } 49 | 50 | pub fn set_timeout_msec(&mut self, ms: u64) 51 | { 52 | self.set_param_value("timeout", &format!("{}", ms)); 53 | } 54 | } 55 | 56 | impl Drop for Config { 57 | fn drop(&mut self) { 58 | unsafe { 59 | debug!("drop config {:p}", self.z3_cfg); 60 | let guard = Z3_MUTEX.lock().unwrap(); 61 | Z3_del_config(self.z3_cfg); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | use z3_sys::*; 2 | use Config; 3 | use Context; 4 | use Sort; 5 | use Symbol; 6 | use Ast; 7 | use Z3_MUTEX; 8 | 9 | impl Context { 10 | pub fn new(cfg: &Config) -> Context { 11 | Context { 12 | z3_ctx: unsafe { 13 | let guard = Z3_MUTEX.lock().unwrap(); 14 | let p = Z3_mk_context_rc(cfg.z3_cfg); 15 | debug!("new context {:p}", p); 16 | p 17 | } 18 | } 19 | } 20 | 21 | // Helpers for common constructions 22 | 23 | pub fn bool_sort(&self) -> Sort { 24 | Sort::bool(self) 25 | } 26 | 27 | pub fn int_sort(&self) -> Sort { 28 | Sort::int(self) 29 | } 30 | 31 | pub fn real_sort(&self) -> Sort { 32 | Sort::real(self) 33 | } 34 | 35 | pub fn bitvector_sort(&self, sz: u32) -> Sort { 36 | Sort::bitvector(self, sz) 37 | } 38 | 39 | pub fn array_sort<'ctx>(&'ctx self, 40 | domain: &Sort<'ctx>, 41 | range: &Sort<'ctx>) -> Sort<'ctx> { 42 | Sort::array(self, domain, range) 43 | } 44 | 45 | pub fn set_sort<'ctx>(&'ctx self, elt: &Sort<'ctx>) -> Sort<'ctx> { 46 | Sort::set(self, elt) 47 | } 48 | 49 | pub fn int_sym(&self, i: u32) -> Symbol { 50 | Symbol::from_int(self, i) 51 | } 52 | 53 | pub fn str_sym(&self, s: &str) -> Symbol { 54 | Symbol::from_string(self, s) 55 | } 56 | 57 | pub fn named_const<'ctx>(&'ctx self, s: &str, sort: &'ctx Sort) -> Ast<'ctx> { 58 | Ast::new_const(&self.str_sym(s), sort) 59 | } 60 | 61 | pub fn numbered_const<'ctx>(&'ctx self, i: u32, sort: &'ctx Sort) -> Ast<'ctx> { 62 | Ast::new_const(&self.int_sym(i), sort) 63 | } 64 | 65 | pub fn fresh_const<'ctx>(&'ctx self, prefix: &str, sort: &'ctx Sort) -> Ast<'ctx> { 66 | Ast::fresh_const(&self, prefix, sort) 67 | } 68 | 69 | pub fn named_bool_const(&self, s: &str) -> Ast { 70 | Ast::new_const(&self.str_sym(s), &self.bool_sort()) 71 | } 72 | 73 | pub fn numbered_bool_const(&self, i: u32) -> Ast { 74 | Ast::new_const(&self.int_sym(i), &self.bool_sort()) 75 | } 76 | 77 | pub fn fresh_bool_const<'ctx>(&'ctx self, prefix: &str) -> Ast<'ctx> { 78 | Ast::fresh_const(&self, prefix, &self.bool_sort()) 79 | } 80 | 81 | pub fn named_int_const(&self, s: &str) -> Ast { 82 | Ast::new_const(&self.str_sym(s), &self.int_sort()) 83 | } 84 | 85 | pub fn numbered_int_const(&self, i: u32) -> Ast { 86 | Ast::new_const(&self.int_sym(i), &self.int_sort()) 87 | } 88 | 89 | pub fn fresh_int_const<'ctx>(&'ctx self, prefix: &str) -> Ast<'ctx> { 90 | Ast::fresh_const(&self, prefix, &self.int_sort()) 91 | } 92 | 93 | pub fn named_real_const(&self, s: &str) -> Ast { 94 | Ast::new_const(&self.str_sym(s), &self.real_sort()) 95 | } 96 | 97 | pub fn numbered_real_const(&self, i: u32) -> Ast { 98 | Ast::new_const(&self.int_sym(i), &self.real_sort()) 99 | } 100 | 101 | pub fn fresh_real_const<'ctx>(&'ctx self, prefix: &str) -> Ast<'ctx> { 102 | Ast::fresh_const(&self, prefix, &self.real_sort()) 103 | } 104 | 105 | pub fn named_bitvector_const(&self, s: &str, sz: u32) -> Ast { 106 | Ast::new_const(&self.str_sym(s), &self.bitvector_sort(sz)) 107 | } 108 | 109 | pub fn numbered_bitvector_const(&self, i: u32, sz: u32) -> Ast { 110 | Ast::new_const(&self.int_sym(i), &self.bitvector_sort(sz)) 111 | } 112 | 113 | pub fn fresh_bitvector_const<'ctx>(&'ctx self, prefix: &str, sz: u32) -> Ast<'ctx> { 114 | Ast::fresh_const(&self, prefix, &self.bitvector_sort(sz)) 115 | } 116 | 117 | pub fn from_bool(&self, b: bool) -> Ast { 118 | Ast::from_bool(self, b) 119 | } 120 | 121 | pub fn from_u64(&self, u: u64) -> Ast { 122 | Ast::from_u64(self, u) 123 | } 124 | 125 | pub fn from_i64(&self, i: i64) -> Ast { 126 | Ast::from_i64(self, i) 127 | } 128 | 129 | pub fn from_real(&self, num: i32, den: i32) -> Ast { 130 | Ast::from_real(self, num, den) 131 | } 132 | } 133 | 134 | impl Drop for Context { 135 | fn drop(&mut self) { 136 | unsafe { 137 | debug!("drop context {:p}", self.z3_ctx); 138 | Z3_del_context(self.z3_ctx); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_variables)] 3 | 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | 8 | #[macro_use] 9 | extern crate lazy_static; 10 | 11 | extern crate z3_sys; 12 | extern crate libc; 13 | 14 | use std::sync::Mutex; 15 | use std::ffi::CString; 16 | use z3_sys::*; 17 | 18 | mod sort; 19 | mod config; 20 | mod context; 21 | mod symbol; 22 | mod ast; 23 | mod solver; 24 | mod optimize; 25 | mod model; 26 | 27 | // Z3 appears to be only mostly-threadsafe, a few initializers 28 | // and such race; so we mutex-guard all access to the library. 29 | lazy_static! { 30 | static ref Z3_MUTEX: Mutex<()> = Mutex::new(()); 31 | } 32 | 33 | pub struct Config { 34 | kvs: Vec<(CString,CString)>, 35 | z3_cfg: Z3_config 36 | } 37 | 38 | pub struct Context { 39 | z3_ctx: Z3_context 40 | } 41 | 42 | pub struct Symbol<'ctx> 43 | { 44 | ctx: &'ctx Context, 45 | cst: Option, 46 | z3_sym: Z3_symbol 47 | } 48 | 49 | pub struct Sort<'ctx> 50 | { 51 | ctx: &'ctx Context, 52 | z3_sort: Z3_sort 53 | } 54 | 55 | pub struct Ast<'ctx> 56 | { 57 | ctx: &'ctx Context, 58 | z3_ast: Z3_ast 59 | } 60 | 61 | pub struct Solver<'ctx> 62 | { 63 | ctx: &'ctx Context, 64 | z3_slv: Z3_solver 65 | } 66 | 67 | pub struct Model<'ctx> 68 | { 69 | ctx: &'ctx Context, 70 | z3_mdl: Z3_model 71 | } 72 | 73 | pub struct Optimize<'ctx> 74 | { 75 | ctx: &'ctx Context, 76 | z3_opt: Z3_optimize 77 | } 78 | 79 | -------------------------------------------------------------------------------- /src/model.rs: -------------------------------------------------------------------------------- 1 | use z3_sys::*; 2 | use Solver; 3 | use Optimize; 4 | use Model; 5 | use Ast; 6 | use Z3_MUTEX; 7 | 8 | impl<'ctx> Model<'ctx> { 9 | pub fn of_solver(slv: &Solver<'ctx>) -> Model<'ctx> { 10 | Model { 11 | ctx: slv.ctx, 12 | z3_mdl: unsafe { 13 | let guard = Z3_MUTEX.lock().unwrap(); 14 | let m = Z3_solver_get_model(slv.ctx.z3_ctx, slv.z3_slv); 15 | Z3_model_inc_ref(slv.ctx.z3_ctx, m); 16 | m 17 | } 18 | } 19 | } 20 | 21 | pub fn of_optimize(opt: &Optimize<'ctx>) -> Model<'ctx> { 22 | Model { 23 | ctx: opt.ctx, 24 | z3_mdl: unsafe { 25 | let guard = Z3_MUTEX.lock().unwrap(); 26 | let m = Z3_optimize_get_model(opt.ctx.z3_ctx, opt.z3_opt); 27 | Z3_model_inc_ref(opt.ctx.z3_ctx, m); 28 | m 29 | } 30 | } 31 | } 32 | 33 | pub fn eval(&self, ast: &Ast<'ctx>) -> Option> { 34 | unsafe { 35 | let mut tmp : Z3_ast = ast.z3_ast; 36 | let res; 37 | { 38 | let guard = Z3_MUTEX.lock().unwrap(); 39 | res = Z3_model_eval(self.ctx.z3_ctx, 40 | self.z3_mdl, 41 | ast.z3_ast, 42 | Z3_TRUE, 43 | &mut tmp) 44 | } 45 | if res == Z3_TRUE { 46 | Some(Ast::new(self.ctx, tmp)) 47 | } else { 48 | None 49 | } 50 | } 51 | } 52 | } 53 | 54 | impl<'ctx> Drop for Model<'ctx> { 55 | fn drop(&mut self) { 56 | unsafe { 57 | let guard = Z3_MUTEX.lock().unwrap(); 58 | Z3_model_dec_ref(self.ctx.z3_ctx, self.z3_mdl); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/optimize.rs: -------------------------------------------------------------------------------- 1 | use z3_sys::*; 2 | use Context; 3 | use Optimize; 4 | use Model; 5 | use Ast; 6 | use Z3_MUTEX; 7 | 8 | impl<'ctx> Optimize<'ctx> { 9 | pub fn new(ctx: &'ctx Context) -> Optimize<'ctx> { 10 | Optimize { 11 | ctx: ctx, 12 | z3_opt: unsafe { 13 | let guard = Z3_MUTEX.lock().unwrap(); 14 | let opt = Z3_mk_optimize(ctx.z3_ctx); 15 | Z3_optimize_inc_ref(ctx.z3_ctx, opt); 16 | opt 17 | } 18 | } 19 | } 20 | 21 | pub fn assert(&self, ast: &Ast<'ctx>) { 22 | unsafe { 23 | let guard = Z3_MUTEX.lock().unwrap(); 24 | Z3_optimize_assert(self.ctx.z3_ctx, 25 | self.z3_opt, 26 | ast.z3_ast); 27 | } 28 | } 29 | 30 | pub fn maximize(&self, ast: &Ast<'ctx>) { 31 | unsafe { 32 | let guard = Z3_MUTEX.lock().unwrap(); 33 | Z3_optimize_maximize(self.ctx.z3_ctx, 34 | self.z3_opt, 35 | ast.z3_ast); 36 | } 37 | } 38 | 39 | pub fn minimize(&self, ast: &Ast<'ctx>) { 40 | unsafe { 41 | let guard = Z3_MUTEX.lock().unwrap(); 42 | Z3_optimize_minimize(self.ctx.z3_ctx, 43 | self.z3_opt, 44 | ast.z3_ast); 45 | } 46 | } 47 | 48 | pub fn check(&self) -> bool { 49 | unsafe { 50 | let guard = Z3_MUTEX.lock().unwrap(); 51 | Z3_optimize_check(self.ctx.z3_ctx, 52 | self.z3_opt) == Z3_TRUE 53 | } 54 | } 55 | 56 | pub fn get_model(&self) -> Model<'ctx> { 57 | Model::of_optimize(self) 58 | } 59 | } 60 | 61 | impl<'ctx> Drop for Optimize<'ctx> { 62 | fn drop(&mut self) { 63 | unsafe { 64 | let guard = Z3_MUTEX.lock().unwrap(); 65 | Z3_optimize_dec_ref(self.ctx.z3_ctx, self.z3_opt); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/solver.rs: -------------------------------------------------------------------------------- 1 | use z3_sys::*; 2 | use Context; 3 | use Solver; 4 | use Model; 5 | use Ast; 6 | use Z3_MUTEX; 7 | 8 | impl<'ctx> Solver<'ctx> { 9 | pub fn new(ctx: &Context) -> Solver { 10 | Solver { 11 | ctx: ctx, 12 | z3_slv: unsafe { 13 | let guard = Z3_MUTEX.lock().unwrap(); 14 | let s = Z3_mk_solver(ctx.z3_ctx); 15 | Z3_solver_inc_ref(ctx.z3_ctx, s); 16 | s 17 | } 18 | } 19 | } 20 | 21 | pub fn assert(&self, ast: &Ast<'ctx>) { 22 | unsafe { 23 | let guard = Z3_MUTEX.lock().unwrap(); 24 | Z3_solver_assert(self.ctx.z3_ctx, 25 | self.z3_slv, 26 | ast.z3_ast); 27 | } 28 | } 29 | 30 | pub fn check(&self) -> bool { 31 | unsafe { 32 | let guard = Z3_MUTEX.lock().unwrap(); 33 | Z3_solver_check(self.ctx.z3_ctx, 34 | self.z3_slv) == Z3_TRUE 35 | } 36 | } 37 | 38 | pub fn get_model(&self) -> Model<'ctx> { 39 | Model::of_solver(self) 40 | } 41 | } 42 | 43 | 44 | impl<'ctx> Drop for Solver<'ctx> { 45 | fn drop(&mut self) { 46 | unsafe { 47 | let guard = Z3_MUTEX.lock().unwrap(); 48 | Z3_solver_dec_ref(self.ctx.z3_ctx, self.z3_slv); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/sort.rs: -------------------------------------------------------------------------------- 1 | use z3_sys::*; 2 | use Context; 3 | use Symbol; 4 | use Sort; 5 | use Z3_MUTEX; 6 | 7 | impl<'ctx> Sort<'ctx> { 8 | 9 | pub fn uninterpretd(ctx: &'ctx Context, sym: &Symbol<'ctx>) -> Sort<'ctx> { 10 | Sort { 11 | ctx: ctx, 12 | z3_sort: unsafe { 13 | let guard = Z3_MUTEX.lock().unwrap(); 14 | Z3_mk_uninterpreted_sort(ctx.z3_ctx, sym.z3_sym) 15 | } 16 | } 17 | } 18 | 19 | pub fn bool(ctx: &Context) -> Sort { 20 | Sort { 21 | ctx: ctx, 22 | z3_sort: unsafe { 23 | let guard = Z3_MUTEX.lock().unwrap(); 24 | Z3_mk_bool_sort(ctx.z3_ctx) 25 | } 26 | } 27 | } 28 | 29 | pub fn int(ctx: &Context) -> Sort { 30 | Sort { 31 | ctx: ctx, 32 | z3_sort: unsafe { 33 | let guard = Z3_MUTEX.lock().unwrap(); 34 | Z3_mk_int_sort(ctx.z3_ctx) 35 | } 36 | } 37 | } 38 | 39 | pub fn real(ctx: &Context) -> Sort { 40 | Sort { 41 | ctx: ctx, 42 | z3_sort: unsafe { 43 | let guard = Z3_MUTEX.lock().unwrap(); 44 | Z3_mk_real_sort(ctx.z3_ctx) 45 | } 46 | } 47 | } 48 | 49 | pub fn bitvector(ctx: &Context, sz: u32) -> Sort { 50 | Sort { 51 | ctx: ctx, 52 | z3_sort: unsafe { 53 | let guard = Z3_MUTEX.lock().unwrap(); 54 | Z3_mk_bv_sort(ctx.z3_ctx, sz as ::libc::c_uint) 55 | } 56 | } 57 | } 58 | 59 | pub fn array(ctx: &'ctx Context, 60 | domain: &Sort<'ctx>, 61 | range: &Sort<'ctx>) -> Sort<'ctx> { 62 | Sort { 63 | ctx: ctx, 64 | z3_sort: unsafe { 65 | let guard = Z3_MUTEX.lock().unwrap(); 66 | Z3_mk_array_sort(ctx.z3_ctx, domain.z3_sort, range.z3_sort) 67 | } 68 | } 69 | } 70 | 71 | pub fn set(ctx: &'ctx Context, elt: &Sort<'ctx>) -> Sort<'ctx> { 72 | Sort { 73 | ctx: ctx, 74 | z3_sort: unsafe { 75 | let guard = Z3_MUTEX.lock().unwrap(); 76 | Z3_mk_set_sort(ctx.z3_ctx, elt.z3_sort) 77 | } 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/symbol.rs: -------------------------------------------------------------------------------- 1 | use z3_sys::*; 2 | use Symbol; 3 | use Context; 4 | use Z3_MUTEX; 5 | use std::ffi::CString; 6 | 7 | impl<'ctx> Symbol<'ctx> { 8 | pub fn from_int(ctx: &Context, i: u32) -> Symbol { 9 | Symbol { 10 | ctx: ctx, 11 | cst: None, 12 | z3_sym: unsafe { 13 | let guard = Z3_MUTEX.lock().unwrap(); 14 | Z3_mk_int_symbol(ctx.z3_ctx, i as ::libc::c_int) 15 | } 16 | } 17 | } 18 | 19 | pub fn from_string(ctx: &'ctx Context, s: &str) -> Symbol<'ctx> { 20 | let ss = CString::new(s).unwrap(); 21 | let p = ss.as_ptr(); 22 | Symbol { 23 | ctx: ctx, 24 | cst: Some(ss), 25 | z3_sym: unsafe { 26 | let guard = Z3_MUTEX.lock().unwrap(); 27 | Z3_mk_string_symbol(ctx.z3_ctx, p) 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate env_logger; 4 | 5 | extern crate z3; 6 | use z3::*; 7 | 8 | #[test] 9 | fn test_config() { 10 | let _ = env_logger::init(); 11 | let mut c = Config::new(); 12 | c.set_proof_generation(true); 13 | } 14 | 15 | #[test] 16 | fn test_context() { 17 | let _ = env_logger::init(); 18 | let mut cfg = Config::new(); 19 | cfg.set_proof_generation(true); 20 | let _ = Context::new(&cfg); 21 | } 22 | 23 | #[test] 24 | fn test_sorts_and_symbols() { 25 | let _ = env_logger::init(); 26 | let cfg = Config::new(); 27 | let ctx = Context::new(&cfg); 28 | let _ = ctx.named_int_const("x"); 29 | let _ = ctx.named_int_const("y"); 30 | } 31 | 32 | #[test] 33 | fn test_solving() { 34 | let _ = env_logger::init(); 35 | let cfg = Config::new(); 36 | let ctx = Context::new(&cfg); 37 | let x = ctx.named_int_const("x"); 38 | let y = ctx.named_int_const("y"); 39 | 40 | let solver = Solver::new(&ctx); 41 | solver.assert(&x.gt(&y)); 42 | assert!(solver.check()); 43 | } 44 | 45 | #[test] 46 | fn test_solving_for_model() { 47 | let _ = env_logger::init(); 48 | let cfg = Config::new(); 49 | let ctx = Context::new(&cfg); 50 | let x = ctx.named_int_const("x"); 51 | let y = ctx.named_int_const("y"); 52 | let zero = ctx.from_i64(0); 53 | let two = ctx.from_i64(2); 54 | let seven = ctx.from_i64(7); 55 | 56 | let solver = Solver::new(&ctx); 57 | solver.assert(&x.gt(&y)); 58 | solver.assert(&y.gt(&zero)); 59 | solver.assert(&y.rem(&seven)._eq(&two)); 60 | solver.assert(&x.add(&[&two]).gt(&seven)); 61 | assert!(solver.check()); 62 | 63 | let model = solver.get_model(); 64 | let xv = model.eval(&x).unwrap().as_i64().unwrap(); 65 | let yv = model.eval(&y).unwrap().as_i64().unwrap(); 66 | info!("x: {}", xv); 67 | info!("y: {}", yv); 68 | assert!(xv > yv); 69 | assert!(yv % 7 == 2); 70 | assert!(xv + 2 > 7); 71 | } 72 | 73 | -------------------------------------------------------------------------------- /tests/semver_tests.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_variables)] 3 | 4 | #[macro_use] 5 | extern crate log; 6 | extern crate env_logger; 7 | extern crate semver; 8 | extern crate z3; 9 | 10 | use z3::*; 11 | use semver::{Version, VersionReq}; 12 | use std::collections::HashMap; 13 | 14 | struct Spec { 15 | vers: Version, 16 | reqs: HashMap 17 | } 18 | 19 | impl Spec { 20 | pub fn new(vers: &str, reqs: &[(&str,&str)]) -> Spec { 21 | let mut rs = HashMap::new(); 22 | for &(p, r) in reqs { 23 | rs.insert(p.to_string(), VersionReq::parse(r).unwrap()); 24 | } 25 | Spec { 26 | vers: Version::parse(vers).unwrap(), 27 | reqs: rs 28 | } 29 | } 30 | } 31 | 32 | type SpecMap = HashMap>; 33 | 34 | fn get_version(sm: &SpecMap, pkg: &str, ver: usize) -> Option { 35 | match sm.get(pkg) { 36 | None => None, 37 | Some(ref specs) => Some(specs[ver].vers.clone()) 38 | } 39 | } 40 | 41 | fn version_index(sm: &SpecMap, pkg: &str, ver: &str) -> Option { 42 | let ver = Version::parse(ver).unwrap(); 43 | match sm.get(pkg) { 44 | None => None, 45 | Some(ref specs) => 46 | specs.iter().position(|spec| spec.vers == ver) 47 | } 48 | } 49 | 50 | fn first_version_req_index(sm: &SpecMap, pkg: &str, 51 | req: &VersionReq) -> Option { 52 | match sm.get(pkg) { 53 | None => None, 54 | Some(ref specs) => 55 | specs.iter().position(|spec| req.matches(&spec.vers)) 56 | } 57 | } 58 | 59 | fn last_version_req_index(sm: &SpecMap, pkg: &str, 60 | req: &VersionReq) -> Option { 61 | match sm.get(pkg) { 62 | None => None, 63 | Some(ref specs) => 64 | specs.iter().rposition(|spec| req.matches(&spec.vers)) 65 | } 66 | } 67 | 68 | #[test] 69 | fn test_solve_simple_semver_example() { 70 | 71 | // This is a little example of solving version constraints the way cargo 72 | // might someday want to. It uses the optimizer portion of Z3. 73 | // see: https://github.com/rust-lang/cargo/issues/2064 74 | 75 | let _ = env_logger::init(); 76 | 77 | let mut smap : SpecMap = HashMap::new(); 78 | 79 | smap.insert("postgres".to_string(), 80 | vec![ 81 | ("0.1.0",&[]), 82 | ("0.1.1",&[]), 83 | ("0.1.2",&[]), 84 | ("0.1.3",&[]), 85 | ("0.2.0",&[]), 86 | ("0.2.1",&[]), 87 | ("0.2.2",&[]), 88 | ("0.2.3",&[]), 89 | ("0.2.4",&[]), 90 | ("0.3.0",&[]), 91 | ("0.4.0",&[]), 92 | ("0.4.1",&[]), 93 | ("0.4.2",&[]), 94 | ("0.4.3",&[]), 95 | ("0.4.4",&[]), 96 | ("0.4.5",&[]), 97 | ("0.4.6",&[]), 98 | ("0.5.0",&[]), 99 | ("0.5.1",&[]), 100 | ("0.5.2",&[]), 101 | ("0.5.3",&[]), 102 | ("0.5.4",&[]), 103 | ("0.5.5",&[]), 104 | ("0.5.6",&[]), 105 | ("0.6.0",&[]), 106 | ("0.6.1",&[]), 107 | ("0.6.2",&[]), 108 | ("0.6.3",&[]), 109 | ("0.6.4",&[]), 110 | ("0.6.5",&[]), 111 | ("0.7.0",&[]), 112 | ("0.7.1",&[]), 113 | ("0.7.2",&[]), 114 | ("0.7.2",&[]), 115 | ("0.7.3",&[]), 116 | ("0.7.4",&[]), 117 | ("0.7.5",&[]), 118 | ("0.8.0",&[]), 119 | ("0.8.1",&[]), 120 | ("0.8.2",&[]), 121 | ("0.8.3",&[]), 122 | ("0.8.4",&[]), 123 | ("0.8.5",&[]), 124 | ("0.8.6",&[]), 125 | ("0.8.7",&[]), 126 | ("0.8.8",&[]), 127 | ("0.8.9",&[]), 128 | ("0.9.0",&[]), 129 | ("0.9.1",&[]), 130 | ("0.9.2",&[]), 131 | ("0.9.3",&[]), 132 | ("0.9.4",&[]), 133 | ("0.9.5",&[]), 134 | ("0.9.6",&[]), 135 | ("0.10.0",&[]), 136 | ("0.10.1",&[]), 137 | ("0.10.2",&[]) 138 | ].iter().map(|&(v,r)| Spec::new(v,r)).collect()); 139 | 140 | smap.insert("r2d2-postgres".to_string(), 141 | vec![ 142 | ("0.2.0",&[("postgres", "^0.2")]), 143 | ("0.2.1",&[("postgres", "^0.2")]), 144 | ("0.3.0",&[("postgres", "^0.4")]), 145 | ("0.3.1",&[("postgres", "^0.4")]), 146 | ("0.4.0",&[("postgres", "^0.5")]), 147 | ("0.5.0",&[("postgres", "^0.5")]), 148 | ("0.6.0",&[("postgres", "^0.6")]), 149 | ("0.7.0",&[("postgres", "^0.6")]), 150 | ("0.8.0",&[("postgres", "^0.7")]), 151 | ("0.9.0",&[("postgres", "^0.8")]), 152 | ("0.9.1",&[("postgres", "^0.9")]), 153 | ("0.9.2",&[("postgres", "^0.9")]), 154 | ("0.9.3",&[("postgres", "^0.10")]) 155 | ].iter().map(|&(v,r)| Spec::new(v,r)).collect()); 156 | 157 | 158 | let mut cfg = Config::new(); 159 | let ctx = Context::new(&cfg); 160 | let opt = Optimize::new(&ctx); 161 | 162 | let mut root : HashMap = HashMap::new(); 163 | let mut asts : HashMap = HashMap::new(); 164 | 165 | root.insert("postgres".to_string(), 166 | VersionReq::parse("0.9").unwrap()); 167 | 168 | root.insert("r2d2-postgres".to_string(), 169 | VersionReq::parse("0.9").unwrap()); 170 | 171 | 172 | // Make a root Z3 Int constant for each pkg we're trying to solve for. 173 | for (k, v) in &root { 174 | let ast = ctx.fresh_int_const("root-pkg"); 175 | info!("new AST for root {}", k); 176 | 177 | match first_version_req_index(&smap, k, v) { 178 | None => (), 179 | Some(low) => { 180 | info!("Asserting: {} >= #{} (root)", k, low); 181 | opt.assert(&ast.ge(&ctx.from_u64(low as u64))) 182 | } 183 | } 184 | match last_version_req_index(&smap, k, v) { 185 | None => (), 186 | Some(high) => { 187 | info!("Asserting: {} <= #{} (root)", k, high); 188 | opt.assert(&ast.le(&ctx.from_u64(high as u64))) 189 | } 190 | } 191 | asts.insert(k.clone(), ast); 192 | } 193 | 194 | // Tell the optimizer to maximizes the sum of the root constants. 195 | opt.maximize(&ctx.from_i64(0).add(&asts.values().collect::>())); 196 | 197 | // Ensure we have a constant for every pkg _or_ dep listed 198 | for k in (&smap).keys() { 199 | asts.entry(k.clone()).or_insert_with(|| { 200 | info!("new AST for {}", k); 201 | ctx.fresh_int_const("pkg") 202 | }); 203 | } 204 | for specs in smap.values() { 205 | for spec in specs { 206 | for r in (&spec).reqs.keys() { 207 | asts.entry(r.clone()).or_insert_with(|| { 208 | info!("new AST for {}", r); 209 | ctx.fresh_int_const("dep-pkg") 210 | }); 211 | } 212 | } 213 | } 214 | 215 | // Then assert all version constraints. Specifically: assert 216 | // an implication that whenever a package is of some version, 217 | // its required package is inside the acceptable range. 218 | for (k, specs) in &smap { 219 | let k_ast = asts.get(k).unwrap(); 220 | for (n, spec) in (&specs).iter().enumerate() { 221 | for (r, req) in &spec.reqs { 222 | let r_ast = asts.get(r).unwrap(); 223 | match first_version_req_index(&smap, r, req) { 224 | None => (), 225 | Some(low) => { 226 | info!("Asserting: {} == #{} {} => {} >= #{} {}", 227 | k, n, get_version(&smap, k, n as usize).unwrap(), 228 | r, low, get_version(&smap, r, low as usize).unwrap()); 229 | opt.assert(&k_ast._eq(&ctx.from_u64(n as u64)). 230 | implies(&r_ast.ge(&ctx.from_u64(low as u64)))) 231 | } 232 | } 233 | match last_version_req_index(&smap, r, req) { 234 | None => (), 235 | Some(high) => { 236 | info!("Asserting: {} == #{} {} => {} <= #{} {}", 237 | k, n, get_version(&smap, k, n as usize).unwrap(), 238 | r, high, get_version(&smap, r, high as usize).unwrap()); 239 | opt.assert(&k_ast._eq(&ctx.from_u64(n as u64)). 240 | implies(&r_ast.le(&ctx.from_u64(high as u64)))) 241 | } 242 | } 243 | } 244 | } 245 | } 246 | 247 | assert!(opt.check()); 248 | let model = opt.get_model(); 249 | 250 | for k in root.keys() { 251 | let ast = asts.get(k).unwrap(); 252 | let idx = model.eval(&ast).unwrap().as_i64().unwrap(); 253 | info!("solved: {}: #{} = {}", 254 | k, idx, get_version(&smap, k, idx as usize).unwrap()); 255 | } 256 | 257 | let pg_a = asts.get("postgres").unwrap(); 258 | let r2_a = asts.get("r2d2-postgres").unwrap(); 259 | 260 | let pg_v = model.eval(&pg_a).unwrap().as_i64().unwrap() as usize; 261 | let r2_v = model.eval(&r2_a).unwrap().as_i64().unwrap() as usize; 262 | 263 | assert!(get_version(&smap, "postgres", pg_v).unwrap() == 264 | Version::parse("0.9.6").unwrap()); 265 | 266 | assert!(get_version(&smap, "r2d2-postgres", r2_v).unwrap() == 267 | Version::parse("0.9.2").unwrap()); 268 | 269 | } 270 | --------------------------------------------------------------------------------