Skip to content

Commit 0dbe836

Browse files
authored
Merge pull request #517 from sosy-lab/feat/improve-separation-logic
Improve separation logic (CVC4 and CVC5)
2 parents 71fcd6a + 949069b commit 0dbe836

13 files changed

+616
-109
lines changed

src/org/sosy_lab/java_smt/api/SLFormulaManager.java

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// an API wrapper for a collection of SMT solvers:
33
// https://github.com/sosy-lab/java-smt
44
//
5-
// SPDX-FileCopyrightText: 2020 Dirk Beyer <https://www.sosy-lab.org>
5+
// SPDX-FileCopyrightText: 2025 Dirk Beyer <https://www.sosy-lab.org>
66
//
77
// SPDX-License-Identifier: Apache-2.0
88

@@ -15,18 +15,73 @@
1515
* all formulae for one heap need to use matching types (sorts) for the AdressFormulae and
1616
* ValueFormulae. The user has to take care of this, otherwise the {@link ProverEnvironment}
1717
* complains at runtime!
18+
*
19+
* @see <a href="https://en.wikipedia.org/wiki/Separation_logic">Separation Logic</a>
20+
* @see <a href="https://cacm.acm.org/research/separation-logic/">Separation Logic</a>
21+
* @see <a
22+
* href="https://www.cs.cmu.edu/afs/cs.cmu.edu/project/fox-19/member/jcr/www15818As2011/cs818A3-11.html">15-818A3
23+
* Introduction to Separation Logic (6 units) Spring 2011</a>
24+
* @see <a href="https://www.philipzucker.com/executable-seperation">Modeling Separation Logic with
25+
* Python Dicts and Z3</a>
1826
*/
1927
@SuppressWarnings("MethodTypeParameterName")
2028
public interface SLFormulaManager {
2129

30+
/**
31+
* Creates a formula representing the "separating conjunction" (*) of two Boolean formulas. In
32+
* separation logic, the separating conjunction asserts that the two formulas describe disjoint
33+
* parts of the heap. It is similar to the logical AND operator, but with the additional
34+
* constraint that the memory regions described by the two formulas do not overlap.
35+
*
36+
* @return a Boolean formula representing the separating conjunction of {@code f1} and {@code f2}.
37+
*/
2238
BooleanFormula makeStar(BooleanFormula f1, BooleanFormula f2);
2339

40+
/**
41+
* Creates a formula representing a "points-to" relation in the heap. The "points-to" relation
42+
* asserts that a pointer points to a specific value in memory.
43+
*
44+
* @param <AF> the type of the address formula.
45+
* @param <VF> the type of the value formula.
46+
* @param ptr the pointer formula.
47+
* @param to the value formula.
48+
* @return a Boolean formula representing the "points-to" relation.
49+
*/
2450
<AF extends Formula, VF extends Formula> BooleanFormula makePointsTo(AF ptr, VF to);
2551

52+
/**
53+
* Creates a formula representing the "magic wand" (-*) operator in separation logic. The "magic
54+
* wand" operator asserts that if the first formula holds, then the second formula can be added to
55+
* the heap without conflict. It is a form of implication (=&gt;) that takes into account the
56+
* disjointness of memory regions.
57+
*
58+
* @return a Boolean formula representing the "magic wand" of {@code f1} and {@code f2}.
59+
*/
2660
BooleanFormula makeMagicWand(BooleanFormula f1, BooleanFormula f2);
2761

62+
/**
63+
* Creates a formula representing an empty heap. The empty heap formula asserts that no memory is
64+
* allocated for the given address and value types.
65+
*
66+
* @param <AF> the type of the address formula.
67+
* @param <VF> the type of the value formula.
68+
* @param <AT> the type of the address formula type.
69+
* @param <VT> the type of the value formula type.
70+
* @param pAddressType the type of the address formula.
71+
* @param pValueType the type of the value formula.
72+
* @return a Boolean formula representing an empty heap.
73+
*/
2874
<AF extends Formula, VF extends Formula, AT extends FormulaType<AF>, VT extends FormulaType<VF>>
29-
BooleanFormula makeEmptyHeap(AT pAdressType, VT pValueType);
75+
BooleanFormula makeEmptyHeap(AT pAddressType, VT pValueType);
3076

77+
/**
78+
* Creates a formula representing the "nil" element for a given address type. The "nil" element is
79+
* used to represent a null pointer or an unallocated memory address.
80+
*
81+
* @param <AF> the type of the address formula.
82+
* @param <AT> the type of the address formula type.
83+
* @param pAdressType the type of the address formula.
84+
* @return a formula representing the "nil" element for the given address type.
85+
*/
3186
<AF extends Formula, AT extends FormulaType<AF>> AF makeNilElement(AT pAdressType);
3287
}

