88
99package 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+
1214import com .google .common .collect .Collections2 ;
15+ import com .google .common .collect .HashMultimap ;
1316import com .google .common .collect .ImmutableList ;
1417import 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 ;
1521import com .google .errorprone .annotations .CanIgnoreReturnValue ;
1622import io .github .cvc5 .CVC5ApiException ;
1723import io .github .cvc5 .CVC5ApiRecoverableException ;
24+ import io .github .cvc5 .Kind ;
1825import io .github .cvc5 .Result ;
1926import io .github .cvc5 .Solver ;
27+ import io .github .cvc5 .Sort ;
2028import io .github .cvc5 .Term ;
2129import io .github .cvc5 .TermManager ;
2230import io .github .cvc5 .UnknownExplanation ;
2331import java .util .ArrayDeque ;
2432import java .util .ArrayList ;
2533import java .util .Collection ;
2634import java .util .Deque ;
35+ import java .util .HashSet ;
2736import java .util .List ;
2837import java .util .Optional ;
2938import java .util .Set ;
39+ import javax .annotation .Nonnull ;
3040import org .sosy_lab .common .ShutdownNotifier ;
3141import org .sosy_lab .common .UniqueIdGenerator ;
3242import 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