├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── ISSUE_TEMPLATE.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── src ├── lib.rs ├── smt_err.rs ├── smt_ops.rs └── z3.rs └── tests └── test.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | sudo: required 4 | dist: trusty 5 | addons: 6 | apt: 7 | packages: 8 | - libssl-dev 9 | language: rust 10 | rust: 11 | - nightly-2019-03-23 12 | cache: cargo 13 | 14 | before_script: 15 | # install z3 16 | - wget -O z3.zip https://github.com/Z3Prover/bin/blob/10fafc192673e9ec80ae8abff38bcc1e1a3a0a9c/nightly/z3-4.8.5.606754c09a68-x64-ubuntu-14.04.zip?raw=true 17 | - unzip z3 18 | - sudo mv z3-4.8.5.606754c09a68-x64-ubuntu-14.04/include/* /usr/include/ 19 | - sudo mv z3-4.8.5.606754c09a68-x64-ubuntu-14.04/bin/libz3.* /usr/lib/x86_64-linux-gnu/ 20 | # install formatter 21 | - rustup component add rustfmt-preview 22 | # install linter 23 | - rustup component add clippy-preview 24 | # install code coverage tool 25 | - RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install --force cargo-tarpaulin || true 26 | 27 | script: 28 | # Exit immediately if a command exits with a non-zero status. 29 | - set -e 30 | # Run format checks 31 | - cargo fmt --all -- --check 32 | # Run lint checks 33 | - cargo clippy -- -D warnings 34 | # Build 35 | - cargo build 36 | # Run unit and integration tests 37 | - RUST_BACKTRACE=1 cargo test 38 | # Run code coverage tests 39 | - cargo clean -p rust_smt 40 | - cargo tarpaulin --ignore-tests -v --out Xml || true 41 | - bash <(curl -s https://codecov.io/bash) 42 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 0.1.0 (March 22, 2019) 2 | 3 | ### Initial Release 4 | 5 | * Initial release of SMT API for Rust 6 | * Support for Z3 SMT solver 7 | 8 | 0.1.1 (April 5, 2019) 9 | 10 | ### Patch Release 11 | 12 | * Bug fix - properly handle ref counting of Z3 solver and model objects 13 | 14 | 0.2.0 (April 25, 2019) 15 | 16 | ### Minor Release 17 | 18 | * Some bug fixes 19 | * Support for records 20 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Facebook has adopted a Code of Conduct that we expect project participants to adhere to. 4 | Please read the [full text](https://code.fb.com/codeofconduct/) 5 | so that you can understand what actions will and will not be tolerated. 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Rust-SMT-LIB-API 2 | We welcome contributions and want to make contributing to this project as easy and transparent as 3 | possible. Contributors are encouraged to have a good understanding of 4 | SMT and SMT-LIB. See [the SMT-LIB website](http://www.smt-lib.org) 5 | for information and documentation on SMT and SMT-LIB. 6 | 7 | ## Our Development Process 8 | Every pull request should have a description that motivates it and 9 | should have tests that cover all of the source lines touched and added 10 | by the request. No pull request will be merged unless all tests are 11 | passing. 12 | 13 | A TODO comment should include the number of an issue that describes the outstanding work in greater detail. 14 | 15 | All APIs should be [documented](https://rust-lang-nursery.github.io/api-guidelines/documentation.html) in the code and 16 | all public methods and functions should have explicit assertion statements to document and enforce any preconditions 17 | that they depend on. 18 | 19 | A pull request that is a work in progress that has been published to get help and feedback from the community should 20 | be tagged with WIP. 21 | 22 | The person approving a pull request will also merge it into master. 23 | 24 | ## Pull Requests 25 | We actively welcome your pull requests. 26 | 27 | 1. Fork the repo and create your branch from `master`. 28 | 2. If you've added code that should be tested, add tests. 29 | 3. If you've changed APIs, update the documentation. 30 | 4. Ensure the test suite passes. 31 | 5. Make sure your code lints. 32 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 33 | 34 | ## Contributor License Agreement ("CLA") 35 | In order to accept your pull request, we need you to submit a CLA. You only need 36 | to do this once to work on any of Facebook's open source projects. 37 | 38 | Complete your CLA here: 39 | 40 | ## Issues 41 | We use GitHub issues to track public bugs. Please ensure your description is 42 | clear and has sufficient instructions to be able to reproduce the issue. 43 | 44 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 45 | disclosure of security bugs. In those cases, please go through the process 46 | outlined on that page and do not file a public issue. 47 | 48 | ## Coding Style 49 | We use the [Rust style guide](https://github.com/rust-lang-nursery/fmt-rfcs/blob/master/guide/guide.md) 50 | 51 | ## License 52 | By contributing to Rust-SMT-LIB-API, you agree that your contributions will be licensed 53 | under the LICENSE file in the root directory of this source tree. -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "lazy_static" 3 | version = "1.3.0" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "rust_smt" 8 | version = "0.2.0" 9 | dependencies = [ 10 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 11 | "z3-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 12 | ] 13 | 14 | [[package]] 15 | name = "z3-sys" 16 | version = "0.5.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | 19 | [metadata] 20 | "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" 21 | "checksum z3-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ae57b4c31eabc2620fbf2ea3e4deac2cd8a12522ccc0ebcc29e60dff8137de31" 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust_smt" 3 | version = "0.2.0" 4 | authors = ["Clark Barrett "] 5 | edition = "2018" 6 | description = "A generic solver-agnostic API for interacting with SMT solvers based on the SMT-LIB standard." 7 | homepage = "https://github.com/facebookincubator/Rust-SMT-LIB-API" 8 | repository = "https://github.com/facebookincubator/Rust-SMT-LIB-API.git" 9 | readme = "README.md" 10 | keywords = ["smt", "satisfiability", "modulo", "theories", "z3"] 11 | categories = ["api-bindings", "science"] 12 | license = "MIT" 13 | exclude = [".gitignore", ".travis.yml"] 14 | 15 | [badges] 16 | maintenance = { status = "actively-developed" } 17 | 18 | [dependencies] 19 | lazy_static = "1" 20 | z3-sys = "0.5.0" 21 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Issue 2 | 3 | A summary of what the issue is about. 4 | 5 | ## Steps to Reproduce 6 | 7 | Please provide instructions that can be used to reproduce your issue. Usually this will include a test case that 8 | produces the wrong output. 9 | 10 | ## Expected Behavior 11 | 12 | What you expected to happen. For example the error message you expected to see. 13 | 14 | ## Actual Results 15 | 16 | What actually happened. For example, an error message you did not expect to see. 17 | 18 | ## Environment 19 | 20 | Rust version (rustc --version) 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Facebook, Inc. and its affiliates. 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. -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | - [ ] Bug fix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] API change with a documentation update 13 | - [ ] Additional test coverage 14 | 15 | ## How Has This Been Tested? 16 | 17 | ## Checklist: 18 | 19 | - [ ] Fork the repo and create your branch from `master`. 20 | - [ ] If you've added code that should be tested, add tests. 21 | - [ ] If you've changed APIs, update the documentation. 22 | - [ ] Ensure the test suite passes. 23 | - [ ] Make sure your code lints. 24 | - [ ] If you haven't already, complete your CLA here: 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust-SMT-LIB-API [![Build Status](https://travis-ci.com/facebookincubator/Rust-SMT-LIB-API.svg?token=eGmra6RUfZeknKfxxdBS&branch=master)](https://travis-ci.com/facebookincubator/Rust-SMT-LIB-API) [![codecov](https://codecov.io/gh/facebookincubator/Rust-SMT-LIB-API/branch/master/graph/badge.svg?token=ZvGjkOKO5l)](https://codecov.io/gh/facebookincubator/Rust-SMT-LIB-API) 2 | 3 | 4 | This crate provides a generic high-level API for interacting with SMT 5 | solvers. The aim of this interface is to be solver-agnostic (i.e. the 6 | user can switch between back-end SMT solvers by modifying a single 7 | line of code) and to mimic the SMT-LIB standard commands as closely as 8 | possible. Currently, Z3 is supported as a back-end. See links below 9 | for more information on SMT-LIB and Z3. See tests/test.rs for 10 | examples of how to use the interface. 11 | 12 | ## Installing z3 13 | * Rust-SMT-LIB-API requires the latest nightly build of Z3 14 | * Follow the install instructions on the Z3 website (see below) 15 | 16 | ## Building 17 | * Download or clone Rust-SMT-LIB-API 18 | * cd to the root directory of Rust-SMT-LIB-API and run: cargo test 19 | 20 | ## Additional links 21 | * [SMT-LIB standard and documentation](http://smtlib.org). 22 | * [Z3 SMT solver](https://github.com/Z3Prover/z3). 23 | 24 | ## License 25 | Rust-SMT-LIB-API is MIT licensed, as found in the [LICENSE](https://github.com/facebookincubator/Rust-SMT-LIB-API/blob/master/LICENSE) file. 26 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | // An abstract interface for SMT solvers, parameterized by implementations of 7 | // Sort, Term, and UninterpretedFunction. This interface aims to mimic the 8 | // SMT-LIB commands as closely as possible. See http:://www.smtlib.org for 9 | // documentation on the SMT standard and interface. See tests/test.rs for 10 | // examples of how to use the interface. 11 | 12 | pub mod smt_err; 13 | pub mod smt_ops; 14 | 15 | // Most functions return an SMTResult. If an error is returned, there are three 16 | // possibilities: 17 | // APIError - this results when the API is misused. 18 | // UnsupportedError - this results when the solver doesn't support a feature 19 | // InternalError - this results when the solver or a library call fails. 20 | // See smt_err.rs for more details. 21 | type SMTResult = Result; 22 | 23 | // The result of calling check_sat is either satisfiable (Sat), unsatisfiable 24 | // (Unsat), or unknown (Unknown). 25 | #[derive(Debug, PartialEq, Clone, Copy)] 26 | pub enum CheckSatResult { 27 | Sat, 28 | Unsat, 29 | Unknown, 30 | } 31 | 32 | // An abstract data type for SMT sorts. 33 | pub trait Sort: Clone + std::fmt::Debug + Eq + std::hash::Hash + Sized { 34 | // Get a string representing the SMT-LIB name for the Sort. The only 35 | // possible error is InternalError. 36 | fn to_string(&self) -> SMTResult; 37 | } 38 | 39 | // An abstract data type for uninterpreted function symbols. 40 | pub trait UninterpretedFunction: std::fmt::Debug + std::clone::Clone + Sized { 41 | // Get the name of the uninterpreted function. The only possible error is 42 | // InternalError. 43 | fn to_string(&self) -> SMTResult; 44 | } 45 | 46 | // A Function is either a built-in operator or an uninterpreted function. 47 | pub enum Function<'a, F: UninterpretedFunction> { 48 | Op(smt_ops::Fn<'a>), 49 | UF(F), 50 | } 51 | 52 | // An abstract data type for SMT terms. 53 | pub trait Term: std::fmt::Debug + std::clone::Clone + Sized { 54 | // Get a string for the SMT-LIB representation of the term. The only 55 | // possible error is InternalError. 56 | fn to_string(&self) -> SMTResult; 57 | 58 | // For terms that are constant values representable as i64, return the 59 | // corresponding i64 value. Returns APIError if term is not a constant of 60 | // real, int, or bitvector type, or if it is a non-integral real constant, 61 | // or if the integral value doesn't fit in 64 bits. 62 | fn to_int(&self) -> SMTResult; 63 | } 64 | 65 | // An abstract data type for SMT solvers. 66 | pub trait SMTSolver { 67 | type S: Sort; 68 | type T: Term; 69 | type F: UninterpretedFunction; 70 | 71 | // Return a new solver object. 72 | fn new() -> Self; 73 | 74 | /////////////////////////////////////////////////////////////////////////// 75 | // Sorts // 76 | /////////////////////////////////////////////////////////////////////////// 77 | 78 | // Get the sort of a term. 79 | fn get_sort(&self, t: &Self::T) -> SMTResult; 80 | 81 | // Declare a new uninterpreted sort with the given name. Only 82 | // InternalError errors are possible. 83 | fn declare_sort(&self, name: &str) -> SMTResult; 84 | 85 | // Lookup a built-in sort belonging to an SMT-LIB theory. Returns an 86 | // APIError if s is a sort constructor (e.g. Array). 87 | fn lookup_sort(&self, s: smt_ops::Sorts) -> SMTResult; 88 | 89 | // Apply a built-in sort constructor of arity 2. Returns an APIError if s 90 | // is not a sort constructor of arity 2. 91 | fn apply_sort(&self, s: smt_ops::Sorts, s1: &Self::S, s2: &Self::S) -> SMTResult; 92 | 93 | // Create a record with field names given in fields and corresponding sorts 94 | // given in sorts. The fields are used together with the RecordSelect and 95 | // RecordUpdate operators to read from or update records of this sort. An 96 | // APIError results if the number of fields does not match the number of 97 | // sorts, if the field names are not all distinct, or if a record of the 98 | // same name has already been declared. 99 | fn declare_record_sort( 100 | &mut self, 101 | name: &str, 102 | fields: &[&str], 103 | sorts: &[&Self::S], 104 | ) -> SMTResult; 105 | 106 | // Return true iff the sort is a record sort. 107 | fn is_record_sort(&self, sort: &Self::S) -> bool; 108 | 109 | /////////////////////////////////////////////////////////////////////////// 110 | // Functions // 111 | /////////////////////////////////////////////////////////////////////////// 112 | 113 | // Declare a new uninterpreted function with the given name. args specifies 114 | // the argument sorts, and sort specifies the return sort. Returns an 115 | // UninterpretedFunction object. Only InternalError errors are possible. 116 | fn declare_fun(&self, name: &str, args: &[&Self::S], sort: &Self::S) -> SMTResult; 117 | 118 | /////////////////////////////////////////////////////////////////////////// 119 | // Terms // 120 | /////////////////////////////////////////////////////////////////////////// 121 | 122 | // Declare a new constant with a given name and sort. Only InternalError 123 | // errors are possible. 124 | fn declare_const(&self, name: &str, sort: &Self::S) -> SMTResult; 125 | 126 | // Lookup a built-in constant belonging to an SMT-LIB theory. Returns an 127 | // APIError if f is not a built-in constant. 128 | fn lookup_const(&self, f: smt_ops::Fn) -> SMTResult; 129 | 130 | // Construct a constant from a 64-bit integer of a given sort. Supported 131 | // sorts are integer, real, and bitvector (for bitvector the value must be 132 | // non-negative and fit in the bit-width). If an invalid sort is used or 133 | // an invalid value is used with a bitvector sort, the result is an 134 | // APIError. 135 | fn const_from_int(&self, value: i64, sort: &Self::S) -> SMTResult; 136 | 137 | // Construct a constant of a given sort from a numeric string. Supported 138 | // sorts are integer, real, and bitvector. Expects only digits 139 | // (non-bitvectors can also have a single unary minus at the beginning, and 140 | // reals can have at most one decimal point). Currently does not check if 141 | // value fits within the bitwidth for bitvector sorts. Behavior in that 142 | // case is dependent on the solver. 143 | fn const_from_string(&self, value: &str, sort: &Self::S) -> SMTResult; 144 | 145 | // Construct a record literal of sort record_sort using the terms in 146 | // field_values. 147 | fn record_const(&self, record_sort: &Self::S, field_values: &[Self::T]) -> SMTResult; 148 | 149 | // Sams as above, except the arguments are in a vector of references to 150 | // terms rather than a vector of terms. 151 | fn record_const_refs( 152 | &self, 153 | record_sort: &Self::S, 154 | field_values: &[&Self::T], 155 | ) -> SMTResult; 156 | 157 | // Apply a function f to a vector of arguments to get a Term object. f can 158 | // be either a built-in function operator or the result of an earlier call 159 | // to declare_fun. The number and sorts of the terms in args should match 160 | // the arity and argument sorts of the function f. Behavior if the 161 | // arguments are incorrect is solver-dependent. If a solver does not 162 | // support an SMT-LIB operation, an UnsupportedError is returned. 163 | fn apply_fun(&self, f: &Function, args: &[Self::T]) -> SMTResult; 164 | 165 | // Sams as above, except the arguments are in a vector of references to 166 | // terms rather than a vector of terms. 167 | fn apply_fun_refs(&self, f: &Function, args: &[&Self::T]) -> SMTResult; 168 | 169 | /////////////////////////////////////////////////////////////////////////// 170 | // Solving // 171 | /////////////////////////////////////////////////////////////////////////// 172 | 173 | // Returns the current level of the solver. Initially the level is 0. The 174 | // level increases with each push and decreases with each pop. 175 | fn level(&self) -> u32; 176 | 177 | // A push sets a checkpoint in the state of the solver. This method pushes 178 | // n times. Returns Ok(true) if successful. Otherwise, returns 179 | // InternalError. 180 | fn push(&mut self, n: u32) -> SMTResult; 181 | 182 | // A pop restores the solver to the state it had at the last checkpoint. 183 | // This method pops n times. n must be less than or equal to the current 184 | // level. If it is not, returns an APIError. Otherwise, returns 185 | // InternalError if unsuccessful. 186 | fn pop(&mut self, n: u32) -> SMTResult; 187 | 188 | // Add an assertion t to the solver context. The sort of the assertion must 189 | // be Boolean. Returns Ok(true) if successful. Otherwise returns 190 | // InternalError. 191 | fn assert(&mut self, t: &Self::T) -> SMTResult; 192 | 193 | // Check the satisfiability of all the assertions in the current solver 194 | // context. Returns a CheckSatResult (see above). 195 | fn check_sat(&mut self) -> CheckSatResult; 196 | 197 | // After a call to check_sat that returns Sat, if the solver has model 198 | // production enabled, then it can report a concrete constant value for any 199 | // term t. get_value returns that value, given a term t. If check_sat has 200 | // not been called or if the most recent call returned unsat, an APIError 201 | // is returned. 202 | fn get_value(&mut self, t: &Self::T) -> SMTResult; 203 | } 204 | 205 | // Support for Z3 solver. 206 | #[macro_use] 207 | extern crate lazy_static; 208 | pub mod z3; 209 | pub use z3::Z3Solver; 210 | 211 | pub fn new_z3_solver() -> Z3Solver { 212 | Z3Solver::new() 213 | } 214 | -------------------------------------------------------------------------------- /src/smt_err.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use std::error::Error; 7 | use std::fmt; 8 | 9 | #[derive(Debug, PartialEq)] 10 | pub enum SMTError { 11 | APIError(String), 12 | UnsupportedError(String), 13 | InternalError(String), 14 | } 15 | use SMTError::*; 16 | 17 | impl SMTError { 18 | // An error resulting from an illegal use of the API. 19 | pub fn new_api(msg: &str) -> SMTError { 20 | APIError(msg.to_string()) 21 | } 22 | 23 | // An error resulting from an unsupported feature. 24 | pub fn new_unsupported(msg: &str) -> SMTError { 25 | UnsupportedError(msg.to_string()) 26 | } 27 | 28 | // An error propagated from the solver or from some library function. 29 | pub fn new_internal(msg: &str) -> SMTError { 30 | InternalError(msg.to_string()) 31 | } 32 | } 33 | 34 | impl fmt::Display for SMTError { 35 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 36 | let message = match self { 37 | APIError(s) => s, 38 | UnsupportedError(s) => s, 39 | InternalError(s) => s, 40 | }; 41 | write!(f, "{}", message) 42 | } 43 | } 44 | 45 | impl Error for SMTError { 46 | fn description(&self) -> &str { 47 | match self { 48 | APIError(s) => s, 49 | UnsupportedError(s) => s, 50 | InternalError(s) => s, 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/smt_ops.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub enum Sorts { 7 | // Core theory 8 | Bool, 9 | 10 | // Arithmetic 11 | Int, 12 | Real, 13 | 14 | // Arrays 15 | Array, 16 | 17 | // Bitvectors 18 | BitVec(u32), 19 | } 20 | 21 | pub enum Fn<'a> { 22 | // Core theory 23 | False, 24 | True, 25 | Not, 26 | Implies, 27 | And, 28 | Or, 29 | Xor, 30 | Eq, 31 | Neq, // abbreviation for Not o Eq 32 | Ite, 33 | Distinct, 34 | 35 | // Arithmetic 36 | Uminus, 37 | Minus, 38 | Plus, 39 | Times, 40 | Divide, 41 | Div, 42 | Mod, 43 | Abs, 44 | LE, 45 | LT, 46 | GE, 47 | GT, 48 | ToReal, 49 | ToInt, 50 | IsInt, 51 | 52 | // Arrays 53 | Select, 54 | Store, 55 | 56 | // Records 57 | RecordSelect(&'a str), 58 | RecordUpdate(&'a str), 59 | 60 | // Bitvectors 61 | Concat, 62 | Extract(u32, u32), 63 | Bvnot, 64 | Bvand, 65 | Bvor, 66 | Bvneg, 67 | Bvadd, 68 | Bvmul, 69 | Bvudiv, 70 | Bvurem, 71 | Bvshl, 72 | Bvlshr, 73 | Bvult, 74 | Bvnand, 75 | Bvnor, 76 | Bvxor, 77 | Bvxnor, 78 | Bvcomp, 79 | Bvsub, 80 | Bvsdiv, 81 | Bvsrem, 82 | Bvsmod, 83 | Bvashr, 84 | Repeat(u32), 85 | ZeroExtend(u32), 86 | SignExtend(u32), 87 | RotateLeft(u32), 88 | RotateRight(u32), 89 | Bvule, 90 | Bvugt, 91 | Bvuge, 92 | Bvslt, 93 | Bvsle, 94 | Bvsgt, 95 | Bvsge, 96 | } 97 | -------------------------------------------------------------------------------- /src/z3.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | // This file contains an implementation of the SMTSolver trait using 7 | // the Z3 SMT solver. We use the z3-sys crate for bindings to Z3. 8 | 9 | use std::collections::hash_map::Entry; 10 | use std::collections::HashMap; 11 | use std::error::Error; 12 | use std::ffi::{CStr, CString}; 13 | 14 | extern crate z3_sys; 15 | use z3_sys::*; 16 | 17 | use crate::smt_err::SMTError; 18 | use crate::smt_ops::*; 19 | use crate::Function::*; 20 | use crate::*; 21 | 22 | // Z3 is not thread-safe, so we guard each call to Z3 using the 23 | // following semaphore. This imposes some overhead, so if 24 | // thread-safety is not required, it can be disabled (see below). 25 | use std::sync::Mutex; 26 | lazy_static! { 27 | static ref Z3_MUTEX: Mutex<()> = Mutex::new(()); 28 | } 29 | 30 | // Macro for thread-safety. If not needed, comment this out and 31 | // replace with the line below it. 32 | macro_rules! mutex { 33 | () => { 34 | let _mutex = Z3_MUTEX.lock().unwrap(); 35 | }; 36 | } 37 | // macro_rules! mutex { () => { }; } 38 | 39 | // Implementation of Sort for Z3. 40 | #[derive(Debug, Eq, Hash, PartialEq)] 41 | pub struct Z3Sort { 42 | context: Z3_context, 43 | sort: Z3_sort, 44 | } 45 | 46 | fn new_z3_sort(context: Z3_context, sort: Z3_sort, mutex: bool) -> Z3Sort { 47 | unsafe { 48 | if mutex { 49 | mutex!(); 50 | } 51 | Z3_inc_ref(context, Z3_sort_to_ast(context, sort)); 52 | } 53 | Z3Sort { context, sort } 54 | } 55 | 56 | impl Clone for Z3Sort { 57 | fn clone(&self) -> Z3Sort { 58 | new_z3_sort(self.context, self.sort, true) 59 | } 60 | } 61 | 62 | impl Drop for Z3Sort { 63 | fn drop(&mut self) { 64 | unsafe { 65 | mutex!(); 66 | Z3_dec_ref(self.context, Z3_sort_to_ast(self.context, self.sort)); 67 | } 68 | } 69 | } 70 | 71 | impl Sort for Z3Sort { 72 | fn to_string(&self) -> SMTResult { 73 | unsafe { 74 | mutex!(); 75 | let ptr = Z3_sort_to_string(self.context, self.sort) as *mut i8; 76 | if ptr.is_null() { 77 | Err(SMTError::new_internal("Unable to convert Sort to string")) 78 | } else { 79 | let cstr = CStr::from_ptr(ptr); 80 | match cstr.to_str() { 81 | Ok(s) => Ok(String::from(s)), 82 | Err(e) => Err(SMTError::new_internal(e.description())), 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | // Implementation of UninterpretedFunction for Z3. 90 | #[derive(Debug, Eq, Hash, PartialEq)] 91 | pub struct Z3UninterpretedFunction { 92 | context: Z3_context, 93 | decl: Z3_func_decl, 94 | } 95 | 96 | fn new_z3_uninterpreted_function( 97 | context: Z3_context, 98 | decl: Z3_func_decl, 99 | mutex: bool, 100 | ) -> Z3UninterpretedFunction { 101 | unsafe { 102 | if mutex { 103 | mutex!(); 104 | } 105 | Z3_inc_ref(context, Z3_func_decl_to_ast(context, decl)); 106 | Z3UninterpretedFunction { context, decl } 107 | } 108 | } 109 | 110 | impl Clone for Z3UninterpretedFunction { 111 | fn clone(&self) -> Z3UninterpretedFunction { 112 | new_z3_uninterpreted_function(self.context, self.decl, true) 113 | } 114 | } 115 | 116 | impl Drop for Z3UninterpretedFunction { 117 | fn drop(&mut self) { 118 | unsafe { 119 | mutex!(); 120 | Z3_dec_ref(self.context, Z3_func_decl_to_ast(self.context, self.decl)); 121 | } 122 | } 123 | } 124 | 125 | impl UninterpretedFunction for Z3UninterpretedFunction { 126 | fn to_string(&self) -> SMTResult { 127 | unsafe { 128 | mutex!(); 129 | let ptr = Z3_get_decl_name(self.context, self.decl); 130 | if ptr.is_null() { 131 | Err(SMTError::new_internal( 132 | "Unable to get name for UninterpretedFunction", 133 | )) 134 | } else { 135 | let ptr = Z3_get_symbol_string(self.context, ptr); 136 | if ptr.is_null() { 137 | Err(SMTError::new_internal( 138 | "Unable to convert UninterpretedFunction name to string", 139 | )) 140 | } else { 141 | let cstr = CStr::from_ptr(ptr); 142 | match cstr.to_str() { 143 | Ok(s) => Ok(String::from(s)), 144 | Err(e) => Err(SMTError::new_internal(e.description())), 145 | } 146 | } 147 | } 148 | } 149 | } 150 | } 151 | 152 | // Implementation of Term for Z3. 153 | #[derive(Debug, Eq, Hash, PartialEq)] 154 | pub struct Z3Term { 155 | context: Z3_context, 156 | ast: Z3_ast, 157 | } 158 | 159 | fn new_z3_term(context: Z3_context, ast: Z3_ast, mutex: bool) -> Z3Term { 160 | unsafe { 161 | if mutex { 162 | mutex!(); 163 | } 164 | Z3_inc_ref(context, ast); 165 | } 166 | Z3Term { context, ast } 167 | } 168 | 169 | impl Clone for Z3Term { 170 | fn clone(&self) -> Z3Term { 171 | new_z3_term(self.context, self.ast, true) 172 | } 173 | } 174 | 175 | impl Drop for Z3Term { 176 | fn drop(&mut self) { 177 | unsafe { 178 | mutex!(); 179 | Z3_dec_ref(self.context, self.ast); 180 | } 181 | } 182 | } 183 | 184 | impl Term for Z3Term { 185 | fn to_string(&self) -> SMTResult { 186 | unsafe { 187 | mutex!(); 188 | let ptr = Z3_ast_to_string(self.context, self.ast) as *mut i8; 189 | if ptr.is_null() { 190 | Err(SMTError::new_internal("Unable to convert Term to string")) 191 | } else { 192 | let cstr = CStr::from_ptr(ptr); 193 | match cstr.to_str() { 194 | Ok(s) => Ok(String::from(s)), 195 | Err(e) => Err(SMTError::new_internal(e.description())), 196 | } 197 | } 198 | } 199 | } 200 | fn to_int(&self) -> SMTResult { 201 | unsafe { 202 | mutex!(); 203 | let z3sort = Z3_get_sort(self.context, self.ast); 204 | let ok = match Z3_get_sort_kind(self.context, z3sort) { 205 | SortKind::Int => true, 206 | SortKind::Real => true, 207 | SortKind::BV => true, 208 | _ => false, 209 | }; 210 | if !ok { 211 | Err(SMTError::new_api( 212 | "Term::to_int: sort of term is not int, real, or bitvector", 213 | )) 214 | } else { 215 | let ok = match Z3_get_ast_kind(self.context, self.ast) { 216 | AstKind::Numeral => true, 217 | _ => false, 218 | }; 219 | if !ok { 220 | Err(SMTError::new_api( 221 | "Term::to_int: term is not a numeric constant", 222 | )) 223 | } else { 224 | let mut tmp: ::std::os::raw::c_longlong = 0; 225 | let res = Z3_get_numeral_int64(self.context, self.ast, &mut tmp); 226 | if res { 227 | Ok(tmp) 228 | } else { 229 | Err(SMTError::new_api( 230 | "Term::to_int: unable to convert to 64-bit integer", 231 | )) 232 | } 233 | } 234 | } 235 | } 236 | } 237 | } 238 | 239 | // Used to keep track of declared record sorts. 240 | struct RecordInfo { 241 | field_map: HashMap, 242 | cons_decl: Z3UninterpretedFunction, 243 | } 244 | 245 | // Implementation of SMTSolver for Z3. 246 | pub struct Z3Solver { 247 | config: Z3_config, 248 | context: Z3_context, 249 | solver: Z3_solver, 250 | level: u32, 251 | model: Option, 252 | last_result: CheckSatResult, 253 | record_map: HashMap, 254 | } 255 | 256 | impl Drop for Z3Solver { 257 | fn drop(&mut self) { 258 | unsafe { 259 | self.record_map.clear(); 260 | mutex!(); 261 | if let Some(m) = self.model { 262 | Z3_model_dec_ref(self.context, m); 263 | } 264 | Z3_solver_dec_ref(self.context, self.solver); 265 | Z3_del_context(self.context); 266 | Z3_del_config(self.config); 267 | } 268 | } 269 | } 270 | 271 | impl SMTSolver for Z3Solver { 272 | type S = Z3Sort; 273 | type T = Z3Term; 274 | type F = Z3UninterpretedFunction; 275 | fn new() -> Z3Solver { 276 | unsafe { 277 | mutex!(); 278 | let cfg = Z3_mk_config(); 279 | let cxt = Z3_mk_context(cfg); 280 | let s = Z3_mk_solver(cxt); 281 | Z3_solver_inc_ref(cxt, s); 282 | Z3Solver { 283 | config: cfg, 284 | context: cxt, 285 | solver: s, 286 | level: 0, 287 | model: None, 288 | last_result: CheckSatResult::Unknown, 289 | 290 | record_map: HashMap::new(), 291 | } 292 | } 293 | } 294 | fn get_sort(&self, t: &Z3Term) -> SMTResult { 295 | unsafe { 296 | mutex!(); 297 | let z3sort = Z3_get_sort(self.context, t.ast); 298 | Ok(new_z3_sort(self.context, z3sort, false)) 299 | } 300 | } 301 | fn declare_sort(&self, name: &str) -> SMTResult { 302 | unsafe { 303 | match CString::new(name) { 304 | Err(e) => Err(SMTError::new_internal(e.description())), 305 | Ok(str) => { 306 | let sort = { 307 | mutex!(); 308 | let sym = Z3_mk_string_symbol(self.context, str.as_ptr()); 309 | Z3_mk_uninterpreted_sort(self.context, sym) 310 | }; 311 | Ok(new_z3_sort(self.context, sort, true)) 312 | } 313 | } 314 | } 315 | } 316 | fn lookup_sort(&self, s: Sorts) -> SMTResult { 317 | unsafe { 318 | mutex!(); 319 | match s { 320 | Sorts::Bool => Ok(new_z3_sort( 321 | self.context, 322 | Z3_mk_bool_sort(self.context), 323 | false, 324 | )), 325 | Sorts::Int => Ok(new_z3_sort( 326 | self.context, 327 | Z3_mk_int_sort(self.context), 328 | false, 329 | )), 330 | Sorts::Real => Ok(new_z3_sort( 331 | self.context, 332 | Z3_mk_real_sort(self.context), 333 | false, 334 | )), 335 | Sorts::Array => Err(SMTError::new_api("Use apply_sort to create Array sorts")), 336 | Sorts::BitVec(i) => Ok(new_z3_sort( 337 | self.context, 338 | Z3_mk_bv_sort(self.context, i), 339 | false, 340 | )), 341 | } 342 | } 343 | } 344 | fn apply_sort(&self, s: Sorts, s1: &Z3Sort, s2: &Z3Sort) -> SMTResult { 345 | unsafe { 346 | match s { 347 | Sorts::Array => { 348 | let sort = { 349 | mutex!(); 350 | Z3_mk_array_sort(self.context, s1.sort, s2.sort) 351 | }; 352 | Ok(new_z3_sort(self.context, sort, true)) 353 | } 354 | _ => Err(SMTError::new_api( 355 | "apply_sort called with non-sort constructor", 356 | )), 357 | } 358 | } 359 | } 360 | fn declare_record_sort( 361 | &mut self, 362 | name: &str, 363 | fields: &[&str], 364 | sorts: &[&Z3Sort], 365 | ) -> SMTResult { 366 | let mut result = Ok(0 as Z3_sort); 367 | if fields.len() != sorts.len() { 368 | result = Err(SMTError::new_api( 369 | "declare_record_sort: fields and sorts must have same length", 370 | )); 371 | }; 372 | unsafe { 373 | mutex!(); 374 | let record_name = name.to_string(); 375 | let cons_name = record_name.clone() + "_cons"; 376 | let recognizer_name = record_name.clone() + "_is_cons"; 377 | 378 | let record_name_sym = match CString::new(record_name.clone()) { 379 | Err(e) => { 380 | if result.is_ok() { 381 | result = Err(SMTError::new_internal(e.description())); 382 | } 383 | 0 as Z3_symbol 384 | } 385 | Ok(str) => Z3_mk_string_symbol(self.context, str.as_ptr()), 386 | }; 387 | let cons_name_sym = match CString::new(cons_name) { 388 | Err(e) => { 389 | if result.is_ok() { 390 | result = Err(SMTError::new_internal(e.description())); 391 | }; 392 | 0 as Z3_symbol 393 | } 394 | Ok(str) => Z3_mk_string_symbol(self.context, str.as_ptr()), 395 | }; 396 | let recognizer_name_sym = match CString::new(recognizer_name) { 397 | Err(e) => { 398 | if result.is_ok() { 399 | result = Err(SMTError::new_internal(e.description())); 400 | }; 401 | 0 as Z3_symbol 402 | } 403 | Ok(str) => Z3_mk_string_symbol(self.context, str.as_ptr()), 404 | }; 405 | let mut selector_names = Vec::new(); 406 | let mut z3_sorts = Vec::new(); 407 | let mut sort_refs = Vec::new(); 408 | for (i, sort) in sorts.iter().enumerate() { 409 | let selector_name = fields[i].to_string(); 410 | let selector_name_sym = match CString::new(selector_name) { 411 | Err(e) => { 412 | if result.is_ok() { 413 | result = Err(SMTError::new_internal(e.description())); 414 | }; 415 | 0 as Z3_symbol 416 | } 417 | Ok(str) => Z3_mk_string_symbol(self.context, str.as_ptr()), 418 | }; 419 | selector_names.push(selector_name_sym); 420 | z3_sorts.push(sort.sort); 421 | sort_refs.push(0); 422 | } 423 | if result.is_ok() { 424 | let constructor = Z3_mk_constructor( 425 | self.context, 426 | cons_name_sym, 427 | recognizer_name_sym, 428 | fields.len() as ::std::os::raw::c_uint, 429 | selector_names.as_ptr(), 430 | z3_sorts.as_ptr(), 431 | sort_refs.as_mut_ptr(), 432 | ); 433 | let mut tmp = Vec::new(); 434 | tmp.push(constructor); 435 | let datatype = Z3_mk_datatype(self.context, record_name_sym, 1, tmp.as_mut_ptr()); 436 | let dummy = 0 as Z3_func_decl; 437 | let mut cons_decls = [dummy]; 438 | let mut tester_decls = [dummy]; 439 | let mut accessors = Vec::new(); 440 | for _sort in sorts { 441 | accessors.push(dummy); 442 | } 443 | Z3_query_constructor( 444 | self.context, 445 | constructor, 446 | fields.len() as ::std::os::raw::c_uint, 447 | cons_decls.as_mut_ptr(), 448 | tester_decls.as_mut_ptr(), 449 | accessors.as_mut_ptr(), 450 | ); 451 | let mut field_map = HashMap::new(); 452 | for (i, field) in fields.iter().enumerate() { 453 | match field_map.entry(field.to_string()) { 454 | Entry::Occupied(_) => { 455 | if result.is_ok() { 456 | result = Err(SMTError::new_api( 457 | "declare_record_sort: field names must be distinct", 458 | )); 459 | } 460 | } 461 | Entry::Vacant(v) => { 462 | v.insert(i as u32); 463 | } 464 | } 465 | } 466 | match self.record_map.entry(datatype) { 467 | Entry::Occupied(_) => { 468 | if result.is_ok() { 469 | result = Err(SMTError::new_api( 470 | "declare_record_sort: record already exists", 471 | )); 472 | } 473 | } 474 | Entry::Vacant(v) => { 475 | let record_info = RecordInfo { 476 | field_map, 477 | cons_decl: new_z3_uninterpreted_function( 478 | self.context, 479 | cons_decls[0], 480 | false, 481 | ), 482 | }; 483 | v.insert(record_info); 484 | } 485 | }; 486 | Z3_del_constructor(self.context, constructor); 487 | if result.is_ok() { 488 | result = Ok(datatype); 489 | } 490 | } 491 | }; 492 | match result { 493 | Err(err) => Err(err), 494 | Ok(sort) => Ok(new_z3_sort(self.context, sort, true)), 495 | } 496 | } 497 | fn is_record_sort(&self, sort: &Z3Sort) -> bool { 498 | self.record_map.get(&sort.sort).is_some() 499 | } 500 | fn declare_fun( 501 | &self, 502 | name: &str, 503 | args: &[&Z3Sort], 504 | sort: &Z3Sort, 505 | ) -> SMTResult { 506 | unsafe { 507 | match CString::new(name) { 508 | Err(e) => Err(SMTError::new_internal(e.description())), 509 | Ok(str) => { 510 | let decl = { 511 | mutex!(); 512 | let sym = Z3_mk_string_symbol(self.context, str.as_ptr()); 513 | let mut tmp = Vec::new(); 514 | for arg in args { 515 | tmp.push(arg.sort); 516 | } 517 | Z3_mk_func_decl( 518 | self.context, 519 | sym, 520 | tmp.len() as ::std::os::raw::c_uint, 521 | tmp.as_ptr(), 522 | sort.sort, 523 | ) 524 | }; 525 | Ok(new_z3_uninterpreted_function(self.context, decl, true)) 526 | } 527 | } 528 | } 529 | } 530 | fn declare_const(&self, name: &str, sort: &Z3Sort) -> SMTResult { 531 | unsafe { 532 | match CString::new(name) { 533 | Err(e) => Err(SMTError::new_internal(e.description())), 534 | Ok(str) => { 535 | let sym = Z3_mk_string_symbol(self.context, str.as_ptr()); 536 | let term = { 537 | mutex!(); 538 | Z3_mk_const(self.context, sym, sort.sort) 539 | }; 540 | Ok(new_z3_term(self.context, term, true)) 541 | } 542 | } 543 | } 544 | } 545 | fn lookup_const(&self, f: Fn) -> SMTResult { 546 | unsafe { 547 | mutex!(); 548 | match f { 549 | Fn::False => Ok(new_z3_term(self.context, Z3_mk_false(self.context), false)), 550 | Fn::True => Ok(new_z3_term(self.context, Z3_mk_true(self.context), false)), 551 | _ => Err(SMTError::new_api("lookup_const called with non-constant")), 552 | } 553 | } 554 | } 555 | fn const_from_int(&self, value: i64, sort: &Z3Sort) -> SMTResult { 556 | unsafe { 557 | mutex!(); 558 | let sortkind = Z3_get_sort_kind(self.context, sort.sort); 559 | let ok = match sortkind { 560 | SortKind::Int => true, 561 | SortKind::Real => true, 562 | SortKind::BV => { 563 | if value < 0 { 564 | false 565 | } else { 566 | let size = Z3_get_bv_sort_size(self.context, sort.sort); 567 | if size >= 63 { 568 | true 569 | } else { 570 | value < ((1 as i64) << size) 571 | } 572 | } 573 | } 574 | _ => false, 575 | }; 576 | if !ok { 577 | if sortkind == SortKind::BV { 578 | Err(SMTError::new_api("const_from_int: bitvector requires non-negative value representable in the bit-width")) 579 | } else { 580 | Err(SMTError::new_api( 581 | "const_from_int: sort of term is not int, real, or bitvector", 582 | )) 583 | } 584 | } else { 585 | Ok(new_z3_term( 586 | self.context, 587 | Z3_mk_int64(self.context, value, sort.sort), 588 | false, 589 | )) 590 | } 591 | } 592 | } 593 | fn const_from_string(&self, value: &str, sort: &Z3Sort) -> SMTResult { 594 | unsafe { 595 | mutex!(); 596 | let sortkind = Z3_get_sort_kind(self.context, sort.sort); 597 | let mut ok = match sortkind { 598 | SortKind::Int => true, 599 | SortKind::Real => true, 600 | SortKind::BV => true, 601 | _ => false, 602 | }; 603 | if !ok { 604 | Err(SMTError::new_api( 605 | "const_from_string: sort must be int, real or bitvector", 606 | )) 607 | } else { 608 | let mut dec = 0; 609 | ok = true; 610 | let mut neg = false; 611 | for (i, c) in value.chars().enumerate() { 612 | if c == '.' { 613 | dec += 1; 614 | } else if c == '-' && i == 0 { 615 | neg = true; 616 | } else if !c.is_digit(10) { 617 | ok = false; 618 | } 619 | } 620 | if sortkind != SortKind::Real && dec > 0 { 621 | Err(SMTError::new_api( 622 | "const_from_string: decimal not allowed in non-real", 623 | )) 624 | } else if dec > 1 { 625 | Err(SMTError::new_api( 626 | "const_from_string: no more than one decimal place allowed", 627 | )) 628 | } else if neg && sortkind == SortKind::BV { 629 | Err(SMTError::new_api( 630 | "const_from_string: negative value not allowed for bitvectors", 631 | )) 632 | } else if !ok { 633 | Err(SMTError::new_api( 634 | "const_from_string: string contains invalid character: digit expected", 635 | )) 636 | } else { 637 | match CString::new(value) { 638 | Err(e) => Err(SMTError::new_internal(e.description())), 639 | Ok(str) => Ok(new_z3_term( 640 | self.context, 641 | Z3_mk_numeral(self.context, str.as_ptr(), sort.sort), 642 | false, 643 | )), 644 | } 645 | } 646 | } 647 | } 648 | } 649 | fn record_const(&self, record_sort: &Z3Sort, field_values: &[Z3Term]) -> SMTResult { 650 | let mut tmp = Vec::new(); 651 | for value in field_values { 652 | tmp.push(value); 653 | } 654 | self.record_const_refs(record_sort, &tmp) 655 | } 656 | fn record_const_refs( 657 | &self, 658 | record_sort: &Z3Sort, 659 | field_values: &[&Z3Term], 660 | ) -> SMTResult { 661 | let mut tmp = Vec::new(); 662 | for value in field_values { 663 | tmp.push(value.ast); 664 | } 665 | match self.record_map.get(&record_sort.sort) { 666 | None => Err(SMTError::new_api( 667 | "record_const_refs: Non-record or unknown sort", 668 | )), 669 | Some(record_info) => unsafe { 670 | mutex!(); 671 | Ok(new_z3_term( 672 | self.context, 673 | Z3_mk_app( 674 | self.context, 675 | record_info.cons_decl.decl, 676 | tmp.len() as ::std::os::raw::c_uint, 677 | tmp.as_ptr(), 678 | ), 679 | false, 680 | )) 681 | }, 682 | } 683 | } 684 | fn apply_fun( 685 | &self, 686 | f: &Function, 687 | args: &[Z3Term], 688 | ) -> SMTResult { 689 | let mut tmp = Vec::new(); 690 | for arg in args { 691 | tmp.push(arg); 692 | } 693 | self.apply_fun_refs(f, &tmp) 694 | } 695 | fn apply_fun_refs( 696 | &self, 697 | f: &Function, 698 | args: &[&Z3Term], 699 | ) -> SMTResult { 700 | macro_rules! unary_fun { 701 | ( $z3fun:ident ) => { 702 | Ok($z3fun(self.context, args[0].ast)) 703 | }; 704 | } 705 | macro_rules! unary_int_fun { 706 | ( $z3fun:ident, $i:ident ) => { 707 | Ok($z3fun( 708 | self.context, 709 | *$i as ::std::os::raw::c_uint, 710 | args[0].ast, 711 | )) 712 | }; 713 | } 714 | macro_rules! binary_fun { 715 | ( $z3fun:ident ) => { 716 | Ok($z3fun(self.context, args[0].ast, args[1].ast)) 717 | }; 718 | } 719 | macro_rules! trinary_fun { 720 | ( $z3fun:ident ) => { 721 | Ok($z3fun(self.context, args[0].ast, args[1].ast, args[2].ast)) 722 | }; 723 | } 724 | macro_rules! nary_fun { 725 | ( $z3fun:ident ) => {{ 726 | let mut tmp = Vec::new(); 727 | for arg in args { 728 | tmp.push(arg.ast); 729 | } 730 | Ok($z3fun( 731 | self.context, 732 | tmp.len() as ::std::os::raw::c_uint, 733 | tmp.as_ptr(), 734 | )) 735 | }}; 736 | } 737 | 738 | let term = unsafe { 739 | mutex!(); 740 | match f { 741 | // Uninterpreted function 742 | UF(f) => { 743 | let mut tmp = Vec::new(); 744 | for arg in args { 745 | tmp.push(arg.ast); 746 | } 747 | Ok(Z3_mk_app( 748 | self.context, 749 | f.decl, 750 | tmp.len() as ::std::os::raw::c_uint, 751 | tmp.as_ptr(), 752 | )) 753 | } 754 | 755 | // Core 756 | Op(Fn::Not) => unary_fun!(Z3_mk_not), 757 | Op(Fn::Implies) => binary_fun!(Z3_mk_implies), 758 | Op(Fn::And) => nary_fun!(Z3_mk_and), 759 | Op(Fn::Or) => nary_fun!(Z3_mk_or), 760 | Op(Fn::Xor) => binary_fun!(Z3_mk_xor), 761 | Op(Fn::Eq) => binary_fun!(Z3_mk_eq), 762 | Op(Fn::Neq) => binary_fun!(Z3_mk_eq), 763 | Op(Fn::Ite) => trinary_fun!(Z3_mk_ite), 764 | Op(Fn::Distinct) => nary_fun!(Z3_mk_distinct), 765 | 766 | // Arithmetic 767 | Op(Fn::Uminus) => unary_fun!(Z3_mk_unary_minus), 768 | Op(Fn::Minus) => nary_fun!(Z3_mk_sub), 769 | Op(Fn::Plus) => nary_fun!(Z3_mk_add), 770 | Op(Fn::Times) => nary_fun!(Z3_mk_mul), 771 | Op(Fn::Divide) => binary_fun!(Z3_mk_div), 772 | Op(Fn::Div) => binary_fun!(Z3_mk_div), 773 | Op(Fn::Mod) => binary_fun!(Z3_mk_mod), 774 | Op(Fn::Abs) => Err(SMTError::new_unsupported("Z3 does not support abs")), 775 | Op(Fn::LE) => binary_fun!(Z3_mk_le), 776 | Op(Fn::LT) => binary_fun!(Z3_mk_lt), 777 | Op(Fn::GE) => binary_fun!(Z3_mk_ge), 778 | Op(Fn::GT) => binary_fun!(Z3_mk_gt), 779 | Op(Fn::ToReal) => unary_fun!(Z3_mk_int2real), 780 | Op(Fn::ToInt) => unary_fun!(Z3_mk_real2int), 781 | Op(Fn::IsInt) => unary_fun!(Z3_mk_is_int), 782 | 783 | // Arrays 784 | Op(Fn::Select) => binary_fun!(Z3_mk_select), 785 | Op(Fn::Store) => trinary_fun!(Z3_mk_store), 786 | 787 | // Bitvectors 788 | Op(Fn::Concat) => binary_fun!(Z3_mk_concat), 789 | Op(Fn::Bvnot) => unary_fun!(Z3_mk_bvnot), 790 | Op(Fn::Bvand) => binary_fun!(Z3_mk_bvand), 791 | Op(Fn::Bvor) => binary_fun!(Z3_mk_bvor), 792 | Op(Fn::Bvneg) => unary_fun!(Z3_mk_bvneg), 793 | Op(Fn::Bvadd) => binary_fun!(Z3_mk_bvadd), 794 | Op(Fn::Bvmul) => binary_fun!(Z3_mk_bvmul), 795 | Op(Fn::Bvudiv) => binary_fun!(Z3_mk_bvudiv), 796 | Op(Fn::Bvurem) => binary_fun!(Z3_mk_bvurem), 797 | Op(Fn::Bvshl) => binary_fun!(Z3_mk_bvshl), 798 | Op(Fn::Bvlshr) => binary_fun!(Z3_mk_bvlshr), 799 | Op(Fn::Bvult) => binary_fun!(Z3_mk_bvult), 800 | Op(Fn::Bvnand) => binary_fun!(Z3_mk_bvnand), 801 | Op(Fn::Bvnor) => binary_fun!(Z3_mk_bvnor), 802 | Op(Fn::Bvxor) => binary_fun!(Z3_mk_bvxor), 803 | Op(Fn::Bvxnor) => binary_fun!(Z3_mk_bvxnor), 804 | Op(Fn::Bvcomp) => Err(SMTError::new_unsupported("Z3 does not support bvcomp")), 805 | Op(Fn::Bvsub) => binary_fun!(Z3_mk_bvsub), 806 | Op(Fn::Bvsdiv) => binary_fun!(Z3_mk_bvsdiv), 807 | Op(Fn::Bvsrem) => binary_fun!(Z3_mk_bvsrem), 808 | Op(Fn::Bvsmod) => binary_fun!(Z3_mk_bvsmod), 809 | Op(Fn::Bvashr) => binary_fun!(Z3_mk_bvashr), 810 | Op(Fn::Bvule) => binary_fun!(Z3_mk_bvule), 811 | Op(Fn::Bvugt) => binary_fun!(Z3_mk_bvugt), 812 | Op(Fn::Bvuge) => binary_fun!(Z3_mk_bvuge), 813 | Op(Fn::Bvslt) => binary_fun!(Z3_mk_bvslt), 814 | Op(Fn::Bvsle) => binary_fun!(Z3_mk_bvsle), 815 | Op(Fn::Bvsgt) => binary_fun!(Z3_mk_bvsgt), 816 | Op(Fn::Bvsge) => binary_fun!(Z3_mk_bvsge), 817 | // Bitvector ops with one integer index 818 | Op(Fn::Repeat(i)) => unary_int_fun!(Z3_mk_repeat, i), 819 | Op(Fn::ZeroExtend(i)) => unary_int_fun!(Z3_mk_zero_ext, i), 820 | Op(Fn::SignExtend(i)) => unary_int_fun!(Z3_mk_sign_ext, i), 821 | Op(Fn::RotateLeft(i)) => unary_int_fun!(Z3_mk_rotate_left, i), 822 | Op(Fn::RotateRight(i)) => unary_int_fun!(Z3_mk_rotate_right, i), 823 | // Bitvector ops with two integer indices 824 | Op(Fn::Extract(i, j)) => Ok(Z3_mk_extract( 825 | self.context, 826 | *i as ::std::os::raw::c_uint, 827 | *j as ::std::os::raw::c_uint, 828 | args[0].ast, 829 | )), 830 | // Record operators 831 | Op(Fn::RecordSelect(field)) => { 832 | if args.len() != 1 { 833 | Err(SMTError::new_api( 834 | "RecordSelect should have exactly one argument", 835 | )) 836 | } else { 837 | let sort = Z3_get_sort(self.context, args[0].ast); 838 | match self.record_map.get(&sort) { 839 | None => Err(SMTError::new_api( 840 | "RecordSelect applied to non-record or unknown sort", 841 | )), 842 | Some(record_info) => { 843 | match record_info.field_map.get(&field.to_string()) { 844 | None => Err(SMTError::new_api( 845 | "RecordSelect applied with unknown field", 846 | )), 847 | Some(i) => { 848 | let selector = Z3_get_datatype_sort_constructor_accessor( 849 | self.context, 850 | sort, 851 | 0, 852 | *i, 853 | ); 854 | let mut tmp = Vec::new(); 855 | tmp.push(args[0].ast); 856 | Ok(Z3_mk_app(self.context, selector, 1, tmp.as_ptr())) 857 | } 858 | } 859 | } 860 | } 861 | } 862 | } 863 | Op(Fn::RecordUpdate(field)) => { 864 | if args.len() != 2 { 865 | Err(SMTError::new_api( 866 | "RecordUpdate should have exactly two arguments", 867 | )) 868 | } else { 869 | let sort = Z3_get_sort(self.context, args[0].ast); 870 | match self.record_map.get(&sort) { 871 | None => Err(SMTError::new_api( 872 | "First argument of RecordUpdate is non-record or unknown sort", 873 | )), 874 | Some(record_info) => { 875 | match record_info.field_map.get(&field.to_string()) { 876 | None => Err(SMTError::new_api( 877 | "RecordUpdate applied with unknown field", 878 | )), 879 | Some(i) => { 880 | let selector = Z3_get_datatype_sort_constructor_accessor( 881 | self.context, 882 | sort, 883 | 0, 884 | *i, 885 | ); 886 | if Z3_get_sort(self.context, args[1].ast) 887 | != Z3_get_range(self.context, selector) 888 | { 889 | Err(SMTError::new_api("Second argument of RecordUpdate does not have correct sort")) 890 | } else { 891 | Ok(Z3_datatype_update_field( 892 | self.context, 893 | selector, 894 | args[0].ast, 895 | args[1].ast, 896 | )) 897 | } 898 | } 899 | } 900 | } 901 | } 902 | } 903 | } 904 | 905 | // Unknown operator 906 | _ => Err(SMTError::new_unsupported( 907 | "apply_fun called with unknown operator", 908 | )), 909 | } 910 | }; 911 | match term { 912 | Ok(t) => Ok(new_z3_term(self.context, t, true)), 913 | Err(err) => Err(err), 914 | } 915 | } 916 | fn level(&self) -> u32 { 917 | self.level 918 | } 919 | fn push(&mut self, n: u32) -> SMTResult { 920 | unsafe { 921 | mutex!(); 922 | for _x in 0..n { 923 | Z3_solver_push(self.context, self.solver); 924 | } 925 | } 926 | self.level += n; 927 | Ok(true) 928 | } 929 | fn pop(&mut self, n: u32) -> SMTResult { 930 | if n > self.level { 931 | Err(SMTError::new_api("pop: called with n > level")) 932 | } else { 933 | unsafe { 934 | mutex!(); 935 | if let Some(m) = self.model { 936 | Z3_model_dec_ref(self.context, m); 937 | } 938 | self.model = None; 939 | Z3_solver_pop(self.context, self.solver, 1); 940 | } 941 | self.level -= n; 942 | Ok(true) 943 | } 944 | } 945 | fn assert(&mut self, t: &Z3Term) -> SMTResult { 946 | unsafe { 947 | mutex!(); 948 | Z3_solver_assert(self.context, self.solver, t.ast); 949 | Ok(true) 950 | } 951 | } 952 | fn check_sat(&mut self) -> CheckSatResult { 953 | unsafe { 954 | mutex!(); 955 | if let Some(m) = self.model { 956 | Z3_model_dec_ref(self.context, m); 957 | } 958 | self.model = None; 959 | self.last_result = match Z3_solver_check(self.context, self.solver) { 960 | Z3_L_TRUE => CheckSatResult::Sat, 961 | Z3_L_FALSE => CheckSatResult::Unsat, 962 | _ => CheckSatResult::Unknown, 963 | }; 964 | self.last_result 965 | } 966 | } 967 | fn get_value(&mut self, t: &Z3Term) -> SMTResult { 968 | if self.last_result != CheckSatResult::Sat { 969 | Err(SMTError::new_api( 970 | "get_value: can only be called after a call to check_sat that returns Sat", 971 | )) 972 | } else { 973 | unsafe { 974 | mutex!(); 975 | if self.model == None { 976 | let m = Z3_solver_get_model(self.context, self.solver); 977 | if !m.is_null() { 978 | Z3_model_inc_ref(self.context, m); 979 | self.model = Some(m); 980 | } 981 | } 982 | if let Some(m) = self.model { 983 | let mut tmp: Z3_ast = t.ast; 984 | let res = Z3_model_eval(self.context, m, t.ast, true, &mut tmp); 985 | if !res { 986 | Err(SMTError::new_internal("Unable to get value")) 987 | } else { 988 | Ok(new_z3_term(self.context, tmp, false)) 989 | } 990 | } else { 991 | Err(SMTError::new_internal("Model not found")) 992 | } 993 | } 994 | } 995 | } 996 | } 997 | -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Facebook, Inc. and its affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use rust_smt::smt_err::*; 7 | use rust_smt::smt_ops::*; 8 | use rust_smt::Function::*; 9 | use rust_smt::*; 10 | 11 | #[test] 12 | fn test_create_solver() { 13 | let _smt = new_z3_solver(); 14 | } 15 | 16 | #[test] 17 | fn test_get_sort() { 18 | let smt = new_z3_solver(); 19 | let bool_sort = smt.lookup_sort(Sorts::Bool).unwrap(); 20 | assert_eq!( 21 | bool_sort, 22 | smt.get_sort(&smt.lookup_const(Fn::True).unwrap()).unwrap() 23 | ); 24 | } 25 | 26 | #[test] 27 | fn test_declare_sort() { 28 | let smt = new_z3_solver(); 29 | let s = smt.declare_sort("s").unwrap(); 30 | assert_eq!(s.to_string().unwrap(), "s"); 31 | } 32 | 33 | #[test] 34 | fn test_lookup_sort_bool() { 35 | let smt = new_z3_solver(); 36 | let bool_sort = smt.lookup_sort(Sorts::Bool).unwrap(); 37 | assert_eq!(bool_sort.to_string().unwrap(), "Bool"); 38 | } 39 | 40 | #[test] 41 | fn test_lookup_sort_int() { 42 | let smt = new_z3_solver(); 43 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 44 | assert_eq!(int_sort.to_string().unwrap(), "Int"); 45 | } 46 | 47 | #[test] 48 | fn test_lookup_sort_real() { 49 | let smt = new_z3_solver(); 50 | let real_sort = smt.lookup_sort(Sorts::Real).unwrap(); 51 | assert_eq!(real_sort.to_string().unwrap(), "Real"); 52 | } 53 | 54 | #[test] 55 | fn test_lookup_sort_bitvec8() { 56 | let smt = new_z3_solver(); 57 | let bv8_sort = smt.lookup_sort(Sorts::BitVec(8)).unwrap(); 58 | assert_eq!(bv8_sort.to_string().unwrap(), "(_ BitVec 8)"); 59 | } 60 | 61 | #[test] 62 | fn test_lookup_sort_array_error() { 63 | let smt = new_z3_solver(); 64 | assert_eq!( 65 | smt.lookup_sort(Sorts::Array).unwrap_err(), 66 | SMTError::new_api("Use apply_sort to create Array sorts") 67 | ); 68 | } 69 | 70 | #[test] 71 | fn test_apply_sort_array_int_int() { 72 | let smt = new_z3_solver(); 73 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 74 | let array_sort = smt.apply_sort(Sorts::Array, &int_sort, &int_sort).unwrap(); 75 | assert_eq!(array_sort.to_string().unwrap(), "(Array Int Int)"); 76 | } 77 | 78 | #[test] 79 | fn test_apply_sort_error() { 80 | let smt = new_z3_solver(); 81 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 82 | assert_eq!( 83 | smt.apply_sort(Sorts::Int, &int_sort, &int_sort) 84 | .unwrap_err(), 85 | SMTError::new_api("apply_sort called with non-sort constructor") 86 | ); 87 | } 88 | 89 | #[test] 90 | fn test_declare_record_sort() { 91 | let mut smt = new_z3_solver(); 92 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 93 | let record_sort = smt 94 | .declare_record_sort("Record", &["hd", "tl"], &[&int_sort, &int_sort]) 95 | .unwrap(); 96 | assert_eq!(record_sort.to_string().unwrap(), "Record"); 97 | } 98 | 99 | #[test] 100 | fn test_declare_record_sort_field_sort_mismatch_error() { 101 | let mut smt = new_z3_solver(); 102 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 103 | assert_eq!( 104 | smt.declare_record_sort("Record", &["hd", "tl"], &[&int_sort]) 105 | .unwrap_err(), 106 | SMTError::new_api("declare_record_sort: fields and sorts must have same length") 107 | ); 108 | } 109 | 110 | #[test] 111 | fn test_declare_record_sort_duplicate_fields_error() { 112 | let mut smt = new_z3_solver(); 113 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 114 | assert_eq!( 115 | smt.declare_record_sort("Record", &["hd", "hd"], &[&int_sort, &int_sort]) 116 | .unwrap_err(), 117 | SMTError::new_api("declare_record_sort: field names must be distinct") 118 | ); 119 | } 120 | 121 | #[test] 122 | fn test_declare_record_sort_duplicate_records_ok() { 123 | let mut smt = new_z3_solver(); 124 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 125 | let _record_sort = smt 126 | .declare_record_sort("Record", &["hd", "tl"], &[&int_sort, &int_sort]) 127 | .unwrap(); 128 | let _record_sort2 = smt 129 | .declare_record_sort("Record2", &["hd", "tl"], &[&int_sort, &int_sort]) 130 | .unwrap(); 131 | } 132 | 133 | #[test] 134 | fn test_declare_record_sort_duplicate_records_error() { 135 | let mut smt = new_z3_solver(); 136 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 137 | let _record_sort = smt 138 | .declare_record_sort("Record", &["hd", "tl"], &[&int_sort, &int_sort]) 139 | .unwrap(); 140 | assert_eq!( 141 | smt.declare_record_sort("Record", &["hd", "tl"], &[&int_sort, &int_sort]) 142 | .unwrap_err(), 143 | SMTError::new_api("declare_record_sort: record already exists") 144 | ); 145 | } 146 | 147 | #[test] 148 | fn test_is_record() { 149 | let mut smt = new_z3_solver(); 150 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 151 | let record_sort = smt 152 | .declare_record_sort("Record", &["hd", "tl"], &[&int_sort, &int_sort]) 153 | .unwrap(); 154 | assert_eq!(smt.is_record_sort(&record_sort), true); 155 | assert_eq!(smt.is_record_sort(&int_sort), false); 156 | } 157 | 158 | #[test] 159 | fn test_declare_fun() { 160 | let smt = new_z3_solver(); 161 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 162 | let f = smt.declare_fun("f", &[&int_sort], &int_sort).unwrap(); 163 | assert_eq!(f.to_string().unwrap(), "f"); 164 | } 165 | 166 | #[test] 167 | fn test_declare_const() { 168 | let smt = new_z3_solver(); 169 | let s = smt.lookup_sort(Sorts::Int).unwrap(); 170 | let x = smt.declare_const("x", &s).unwrap(); 171 | assert_eq!(x.to_string().unwrap(), "x"); 172 | } 173 | 174 | #[test] 175 | fn test_clone_term() { 176 | let smt = new_z3_solver(); 177 | let s = smt.lookup_sort(Sorts::Int).unwrap(); 178 | let x = smt.declare_const("x", &s).unwrap(); 179 | let x_prime = x.clone(); 180 | assert_eq!(x.to_string().unwrap(), x_prime.to_string().unwrap()); 181 | } 182 | 183 | #[test] 184 | fn test_lookup_const_true() { 185 | let smt = new_z3_solver(); 186 | let t = smt.lookup_const(Fn::True).unwrap(); 187 | assert_eq!(t.to_string().unwrap(), "true"); 188 | } 189 | 190 | #[test] 191 | fn test_lookup_const_false() { 192 | let smt = new_z3_solver(); 193 | let f = smt.lookup_const(Fn::False).unwrap(); 194 | assert_eq!(f.to_string().unwrap(), "false"); 195 | } 196 | 197 | #[test] 198 | fn test_lookup_nonconst_error() { 199 | let smt = new_z3_solver(); 200 | assert_eq!( 201 | smt.lookup_const(Fn::Uminus).unwrap_err(), 202 | SMTError::new_api("lookup_const called with non-constant") 203 | ); 204 | } 205 | 206 | #[test] 207 | fn test_symbolic_const_to_int_error() { 208 | let smt = new_z3_solver(); 209 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 210 | let x = smt.declare_const("x", &int_sort).unwrap(); 211 | assert_eq!( 212 | x.to_int().unwrap_err(), 213 | SMTError::new_api("Term::to_int: term is not a numeric constant") 214 | ); 215 | } 216 | 217 | #[test] 218 | fn test_bad_sorted_const_to_int_error() { 219 | let smt = new_z3_solver(); 220 | let bool_sort = smt.lookup_sort(Sorts::Bool).unwrap(); 221 | let b = smt.declare_const("b", &bool_sort).unwrap(); 222 | assert_eq!( 223 | b.to_int().unwrap_err(), 224 | SMTError::new_api("Term::to_int: sort of term is not int, real, or bitvector") 225 | ); 226 | } 227 | 228 | #[test] 229 | fn test_non_int_real_const_to_int_error() { 230 | let smt = new_z3_solver(); 231 | let real_sort = smt.lookup_sort(Sorts::Real).unwrap(); 232 | let x = smt.const_from_string("0.5", &real_sort).unwrap(); 233 | assert_eq!( 234 | x.to_int().unwrap_err(), 235 | SMTError::new_api("Term::to_int: unable to convert to 64-bit integer") 236 | ); 237 | } 238 | 239 | #[test] 240 | fn test_const_from_int_to_int() { 241 | let smt = new_z3_solver(); 242 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 243 | assert_eq!( 244 | smt.const_from_int(0, &int_sort).unwrap().to_int().unwrap(), 245 | 0 246 | ); 247 | assert_eq!( 248 | smt.const_from_int(-1000, &int_sort) 249 | .unwrap() 250 | .to_int() 251 | .unwrap(), 252 | -1000 253 | ); 254 | assert_eq!( 255 | smt.const_from_int(32768, &int_sort) 256 | .unwrap() 257 | .to_int() 258 | .unwrap(), 259 | 32768 260 | ); 261 | } 262 | 263 | #[test] 264 | fn test_const_from_int_to_real() { 265 | let smt = new_z3_solver(); 266 | let real_sort = smt.lookup_sort(Sorts::Real).unwrap(); 267 | assert_eq!( 268 | smt.const_from_int(1, &real_sort) 269 | .unwrap() 270 | .to_string() 271 | .unwrap(), 272 | "1.0" 273 | ); 274 | assert_eq!( 275 | smt.const_from_int(-1234, &real_sort) 276 | .unwrap() 277 | .to_string() 278 | .unwrap(), 279 | "(- 1234.0)" 280 | ); 281 | assert_eq!( 282 | smt.const_from_int(100000, &real_sort) 283 | .unwrap() 284 | .to_string() 285 | .unwrap(), 286 | "100000.0" 287 | ); 288 | } 289 | 290 | #[test] 291 | fn test_const_from_int_to_bv() { 292 | let smt = new_z3_solver(); 293 | let bv8_sort = smt.lookup_sort(Sorts::BitVec(8)).unwrap(); 294 | assert_eq!( 295 | smt.const_from_int(1, &bv8_sort) 296 | .unwrap() 297 | .to_string() 298 | .unwrap(), 299 | "#x01" 300 | ); 301 | assert_eq!( 302 | smt.const_from_int(123, &bv8_sort) 303 | .unwrap() 304 | .to_string() 305 | .unwrap(), 306 | "#x7b" 307 | ); 308 | assert_eq!( 309 | smt.const_from_int(0xFF, &bv8_sort) 310 | .unwrap() 311 | .to_string() 312 | .unwrap(), 313 | "#xff" 314 | ); 315 | } 316 | 317 | #[test] 318 | fn test_const_from_int_to_bv_to_int() { 319 | let smt = new_z3_solver(); 320 | let bv8_sort = smt.lookup_sort(Sorts::BitVec(8)).unwrap(); 321 | assert_eq!( 322 | smt.const_from_int(1, &bv8_sort).unwrap().to_int().unwrap(), 323 | 1 324 | ); 325 | assert_eq!( 326 | smt.const_from_int(123, &bv8_sort) 327 | .unwrap() 328 | .to_int() 329 | .unwrap(), 330 | 123 331 | ); 332 | assert_eq!( 333 | smt.const_from_int(0xFF, &bv8_sort) 334 | .unwrap() 335 | .to_int() 336 | .unwrap(), 337 | 255 338 | ); 339 | } 340 | 341 | #[test] 342 | fn test_const_from_int_bad_sort_error() { 343 | let smt = new_z3_solver(); 344 | let bool_sort = smt.lookup_sort(Sorts::Bool).unwrap(); 345 | assert_eq!( 346 | smt.const_from_int(1, &bool_sort).unwrap_err(), 347 | SMTError::new_api("const_from_int: sort of term is not int, real, or bitvector") 348 | ); 349 | } 350 | 351 | #[test] 352 | fn test_const_from_int_negative_bv_error() { 353 | let smt = new_z3_solver(); 354 | let bv16_sort = smt.lookup_sort(Sorts::BitVec(16)).unwrap(); 355 | assert_eq!( 356 | smt.const_from_int(-1, &bv16_sort).unwrap_err(), 357 | SMTError::new_api( 358 | "const_from_int: bitvector requires non-negative value representable in the bit-width" 359 | ) 360 | ); 361 | } 362 | 363 | #[test] 364 | fn test_const_from_int_bv_too_large_error() { 365 | let smt = new_z3_solver(); 366 | let bv16_sort = smt.lookup_sort(Sorts::BitVec(16)).unwrap(); 367 | assert_eq!( 368 | smt.const_from_int(65535, &bv16_sort) 369 | .unwrap() 370 | .to_string() 371 | .unwrap(), 372 | "#xffff" 373 | ); 374 | assert_eq!( 375 | smt.const_from_int(65536, &bv16_sort).unwrap_err(), 376 | SMTError::new_api( 377 | "const_from_int: bitvector requires non-negative value representable in the bit-width" 378 | ) 379 | ); 380 | } 381 | 382 | #[test] 383 | fn test_const_from_string_to_int() { 384 | let smt = new_z3_solver(); 385 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 386 | assert_eq!( 387 | smt.const_from_string("0", &int_sort) 388 | .unwrap() 389 | .to_string() 390 | .unwrap(), 391 | "0" 392 | ); 393 | assert_eq!( 394 | smt.const_from_string("-1000000000000000", &int_sort) 395 | .unwrap() 396 | .to_string() 397 | .unwrap(), 398 | "(- 1000000000000000)" 399 | ); 400 | assert_eq!( 401 | smt.const_from_string("123456789012345678901234567890", &int_sort) 402 | .unwrap() 403 | .to_string() 404 | .unwrap(), 405 | "123456789012345678901234567890" 406 | ); 407 | } 408 | 409 | #[test] 410 | fn test_const_from_string_to_real() { 411 | let smt = new_z3_solver(); 412 | let real_sort = smt.lookup_sort(Sorts::Real).unwrap(); 413 | assert_eq!( 414 | smt.const_from_string("100", &real_sort) 415 | .unwrap() 416 | .to_string() 417 | .unwrap(), 418 | "100.0" 419 | ); 420 | assert_eq!( 421 | smt.const_from_string("0.25", &real_sort) 422 | .unwrap() 423 | .to_string() 424 | .unwrap(), 425 | "(/ 1.0 4.0)" 426 | ); 427 | assert_eq!( 428 | smt.const_from_string("123456789012345678901234567890", &real_sort) 429 | .unwrap() 430 | .to_string() 431 | .unwrap(), 432 | "123456789012345678901234567890.0" 433 | ); 434 | } 435 | 436 | #[test] 437 | fn test_const_from_string_to_bv() { 438 | let smt = new_z3_solver(); 439 | let bv256_sort = smt.lookup_sort(Sorts::BitVec(256)).unwrap(); 440 | assert_eq!( 441 | smt.const_from_string("100", &bv256_sort) 442 | .unwrap() 443 | .to_string() 444 | .unwrap(), 445 | "#x0000000000000000000000000000000000000000000000000000000000000064" 446 | ); 447 | assert_eq!( 448 | smt.const_from_string("65536", &bv256_sort) 449 | .unwrap() 450 | .to_string() 451 | .unwrap(), 452 | "#x0000000000000000000000000000000000000000000000000000000000010000" 453 | ); 454 | assert_eq!( 455 | smt.const_from_string("123456789012345678901234567890", &bv256_sort) 456 | .unwrap() 457 | .to_string() 458 | .unwrap(), 459 | "#x00000000000000000000000000000000000000018ee90ff6c373e0ee4e3f0ad2" 460 | ); 461 | } 462 | 463 | #[test] 464 | fn test_const_from_string_bad_sort_error() { 465 | let smt = new_z3_solver(); 466 | let bool_sort = smt.lookup_sort(Sorts::Bool).unwrap(); 467 | assert_eq!( 468 | smt.const_from_string("1", &bool_sort).unwrap_err(), 469 | SMTError::new_api("const_from_string: sort must be int, real or bitvector") 470 | ); 471 | } 472 | 473 | #[test] 474 | fn test_const_from_string_decimal_in_int_error() { 475 | let smt = new_z3_solver(); 476 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 477 | assert_eq!( 478 | smt.const_from_string("1.0", &int_sort).unwrap_err(), 479 | SMTError::new_api("const_from_string: decimal not allowed in non-real") 480 | ); 481 | } 482 | 483 | #[test] 484 | fn test_const_from_string_too_many_decimals_error() { 485 | let smt = new_z3_solver(); 486 | let real_sort = smt.lookup_sort(Sorts::Real).unwrap(); 487 | assert_eq!( 488 | smt.const_from_string("1.0.", &real_sort).unwrap_err(), 489 | SMTError::new_api("const_from_string: no more than one decimal place allowed") 490 | ) 491 | } 492 | 493 | #[test] 494 | fn test_const_from_string_neg_bitvector_error() { 495 | let smt = new_z3_solver(); 496 | let bv32_sort = smt.lookup_sort(Sorts::BitVec(32)).unwrap(); 497 | assert_eq!( 498 | smt.const_from_string("-1", &bv32_sort).unwrap_err(), 499 | SMTError::new_api("const_from_string: negative value not allowed for bitvectors") 500 | ); 501 | } 502 | 503 | #[test] 504 | fn test_const_from_string_invalid_char_error() { 505 | let smt = new_z3_solver(); 506 | let bv32_sort = smt.lookup_sort(Sorts::BitVec(32)).unwrap(); 507 | assert_eq!( 508 | smt.const_from_string("1234567890ABCDEF", &bv32_sort) 509 | .unwrap_err(), 510 | SMTError::new_api("const_from_string: string contains invalid character: digit expected") 511 | ); 512 | } 513 | 514 | #[test] 515 | fn test_record_const() { 516 | let mut smt = new_z3_solver(); 517 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 518 | let record_sort = smt 519 | .declare_record_sort("Record", &["hd", "tl"], &[&int_sort, &int_sort]) 520 | .unwrap(); 521 | let x = smt.declare_const("x", &int_sort).unwrap(); 522 | let zero = smt.const_from_int(0, &int_sort).unwrap(); 523 | assert_eq!( 524 | smt.record_const_refs(&record_sort, &[&x, &zero]) 525 | .unwrap() 526 | .to_string() 527 | .unwrap(), 528 | "(Record_cons x 0)" 529 | ); 530 | assert_eq!( 531 | smt.record_const(&record_sort, &[x, zero]) 532 | .unwrap() 533 | .to_string() 534 | .unwrap(), 535 | "(Record_cons x 0)" 536 | ); 537 | } 538 | 539 | #[test] 540 | fn test_record_const_error() { 541 | let smt = new_z3_solver(); 542 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 543 | let x = smt.declare_const("x", &int_sort).unwrap(); 544 | let zero = smt.const_from_int(0, &int_sort).unwrap(); 545 | assert_eq!( 546 | smt.record_const_refs(&int_sort, &[&x, &zero]).unwrap_err(), 547 | SMTError::new_api("record_const_refs: Non-record or unknown sort") 548 | ); 549 | } 550 | 551 | #[test] 552 | fn test_apply_fun_uf() { 553 | let smt = new_z3_solver(); 554 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 555 | let f = UF(smt.declare_fun("f", &[&int_sort], &int_sort).unwrap()); 556 | let x = smt.declare_const("x", &int_sort).unwrap(); 557 | let fx = smt.apply_fun(&f, &[x]).unwrap(); 558 | assert_eq!(fx.to_string().unwrap(), "(f x)"); 559 | } 560 | 561 | #[test] 562 | fn test_apply_fun_core() { 563 | let smt = new_z3_solver(); 564 | let bool_sort = smt.lookup_sort(Sorts::Bool).unwrap(); 565 | let p = smt.declare_const("p", &bool_sort).unwrap(); 566 | let q = smt.declare_const("q", &bool_sort).unwrap(); 567 | assert_eq!( 568 | smt.apply_fun_refs(&Op(Fn::Not), &[&p]) 569 | .unwrap() 570 | .to_string() 571 | .unwrap(), 572 | "(not p)" 573 | ); 574 | assert_eq!( 575 | smt.apply_fun_refs(&Op(Fn::Implies), &[&p, &q]) 576 | .unwrap() 577 | .to_string() 578 | .unwrap(), 579 | "(=> p q)" 580 | ); 581 | assert_eq!( 582 | smt.apply_fun_refs(&Op(Fn::And), &[&p, &q]) 583 | .unwrap() 584 | .to_string() 585 | .unwrap(), 586 | "(and p q)" 587 | ); 588 | assert_eq!( 589 | smt.apply_fun_refs(&Op(Fn::Or), &[&p, &q]) 590 | .unwrap() 591 | .to_string() 592 | .unwrap(), 593 | "(or p q)" 594 | ); 595 | assert_eq!( 596 | smt.apply_fun_refs(&Op(Fn::Xor), &[&p, &q]) 597 | .unwrap() 598 | .to_string() 599 | .unwrap(), 600 | "(xor p q)" 601 | ); 602 | assert_eq!( 603 | smt.apply_fun_refs(&Op(Fn::Eq), &[&p, &q]) 604 | .unwrap() 605 | .to_string() 606 | .unwrap(), 607 | "(= p q)" 608 | ); 609 | assert_eq!( 610 | smt.apply_fun_refs(&Op(Fn::Ite), &[&p, &q, &p]) 611 | .unwrap() 612 | .to_string() 613 | .unwrap(), 614 | "(ite p q p)" 615 | ); 616 | assert_eq!( 617 | smt.apply_fun_refs(&Op(Fn::Distinct), &[&p, &q]) 618 | .unwrap() 619 | .to_string() 620 | .unwrap(), 621 | "(distinct p q)" 622 | ); 623 | } 624 | 625 | #[test] 626 | fn test_apply_fun_arith() { 627 | let smt = new_z3_solver(); 628 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 629 | let real_sort = smt.lookup_sort(Sorts::Real).unwrap(); 630 | let x = smt.declare_const("x", &int_sort).unwrap(); 631 | let y = smt.declare_const("y", &int_sort).unwrap(); 632 | let z = smt.declare_const("z", &int_sort).unwrap(); 633 | let a = smt.declare_const("a", &real_sort).unwrap(); 634 | let b = smt.declare_const("b", &real_sort).unwrap(); 635 | assert_eq!( 636 | smt.apply_fun_refs(&Op(Fn::Uminus), &[&x]) 637 | .unwrap() 638 | .to_string() 639 | .unwrap(), 640 | "(- x)" 641 | ); 642 | assert_eq!( 643 | smt.apply_fun_refs(&Op(Fn::Minus), &[&a, &b]) 644 | .unwrap() 645 | .to_string() 646 | .unwrap(), 647 | "(- a b)" 648 | ); 649 | assert_eq!( 650 | smt.apply_fun_refs(&Op(Fn::Plus), &[&x, &y]) 651 | .unwrap() 652 | .to_string() 653 | .unwrap(), 654 | "(+ x y)" 655 | ); 656 | assert_eq!( 657 | smt.apply_fun_refs(&Op(Fn::Times), &[&x, &y, &z]) 658 | .unwrap() 659 | .to_string() 660 | .unwrap(), 661 | "(* x y z)" 662 | ); 663 | assert_eq!( 664 | smt.apply_fun_refs(&Op(Fn::Divide), &[&a, &b]) 665 | .unwrap() 666 | .to_string() 667 | .unwrap(), 668 | "(/ a b)" 669 | ); 670 | assert_eq!( 671 | smt.apply_fun_refs(&Op(Fn::Div), &[&x, &y]) 672 | .unwrap() 673 | .to_string() 674 | .unwrap(), 675 | "(div x y)" 676 | ); 677 | assert_eq!( 678 | smt.apply_fun_refs(&Op(Fn::Mod), &[&x, &y]) 679 | .unwrap() 680 | .to_string() 681 | .unwrap(), 682 | "(mod x y)" 683 | ); 684 | assert_eq!( 685 | smt.apply_fun_refs(&Op(Fn::Abs), &[&x, &y]).unwrap_err(), 686 | SMTError::new_unsupported("Z3 does not support abs") 687 | ); 688 | assert_eq!( 689 | smt.apply_fun_refs(&Op(Fn::LE), &[&a, &b]) 690 | .unwrap() 691 | .to_string() 692 | .unwrap(), 693 | "(<= a b)" 694 | ); 695 | assert_eq!( 696 | smt.apply_fun_refs(&Op(Fn::LT), &[&x, &y]) 697 | .unwrap() 698 | .to_string() 699 | .unwrap(), 700 | "(< x y)" 701 | ); 702 | assert_eq!( 703 | smt.apply_fun_refs(&Op(Fn::GE), &[&a, &b]) 704 | .unwrap() 705 | .to_string() 706 | .unwrap(), 707 | "(>= a b)" 708 | ); 709 | assert_eq!( 710 | smt.apply_fun_refs(&Op(Fn::GT), &[&x, &y]) 711 | .unwrap() 712 | .to_string() 713 | .unwrap(), 714 | "(> x y)" 715 | ); 716 | assert_eq!( 717 | smt.apply_fun_refs(&Op(Fn::ToReal), &[&x]) 718 | .unwrap() 719 | .to_string() 720 | .unwrap(), 721 | "(to_real x)" 722 | ); 723 | assert_eq!( 724 | smt.apply_fun_refs(&Op(Fn::ToInt), &[&a]) 725 | .unwrap() 726 | .to_string() 727 | .unwrap(), 728 | "(to_int a)" 729 | ); 730 | assert_eq!( 731 | smt.apply_fun_refs(&Op(Fn::IsInt), &[&b]) 732 | .unwrap() 733 | .to_string() 734 | .unwrap(), 735 | "(is_int b)" 736 | ); 737 | } 738 | 739 | #[test] 740 | fn test_apply_fun_array() { 741 | let smt = new_z3_solver(); 742 | let index_sort = smt.declare_sort("A").unwrap(); 743 | let element_sort = smt.declare_sort("B").unwrap(); 744 | let array_sort = smt 745 | .apply_sort(Sorts::Array, &index_sort, &element_sort) 746 | .unwrap(); 747 | let a = smt.declare_const("a", &array_sort).unwrap(); 748 | let i = smt.declare_const("i", &index_sort).unwrap(); 749 | let x = smt.declare_const("x", &element_sort).unwrap(); 750 | assert_eq!( 751 | smt.apply_fun_refs(&Op(Fn::Select), &[&a, &i]) 752 | .unwrap() 753 | .to_string() 754 | .unwrap(), 755 | "(select a i)" 756 | ); 757 | assert_eq!( 758 | smt.apply_fun_refs(&Op(Fn::Store), &[&a, &i, &x]) 759 | .unwrap() 760 | .to_string() 761 | .unwrap(), 762 | "(store a i x)" 763 | ); 764 | } 765 | 766 | #[test] 767 | fn test_apply_fun_bitvec() { 768 | let smt = new_z3_solver(); 769 | let bv32_sort = smt.lookup_sort(Sorts::BitVec(32)).unwrap(); 770 | let x = smt.declare_const("x", &bv32_sort).unwrap(); 771 | let y = smt.declare_const("y", &bv32_sort).unwrap(); 772 | assert_eq!( 773 | smt.apply_fun_refs(&Op(Fn::Concat), &[&x, &y]) 774 | .unwrap() 775 | .to_string() 776 | .unwrap(), 777 | "(concat x y)" 778 | ); 779 | assert_eq!( 780 | smt.apply_fun_refs(&Op(Fn::Bvnot), &[&x]) 781 | .unwrap() 782 | .to_string() 783 | .unwrap(), 784 | "(bvnot x)" 785 | ); 786 | assert_eq!( 787 | smt.apply_fun_refs(&Op(Fn::Bvand), &[&x, &y]) 788 | .unwrap() 789 | .to_string() 790 | .unwrap(), 791 | "(bvand x y)" 792 | ); 793 | assert_eq!( 794 | smt.apply_fun_refs(&Op(Fn::Bvor), &[&x, &y]) 795 | .unwrap() 796 | .to_string() 797 | .unwrap(), 798 | "(bvor x y)" 799 | ); 800 | assert_eq!( 801 | smt.apply_fun_refs(&Op(Fn::Bvneg), &[&x]) 802 | .unwrap() 803 | .to_string() 804 | .unwrap(), 805 | "(bvneg x)" 806 | ); 807 | assert_eq!( 808 | smt.apply_fun_refs(&Op(Fn::Bvadd), &[&x, &y]) 809 | .unwrap() 810 | .to_string() 811 | .unwrap(), 812 | "(bvadd x y)" 813 | ); 814 | assert_eq!( 815 | smt.apply_fun_refs(&Op(Fn::Bvmul), &[&x, &y]) 816 | .unwrap() 817 | .to_string() 818 | .unwrap(), 819 | "(bvmul x y)" 820 | ); 821 | assert_eq!( 822 | smt.apply_fun_refs(&Op(Fn::Bvudiv), &[&x, &y]) 823 | .unwrap() 824 | .to_string() 825 | .unwrap(), 826 | "(bvudiv x y)" 827 | ); 828 | assert_eq!( 829 | smt.apply_fun_refs(&Op(Fn::Bvurem), &[&x, &y]) 830 | .unwrap() 831 | .to_string() 832 | .unwrap(), 833 | "(bvurem x y)" 834 | ); 835 | assert_eq!( 836 | smt.apply_fun_refs(&Op(Fn::Bvshl), &[&x, &y]) 837 | .unwrap() 838 | .to_string() 839 | .unwrap(), 840 | "(bvshl x y)" 841 | ); 842 | assert_eq!( 843 | smt.apply_fun_refs(&Op(Fn::Bvlshr), &[&x, &y]) 844 | .unwrap() 845 | .to_string() 846 | .unwrap(), 847 | "(bvlshr x y)" 848 | ); 849 | assert_eq!( 850 | smt.apply_fun_refs(&Op(Fn::Bvult), &[&x, &y]) 851 | .unwrap() 852 | .to_string() 853 | .unwrap(), 854 | "(bvult x y)" 855 | ); 856 | assert_eq!( 857 | smt.apply_fun_refs(&Op(Fn::Bvnand), &[&x, &y]) 858 | .unwrap() 859 | .to_string() 860 | .unwrap(), 861 | "(bvnand x y)" 862 | ); 863 | assert_eq!( 864 | smt.apply_fun_refs(&Op(Fn::Bvnor), &[&x, &y]) 865 | .unwrap() 866 | .to_string() 867 | .unwrap(), 868 | "(bvnor x y)" 869 | ); 870 | assert_eq!( 871 | smt.apply_fun_refs(&Op(Fn::Bvxor), &[&x, &y]) 872 | .unwrap() 873 | .to_string() 874 | .unwrap(), 875 | "(bvxor x y)" 876 | ); 877 | assert_eq!( 878 | smt.apply_fun_refs(&Op(Fn::Bvcomp), &[&x, &y]).unwrap_err(), 879 | SMTError::new_unsupported("Z3 does not support bvcomp") 880 | ); 881 | assert_eq!( 882 | smt.apply_fun_refs(&Op(Fn::Bvxnor), &[&x, &y]) 883 | .unwrap() 884 | .to_string() 885 | .unwrap(), 886 | "(bvxnor x y)" 887 | ); 888 | assert_eq!( 889 | smt.apply_fun_refs(&Op(Fn::Bvsub), &[&x, &y]) 890 | .unwrap() 891 | .to_string() 892 | .unwrap(), 893 | "(bvsub x y)" 894 | ); 895 | assert_eq!( 896 | smt.apply_fun_refs(&Op(Fn::Bvsdiv), &[&x, &y]) 897 | .unwrap() 898 | .to_string() 899 | .unwrap(), 900 | "(bvsdiv x y)" 901 | ); 902 | assert_eq!( 903 | smt.apply_fun_refs(&Op(Fn::Bvsrem), &[&x, &y]) 904 | .unwrap() 905 | .to_string() 906 | .unwrap(), 907 | "(bvsrem x y)" 908 | ); 909 | assert_eq!( 910 | smt.apply_fun_refs(&Op(Fn::Bvsmod), &[&x, &y]) 911 | .unwrap() 912 | .to_string() 913 | .unwrap(), 914 | "(bvsmod x y)" 915 | ); 916 | assert_eq!( 917 | smt.apply_fun_refs(&Op(Fn::Bvashr), &[&x, &y]) 918 | .unwrap() 919 | .to_string() 920 | .unwrap(), 921 | "(bvashr x y)" 922 | ); 923 | assert_eq!( 924 | smt.apply_fun_refs(&Op(Fn::Bvule), &[&x, &y]) 925 | .unwrap() 926 | .to_string() 927 | .unwrap(), 928 | "(bvule x y)" 929 | ); 930 | assert_eq!( 931 | smt.apply_fun_refs(&Op(Fn::Bvugt), &[&x, &y]) 932 | .unwrap() 933 | .to_string() 934 | .unwrap(), 935 | "(bvugt x y)" 936 | ); 937 | assert_eq!( 938 | smt.apply_fun_refs(&Op(Fn::Bvuge), &[&x, &y]) 939 | .unwrap() 940 | .to_string() 941 | .unwrap(), 942 | "(bvuge x y)" 943 | ); 944 | assert_eq!( 945 | smt.apply_fun_refs(&Op(Fn::Bvslt), &[&x, &y]) 946 | .unwrap() 947 | .to_string() 948 | .unwrap(), 949 | "(bvslt x y)" 950 | ); 951 | assert_eq!( 952 | smt.apply_fun_refs(&Op(Fn::Bvsle), &[&x, &y]) 953 | .unwrap() 954 | .to_string() 955 | .unwrap(), 956 | "(bvsle x y)" 957 | ); 958 | assert_eq!( 959 | smt.apply_fun_refs(&Op(Fn::Bvsgt), &[&x, &y]) 960 | .unwrap() 961 | .to_string() 962 | .unwrap(), 963 | "(bvsgt x y)" 964 | ); 965 | assert_eq!( 966 | smt.apply_fun_refs(&Op(Fn::Bvsge), &[&x, &y]) 967 | .unwrap() 968 | .to_string() 969 | .unwrap(), 970 | "(bvsge x y)" 971 | ); 972 | assert_eq!( 973 | smt.apply_fun_refs(&Op(Fn::Repeat(2)), &[&x]) 974 | .unwrap() 975 | .to_string() 976 | .unwrap(), 977 | "((_ repeat 2) x)" 978 | ); 979 | assert_eq!( 980 | smt.apply_fun_refs(&Op(Fn::ZeroExtend(2)), &[&x]) 981 | .unwrap() 982 | .to_string() 983 | .unwrap(), 984 | "((_ zero_extend 2) x)" 985 | ); 986 | assert_eq!( 987 | smt.apply_fun_refs(&Op(Fn::SignExtend(3)), &[&x]) 988 | .unwrap() 989 | .to_string() 990 | .unwrap(), 991 | "((_ sign_extend 3) x)" 992 | ); 993 | assert_eq!( 994 | smt.apply_fun_refs(&Op(Fn::RotateLeft(4)), &[&x]) 995 | .unwrap() 996 | .to_string() 997 | .unwrap(), 998 | "((_ rotate_left 4) x)" 999 | ); 1000 | assert_eq!( 1001 | smt.apply_fun_refs(&Op(Fn::RotateRight(5)), &[&x]) 1002 | .unwrap() 1003 | .to_string() 1004 | .unwrap(), 1005 | "((_ rotate_right 5) x)" 1006 | ); 1007 | assert_eq!( 1008 | smt.apply_fun_refs(&Op(Fn::Extract(7, 0)), &[&x]) 1009 | .unwrap() 1010 | .to_string() 1011 | .unwrap(), 1012 | "((_ extract 7 0) x)" 1013 | ); 1014 | } 1015 | 1016 | #[test] 1017 | fn test_apply_fun_record_select() { 1018 | let mut smt = new_z3_solver(); 1019 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 1020 | let record_sort = smt 1021 | .declare_record_sort("IntXInt", &["first", "second"], &[&int_sort, &int_sort]) 1022 | .unwrap(); 1023 | let r = smt.declare_const("r", &record_sort).unwrap(); 1024 | assert_eq!( 1025 | smt.apply_fun_refs(&Op(Fn::RecordSelect("first")), &[&r]) 1026 | .unwrap() 1027 | .to_string() 1028 | .unwrap(), 1029 | "(first r)" 1030 | ); 1031 | assert_eq!( 1032 | smt.apply_fun_refs(&Op(Fn::RecordSelect("second")), &[&r]) 1033 | .unwrap() 1034 | .to_string() 1035 | .unwrap(), 1036 | "(second r)" 1037 | ); 1038 | } 1039 | 1040 | #[test] 1041 | fn test_apply_fun_record_select_error() { 1042 | let mut smt = new_z3_solver(); 1043 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 1044 | let record_sort = smt 1045 | .declare_record_sort("IntXInt", &["first", "second"], &[&int_sort, &int_sort]) 1046 | .unwrap(); 1047 | let r = smt.declare_const("r", &record_sort).unwrap(); 1048 | assert_eq!( 1049 | smt.apply_fun_refs(&Op(Fn::RecordSelect("third")), &[&r]) 1050 | .unwrap_err(), 1051 | SMTError::new_api("RecordSelect applied with unknown field") 1052 | ); 1053 | let x = smt.declare_const("x", &int_sort).unwrap(); 1054 | assert_eq!( 1055 | smt.apply_fun_refs(&Op(Fn::RecordSelect("first")), &[&x]) 1056 | .unwrap_err(), 1057 | SMTError::new_api("RecordSelect applied to non-record or unknown sort") 1058 | ); 1059 | } 1060 | 1061 | #[test] 1062 | fn test_apply_fun_record_update() { 1063 | let mut smt = new_z3_solver(); 1064 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 1065 | let record_sort = smt 1066 | .declare_record_sort("IntXInt", &["first", "second"], &[&int_sort, &int_sort]) 1067 | .unwrap(); 1068 | let zero = smt.const_from_int(0, &int_sort).unwrap(); 1069 | let one = smt.const_from_int(1, &int_sort).unwrap(); 1070 | let r = smt 1071 | .record_const_refs(&record_sort, &[&zero, &zero]) 1072 | .unwrap(); 1073 | assert_eq!( 1074 | smt.apply_fun_refs(&Op(Fn::RecordUpdate("first")), &[&r, &one]) 1075 | .unwrap() 1076 | .to_string() 1077 | .unwrap(), 1078 | "((_ update-field (first (IntXInt) Int)) (IntXInt_cons 0 0) 1)" 1079 | ); 1080 | assert_eq!( 1081 | smt.apply_fun_refs(&Op(Fn::RecordUpdate("second")), &[&r, &one]) 1082 | .unwrap() 1083 | .to_string() 1084 | .unwrap(), 1085 | "((_ update-field (second (IntXInt) Int)) (IntXInt_cons 0 0) 1)" 1086 | ); 1087 | } 1088 | 1089 | #[test] 1090 | fn test_apply_fun_record_update_error() { 1091 | let mut smt = new_z3_solver(); 1092 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 1093 | let record_sort = smt 1094 | .declare_record_sort("IntXInt", &["first", "second"], &[&int_sort, &int_sort]) 1095 | .unwrap(); 1096 | let r = smt.declare_const("r", &record_sort).unwrap(); 1097 | assert_eq!( 1098 | smt.apply_fun_refs(&Op(Fn::RecordUpdate("first")), &[&r]) 1099 | .unwrap_err(), 1100 | SMTError::new_api("RecordUpdate should have exactly two arguments") 1101 | ); 1102 | let x = smt.declare_const("x", &int_sort).unwrap(); 1103 | assert_eq!( 1104 | smt.apply_fun_refs(&Op(Fn::RecordUpdate("first")), &[&x, &x]) 1105 | .unwrap_err(), 1106 | SMTError::new_api("First argument of RecordUpdate is non-record or unknown sort") 1107 | ); 1108 | assert_eq!( 1109 | smt.apply_fun_refs(&Op(Fn::RecordUpdate("third")), &[&r, &x]) 1110 | .unwrap_err(), 1111 | SMTError::new_api("RecordUpdate applied with unknown field") 1112 | ); 1113 | assert_eq!( 1114 | smt.apply_fun_refs(&Op(Fn::RecordUpdate("first")), &[&r, &r]) 1115 | .unwrap_err(), 1116 | SMTError::new_api("Second argument of RecordUpdate does not have correct sort") 1117 | ); 1118 | } 1119 | 1120 | #[test] 1121 | fn test_level() { 1122 | let smt = new_z3_solver(); 1123 | assert_eq!(smt.level(), 0); 1124 | } 1125 | 1126 | #[test] 1127 | fn test_push_pop() { 1128 | let mut smt = new_z3_solver(); 1129 | smt.push(1).unwrap(); 1130 | assert_eq!(smt.level(), 1); 1131 | smt.pop(1).unwrap(); 1132 | assert_eq!(smt.level(), 0); 1133 | } 1134 | 1135 | #[test] 1136 | fn test_pop_too_far_error() { 1137 | let mut smt = new_z3_solver(); 1138 | smt.push(1).unwrap(); 1139 | assert_eq!( 1140 | smt.pop(2).unwrap_err(), 1141 | SMTError::new_api("pop: called with n > level") 1142 | ); 1143 | } 1144 | 1145 | #[test] 1146 | fn test_assert() { 1147 | let mut smt = new_z3_solver(); 1148 | smt.assert(&smt.lookup_const(Fn::True).unwrap()).unwrap(); 1149 | } 1150 | 1151 | #[test] 1152 | fn test_check_sat_true() { 1153 | let mut smt = new_z3_solver(); 1154 | smt.assert(&smt.lookup_const(Fn::True).unwrap()).unwrap(); 1155 | assert_eq!(smt.check_sat(), CheckSatResult::Sat); 1156 | } 1157 | 1158 | #[test] 1159 | fn test_check_sat_false() { 1160 | let mut smt = new_z3_solver(); 1161 | smt.assert(&smt.lookup_const(Fn::False).unwrap()).unwrap(); 1162 | assert_eq!(smt.check_sat(), CheckSatResult::Unsat); 1163 | } 1164 | 1165 | #[test] 1166 | fn test_check_sat_xlty() { 1167 | let mut smt = new_z3_solver(); 1168 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 1169 | let x = smt.declare_const("x", &int_sort).unwrap(); 1170 | let y = smt.declare_const("y", &int_sort).unwrap(); 1171 | let x_lt_y = smt.apply_fun_refs(&Op(Fn::LT), &[&x, &y]).unwrap(); 1172 | smt.assert(&x_lt_y).unwrap(); 1173 | let smt_result = smt.check_sat(); 1174 | assert_eq!(smt_result, CheckSatResult::Sat); 1175 | } 1176 | 1177 | #[test] 1178 | fn test_check_sat_xnoteqx() { 1179 | let mut smt = new_z3_solver(); 1180 | let s = smt.declare_sort("s").unwrap(); 1181 | let x = smt.declare_const("x", &s).unwrap(); 1182 | let xeqx = smt.apply_fun_refs(&Op(Fn::Eq), &[&x, &x]).unwrap(); 1183 | let xnoteqx = smt.apply_fun_refs(&Op(Fn::Not), &[&xeqx]).unwrap(); 1184 | smt.assert(&xnoteqx).unwrap(); 1185 | let smt_result = smt.check_sat(); 1186 | assert_eq!(smt_result, CheckSatResult::Unsat); 1187 | } 1188 | 1189 | #[test] 1190 | fn test_check_sat_with_push() { 1191 | let mut smt = new_z3_solver(); 1192 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 1193 | let x = smt.declare_const("x", &int_sort).unwrap(); 1194 | let y = smt.declare_const("y", &int_sort).unwrap(); 1195 | let f = UF(smt.declare_fun("f", &[&int_sort], &int_sort).unwrap()); 1196 | let fx = smt.apply_fun_refs(&f, &[&x]).unwrap(); 1197 | let fy = smt.apply_fun_refs(&f, &[&y]).unwrap(); 1198 | let fx_eq_fy = smt.apply_fun_refs(&Op(Fn::Eq), &[&fx, &fy]).unwrap(); 1199 | smt.assert(&smt.apply_fun_refs(&Op(Fn::Not), &[&fx_eq_fy]).unwrap()) 1200 | .unwrap(); 1201 | smt.push(1).unwrap(); 1202 | smt.assert(&smt.apply_fun_refs(&Op(Fn::Eq), &[&x, &y]).unwrap()) 1203 | .unwrap(); 1204 | assert_eq!(smt.check_sat(), CheckSatResult::Unsat); 1205 | smt.pop(1).unwrap(); 1206 | assert_eq!(smt.check_sat(), CheckSatResult::Sat); 1207 | } 1208 | 1209 | #[test] 1210 | fn test_get_value() { 1211 | let mut smt = new_z3_solver(); 1212 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 1213 | let x = smt.declare_const("x", &int_sort).unwrap(); 1214 | let xeq0 = smt 1215 | .apply_fun_refs( 1216 | &Op(Fn::Eq), 1217 | &[&x, &smt.const_from_int(0, &int_sort).unwrap()], 1218 | ) 1219 | .unwrap(); 1220 | smt.assert(&xeq0).unwrap(); 1221 | assert_eq!(smt.check_sat(), CheckSatResult::Sat); 1222 | let value = smt.get_value(&x).unwrap(); 1223 | assert_eq!(value.to_string().unwrap(), "0"); 1224 | } 1225 | 1226 | #[test] 1227 | fn test_get_value_after_unsat_error() { 1228 | let mut smt = new_z3_solver(); 1229 | let s = smt.declare_sort("s").unwrap(); 1230 | let x = smt.declare_const("x", &s).unwrap(); 1231 | let xeqx = smt.apply_fun_refs(&Op(Fn::Eq), &[&x, &x]).unwrap(); 1232 | let xnoteqx = smt.apply_fun_refs(&Op(Fn::Not), &[&xeqx]).unwrap(); 1233 | smt.assert(&xnoteqx).unwrap(); 1234 | let smt_result = smt.check_sat(); 1235 | assert_eq!(smt_result, CheckSatResult::Unsat); 1236 | assert_eq!( 1237 | smt.get_value(&x).unwrap_err(), 1238 | SMTError::new_api( 1239 | "get_value: can only be called after a call to check_sat that returns Sat" 1240 | ) 1241 | ); 1242 | } 1243 | 1244 | #[test] 1245 | fn test_push_pop_get_model() { 1246 | let mut smt = new_z3_solver(); 1247 | let x = smt 1248 | .declare_const("x", &smt.lookup_sort(Sorts::Int).unwrap()) 1249 | .unwrap(); 1250 | smt.push(1).unwrap(); 1251 | let smt_result = smt.check_sat(); 1252 | assert_eq!(smt_result, CheckSatResult::Sat); 1253 | let _value = smt.get_value(&x).unwrap(); 1254 | smt.pop(1).unwrap(); 1255 | } 1256 | 1257 | #[test] 1258 | fn test_records() { 1259 | let mut smt = new_z3_solver(); 1260 | let int_sort = smt.lookup_sort(Sorts::Int).unwrap(); 1261 | let real_sort = smt.lookup_sort(Sorts::Real).unwrap(); 1262 | let record_sort = smt 1263 | .declare_record_sort("Record", &["first", "second"], &[&int_sort, &real_sort]) 1264 | .unwrap(); 1265 | let r1 = smt.declare_const("r1", &record_sort).unwrap(); 1266 | let x = smt.declare_const("x", &int_sort).unwrap(); 1267 | let r1_first = smt 1268 | .apply_fun_refs(&Op(Fn::RecordSelect("first")), &[&r1]) 1269 | .unwrap(); 1270 | 1271 | // r1.first != x 1272 | smt.assert( 1273 | &smt.apply_fun_refs(&Op(Fn::Distinct), &[&r1_first, &x]) 1274 | .unwrap(), 1275 | ) 1276 | .unwrap(); 1277 | let r1x = smt 1278 | .apply_fun_refs(&Op(Fn::RecordUpdate("first")), &[&r1, &x]) 1279 | .unwrap(); 1280 | 1281 | // check r1 != r1 WITH .first := x 1282 | smt.push(1).unwrap(); 1283 | smt.assert(&smt.apply_fun_refs(&Op(Fn::Eq), &[&r1, &r1x]).unwrap()) 1284 | .unwrap(); 1285 | assert_eq!(smt.check_sat(), CheckSatResult::Unsat); 1286 | smt.pop(1).unwrap(); 1287 | 1288 | // r2 = { first: y, second: 0 } 1289 | let y = smt.declare_const("y", &int_sort).unwrap(); 1290 | let zero = smt.const_from_int(0, &real_sort).unwrap(); 1291 | let r2 = smt.record_const_refs(&record_sort, &[&y, &zero]).unwrap(); 1292 | 1293 | // check r2 != r1 WITH .first := x is satisfiable 1294 | let r1x_neq_r2 = smt.apply_fun_refs(&Op(Fn::Distinct), &[&r1x, &r2]).unwrap(); 1295 | smt.assert(&r1x_neq_r2).unwrap(); 1296 | assert_eq!(smt.check_sat(), CheckSatResult::Sat); 1297 | 1298 | // r1.second = 0 1299 | let r1_second = smt 1300 | .apply_fun_refs(&Op(Fn::RecordSelect("second")), &[&r1]) 1301 | .unwrap(); 1302 | smt.assert( 1303 | &smt.apply_fun_refs(&Op(Fn::Eq), &[&r1_second, &zero]) 1304 | .unwrap(), 1305 | ) 1306 | .unwrap(); 1307 | 1308 | // check r2 != r1 WITH .first := x is still satisfiable 1309 | assert_eq!(smt.check_sat(), CheckSatResult::Sat); 1310 | 1311 | // x = y 1312 | smt.assert(&smt.apply_fun_refs(&Op(Fn::Eq), &[&x, &y]).unwrap()) 1313 | .unwrap(); 1314 | 1315 | // Now r2 != r1 WITH .first := x should be unsatisfiable 1316 | assert_eq!(smt.check_sat(), CheckSatResult::Unsat); 1317 | } 1318 | --------------------------------------------------------------------------------