diff --git a/.classpath b/.classpath index e37843a328..06e8af411e 100644 --- a/.classpath +++ b/.classpath @@ -33,6 +33,7 @@ SPDX-License-Identifier: Apache-2.0 + diff --git a/.idea/JavaSMT.iml b/.idea/JavaSMT.iml index fd67bcca0f..77b8b55e62 100644 --- a/.idea/JavaSMT.iml +++ b/.idea/JavaSMT.iml @@ -295,6 +295,15 @@ SPDX-License-Identifier: Apache-2.0 + + + + + + + + + diff --git a/README.md b/README.md index de7ce1fdf0..c44ad22a73 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ JavaSMT supports several SMT solvers (see [Getting Started](doc/Getting-started. | [SMTInterpol](https://ultimate.informatik.uni-freiburg.de/smtinterpol/) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Java-based SMT solver | | [Yices2](https://yices.csl.sri.com/) | :heavy_check_mark: | | [maybe](https://github.com/sosy-lab/java-smt/pull/215) | | [maybe](https://github.com/sosy-lab/java-smt/pull/400)⁴ | | | | [Z3](https://github.com/Z3Prover/z3) | :heavy_check_mark:³ | :heavy_check_mark:³ | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | mature and well-known solver | +| [Z3_WITH_INTERPOLATION](https://github.com/Z3Prover/z3) | :heavy_check_mark: | :heavy_check_mark: | | | | | an older version of Z3 that still provides interpolation support | We support a reasonable list of operating systems and versions. - Our main target is Linux (mainly Ubuntu or comparable Linux distributions). diff --git a/build.xml b/build.xml index c4cd550f97..d514b835e2 100644 --- a/build.xml +++ b/build.xml @@ -32,7 +32,8 @@ SPDX-License-Identifier: Apache-2.0 runtime-princess, runtime-smtinterpol, runtime-yices2, - runtime-z3 + runtime-z3, + runtime-z3-legacy "/> diff --git a/build/build-publish-solvers.xml b/build/build-publish-solvers.xml index 53d3ddb924..c609d3c45d 100644 --- a/build/build-publish-solvers.xml +++ b/build/build-publish-solvers.xml @@ -21,5 +21,6 @@ SPDX-License-Identifier: Apache-2.0 + diff --git a/build/build-publish-solvers/solver-z3-legacy.xml b/build/build-publish-solvers/solver-z3-legacy.xml new file mode 100644 index 0000000000..1e600f1894 --- /dev/null +++ b/build/build-publish-solvers/solver-z3-legacy.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + INFO + Please specify the path to Z3 sources directory with the flag '-Dz3.path=/path/to/z3'. + The path should contain directories like './build/', and should be absolute. + Please provide the patched legacy Z3 version in this path. + This build script will compile and package Z3 as required for JavaSMT. + Currently only Linux x64 and arm64 version for Z3 is available. + For example, the directory structure can look like this: + + z3/ // <-- parent directory and target of 'z3.path' + |-- build/ + |-- scripts/ + |-- src/ + |-- ... + + The patched version can be achieved by following the steps in '/lib/native/source/z3-4.5.0/README.md'. + + + + + + INFO + Please specify the version string used for Z3 legacy with the flag '-Dz3.customRev=SOME_VERSION'. + This is just '4.5.0' or some updated version. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lets copy the files for architecture x64 into main directory, for backwards compatibility. + Afterward, please execute the SVN command from above. + + + + + + + + diff --git a/doc/Developers-How-to-Release-into-Ivy.md b/doc/Developers-How-to-Release-into-Ivy.md index 0fb32eccad..c462daff9a 100644 --- a/doc/Developers-How-to-Release-into-Ivy.md +++ b/doc/Developers-How-to-Release-into-Ivy.md @@ -32,9 +32,13 @@ This is one of the most critical steps in JavaSMT development. By default, Java-based solvers are copied over from Maven Central. Please execute the following in the root directory of the [Ivy Repository][]: - -- ant install -Dorganisation=io.github.uuverifiers -Dmodule=princess_2.13 -Drevision=????-??-?? -- ant install -Dorganisation=de.uni-freiburg.informatik.ultimate -Dmodule=smtinterpol -Drevision=?.?-???-g??????? +```bash +ant install -Dorganisation=io.github.uuverifiers -Dmodule=princess_2.13 -Drevision=????-??-?? +``` +```bash +ant install -Dorganisation=de.uni-freiburg.informatik.ultimate -Dmodule=smtinterpol +-Drevision=?.?-???-g??????? +``` Potentially outdated: For manually uploading a Java-based solver to the [Ivy Repository][], @@ -51,7 +55,6 @@ please build from source only if necessary (e.g., in case of an important bugfix To publish Z3, download the **Linux**, **Windows**, and **OSX** binary (for both, x64 and ARM64 architecture) and the sources (for JavaDoc) for the [latest release](https://github.com/Z3Prover/z3/releases) and unzip them. For example, the directory structure can look like this: - ``` z3/ // <-- parent directory |-- z3-4.13.3-arm64-glibc-2.34/ // <-- unpacked release artifact @@ -71,48 +74,61 @@ For simple usage, we provide a Docker definition/environment under `/docker`, in In the unpacked sources directory, prepare Java sources via `python3 scripts/mk_make.py --java`. Then execute the following command in the JavaSMT directory, where `$Z3_DIR` is the path of the sources directory and `$Z3_VERSION` is the version number: -``` +```bash ant publish-z3 -Dz3.path=$Z3_DIR -Dz3.version=$Z3_VERSION ``` Example: -``` +```bash ant publish-z3 -Dz3.path=/workspace/solvers/z3/z3-z3-4.13.3 -Dz3.version=4.13.3 ``` Finally, follow the instructions shown in the message at the end. #### Optional (from source for Linux target with older GLIBC) + This step is for the following use case: Newer releases of Z3 depend on newer versions of GLIBC (>=v2.35), so we want to compile the Linux release on our own and then combine it with the provided libraries for Windows and macOS. We follow the steps from above, download and unpack the given zip archives for all platforms, except the Linux release (where the GLIBC is too new). For simple usage, we provide a Docker definition/environment under `/docker` (based on Ubuntu 18.04 with GLIBC 2.27), in which the following build command can be run in the unpacked source directory: -``` +```bash python3 scripts/mk_make.py --java && cd build && make -j 2 ``` Afterward, copy the native libraries for Linux (`libz3.so` and `libz3java.so`) from the directory `./build` into `./bin` (if needed, adjust the directory to match the x64 or arm64 path for Linux). Then perform as written above with adding the additional pre-compiled binaries for other operating systems, and publish the directory `./bin` with an ant command like the one from above: -``` +```bash ant publish-z3 -Dz3.path=$Z3_DIR -Dz3.version=$Z3_VERSION-glibc_2.27 ``` -#### Optional (from source for Linux target) (Info: this step is outdated and no longer used for releases of JavaSMT) -To publish Z3 from source, [download it](https://github.com/Z3Prover/z3) and build -it with the following command in its directory on a 64bit Ubuntu 16.04 system: -``` -./configure --staticlib --java --git-describe && cd build && make -j 2 -``` -(Note that additional binaries for other operating systems need to be provided, too. -This step is currently not fully tested from our side.) -Then execute the following command in the JavaSMT directory, where `$Z3_DIR` is the absolute path of the Z3 directory: +### Publishing Z3 v4.5.0 (aka LegacyZ3) + +The legacy version of Z3 (v4.5.0) is still integrated, +because it provides support for Craig interpolation in Z3. +This feature was removed in some later version of Z3. + +For publishing the legacy version of Z3 (v4.5.0), +please follow the instructions in the `lib/native/source/z3-4.5.0/README.md` file, +which explains how to patch the original Z3 v4.5.0 source code +so that the Java package names include the string `legacy`. +This is necessary to avoid conflicts with later versions of Z3. + +These steps can be performed in a Docker environment based on Ubuntu 18.04, +which is provided under `/docker` in the JavaSMT repository. +After building the patched Z3 v4.5.0 version, +you can publish it with the following command in the JavaSMT directory: +```bash +ant publish-z3-legacy -Dz3.path= -Dz3.customRev= ``` -ant publish-z3 -Dz3.path=$Z3_DIR/build + +Example: +```bash +ant publish-z3-legacy -Dz3.path=../solvers/z3/z3 -Dz3.customRev=4.5.0 ``` -Finally follow the instructions shown in the message at the end. + ### Publishing CVC5 @@ -125,8 +141,7 @@ Our build-script downloads daily build artifacts, extracts the native libraries and publishes them for JavaSMT. To publish a daily version of CVC5, execute the following command in the JavaSMT directory: - -``` +```bash ant publish-cvc5 -Dcvc5.version=$CVC5_VERSION ``` @@ -134,8 +149,7 @@ Where `CVC5_VERSION` must match one of the daily releases from their [GitHub](https://github.com/cvc5/cvc5/releases/tag/latest) website Example: - -``` +```bash ant publish-cvc5 -Dcvc5.version=2025-03-31-34518c3 ``` @@ -158,12 +172,12 @@ and include the following directories: If you want to build your own dependencies, please apply the following steps: Provide GMP from http://gmplib.org/ in version 6.3.0 (version 6.2.1 also works) and build GMP: - For linux-x64 in directory $GMP_DIR_LINUX_X64: - ``` + ```bash ./configure --enable-cxx --with-pic --disable-shared --enable-static --enable-fat make -j4 ``` - For linux-arm64 in directory $GMP_DIR_LINUX_ARM64: - ``` + ```bash ./configure --enable-cxx --with-pic --disable-shared --enable-static --enable-fat \ --host=aarch64-linux-gnu \ CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ LD=aarch64-linux-gnu-ld @@ -175,7 +189,7 @@ Download the zip archive from https://jdk.java.net/ and unpack it into $JDK_DIR_ To publish OpenSMT, checkout the [OpenSMT repository](https://github.com/usi-verification-and-security/opensmt). Then execute the following command in the JavaSMT directory: -``` +```bash ant publish-opensmt \ -Dopensmt.path=$OPENSMT_DIR \ -Dopensmt.customRev=$VERSION \ @@ -184,7 +198,7 @@ ant publish-opensmt \ -Djdk-linux-arm64.path=$JDK_DIR_LINUX_ARM64 ``` Example: -``` +```bash ant publish-opensmt \ -Dopensmt.path=/workspace/solvers/opensmt/opensmt \ -Dopensmt.customRev=2.9.0 \ @@ -215,11 +229,11 @@ in which the following command can be run. To publish Boolector, checkout the [Boolector repository](https://github.com/Boolector/boolector). Then execute the following command in the JavaSMT directory, where `$BTOR_DIR` is the path to the Boolector directory and `$BTOR_VERSION` is the version number: -``` +```bash CC=gcc-7 ant publish-boolector -Dboolector.path=$BTOR_DIR -Dboolector.customRev=$BTOR_VERSION ``` Example: -``` +```bash ant publish-boolector -Dboolector.path=../boolector -Dboolector.customRev=3.2.2 ``` Our build script will automatically append the git revision of Boolector, if available. @@ -238,7 +252,7 @@ in which the following command can be run. To publish Bitwuzla, checkout the [Bitwuzla repository](https://github.com/bitwuzla/bitwuzla). Then execute the following command in the JavaSMT directory: -``` +```bash ant publish-bitwuzla \ -Dbitwuzla.path=$BITWUZLA_DIR \ -Dbitwuzla.customRev=$VERSION \ @@ -247,7 +261,7 @@ ant publish-bitwuzla \ -Djdk-linux-arm64.path=$JDK_DIR_LINUX_ARM64 ``` Example: -``` +```bash ant publish-bitwuzla \ -Dbitwuzla.path=/workspace/solvers/bitwuzla/bitwuzla/ \ -Dbitwuzla.customRev=0.7.0-13 \ @@ -281,7 +295,7 @@ as described in the compilation scripts (see `lib/native/source/libmathsat5j/com Then execute the following command in the JavaSMT directory, where `$MATHSAT_PATH_` is the paths to the corresponding MathSAT root directory, and `$MATHSAT_VERSION` is the version number of MathSAT (all-in-one command, runtime is about 10s): -``` +```bash ant publish-mathsat \ -Dmathsat-linux-x64.path=$MATHSAT_PATH_LINUX_X64 \ -Dgmp-linux-x64.path=$GMP_PATH_LINUX_X64 \ @@ -294,7 +308,7 @@ ant publish-mathsat \ -Dmathsat.version=$MATHSAT_VERSION ``` Example: -``` +```bash ant publish-mathsat \ -Dmathsat-linux-x64.path=/workspace/solvers/mathsat/mathsat-5.6.12-linux-x86_64 \ -Dgmp-linux-x64.path=/workspace/solvers/gmp/gmp-6.3.0-linux-x64 \ @@ -310,11 +324,11 @@ Finally, follow the instructions shown in the message at the end. A similar procedure applies to [OptiMathSAT](http://optimathsat.disi.unitn.it/) solver, except that Windows is not yet supported and the publishing command is simpler: -``` +```bash ant publish-optimathsat -Dmathsat.path=$OPTIMATHSAT_PATH -Dgmp.path=$GMP_PATH -Dmathsat.version=$OPTIMATHSAT_VERSION ``` Example: -``` +```bash ant publish-optimathsat \ -Dmathsat.path=/workspace/solvers/optimathsat/optimathsat-1.7.3-linux-64-bit \ -Dgmp.path=/workspace/solvers/gmp/gmp-6.3.0-linux-x64 \ @@ -330,23 +344,23 @@ The Java components were splitt from the rest of JavaSMT because of the GPL. #### Publishing the solver binary for Yices2 Prepare gperf and gmp (required for our own static binary): -``` +```bash wget http://ftp.gnu.org/pub/gnu/gperf/gperf-3.1.tar.gz && tar -zxvf gperf-3.1.tar.gz && cd gperf-3.1 && ./configure --enable-cxx --with-pic --disable-shared --enable-fat && make wget https://gmplib.org/download/gmp/gmp-6.2.0.tar.xz && tar -xvf gmp-6.2.0.tar.xz && cd gmp-6.2.0 && ./configure --enable-cxx --with-pic --disable-shared --enable-fat && make ``` Download and build Yices2 from source: -``` +```bash git clone git@github.com:SRI-CSL/yices2.git && cd yices2 && autoconf && ./configure --with-pic-gmp=../gmp-6.2.0/.libs/libgmp.a && make ``` Get the version of Yices2: -``` +```bash git describe --tags ``` Publish the solver binary from within JavaSMT (adjust all paths to your system!): -``` +```bash ant publish-yices2 -Dyices2.path=../solvers/yices2 -Dgmp.path=../solvers/gmp-6.2.0 -Dgperf.path=../solvers/gperf-3.1 -Dyices2.version=2.6.2-89-g0f77dc4b ``` diff --git a/lib/ivy.xml b/lib/ivy.xml index 7bee886f95..4c18eebdbc 100644 --- a/lib/ivy.xml +++ b/lib/ivy.xml @@ -53,6 +53,9 @@ SPDX-License-Identifier: Apache-2.0 + + + @@ -68,7 +71,7 @@ SPDX-License-Identifier: Apache-2.0 + diff --git a/lib/native/arm64-linux/libz3javalegacy.so b/lib/native/arm64-linux/libz3javalegacy.so new file mode 120000 index 0000000000..440d016eaf --- /dev/null +++ b/lib/native/arm64-linux/libz3javalegacy.so @@ -0,0 +1 @@ +../../java/runtime-z3-legacy/arm64/libz3javalegacy.so \ No newline at end of file diff --git a/lib/native/arm64-linux/libz3legacy.so b/lib/native/arm64-linux/libz3legacy.so new file mode 120000 index 0000000000..ce474b6e5d --- /dev/null +++ b/lib/native/arm64-linux/libz3legacy.so @@ -0,0 +1 @@ +../../java/runtime-z3-legacy/arm64/libz3legacy.so \ No newline at end of file diff --git a/lib/native/source/z3-4.5.0/4.5.0-legacy.patch b/lib/native/source/z3-4.5.0/4.5.0-legacy.patch new file mode 100644 index 0000000000..31c1d62543 --- /dev/null +++ b/lib/native/source/z3-4.5.0/4.5.0-legacy.patch @@ -0,0 +1,1581 @@ +diff --git a/contrib/cmake/src/api/java/CMakeLists.txt b/contrib/cmake/src/api/java/CMakeLists.txt +index b34277266..4450ef120 100644 +--- a/contrib/cmake/src/api/java/CMakeLists.txt ++++ b/contrib/cmake/src/api/java/CMakeLists.txt +@@ -55,7 +55,7 @@ target_include_directories(z3java PRIVATE + # This prevents CMake from automatically defining ``z3java_EXPORTS`` + set_property(TARGET z3java PROPERTY DEFINE_SYMBOL "") + +-# Rule to generate the ``com.microsoft.z3.enumerations`` package ++# Rule to generate the ``com.microsoft.z3legacy.enumerations`` package + # FIXME: This list of files is fragile + set(Z3_JAVA_ENUMERATION_PACKAGE_FILES + Z3_ast_kind.java +@@ -200,7 +200,7 @@ add_custom_target(build_z3_java_bindings + z3JavaJar + ) + +-# Rule to build ``com.microsoft.z3.jar`` ++# Rule to build ``com.microsoft.z3legacy.jar`` + # TODO: Should we set ``CMAKE_JNI_TARGET`` to ``TRUE``? + add_jar(z3JavaJar + SOURCES ${Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH} +diff --git a/scripts/mk_project.py b/scripts/mk_project.py +index 986e92bde..a4ce6b91d 100644 +--- a/scripts/mk_project.py ++++ b/scripts/mk_project.py +@@ -85,12 +85,12 @@ def init_project_def(): + add_exe('test', ['api', 'fuzzing', 'simplex'], exe_name='test-z3', install=False) + _libz3Component = add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll', + reexports=['api'], +- dll_name='libz3', ++ dll_name='libz3legacy', + static=build_static_lib(), + export_files=API_files, + staging_link='python') + add_dot_net_dll('dotnet', ['api_dll'], 'api/dotnet', dll_name='Microsoft.Z3', assembly_info_dir='Properties', default_key_file='src/api/dotnet/Microsoft.Z3.snk') +- add_java_dll('java', ['api_dll'], 'api/java', dll_name='libz3java', package_name="com.microsoft.z3", manifest_file='manifest') ++ add_java_dll('java', ['api_dll'], 'api/java', dll_name='libz3javalegacy', package_name="com.microsoft.z3legacy", manifest_file='manifest') + add_ml_lib('ml', ['api_dll'], 'api/ml', lib_name='libz3ml') + add_hlib('cpp', 'api/c++', includes2install=['z3++.h']) + set_z3py_dir('api/python') +diff --git a/scripts/mk_util.py b/scripts/mk_util.py +index cf06a10a7..f26c64a7f 100644 +--- a/scripts/mk_util.py ++++ b/scripts/mk_util.py +@@ -1768,7 +1768,7 @@ class JavaDLLComponent(Component): + if is_java_enabled(): + mk_dir(os.path.join(BUILD_DIR, 'api', 'java', 'classes')) + dllfile = '%s$(SO_EXT)' % self.dll_name +- out.write('libz3java$(SO_EXT): libz3$(SO_EXT) %s\n' % os.path.join(self.to_src_dir, 'Native.cpp')) ++ out.write('libz3javalegacy$(SO_EXT): libz3legacy$(SO_EXT) %s\n' % os.path.join(self.to_src_dir, 'Native.cpp')) + t = '\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)api/java/Native$(OBJ_EXT) -I"%s" -I"%s/PLATFORM" -I%s %s/Native.cpp\n' % (JNI_HOME, JNI_HOME, get_component('api').to_src_dir, self.to_src_dir) + if IS_OSX: + t = t.replace('PLATFORM', 'darwin') +@@ -1784,12 +1784,12 @@ class JavaDLLComponent(Component): + t = t.replace('PLATFORM', 'win32') + out.write(t) + if IS_WINDOWS: # On Windows, CL creates a .lib file to link against. +- out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(LIB_EXT)\n' % ++ out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3javalegacy$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3legacy$(LIB_EXT)\n' % + os.path.join('api', 'java', 'Native')) + else: +- out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(SO_EXT)\n' % ++ out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3javalegacy$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3legacy$(SO_EXT)\n' % + os.path.join('api', 'java', 'Native')) +- out.write('%s.jar: libz3java$(SO_EXT) ' % self.package_name) ++ out.write('%s.jar: libz3javalegacy$(SO_EXT) ' % self.package_name) + deps = '' + for jfile in get_java_files(self.src_dir): + deps += ('%s ' % os.path.join(self.to_src_dir, jfile)) +diff --git a/scripts/update_api.py b/scripts/update_api.py +index 04378b371..49afbfdc1 100755 +--- a/scripts/update_api.py ++++ b/scripts/update_api.py +@@ -365,7 +365,7 @@ def mk_dotnet(dotnet): + dotnet.write(' public delegate void Z3_error_handler(Z3_context c, Z3_error_code e);\n\n') + dotnet.write(' public class LIB\n') + dotnet.write(' {\n') +- dotnet.write(' const string Z3_DLL_NAME = \"libz3.dll\";\n' ++ dotnet.write(' const string Z3_DLL_NAME = \"libz3legacy.dll\";\n' + ' \n') + dotnet.write(' [DllImport(Z3_DLL_NAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n') + dotnet.write(' public extern static void Z3_set_error_handler(Z3_context a0, Z3_error_handler a1);\n\n') +@@ -494,8 +494,13 @@ def mk_java(java_dir, package_name): + java_native.write(' public static native void setInternalErrorHandler(long ctx);\n\n') + + java_native.write(' static {\n') +- java_native.write(' try { System.loadLibrary("z3java"); }\n') +- java_native.write(' catch (UnsatisfiedLinkError ex) { System.loadLibrary("libz3java"); }\n') ++ java_native.write(' if (!Boolean.parseBoolean(System.getProperty("z3.skipLibraryLoad"))) {\n') ++ java_native.write(' try {\n') ++ java_native.write(' System.loadLibrary("z3legacyjava");\n') ++ java_native.write(' } catch (UnsatisfiedLinkError ex) {\n') ++ java_native.write(' System.loadLibrary("libz3legacyjava");\n') ++ java_native.write(' }\n') ++ java_native.write(' }\n') + java_native.write(' }\n') + + java_native.write('\n') +@@ -1644,9 +1649,9 @@ def init(PATH): + if PATH: + PATH = os.path.realpath(PATH) + if os.path.isdir(PATH): +- PATH = os.path.join(PATH, 'libz3.%s' % _ext) ++ PATH = os.path.join(PATH, 'libz3legacy.%s' % _ext) + else: +- PATH = 'libz3.%s' % _ext ++ PATH = 'libz3legacy.%s' % _ext + + global _lib + _lib = ctypes.CDLL(PATH) +diff --git a/src/api/java/AST.java b/src/api/java/AST.java +index e1cde837f..d242bdcab 100644 +--- a/src/api/java/AST.java ++++ b/src/api/java/AST.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_ast_kind; ++import com.microsoft.z3legacy.enumerations.Z3_ast_kind; + + /** + * The abstract syntax tree (AST) class. +diff --git a/src/api/java/ASTDecRefQueue.java b/src/api/java/ASTDecRefQueue.java +index b0a6fa217..d7e8bdf1d 100644 +--- a/src/api/java/ASTDecRefQueue.java ++++ b/src/api/java/ASTDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class ASTDecRefQueue extends IDecRefQueue + { +diff --git a/src/api/java/ASTMap.java b/src/api/java/ASTMap.java +index 916811cec..6e5494329 100644 +--- a/src/api/java/ASTMap.java ++++ b/src/api/java/ASTMap.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Map from AST to AST +diff --git a/src/api/java/ASTVector.java b/src/api/java/ASTVector.java +index 4d9ab291a..cec7e4b04 100644 +--- a/src/api/java/ASTVector.java ++++ b/src/api/java/ASTVector.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Vectors of ASTs. +diff --git a/src/api/java/AlgebraicNum.java b/src/api/java/AlgebraicNum.java +index 6725d3937..731f58aa8 100644 +--- a/src/api/java/AlgebraicNum.java ++++ b/src/api/java/AlgebraicNum.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Algebraic numbers +diff --git a/src/api/java/ApplyResult.java b/src/api/java/ApplyResult.java +index 6fafbd888..cb650d028 100644 +--- a/src/api/java/ApplyResult.java ++++ b/src/api/java/ApplyResult.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * ApplyResult objects represent the result of an application of a tactic to a +diff --git a/src/api/java/ApplyResultDecRefQueue.java b/src/api/java/ApplyResultDecRefQueue.java +index e1a660781..f016ee1b3 100644 +--- a/src/api/java/ApplyResultDecRefQueue.java ++++ b/src/api/java/ApplyResultDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class ApplyResultDecRefQueue extends IDecRefQueue + { +diff --git a/src/api/java/ArithExpr.java b/src/api/java/ArithExpr.java +index d92d8523b..f20476847 100644 +--- a/src/api/java/ArithExpr.java ++++ b/src/api/java/ArithExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Arithmetic expressions (int/real) +diff --git a/src/api/java/ArithSort.java b/src/api/java/ArithSort.java +index 5f4d4c1eb..26acf9263 100644 +--- a/src/api/java/ArithSort.java ++++ b/src/api/java/ArithSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * An arithmetic sort, i.e., Int or Real. +diff --git a/src/api/java/ArrayExpr.java b/src/api/java/ArrayExpr.java +index b8318b648..3c8ae053b 100644 +--- a/src/api/java/ArrayExpr.java ++++ b/src/api/java/ArrayExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + + /** +diff --git a/src/api/java/ArraySort.java b/src/api/java/ArraySort.java +index 1574823d1..44d55ef2f 100644 +--- a/src/api/java/ArraySort.java ++++ b/src/api/java/ArraySort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Array sorts. +diff --git a/src/api/java/AstMapDecRefQueue.java b/src/api/java/AstMapDecRefQueue.java +index 6c96970b7..98fcd42ab 100644 +--- a/src/api/java/AstMapDecRefQueue.java ++++ b/src/api/java/AstMapDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class ASTMapDecRefQueue extends IDecRefQueue { + public ASTMapDecRefQueue() +diff --git a/src/api/java/AstVectorDecRefQueue.java b/src/api/java/AstVectorDecRefQueue.java +index e7ce3e33e..20eca6891 100644 +--- a/src/api/java/AstVectorDecRefQueue.java ++++ b/src/api/java/AstVectorDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class ASTVectorDecRefQueue extends IDecRefQueue { + public ASTVectorDecRefQueue() +diff --git a/src/api/java/BitVecExpr.java b/src/api/java/BitVecExpr.java +index 175da9d66..57bc5261e 100644 +--- a/src/api/java/BitVecExpr.java ++++ b/src/api/java/BitVecExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Bit-vector expressions +diff --git a/src/api/java/BitVecNum.java b/src/api/java/BitVecNum.java +index d6c176855..b85d7a230 100644 +--- a/src/api/java/BitVecNum.java ++++ b/src/api/java/BitVecNum.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + import java.math.BigInteger; + +diff --git a/src/api/java/BitVecSort.java b/src/api/java/BitVecSort.java +index 5c8e9a1e5..ae025c1d1 100644 +--- a/src/api/java/BitVecSort.java ++++ b/src/api/java/BitVecSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Bit-vector sorts. +diff --git a/src/api/java/BoolExpr.java b/src/api/java/BoolExpr.java +index dc75b2e7c..5256fc8ad 100644 +--- a/src/api/java/BoolExpr.java ++++ b/src/api/java/BoolExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Boolean expressions +diff --git a/src/api/java/BoolSort.java b/src/api/java/BoolSort.java +index 66a4ddaa9..4c2ce611c 100644 +--- a/src/api/java/BoolSort.java ++++ b/src/api/java/BoolSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * A Boolean sort. +diff --git a/src/api/java/Constructor.java b/src/api/java/Constructor.java +index 87ab86c3f..6a948770d 100644 +--- a/src/api/java/Constructor.java ++++ b/src/api/java/Constructor.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Constructors are used for datatype sorts. +diff --git a/src/api/java/ConstructorDecRefQueue.java b/src/api/java/ConstructorDecRefQueue.java +index 5003dde5f..4b2b81740 100644 +--- a/src/api/java/ConstructorDecRefQueue.java ++++ b/src/api/java/ConstructorDecRefQueue.java +@@ -1,4 +1,4 @@ +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + public class ConstructorDecRefQueue extends IDecRefQueue { + public ConstructorDecRefQueue() { +diff --git a/src/api/java/ConstructorList.java b/src/api/java/ConstructorList.java +index c79e08d9e..1a14466d0 100644 +--- a/src/api/java/ConstructorList.java ++++ b/src/api/java/ConstructorList.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Lists of constructors +diff --git a/src/api/java/ConstructorListDecRefQueue.java b/src/api/java/ConstructorListDecRefQueue.java +index 1a2460731..309d201eb 100644 +--- a/src/api/java/ConstructorListDecRefQueue.java ++++ b/src/api/java/ConstructorListDecRefQueue.java +@@ -1,4 +1,4 @@ +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + public class ConstructorListDecRefQueue extends IDecRefQueue { + public ConstructorListDecRefQueue() { +diff --git a/src/api/java/Context.java b/src/api/java/Context.java +index b7656c5da..1e26b27be 100644 +--- a/src/api/java/Context.java ++++ b/src/api/java/Context.java +@@ -15,11 +15,11 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import static com.microsoft.z3.Constructor.of; ++import static com.microsoft.z3legacy.Constructor.of; + +-import com.microsoft.z3.enumerations.Z3_ast_print_mode; ++import com.microsoft.z3legacy.enumerations.Z3_ast_print_mode; + + import java.util.Map; + +diff --git a/src/api/java/DatatypeExpr.java b/src/api/java/DatatypeExpr.java +index 9abe230f6..b6deb1d34 100644 +--- a/src/api/java/DatatypeExpr.java ++++ b/src/api/java/DatatypeExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Datatype expressions +diff --git a/src/api/java/DatatypeSort.java b/src/api/java/DatatypeSort.java +index 644d434d3..669a0e769 100644 +--- a/src/api/java/DatatypeSort.java ++++ b/src/api/java/DatatypeSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Datatype sorts. +diff --git a/src/api/java/EnumSort.java b/src/api/java/EnumSort.java +index ce2f8d578..8c05378a0 100644 +--- a/src/api/java/EnumSort.java ++++ b/src/api/java/EnumSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Enumeration sorts. +diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java +index ea3fd2147..0cedc549a 100644 +--- a/src/api/java/Expr.java ++++ b/src/api/java/Expr.java +@@ -15,12 +15,12 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_ast_kind; +-import com.microsoft.z3.enumerations.Z3_decl_kind; +-import com.microsoft.z3.enumerations.Z3_lbool; +-import com.microsoft.z3.enumerations.Z3_sort_kind; ++import com.microsoft.z3legacy.enumerations.Z3_ast_kind; ++import com.microsoft.z3legacy.enumerations.Z3_decl_kind; ++import com.microsoft.z3legacy.enumerations.Z3_lbool; ++import com.microsoft.z3legacy.enumerations.Z3_sort_kind; + + /* using System; */ + +diff --git a/src/api/java/FPExpr.java b/src/api/java/FPExpr.java +index 8218f0f79..1efc82c76 100644 +--- a/src/api/java/FPExpr.java ++++ b/src/api/java/FPExpr.java +@@ -14,7 +14,7 @@ Author: + Notes: + + --*/ +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * FloatingPoint Expressions +diff --git a/src/api/java/FPNum.java b/src/api/java/FPNum.java +index 402d25ebe..29c188f31 100644 +--- a/src/api/java/FPNum.java ++++ b/src/api/java/FPNum.java +@@ -14,7 +14,7 @@ Author: + Notes: + + --*/ +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * FloatingPoint Numerals +diff --git a/src/api/java/FPRMExpr.java b/src/api/java/FPRMExpr.java +index 08db43b8a..a10c07ff1 100644 +--- a/src/api/java/FPRMExpr.java ++++ b/src/api/java/FPRMExpr.java +@@ -14,7 +14,7 @@ Author: + Notes: + + --*/ +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * FloatingPoint RoundingMode Expressions +diff --git a/src/api/java/FPRMNum.java b/src/api/java/FPRMNum.java +index ef8897965..a0784e78a 100644 +--- a/src/api/java/FPRMNum.java ++++ b/src/api/java/FPRMNum.java +@@ -14,9 +14,9 @@ Author: + Notes: + + --*/ +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_decl_kind; ++import com.microsoft.z3legacy.enumerations.Z3_decl_kind; + + /** + * FloatingPoint RoundingMode Numerals +diff --git a/src/api/java/FPRMSort.java b/src/api/java/FPRMSort.java +index c33aa074e..f8f038bf6 100644 +--- a/src/api/java/FPRMSort.java ++++ b/src/api/java/FPRMSort.java +@@ -14,7 +14,7 @@ Author: + Notes: + + --*/ +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * The FloatingPoint RoundingMode sort +diff --git a/src/api/java/FPSort.java b/src/api/java/FPSort.java +index 59313fe27..0a1858ed2 100644 +--- a/src/api/java/FPSort.java ++++ b/src/api/java/FPSort.java +@@ -14,7 +14,7 @@ Author: + Notes: + + --*/ +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * A FloatingPoint sort +diff --git a/src/api/java/FiniteDomainExpr.java b/src/api/java/FiniteDomainExpr.java +index f7d930758..45d1d80b6 100644 +--- a/src/api/java/FiniteDomainExpr.java ++++ b/src/api/java/FiniteDomainExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Finite-domain expressions +diff --git a/src/api/java/FiniteDomainNum.java b/src/api/java/FiniteDomainNum.java +index 68467e408..2e7e2f5e4 100644 +--- a/src/api/java/FiniteDomainNum.java ++++ b/src/api/java/FiniteDomainNum.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + import java.math.BigInteger; + +diff --git a/src/api/java/FiniteDomainSort.java b/src/api/java/FiniteDomainSort.java +index dcb52cd22..2a41f688b 100644 +--- a/src/api/java/FiniteDomainSort.java ++++ b/src/api/java/FiniteDomainSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Finite domain sorts. +diff --git a/src/api/java/Fixedpoint.java b/src/api/java/Fixedpoint.java +index ad6d5a658..84d56bedc 100644 +--- a/src/api/java/Fixedpoint.java ++++ b/src/api/java/Fixedpoint.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_lbool; ++import com.microsoft.z3legacy.enumerations.Z3_lbool; + + /** + * Object for managing fixedpoints +diff --git a/src/api/java/FixedpointDecRefQueue.java b/src/api/java/FixedpointDecRefQueue.java +index 69ed82092..75bcb935d 100644 +--- a/src/api/java/FixedpointDecRefQueue.java ++++ b/src/api/java/FixedpointDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class FixedpointDecRefQueue extends IDecRefQueue { + public FixedpointDecRefQueue() +diff --git a/src/api/java/FuncDecl.java b/src/api/java/FuncDecl.java +index 273e853c0..4e712a231 100644 +--- a/src/api/java/FuncDecl.java ++++ b/src/api/java/FuncDecl.java +@@ -15,11 +15,11 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_ast_kind; +-import com.microsoft.z3.enumerations.Z3_decl_kind; +-import com.microsoft.z3.enumerations.Z3_parameter_kind; ++import com.microsoft.z3legacy.enumerations.Z3_ast_kind; ++import com.microsoft.z3legacy.enumerations.Z3_decl_kind; ++import com.microsoft.z3legacy.enumerations.Z3_parameter_kind; + + /** + * Function declarations. +diff --git a/src/api/java/FuncInterp.java b/src/api/java/FuncInterp.java +index b5873d98e..fe1ef2415 100644 +--- a/src/api/java/FuncInterp.java ++++ b/src/api/java/FuncInterp.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * A function interpretation is represented as a finite map and an 'else' value. +diff --git a/src/api/java/FuncInterpDecRefQueue.java b/src/api/java/FuncInterpDecRefQueue.java +index d8715bd0e..1aa389096 100644 +--- a/src/api/java/FuncInterpDecRefQueue.java ++++ b/src/api/java/FuncInterpDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class FuncInterpDecRefQueue extends IDecRefQueue + { +diff --git a/src/api/java/FuncInterpEntryDecRefQueue.java b/src/api/java/FuncInterpEntryDecRefQueue.java +index a4d8a0690..fed12e6e9 100644 +--- a/src/api/java/FuncInterpEntryDecRefQueue.java ++++ b/src/api/java/FuncInterpEntryDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class FuncInterpEntryDecRefQueue extends IDecRefQueue { + public FuncInterpEntryDecRefQueue() +diff --git a/src/api/java/Global.java b/src/api/java/Global.java +index fb2bdf916..ace691b3c 100644 +--- a/src/api/java/Global.java ++++ b/src/api/java/Global.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Global functions for Z3. +diff --git a/src/api/java/Goal.java b/src/api/java/Goal.java +index 25b1fe511..236304836 100644 +--- a/src/api/java/Goal.java ++++ b/src/api/java/Goal.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_goal_prec; ++import com.microsoft.z3legacy.enumerations.Z3_goal_prec; + + /** + * A goal (aka problem). A goal is essentially a set of formulas, that can be +diff --git a/src/api/java/GoalDecRefQueue.java b/src/api/java/GoalDecRefQueue.java +index 90bad1fb1..685174610 100644 +--- a/src/api/java/GoalDecRefQueue.java ++++ b/src/api/java/GoalDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class GoalDecRefQueue extends IDecRefQueue { + public GoalDecRefQueue() +diff --git a/src/api/java/IDecRefQueue.java b/src/api/java/IDecRefQueue.java +index 4b515a3b6..5ac21c1de 100644 +--- a/src/api/java/IDecRefQueue.java ++++ b/src/api/java/IDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + import java.lang.ref.PhantomReference; + import java.lang.ref.Reference; +diff --git a/src/api/java/IntExpr.java b/src/api/java/IntExpr.java +index 56ba9ccdf..f76049eb3 100644 +--- a/src/api/java/IntExpr.java ++++ b/src/api/java/IntExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Int expressions +diff --git a/src/api/java/IntNum.java b/src/api/java/IntNum.java +index d3a5b456f..ed5de55c4 100644 +--- a/src/api/java/IntNum.java ++++ b/src/api/java/IntNum.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + import java.math.BigInteger; + +diff --git a/src/api/java/IntSort.java b/src/api/java/IntSort.java +index 59526ed0b..ba92f91f4 100644 +--- a/src/api/java/IntSort.java ++++ b/src/api/java/IntSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * An Integer sort +diff --git a/src/api/java/IntSymbol.java b/src/api/java/IntSymbol.java +index fab242d28..817655e9f 100644 +--- a/src/api/java/IntSymbol.java ++++ b/src/api/java/IntSymbol.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_symbol_kind; ++import com.microsoft.z3legacy.enumerations.Z3_symbol_kind; + + /** + * Numbered symbols +diff --git a/src/api/java/InterpolationContext.java b/src/api/java/InterpolationContext.java +index 99a63821f..9eb8d0253 100644 +--- a/src/api/java/InterpolationContext.java ++++ b/src/api/java/InterpolationContext.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_lbool; ++import com.microsoft.z3legacy.enumerations.Z3_lbool; + + import java.util.Map; + +diff --git a/src/api/java/ListSort.java b/src/api/java/ListSort.java +index 0ff2c36cf..f6fff8533 100644 +--- a/src/api/java/ListSort.java ++++ b/src/api/java/ListSort.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.Native.LongPtr; ++import com.microsoft.z3legacy.Native.LongPtr; + + /** + * List sorts. +diff --git a/src/api/java/Log.java b/src/api/java/Log.java +index 7dc9a1ef1..a02f74fd8 100644 +--- a/src/api/java/Log.java ++++ b/src/api/java/Log.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Interaction logging for Z3. +diff --git a/src/api/java/Model.java b/src/api/java/Model.java +index 60abb001d..898031dbd 100644 +--- a/src/api/java/Model.java ++++ b/src/api/java/Model.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_sort_kind; ++import com.microsoft.z3legacy.enumerations.Z3_sort_kind; + + /** + * A Model contains interpretations (assignments) of constants and functions. +diff --git a/src/api/java/ModelDecRefQueue.java b/src/api/java/ModelDecRefQueue.java +index f1b7c3fdd..a8fb5380e 100644 +--- a/src/api/java/ModelDecRefQueue.java ++++ b/src/api/java/ModelDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class ModelDecRefQueue extends IDecRefQueue { + public ModelDecRefQueue() +diff --git a/src/api/java/Optimize.java b/src/api/java/Optimize.java +index ea100d1ca..1d7d8e30b 100644 +--- a/src/api/java/Optimize.java ++++ b/src/api/java/Optimize.java +@@ -17,9 +17,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_lbool; ++import com.microsoft.z3legacy.enumerations.Z3_lbool; + + + /** +diff --git a/src/api/java/OptimizeDecRefQueue.java b/src/api/java/OptimizeDecRefQueue.java +index 0acf20068..5db342065 100644 +--- a/src/api/java/OptimizeDecRefQueue.java ++++ b/src/api/java/OptimizeDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class OptimizeDecRefQueue extends IDecRefQueue { + public OptimizeDecRefQueue() +diff --git a/src/api/java/ParamDescrs.java b/src/api/java/ParamDescrs.java +index 0008515e3..c20832d51 100644 +--- a/src/api/java/ParamDescrs.java ++++ b/src/api/java/ParamDescrs.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_param_kind; ++import com.microsoft.z3legacy.enumerations.Z3_param_kind; + + /** + * A ParamDescrs describes a set of parameters. +diff --git a/src/api/java/ParamDescrsDecRefQueue.java b/src/api/java/ParamDescrsDecRefQueue.java +index ee3257db9..8b3807949 100644 +--- a/src/api/java/ParamDescrsDecRefQueue.java ++++ b/src/api/java/ParamDescrsDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class ParamDescrsDecRefQueue extends IDecRefQueue { + public ParamDescrsDecRefQueue() +diff --git a/src/api/java/Params.java b/src/api/java/Params.java +index a76dd3cab..0606053e1 100644 +--- a/src/api/java/Params.java ++++ b/src/api/java/Params.java +@@ -16,7 +16,7 @@ Notes: + **/ + + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * A ParameterSet represents a configuration in the form of Symbol/value pairs. +diff --git a/src/api/java/ParamsDecRefQueue.java b/src/api/java/ParamsDecRefQueue.java +index 349713f67..80fb05b5b 100644 +--- a/src/api/java/ParamsDecRefQueue.java ++++ b/src/api/java/ParamsDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class ParamsDecRefQueue extends IDecRefQueue { + public ParamsDecRefQueue() +diff --git a/src/api/java/Pattern.java b/src/api/java/Pattern.java +index 852ffcd0f..96e2907d4 100644 +--- a/src/api/java/Pattern.java ++++ b/src/api/java/Pattern.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Patterns comprise a list of terms. The list should be non-empty. If the list +diff --git a/src/api/java/Probe.java b/src/api/java/Probe.java +index a36f3b64b..98ac49c07 100644 +--- a/src/api/java/Probe.java ++++ b/src/api/java/Probe.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Probes are used to inspect a goal (aka problem) and collect information that +diff --git a/src/api/java/ProbeDecRefQueue.java b/src/api/java/ProbeDecRefQueue.java +index b25446c0c..214214d96 100644 +--- a/src/api/java/ProbeDecRefQueue.java ++++ b/src/api/java/ProbeDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class ProbeDecRefQueue extends IDecRefQueue + { +diff --git a/src/api/java/Quantifier.java b/src/api/java/Quantifier.java +index bc2537107..f718cde3d 100644 +--- a/src/api/java/Quantifier.java ++++ b/src/api/java/Quantifier.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_ast_kind; ++import com.microsoft.z3legacy.enumerations.Z3_ast_kind; + + /** + * Quantifier expressions. +diff --git a/src/api/java/README b/src/api/java/README +index c9dbcf8f7..c08f6a42c 100644 +--- a/src/api/java/README ++++ b/src/api/java/README +@@ -3,4 +3,4 @@ Java bindings + + The Java bindings will be included in the Z3 build if it is configured with + the option --java to python scripts/mk_make.py. This will produce the +-com.microsoft.z3.jar package in the build directory. ++com.microsoft.z3legacy.jar package in the build directory. +diff --git a/src/api/java/RatNum.java b/src/api/java/RatNum.java +index 2bf1b28dd..4b1450145 100644 +--- a/src/api/java/RatNum.java ++++ b/src/api/java/RatNum.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + import java.math.BigInteger; + +diff --git a/src/api/java/ReExpr.java b/src/api/java/ReExpr.java +index 60dc2bf96..722fe0df7 100644 +--- a/src/api/java/ReExpr.java ++++ b/src/api/java/ReExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Re expressions +diff --git a/src/api/java/ReSort.java b/src/api/java/ReSort.java +index 74e7c5c5f..fc204372e 100644 +--- a/src/api/java/ReSort.java ++++ b/src/api/java/ReSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * A Regular expression sort +diff --git a/src/api/java/RealExpr.java b/src/api/java/RealExpr.java +index c977e2ac0..7c3f9b57b 100644 +--- a/src/api/java/RealExpr.java ++++ b/src/api/java/RealExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Real expressions +diff --git a/src/api/java/RealSort.java b/src/api/java/RealSort.java +index 0f6333314..45184480b 100644 +--- a/src/api/java/RealSort.java ++++ b/src/api/java/RealSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * A real sort +diff --git a/src/api/java/RelationSort.java b/src/api/java/RelationSort.java +index e996479ab..004672251 100644 +--- a/src/api/java/RelationSort.java ++++ b/src/api/java/RelationSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Relation sorts. +diff --git a/src/api/java/SeqExpr.java b/src/api/java/SeqExpr.java +index 47976dd5e..0b20a5f3b 100644 +--- a/src/api/java/SeqExpr.java ++++ b/src/api/java/SeqExpr.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Seq expressions +diff --git a/src/api/java/SeqSort.java b/src/api/java/SeqSort.java +index 5c7a549c9..021d2e3cd 100644 +--- a/src/api/java/SeqSort.java ++++ b/src/api/java/SeqSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * A Sequence sort +diff --git a/src/api/java/SetSort.java b/src/api/java/SetSort.java +index 2aa821250..693cb8b7f 100644 +--- a/src/api/java/SetSort.java ++++ b/src/api/java/SetSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Set sorts. +diff --git a/src/api/java/Solver.java b/src/api/java/Solver.java +index a98fcbf94..e428835ce 100644 +--- a/src/api/java/Solver.java ++++ b/src/api/java/Solver.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_lbool; ++import com.microsoft.z3legacy.enumerations.Z3_lbool; + + /** + * Solvers. +diff --git a/src/api/java/SolverDecRefQueue.java b/src/api/java/SolverDecRefQueue.java +index efa15d939..e84710117 100644 +--- a/src/api/java/SolverDecRefQueue.java ++++ b/src/api/java/SolverDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class SolverDecRefQueue extends IDecRefQueue { + public SolverDecRefQueue() { super(); } +diff --git a/src/api/java/Sort.java b/src/api/java/Sort.java +index 0763a69a3..d2a7f46de 100644 +--- a/src/api/java/Sort.java ++++ b/src/api/java/Sort.java +@@ -15,10 +15,10 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_ast_kind; +-import com.microsoft.z3.enumerations.Z3_sort_kind; ++import com.microsoft.z3legacy.enumerations.Z3_ast_kind; ++import com.microsoft.z3legacy.enumerations.Z3_sort_kind; + + /** + * The Sort class implements type information for ASTs. +diff --git a/src/api/java/Statistics.java b/src/api/java/Statistics.java +index 356cbeadb..995d3ab0f 100644 +--- a/src/api/java/Statistics.java ++++ b/src/api/java/Statistics.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Objects of this class track statistical information about solvers. +diff --git a/src/api/java/StatisticsDecRefQueue.java b/src/api/java/StatisticsDecRefQueue.java +index ed698e4ca..1f49cda3d 100644 +--- a/src/api/java/StatisticsDecRefQueue.java ++++ b/src/api/java/StatisticsDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class StatisticsDecRefQueue extends IDecRefQueue { + public StatisticsDecRefQueue() +diff --git a/src/api/java/Status.java b/src/api/java/Status.java +index c1f534d6a..40f5d6b5b 100644 +--- a/src/api/java/Status.java ++++ b/src/api/java/Status.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Status values. +diff --git a/src/api/java/StringSymbol.java b/src/api/java/StringSymbol.java +index 576737ea7..ae7bdc902 100644 +--- a/src/api/java/StringSymbol.java ++++ b/src/api/java/StringSymbol.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_symbol_kind; ++import com.microsoft.z3legacy.enumerations.Z3_symbol_kind; + + /** + * Named symbols +diff --git a/src/api/java/Symbol.java b/src/api/java/Symbol.java +index 139894be1..0ebd060b0 100644 +--- a/src/api/java/Symbol.java ++++ b/src/api/java/Symbol.java +@@ -15,9 +15,9 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + +-import com.microsoft.z3.enumerations.Z3_symbol_kind; ++import com.microsoft.z3legacy.enumerations.Z3_symbol_kind; + + /** + * Symbols are used to name several term and type constructors. +diff --git a/src/api/java/Tactic.java b/src/api/java/Tactic.java +index 11d02ca73..ee5396de1 100644 +--- a/src/api/java/Tactic.java ++++ b/src/api/java/Tactic.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Tactics are the basic building block for creating custom solvers for specific +diff --git a/src/api/java/TacticDecRefQueue.java b/src/api/java/TacticDecRefQueue.java +index 8f151f25c..647de34e5 100644 +--- a/src/api/java/TacticDecRefQueue.java ++++ b/src/api/java/TacticDecRefQueue.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + class TacticDecRefQueue extends IDecRefQueue { + public TacticDecRefQueue() +diff --git a/src/api/java/TupleSort.java b/src/api/java/TupleSort.java +index ede20d260..571981472 100644 +--- a/src/api/java/TupleSort.java ++++ b/src/api/java/TupleSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Tuple sorts. +diff --git a/src/api/java/UninterpretedSort.java b/src/api/java/UninterpretedSort.java +index 7f7427493..b32cf7e2f 100644 +--- a/src/api/java/UninterpretedSort.java ++++ b/src/api/java/UninterpretedSort.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Uninterpreted Sorts +diff --git a/src/api/java/Version.java b/src/api/java/Version.java +index 0579e2b05..f125ec1c2 100644 +--- a/src/api/java/Version.java ++++ b/src/api/java/Version.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Version information. +diff --git a/src/api/java/Z3Exception.java b/src/api/java/Z3Exception.java +index a27e40048..5da6ce9bc 100644 +--- a/src/api/java/Z3Exception.java ++++ b/src/api/java/Z3Exception.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + + /** +diff --git a/src/api/java/Z3Object.java b/src/api/java/Z3Object.java +index 7c5f606d9..349dd4305 100644 +--- a/src/api/java/Z3Object.java ++++ b/src/api/java/Z3Object.java +@@ -15,7 +15,7 @@ Notes: + + **/ + +-package com.microsoft.z3; ++package com.microsoft.z3legacy; + + /** + * Internal base class for interfacing with native Z3 objects. Should not be +diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp +index bd8d4958d..5ddae2b21 100644 +--- a/src/util/hwf.cpp ++++ b/src/util/hwf.cpp +@@ -21,20 +21,22 @@ Revision History: + #include + + #ifdef _WINDOWS ++#if defined(_MSC_VER) + #pragma float_control( except, on ) // exception semantics; this does _not_ mean that exceptions are enabled (we want them off!) + #pragma float_control( precise, on ) // precise semantics (no guessing!) + #pragma fp_contract(off) // contractions off (`contraction' means x*y+z is turned into a fused-mul-add). + #pragma fenv_access(on) // fpu environment sensitivity (needed to be allowed to make FPU mode changes). ++#endif + #else + #include + #endif + +-#if defined(__x86_64__) || defined(_M_X64) || \ ++#if defined(__x86_64__) || defined(_M_X64) || \ + defined(__i386) || defined(_M_IX86) + #define USE_INTRINSICS + #endif + +-#include"hwf.h" ++#include "hwf.h" + + // Note: + // Which FPU will be used is determined by compiler settings. On x64 it's always SSE2, +@@ -45,20 +47,23 @@ Revision History: + // For SSE2, it is best to use compiler intrinsics because this makes it completely + // clear to the compiler what instructions should be used. E.g., for sqrt(), the Windows compiler selects + // the x87 FPU, even when /arch:SSE2 is on. +-// Luckily, these are kind of standardized, at least for Windows/Linux/OSX. +-#ifdef __clang__ ++// Luckily, these are kind of standardized, at least for Windows/Linux/macOS. ++#if (defined(__clang__) && !defined(__MINGW32__)) || defined(_M_ARM) && defined(_M_ARM64) + #undef USE_INTRINSICS + #endif + + #ifdef USE_INTRINSICS + #include ++#if defined(_MSC_VER) || defined(__SSE4_1__) ++#include ++#endif + #endif + + hwf_manager::hwf_manager() : + m_mpz_manager(m_mpq_manager) + { + #ifdef _WINDOWS +-#if defined(_AMD64_) || defined(_M_IA64) ++#if defined(_WIN64) + // Precision control is not supported on x64. + // See: http://msdn.microsoft.com/en-us/library/e9b52ceh(VS.110).aspx + // CMW: I think this is okay though, the compiler will chose the right instructions +@@ -72,14 +77,14 @@ hwf_manager::hwf_manager() : + #endif + #endif + #else +- // OSX/Linux: Nothing. ++ // macOS/Linux: Nothing. + #endif + + // We only set the precision of the FPU here in the constructor. At the moment, there are no + // other parts of the code that could overwrite this, and Windows takes care of context switches. + + // CMW: I'm not sure what happens on CPUs with hyper-threading (since the FPU is shared). +- // I have yet to discover whether Linux and OSX save the FPU state when switching context. ++ // I have yet to discover whether Linux and macOS save the FPU state when switching context. + // As long as we stick to using the SSE2 FPU though, there shouldn't be any problems with respect + // to the precision (not sure about the rounding modes though). + } +@@ -144,7 +149,7 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mp + + mpq sig; + m_mpq_manager.set(sig, significand); +- int64 exp = m_mpz_manager.get_int64(exponent); ++ int64_t exp = m_mpz_manager.get_int64(exponent); + + if (m_mpq_manager.is_zero(significand)) + o.value = 0.0; +@@ -250,42 +255,11 @@ void hwf_manager::div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & + #endif + } + +-#ifdef _M_IA64 +-#pragma fp_contract(on) +-#endif +- + void hwf_manager::fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o) { +- // CMW: fused_mul_add is not available on most CPUs. As of 2012, only Itanium, +- // Intel Sandybridge and AMD Bulldozers support that (via AVX). +- + set_rounding_mode(rm); +- +-#ifdef _M_IA64 +- // IA64 (Itanium) will do it, if contractions are on. +- o.value = x.value * y.value + z.value; +-#else +-#if defined(_WINDOWS) +-#if _MSC_VER >= 1800 + o.value = ::fma(x.value, y.value, z.value); +-#else // Windows, older than VS 2013 +- #ifdef USE_INTRINSICS +- _mm_store_sd(&o.value, _mm_fmadd_sd(_mm_set_sd(x.value), _mm_set_sd(y.value), _mm_set_sd(z.value))); +- #else +- // If all else fails, we are imprecise. +- o.value = (x.value * y.value) + z; +- #endif +-#endif +-#else +- // Linux, OSX +- o.value = ::fma(x.value, y.value, z.value); +-#endif +-#endif + } + +-#ifdef _M_IA64 +-#pragma fp_contract(off) +-#endif +- + void hwf_manager::sqrt(mpf_rounding_mode rm, hwf const & x, hwf & o) { + set_rounding_mode(rm); + #ifdef USE_INTRINSICS +@@ -300,10 +274,15 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o + // CMW: modf is not the right function here. + // modf(x.value, &o.value); + +- // According to the Intel Architecture manual, the x87-instrunction FRNDINT is the ++ // According to the Intel Architecture manual, the x87-instruction FRNDINT is the + // same in 32-bit and 64-bit mode. The _mm_round_* intrinsics are SSE4 extensions. +-#ifdef _WINDOWS +-#ifdef USE_INTRINSICS ++#if defined(_WINDOWS) && !defined(_M_ARM) && !defined(_M_ARM64) ++#if defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) ++ o.value = nearbyint(x.value); ++#else ++ #if defined(USE_INTRINSICS) && \ ++ (defined(_WINDOWS) && (defined(__AVX__) || defined(_M_X64))) || \ ++ (!defined(_WINDOWS) && defined(__SSE4_1__)) + switch (rm) { + case 0: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_NEAREST_INT)); break; + case 2: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_POS_INF)); break; +@@ -315,7 +294,7 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o + default: + UNREACHABLE(); // Unknown rounding mode. + } +-#else ++ #else + double xv = x.value; + double & ov = o.value; + +@@ -324,21 +303,16 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o + frndint + fstp ov // Store result away. + } ++ #endif + #endif + #else +- // Linux, OSX. ++ // Linux, macOS. + o.value = nearbyint(x.value); + #endif + } + + void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { +-#if defined(_WINDOWS) && _MSC_VER <= 1700 +- o.value = fmod(x.value, y.value); +- if (o.value >= (y.value/2.0)) +- o.value -= y.value; +-#else + o.value = remainder(x.value, y.value); +-#endif + } + + void hwf_manager::maximum(hwf const & x, hwf const & y, hwf & o) { +@@ -362,7 +336,7 @@ void hwf_manager::minimum(hwf const & x, hwf const & y, hwf & o) { + _mm_store_sd(&o.value, _mm_min_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); + #else + // use __min ? +- if (is_nan(x) || is_nan(x)) ++ if (is_nan(x)) + o.value = y.value; + else if (is_nan(y)) + o.value = x.value; +@@ -408,12 +382,12 @@ void hwf_manager::to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o) + scoped_mpz n(qm), d(qm); + + if (is_normal(x)) +- qm.set(n, sig(x) | 0x0010000000000000ull); ++ qm.set(n, (uint64)(sig(x) | 0x0010000000000000ull)); + else + qm.set(n, sig(x)); + if (sgn(x)) + qm.neg(n); +- qm.set(d, 0x0010000000000000ull); ++ qm.set(d, (uint64)0x0010000000000000ull); + int e = exp(x); + if (e >= 0) + qm.mul2k(n, (unsigned)e); +@@ -552,7 +526,7 @@ void hwf_manager::mk_ninf(hwf & o) { + } + + #ifdef _WINDOWS +-#if defined(_AMD64_) || defined(_M_IA64) ++#if defined(_WIN64) + #ifdef USE_INTRINSICS + #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) + #else +@@ -618,7 +592,7 @@ void hwf_manager::set_rounding_mode(mpf_rounding_mode rm) + UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! + } + #endif +-#else // OSX/Linux ++#else // macOS/Linux + switch (rm) { + case MPF_ROUND_NEAREST_TEVEN: + SETRM(FE_TONEAREST); +@@ -637,4 +611,4 @@ void hwf_manager::set_rounding_mode(mpf_rounding_mode rm) + UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! + } + #endif +-} ++} +\ No newline at end of file +diff --git a/src/util/hwf.h b/src/util/hwf.h +index cf0c9b7ea..aec4f56f9 100644 +--- a/src/util/hwf.h ++++ b/src/util/hwf.h +@@ -19,7 +19,8 @@ Revision History: + #ifndef HWF_H_ + #define HWF_H_ + +-#include ++#include ++#include + #include"mpz.h" + #include"mpq.h" + #include"mpf.h" // we use the same rounding modes as mpf's diff --git a/lib/native/source/z3-4.5.0/4.5.0-legacy.patch.license b/lib/native/source/z3-4.5.0/4.5.0-legacy.patch.license new file mode 100644 index 0000000000..58d47557d6 --- /dev/null +++ b/lib/native/source/z3-4.5.0/4.5.0-legacy.patch.license @@ -0,0 +1,7 @@ +// This file is part of JavaSMT, +// an API wrapper for a collection of SMT solvers: +// https://github.com/sosy-lab/java-smt +// +// SPDX-FileCopyrightText: 2025 Dirk Beyer +// +// SPDX-License-Identifier: Apache-2.0 OR MIT diff --git a/lib/native/source/z3-4.5.0/README.md b/lib/native/source/z3-4.5.0/README.md new file mode 100644 index 0000000000..6ff87e37f8 --- /dev/null +++ b/lib/native/source/z3-4.5.0/README.md @@ -0,0 +1,76 @@ + + +# README — Patch: build Z3 4.5.0 with `legacy` in package names + +This README explains how to apply a patchfile to the Z3 `d57a2a6` (4.5.0) tree and build it so that package names include the string `legacy`. + +We use the Docker file for Ubuntu 18.04 as starting point (see [runUbuntu1804.sh](../../../../docker/runUbuntu1804.sh) +and apply all commands within its context. + +# 1) clone and checkout the exact commit +```bash +git clone https://github.com/Z3Prover/z3.git +cd z3 +git checkout d57a2a6dce9291acf9c71a561252f3e133f0c894 +``` + +# 2) copy your patchfile into the repo (example: 4.5.0-legacy.patch) +# then check it applies cleanly +```bash +git apply --check 4.5.0-legacy.patch +``` + +# 3) apply the patch +```bash +git apply 4.5.0-legacy.patch +``` + +# 4) Afterward, build and publish the build Z3 with the following command: +```bash +ant publish-z3-legacy -Dz3.path= -Dz3.customRev= +``` + +Example: +```bash +ant publish-z3-legacy -Dz3.path=../solvers/z3/z3 -Dz3.customRev=4.5.0 +``` + +This will compile Z3 for Linux x64 and arm64 and package all files for JavaSMT. + +## Details for the build process (the classic 4.5.0 build workflow using mk_make.py) + +For Linux x64: +```bash +rm -rf build +mkdir release-x64-linux +python3 scripts/mk_make.py --prefix="$PWD/release-x64-linux" --java +cd build && make -j "$(nproc)" && make install +``` + +For Linux arm64: +```bash +rm -rf build +mkdir release-arm64-linux +CXX=aarch64-linux-gnu-g++ CC=aarch64-linux-gnu-gcc AR=aarch64-linux-gnu-ar \ + python3 scripts/mk_make.py --prefix="$PWD/release-arm64-linux" --java +cd build && make -j "$(nproc)" && make install +``` + +For Windows x64: Not yet integrated! +MinGW on Linux crashes with invalid compiler arguments for "-lrt". +The real-time library should not be used as argument, but should be provided by MinGW. +``` +rm -rf build +mkdir release-x64-windows +CXX=x86_64-w64-mingw32-g++ CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar \ + python3 scripts/mk_make.py --prefix="$PWD/release-x64-windows" --java +cd build && make -j "$(nproc)" && make install +``` diff --git a/lib/native/x86_64-linux/libz3javalegacy.so b/lib/native/x86_64-linux/libz3javalegacy.so new file mode 120000 index 0000000000..9a45619fff --- /dev/null +++ b/lib/native/x86_64-linux/libz3javalegacy.so @@ -0,0 +1 @@ +../../java/runtime-z3-legacy/x64/libz3javalegacy.so \ No newline at end of file diff --git a/lib/native/x86_64-linux/libz3legacy.so b/lib/native/x86_64-linux/libz3legacy.so new file mode 120000 index 0000000000..95629a57ec --- /dev/null +++ b/lib/native/x86_64-linux/libz3legacy.so @@ -0,0 +1 @@ +../../java/runtime-z3-legacy/x64/libz3legacy.so \ No newline at end of file diff --git a/runExamples.sh b/runExamples.sh index 1e965f4216..8dbc6e133c 100755 --- a/runExamples.sh +++ b/runExamples.sh @@ -77,7 +77,8 @@ esac # build the classpath including all solvers CLASSPATH="$CLASSPATH$SEP$PATH_TO_JAVASMT/bin$SEP$PATH_TO_JAVASMT/lib/java/core/*" -SOLVERS="bitwuzla boolector cvc4 cvc5 mathsat opensmt optimathsat princess smtinterpol yices2 z3" +SOLVERS="bitwuzla boolector cvc4 cvc5 mathsat opensmt optimathsat princess smtinterpol yices2 z3 +z3-legacy" for solver in $SOLVERS ; do CLASSPATH="$CLASSPATH$SEP$PATH_TO_JAVASMT/lib/java/runtime-$solver/*" done diff --git a/solvers_ivy_conf/ivy_z3-legacy.xml b/solvers_ivy_conf/ivy_z3-legacy.xml new file mode 100644 index 0000000000..2fe5a90321 --- /dev/null +++ b/solvers_ivy_conf/ivy_z3-legacy.xml @@ -0,0 +1,54 @@ + + + + + + + + + Z3 solver and JavaSMT JNI wrapper for legacy version 4.5.0. + Z3 is provided under the MIT License. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/org/sosy_lab/java_smt/SolverContextFactory.java b/src/org/sosy_lab/java_smt/SolverContextFactory.java index 34c547ae28..636ebddd9c 100644 --- a/src/org/sosy_lab/java_smt/SolverContextFactory.java +++ b/src/org/sosy_lab/java_smt/SolverContextFactory.java @@ -40,6 +40,7 @@ import org.sosy_lab.java_smt.solvers.smtinterpol.SmtInterpolSolverContext; import org.sosy_lab.java_smt.solvers.yices2.Yices2SolverContext; import org.sosy_lab.java_smt.solvers.z3.Z3SolverContext; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacySolverContext; /** * Factory class for loading and generating solver contexts. Generates a {@link SolverContext} @@ -55,6 +56,7 @@ public enum Solvers { MATHSAT5, SMTINTERPOL, Z3, + Z3_WITH_INTERPOLATION, PRINCESS, BOOLECTOR, CVC4, @@ -290,6 +292,17 @@ private SolverContext generateContext0(Solvers solverToCreate) nonLinearArithmetic, loader); + case Z3_WITH_INTERPOLATION: + return Z3LegacySolverContext.create( + logger, + config, + shutdownNotifier, + logfile, + randomSeed, + floatingPointRoundingMode, + nonLinearArithmetic, + loader); + case PRINCESS: return PrincessSolverContext.create( config, shutdownNotifier, logfile, (int) randomSeed, nonLinearArithmetic); diff --git a/src/org/sosy_lab/java_smt/basicimpl/AbstractProver.java b/src/org/sosy_lab/java_smt/basicimpl/AbstractProver.java index f3d83cb22b..c46f020a53 100644 --- a/src/org/sosy_lab/java_smt/basicimpl/AbstractProver.java +++ b/src/org/sosy_lab/java_smt/basicimpl/AbstractProver.java @@ -12,6 +12,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; @@ -20,6 +21,8 @@ import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import org.checkerframework.checker.nullness.qual.Nullable; import org.sosy_lab.java_smt.api.BasicProverEnvironment; @@ -221,6 +224,23 @@ protected ImmutableSet getAssertedConstraintIds() { return builder.build(); } + /** + * Flatten and inverse the prover-stack and return all asserted constraints. + * + *

Each formula can be asserted several times. However, each assertion has a unique id. This + * implies that the inverted mapping is a plain {@link Map}, not a {@link Multimap}. + */ + protected ImmutableMap getAssertedFormulasById() { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (Multimap level : assertedFormulas) { + for (Entry entry : level.entries()) { + // the id (entry.value) is unique across all levels. + builder.put(entry.getValue(), entry.getKey()); + } + } + return builder.buildOrThrow(); + } + /** * This method registers the Evaluator to be cleaned up before the next change on the prover * stack. diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyAbstractProver.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyAbstractProver.java new file mode 100644 index 0000000000..f170097347 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyAbstractProver.java @@ -0,0 +1,397 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.MoreFiles; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.microsoft.z3legacy.Native; +import com.microsoft.z3legacy.Z3Exception; +import com.microsoft.z3legacy.enumerations.Z3_lbool; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.sosy_lab.common.ShutdownNotifier; +import org.sosy_lab.common.ShutdownNotifier.ShutdownRequestListener; +import org.sosy_lab.common.UniqueIdGenerator; +import org.sosy_lab.common.collect.PathCopyingPersistentTreeMap; +import org.sosy_lab.common.collect.PersistentMap; +import org.sosy_lab.common.io.PathCounterTemplate; +import org.sosy_lab.java_smt.api.BooleanFormula; +import org.sosy_lab.java_smt.api.Model; +import org.sosy_lab.java_smt.api.SolverContext.ProverOptions; +import org.sosy_lab.java_smt.api.SolverException; +import org.sosy_lab.java_smt.basicimpl.AbstractProverWithAllSat; +import org.sosy_lab.java_smt.basicimpl.CachingModel; + +abstract class Z3LegacyAbstractProver extends AbstractProverWithAllSat { + + protected final long z3solver; + protected final Z3LegacyFormulaCreator creator; + protected final long z3context; + protected final Z3LegacyFormulaManager mgr; + private final ShutdownRequestListener interruptListener; + + private final UniqueIdGenerator trackId = new UniqueIdGenerator(); + @Nullable private final Deque> storedConstraints; + + private final @Nullable PathCounterTemplate logfile; + + Z3LegacyAbstractProver( + Z3LegacyFormulaCreator pCreator, + Z3LegacyFormulaManager pMgr, + Set pOptions, + @Nullable PathCounterTemplate pLogfile, + ShutdownNotifier pShutdownNotifier) { + super(pOptions, pMgr.getBooleanFormulaManager(), pShutdownNotifier); + creator = pCreator; + z3context = creator.getEnv(); + z3solver = Native.mkSolver(z3context); + + Native.solverIncRef(z3context, z3solver); + + interruptListener = reason -> Native.interrupt(z3context); + shutdownNotifier.register(interruptListener); + + if (pOptions.contains(ProverOptions.GENERATE_UNSAT_CORE)) { + storedConstraints = new ArrayDeque<>(); + storedConstraints.push(PathCopyingPersistentTreeMap.of()); + } else { + storedConstraints = null; // we use NULL as flag for "no unsat-core" + } + + logfile = pLogfile; + mgr = pMgr; + } + + void addParameter(long z3params, String key, Object value) { + long keySymbol = Native.mkStringSymbol(z3context, key); + if (value instanceof Boolean) { + Native.paramsSetBool(z3context, z3params, keySymbol, (Boolean) value); + } else if (value instanceof Integer) { + Native.paramsSetUint(z3context, z3params, keySymbol, (Integer) value); + } else if (value instanceof Double) { + Native.paramsSetDouble(z3context, z3params, keySymbol, (Double) value); + } else if (value instanceof String) { + long valueSymbol = Native.mkStringSymbol(z3context, (String) value); + Native.paramsSetSymbol(z3context, z3params, keySymbol, valueSymbol); + } else { + throw new IllegalArgumentException( + String.format( + "unexpected type '%s' with value '%s' for parameter '%s'", + value.getClass(), value, key)); + } + } + + /** dump the current solver stack into a new SMTLIB file. */ + protected void logSolverStack() throws SolverException { + if (logfile != null) { // if logging is not disabled + try { + // write stack content to logfile + Path filename = logfile.getFreshPath(); + MoreFiles.createParentDirectories(filename); + Files.writeString(filename, this + "(check-sat)\n"); + } catch (IOException e) { + throw new SolverException("Cannot write Z3 log file", e); + } + } + } + + @Override + protected boolean hasPersistentModel() { + return true; + } + + @SuppressWarnings("resource") + @Override + public Model getModel() throws SolverException { + checkGenerateModels(); + return new CachingModel(getEvaluatorWithoutChecks()); + } + + @Override + protected Z3LegacyModel getEvaluatorWithoutChecks() throws SolverException { + return new Z3LegacyModel(this, z3context, getZ3Model(), creator); + } + + protected long getZ3Model() { + try { + return Native.solverGetModel(z3context, z3solver); + } catch (Z3Exception e) { + throw creator.handleZ3ExceptionAsRuntimeException(e); + } + } + + @Override + protected void pushImpl() { + push0(); + try { + Native.solverPush(z3context, z3solver); + } catch (Z3Exception exception) { + throw creator.handleZ3ExceptionAsRuntimeException(exception); + } + } + + @Override + protected void popImpl() { + Native.solverPop(z3context, z3solver, 1); + pop0(); + } + + protected void assertContraint(long constraint) { + Native.solverAssert(z3context, z3solver, constraint); + } + + protected void assertContraintAndTrack(long constraint, long symbol) { + Native.solverAssertAndTrack(z3context, z3solver, constraint, symbol); + } + + @CanIgnoreReturnValue + @SuppressWarnings("unchecked") + @Override + protected T addConstraintImpl(BooleanFormula f) throws InterruptedException { + Preconditions.checkState(!closed); + long e = creator.extractInfo(f); + try { + if (storedConstraints != null) { // Unsat core generation is on. + String varName = String.format("Z3_UNSAT_CORE_%d", trackId.getFreshId()); + BooleanFormula t = mgr.getBooleanFormulaManager().makeVariable(varName); + assertContraintAndTrack(e, creator.extractInfo(t)); + storedConstraints.push(storedConstraints.pop().putAndCopy(varName, f)); + } else { + assertContraint(e); + } + } catch (Z3Exception exception) { + throw creator.handleZ3ExceptionAsRuntimeException(exception); + } + return (T) Long.valueOf(e); + } + + protected void push0() { + Preconditions.checkState(!closed); + if (storedConstraints != null) { + storedConstraints.push(storedConstraints.peek()); + } + } + + protected void pop0() { + Preconditions.checkState(!closed); + if (storedConstraints != null) { + storedConstraints.pop(); + } + } + + @Override + public boolean isUnsatImpl() throws SolverException, InterruptedException { + logSolverStack(); + int result; + try { + result = Native.solverCheck(z3context, z3solver); + } catch (Z3Exception e) { + throw creator.handleZ3Exception(e); + } + undefinedStatusToException(result); + return result == Z3_lbool.Z3_L_FALSE.toInt(); + } + + @Override + public boolean isUnsatWithAssumptions(Collection assumptions) + throws SolverException, InterruptedException { + Preconditions.checkState(!closed); + changedSinceLastSatQuery = false; + + int result; + try { + result = + Native.solverCheckAssumptions( + z3context, + z3solver, + assumptions.size(), + assumptions.stream().mapToLong(creator::extractInfo).toArray()); + } catch (Z3Exception e) { + throw creator.handleZ3Exception(e); + } + undefinedStatusToException(result); + return result == Z3_lbool.Z3_L_FALSE.toInt(); + } + + private void undefinedStatusToException(int solverStatus) + throws SolverException, InterruptedException { + if (solverStatus == Z3_lbool.Z3_L_UNDEF.toInt()) { + creator.shutdownNotifier.shutdownIfNecessary(); + final String reason = Native.solverGetReasonUnknown(z3context, z3solver); + switch (reason) { + case "canceled": // see Z3: src/tactic/tactic.cpp + case "interrupted": // see Z3: src/solver/check_sat_result.cpp + case "interrupted from keyboard": // see Z3: src/solver/check_sat_result.cpp + throw new InterruptedException(reason); + default: + throw new SolverException("Z3 returned 'unknown' status, reason: " + reason); + } + } + } + + protected long getUnsatCore0() { + return Native.solverGetUnsatCore(z3context, z3solver); + } + + // This method is used to get the Z3 proof as a long for testing exclusively + long getZ3Proof() { + return Native.solverGetProof(z3context, z3solver); + } + + // This method is used to get the Z3 solver object for testing exclusively + long getZ3solver() { + return z3solver; + } + + @Override + public int size() { + Preconditions.checkState(!closed); + Preconditions.checkState( + Native.solverGetNumScopes(z3context, z3solver) == super.size(), + "prover-size %s does not match stack-size %s", + Native.solverGetNumScopes(z3context, z3solver), + super.size()); + return super.size(); + } + + protected long getStatistics0() { + return Native.solverGetStatistics(z3context, z3solver); + } + + @Override + public String toString() { + Preconditions.checkState(!closed); + return Native.solverToString(z3context, z3solver); + } + + @Override + public List getUnsatCore() { + Preconditions.checkState(!closed); + checkGenerateUnsatCores(); + if (storedConstraints == null) { + throw new UnsupportedOperationException( + "Option to generate the UNSAT core wasn't enabled when creating the prover environment."); + } + + List constraints = new ArrayList<>(); + long unsatCore = getUnsatCore0(); + Native.astVectorIncRef(z3context, unsatCore); + for (int i = 0; i < Native.astVectorSize(z3context, unsatCore); i++) { + long ast = Native.astVectorGet(z3context, unsatCore, i); + Native.incRef(z3context, ast); + String varName = Native.astToString(z3context, ast); + Native.decRef(z3context, ast); + constraints.add(storedConstraints.peek().get(varName)); + } + Native.astVectorDecRef(z3context, unsatCore); + return constraints; + } + + @Override + public Optional> unsatCoreOverAssumptions( + Collection assumptions) throws SolverException, InterruptedException { + checkGenerateUnsatCoresOverAssumptions(); + if (!isUnsatWithAssumptions(assumptions)) { + return Optional.empty(); + } + List core = new ArrayList<>(); + long unsatCore = getUnsatCore0(); + Native.astVectorIncRef(z3context, unsatCore); + for (int i = 0; i < Native.astVectorSize(z3context, unsatCore); i++) { + long ast = Native.astVectorGet(z3context, unsatCore, i); + core.add(creator.encapsulateBoolean(ast)); + } + Native.astVectorDecRef(z3context, unsatCore); + return Optional.of(core); + } + + @Override + public ImmutableMap getStatistics() { + // Z3 sigsevs if you try to get statistics for closed environments + Preconditions.checkState(!closed); + + ImmutableMap.Builder builder = ImmutableMap.builder(); + Set seenKeys = new HashSet<>(); + + final long stats = getStatistics0(); + for (int i = 0; i < Native.statsSize(z3context, stats); i++) { + String key = getUnusedKey(seenKeys, Native.statsGetKey(z3context, stats, i)); + if (Native.statsIsUint(z3context, stats, i)) { + builder.put(key, Integer.toString(Native.statsGetUintValue(z3context, stats, i))); + } else if (Native.statsIsDouble(z3context, stats, i)) { + builder.put(key, Double.toString(Native.statsGetDoubleValue(z3context, stats, i))); + } else { + throw new IllegalStateException( + String.format( + "Unknown data entry value for key %s at position %d in statistics '%s'", + key, i, Native.statsToString(z3context, stats))); + } + } + + return builder.buildOrThrow(); + } + + /** + * In some cases, Z3 uses the same statistics key twice. In those cases, we append an index to the + * second usage. + */ + private String getUnusedKey(Set seenKeys, final String originalKey) { + if (seenKeys.add(originalKey)) { + return originalKey; + } + String key; + int index = 0; + do { + index++; + key = originalKey + " (" + index + ")"; + } while (!seenKeys.add(key)); + return key; + } + + @Override + public void close() { + if (!closed) { + Preconditions.checkArgument( + Native.solverGetNumScopes(z3context, z3solver) >= 0, + "a negative number of scopes is not allowed"); + + Native.solverReset(z3context, z3solver); // remove all assertions from the solver + Native.solverDecRef(z3context, z3solver); + shutdownNotifier.unregister(interruptListener); + if (storedConstraints != null) { + storedConstraints.clear(); + } + } + super.close(); + } + + @Override + public R allSat(AllSatCallback callback, List important) + throws InterruptedException, SolverException { + try { + return super.allSat(callback, important); + } catch (Z3Exception e) { + throw creator.handleZ3Exception(e); + } + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyArrayFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyArrayFormulaManager.java new file mode 100644 index 0000000000..901a0314f6 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyArrayFormulaManager.java @@ -0,0 +1,56 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.microsoft.z3legacy.Native; +import org.sosy_lab.java_smt.api.Formula; +import org.sosy_lab.java_smt.api.FormulaType; +import org.sosy_lab.java_smt.basicimpl.AbstractArrayFormulaManager; + +@SuppressWarnings("MethodTypeParameterName") +class Z3LegacyArrayFormulaManager extends AbstractArrayFormulaManager { + + private final long z3context; + + Z3LegacyArrayFormulaManager(Z3LegacyFormulaCreator creator) { + super(creator); + this.z3context = creator.getEnv(); + } + + @Override + protected Long select(Long pArray, Long pIndex) { + return Native.mkSelect(z3context, pArray, pIndex); + } + + @Override + protected Long store(Long pArray, Long pIndex, Long pValue) { + return Native.mkStore(z3context, pArray, pIndex, pValue); + } + + @Override + @SuppressWarnings("MethodTypeParameterName") + protected Long internalMakeArray( + String pName, FormulaType pIndexType, FormulaType pElementType) { + final Long z3ArrayType = toSolverType(FormulaType.getArrayType(pIndexType, pElementType)); + return getFormulaCreator().makeVariable(z3ArrayType, pName); + } + + @Override + protected Long internalMakeArray( + FormulaType pIndexType, FormulaType pElementType, Long defaultElement) { + return Native.mkConstArray(z3context, toSolverType(pIndexType), defaultElement); + } + + @Override + protected Long equivalence(Long pArray1, Long pArray2) { + return Native.mkEq(z3context, pArray1, pArray2); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyBitvectorFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyBitvectorFormulaManager.java new file mode 100644 index 0000000000..8b350a080e --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyBitvectorFormulaManager.java @@ -0,0 +1,209 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.primitives.Longs; +import com.microsoft.z3legacy.Native; +import java.math.BigInteger; +import java.util.List; +import org.sosy_lab.java_smt.basicimpl.AbstractBitvectorFormulaManager; + +class Z3LegacyBitvectorFormulaManager + extends AbstractBitvectorFormulaManager { + + private final long z3context; + + Z3LegacyBitvectorFormulaManager( + Z3LegacyFormulaCreator creator, Z3LegacyBooleanFormulaManager pBmgr) { + super(creator, pBmgr); + this.z3context = creator.getEnv(); + } + + @Override + public Long concat(Long pFirst, Long pSecond) { + return Native.mkConcat(z3context, pFirst, pSecond); + } + + @Override + public Long extract(Long pNumber, int pMsb, int pLsb) { + return Native.mkExtract(z3context, pMsb, pLsb, pNumber); + } + + @Override + public Long extend(Long pNumber, int pExtensionBits, boolean pSigned) { + if (pSigned) { + return Native.mkSignExt(z3context, pExtensionBits, pNumber); + } else { + return Native.mkZeroExt(z3context, pExtensionBits, pNumber); + } + } + + @Override + protected Long makeBitvectorImpl(int pLength, BigInteger pI) { + pI = transformValueToRange(pLength, pI); + long sort = Native.mkBvSort(z3context, pLength); + return Native.mkNumeral(z3context, pI.toString(), sort); + } + + @Override + protected Long makeBitvectorImpl(int pLength, Long pNumeralFormula) { + return Native.mkInt2bv(z3context, pLength, pNumeralFormula); + } + + @Override + protected Long toIntegerFormulaImpl(Long pBVFormula, boolean pSigned) { + return Native.mkBv2int(z3context, pBVFormula, pSigned); + } + + @Override + public Long makeVariableImpl(int length, String varName) { + long type = getFormulaCreator().getBitvectorType(length); + return getFormulaCreator().makeVariable(type, varName); + } + + /** + * Return a term representing the (arithmetic if signed is true) right shift of number by toShift. + */ + @Override + public Long shiftRight(Long number, Long toShift, boolean signed) { + if (signed) { + return Native.mkBvashr(z3context, number, toShift); + } else { + return Native.mkBvlshr(z3context, number, toShift); + } + } + + @Override + public Long shiftLeft(Long number, Long toShift) { + return Native.mkBvshl(z3context, number, toShift); + } + + @Override + public Long rotateLeftByConstant(Long number, int toShift) { + return Native.mkRotateLeft(z3context, toShift, number); + } + + @Override + public Long rotateLeft(Long number, Long toShift) { + return Native.mkExtRotateLeft(z3context, number, toShift); + } + + @Override + public Long rotateRightByConstant(Long number, int toShift) { + return Native.mkRotateRight(z3context, toShift, number); + } + + @Override + public Long rotateRight(Long number, Long toShift) { + return Native.mkExtRotateRight(z3context, number, toShift); + } + + @Override + public Long not(Long pBits) { + return Native.mkBvnot(z3context, pBits); + } + + @Override + public Long and(Long pBits1, Long pBits2) { + return Native.mkBvand(z3context, pBits1, pBits2); + } + + @Override + public Long or(Long pBits1, Long pBits2) { + return Native.mkBvor(z3context, pBits1, pBits2); + } + + @Override + public Long xor(Long pBits1, Long pBits2) { + return Native.mkBvxor(z3context, pBits1, pBits2); + } + + @Override + public Long negate(Long pNumber) { + return Native.mkBvneg(z3context, pNumber); + } + + @Override + public Long add(Long pNumber1, Long pNumber2) { + return Native.mkBvadd(z3context, pNumber1, pNumber2); + } + + @Override + public Long subtract(Long pNumber1, Long pNumber2) { + return Native.mkBvsub(z3context, pNumber1, pNumber2); + } + + @Override + public Long divide(Long pNumber1, Long pNumber2, boolean signed) { + if (signed) { + return Native.mkBvsdiv(z3context, pNumber1, pNumber2); + } else { + return Native.mkBvudiv(z3context, pNumber1, pNumber2); + } + } + + @Override + public Long remainder(Long pNumber1, Long pNumber2, boolean signed) { + if (signed) { + return Native.mkBvsrem(z3context, pNumber1, pNumber2); + } else { + return Native.mkBvurem(z3context, pNumber1, pNumber2); + } + } + + @Override + protected Long smodulo(Long pParam1, Long pParam2) { + return Native.mkBvsmod(z3context, pParam1, pParam2); + } + + @Override + public Long multiply(Long pNumber1, Long pNumber2) { + return Native.mkBvmul(z3context, pNumber1, pNumber2); + } + + @Override + public Long equal(Long pNumber1, Long pNumber2) { + return Native.mkEq(z3context, pNumber1, pNumber2); + } + + @Override + public Long lessThan(Long pNumber1, Long pNumber2, boolean signed) { + if (signed) { + return Native.mkBvslt(z3context, pNumber1, pNumber2); + } else { + return Native.mkBvult(z3context, pNumber1, pNumber2); + } + } + + @Override + public Long lessOrEquals(Long pNumber1, Long pNumber2, boolean signed) { + if (signed) { + return Native.mkBvsle(z3context, pNumber1, pNumber2); + } else { + return Native.mkBvule(z3context, pNumber1, pNumber2); + } + } + + @Override + public Long greaterThan(Long pNumber1, Long pNumber2, boolean signed) { + return lessThan(pNumber2, pNumber1, signed); + } + + @Override + public Long greaterOrEquals(Long pNumber1, Long pNumber2, boolean signed) { + return lessOrEquals(pNumber2, pNumber1, signed); + } + + @Override + protected Long distinctImpl(List pBits) { + return Native.mkDistinct(z3context, pBits.size(), Longs.toArray(pBits)); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyBooleanFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyBooleanFormulaManager.java new file mode 100644 index 0000000000..8da416ac29 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyBooleanFormulaManager.java @@ -0,0 +1,180 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import static org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormulaCreator.isOP; + +import com.google.common.collect.Iterables; +import com.google.common.primitives.Longs; +import com.microsoft.z3legacy.Native; +import com.microsoft.z3legacy.enumerations.Z3_decl_kind; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; +import org.sosy_lab.java_smt.basicimpl.AbstractBooleanFormulaManager; + +class Z3LegacyBooleanFormulaManager extends AbstractBooleanFormulaManager { + + private final long z3context; + private final Long z3true; + private final Long z3false; + + Z3LegacyBooleanFormulaManager(Z3LegacyFormulaCreator creator) { + super(creator); + z3context = creator.getEnv(); + z3true = Native.mkTrue(z3context); + Native.incRef(z3context, z3true); + z3false = Native.mkFalse(z3context); + Native.incRef(z3context, z3false); + } + + @Override + protected Long makeVariableImpl(String varName) { + long type = getFormulaCreator().getBoolType(); + return getFormulaCreator().makeVariable(type, varName); + } + + @Override + protected Long makeBooleanImpl(boolean pValue) { + return pValue ? z3true : z3false; + } + + @Override + protected Long not(Long pParam) { + if (isTrue(pParam)) { + return z3false; + } else if (isFalse(pParam)) { + return z3true; + } else if (isOP(z3context, pParam, Z3_decl_kind.Z3_OP_NOT)) { + return Native.getAppArg(z3context, pParam, 0); + } + return Native.mkNot(z3context, pParam); + } + + @Override + protected Long and(Long pParam1, Long pParam2) { + if (isTrue(pParam1)) { + return pParam2; + } else if (isTrue(pParam2)) { + return pParam1; + } else if (isFalse(pParam1)) { + return z3false; + } else if (isFalse(pParam2)) { + return z3false; + } else if (Native.isEqAst(z3context, pParam1, pParam2)) { + return pParam1; + } + return Native.mkAnd(z3context, 2, new long[] {pParam1, pParam2}); + } + + @Override + protected Long or(Long pParam1, Long pParam2) { + if (isTrue(pParam1)) { + return z3true; + } else if (isTrue(pParam2)) { + return z3true; + } else if (isFalse(pParam1)) { + return pParam2; + } else if (isFalse(pParam2)) { + return pParam1; + } else if (Native.isEqAst(z3context, pParam1, pParam2)) { + return pParam1; + } + return Native.mkOr(z3context, 2, new long[] {pParam1, pParam2}); + } + + @Override + protected Long orImpl(Collection params) { + // Z3 does not do any simplifications, + // so we filter "true", short-circuit on "false", and filter out (simple) redundancies. + final Set operands = new LinkedHashSet<>(); + for (final Long operand : params) { + if (isTrue(operand)) { + return z3true; + } + if (!isFalse(operand)) { + operands.add(operand); + } + } + switch (operands.size()) { + case 0: + return z3false; + case 1: + return Iterables.getOnlyElement(operands); + default: + return Native.mkOr(z3context, operands.size(), Longs.toArray(operands)); + } + } + + @Override + protected Long andImpl(Collection params) { + // Z3 does not do any simplifications, + // so we filter "true", short-circuit on "false", and filter out (simple) redundancies. + final Set operands = new LinkedHashSet<>(); + for (final Long operand : params) { + if (isFalse(operand)) { + return z3false; + } + if (!isTrue(operand)) { + operands.add(operand); + } + } + switch (operands.size()) { + case 0: + return z3true; + case 1: + return Iterables.getOnlyElement(operands); + default: + return Native.mkAnd(z3context, operands.size(), Longs.toArray(operands)); + } + } + + @Override + protected Long xor(Long pParam1, Long pParam2) { + return Native.mkXor(z3context, pParam1, pParam2); + } + + @Override + protected Long equivalence(Long pBits1, Long pBits2) { + return Native.mkEq(z3context, pBits1, pBits2); + } + + @Override + protected Long implication(Long pBits1, Long pBits2) { + return Native.mkImplies(z3context, pBits1, pBits2); + } + + @Override + protected boolean isTrue(Long pParam) { + return z3true.equals(pParam); + } + + @Override + protected boolean isFalse(Long pParam) { + return z3false.equals(pParam); + } + + @Override + protected Long ifThenElse(Long pCond, Long pF1, Long pF2) { + if (isTrue(pCond)) { + return pF1; + } else if (isFalse(pCond)) { + return pF2; + } else if (pF1.equals(pF2)) { + return pF1; + } else if (isTrue(pF1) && isFalse(pF2)) { + return pCond; + } else if (isFalse(pF1) && isTrue(pF2)) { + return not(pCond); + } + return Native.mkIte(z3context, pCond, pF1, pF2); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyEnumerationFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyEnumerationFormulaManager.java new file mode 100644 index 0000000000..fb35e53205 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyEnumerationFormulaManager.java @@ -0,0 +1,60 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.collect.ImmutableMap; +import com.microsoft.z3legacy.Native; +import org.sosy_lab.java_smt.api.FormulaType.EnumerationFormulaType; +import org.sosy_lab.java_smt.basicimpl.AbstractEnumerationFormulaManager; + +class Z3LegacyEnumerationFormulaManager + extends AbstractEnumerationFormulaManager { + + private final long z3context; + + Z3LegacyEnumerationFormulaManager(Z3LegacyFormulaCreator creator) { + super(creator); + this.z3context = creator.getEnv(); + } + + @Override + protected EnumType declareEnumeration0(EnumerationFormulaType pType) { + long symbol = Native.mkStringSymbol(z3context, pType.getName()); + + String[] elements = pType.getElements().toArray(new String[] {}); + long[] elementSymbols = new long[elements.length]; + for (int i = 0; i < elements.length; i++) { + elementSymbols[i] = Native.mkStringSymbol(z3context, elements[i]); + } + + long[] constants = new long[pType.getElements().size()]; + long[] predicates = new long[pType.getElements().size()]; // unused later + + long enumType = + Native.mkEnumerationSort( + z3context, symbol, elements.length, elementSymbols, constants, predicates); + Native.incRef(z3context, enumType); + + // we store the constants for later access + ImmutableMap.Builder constantsMapping = ImmutableMap.builder(); + for (int i = 0; i < elements.length; i++) { + long constantApp = Native.mkApp(z3context, constants[i], 0, null); + Native.incRef(z3context, constantApp); + constantsMapping.put(elements[i], constantApp); + } + return new EnumType(pType, enumType, constantsMapping.buildOrThrow()); + } + + @Override + protected Long equivalenceImpl(Long pF1, Long pF2) { + return Native.mkEq(z3context, pF1, pF2); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFloatingPointFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFloatingPointFormulaManager.java new file mode 100644 index 0000000000..85dc4a353b --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFloatingPointFormulaManager.java @@ -0,0 +1,319 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.collect.ImmutableList; +import com.microsoft.z3legacy.Native; +import java.math.BigInteger; +import org.sosy_lab.java_smt.api.FloatingPointNumber.Sign; +import org.sosy_lab.java_smt.api.FloatingPointRoundingMode; +import org.sosy_lab.java_smt.api.FormulaType; +import org.sosy_lab.java_smt.api.FormulaType.FloatingPointType; +import org.sosy_lab.java_smt.basicimpl.AbstractFloatingPointFormulaManager; + +class Z3LegacyFloatingPointFormulaManager + extends AbstractFloatingPointFormulaManager { + + private static final FloatingPointType highPrec = FormulaType.getFloatingPointType(15, 112); + + private final long z3context; + private final long roundingMode; + + Z3LegacyFloatingPointFormulaManager( + Z3LegacyFormulaCreator creator, FloatingPointRoundingMode pFloatingPointRoundingMode) { + super(creator); + z3context = creator.getEnv(); + roundingMode = getRoundingModeImpl(pFloatingPointRoundingMode); + } + + @Override + protected Long getDefaultRoundingMode() { + return roundingMode; + } + + @Override + protected Long getRoundingModeImpl(FloatingPointRoundingMode pFloatingPointRoundingMode) { + long out; + switch (pFloatingPointRoundingMode) { + case NEAREST_TIES_TO_EVEN: + out = Native.mkFpaRoundNearestTiesToEven(z3context); + break; + case NEAREST_TIES_AWAY: + out = Native.mkFpaRoundNearestTiesToAway(z3context); + break; + case TOWARD_POSITIVE: + out = Native.mkFpaRoundTowardPositive(z3context); + break; + case TOWARD_NEGATIVE: + out = Native.mkFpaRoundTowardNegative(z3context); + break; + case TOWARD_ZERO: + out = Native.mkFpaRoundTowardZero(z3context); + break; + default: + throw new AssertionError("Unexpected value"); + } + Native.incRef(z3context, out); + return out; + } + + private long mkFpaSort(FloatingPointType pType) { + return getFormulaCreator().getFloatingPointType(pType); + } + + @Override + protected Long makeNumberImpl(double pN, FloatingPointType pType, Long pRoundingMode) { + return makeNumberImpl(Double.toString(pN), pType, pRoundingMode); + } + + @Override + protected Long makeNumberImpl( + BigInteger exponent, BigInteger mantissa, Sign sign, FloatingPointType type) { + + final long signSort = getFormulaCreator().getBitvectorType(1); + final long expoSort = getFormulaCreator().getBitvectorType(type.getExponentSize()); + final long mantSort = getFormulaCreator().getBitvectorType(type.getMantissaSize()); + + final long signBv = Native.mkNumeral(z3context, sign.isNegative() ? "1" : "0", signSort); + Native.incRef(z3context, signBv); + final long expoBv = Native.mkNumeral(z3context, exponent.toString(), expoSort); + Native.incRef(z3context, expoBv); + final long mantBv = Native.mkNumeral(z3context, mantissa.toString(), mantSort); + Native.incRef(z3context, mantBv); + + final long fp = Native.mkFpaFp(z3context, signBv, expoBv, mantBv); + Native.decRef(z3context, mantBv); + Native.decRef(z3context, expoBv); + Native.decRef(z3context, signBv); + return fp; + } + + @Override + protected Long makeNumberAndRound(String pN, FloatingPointType pType, Long pRoundingMode) { + // Z3 does not allow specifying a rounding mode for numerals, + // so we create it first with a high precision and then round it down explicitly. + if (pType.getExponentSize() <= highPrec.getExponentSize() + || pType.getMantissaSize() <= highPrec.getMantissaSize()) { + long highPrecNumber = Native.mkNumeral(z3context, pN, mkFpaSort(highPrec)); + Native.incRef(z3context, highPrecNumber); + long smallPrecNumber = + castToImpl(highPrecNumber, /* irrelevant: */ true, pType, pRoundingMode); + Native.incRef(z3context, smallPrecNumber); + long result = Native.simplify(z3context, smallPrecNumber); + Native.decRef(z3context, highPrecNumber); + Native.decRef(z3context, smallPrecNumber); + return result; + } + return Native.mkNumeral(z3context, pN, mkFpaSort(pType)); + } + + @Override + protected Long makeVariableImpl(String var, FloatingPointType pType) { + return getFormulaCreator().makeVariable(mkFpaSort(pType), var); + } + + @Override + protected Long makePlusInfinityImpl(FloatingPointType pType) { + return Native.mkFpaInf(z3context, mkFpaSort(pType), false); + } + + @Override + protected Long makeMinusInfinityImpl(FloatingPointType pType) { + return Native.mkFpaInf(z3context, mkFpaSort(pType), true); + } + + @Override + protected Long makeNaNImpl(FloatingPointType pType) { + return Native.mkFpaNan(z3context, mkFpaSort(pType)); + } + + @Override + protected Long castToImpl( + Long pNumber, boolean pSigned, FormulaType pTargetType, Long pRoundingMode) { + if (pTargetType.isFloatingPointType()) { + FloatingPointType targetType = (FloatingPointType) pTargetType; + return Native.mkFpaToFpFloat(z3context, pRoundingMode, pNumber, mkFpaSort(targetType)); + + } else if (pTargetType.isBitvectorType()) { + FormulaType.BitvectorType targetType = (FormulaType.BitvectorType) pTargetType; + if (pSigned) { + return Native.mkFpaToSbv(z3context, pRoundingMode, pNumber, targetType.getSize()); + } else { + return Native.mkFpaToUbv(z3context, pRoundingMode, pNumber, targetType.getSize()); + } + + } else if (pTargetType.isRationalType()) { + return Native.mkFpaToReal(z3context, pNumber); + + } else { + return genericCast(pNumber, pTargetType); + } + } + + @Override + protected Long castFromImpl( + Long pNumber, boolean pSigned, FloatingPointType pTargetType, Long pRoundingMode) { + FormulaType formulaType = getFormulaCreator().getFormulaType(pNumber); + + if (formulaType.isFloatingPointType()) { + return castToImpl(pNumber, pSigned, pTargetType, pRoundingMode); + + } else if (formulaType.isBitvectorType()) { + if (pSigned) { + return Native.mkFpaToFpSigned(z3context, pRoundingMode, pNumber, mkFpaSort(pTargetType)); + } else { + return Native.mkFpaToFpUnsigned(z3context, pRoundingMode, pNumber, mkFpaSort(pTargetType)); + } + + } else if (formulaType.isRationalType()) { + return Native.mkFpaToFpReal(z3context, pRoundingMode, pNumber, mkFpaSort(pTargetType)); + + } else { + return genericCast(pNumber, pTargetType); + } + } + + private Long genericCast(Long pNumber, FormulaType pTargetType) { + FormulaType argType = getFormulaCreator().getFormulaType(pNumber); + long castFuncDecl = + getFormulaCreator() + .declareUFImpl( + "__cast_" + argType + "_to_" + pTargetType, + toSolverType(pTargetType), + ImmutableList.of(toSolverType(argType))); + return Native.mkApp(z3context, castFuncDecl, 1, new long[] {pNumber}); + } + + @Override + protected Long fromIeeeBitvectorImpl(Long pNumber, FloatingPointType pTargetType) { + return Native.mkFpaToFpBv(z3context, pNumber, mkFpaSort(pTargetType)); + } + + @Override + protected Long toIeeeBitvectorImpl(Long pNumber) { + return Native.mkFpaToIeeeBv(z3context, pNumber); + } + + @Override + protected Long negate(Long pNumber) { + return Native.mkFpaNeg(z3context, pNumber); + } + + @Override + protected Long abs(Long pNumber) { + return Native.mkFpaAbs(z3context, pNumber); + } + + @Override + protected Long max(Long pNumber1, Long pNumber2) { + return Native.mkFpaMax(z3context, pNumber1, pNumber2); + } + + @Override + protected Long min(Long pNumber1, Long pNumber2) { + return Native.mkFpaMin(z3context, pNumber1, pNumber2); + } + + @Override + protected Long sqrt(Long pNumber, Long pRoundingMode) { + return Native.mkFpaSqrt(z3context, pRoundingMode, pNumber); + } + + @Override + protected Long add(Long pNumber1, Long pNumber2, Long pRoundingMode) { + return Native.mkFpaAdd(z3context, pRoundingMode, pNumber1, pNumber2); + } + + @Override + protected Long subtract(Long pNumber1, Long pNumber2, Long pRoundingMode) { + return Native.mkFpaSub(z3context, pRoundingMode, pNumber1, pNumber2); + } + + @Override + protected Long multiply(Long pNumber1, Long pNumber2, Long pRoundingMode) { + return Native.mkFpaMul(z3context, pRoundingMode, pNumber1, pNumber2); + } + + @Override + protected Long remainder(Long pParam1, Long pParam2) { + return Native.mkFpaRem(z3context, pParam1, pParam2); + } + + @Override + protected Long divide(Long pNumber1, Long pNumber2, Long pRoundingMode) { + return Native.mkFpaDiv(z3context, pRoundingMode, pNumber1, pNumber2); + } + + @Override + protected Long assignment(Long pNumber1, Long pNumber2) { + return Native.mkEq(z3context, pNumber1, pNumber2); + } + + @Override + protected Long equalWithFPSemantics(Long pNumber1, Long pNumber2) { + return Native.mkFpaEq(z3context, pNumber1, pNumber2); + } + + @Override + protected Long greaterThan(Long pNumber1, Long pNumber2) { + return Native.mkFpaGt(z3context, pNumber1, pNumber2); + } + + @Override + protected Long greaterOrEquals(Long pNumber1, Long pNumber2) { + return Native.mkFpaGeq(z3context, pNumber1, pNumber2); + } + + @Override + protected Long lessThan(Long pNumber1, Long pNumber2) { + return Native.mkFpaLt(z3context, pNumber1, pNumber2); + } + + @Override + protected Long lessOrEquals(Long pNumber1, Long pNumber2) { + return Native.mkFpaLeq(z3context, pNumber1, pNumber2); + } + + @Override + protected Long isNaN(Long pParam) { + return Native.mkFpaIsNan(z3context, pParam); + } + + @Override + protected Long isInfinity(Long pParam) { + return Native.mkFpaIsInfinite(z3context, pParam); + } + + @Override + protected Long isZero(Long pParam) { + return Native.mkFpaIsZero(z3context, pParam); + } + + @Override + protected Long isSubnormal(Long pParam) { + return Native.mkFpaIsSubnormal(z3context, pParam); + } + + @Override + protected Long isNormal(Long pParam) { + return Native.mkFpaIsNormal(z3context, pParam); + } + + @Override + protected Long isNegative(Long pParam) { + return Native.mkFpaIsNegative(z3context, pParam); + } + + @Override + protected Long round(Long pFormula, FloatingPointRoundingMode pRoundingMode) { + return Native.mkFpaRoundToIntegral(z3context, getRoundingModeImpl(pRoundingMode), pFormula); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFormula.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFormula.java new file mode 100644 index 0000000000..6c3fc26bc5 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFormula.java @@ -0,0 +1,167 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.errorprone.annotations.Immutable; +import com.microsoft.z3legacy.Native; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.sosy_lab.java_smt.api.ArrayFormula; +import org.sosy_lab.java_smt.api.BitvectorFormula; +import org.sosy_lab.java_smt.api.BooleanFormula; +import org.sosy_lab.java_smt.api.EnumerationFormula; +import org.sosy_lab.java_smt.api.FloatingPointFormula; +import org.sosy_lab.java_smt.api.FloatingPointRoundingModeFormula; +import org.sosy_lab.java_smt.api.Formula; +import org.sosy_lab.java_smt.api.FormulaType; +import org.sosy_lab.java_smt.api.NumeralFormula.IntegerFormula; +import org.sosy_lab.java_smt.api.NumeralFormula.RationalFormula; +import org.sosy_lab.java_smt.api.RegexFormula; +import org.sosy_lab.java_smt.api.StringFormula; + +@Immutable +abstract class Z3LegacyFormula implements Formula { + + private final long z3expr; + private final long z3context; + private final int hashCache; + + private Z3LegacyFormula(long z3context, long z3expr) { + checkArgument(z3context != 0, "Z3 context is null"); + checkArgument(z3expr != 0, "Z3 formula is null"); + this.z3expr = z3expr; + this.z3context = z3context; + + Native.incRef(z3context, z3expr); + this.hashCache = Native.getAstHash(z3context, z3expr); + } + + @Override + public final String toString() { + return Native.astToString(z3context, z3expr); + } + + @Override + public final boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Z3LegacyFormula)) { + return false; + } + Z3LegacyFormula other = (Z3LegacyFormula) obj; + return (z3context == other.z3context) && Native.isEqAst(z3context, z3expr, other.z3expr); + } + + @Override + public final int hashCode() { + return hashCache; + } + + final long getFormulaInfo() { + return z3expr; + } + + @SuppressWarnings("ClassTypeParameterName") + static final class Z3ArrayLegacyFormula + extends Z3LegacyFormula implements ArrayFormula { + + private final FormulaType indexType; + private final FormulaType elementType; + + Z3ArrayLegacyFormula( + long pZ3context, long pZ3expr, FormulaType pIndexType, FormulaType pElementType) { + super(pZ3context, pZ3expr); + indexType = pIndexType; + elementType = pElementType; + } + + public FormulaType getIndexType() { + return indexType; + } + + public FormulaType getElementType() { + return elementType; + } + } + + @Immutable + static final class Z3BitvectorLegacyFormula extends Z3LegacyFormula implements BitvectorFormula { + + Z3BitvectorLegacyFormula(long z3context, long z3expr) { + super(z3context, z3expr); + } + } + + @Immutable + static final class Z3FloatingPointLegacyFormula extends Z3LegacyFormula + implements FloatingPointFormula { + + Z3FloatingPointLegacyFormula(long z3context, long z3expr) { + super(z3context, z3expr); + } + } + + @Immutable + static final class Z3FloatingPointRoundingModeLegacyFormula extends Z3LegacyFormula + implements FloatingPointRoundingModeFormula { + + Z3FloatingPointRoundingModeLegacyFormula(long z3context, long z3expr) { + super(z3context, z3expr); + } + } + + @Immutable + static final class Z3IntegerLegacyFormula extends Z3LegacyFormula implements IntegerFormula { + + Z3IntegerLegacyFormula(long z3context, long z3expr) { + super(z3context, z3expr); + } + } + + @Immutable + static final class Z3RationalLegacyFormula extends Z3LegacyFormula implements RationalFormula { + + Z3RationalLegacyFormula(long z3context, long z3expr) { + super(z3context, z3expr); + } + } + + @Immutable + static final class Z3BooleanLegacyFormula extends Z3LegacyFormula implements BooleanFormula { + Z3BooleanLegacyFormula(long z3context, long z3expr) { + super(z3context, z3expr); + } + } + + @Immutable + static final class Z3StringLegacyFormula extends Z3LegacyFormula implements StringFormula { + Z3StringLegacyFormula(long z3context, long z3expr) { + super(z3context, z3expr); + } + } + + @Immutable + static final class Z3RegexLegacyFormula extends Z3LegacyFormula implements RegexFormula { + Z3RegexLegacyFormula(long z3context, long z3expr) { + super(z3context, z3expr); + } + } + + @Immutable + static final class Z3EnumerationLegacyFormula extends Z3LegacyFormula + implements EnumerationFormula { + Z3EnumerationLegacyFormula(long z3context, long z3expr) { + super(z3context, z3expr); + } + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFormulaCreator.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFormulaCreator.java new file mode 100644 index 0000000000..143fca840a --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFormulaCreator.java @@ -0,0 +1,1171 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.microsoft.z3legacy.enumerations.Z3_decl_kind.Z3_OP_DISTINCT; + +import com.google.common.base.Preconditions; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Table; +import com.google.common.primitives.Longs; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.microsoft.z3legacy.Native; +import com.microsoft.z3legacy.Z3Exception; +import com.microsoft.z3legacy.enumerations.Z3_ast_kind; +import com.microsoft.z3legacy.enumerations.Z3_decl_kind; +import com.microsoft.z3legacy.enumerations.Z3_sort_kind; +import com.microsoft.z3legacy.enumerations.Z3_symbol_kind; +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.sosy_lab.common.ShutdownNotifier; +import org.sosy_lab.common.configuration.Configuration; +import org.sosy_lab.common.configuration.InvalidConfigurationException; +import org.sosy_lab.common.configuration.Option; +import org.sosy_lab.common.configuration.Options; +import org.sosy_lab.common.rationals.Rational; +import org.sosy_lab.common.time.Timer; +import org.sosy_lab.java_smt.api.ArrayFormula; +import org.sosy_lab.java_smt.api.BitvectorFormula; +import org.sosy_lab.java_smt.api.BooleanFormula; +import org.sosy_lab.java_smt.api.EnumerationFormula; +import org.sosy_lab.java_smt.api.FloatingPointFormula; +import org.sosy_lab.java_smt.api.FloatingPointNumber; +import org.sosy_lab.java_smt.api.FloatingPointNumber.Sign; +import org.sosy_lab.java_smt.api.FloatingPointRoundingMode; +import org.sosy_lab.java_smt.api.FloatingPointRoundingModeFormula; +import org.sosy_lab.java_smt.api.Formula; +import org.sosy_lab.java_smt.api.FormulaType; +import org.sosy_lab.java_smt.api.FormulaType.ArrayFormulaType; +import org.sosy_lab.java_smt.api.FormulaType.FloatingPointType; +import org.sosy_lab.java_smt.api.FunctionDeclarationKind; +import org.sosy_lab.java_smt.api.QuantifiedFormulaManager.Quantifier; +import org.sosy_lab.java_smt.api.RegexFormula; +import org.sosy_lab.java_smt.api.SolverException; +import org.sosy_lab.java_smt.api.StringFormula; +import org.sosy_lab.java_smt.api.visitors.FormulaVisitor; +import org.sosy_lab.java_smt.basicimpl.AbstractStringFormulaManager; +import org.sosy_lab.java_smt.basicimpl.FormulaCreator; +import org.sosy_lab.java_smt.basicimpl.FunctionDeclarationImpl; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3ArrayLegacyFormula; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3BitvectorLegacyFormula; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3BooleanLegacyFormula; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3EnumerationLegacyFormula; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3FloatingPointLegacyFormula; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3FloatingPointRoundingModeLegacyFormula; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3IntegerLegacyFormula; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3RationalLegacyFormula; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3RegexLegacyFormula; +import org.sosy_lab.java_smt.solvers.z3legacy.Z3LegacyFormula.Z3StringLegacyFormula; + +@Options(prefix = "solver.z3") +class Z3LegacyFormulaCreator extends FormulaCreator { + + private static final ImmutableMap Z3_CONSTANTS = + ImmutableMap.builder() + .put(Z3_decl_kind.Z3_OP_TRUE.toInt(), true) + .put(Z3_decl_kind.Z3_OP_FALSE.toInt(), false) + .put( + Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN.toInt(), + FloatingPointRoundingMode.NEAREST_TIES_TO_EVEN) + .put( + Z3_decl_kind.Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY.toInt(), + FloatingPointRoundingMode.NEAREST_TIES_AWAY) + .put( + Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_POSITIVE.toInt(), + FloatingPointRoundingMode.TOWARD_POSITIVE) + .put( + Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_NEGATIVE.toInt(), + FloatingPointRoundingMode.TOWARD_NEGATIVE) + .put(Z3_decl_kind.Z3_OP_FPA_RM_TOWARD_ZERO.toInt(), FloatingPointRoundingMode.TOWARD_ZERO) + .buildOrThrow(); + + private static final ImmutableSet Z3_FP_CONSTANTS = + ImmutableSet.of( + Z3_decl_kind.Z3_OP_FPA_PLUS_ZERO.toInt(), + Z3_decl_kind.Z3_OP_FPA_MINUS_ZERO.toInt(), + Z3_decl_kind.Z3_OP_FPA_PLUS_INF.toInt(), + Z3_decl_kind.Z3_OP_FPA_MINUS_INF.toInt(), + Z3_decl_kind.Z3_OP_FPA_NAN.toInt()); + + // Set of error messages that might occur if Z3 is interrupted. + private static final ImmutableSet Z3_INTERRUPT_ERRORS = + ImmutableSet.of( + "canceled", // Z3::src/util/common_msgs.cpp + "push canceled", // src/smt/smt_context.cpp + "interrupted from keyboard", // Z3: src/solver/check_sat_result.cpp + "Proof error!", + "interrupted", // Z3::src/solver/check_sat_result.cpp + "maximization suspended" // Z3::src/opt/opt_solver.cpp + ); + + @Option(secure = true, description = "Whether to use PhantomReferences for discarding Z3 AST") + private boolean usePhantomReferences = false; + + /** + * We need to track all created symbols for parsing. + * + *

This map stores symbols (names) and their declaration (type information). + */ + private final Map symbolsToDeclarations = new LinkedHashMap<>(); + + private final Table allocatedArraySorts = HashBasedTable.create(); + + /** Automatic clean-up of Z3 ASTs. */ + private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + + private final Z3AstReference referenceListHead; + + // todo: getters for statistic. + private final Timer cleanupTimer = new Timer(); + protected final ShutdownNotifier shutdownNotifier; + + @SuppressWarnings("ParameterNumber") + Z3LegacyFormulaCreator( + long pEnv, + long pBoolType, + long pIntegerType, + long pRealType, + long pStringType, + long pRegexType, + Configuration config, + ShutdownNotifier pShutdownNotifier) + throws InvalidConfigurationException { + super(pEnv, pBoolType, pIntegerType, pRealType, pStringType, pRegexType); + shutdownNotifier = pShutdownNotifier; + config.inject(this); + + if (usePhantomReferences) { + // Setup sentinel nodes for doubly-linked phantom reference list. + Z3AstReference head = new Z3AstReference(); + Z3AstReference tail = new Z3AstReference(); + head.next = tail; + tail.prev = head; + referenceListHead = head; + } else { + referenceListHead = null; + } + } + + /** + * This method throws an {@link InterruptedException} if Z3 was interrupted by a shutdown hook. + * Otherwise, the given exception is wrapped and thrown as a SolverException. + */ + @CanIgnoreReturnValue + final SolverException handleZ3Exception(Z3Exception e) + throws SolverException, InterruptedException { + if (Z3_INTERRUPT_ERRORS.contains(e.getMessage())) { + shutdownNotifier.shutdownIfNecessary(); + } + throw new SolverException("Z3 has thrown an exception", e); + } + + /** + * This method handles a Z3Exception, however it only throws a RuntimeException. This method is + * used in places where we cannot throw a checked exception in JavaSMT due to API restrictions. + * + * @param e the Z3Exception to handle + * @return nothing, always throw a RuntimeException + * @throws RuntimeException always thrown for the given Z3Exception + */ + final RuntimeException handleZ3ExceptionAsRuntimeException(Z3Exception e) { + try { + throw handleZ3Exception(e); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw sneakyThrow(e); + } catch (SolverException ex) { + throw sneakyThrow(e); + } + } + + @SuppressWarnings("unchecked") + private static RuntimeException sneakyThrow(Throwable e) throws E { + throw (E) e; + } + + @Override + public Long makeVariable(Long type, String varName) { + long z3context = getEnv(); + long symbol = Native.mkStringSymbol(z3context, varName); + long var = Native.mkConst(z3context, symbol, type); + Native.incRef(z3context, var); + symbolsToDeclarations.put(varName, Native.getAppDecl(z3context, var)); + return var; + } + + @Override + public Long extractInfo(Formula pT) { + if (pT instanceof Z3LegacyFormula) { + return ((Z3LegacyFormula) pT).getFormulaInfo(); + } + throw new IllegalArgumentException( + "Cannot get the formula info of type " + pT.getClass().getSimpleName() + " in the Solver!"); + } + + @SuppressWarnings("unchecked") + @Override + public FormulaType getFormulaType(T pFormula) { + Long term = extractInfo(pFormula); + return (FormulaType) getFormulaType(term); + } + + public FormulaType getFormulaTypeFromSort(Long pSort) { + long z3context = getEnv(); + Z3_sort_kind sortKind = Z3_sort_kind.fromInt(Native.getSortKind(z3context, pSort)); + switch (sortKind) { + case Z3_BOOL_SORT: + return FormulaType.BooleanType; + case Z3_INT_SORT: + return FormulaType.IntegerType; + case Z3_REAL_SORT: + return FormulaType.RationalType; + case Z3_BV_SORT: + return FormulaType.getBitvectorTypeWithSize(Native.getBvSortSize(z3context, pSort)); + case Z3_ARRAY_SORT: + long domainSort = Native.getArraySortDomain(z3context, pSort); + long rangeSort = Native.getArraySortRange(z3context, pSort); + return FormulaType.getArrayType( + getFormulaTypeFromSort(domainSort), getFormulaTypeFromSort(rangeSort)); + case Z3_FLOATING_POINT_SORT: + return FormulaType.getFloatingPointType( + Native.fpaGetEbits(z3context, pSort), Native.fpaGetSbits(z3context, pSort) - 1); + case Z3_ROUNDING_MODE_SORT: + return FormulaType.FloatingPointRoundingModeType; + case Z3_RE_SORT: + return FormulaType.RegexType; + case Z3_DATATYPE_SORT: + int n = Native.getDatatypeSortNumConstructors(z3context, pSort); + ImmutableSet.Builder elements = ImmutableSet.builder(); + for (int i = 0; i < n; i++) { + long decl = Native.getDatatypeSortConstructor(z3context, pSort, i); + elements.add(symbolToString(Native.getDeclName(z3context, decl))); + } + return FormulaType.getEnumerationType( + Native.sortToString(z3context, pSort), elements.build()); + case Z3_RELATION_SORT: + case Z3_FINITE_DOMAIN_SORT: + case Z3_SEQ_SORT: + case Z3_UNKNOWN_SORT: + case Z3_UNINTERPRETED_SORT: + if (Native.isStringSort(z3context, pSort)) { + return FormulaType.StringType; + } else { + // TODO: support for remaining sorts. + throw new IllegalArgumentException( + "Unknown formula type " + + Native.sortToString(z3context, pSort) + + " with sort " + + sortKind); + } + default: + throw new UnsupportedOperationException("Unexpected state."); + } + } + + @Override + public FormulaType getFormulaType(Long pFormula) { + long sort = Native.getSort(getEnv(), pFormula); + return getFormulaTypeFromSort(sort); + } + + @Override + @SuppressWarnings("MethodTypeParameterName") + protected FormulaType getArrayFormulaElementType( + ArrayFormula pArray) { + return ((Z3ArrayLegacyFormula) pArray).getElementType(); + } + + @Override + @SuppressWarnings("MethodTypeParameterName") + protected FormulaType getArrayFormulaIndexType( + ArrayFormula pArray) { + return ((Z3ArrayLegacyFormula) pArray).getIndexType(); + } + + @Override + @SuppressWarnings("MethodTypeParameterName") + protected ArrayFormula encapsulateArray( + Long pTerm, FormulaType pIndexType, FormulaType pElementType) { + assert getFormulaType(pTerm).equals(FormulaType.getArrayType(pIndexType, pElementType)); + cleanupReferences(); + return storePhantomReference( + new Z3ArrayLegacyFormula<>(getEnv(), pTerm, pIndexType, pElementType), pTerm); + } + + @SuppressWarnings("unchecked") + @Override + public T encapsulate(FormulaType pType, Long pTerm) { + assert pType.equals(getFormulaType(pTerm)) + || (pType.equals(FormulaType.RationalType) + && getFormulaType(pTerm).equals(FormulaType.IntegerType)) + : String.format( + "Trying to encapsulate formula of type %s as %s", getFormulaType(pTerm), pType); + cleanupReferences(); + if (pType.isBooleanType()) { + return (T) storePhantomReference(new Z3BooleanLegacyFormula(getEnv(), pTerm), pTerm); + } else if (pType.isIntegerType()) { + return (T) storePhantomReference(new Z3IntegerLegacyFormula(getEnv(), pTerm), pTerm); + } else if (pType.isRationalType()) { + return (T) storePhantomReference(new Z3RationalLegacyFormula(getEnv(), pTerm), pTerm); + } else if (pType.isStringType()) { + return (T) storePhantomReference(new Z3StringLegacyFormula(getEnv(), pTerm), pTerm); + } else if (pType.isRegexType()) { + return (T) storePhantomReference(new Z3RegexLegacyFormula(getEnv(), pTerm), pTerm); + } else if (pType.isBitvectorType()) { + return (T) storePhantomReference(new Z3BitvectorLegacyFormula(getEnv(), pTerm), pTerm); + } else if (pType.isFloatingPointType()) { + return (T) storePhantomReference(new Z3FloatingPointLegacyFormula(getEnv(), pTerm), pTerm); + } else if (pType.isFloatingPointRoundingModeType()) { + return (T) + storePhantomReference( + new Z3FloatingPointRoundingModeLegacyFormula(getEnv(), pTerm), pTerm); + } else if (pType.isArrayType()) { + ArrayFormulaType arrFt = (ArrayFormulaType) pType; + return (T) + storePhantomReference( + new Z3ArrayLegacyFormula<>( + getEnv(), pTerm, arrFt.getIndexType(), arrFt.getElementType()), + pTerm); + } else if (pType.isEnumerationType()) { + return (T) storePhantomReference(new Z3EnumerationLegacyFormula(getEnv(), pTerm), pTerm); + } + + throw new IllegalArgumentException("Cannot create formulas of type " + pType + " in Z3"); + } + + @Override + public BooleanFormula encapsulateBoolean(Long pTerm) { + assert getFormulaType(pTerm).isBooleanType(); + cleanupReferences(); + return storePhantomReference(new Z3BooleanLegacyFormula(getEnv(), pTerm), pTerm); + } + + @Override + public BitvectorFormula encapsulateBitvector(Long pTerm) { + assert getFormulaType(pTerm).isBitvectorType(); + cleanupReferences(); + return storePhantomReference(new Z3BitvectorLegacyFormula(getEnv(), pTerm), pTerm); + } + + @Override + protected FloatingPointFormula encapsulateFloatingPoint(Long pTerm) { + assert getFormulaType(pTerm).isFloatingPointType(); + cleanupReferences(); + return storePhantomReference(new Z3FloatingPointLegacyFormula(getEnv(), pTerm), pTerm); + } + + @Override + protected FloatingPointRoundingModeFormula encapsulateRoundingMode(Long pTerm) { + assert getFormulaType(pTerm).isFloatingPointRoundingModeType(); + cleanupReferences(); + return storePhantomReference( + new Z3FloatingPointRoundingModeLegacyFormula(getEnv(), pTerm), pTerm); + } + + @Override + protected StringFormula encapsulateString(Long pTerm) { + assert getFormulaType(pTerm).isStringType() + : String.format( + "Term %s has unexpected type %s.", + Native.astToString(getEnv(), pTerm), + Native.sortToString(getEnv(), Native.getSort(getEnv(), pTerm))); + cleanupReferences(); + return storePhantomReference(new Z3StringLegacyFormula(getEnv(), pTerm), pTerm); + } + + @Override + protected RegexFormula encapsulateRegex(Long pTerm) { + assert getFormulaType(pTerm).isRegexType() + : String.format( + "Term %s has unexpected type %s.", + Native.astToString(getEnv(), pTerm), + Native.sortToString(getEnv(), Native.getSort(getEnv(), pTerm))); + cleanupReferences(); + return storePhantomReference(new Z3RegexLegacyFormula(getEnv(), pTerm), pTerm); + } + + @Override + protected EnumerationFormula encapsulateEnumeration(Long pTerm) { + assert getFormulaType(pTerm).isEnumerationType() + : String.format( + "Term %s has unexpected type %s.", + Native.astToString(getEnv(), pTerm), + Native.sortToString(getEnv(), Native.getSort(getEnv(), pTerm))); + cleanupReferences(); + return storePhantomReference(new Z3EnumerationLegacyFormula(getEnv(), pTerm), pTerm); + } + + @Override + public Long getArrayType(Long pIndexType, Long pElementType) { + Long allocatedArraySort = allocatedArraySorts.get(pIndexType, pElementType); + if (allocatedArraySort == null) { + allocatedArraySort = Native.mkArraySort(getEnv(), pIndexType, pElementType); + Native.incRef(getEnv(), allocatedArraySort); + allocatedArraySorts.put(pIndexType, pElementType, allocatedArraySort); + } + return allocatedArraySort; + } + + @Override + protected FloatingPointRoundingMode getRoundingMode(Long f) { + checkArgument( + Native.getSortKind(environment, Native.getSort(environment, f)) + == Z3_sort_kind.Z3_ROUNDING_MODE_SORT.toInt(), + "Expected a floating point rounding mode, but got %s", + Native.astToString(environment, f)); + + int roundingModeOp = Native.getDeclKind(environment, Native.getAppDecl(environment, f)); + switch (Z3_decl_kind.fromInt(roundingModeOp)) { + case Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN: + return FloatingPointRoundingMode.NEAREST_TIES_TO_EVEN; + case Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY: + return FloatingPointRoundingMode.NEAREST_TIES_AWAY; + case Z3_OP_FPA_RM_TOWARD_POSITIVE: + return FloatingPointRoundingMode.TOWARD_POSITIVE; + case Z3_OP_FPA_RM_TOWARD_NEGATIVE: + return FloatingPointRoundingMode.TOWARD_NEGATIVE; + case Z3_OP_FPA_RM_TOWARD_ZERO: + return FloatingPointRoundingMode.TOWARD_ZERO; + default: + throw new IllegalArgumentException( + "Cannot get rounding mode for Z3 operator: " + roundingModeOp); + } + } + + @Override + public Long getBitvectorType(int pBitwidth) { + checkArgument(pBitwidth > 0, "Cannot use bitvector type with size %s", pBitwidth); + long bvSort = Native.mkBvSort(getEnv(), pBitwidth); + Native.incRef(getEnv(), Native.sortToAst(getEnv(), bvSort)); + return bvSort; + } + + @Override + public Long getFloatingPointType(FloatingPointType type) { + long fpSort = Native.mkFpaSort(getEnv(), type.getExponentSize(), type.getMantissaSize() + 1); + Native.incRef(getEnv(), Native.sortToAst(getEnv(), fpSort)); + return fpSort; + } + + private static final class Z3AstReference extends PhantomReference { + private final long z3Ast; + private @Nullable Z3AstReference prev; + private @Nullable Z3AstReference next; + + // To generate dummy head and tail nodes + private Z3AstReference() { + this(null, null, 0); + } + + private Z3AstReference( + Z3LegacyFormula referent, ReferenceQueue q, long z3Ast) { + super(referent, q); + this.z3Ast = z3Ast; + } + + private void insert(Z3AstReference ref) { + assert next != null; + ref.prev = this; + ref.next = this.next; + ref.next.prev = ref; + this.next = ref; + } + + private void cleanup(long environment) { + Native.decRef(environment, z3Ast); + assert (prev != null && next != null); + prev.next = next; + next.prev = prev; + } + } + + private T storePhantomReference(T out, long pTerm) { + if (usePhantomReferences) { + referenceListHead.insert(new Z3AstReference(out, referenceQueue, pTerm)); + } + return out; + } + + private void cleanupReferences() { + if (!usePhantomReferences) { + return; + } + cleanupTimer.start(); + try { + Z3AstReference ref; + while ((ref = (Z3AstReference) referenceQueue.poll()) != null) { + ref.cleanup(environment); + } + } finally { + cleanupTimer.stop(); + } + } + + private String getAppName(long f) { + long funcDecl = Native.getAppDecl(environment, f); + long symbol = Native.getDeclName(environment, funcDecl); + return symbolToString(symbol); + } + + @SuppressWarnings("deprecation") + @Override + public R visit(FormulaVisitor visitor, final Formula formula, final Long f) { + switch (Z3_ast_kind.fromInt(Native.getAstKind(environment, f))) { + case Z3_NUMERAL_AST: + return visitor.visitConstant(formula, convertValue(f)); + case Z3_APP_AST: + int arity = Native.getAppNumArgs(environment, f); + int declKind = Native.getDeclKind(environment, Native.getAppDecl(environment, f)); + + if (arity == 0) { + // constants + Object value = Z3_CONSTANTS.get(declKind); + int sortKind = Native.getSortKind(environment, Native.getSort(environment, f)); + if (value != null) { + return visitor.visitConstant(formula, value); + + } else if (Z3_FP_CONSTANTS.contains(declKind)) { + return visitor.visitConstant(formula, convertValue(f)); + + // Rounding mode + } else if (declKind == Z3_decl_kind.Z3_OP_FPA_NUM.toInt()) { + return visitor.visitConstant(formula, convertValue(f)); + } else if (sortKind == Z3_sort_kind.Z3_ROUNDING_MODE_SORT.toInt()) { + return visitor.visitConstant(formula, getRoundingMode(f)); + + // string constant + } else if (declKind == Z3_decl_kind.Z3_OP_INTERNAL.toInt() + && sortKind == Z3_sort_kind.Z3_SEQ_SORT.toInt()) { + return visitor.visitConstant(formula, convertValue(f)); + + // Free variable + } else if (declKind == Z3_decl_kind.Z3_OP_UNINTERPRETED.toInt() + || declKind == Z3_decl_kind.Z3_OP_INTERNAL.toInt()) { + return visitor.visitFreeVariable(formula, getAppName(f)); + + // enumeration constant + } else if (declKind == Z3_decl_kind.Z3_OP_DT_CONSTRUCTOR.toInt()) { + return visitor.visitConstant(formula, convertValue(f)); + } // else: fall-through with a function application + + } else if (arity == 3) { + + // FP from BV + if (declKind == Z3_decl_kind.Z3_OP_FPA_FP.toInt()) { + final var signBv = Native.getAppArg(environment, f, 0); + final var expoBv = Native.getAppArg(environment, f, 1); + final var mantBv = Native.getAppArg(environment, f, 2); + if (isConstant(signBv) && isConstant(expoBv) && isConstant(mantBv)) { + return visitor.visitConstant(formula, convertValue(f)); + } + } + } + + // Function application with zero or more parameters + ImmutableList.Builder args = ImmutableList.builder(); + ImmutableList.Builder> argTypes = ImmutableList.builder(); + for (int i = 0; i < arity; i++) { + long arg = Native.getAppArg(environment, f, i); + FormulaType argumentType = getFormulaType(arg); + args.add(encapsulate(argumentType, arg)); + argTypes.add(argumentType); + } + return visitor.visitFunction( + formula, + args.build(), + FunctionDeclarationImpl.of( + getAppName(f), + getDeclarationKind(f), + argTypes.build(), + getFormulaType(f), + Native.getAppDecl(environment, f))); + case Z3_VAR_AST: + int deBruijnIdx = Native.getIndexValue(environment, f); + return visitor.visitBoundVariable(formula, deBruijnIdx); + case Z3_QUANTIFIER_AST: + return visitQuantifier(visitor, (BooleanFormula) formula, f); + case Z3_SORT_AST: + case Z3_FUNC_DECL_AST: + case Z3_UNKNOWN_AST: + default: + throw new UnsupportedOperationException( + "Input should be a formula AST, " + "got unexpected type instead"); + } + } + + protected String symbolToString(long symbol) { + switch (Z3_symbol_kind.fromInt(Native.getSymbolKind(environment, symbol))) { + case Z3_STRING_SYMBOL: + return Native.getSymbolString(environment, symbol); + case Z3_INT_SYMBOL: + + // Bound variable. + return "#" + Native.getSymbolInt(environment, symbol); + default: + throw new UnsupportedOperationException("Unexpected state"); + } + } + + private R visitQuantifier(FormulaVisitor pVisitor, BooleanFormula pFormula, Long pF) { + int numBound = Native.getQuantifierNumBound(environment, pF); + long[] freeVars = new long[numBound]; + for (int i = 0; i < numBound; i++) { + // The indices are reversed according to + // https://github.com/Z3Prover/z3/issues/7970#issuecomment-3407924907 + int inverseIndex = numBound - 1 - i; + long varName = Native.getQuantifierBoundName(environment, pF, inverseIndex); + long varSort = Native.getQuantifierBoundSort(environment, pF, inverseIndex); + long freeVar = Native.mkConst(environment, varName, varSort); + Native.incRef(environment, freeVar); + freeVars[i] = freeVar; + } + + // For every bound variable (de-Bruijn index from 0 to numBound), we replace the bound variable + // with its free version. + long body = Native.getQuantifierBody(environment, pF); + long substBody = Native.substituteVars(environment, body, numBound, freeVars); + + Quantifier q = + Native.isQuantifierForall(environment, pF) ? Quantifier.FORALL : Quantifier.EXISTS; + + return pVisitor.visitQuantifier( + pFormula, + q, + Longs.asList(freeVars).stream() + .map(this::encapsulateWithTypeOf) + .collect(Collectors.toList()), + encapsulateBoolean(substBody)); + } + + private FunctionDeclarationKind getDeclarationKind(long f) { + final int arity = Native.getArity(environment, Native.getAppDecl(environment, f)); + if (getAppName(f).equals("div0")) { + // Z3 segfaults in getDeclKind for this term (cf. https://github.com/Z3Prover/z3/issues/669) + return FunctionDeclarationKind.OTHER; + } + Z3_decl_kind decl = + Z3_decl_kind.fromInt(Native.getDeclKind(environment, Native.getAppDecl(environment, f))); + + assert (arity > 0) || (arity == 0 && decl == Z3_OP_DISTINCT) + : String.format( + "Unexpected arity '%s' for formula '%s' for handling a function application.", + arity, Native.astToString(environment, f)); + + switch (decl) { + case Z3_OP_AND: + return FunctionDeclarationKind.AND; + case Z3_OP_NOT: + return FunctionDeclarationKind.NOT; + case Z3_OP_OR: + return FunctionDeclarationKind.OR; + case Z3_OP_IFF: + return FunctionDeclarationKind.IFF; + case Z3_OP_ITE: + return FunctionDeclarationKind.ITE; + case Z3_OP_XOR: + return FunctionDeclarationKind.XOR; + case Z3_OP_DISTINCT: + return FunctionDeclarationKind.DISTINCT; + case Z3_OP_IMPLIES: + return FunctionDeclarationKind.IMPLIES; + + case Z3_OP_SUB: + return FunctionDeclarationKind.SUB; + case Z3_OP_ADD: + return FunctionDeclarationKind.ADD; + case Z3_OP_DIV: + return FunctionDeclarationKind.DIV; + case Z3_OP_MUL: + return FunctionDeclarationKind.MUL; + case Z3_OP_MOD: + return FunctionDeclarationKind.MODULO; + case Z3_OP_TO_INT: + return FunctionDeclarationKind.FLOOR; + case Z3_OP_TO_REAL: + return FunctionDeclarationKind.TO_REAL; + case Z3_OP_INT2BV: + return FunctionDeclarationKind.INT_TO_BV; + + case Z3_OP_UNINTERPRETED: + return FunctionDeclarationKind.UF; + + case Z3_OP_LT: + return FunctionDeclarationKind.LT; + case Z3_OP_LE: + return FunctionDeclarationKind.LTE; + case Z3_OP_GT: + return FunctionDeclarationKind.GT; + case Z3_OP_GE: + return FunctionDeclarationKind.GTE; + case Z3_OP_EQ: + return FunctionDeclarationKind.EQ; + + case Z3_OP_STORE: + return FunctionDeclarationKind.STORE; + case Z3_OP_SELECT: + return FunctionDeclarationKind.SELECT; + case Z3_OP_CONST_ARRAY: + return FunctionDeclarationKind.CONST; + + case Z3_OP_TRUE: + case Z3_OP_FALSE: + case Z3_OP_ANUM: + case Z3_OP_AGNUM: + throw new UnsupportedOperationException("Unexpected state: constants not expected"); + case Z3_OP_OEQ: + throw new UnsupportedOperationException("Unexpected state: not a proof"); + case Z3_OP_UMINUS: + return FunctionDeclarationKind.UMINUS; + case Z3_OP_IDIV: + + // TODO: different handling for integer division? + return FunctionDeclarationKind.DIV; + + case Z3_OP_EXTRACT: + return FunctionDeclarationKind.BV_EXTRACT; + case Z3_OP_CONCAT: + return FunctionDeclarationKind.BV_CONCAT; + case Z3_OP_BNOT: + return FunctionDeclarationKind.BV_NOT; + case Z3_OP_BNEG: + return FunctionDeclarationKind.BV_NEG; + case Z3_OP_BAND: + return FunctionDeclarationKind.BV_AND; + case Z3_OP_BOR: + return FunctionDeclarationKind.BV_OR; + case Z3_OP_BXOR: + return FunctionDeclarationKind.BV_XOR; + case Z3_OP_ULT: + return FunctionDeclarationKind.BV_ULT; + case Z3_OP_SLT: + return FunctionDeclarationKind.BV_SLT; + case Z3_OP_ULEQ: + return FunctionDeclarationKind.BV_ULE; + case Z3_OP_SLEQ: + return FunctionDeclarationKind.BV_SLE; + case Z3_OP_UGT: + return FunctionDeclarationKind.BV_UGT; + case Z3_OP_SGT: + return FunctionDeclarationKind.BV_SGT; + case Z3_OP_UGEQ: + return FunctionDeclarationKind.BV_UGE; + case Z3_OP_SGEQ: + return FunctionDeclarationKind.BV_SGE; + case Z3_OP_BADD: + return FunctionDeclarationKind.BV_ADD; + case Z3_OP_BSUB: + return FunctionDeclarationKind.BV_SUB; + case Z3_OP_BMUL: + return FunctionDeclarationKind.BV_MUL; + case Z3_OP_BUDIV: + case Z3_OP_BUDIV_I: // same as above, and divisor is non-zero + return FunctionDeclarationKind.BV_UDIV; + case Z3_OP_BSDIV: + case Z3_OP_BSDIV_I: // same as above, and divisor is non-zero + return FunctionDeclarationKind.BV_SDIV; + case Z3_OP_BUREM: + case Z3_OP_BUREM_I: // same as above, and divisor is non-zero + return FunctionDeclarationKind.BV_UREM; + case Z3_OP_BSREM: + case Z3_OP_BSREM_I: // same as above, and divisor is non-zero + return FunctionDeclarationKind.BV_SREM; + case Z3_OP_BSMOD: + case Z3_OP_BSMOD_I: // same as above, and divisor is non-zero + return FunctionDeclarationKind.BV_SMOD; + case Z3_OP_BSHL: + return FunctionDeclarationKind.BV_SHL; + case Z3_OP_BLSHR: + return FunctionDeclarationKind.BV_LSHR; + case Z3_OP_BASHR: + return FunctionDeclarationKind.BV_ASHR; + case Z3_OP_SIGN_EXT: + return FunctionDeclarationKind.BV_SIGN_EXTENSION; + case Z3_OP_ZERO_EXT: + return FunctionDeclarationKind.BV_ZERO_EXTENSION; + case Z3_OP_ROTATE_LEFT: + return FunctionDeclarationKind.BV_ROTATE_LEFT_BY_INT; + case Z3_OP_ROTATE_RIGHT: + return FunctionDeclarationKind.BV_ROTATE_RIGHT_BY_INT; + case Z3_OP_EXT_ROTATE_LEFT: + return FunctionDeclarationKind.BV_ROTATE_LEFT; + case Z3_OP_EXT_ROTATE_RIGHT: + return FunctionDeclarationKind.BV_ROTATE_RIGHT; + case Z3_OP_BV2INT: + return FunctionDeclarationKind.UBV_TO_INT; + + case Z3_OP_FPA_NEG: + return FunctionDeclarationKind.FP_NEG; + case Z3_OP_FPA_ABS: + return FunctionDeclarationKind.FP_ABS; + case Z3_OP_FPA_MAX: + return FunctionDeclarationKind.FP_MAX; + case Z3_OP_FPA_MIN: + return FunctionDeclarationKind.FP_MIN; + case Z3_OP_FPA_SQRT: + return FunctionDeclarationKind.FP_SQRT; + case Z3_OP_FPA_SUB: + return FunctionDeclarationKind.FP_SUB; + case Z3_OP_FPA_ADD: + return FunctionDeclarationKind.FP_ADD; + case Z3_OP_FPA_DIV: + return FunctionDeclarationKind.FP_DIV; + case Z3_OP_FPA_MUL: + return FunctionDeclarationKind.FP_MUL; + case Z3_OP_FPA_REM: + return FunctionDeclarationKind.FP_REM; + case Z3_OP_FPA_LT: + return FunctionDeclarationKind.FP_LT; + case Z3_OP_FPA_LE: + return FunctionDeclarationKind.FP_LE; + case Z3_OP_FPA_GE: + return FunctionDeclarationKind.FP_GE; + case Z3_OP_FPA_GT: + return FunctionDeclarationKind.FP_GT; + case Z3_OP_FPA_EQ: + return FunctionDeclarationKind.FP_EQ; + case Z3_OP_FPA_ROUND_TO_INTEGRAL: + return FunctionDeclarationKind.FP_ROUND_TO_INTEGRAL; + case Z3_OP_FPA_TO_FP_UNSIGNED: + return FunctionDeclarationKind.BV_UCASTTO_FP; + case Z3_OP_FPA_TO_SBV: + return FunctionDeclarationKind.FP_CASTTO_SBV; + case Z3_OP_FPA_TO_UBV: + return FunctionDeclarationKind.FP_CASTTO_UBV; + case Z3_OP_FPA_TO_IEEE_BV: + return FunctionDeclarationKind.FP_AS_IEEEBV; + case Z3_OP_FPA_TO_FP: + // use the last argument. other arguments can be part of rounding or casting. + long arg = Native.getAppArg(environment, f, Native.getAppNumArgs(environment, f) - 1); + Z3_sort_kind sortKind = + Z3_sort_kind.fromInt(Native.getSortKind(environment, Native.getSort(environment, arg))); + if (Z3_sort_kind.Z3_BV_SORT == sortKind) { + return FunctionDeclarationKind.BV_SCASTTO_FP; + } else { + return FunctionDeclarationKind.FP_CASTTO_FP; + } + case Z3_OP_FPA_IS_NAN: + return FunctionDeclarationKind.FP_IS_NAN; + case Z3_OP_FPA_IS_INF: + return FunctionDeclarationKind.FP_IS_INF; + case Z3_OP_FPA_IS_ZERO: + return FunctionDeclarationKind.FP_IS_ZERO; + case Z3_OP_FPA_IS_NEGATIVE: + return FunctionDeclarationKind.FP_IS_NEGATIVE; + case Z3_OP_FPA_IS_SUBNORMAL: + return FunctionDeclarationKind.FP_IS_SUBNORMAL; + case Z3_OP_FPA_IS_NORMAL: + return FunctionDeclarationKind.FP_IS_NORMAL; + + case Z3_OP_SEQ_CONCAT: + return FunctionDeclarationKind.STR_CONCAT; + case Z3_OP_SEQ_PREFIX: + return FunctionDeclarationKind.STR_PREFIX; + case Z3_OP_SEQ_SUFFIX: + return FunctionDeclarationKind.STR_SUFFIX; + case Z3_OP_SEQ_CONTAINS: + return FunctionDeclarationKind.STR_CONTAINS; + case Z3_OP_SEQ_EXTRACT: + return FunctionDeclarationKind.STR_SUBSTRING; + case Z3_OP_SEQ_REPLACE: + return FunctionDeclarationKind.STR_REPLACE; + case Z3_OP_SEQ_AT: + return FunctionDeclarationKind.STR_CHAR_AT; + case Z3_OP_SEQ_LENGTH: + return FunctionDeclarationKind.STR_LENGTH; + case Z3_OP_SEQ_INDEX: + return FunctionDeclarationKind.STR_INDEX_OF; + case Z3_OP_SEQ_TO_RE: + return FunctionDeclarationKind.STR_TO_RE; + case Z3_OP_SEQ_IN_RE: + return FunctionDeclarationKind.STR_IN_RE; + case Z3_OP_RE_PLUS: + return FunctionDeclarationKind.RE_PLUS; + case Z3_OP_RE_STAR: + return FunctionDeclarationKind.RE_STAR; + case Z3_OP_RE_OPTION: + return FunctionDeclarationKind.RE_OPTIONAL; + case Z3_OP_RE_CONCAT: + return FunctionDeclarationKind.RE_CONCAT; + case Z3_OP_RE_UNION: + return FunctionDeclarationKind.RE_UNION; + + default: + return FunctionDeclarationKind.OTHER; + } + } + + /** + * @param value Z3_ast + * @return Whether the value is a constant and can be passed to {@link #convertValue(Long)}. + */ + public boolean isConstant(long value) { + return Native.isNumeralAst(environment, value) + || Native.isAlgebraicNumber(environment, value) + || Native.isString(environment, value) + || isOP(environment, value, Z3_decl_kind.Z3_OP_FPA_FP) // FP from IEEE-BV + || isOP(environment, value, Z3_decl_kind.Z3_OP_TRUE) + || isOP(environment, value, Z3_decl_kind.Z3_OP_FALSE) + || isOP(environment, value, Z3_decl_kind.Z3_OP_DT_CONSTRUCTOR); // enumeration value + } + + /** + * @param value Z3_ast representing a constant value. + * @return {@link BigInteger} or {@link Double} or {@link Rational} or {@link Boolean} or {@link + * FloatingPointRoundingMode} or {@link String}. + */ + @Override + public Object convertValue(Long value) { + if (!isConstant(value)) { + return null; + } + + Native.incRef(environment, value); + + Object constantValue = + Z3_CONSTANTS.get(Native.getDeclKind(environment, Native.getAppDecl(environment, value))); + if (constantValue != null) { + return constantValue; + } + + try { + FormulaType type = getFormulaType(value); + if (type.isBooleanType()) { + return isOP(environment, value, Z3_decl_kind.Z3_OP_TRUE); + } else if (type.isIntegerType()) { + return new BigInteger(Native.getNumeralString(environment, value)); + } else if (type.isRationalType()) { + Rational ratValue = Rational.ofString(Native.getNumeralString(environment, value)); + return ratValue.isIntegral() ? ratValue.getNum() : ratValue; + } else if (type.isStringType()) { + String str = Native.getString(environment, value); + return AbstractStringFormulaManager.unescapeUnicodeForSmtlib(str); + } else if (type.isBitvectorType()) { + return new BigInteger(Native.getNumeralString(environment, value)); + } else if (type.isFloatingPointType()) { + return convertFloatingPoint((FloatingPointType) type, value); + } else if (type.isEnumerationType()) { + return Native.astToString(environment, value); + } else { + + // Explicitly crash on unknown type. + throw new IllegalArgumentException("Unexpected type encountered: " + type); + } + + } finally { + Native.decRef(environment, value); + } + } + + private FloatingPointNumber convertFloatingPoint(FloatingPointType pType, Long pValue) { + if (isOP(environment, pValue, Z3_decl_kind.Z3_OP_FPA_FP)) { + final var signBv = Native.getAppArg(environment, pValue, 0); + final var expoBv = Native.getAppArg(environment, pValue, 1); + final var mantBv = Native.getAppArg(environment, pValue, 2); + assert isConstant(signBv) && isConstant(expoBv) && isConstant(mantBv); + final var sign = Native.getNumeralString(environment, signBv); + assert "0".equals(sign) || "1".equals(sign); + final var expo = new BigInteger(Native.getNumeralString(environment, expoBv)); + final var mant = new BigInteger(Native.getNumeralString(environment, mantBv)); + return FloatingPointNumber.of( + Sign.of(sign.charAt(0) == '1'), + expo, + mant, + pType.getExponentSize(), + pType.getMantissaSize()); + + // } else if (Native.fpaIsNumeralInf(environment, pValue)) { + // // Floating Point Inf uses: + // // - an sign for posiive/negative infinity, + // // - "11..11" as exponent, + // // - "00..00" as mantissa. + // String sign = getSign(pValue).isNegative() ? "1" : "0"; + // return FloatingPointNumber.of( + // sign + "1".repeat(pType.getExponentSize()) + "0".repeat(pType.getMantissaSize()), + // pType.getExponentSize(), + // pType.getMantissaSize()); + // + // } else if (Native.fpaIsNumeralNan(environment, pValue)) { + // // TODO We are underspecified here and choose several bits on our own. + // // This is not sound, if we combine FP anf BV theory. + // // Floating Point NaN uses: + // // - an unspecified sign (we choose "0"), + // // - "11..11" as exponent, + // // - an unspecified mantissa (we choose all "1"). + // return FloatingPointNumber.of( + // "0" + "1".repeat(pType.getExponentSize()) + "1".repeat(pType.getMantissaSize()), + // pType.getExponentSize(), + // pType.getMantissaSize()); + + } else { + Sign sign = getSign(pValue); + var exponent = Native.fpaGetNumeralExponentString(environment, pValue); + var mantissa = Native.fpaGetNumeralSignificandString(environment, pValue); + return FloatingPointNumber.of( + sign, + new BigInteger(exponent), + new BigInteger(mantissa), + pType.getExponentSize(), + pType.getMantissaSize()); + } + } + + private Sign getSign(Long pValue) { + Native.IntPtr signPtr = new Native.IntPtr(); + Preconditions.checkState( + Native.fpaGetNumeralSign(environment, pValue, signPtr), "Sign is not a Boolean value"); + var sign = signPtr.value != 0; + return Sign.of(sign); + } + + @Override + public Long declareUFImpl(String pName, Long returnType, List pArgTypes) { + long symbol = Native.mkStringSymbol(environment, pName); + long[] sorts = Longs.toArray(pArgTypes); + long func = Native.mkFuncDecl(environment, symbol, sorts.length, sorts, returnType); + Native.incRef(environment, func); + symbolsToDeclarations.put(pName, func); + return func; + } + + @Override + public Long callFunctionImpl(Long declaration, List args) { + return Native.mkApp(environment, declaration, args.size(), Longs.toArray(args)); + } + + @Override + protected Long getBooleanVarDeclarationImpl(Long pLong) { + return Native.getAppDecl(getEnv(), pLong); + } + + /** returns, if the function of the expression is the given operation. */ + static boolean isOP(long z3context, long expr, Z3_decl_kind op) { + if (!Native.isApp(z3context, expr)) { + return false; + } + + long decl = Native.getAppDecl(z3context, expr); + return Native.getDeclKind(z3context, decl) == op.toInt(); + } + + /** + * Apply multiple tactics in sequence. + * + * @throws InterruptedException thrown by JNI code in case of termination request + * @throws SolverException thrown by JNI code in case of error + */ + public long applyTactics(long z3context, final Long pF, String... pTactics) + throws InterruptedException, SolverException { + long overallResult = pF; + for (String tactic : pTactics) { + overallResult = applyTactic(z3context, overallResult, tactic); + } + return overallResult; + } + + /** + * Apply tactic on a Z3_ast object, convert the result back to Z3_ast. + * + * @param z3context Z3_context + * @param tactic Z3 Tactic Name + * @param pF Z3_ast + * @return Z3_ast + * @throws InterruptedException If execution gets interrupted. + */ + public long applyTactic(long z3context, long pF, String tactic) + throws InterruptedException, SolverException { + long tacticObject = Native.mkTactic(z3context, tactic); + Native.tacticIncRef(z3context, tacticObject); + + long goal = Native.mkGoal(z3context, true, false, false); + Native.goalIncRef(z3context, goal); + Native.goalAssert(z3context, goal, pF); + + long result; + try { + result = Native.tacticApply(z3context, tacticObject, goal); + } catch (Z3Exception exp) { + throw handleZ3Exception(exp); + } + + try { + return applyResultToAST(z3context, result); + } finally { + Native.goalDecRef(z3context, goal); + Native.tacticDecRef(z3context, tacticObject); + } + } + + private long applyResultToAST(long z3context, long applyResult) { + int subgoalsCount = Native.applyResultGetNumSubgoals(z3context, applyResult); + long[] goalFormulas = new long[subgoalsCount]; + for (int i = 0; i < subgoalsCount; i++) { + long subgoal = Native.applyResultGetSubgoal(z3context, applyResult, i); + goalFormulas[i] = goalToAST(z3context, subgoal); + } + return goalFormulas.length == 1 + ? goalFormulas[0] + : Native.mkOr(z3context, goalFormulas.length, goalFormulas); + } + + private long goalToAST(long z3context, long goal) { + int subgoalFormulasCount = Native.goalSize(z3context, goal); + long[] subgoalFormulas = new long[subgoalFormulasCount]; + for (int k = 0; k < subgoalFormulasCount; k++) { + subgoalFormulas[k] = Native.goalFormula(z3context, goal, k); + } + return subgoalFormulas.length == 1 + ? subgoalFormulas[0] + : Native.mkAnd(z3context, subgoalFormulas.length, subgoalFormulas); + } + + /** Closing the context. */ + @SuppressWarnings("empty-statement") + public void forceClose() { + // Force clean all ASTs, even those which were not GC'd yet. + if (usePhantomReferences) { + Z3AstReference cur = referenceListHead.next; + assert cur != null; + while (cur.next != null) { + Native.decRef(environment, cur.z3Ast); + cur = cur.next; + } + Z3AstReference tail = cur; + // Bulk delete everything between head and tail + referenceListHead.next = tail; + tail.prev = referenceListHead; + + // Remove already enqueued references. + while (referenceQueue.poll() != null) { + // NOTE: Together with the above list deletion, this empty loop will guarantee that no more + // ast references are reachable by the GC making them all eligible for garbage collection + // and preventing them from getting enqueued into the reference queue in the future. + } + } + } + + /** + * get a previously created application declaration, or NULL if the symbol is + * unknown. + */ + @Nullable Long getKnownDeclaration(String symbolName) { + return symbolsToDeclarations.get(symbolName); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFormulaManager.java new file mode 100644 index 0000000000..e71c495e25 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyFormulaManager.java @@ -0,0 +1,245 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Longs; +import com.microsoft.z3legacy.Native; +import com.microsoft.z3legacy.Z3Exception; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.sosy_lab.java_smt.api.BooleanFormula; +import org.sosy_lab.java_smt.api.Formula; +import org.sosy_lab.java_smt.api.FormulaManager; +import org.sosy_lab.java_smt.api.FormulaType; +import org.sosy_lab.java_smt.api.SolverException; +import org.sosy_lab.java_smt.basicimpl.AbstractFormulaManager; + +final class Z3LegacyFormulaManager extends AbstractFormulaManager { + + private final Z3LegacyFormulaCreator formulaCreator; + + @SuppressWarnings("checkstyle:parameternumber") + Z3LegacyFormulaManager( + Z3LegacyFormulaCreator pFormulaCreator, + Z3LegacyUFManager pFunctionManager, + Z3LegacyBooleanFormulaManager pBooleanManager, + Z3LegacyIntegerFormulaManager pIntegerManager, + Z3LegacyRationalFormulaManager pRationalManager, + Z3LegacyBitvectorFormulaManager pBitpreciseManager, + Z3LegacyFloatingPointFormulaManager pFloatingPointManager, + Z3LegacyQuantifiedFormulaManager pQuantifiedManager, + Z3LegacyArrayFormulaManager pArrayManager, + Z3LegacyStringFormulaManager pStringManager, + Z3LegacyEnumerationFormulaManager pEnumerationManager) { + super( + pFormulaCreator, + pFunctionManager, + pBooleanManager, + pIntegerManager, + pRationalManager, + pBitpreciseManager, + pFloatingPointManager, + pQuantifiedManager, + pArrayManager, + null, + pStringManager, + pEnumerationManager); + formulaCreator = pFormulaCreator; + } + + @Override + public Long parseImpl(String str) throws IllegalArgumentException { + + // Z3 does not access the existing symbols on its own, + // but requires all symbols as part of the query. + // Thus, we track the used symbols on our own and give them to the parser call, if required. + // Later, we collect all symbols from the parsed query and + // define them again to have them tracked. + + final long env = getEnvironment(); + + // JavaSMT does currently not allow defining new sorts, future work? + long[] sortSymbols = new long[0]; + long[] sorts = new long[0]; + + // first step: lets try to parse the query directly, without additional information + List declSymbols = new ArrayList<>(); + List decls = new ArrayList<>(); + + long e = 0; + boolean finished = false; + while (!finished) { + try { + e = + Native.parseSmtlib2String( + env, + str, + sorts.length, + sortSymbols, + sorts, + declSymbols.size(), + Longs.toArray(declSymbols), + Longs.toArray(decls)); + finished = true; + + } catch (Z3Exception nested) { + // get the missing symbol and restart the parsing with them + Pattern pattern = + Pattern.compile( + "\\(error \"line \\d+ column \\d+: unknown constant" + + " (?.*?)\\s?(?\\(.*\\))?\\s?\\\"\\)\\n"); + Matcher matcher = pattern.matcher(nested.getMessage()); + if (matcher.matches()) { + String missingSymbol = matcher.group(1); + Long appDecl = formulaCreator.getKnownDeclaration(missingSymbol); + if (appDecl != null) { // if the symbol is known, then use it + declSymbols.add(Native.mkStringSymbol(env, missingSymbol)); + decls.add(appDecl); + continue; // restart the parsing + } + } + throw new IllegalArgumentException(nested); + } + } + + Preconditions.checkState(e != 0, "parsing aborted"); + final int size = Native.astVectorSize(env, e); + Preconditions.checkState( + size == 1, "parsing expects exactly one asserted term, but got %s terms", size); + final long term = Native.astVectorGet(env, e, 0); + + // last step: all parsed symbols need to be declared again to have them tracked in the creator. + declareAllSymbols(term); + + return term; + } + + @SuppressWarnings("CheckReturnValue") + private void declareAllSymbols(final long term) { + final long env = getEnvironment(); + final Map symbols = formulaCreator.extractVariablesAndUFs(term, true); + for (Map.Entry symbol : symbols.entrySet()) { + long sym = symbol.getValue(); + String name = symbol.getKey(); + assert Native.isApp(env, sym); + int arity = Native.getAppNumArgs(env, sym); + if (arity == 0) { // constants + formulaCreator.makeVariable(Native.getSort(env, sym), name); + } else { + ImmutableList.Builder argTypes = ImmutableList.builder(); + for (int j = 0; j < arity; j++) { + argTypes.add(Native.getSort(env, Native.getAppArg(env, sym, j))); + } + formulaCreator.declareUFImpl(name, Native.getSort(env, sym), argTypes.build()); + } + } + } + + @Override + protected BooleanFormula applyQELightImpl(BooleanFormula pF) + throws InterruptedException, SolverException { + return applyTacticImpl(pF, "qe-light"); + } + + @Override + protected BooleanFormula applyCNFImpl(BooleanFormula pF) + throws InterruptedException, SolverException { + return applyTacticImpl(pF, "tseitin-cnf"); + } + + @Override + protected BooleanFormula applyNNFImpl(BooleanFormula pF) + throws InterruptedException, SolverException { + return applyTacticImpl(pF, "nnf"); + } + + private BooleanFormula applyTacticImpl(BooleanFormula pF, String tacticName) + throws InterruptedException, SolverException { + long out = + formulaCreator.applyTactic(getFormulaCreator().getEnv(), extractInfo(pF), tacticName); + return formulaCreator.encapsulateBoolean(out); + } + + @Override + public String dumpFormulaImpl(final Long expr) { + assert getFormulaCreator().getFormulaType(expr) == FormulaType.BooleanType + : "Only BooleanFormulas may be dumped"; + + // Serializing a solver is the simplest way to dump a formula in Z3, + // cf https://github.com/Z3Prover/z3/issues/397 + long z3solver = Native.mkSolver(getEnvironment()); + Native.solverIncRef(getEnvironment(), z3solver); + Native.solverAssert(getEnvironment(), z3solver, expr); + String serialized = Native.solverToString(getEnvironment(), z3solver); + Native.solverDecRef(getEnvironment(), z3solver); + return serialized; + } + + @Override + protected Long simplify(Long pF) throws InterruptedException { + try { + try { + return Native.simplify(getFormulaCreator().getEnv(), pF); + } catch (Z3Exception exp) { + throw formulaCreator.handleZ3Exception(exp); + } + } catch (SolverException e) { + // ignore exception and return original formula AS-IS. + return pF; + } + } + + @Override + public T substitute( + final T f, final Map fromToMapping) { + long[] changeFrom = new long[fromToMapping.size()]; + long[] changeTo = new long[fromToMapping.size()]; + int idx = 0; + for (Map.Entry e : fromToMapping.entrySet()) { + changeFrom[idx] = extractInfo(e.getKey()); + changeTo[idx] = extractInfo(e.getValue()); + idx++; + } + FormulaType type = getFormulaType(f); + return getFormulaCreator() + .encapsulate( + type, + Native.substitute( + getFormulaCreator().getEnv(), + extractInfo(f), + fromToMapping.size(), + changeFrom, + changeTo)); + } + + @Override + public BooleanFormula translateFrom(BooleanFormula other, FormulaManager otherManager) { + if (otherManager instanceof Z3LegacyFormulaManager) { + long otherZ3Context = ((Z3LegacyFormulaManager) otherManager).getEnvironment(); + if (otherZ3Context == getEnvironment()) { + + // Same context. + return other; + } else { + + // Z3-to-Z3 translation. + long translatedAST = Native.translate(otherZ3Context, extractInfo(other), getEnvironment()); + return getFormulaCreator().encapsulateBoolean(translatedAST); + } + } + return super.translateFrom(other, otherManager); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyIntegerFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyIntegerFormulaManager.java new file mode 100644 index 0000000000..221d779cb7 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyIntegerFormulaManager.java @@ -0,0 +1,91 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.microsoft.z3legacy.Native; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import org.sosy_lab.java_smt.api.IntegerFormulaManager; +import org.sosy_lab.java_smt.api.NumeralFormula.IntegerFormula; + +class Z3LegacyIntegerFormulaManager + extends Z3LegacyNumeralFormulaManager + implements IntegerFormulaManager { + + Z3LegacyIntegerFormulaManager( + Z3LegacyFormulaCreator pCreator, NonLinearArithmetic pNonLinearArithmetic) { + super(pCreator, pNonLinearArithmetic); + } + + @Override + protected long getNumeralType() { + return getFormulaCreator().getIntegerType(); + } + + @Override + protected Long makeNumberImpl(double pNumber) { + return makeNumberImpl((long) pNumber); + } + + /** + * Creates an integer formula from a BigDecimal value. This method converts BigDecimal values to + * BigInteger by using {@link RoundingMode#FLOOR}. + */ + @Override + protected Long makeNumberImpl(BigDecimal pNumber) { + return makeNumberImpl(pNumber.setScale(0, RoundingMode.FLOOR).toBigInteger()); + } + + @Override + public Long modulo(Long pNumber1, Long pNumber2) { + return Native.mkMod(z3context, pNumber1, pNumber2); + } + + @Override + protected Long modularCongruence(Long pNumber1, Long pNumber2, long pModulo) { + long n = makeNumberImpl(pModulo); + Native.incRef(z3context, n); + try { + return modularCongruence0(pNumber1, pNumber2, makeNumberImpl(pModulo)); + } finally { + Native.decRef(z3context, n); + } + } + + @Override + protected Long modularCongruence(Long pNumber1, Long pNumber2, BigInteger pModulo) { + long n = makeNumberImpl(pModulo); + Native.incRef(z3context, n); + try { + return modularCongruence0(pNumber1, pNumber2, makeNumberImpl(pModulo)); + } finally { + Native.decRef(z3context, n); + } + } + + protected Long modularCongruence0(Long pNumber1, Long pNumber2, Long n) { + // ((_ divisible n) x) <==> (= x (* n (div x n))) + long x = subtract(pNumber1, pNumber2); + Native.incRef(z3context, x); + long div = Native.mkDiv(z3context, x, n); + Native.incRef(z3context, div); + long mul = Native.mkMul(z3context, 2, new long[] {n, div}); + Native.incRef(z3context, mul); + try { + return Native.mkEq(z3context, x, mul); + } finally { + Native.decRef(z3context, x); + Native.decRef(z3context, div); + Native.decRef(z3context, mul); + } + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyInterpolatingProver.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyInterpolatingProver.java new file mode 100644 index 0000000000..62b7ff4288 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyInterpolatingProver.java @@ -0,0 +1,281 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Preconditions; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.primitives.Longs; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.microsoft.z3legacy.Native; +import com.microsoft.z3legacy.Z3Exception; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.List; +import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.sosy_lab.common.ShutdownNotifier; +import org.sosy_lab.common.UniqueIdGenerator; +import org.sosy_lab.common.io.PathCounterTemplate; +import org.sosy_lab.java_smt.api.BooleanFormula; +import org.sosy_lab.java_smt.api.Formula; +import org.sosy_lab.java_smt.api.InterpolatingProverEnvironment; +import org.sosy_lab.java_smt.api.QuantifiedFormulaManager.Quantifier; +import org.sosy_lab.java_smt.api.SolverContext.ProverOptions; +import org.sosy_lab.java_smt.api.SolverException; +import org.sosy_lab.java_smt.api.visitors.DefaultFormulaVisitor; +import org.sosy_lab.java_smt.api.visitors.TraversalProcess; + +class Z3LegacyInterpolatingProver extends Z3LegacyAbstractProver + implements InterpolatingProverEnvironment { + + // The id is provided for the user, and must be globally unique for each formula used for + // interpolation. + private static final UniqueIdGenerator ID_GENERATOR = new UniqueIdGenerator(); + + Z3LegacyInterpolatingProver( + Z3LegacyFormulaCreator pCreator, + Z3LegacyFormulaManager pMgr, + Set pOptions, + @Nullable PathCounterTemplate pLogfile, + ShutdownNotifier pShutdownNotifier) { + super(pCreator, pMgr, pOptions, pLogfile, pShutdownNotifier); + } + + @CanIgnoreReturnValue + @Override + protected Long addConstraintImpl(BooleanFormula f) throws InterruptedException { + super.addConstraintImpl(f); + return (long) ID_GENERATOR.getFreshId(); + } + + @Override + @SuppressWarnings({"unchecked", "varargs"}) + public BooleanFormula getInterpolant(final Collection pFormulasOfA) + throws InterruptedException, SolverException { + Preconditions.checkState(!closed); + final Set assertedConstraints = getAssertedConstraintIds(); + checkArgument( + assertedConstraints.containsAll(pFormulasOfA), + "interpolation can only be done over previously asserted formulas."); + + Set formulasOfA = ImmutableSet.copyOf(pFormulasOfA); + + // calc difference: formulasOfB := assertedFormulas - formulasOfA + Set formulasOfB = + assertedConstraints.stream() + .filter(f -> !formulasOfA.contains(f)) + .collect(ImmutableSet.toImmutableSet()); + + // binary interpolant is a sequence interpolant of only 2 elements + return Iterables.getOnlyElement(getSeqInterpolants(ImmutableList.of(formulasOfA, formulasOfB))); + } + + @Override + public List getTreeInterpolants( + List> partitionedFormulas, int[] startOfSubTree) + throws InterruptedException, SolverException { + Preconditions.checkState(!closed); + assert InterpolatingProverEnvironment.checkTreeStructure( + partitionedFormulas.size(), startOfSubTree); + + final long[] conjunctionFormulas = buildConjunctions(partitionedFormulas); + final long[] interpolationFormulas = + buildFormulaTree(partitionedFormulas, startOfSubTree, conjunctionFormulas); + final long root = interpolationFormulas[interpolationFormulas.length - 1]; + + final long proof = Native.solverGetProof(z3context, z3solver); + Native.incRef(z3context, proof); + + final long interpolationResult = computeInterpolants(root, proof); + + // n partitions -> n-1 interpolants + // the given tree interpolants are sorted in post-order, + // so we only need to copy them + final List result = new ArrayList<>(); + for (int i = 0; i < partitionedFormulas.size() - 1; i++) { + result.add( + creator.encapsulateBoolean(Native.astVectorGet(z3context, interpolationResult, i))); + } + assert result.size() == startOfSubTree.length - 1; + + // cleanup + Native.decRef(z3context, proof); + for (long partition : conjunctionFormulas) { + Native.decRef(z3context, partition); + } + for (long partition : interpolationFormulas) { + Native.decRef(z3context, partition); + } + + checkInterpolantsForUnboundVariables(result); // Do this last after cleanup. + + return result; + } + + /** build a conjunction of each partition. */ + private long[] buildConjunctions(List> partitionedFormulas) { + final ImmutableMap assertedConstraints = getAssertedFormulasById(); + final long[] conjunctionFormulas = new long[partitionedFormulas.size()]; + for (int i = 0; i < partitionedFormulas.size(); i++) { + List formulas = + FluentIterable.from(partitionedFormulas.get(i)) + .transform(assertedConstraints::get) + .transform(creator::extractInfo) + .toList(); + long conjunction = Native.mkAnd(z3context, formulas.size(), Longs.toArray(formulas)); + Native.incRef(z3context, conjunction); + conjunctionFormulas[i] = conjunction; + } + return conjunctionFormulas; + } + + /** build tree of interpolation-points. */ + private long[] buildFormulaTree( + List> partitionedFormulas, + int[] startOfSubTree, + final long[] conjunctionFormulas) { + final long[] interpolationFormulas = new long[partitionedFormulas.size()]; + final Deque stack = new ArrayDeque<>(); + + int lastSubtree = -1; // subtree starts with 0. With -1<0 we start a new subtree. + for (int i = 0; i < startOfSubTree.length; i++) { + final int currentSubtree = startOfSubTree[i]; + final long conjunction; + if (currentSubtree > lastSubtree) { + // start of a new subtree -> first element has no children + conjunction = conjunctionFormulas[i]; + + } else { // if (currentSubtree <= lastSubtree) { + // merge-point in tree, several children at a node -> pop from stack and conjunct + final Deque children = new ArrayDeque<>(); + while (!stack.isEmpty() && currentSubtree <= stack.peek().getRootOfTree()) { + // adding at front is important for tree-structure! + children.addFirst(stack.pop().getInterpolationPoint()); + } + children.add(conjunctionFormulas[i]); // add the node itself + conjunction = Native.mkAnd(z3context, children.size(), Longs.toArray(children)); + } + + final long interpolationPoint; + if (i == startOfSubTree.length - 1) { + // the last node in the tree (=root) does not need the interpolation-point-flag + interpolationPoint = conjunction; + Preconditions.checkState(currentSubtree == 0, "subtree of root should start at 0."); + Preconditions.checkState(stack.isEmpty(), "root should be the last element in the stack."); + } else { + interpolationPoint = Native.mkInterpolant(z3context, conjunction); + } + + Native.incRef(z3context, interpolationPoint); + interpolationFormulas[i] = interpolationPoint; + stack.push(new Z3TreeInterpolant(currentSubtree, interpolationPoint)); + lastSubtree = currentSubtree; + } + + Preconditions.checkState( + stack.peek().getRootOfTree() == 0, "subtree of root should start at 0."); + long root = stack.pop().getInterpolationPoint(); + Preconditions.checkState( + root == interpolationFormulas[interpolationFormulas.length - 1], + "subtree of root should start at 0."); + Preconditions.checkState( + stack.isEmpty(), "root should have been the last element in the stack."); + + return interpolationFormulas; + } + + /** compute interpolants for the given tree of formulas and dump the interpolation problem. */ + private long computeInterpolants(final long root, final long proof) + throws SolverException, InterruptedException { + long interpolationResult; + try { + interpolationResult = + Native.getInterpolant( + z3context, + proof, // refutation of premises := proof + root, // last element is end of chain (root of tree), pattern := interpolation tree + Native.mkParams(z3context)); + } catch (Z3Exception e) { + if ("theory not supported by interpolation or bad proof".equals(e.getMessage())) { + throw new SolverException(e.getMessage(), e); + } + throw creator.handleZ3Exception(e); + } + return interpolationResult; + } + + /** + * Check whether any formula in a given list contains unbound variables. Z3 has the problem that + * it sometimes returns such invalid formulas as interpolants + * (https://github.com/Z3Prover/z3/issues/665). + */ + @SuppressWarnings("deprecation") + private void checkInterpolantsForUnboundVariables(List itps) + throws SolverException { + List unboundVariables = new ArrayList<>(1); + final DefaultFormulaVisitor unboundVariablesCollector = + new DefaultFormulaVisitor<>() { + @Override + public TraversalProcess visitBoundVariable(Formula f, int deBruijnIdx) { + unboundVariables.add(f); + return TraversalProcess.ABORT; + } + + @Override + public TraversalProcess visitQuantifier( + BooleanFormula pF, + Quantifier pQ, + List pBoundVariables, + BooleanFormula pBody) { + return TraversalProcess.SKIP; // bound variables in quantifiers are probably ok + } + + @Override + protected TraversalProcess visitDefault(org.sosy_lab.java_smt.api.Formula pF) { + return TraversalProcess.CONTINUE; + } + }; + + for (BooleanFormula itp : itps) { + creator.visitRecursively(unboundVariablesCollector, itp); + if (!unboundVariables.isEmpty()) { + throw new SolverException( + "Unbound variable " + unboundVariables.get(0) + " in interpolant " + itp); + } + } + } + + private static final class Z3TreeInterpolant { + private final int rootOfSubTree; + private final long interpolationPoint; + + private Z3TreeInterpolant(int pRootOfSubtree, long pInterpolationPoint) { + rootOfSubTree = pRootOfSubtree; + interpolationPoint = pInterpolationPoint; + } + + private int getRootOfTree() { + return rootOfSubTree; + } + + private long getInterpolationPoint() { + return interpolationPoint; + } + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyModel.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyModel.java new file mode 100644 index 0000000000..906bec948a --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyModel.java @@ -0,0 +1,411 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.base.Preconditions; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableList; +import com.microsoft.z3legacy.Native; +import com.microsoft.z3legacy.Native.LongPtr; +import com.microsoft.z3legacy.Z3Exception; +import com.microsoft.z3legacy.enumerations.Z3_decl_kind; +import com.microsoft.z3legacy.enumerations.Z3_sort_kind; +import com.microsoft.z3legacy.enumerations.Z3_symbol_kind; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.sosy_lab.java_smt.basicimpl.AbstractModel; +import org.sosy_lab.java_smt.basicimpl.AbstractProver; + +final class Z3LegacyModel extends AbstractModel { + + private final long model; + private final long z3context; + private static final Pattern Z3_IRRELEVANT_MODEL_TERM_PATTERN = Pattern.compile(".*![0-9]+"); + + private final Z3LegacyFormulaCreator z3creator; + + Z3LegacyModel( + AbstractProver pProver, long z3context, long z3model, Z3LegacyFormulaCreator pCreator) { + super(pProver, pCreator); + Native.modelIncRef(z3context, z3model); + model = z3model; + this.z3context = z3context; + z3creator = pCreator; + } + + @Override + public ImmutableList asList() { + Preconditions.checkState(!isClosed()); + ImmutableList.Builder out = ImmutableList.builder(); + + try { + // Iterate through constants. + for (int constIdx = 0; constIdx < Native.modelGetNumConsts(z3context, model); constIdx++) { + long keyDecl = Native.modelGetConstDecl(z3context, model, constIdx); + Native.incRef(z3context, keyDecl); + out.addAll(getConstAssignments(keyDecl)); + Native.decRef(z3context, keyDecl); + } + + // Iterate through function applications. + for (int funcIdx = 0; funcIdx < Native.modelGetNumFuncs(z3context, model); funcIdx++) { + long funcDecl = Native.modelGetFuncDecl(z3context, model, funcIdx); + Native.incRef(z3context, funcDecl); + if (!isInternalSymbol(funcDecl)) { + String functionName = z3creator.symbolToString(Native.getDeclName(z3context, funcDecl)); + out.addAll(getFunctionAssignments(funcDecl, funcDecl, functionName)); + } + Native.decRef(z3context, funcDecl); + } + } catch (Z3Exception e) { + throw z3creator.handleZ3ExceptionAsRuntimeException(e); + } + + return out.build(); + } + + /** + * The symbol "!" is part of temporary symbols used for quantified formulas or aliases. This + * method is only a heuristic, because the user can also create a symbol containing "!". + */ + private boolean isInternalSymbol(long funcDecl) { + switch (Z3_decl_kind.fromInt(Native.getDeclKind(z3context, funcDecl))) { + case Z3_OP_SELECT: + case Z3_OP_ARRAY_EXT: + return true; + default: + long declName = Native.getDeclName(z3context, funcDecl); + Z3_symbol_kind kind = Z3_symbol_kind.fromInt(Native.getSymbolKind(z3context, declName)); + if (kind == Z3_symbol_kind.Z3_INT_SYMBOL) { // bound variables + return true; + } + return Z3_IRRELEVANT_MODEL_TERM_PATTERN + .matcher(z3creator.symbolToString(declName)) + .matches(); + } + } + + /** + * @return ValueAssignments for a constant declaration in the model + */ + private Collection getConstAssignments(long keyDecl) { + Preconditions.checkArgument( + Native.getArity(z3context, keyDecl) == 0, "Declaration is not a constant"); + + long var = Native.mkApp(z3context, keyDecl, 0, new long[] {}); + long value = Native.modelGetConstInterp(z3context, model, keyDecl); + checkReturnValue(value, keyDecl); + Native.incRef(z3context, value); + + long equality = Native.mkEq(z3context, var, value); + Native.incRef(z3context, equality); + + try { + long symbol = Native.getDeclName(z3context, keyDecl); + if (z3creator.isConstant(value)) { + return ImmutableList.of( + new ValueAssignment( + z3creator.encapsulateWithTypeOf(var), + z3creator.encapsulateWithTypeOf(value), + z3creator.encapsulateBoolean(equality), + z3creator.symbolToString(symbol), + z3creator.convertValue(value), + ImmutableList.of())); + + } else if (Native.isAsArray(z3context, value)) { + long arrayFormula = Native.mkConst(z3context, symbol, Native.getSort(z3context, value)); + Native.incRef(z3context, arrayFormula); + return getArrayAssignments(symbol, arrayFormula, value, ImmutableList.of()); + + } else if (Native.isApp(z3context, value)) { + long decl = Native.getAppDecl(z3context, value); + Native.incRef(z3context, decl); + Z3_sort_kind sortKind = + Z3_sort_kind.fromInt(Native.getSortKind(z3context, Native.getSort(z3context, value))); + assert sortKind == Z3_sort_kind.Z3_ARRAY_SORT : "unexpected sort: " + sortKind; + + try { + return getConstantArrayAssignment(symbol, value, decl); + } finally { + Native.decRef(z3context, decl); + } + } + + throw new UnsupportedOperationException( + "unknown model evaluation: " + Native.astToString(z3context, value)); + + } finally { + // cleanup outdated data + Native.decRef(z3context, value); + } + } + + /** unrolls an constant array assignment. */ + private Collection getConstantArrayAssignment( + long arraySymbol, long value, long decl) { + + long arrayFormula = Native.mkConst(z3context, arraySymbol, Native.getSort(z3context, value)); + Native.incRef(z3context, arrayFormula); + + Z3_decl_kind declKind = Z3_decl_kind.fromInt(Native.getDeclKind(z3context, decl)); + int numArgs = Native.getAppNumArgs(z3context, value); + + List out = new ArrayList<>(); + + // avoid doubled ValueAssignments for cases like "(store (store ARR 0 0) 0 1)", + // where we could (but should not!) unroll the array into "[ARR[0]=1, ARR[0]=1]" + Set indizes = new HashSet<>(); + + // unroll an array... + while (Z3_decl_kind.Z3_OP_STORE == declKind) { + assert numArgs == 3; + + long arrayIndex = Native.getAppArg(z3context, value, 1); + Native.incRef(z3context, arrayIndex); + + if (indizes.add(arrayIndex)) { + long select = Native.mkSelect(z3context, arrayFormula, arrayIndex); + Native.incRef(z3context, select); + + long nestedValue = Native.getAppArg(z3context, value, 2); + Native.incRef(z3context, nestedValue); + + long equality = Native.mkEq(z3context, select, nestedValue); + Native.incRef(z3context, equality); + + out.add( + new ValueAssignment( + z3creator.encapsulateWithTypeOf(select), + z3creator.encapsulateWithTypeOf(nestedValue), + z3creator.encapsulateBoolean(equality), + z3creator.symbolToString(arraySymbol), + z3creator.convertValue(nestedValue), + ImmutableList.of(evaluateImpl(arrayIndex)))); + } + + Native.decRef(z3context, arrayIndex); + + // recursive unrolling + value = Native.getAppArg(z3context, value, 0); + decl = Native.getAppDecl(z3context, value); + declKind = Z3_decl_kind.fromInt(Native.getDeclKind(z3context, decl)); + numArgs = Native.getAppNumArgs(z3context, value); + } + + // ...until its end + if (Z3_decl_kind.Z3_OP_CONST_ARRAY == declKind) { + assert numArgs == 1; + // We have an array of zeros (=default value) as "((as const (Array Int Int)) 0)". + // There is no way of modeling a whole array, thus we ignore it. + } + + return out; + } + + /** + * Z3 models an array as an uninterpreted function. + * + * @return a list of assignments {@code a[1]=0; a[2]=0; a[5]=0}. + */ + private Collection getArrayAssignments( + long arraySymbol, long arrayFormula, long value, List upperIndices) { + long evalDecl = Native.getAsArrayFuncDecl(z3context, value); + Native.incRef(z3context, evalDecl); + long interp = Native.modelGetFuncInterp(z3context, model, evalDecl); + checkReturnValue(interp, evalDecl); + Native.funcInterpIncRef(z3context, interp); + + Collection lst = new ArrayList<>(); + + // get all assignments for the array + int numInterpretations = Native.funcInterpGetNumEntries(z3context, interp); + for (int interpIdx = 0; interpIdx < numInterpretations; interpIdx++) { + long entry = Native.funcInterpGetEntry(z3context, interp, interpIdx); + Native.funcEntryIncRef(z3context, entry); + long arrayValue = Native.funcEntryGetValue(z3context, entry); + Native.incRef(z3context, arrayValue); + int noArgs = Native.funcEntryGetNumArgs(z3context, entry); + assert noArgs == 1 : "array modelled as UF is expected to have only one parameter, aka index"; + long arrayIndex = Native.funcEntryGetArg(z3context, entry, 0); + Native.incRef(z3context, arrayIndex); + long select = Native.mkSelect(z3context, arrayFormula, arrayIndex); + Native.incRef(z3context, select); + + List innerIndices = new ArrayList<>(upperIndices); + innerIndices.add(evaluateImpl(arrayIndex)); + + if (z3creator.isConstant(arrayValue)) { + + long equality = Native.mkEq(z3context, select, arrayValue); + Native.incRef(z3context, equality); + + lst.add( + new ValueAssignment( + z3creator.encapsulateWithTypeOf(select), + z3creator.encapsulateWithTypeOf(arrayValue), + z3creator.encapsulateBoolean(equality), + z3creator.symbolToString(arraySymbol), + z3creator.convertValue(arrayValue), + innerIndices)); + + } else if (Native.isAsArray(z3context, arrayValue)) { + lst.addAll(getArrayAssignments(arraySymbol, select, arrayValue, innerIndices)); + } + + Native.decRef(z3context, arrayIndex); + Native.funcEntryDecRef(z3context, entry); + } + + Native.funcInterpDecRef(z3context, interp); + Native.decRef(z3context, evalDecl); + return lst; + } + + private void checkReturnValue(long value, long funcDecl) { + if (value == 0) { + throw new VerifyException( + "Z3 unexpectedly claims that the value of " + + Native.funcDeclToString(z3context, funcDecl) + + " does not matter in model."); + } + } + + /** + * get all ValueAssignments for a function declaration in the model. + * + * @param evalDecl function declaration where the evaluation comes from + * @param funcDecl function declaration where the function name comes from + * @param functionName the name of the funcDecl + */ + private Collection getFunctionAssignments( + long evalDecl, long funcDecl, String functionName) { + long interp = Native.modelGetFuncInterp(z3context, model, evalDecl); + checkReturnValue(interp, evalDecl); + Native.funcInterpIncRef(z3context, interp); + + List lst = new ArrayList<>(); + + int numInterpretations = Native.funcInterpGetNumEntries(z3context, interp); + + if (numInterpretations == 0) { + // we found an alias in the model, follow the alias + long elseInterp = Native.funcInterpGetElse(z3context, interp); + Native.incRef(z3context, elseInterp); + long aliasDecl = Native.getAppDecl(z3context, elseInterp); + Native.incRef(z3context, aliasDecl); + if (isInternalSymbol(aliasDecl)) { + lst.addAll(getFunctionAssignments(aliasDecl, funcDecl, functionName)); + // TODO Can we guarantee termination of this recursive call? + // A chain of aliases should end after several steps. + } else { + // ignore functionDeclarations like "ite", "and",... + } + Native.decRef(z3context, aliasDecl); + Native.decRef(z3context, elseInterp); + + } else { + for (int interpIdx = 0; interpIdx < numInterpretations; interpIdx++) { + long entry = Native.funcInterpGetEntry(z3context, interp, interpIdx); + Native.funcEntryIncRef(z3context, entry); + long entryValue = Native.funcEntryGetValue(z3context, entry); + if (z3creator.isConstant(entryValue)) { + lst.add(getFunctionAssignment(functionName, funcDecl, entry, entryValue)); + } else { + // ignore values of complex types, e.g. Arrays + } + Native.funcEntryDecRef(z3context, entry); + } + } + + Native.funcInterpDecRef(z3context, interp); + return lst; + } + + /** + * @return ValueAssignment for an entry (one evaluation) of an uninterpreted function in the + * model. + */ + private ValueAssignment getFunctionAssignment( + String functionName, long funcDecl, long entry, long entryValue) { + Object value = z3creator.convertValue(entryValue); + int numArgs = Native.funcEntryGetNumArgs(z3context, entry); + long[] args = new long[numArgs]; + List argumentInterpretation = new ArrayList<>(); + + for (int k = 0; k < numArgs; k++) { + long arg = Native.funcEntryGetArg(z3context, entry, k); + Native.incRef(z3context, arg); + // indirect assignments + assert !Native.isAsArray(z3context, arg) + : String.format( + "unexpected array-reference '%s' as evaluation of a UF parameter for UF '%s'.", + Native.astToString(z3context, arg), Native.funcDeclToString(z3context, funcDecl)); + argumentInterpretation.add(z3creator.convertValue(arg)); + args[k] = arg; + } + + long func = Native.mkApp(z3context, funcDecl, args.length, args); + // Clean up memory. + for (long arg : args) { + Native.decRef(z3context, arg); + } + + long equality = Native.mkEq(z3context, func, entryValue); + Native.incRef(z3context, equality); + + return new ValueAssignment( + z3creator.encapsulateWithTypeOf(func), + z3creator.encapsulateWithTypeOf(entryValue), + z3creator.encapsulateBoolean(equality), + functionName, + value, + argumentInterpretation); + } + + @Override + public String toString() { + Preconditions.checkState(!isClosed()); + return Native.modelToString(z3context, model); + } + + @Override + public void close() { + if (!isClosed()) { + Native.modelDecRef(z3context, model); + } + super.close(); + } + + @Override + @Nullable + protected Long evalImpl(Long formula) { + LongPtr resultPtr = new LongPtr(); + boolean satisfiableModel; + try { + satisfiableModel = Native.modelEval(z3context, model, formula, false, resultPtr); + } catch (Z3Exception e) { + throw z3creator.handleZ3ExceptionAsRuntimeException(e); + } + Preconditions.checkState(satisfiableModel); + if (resultPtr.value == 0) { + // unknown evaluation + return null; + } else { + Native.incRef(z3context, resultPtr.value); + return resultPtr.value; + } + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyNumeralFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyNumeralFormulaManager.java new file mode 100644 index 0000000000..49fbd57156 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyNumeralFormulaManager.java @@ -0,0 +1,134 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.collect.Lists; +import com.google.common.primitives.Longs; +import com.microsoft.z3legacy.Native; +import java.math.BigInteger; +import java.util.List; +import org.sosy_lab.java_smt.api.NumeralFormula; +import org.sosy_lab.java_smt.basicimpl.AbstractNumeralFormulaManager; + +@SuppressWarnings("ClassTypeParameterName") +abstract class Z3LegacyNumeralFormulaManager< + ParamFormulaType extends NumeralFormula, ResultFormulaType extends NumeralFormula> + extends AbstractNumeralFormulaManager< + Long, Long, Long, ParamFormulaType, ResultFormulaType, Long> { + + protected final long z3context; + + Z3LegacyNumeralFormulaManager( + Z3LegacyFormulaCreator pCreator, NonLinearArithmetic pNonLinearArithmetic) { + super(pCreator, pNonLinearArithmetic); + this.z3context = pCreator.getEnv(); + } + + protected abstract long getNumeralType(); + + @Override + protected boolean isNumeral(Long val) { + return Native.isNumeralAst(z3context, val); + } + + @Override + protected Long makeNumberImpl(long i) { + long sort = getNumeralType(); + return Native.mkInt64(z3context, i, sort); + } + + @Override + protected Long makeNumberImpl(BigInteger pI) { + return makeNumberImpl(pI.toString()); + } + + @Override + protected Long makeNumberImpl(String pI) { + long sort = getNumeralType(); + return Native.mkNumeral(z3context, pI, sort); + } + + @Override + protected Long makeVariableImpl(String varName) { + long type = getNumeralType(); + return getFormulaCreator().makeVariable(type, varName); + } + + @Override + protected Long negate(Long pNumber) { + long sort = Native.getSort(z3context, pNumber); + long minusOne = Native.mkInt(z3context, -1, sort); + return Native.mkMul(z3context, 2, new long[] {minusOne, pNumber}); + } + + @Override + protected Long add(Long pNumber1, Long pNumber2) { + return Native.mkAdd(z3context, 2, new long[] {pNumber1, pNumber2}); + } + + @Override + protected Long sumImpl(List operands) { + if (operands.isEmpty()) { + return makeNumberImpl(0); + } else { + return Native.mkAdd(z3context, operands.size(), Longs.toArray(operands)); + } + } + + @Override + protected Long subtract(Long pNumber1, Long pNumber2) { + return Native.mkSub(z3context, 2, new long[] {pNumber1, pNumber2}); + } + + @Override + protected Long divide(Long pNumber1, Long pNumber2) { + return Native.mkDiv(z3context, pNumber1, pNumber2); + } + + @Override + protected Long multiply(Long pNumber1, Long pNumber2) { + return Native.mkMul(z3context, 2, new long[] {pNumber1, pNumber2}); + } + + @Override + protected Long equal(Long pNumber1, Long pNumber2) { + return Native.mkEq(z3context, pNumber1, pNumber2); + } + + @Override + protected Long distinctImpl(List pNumbers) { + if (pNumbers.isEmpty()) { + return Native.mkTrue(z3context); + } + pNumbers = Lists.transform(pNumbers, this::toType); + return Native.mkDistinct(z3context, pNumbers.size(), Longs.toArray(pNumbers)); + } + + @Override + protected Long greaterThan(Long pNumber1, Long pNumber2) { + return Native.mkGt(z3context, pNumber1, pNumber2); + } + + @Override + protected Long greaterOrEquals(Long pNumber1, Long pNumber2) { + return Native.mkGe(z3context, pNumber1, pNumber2); + } + + @Override + protected Long lessThan(Long pNumber1, Long pNumber2) { + return Native.mkLt(z3context, pNumber1, pNumber2); + } + + @Override + protected Long lessOrEquals(Long pNumber1, Long pNumber2) { + return Native.mkLe(z3context, pNumber1, pNumber2); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyQuantifiedFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyQuantifiedFormulaManager.java new file mode 100644 index 0000000000..2f41f63452 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyQuantifiedFormulaManager.java @@ -0,0 +1,63 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.primitives.Longs; +import com.microsoft.z3legacy.Native; +import java.util.List; +import org.sosy_lab.java_smt.api.SolverException; +import org.sosy_lab.java_smt.basicimpl.AbstractQuantifiedFormulaManager; + +class Z3LegacyQuantifiedFormulaManager + extends AbstractQuantifiedFormulaManager { + + private final long z3context; + private final Z3LegacyFormulaCreator z3FormulaCreator; + + Z3LegacyQuantifiedFormulaManager(Z3LegacyFormulaCreator creator) { + super(creator); + this.z3context = creator.getEnv(); + z3FormulaCreator = creator; + } + + @Override + public Long mkQuantifier(Quantifier q, List pVariables, Long pBody) { + checkArgument( + !pVariables.isEmpty(), + "Missing variables for quantifier '%s' and body '%s'.", + q, + Native.astToString(z3context, pBody)); + + return Native.mkQuantifierConst( + z3context, + q == Quantifier.FORALL, + 0, + pVariables.size(), + Longs.toArray(pVariables), + 0, + new long[0], + pBody); + } + + @Override + protected Long eliminateQuantifiers(Long pExtractInfo) + throws SolverException, InterruptedException { + // It is recommended (personal communication with Nikolaj Bjorner) + // to run "qe-light" before "qe". + // "qe" does not perform a "qe-light" as a preprocessing on its own! + + // One might want to run the tactic "ctx-solver-simplify" on the result. + + return z3FormulaCreator.applyTactics(z3context, pExtractInfo, "qe-light", "qe"); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyRationalFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyRationalFormulaManager.java new file mode 100644 index 0000000000..eb4848d7d5 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyRationalFormulaManager.java @@ -0,0 +1,58 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.microsoft.z3legacy.Native; +import java.math.BigDecimal; +import org.sosy_lab.java_smt.api.NumeralFormula; +import org.sosy_lab.java_smt.api.NumeralFormula.RationalFormula; +import org.sosy_lab.java_smt.api.RationalFormulaManager; + +class Z3LegacyRationalFormulaManager + extends Z3LegacyNumeralFormulaManager + implements RationalFormulaManager { + + Z3LegacyRationalFormulaManager( + Z3LegacyFormulaCreator pCreator, NonLinearArithmetic pNonLinearArithmetic) { + super(pCreator, pNonLinearArithmetic); + } + + @Override + protected long getNumeralType() { + return getFormulaCreator().getRationalType(); + } + + @Override + protected Long makeNumberImpl(double pNumber) { + return makeNumberImpl(Double.toString(pNumber)); + } + + @Override + protected Long makeNumberImpl(BigDecimal pNumber) { + return makeNumberImpl(pNumber.toPlainString()); + } + + @Override + protected Long toType(Long pNumber) { + if (Native.getSort(z3context, pNumber) == formulaCreator.getIntegerType()) { + long castedNumber = Native.mkInt2real(z3context, pNumber); + Native.incRef(z3context, castedNumber); + return castedNumber; + } else { + return pNumber; + } + } + + @Override + protected Long floor(Long pNumber) { + return Native.mkReal2int(z3context, pNumber); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacySolverContext.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacySolverContext.java new file mode 100644 index 0000000000..d61ccae983 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacySolverContext.java @@ -0,0 +1,277 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.microsoft.z3legacy.Native; +import com.microsoft.z3legacy.enumerations.Z3_ast_print_mode; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.logging.Level; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.sosy_lab.common.ShutdownNotifier; +import org.sosy_lab.common.ShutdownNotifier.ShutdownRequestListener; +import org.sosy_lab.common.configuration.Configuration; +import org.sosy_lab.common.configuration.FileOption; +import org.sosy_lab.common.configuration.InvalidConfigurationException; +import org.sosy_lab.common.configuration.Option; +import org.sosy_lab.common.configuration.Options; +import org.sosy_lab.common.io.IO; +import org.sosy_lab.common.io.PathCounterTemplate; +import org.sosy_lab.common.log.LogManager; +import org.sosy_lab.java_smt.SolverContextFactory.Solvers; +import org.sosy_lab.java_smt.api.FloatingPointRoundingMode; +import org.sosy_lab.java_smt.api.InterpolatingProverEnvironment; +import org.sosy_lab.java_smt.api.OptimizationProverEnvironment; +import org.sosy_lab.java_smt.api.ProverEnvironment; +import org.sosy_lab.java_smt.basicimpl.AbstractNumeralFormulaManager.NonLinearArithmetic; +import org.sosy_lab.java_smt.basicimpl.AbstractSolverContext; + +public final class Z3LegacySolverContext extends AbstractSolverContext { + + private final ShutdownRequestListener interruptListener; + private final ShutdownNotifier shutdownNotifier; + private final ExtraOptions extraOptions; + private final Z3LegacyFormulaCreator creator; + private final Z3LegacyFormulaManager manager; + private final AtomicBoolean closed = new AtomicBoolean(false); + + @Options(prefix = "solver.z3.legacy") + private static class ExtraOptions { + + @Option( + secure = true, + description = "Require proofs from SMT solver", + values = {"true", "false"}) + boolean requireProofs = true; + + @Option( + secure = true, + description = + "Activate replayable logging in Z3." + + " The log can be given as an input to the solver and replayed.") + @FileOption(FileOption.Type.OUTPUT_FILE) + @Nullable Path log = null; + + private final @Nullable PathCounterTemplate logfile; + + private final int randomSeed; + + ExtraOptions(Configuration config, @Nullable PathCounterTemplate pLogfile, int pRandomSeed) + throws InvalidConfigurationException { + config.inject(this); + randomSeed = pRandomSeed; + logfile = pLogfile; + } + } + + private Z3LegacySolverContext( + Z3LegacyFormulaCreator pFormulaCreator, + ShutdownNotifier pShutdownNotifier, + Z3LegacyFormulaManager pManager, + ExtraOptions pExtraOptions) { + super(pManager); + + creator = pFormulaCreator; + interruptListener = reason -> Native.interrupt(pFormulaCreator.getEnv()); + shutdownNotifier = pShutdownNotifier; + pShutdownNotifier.register(interruptListener); + manager = pManager; + extraOptions = pExtraOptions; + } + + @SuppressWarnings("ParameterNumber") + public static synchronized Z3LegacySolverContext create( + LogManager logger, + Configuration config, + ShutdownNotifier pShutdownNotifier, + @Nullable PathCounterTemplate solverLogfile, + long randomSeed, + FloatingPointRoundingMode pFloatingPointRoundingMode, + NonLinearArithmetic pNonLinearArithmetic, + Consumer pLoader) + throws InvalidConfigurationException { + ExtraOptions extraOptions = new ExtraOptions(config, solverLogfile, (int) randomSeed); + + // We need to load z3legacy in addition to z3javalegacy, + // because Z3's own class only loads the latter, + // but it will fail to find the former if not loaded previously. + // We load both libraries here to have all the loading in one place. + loadLibrariesWithFallback( + pLoader, + ImmutableList.of("z3legacy", "z3javalegacy"), + ImmutableList.of("libz3legacy", "libz3javalegacy")); + + // disable Z3's own loading mechanism, see com.microsoft.z3legacy.Native + System.setProperty("z3.skipLibraryLoad", "true"); + + if (extraOptions.log != null) { + Path absolutePath = extraOptions.log.toAbsolutePath(); + try { + // Z3 segfaults if it cannot write to the file, thus we write once first + IO.writeFile(absolutePath, StandardCharsets.US_ASCII, ""); + Native.openLog(absolutePath.toString()); + } catch (IOException e) { + logger.logUserException(Level.WARNING, e, "Cannot write Z3 log file"); + } + } + + long cfg = Native.mkConfig(); + if (extraOptions.requireProofs) { + Native.setParamValue(cfg, "PROOF", "true"); + } + // Native.globalParamSet("smt.random_seed", String.valueOf(randomSeed)); + // Native.globalParamSet("model.compact", "false"); + + final long context = Native.mkContextRc(cfg); + Native.delConfig(cfg); + + long boolSort = Native.mkBoolSort(context); + Native.incRef(context, Native.sortToAst(context, boolSort)); + long integerSort = Native.mkIntSort(context); + Native.incRef(context, Native.sortToAst(context, integerSort)); + long realSort = Native.mkRealSort(context); + Native.incRef(context, Native.sortToAst(context, realSort)); + long stringSort = Native.mkStringSort(context); + Native.incRef(context, Native.sortToAst(context, stringSort)); + long regexSort = Native.mkReSort(context, stringSort); + Native.incRef(context, Native.sortToAst(context, regexSort)); + + // The string representations of Z3s formulas should be in SMTLib2, + // otherwise serialization wouldn't work. + Native.setAstPrintMode(context, Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT.toInt()); + + Z3LegacyFormulaCreator creator = + new Z3LegacyFormulaCreator( + context, + boolSort, + integerSort, + realSort, + stringSort, + regexSort, + config, + pShutdownNotifier); + + // Create managers + Z3LegacyUFManager functionTheory = new Z3LegacyUFManager(creator); + Z3LegacyBooleanFormulaManager booleanTheory = new Z3LegacyBooleanFormulaManager(creator); + Z3LegacyIntegerFormulaManager integerTheory = + new Z3LegacyIntegerFormulaManager(creator, pNonLinearArithmetic); + Z3LegacyRationalFormulaManager rationalTheory = + new Z3LegacyRationalFormulaManager(creator, pNonLinearArithmetic); + Z3LegacyBitvectorFormulaManager bitvectorTheory = + new Z3LegacyBitvectorFormulaManager(creator, booleanTheory); + Z3LegacyFloatingPointFormulaManager floatingPointTheory = + new Z3LegacyFloatingPointFormulaManager(creator, pFloatingPointRoundingMode); + Z3LegacyQuantifiedFormulaManager quantifierManager = + new Z3LegacyQuantifiedFormulaManager(creator); + Z3LegacyArrayFormulaManager arrayManager = new Z3LegacyArrayFormulaManager(creator); + Z3LegacyStringFormulaManager stringTheory = new Z3LegacyStringFormulaManager(creator); + Z3LegacyEnumerationFormulaManager enumTheory = new Z3LegacyEnumerationFormulaManager(creator); + + // Set the custom error handling + // which will throw Z3Exception + // instead of exit(1). + Native.setInternalErrorHandler(context); + + Z3LegacyFormulaManager manager = + new Z3LegacyFormulaManager( + creator, + functionTheory, + booleanTheory, + integerTheory, + rationalTheory, + bitvectorTheory, + floatingPointTheory, + quantifierManager, + arrayManager, + stringTheory, + enumTheory); + return new Z3LegacySolverContext(creator, pShutdownNotifier, manager, extraOptions); + } + + @Override + protected ProverEnvironment newProverEnvironment0(Set options) { + Preconditions.checkState(!closed.get(), "solver context is already closed"); + final ImmutableMap solverOptions = + ImmutableMap.builder() + .put(":random-seed", extraOptions.randomSeed) + .put( + ":model", + options.contains(ProverOptions.GENERATE_MODELS) + || options.contains(ProverOptions.GENERATE_ALL_SAT)) + .put( + ":unsat_core", + options.contains(ProverOptions.GENERATE_UNSAT_CORE) + || options.contains(ProverOptions.GENERATE_UNSAT_CORE_OVER_ASSUMPTIONS)) + .buildOrThrow(); + return new Z3LegacyTheoremProver( + creator, manager, options, solverOptions, extraOptions.logfile, shutdownNotifier); + } + + @Override + protected InterpolatingProverEnvironment newProverEnvironmentWithInterpolation0( + Set options) { + Preconditions.checkState(!closed.get(), "solver context is already closed"); + long z3context = creator.getEnv(); + long z3params = Native.mkParams(z3context); + Native.paramsIncRef(z3context, z3params); + Native.paramsSetBool(z3context, z3params, Native.mkStringSymbol(z3context, ":model"), true); + Native.paramsSetBool(z3context, z3params, Native.mkStringSymbol(z3context, "PROOF"), true); + Native.paramsSetBool( + z3context, z3params, Native.mkStringSymbol(z3context, ":unsat_core"), false); + return new Z3LegacyInterpolatingProver( + creator, manager, options, extraOptions.logfile, shutdownNotifier); + } + + @Override + public OptimizationProverEnvironment newOptimizationProverEnvironment0( + Set options) { + throw new UnsupportedOperationException("z3legacy does not support optimization"); + } + + @Override + public String getVersion() { + Native.IntPtr major = new Native.IntPtr(); + Native.IntPtr minor = new Native.IntPtr(); + Native.IntPtr build = new Native.IntPtr(); + Native.IntPtr revision = new Native.IntPtr(); + Native.getVersion(major, minor, build, revision); + return "Z3 " + major.value + "." + minor.value + "." + build.value + "." + revision.value; + } + + @Override + public Solvers getSolverName() { + return Solvers.Z3; + } + + @Override + public void close() { + if (!closed.getAndSet(true)) { + long context = creator.getEnv(); + creator.forceClose(); + shutdownNotifier.unregister(interruptListener); + Native.closeLog(); + Native.delContext(context); + } + } + + @Override + protected boolean supportsAssumptionSolving() { + return true; + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyStringFormulaManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyStringFormulaManager.java new file mode 100644 index 0000000000..492ab12804 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyStringFormulaManager.java @@ -0,0 +1,193 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.base.Preconditions; +import com.google.common.primitives.Longs; +import com.microsoft.z3legacy.Native; +import java.util.List; +import org.sosy_lab.java_smt.basicimpl.AbstractStringFormulaManager; + +class Z3LegacyStringFormulaManager extends AbstractStringFormulaManager { + + private final long z3context; + + Z3LegacyStringFormulaManager(Z3LegacyFormulaCreator creator) { + super(creator); + z3context = creator.getEnv(); + } + + @Override + protected Long makeStringImpl(String pValue) { + return Native.mkString(z3context, escapeUnicodeForSmtlib(pValue)); + } + + @Override + protected Long makeVariableImpl(String varName) { + long type = getFormulaCreator().getStringType(); + return getFormulaCreator().makeVariable(type, varName); + } + + @Override + protected Long equal(Long pParam1, Long pParam2) { + return Native.mkEq(z3context, pParam1, pParam2); + } + + @Override + protected Long greaterThan(Long pParam1, Long pParam2) { + return lessThan(pParam2, pParam1); + } + + @Override + protected Long greaterOrEquals(Long pParam1, Long pParam2) { + return lessOrEquals(pParam2, pParam1); + } + + @Override + protected Long lessThan(Long pParam1, Long pParam2) { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long lessOrEquals(Long pParam1, Long pParam2) { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long length(Long pParam) { + return Native.mkSeqLength(z3context, pParam); + } + + @Override + protected Long concatImpl(List parts) { + Preconditions.checkArgument(!parts.isEmpty()); + return Native.mkSeqConcat(z3context, parts.size(), Longs.toArray(parts)); + } + + @Override + protected Long prefix(Long prefix, Long str) { + return Native.mkSeqPrefix(z3context, prefix, str); + } + + @Override + protected Long suffix(Long suffix, Long str) { + return Native.mkSeqSuffix(z3context, suffix, str); + } + + @Override + protected Long in(Long str, Long regex) { + return Native.mkSeqInRe(z3context, str, regex); + } + + @Override + protected Long contains(Long str, Long part) { + return Native.mkSeqContains(z3context, str, part); + } + + @Override + protected Long indexOf(Long str, Long part, Long startIndex) { + return Native.mkSeqIndex(z3context, str, part, startIndex); + } + + @Override + protected Long charAt(Long str, Long index) { + return Native.mkSeqAt(z3context, str, index); + } + + @Override + protected Long substring(Long str, Long index, Long length) { + return Native.mkSeqExtract(z3context, str, index, length); + } + + @Override + protected Long replace(Long fullStr, Long target, Long replacement) { + return Native.mkSeqReplace(z3context, fullStr, target, replacement); + } + + @Override + protected Long replaceAll(Long fullStr, Long target, Long replacement) { + throw new UnsupportedOperationException(); + } + + @Override + protected Long makeRegexImpl(String value) { + Long str = makeStringImpl(value); + return Native.mkSeqToRe(z3context, str); + } + + @Override + protected Long noneImpl() { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long allImpl() { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long allCharImpl() { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long range(Long start, Long end) { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long concatRegexImpl(List parts) { + if (parts.isEmpty()) { + return noneImpl(); + } + return Native.mkReConcat(z3context, parts.size(), Longs.toArray(parts)); + } + + @Override + protected Long union(Long pParam1, Long pParam2) { + return Native.mkReUnion(z3context, 2, new long[] {pParam1, pParam2}); + } + + @Override + protected Long intersection(Long pParam1, Long pParam2) { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long closure(Long pParam) { + return Native.mkReStar(z3context, pParam); + } + + @Override + protected Long complement(Long pParam) { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long toIntegerFormula(Long pParam) { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long toStringFormula(Long pParam) { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long toCodePoint(Long pParam) { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } + + @Override + protected Long fromCodePoint(Long pParam) { + throw new UnsupportedOperationException("Not supported in legacy z3"); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyTheoremProver.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyTheoremProver.java new file mode 100644 index 0000000000..4d50886575 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyTheoremProver.java @@ -0,0 +1,41 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import com.google.common.collect.ImmutableMap; +import com.microsoft.z3legacy.Native; +import java.util.Map.Entry; +import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.sosy_lab.common.ShutdownNotifier; +import org.sosy_lab.common.io.PathCounterTemplate; +import org.sosy_lab.java_smt.api.ProverEnvironment; +import org.sosy_lab.java_smt.api.SolverContext.ProverOptions; + +class Z3LegacyTheoremProver extends Z3LegacyAbstractProver implements ProverEnvironment { + + Z3LegacyTheoremProver( + Z3LegacyFormulaCreator creator, + Z3LegacyFormulaManager pMgr, + Set pOptions, + ImmutableMap pSolverOptions, + @Nullable PathCounterTemplate pLogfile, + ShutdownNotifier pShutdownNotifier) { + super(creator, pMgr, pOptions, pLogfile, pShutdownNotifier); + long z3params = Native.mkParams(z3context); + Native.paramsIncRef(z3context, z3params); + for (Entry entry : pSolverOptions.entrySet()) { + addParameter(z3params, entry.getKey(), entry.getValue()); + } + Native.solverSetParams(z3context, z3solver, z3params); + Native.paramsDecRef(z3context, z3params); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyUFManager.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyUFManager.java new file mode 100644 index 0000000000..ef3c5655c8 --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/Z3LegacyUFManager.java @@ -0,0 +1,20 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.sosy_lab.java_smt.solvers.z3legacy; + +import org.sosy_lab.java_smt.basicimpl.AbstractUFManager; + +class Z3LegacyUFManager extends AbstractUFManager { + + Z3LegacyUFManager(Z3LegacyFormulaCreator creator) { + super(creator); + } +} diff --git a/src/org/sosy_lab/java_smt/solvers/z3legacy/package-info.java b/src/org/sosy_lab/java_smt/solvers/z3legacy/package-info.java new file mode 100644 index 0000000000..22b04f381c --- /dev/null +++ b/src/org/sosy_lab/java_smt/solvers/z3legacy/package-info.java @@ -0,0 +1,16 @@ +/* + * This file is part of JavaSMT, + * an API wrapper for a collection of SMT solvers: + * https://github.com/sosy-lab/java-smt + * + * SPDX-FileCopyrightText: 2025 Dirk Beyer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** Interface to the SMT solver Z3 (based on the native C API and JNI). */ +@com.google.errorprone.annotations.CheckReturnValue +@javax.annotation.ParametersAreNonnullByDefault +@org.sosy_lab.common.annotations.FieldsAreNonnullByDefault +@org.sosy_lab.common.annotations.ReturnValuesAreNonnullByDefault +package org.sosy_lab.java_smt.solvers.z3legacy; diff --git a/src/org/sosy_lab/java_smt/test/InterpolatingProverTest.java b/src/org/sosy_lab/java_smt/test/InterpolatingProverTest.java index 9287b00f67..42878d8b74 100644 --- a/src/org/sosy_lab/java_smt/test/InterpolatingProverTest.java +++ b/src/org/sosy_lab/java_smt/test/InterpolatingProverTest.java @@ -164,10 +164,10 @@ public void binaryInterpolationWithConstantFalse() throws SolverException, InterruptedException { InterpolatingProverEnvironment stack = newEnvironmentForTest(); - // build formula: [false, false] - BooleanFormula A = bmgr.makeBoolean(false); - BooleanFormula B = bmgr.makeBoolean(false); - BooleanFormula C = bmgr.makeBoolean(false); + // build formula stack: [false, false, false] + BooleanFormula A = bmgr.makeFalse(); + BooleanFormula B = bmgr.makeFalse(); + BooleanFormula C = bmgr.makeFalse(); T TA = stack.push(A); T TB = stack.push(B); @@ -175,22 +175,26 @@ public void binaryInterpolationWithConstantFalse() assertThat(stack).isUnsatisfiable(); - assertThat(stack.getInterpolant(ImmutableList.of())).isEqualTo(bmgr.makeBoolean(true)); + assertThat(stack.getInterpolant(ImmutableList.of())).isEqualTo(bmgr.makeTrue()); + // some interpolant needs to be FALSE, however, it can be at arbitrary position. + BooleanFormula expectedInterpolant = bmgr.makeFalse(); + if (solverToUse() == Solvers.Z3_WITH_INTERPOLATION) { + expectedInterpolant = bmgr.makeTrue(); // LegacyZ3 has an issue here. + } assertThat( ImmutableList.of( stack.getInterpolant(ImmutableList.of(TA)), stack.getInterpolant(ImmutableList.of(TB)), stack.getInterpolant(ImmutableList.of(TC)))) - .contains(bmgr.makeBoolean(false)); + .contains(expectedInterpolant); assertThat( ImmutableList.of( stack.getInterpolant(ImmutableList.of(TA, TB)), stack.getInterpolant(ImmutableList.of(TB, TC)), stack.getInterpolant(ImmutableList.of(TC, TA)))) - .contains(bmgr.makeBoolean(false)); - assertThat(stack.getInterpolant(ImmutableList.of(TA, TB, TC))) - .isEqualTo(bmgr.makeBoolean(false)); + .contains(expectedInterpolant); + assertThat(stack.getInterpolant(ImmutableList.of(TA, TB, TC))).isEqualTo(bmgr.makeFalse()); stack.close(); } @@ -249,7 +253,7 @@ private void requireTreeItp() { assume() .withMessage("Solver does not support tree-interpolation.") .that(solver) - .isAnyOf(Solvers.SMTINTERPOL, Solvers.PRINCESS); + .isAnyOf(Solvers.SMTINTERPOL, Solvers.PRINCESS, Solvers.Z3_WITH_INTERPOLATION); } @Test @@ -1149,6 +1153,9 @@ public void testInvalidToken() throws InterruptedException, SolverException case SMTINTERPOL: p3 = "some string"; break; + case Z3_WITH_INTERPOLATION: + p3 = 12350; + break; default: p3 = null; // unexpected solver for interpolation } diff --git a/src/org/sosy_lab/java_smt/test/ModelEvaluationTest.java b/src/org/sosy_lab/java_smt/test/ModelEvaluationTest.java index 8974490f3d..523b8e8028 100644 --- a/src/org/sosy_lab/java_smt/test/ModelEvaluationTest.java +++ b/src/org/sosy_lab/java_smt/test/ModelEvaluationTest.java @@ -167,23 +167,27 @@ public void testGetStringsEvaluation() throws SolverException, InterruptedExcept Lists.newArrayList("hello WORLD"), Lists.newArrayList(smgr.makeString("hello WORLD"))); - // Unicode - evaluateInModel( - smgr.equal( - smgr.makeVariable("x"), - smgr.makeString( - AbstractStringFormulaManager.unescapeUnicodeForSmtlib( - "hello æ@€ \u1234 \\u{4321}"))), - smgr.makeVariable("x"), - Lists.newArrayList("hello \u00e6@\u20ac \u1234 \u4321"), - Lists.newArrayList(smgr.makeString("hello \u00e6@\u20ac \u1234 \u4321"))); - - // invalid Unicode escape sequences (should be treated as normal characters) - evaluateInModel( - smgr.equal(smgr.makeVariable("x"), smgr.makeString("\\u")), - smgr.makeVariable("x"), - Lists.newArrayList("\\u"), - Lists.newArrayList(smgr.makeString("\\u"))); + if (solverToUse() != Solvers.Z3_WITH_INTERPOLATION) { + // LegacyZ3 has issues with Unicode and they will not be fixed. + + // Unicode + evaluateInModel( + smgr.equal( + smgr.makeVariable("x"), + smgr.makeString( + AbstractStringFormulaManager.unescapeUnicodeForSmtlib( + "hello æ@€ \u1234 \\u{4321}"))), + smgr.makeVariable("x"), + Lists.newArrayList("hello \u00e6@\u20ac \u1234 \u4321"), + Lists.newArrayList(smgr.makeString("hello \u00e6@\u20ac \u1234 \u4321"))); + + // invalid Unicode escape sequences (should be treated as normal characters) + evaluateInModel( + smgr.equal(smgr.makeVariable("x"), smgr.makeString("\\u")), + smgr.makeVariable("x"), + Lists.newArrayList("\\u"), + Lists.newArrayList(smgr.makeString("\\u"))); + } // foreign variable: x vs y evaluateInModel( diff --git a/src/org/sosy_lab/java_smt/test/ModelTest.java b/src/org/sosy_lab/java_smt/test/ModelTest.java index 199d64fd51..3fd95479f9 100644 --- a/src/org/sosy_lab/java_smt/test/ModelTest.java +++ b/src/org/sosy_lab/java_smt/test/ModelTest.java @@ -92,9 +92,14 @@ public class ModelTest extends SolverBasedTest0.ParameterizedSolverBasedTest0 { FormulaType.getArrayType(getBitvectorTypeWithSize(32), getBitvectorTypeWithSize(32)); private static final ImmutableList SOLVERS_WITH_PARTIAL_MODEL = - ImmutableList.of(Solvers.Z3); + ImmutableList.of(Solvers.Z3, Solvers.Z3_WITH_INTERPOLATION); private static final ImmutableList SOLVERS_WITH_PERSISTENT_MODEL = - ImmutableList.of(Solvers.MATHSAT5, Solvers.Z3, Solvers.SMTINTERPOL, Solvers.YICES2); + ImmutableList.of( + Solvers.MATHSAT5, + Solvers.Z3, + Solvers.Z3_WITH_INTERPOLATION, + Solvers.SMTINTERPOL, + Solvers.YICES2); /** Model value for irrelevant variable. */ private BigInteger defaultValue; diff --git a/src/org/sosy_lab/java_smt/test/QuantifierManagerTest.java b/src/org/sosy_lab/java_smt/test/QuantifierManagerTest.java index 109e9f5883..1e20511714 100644 --- a/src/org/sosy_lab/java_smt/test/QuantifierManagerTest.java +++ b/src/org/sosy_lab/java_smt/test/QuantifierManagerTest.java @@ -1050,6 +1050,10 @@ public void testForallRestrictedRangeWithoutConclusiveSolvers() public void testExistsBasicStringTheorie() throws SolverException, InterruptedException { requireStrings(); requireIntegers(); + assume() + .withMessage("Solver %s does not support the complete theory of strings", solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); // exists var ("a" < var < "c") & length var == 1 -> var == "b" StringFormula stringA = smgr.makeString("a"); @@ -1071,6 +1075,11 @@ public void testForallBasicStringTheorie() throws SolverException, InterruptedEx requireStrings(); requireIntegers(); + assume() + .withMessage("Solver %s does not support the complete theory of strings", solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); + // forall var ("a" < var < "c") & length var == 1 StringFormula stringA = smgr.makeString("a"); StringFormula stringC = smgr.makeString("c"); diff --git a/src/org/sosy_lab/java_smt/test/SolverBasedTest0.java b/src/org/sosy_lab/java_smt/test/SolverBasedTest0.java index 901452e8d7..b30caafc7b 100644 --- a/src/org/sosy_lab/java_smt/test/SolverBasedTest0.java +++ b/src/org/sosy_lab/java_smt/test/SolverBasedTest0.java @@ -227,6 +227,12 @@ protected final void requireRationalFloor() { .withMessage("Solver %s does not support floor for rationals", solverToUse()) .that(solverToUse()) .isNotEqualTo(Solvers.OPENSMT); + assume() + .withMessage( + "Solver %s does not support floor for rationals (random segfaults on ARM64)", + solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); } /** Skip test if the solver does not support bitvectors. */ @@ -235,6 +241,10 @@ protected final void requireBitvectors() { .withMessage("Solver %s does not support the theory of bitvectors", solverToUse()) .that(bvmgr) .isNotNull(); + assume() + .withMessage("Solver %s does not support bitvectors for interpolation", solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); } protected final void requireBitvectorToInt() { @@ -298,6 +308,10 @@ protected final void requireFloats() { .withMessage("Solver %s does not support the theory of floats", solverToUse()) .that(fpmgr) .isNotNull(); + assume() + .withMessage("Solver %s does not support floats for interpolation", solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); } /** Skip test if the solver does not support strings. */ @@ -355,6 +369,13 @@ protected void requireParser() { .withMessage("Solver %s does not support parsing formulae", solverToUse()) .that(solverToUse()) .isNoneOf(Solvers.CVC4, Solvers.BOOLECTOR, Solvers.YICES2, Solvers.CVC5); + + assume() + .withMessage( + "Solver %s segfaults when parsing short queries or reports invalid length", + solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); } protected void requireArrayModel() { @@ -459,6 +480,7 @@ protected void evaluateInModel( if (eval != null) { switch (solverToUse()) { case Z3: + case Z3_WITH_INTERPOLATION: // ignore, Z3 provides arbitrary values break; case BOOLECTOR: diff --git a/src/org/sosy_lab/java_smt/test/SolverConcurrencyTest.java b/src/org/sosy_lab/java_smt/test/SolverConcurrencyTest.java index 840d44941a..a23bf23fd9 100644 --- a/src/org/sosy_lab/java_smt/test/SolverConcurrencyTest.java +++ b/src/org/sosy_lab/java_smt/test/SolverConcurrencyTest.java @@ -137,6 +137,7 @@ private void requireConcurrentMultipleStackSupport() { Solvers.OPENSMT, Solvers.MATHSAT5, Solvers.Z3, + Solvers.Z3_WITH_INTERPOLATION, Solvers.PRINCESS, Solvers.YICES2, Solvers.CVC5); @@ -168,7 +169,8 @@ private void requireOptimization() { Solvers.CVC5, Solvers.YICES2, Solvers.BITWUZLA, - Solvers.OPENSMT); + Solvers.OPENSMT, + Solvers.Z3_WITH_INTERPOLATION); } /** diff --git a/src/org/sosy_lab/java_smt/test/SolverContextFactoryTest.java b/src/org/sosy_lab/java_smt/test/SolverContextFactoryTest.java index de1936d342..1aa1dafcd5 100644 --- a/src/org/sosy_lab/java_smt/test/SolverContextFactoryTest.java +++ b/src/org/sosy_lab/java_smt/test/SolverContextFactoryTest.java @@ -132,6 +132,8 @@ private boolean isSupportedOperatingSystemAndArchitecture() { || (IS_WINDOWS && !IS_ARCH_ARM64); case Z3: return (IS_LINUX && isSufficientVersionOfLibcxx("z3")) || IS_WINDOWS || IS_MAC; + case Z3_WITH_INTERPOLATION: + return IS_LINUX; default: throw new AssertionError("unexpected solver: " + solverToUse()); } @@ -225,9 +227,12 @@ private void checkVersion(SolverContext pContext) { String solverName = solverToUse().toString(); if (solverToUse() == Solvers.YICES2) { solverName = "YICES"; // remove the number "2" from the name + } else if (solverToUse() == Solvers.Z3_WITH_INTERPOLATION) { + solverName = "Z3"; } String optionalSuffix = "([A-Za-z0-9.,:_+\\-\\s()@]+)?"; // any string - String versionNumberRegex = "(version\\s)?\\d+\\.\\d+(\\.\\d+)?"; // 2-3 numbers with dots + String versionNumberRegex = + "(version\\s)?\\d+\\.\\d+(\\.\\d+)?(\\.\\d+)?"; // 2-4 numbers with dots if (solverToUse() == Solvers.PRINCESS) { versionNumberRegex = "\\d+-\\d+-\\d+"; // Princess uses date instead of version } diff --git a/src/org/sosy_lab/java_smt/test/SolverContextTest.java b/src/org/sosy_lab/java_smt/test/SolverContextTest.java index af349e5053..09bdbe05e4 100644 --- a/src/org/sosy_lab/java_smt/test/SolverContextTest.java +++ b/src/org/sosy_lab/java_smt/test/SolverContextTest.java @@ -94,7 +94,7 @@ public void testFormulaAccessAfterClose() { "Solver %s does not support to access formulae after closing the context", solverToUse()) .that(solverToUse()) - .isNotEqualTo(Solvers.Z3); + .isNoneOf(Solvers.Z3, Solvers.Z3_WITH_INTERPOLATION); assertThat(bmgr.isTrue(term)).isFalse(); assertThat(bmgr.isFalse(term)).isFalse(); diff --git a/src/org/sosy_lab/java_smt/test/SolverTheoriesTest.java b/src/org/sosy_lab/java_smt/test/SolverTheoriesTest.java index da3cf33e81..85c6d23c1e 100644 --- a/src/org/sosy_lab/java_smt/test/SolverTheoriesTest.java +++ b/src/org/sosy_lab/java_smt/test/SolverTheoriesTest.java @@ -542,7 +542,7 @@ public void testHardCongruence() throws SolverException, InterruptedException { IntegerFormula c = imgr.makeVariable("c"); List constraints = new ArrayList<>(); Random r = new Random(42); - int bitSize = 7; // difficulty + int bitSize = solverToUse() == Solvers.Z3_WITH_INTERPOLATION ? 5 : 7; // difficulty BigInteger prime1 = BigInteger.probablePrime(bitSize, r); BigInteger prime2 = BigInteger.probablePrime(bitSize + 1, r); BigInteger prime3 = BigInteger.probablePrime(bitSize + 2, r); diff --git a/src/org/sosy_lab/java_smt/test/SolverThreadLocalityTest.java b/src/org/sosy_lab/java_smt/test/SolverThreadLocalityTest.java index 5718007828..263c5ad3f8 100644 --- a/src/org/sosy_lab/java_smt/test/SolverThreadLocalityTest.java +++ b/src/org/sosy_lab/java_smt/test/SolverThreadLocalityTest.java @@ -347,6 +347,7 @@ public void wrongContextTest() Solvers.MATHSAT5, Solvers.SMTINTERPOL, Solvers.Z3, + Solvers.Z3_WITH_INTERPOLATION, Solvers.PRINCESS, Solvers.BOOLECTOR, Solvers.BITWUZLA, diff --git a/src/org/sosy_lab/java_smt/test/SolverVisitorTest.java b/src/org/sosy_lab/java_smt/test/SolverVisitorTest.java index 9dff83944c..c55ae0af9d 100644 --- a/src/org/sosy_lab/java_smt/test/SolverVisitorTest.java +++ b/src/org/sosy_lab/java_smt/test/SolverVisitorTest.java @@ -744,6 +744,11 @@ public TraversalProcess visitFunction( @Test public void stringInBooleanFormulaIdVisit() throws SolverException, InterruptedException { requireStrings(); + assume() + .withMessage("Solver %s does not support the complete theory of strings", solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); + StringFormula x = smgr.makeVariable("xVariable"); StringFormula y = smgr.makeVariable("yVariable"); RegexFormula r = smgr.makeRegex("regex1"); @@ -770,6 +775,11 @@ public void stringInBooleanFormulaIdVisit() throws SolverException, InterruptedE @Test public void stringInStringFormulaVisit() throws SolverException, InterruptedException { requireStrings(); + assume() + .withMessage("Solver %s does not support the complete theory of strings", solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); + StringFormula x = smgr.makeVariable("xVariable"); StringFormula y = smgr.makeVariable("yVariable"); StringFormula z = smgr.makeString("zAsString"); @@ -811,6 +821,11 @@ public void stringInStringFormulaVisit() throws SolverException, InterruptedExce @Test public void stringInRegexFormulaVisit() { requireStrings(); + assume() + .withMessage("Solver %s does not support the complete theory of strings", solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); + RegexFormula r = smgr.makeRegex("regex1"); RegexFormula s = smgr.makeRegex("regex2"); @@ -832,6 +847,11 @@ public void stringInRegexFormulaVisit() { @Test public void stringInIntegerFormulaVisit() throws SolverException, InterruptedException { requireStrings(); + assume() + .withMessage("Solver %s does not support the complete theory of strings", solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); + StringFormula x = smgr.makeVariable("xVariable"); StringFormula y = smgr.makeVariable("yVariable"); IntegerFormula offset = imgr.makeVariable("offset"); diff --git a/src/org/sosy_lab/java_smt/test/StringFormulaManagerTest.java b/src/org/sosy_lab/java_smt/test/StringFormulaManagerTest.java index 231394788c..ce78d030ce 100644 --- a/src/org/sosy_lab/java_smt/test/StringFormulaManagerTest.java +++ b/src/org/sosy_lab/java_smt/test/StringFormulaManagerTest.java @@ -82,6 +82,11 @@ public class StringFormulaManagerTest extends SolverBasedTest0.ParameterizedSolv @Before public void setup() { requireStrings(); + assume() + .withMessage("Solver %s does not support the complete theory of strings", solverToUse()) + .that(solverToUse()) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); + a = smgr.makeString("a"); b = smgr.makeString("b"); ab = smgr.makeString("ab"); diff --git a/src/org/sosy_lab/java_smt/test/TranslateFormulaTest.java b/src/org/sosy_lab/java_smt/test/TranslateFormulaTest.java index 365f97261b..bb0afe743a 100644 --- a/src/org/sosy_lab/java_smt/test/TranslateFormulaTest.java +++ b/src/org/sosy_lab/java_smt/test/TranslateFormulaTest.java @@ -94,6 +94,12 @@ private void requireParserTo() { .withMessage("Solver %s does not support parsing formulae", translateTo) .that(translateTo) .isNoneOf(Solvers.CVC4, Solvers.BOOLECTOR, Solvers.YICES2, Solvers.CVC5); + + assume() + .withMessage( + "Solver %s segfaults when parsing short queries or reports invalid length", translateTo) + .that(translateTo) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); } private void requireParserFrom() { @@ -101,6 +107,13 @@ private void requireParserFrom() { .withMessage("Solver %s does not support parsing formulae", translateFrom) .that(translateFrom) .isNoneOf(Solvers.CVC4, Solvers.BOOLECTOR, Solvers.YICES2, Solvers.CVC5); + + assume() + .withMessage( + "Solver %s segfaults when parsing short queries or reports invalid length", + translateFrom) + .that(translateFrom) + .isNotEqualTo(Solvers.Z3_WITH_INTERPOLATION); } private void requireIntegers() { diff --git a/src/org/sosy_lab/java_smt/test/UFManagerTest.java b/src/org/sosy_lab/java_smt/test/UFManagerTest.java index 9a8fa079e5..26755b1f2c 100644 --- a/src/org/sosy_lab/java_smt/test/UFManagerTest.java +++ b/src/org/sosy_lab/java_smt/test/UFManagerTest.java @@ -107,6 +107,7 @@ public void testDeclareAndCallUFWithIntAndRational() case CVC5: case SMTINTERPOL: case Z3: + case Z3_WITH_INTERPOLATION: // some solvers have an explicit cast for the parameter Truth.assertThat(f2).isNotEqualTo(f); List args = getArguments(f2);