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 super Z3LegacyFormula> 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 extends Formula, ? extends Formula> fromToMapping) {
+ long[] changeFrom = new long[fromToMapping.size()];
+ long[] changeTo = new long[fromToMapping.size()];
+ int idx = 0;
+ for (Map.Entry extends Formula, ? extends Formula> 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 extends Collection> 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 extends Collection> 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 extends Collection> 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