src/org/sosy_lab/java_smt/basicimpl/AbstractProver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
public abstract class AbstractProver<T> implements BasicProverEnvironment<T> {
3131

32-
private final boolean generateModels;
32+
protected final boolean generateModels;
3333
private final boolean generateAllSat;
3434
protected final boolean generateUnsatCores;
3535
private final boolean generateUnsatCoresOverAssumptions;

src/org/sosy_lab/java_smt/solvers/cvc4/CVC4Model.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private ImmutableList<ValueAssignment> generateModel() {
7373

7474
// TODO this method is highly recursive and should be rewritten with a proper visitor
7575
private void recursiveAssignmentFinder(ImmutableSet.Builder<ValueAssignment> builder, Expr expr) {
76-
if (expr.isConst() || expr.isNull()) {
76+
if (expr.getKind() == Kind.SEP_NIL || expr.isConst() || expr.isNull()) {
7777
// We don't care about consts.
7878
return;
7979
} else if (expr.isVariable() && expr.getKind() == Kind.BOUND_VARIABLE) {

src/org/sosy_lab/java_smt/solvers/cvc4/CVC4SLFormulaManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ protected Expr makeEmptyHeap(Type pT1, Type pT2) {
4545

4646
@Override
4747
protected Expr makeNilElement(Type pType) {
48-
return exprManager.mkExpr(Kind.SEP_NIL, pType.mkGroundTerm());
48+
return exprManager.mkNullaryOperator(pType, Kind.SEP_NIL);
4949
}
5050
}

src/org/sosy_lab/java_smt/solvers/cvc4/CVC4TheoremProver.java

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class CVC4TheoremProver extends AbstractProverWithAllSat<Void>
4040
implements ProverEnvironment, BasicProverEnvironment<Void> {
4141

4242
private final CVC4FormulaCreator creator;
43+
private final int randomSeed;
4344
SmtEngine smtEngine; // final except for SL theory
4445
private boolean changedSinceLastSatQuery = false;
4546

@@ -50,37 +51,45 @@ class CVC4TheoremProver extends AbstractProverWithAllSat<Void>
5051
* <p>TODO If the overhead of importing/exporting the expressions is too expensive, we can disable
5152
* this behavior. This change would cost us the flexibility of setting options per Prover.
5253
*/
53-
private final ExprManager exprManager = new ExprManager();
54+
private ExprManager exprManager; // final except for SL theory
5455

5556
/** We copy expression between different ExprManagers. The map serves as cache. */
56-
private final ExprManagerMapCollection exportMapping = new ExprManagerMapCollection();
57+
private ExprManagerMapCollection exportMapping; // final except for SL theory
5758

5859
// CVC4 does not support separation logic in incremental mode.
5960
private final boolean incremental;
6061

6162
protected CVC4TheoremProver(
6263
CVC4FormulaCreator pFormulaCreator,
6364
ShutdownNotifier pShutdownNotifier,
64-
int randomSeed,
65+
int pRandomSeed,
6566
Set<ProverOptions> pOptions,
6667
BooleanFormulaManager pBmgr) {
6768
super(pOptions, pBmgr, pShutdownNotifier);
6869

6970
creator = pFormulaCreator;
70-
smtEngine = new SmtEngine(exprManager);
71+
randomSeed = pRandomSeed;
7172
incremental = !enableSL;
7273

73-
setOptions(randomSeed, pOptions);
74+
createNewEngine();
7475
}
7576

76-
private void setOptions(int randomSeed, Set<ProverOptions> pOptions) {
77-
smtEngine.setOption("incremental", new SExpr(incremental));
78-
if (pOptions.contains(ProverOptions.GENERATE_MODELS)) {
79-
smtEngine.setOption("produce-models", new SExpr(true));
77+
private void createNewEngine() {
78+
if (smtEngine != null) {
79+
smtEngine.delete(); // cleanup
80+
}
81+
if (exportMapping != null) {
82+
exportMapping.delete();
8083
}
81-
if (pOptions.contains(ProverOptions.GENERATE_UNSAT_CORE)) {
82-
smtEngine.setOption("produce-unsat-cores", new SExpr(true));
84+
if (exprManager != null) {
85+
exprManager.delete(); // cleanup
8386
}
87+
exprManager = new ExprManager();
88+
smtEngine = new SmtEngine(exprManager);
89+
exportMapping = new ExprManagerMapCollection();
90+
smtEngine.setOption("incremental", new SExpr(incremental));
91+
smtEngine.setOption("produce-models", new SExpr(generateModels));
92+
smtEngine.setOption("produce-unsat-cores", new SExpr(generateUnsatCores));
8493
smtEngine.setOption("produce-assertions", new SExpr(true));
8594
smtEngine.setOption("dump-models", new SExpr(true));
8695
// smtEngine.setOption("produce-unsat-cores", new SExpr(true));
@@ -93,10 +102,6 @@ private void setOptions(int randomSeed, Set<ProverOptions> pOptions) {
93102
smtEngine.setOption("full-saturate-quant", new SExpr(true));
94103
}
95104

96-
protected void setOptionForIncremental() {
97-
smtEngine.setOption("incremental", new SExpr(true));
98-
}
99-
100105
/** import an expression from global context into this prover's context. */
101106
protected Expr importExpr(Expr expr) {
102107
return expr.exportTo(exprManager, exportMapping);
@@ -172,13 +177,9 @@ protected Evaluator getEvaluatorWithoutChecks() {
172177
}
173178

174179
private void setChanged() {
175-
closeAllEvaluators();
176180
if (!changedSinceLastSatQuery) {
177181
changedSinceLastSatQuery = true;
178-
if (!incremental) {
179-
// create a new clean smtEngine
180-
smtEngine = new SmtEngine(exprManager);
181-
}
182+
closeAllEvaluators();
182183
}
183184
}
184185

@@ -196,17 +197,20 @@ public boolean isUnsat() throws InterruptedException, SolverException {
196197
closeAllEvaluators();
197198
changedSinceLastSatQuery = false;
198199
if (!incremental) {
199-
for (BooleanFormula f : getAssertedFormulas()) {
200-
assertFormula(f);
201-
}
200+
// in non-incremental mode, we need to create a new solver instance for each sat check
201+
createNewEngine();
202+
getAssertedFormulas().forEach(this::assertFormula);
202203
}
203204

204205
Result result;
205206
try (ShutdownHook hook = new ShutdownHook(shutdownNotifier, smtEngine::interrupt)) {
206207
shutdownNotifier.shutdownIfNecessary();
207208
result = smtEngine.checkSat();
209+
} catch (Exception e) {
210+
throw new SolverException("CVC4 failed during satisfiability check", e);
211+
} finally {
212+
shutdownNotifier.shutdownIfNecessary();
208213
}
209-
shutdownNotifier.shutdownIfNecessary();
210214
return convertSatResult(result);
211215
}
212216

0 commit comments

Comments
 (0)