Skip to content

Commit de5bb91

Browse files
committed
CVC5: improve handling of separation logic.
Until now, CVC5 has never set the heap-sort correctly. I wonder how that worked. Tests for separation logic are still missing.
1 parent d66d096 commit de5bb91

File tree

1 file changed

+132
-14
lines changed

1 file changed

+132
-14
lines changed

src/org/sosy_lab/java_smt/solvers/cvc5/CVC5AbstractProver.java

Lines changed: 132 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,35 @@
88

99
package org.sosy_lab.java_smt.solvers.cvc5;
1010

11-
import com.google.common.base.Preconditions;
11+
import static com.google.common.base.Preconditions.checkNotNull;
12+
import static com.google.common.base.Preconditions.checkState;
13+
1214
import com.google.common.collect.Collections2;
15+
import com.google.common.collect.HashMultimap;
1316
import com.google.common.collect.ImmutableList;
1417
import com.google.common.collect.ImmutableMap;
18+
import com.google.common.collect.ImmutableSet;
19+
import com.google.common.collect.Iterables;
20+
import com.google.common.collect.Multimap;
1521
import com.google.errorprone.annotations.CanIgnoreReturnValue;
1622
import io.github.cvc5.CVC5ApiException;
1723
import io.github.cvc5.CVC5ApiRecoverableException;
24+
import io.github.cvc5.Kind;
1825
import io.github.cvc5.Result;
1926
import io.github.cvc5.Solver;
27+
import io.github.cvc5.Sort;
2028
import io.github.cvc5.Term;
2129
import io.github.cvc5.TermManager;
2230
import io.github.cvc5.UnknownExplanation;
2331
import java.util.ArrayDeque;
2432
import java.util.ArrayList;
2533
import java.util.Collection;
2634
import java.util.Deque;
35+
import java.util.HashSet;
2736
import java.util.List;
2837
import java.util.Optional;
2938
import java.util.Set;
39+
import javax.annotation.Nonnull;
3040
import org.sosy_lab.common.ShutdownNotifier;
3141
import org.sosy_lab.common.UniqueIdGenerator;
3242
import org.sosy_lab.common.collect.PathCopyingPersistentTreeMap;
@@ -53,6 +63,10 @@ abstract class CVC5AbstractProver<T> extends AbstractProverWithAllSat<T> {
5363
// --> No, CVC5 aborts on addConstraint.
5464
protected final boolean incremental;
5565

66+
// Separation Logic supports only one heap with one sort. Let's store them here.
67+
private Sort heapSort = null;
68+
private Sort elementSort = null;
69+
5670
protected CVC5AbstractProver(
5771
CVC5FormulaCreator pFormulaCreator,
5872
ShutdownNotifier pShutdownNotifier,
@@ -142,7 +156,7 @@ protected void popImpl() {
142156

143157
@CanIgnoreReturnValue
144158
protected String addConstraint0(BooleanFormula pF) {
145-
Preconditions.checkState(!closed);
159+
checkState(!closed);
146160
setChanged();
147161
Term exp = creator.extractInfo(pF);
148162
if (incremental) {
@@ -156,8 +170,8 @@ protected String addConstraint0(BooleanFormula pF) {
156170
@SuppressWarnings("resource")
157171
@Override
158172
public CVC5Model getModel() throws SolverException {
159-
Preconditions.checkState(!closed);
160-
Preconditions.checkState(!changedSinceLastSatQuery);
173+
checkState(!closed);
174+
checkState(!changedSinceLastSatQuery);
161175
checkGenerateModels();
162176
// special case for CVC5: Models are not permanent and need to be closed
163177
// before any change is applied to the prover stack. So, we register the Model as Evaluator.
@@ -171,7 +185,7 @@ public CVC5Model getModel() throws SolverException {
171185

172186
@Override
173187
public Evaluator getEvaluator() {
174-
Preconditions.checkState(!closed);
188+
checkState(!closed);
175189
checkGenerateModels();
176190
return getEvaluatorWithoutChecks();
177191
}
@@ -191,27 +205,131 @@ protected void setChanged() {
191205

192206
@Override
193207
public ImmutableList<ValueAssignment> getModelAssignments() throws SolverException {
194-
Preconditions.checkState(!closed);
195-
Preconditions.checkState(!changedSinceLastSatQuery);
208+
checkState(!closed);
209+
checkState(!changedSinceLastSatQuery);
196210
return super.getModelAssignments();
197211
}
198212

199213
@Override
200214
@SuppressWarnings("try")
201215
public boolean isUnsat() throws InterruptedException, SolverException {
202-
Preconditions.checkState(!closed);
216+
checkState(!closed);
203217
closeAllEvaluators();
204218
changedSinceLastSatQuery = false;
205219
if (!incremental) {
206-
getAssertedFormulas().forEach(f -> solver.assertFormula(creator.extractInfo(f)));
220+
ImmutableSet<BooleanFormula> assertedFormulas = getAssertedFormulas();
221+
if (enableSL) {
222+
declareHeap(assertedFormulas);
223+
}
224+
assertedFormulas.forEach(f -> solver.assertFormula(creator.extractInfo(f)));
207225
}
208226

209-
/* Shutdown currently not possible in CVC5. */
210-
Result result = solver.checkSat();
211-
shutdownNotifier.shutdownIfNecessary();
227+
Result result;
228+
try {
229+
result = checkSat();
230+
} catch (CVC5ApiException e) {
231+
throw new SolverException("CVC5 failed during satisfiability check", e);
232+
} finally {
233+
/* Shutdown currently not possible in CVC5. */
234+
shutdownNotifier.shutdownIfNecessary();
235+
}
212236
return convertSatResult(result);
213237
}
214238

239+
private Result checkSat() throws CVC5ApiException {
240+
return solver.checkSat();
241+
}
242+
243+
private void declareHeap(ImmutableSet<BooleanFormula> pAssertedFormulas) throws SolverException {
244+
// get heap sort from asserted terms
245+
final Multimap<Sort, Sort> heapSorts;
246+
try {
247+
heapSorts = getHeapSorts(pAssertedFormulas);
248+
} catch (CVC5ApiException e) {
249+
throw new SolverException("CVC5 failed during heap sort collection", e);
250+
}
251+
252+
// if there are no heaps, skip the rest
253+
if (heapSorts.isEmpty()) {
254+
return;
255+
}
256+
257+
// if there are multiple heaps with different sorts, we cannot handle this
258+
if (heapSorts.size() > 1) {
259+
throw new SolverException(
260+
"CVC5 SL support is limited to one heap with one sort. Found heaps with sorts: "
261+
+ heapSorts);
262+
}
263+
264+
// get the (only) heap sort
265+
final Sort newHeapSort = checkNotNull(Iterables.getOnlyElement(heapSorts.keySet()));
266+
final Sort newElementSort = checkNotNull(Iterables.getOnlyElement(heapSorts.get(newHeapSort)));
267+
268+
// if we already have a heap, check that the sorts are the same
269+
if (heapSort != null) {
270+
checkState(elementSort != null, "Heap sort and element sort must both be defined.");
271+
if (!heapSort.equals(newHeapSort) || !elementSort.equals(newElementSort)) {
272+
throw new SolverException(
273+
"CVC5 SL support is limited to one heap with one sort. Already declared heap with"
274+
+ " sorts: "
275+
+ heapSort
276+
+ "->"
277+
+ elementSort
278+
+ ", but found heap with sorts: "
279+
+ newHeapSort
280+
+ "->"
281+
+ newElementSort);
282+
}
283+
} else {
284+
checkState(elementSort == null, "Heap sort and element sort must both be undefined.");
285+
heapSort = newHeapSort;
286+
elementSort = newElementSort;
287+
288+
// then declare the heap in the solver, once before the first sat check
289+
solver.declareSepHeap(heapSort, elementSort);
290+
}
291+
}
292+
293+
@Nonnull
294+
private Multimap<Sort, Sort> getHeapSorts(ImmutableSet<BooleanFormula> pAssertedFormulas)
295+
throws CVC5ApiException, SolverException {
296+
final Deque<Term> waitlist = new ArrayDeque<>();
297+
for (BooleanFormula f : pAssertedFormulas) {
298+
waitlist.add(creator.extractInfo(f));
299+
}
300+
301+
// traverse all subterms of assertedFormulas and collect heap sorts
302+
final Multimap<Sort, Sort> heapSorts = HashMultimap.create();
303+
boolean requiresHeapSort = false;
304+
final Set<Term> finished = new HashSet<>();
305+
while (!waitlist.isEmpty()) {
306+
final Term current = waitlist.pop();
307+
if (finished.add(current)) {
308+
// If current is a SEP_PTO, collect the heap sort from its children,
309+
// we ignore all other SL terms (like SEP_EMP, SEP_STAR, SEP_WAND).
310+
// SEP_EMP would be interesting, but does not provide the heap sort and element sort.
311+
if (current.getKind() == Kind.SEP_PTO) {
312+
heapSorts.put(current.getChild(0).getSort(), current.getChild(1).getSort());
313+
} else if (current.getKind() == Kind.SEP_EMP) {
314+
// SEP_EMP is sortless, but we need to declare the heap sort in CVC5.
315+
requiresHeapSort = true;
316+
}
317+
// add all children to the waitlist
318+
for (int i = 0; i < current.getNumChildren(); i++) {
319+
waitlist.push(current.getChild(i));
320+
}
321+
}
322+
}
323+
324+
// if we found SEP_EMP but no SEP_PTO, we still need a heap sort
325+
if (requiresHeapSort && heapSorts.isEmpty()) {
326+
// use Integer->Integer as default heap sort
327+
heapSorts.put(creator.getIntegerType(), creator.getIntegerType());
328+
}
329+
330+
return heapSorts;
331+
}
332+
215333
private boolean convertSatResult(Result result) throws InterruptedException, SolverException {
216334
if (result.isUnknown()) {
217335
if (result.getUnknownExplanation().equals(UnknownExplanation.INTERRUPTED)) {
@@ -226,9 +344,9 @@ private boolean convertSatResult(Result result) throws InterruptedException, Sol
226344

227345
@Override
228346
public List<BooleanFormula> getUnsatCore() {
229-
Preconditions.checkState(!closed);
347+
checkState(!closed);
230348
checkGenerateUnsatCores();
231-
Preconditions.checkState(!changedSinceLastSatQuery);
349+
checkState(!changedSinceLastSatQuery);
232350
List<BooleanFormula> converted = new ArrayList<>();
233351
for (Term aCore : solver.getUnsatCore()) {
234352
converted.add(creator.encapsulateBoolean(aCore));

0 commit comments

Comments
 (0)