diff --git a/ksmt-bitwuzla/src/main/kotlin/io/ksmt/solver/bitwuzla/KBitwuzlaContext.kt b/ksmt-bitwuzla/src/main/kotlin/io/ksmt/solver/bitwuzla/KBitwuzlaContext.kt index f1999be24..5fb618e35 100644 --- a/ksmt-bitwuzla/src/main/kotlin/io/ksmt/solver/bitwuzla/KBitwuzlaContext.kt +++ b/ksmt-bitwuzla/src/main/kotlin/io/ksmt/solver/bitwuzla/KBitwuzlaContext.kt @@ -298,7 +298,9 @@ open class KBitwuzlaContext(val ctx: KContext) : AutoCloseable { } override fun close() { + if (isClosed) return isClosed = true + sorts.clear() bitwuzlaSorts.clear() diff --git a/ksmt-cvc5/src/main/kotlin/io/ksmt/solver/cvc5/KCvc5Context.kt b/ksmt-cvc5/src/main/kotlin/io/ksmt/solver/cvc5/KCvc5Context.kt index 5edfa4f53..0368b6118 100644 --- a/ksmt-cvc5/src/main/kotlin/io/ksmt/solver/cvc5/KCvc5Context.kt +++ b/ksmt-cvc5/src/main/kotlin/io/ksmt/solver/cvc5/KCvc5Context.kt @@ -351,6 +351,7 @@ class KCvc5Context( override fun close() { + if (isClosed) return isClosed = true currentScopeExpressions.clear() diff --git a/ksmt-test/src/test/kotlin/io/ksmt/test/CheckWithAssumptionsTest.kt b/ksmt-test/src/test/kotlin/io/ksmt/test/CheckWithAssumptionsTest.kt index 162d82e05..a7e6ea033 100644 --- a/ksmt-test/src/test/kotlin/io/ksmt/test/CheckWithAssumptionsTest.kt +++ b/ksmt-test/src/test/kotlin/io/ksmt/test/CheckWithAssumptionsTest.kt @@ -38,6 +38,18 @@ class CheckWithAssumptionsTest { @Test fun testUnsatCoreGenerationCvc() = testUnsatCoreGeneration { KCvc5Solver(it) } + @Test + fun testTrivialUnsatCoreZ3() = testTrivialUnsatCore { KZ3Solver(it) } + + @Test + fun testTrivialUnsatCoreBitwuzla() = testTrivialUnsatCore { KBitwuzlaSolver(it) } + + @Test + fun testTrivialUnsatCoreYices() = testTrivialUnsatCore { KYicesSolver(it) } + + @Test + fun testTrivialUnsatCoreCvc() = testTrivialUnsatCore { KCvc5Solver(it) } + private fun testComplexAssumption(mkSolver: (KContext) -> KSolver<*>) = with(KContext()) { mkSolver(this).use { solver -> val a by bv32Sort @@ -76,4 +88,15 @@ class CheckWithAssumptionsTest { assertTrue(e3 in core) } } + + private fun testTrivialUnsatCore(mkSolver: (KContext) -> KSolver<*>) = with(KContext()) { + mkSolver(this).use { solver -> + val status = solver.checkWithAssumptions(listOf(falseExpr)) + assertEquals(KSolverStatus.UNSAT, status) + + val core = solver.unsatCore() + assertEquals(1, core.size) + assertTrue(falseExpr in core) + } + } } diff --git a/ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Context.kt b/ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Context.kt index 8ce96406e..f4eae36e9 100644 --- a/ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Context.kt +++ b/ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Context.kt @@ -261,6 +261,7 @@ class KZ3Context( } override fun close() { + if (isClosed) return isClosed = true z3Expressions.keys.decRefAll() diff --git a/ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Solver.kt b/ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Solver.kt index d75ca3d37..f8bdfa43f 100644 --- a/ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Solver.kt +++ b/ksmt-z3/src/main/kotlin/io/ksmt/solver/z3/KZ3Solver.kt @@ -25,8 +25,12 @@ import kotlin.time.DurationUnit open class KZ3Solver(private val ctx: KContext) : KSolver { private val z3Ctx = KZ3Context(ctx) private val solver = createSolver() + private var lastCheckStatus = KSolverStatus.UNKNOWN private var lastReasonOfUnknown: String? = null + private var lastModel: KZ3Model? = null + private var lastUnsatCore: List>? = null + private var currentScope: UInt = 0u @Suppress("LeakingThis") @@ -104,7 +108,20 @@ open class KZ3Solver(private val ctx: KContext) : KSolver> = z3Try { require(lastCheckStatus == KSolverStatus.UNSAT) { "Unsat cores are only available after UNSAT checks" } - val unsatCore = solver.solverGetUnsatCore() - - with(exprConverter) { - unsatCore.map { trackedAssertions.get(it) ?: it.convertExpr() } + val unsatCore = lastUnsatCore ?: with(exprConverter) { + val solverUnsatCore = solver.solverGetUnsatCore() + solverUnsatCore.map { trackedAssertions.get(it) ?: it.convertExpr() } } + lastUnsatCore = unsatCore + + unsatCore } override fun reasonOfUnknown(): String = z3Try { @@ -173,6 +199,8 @@ open class KZ3Solver(private val ctx: KContext) : KSolver KSolverStatus): KSolverStatus = try {