1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew V. Jones, Andres Noetzli, Andrew Reynolds |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 by the authors listed in the file AUTHORS |
8 |
|
* in the top-level source directory and their institutional affiliations. |
9 |
|
* All rights reserved. See the file COPYING in the top-level source |
10 |
|
* directory for licensing information. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Two tests to validate the use of the separation logic API. |
14 |
|
* |
15 |
|
* First test validates that we cannot use the API if not using separation |
16 |
|
* logic. |
17 |
|
* |
18 |
|
* Second test validates that the expressions returned from the API are |
19 |
|
* correct and can be interrogated. |
20 |
|
* |
21 |
|
****************************************************************************/ |
22 |
|
|
23 |
|
#include <iostream> |
24 |
|
#include <sstream> |
25 |
|
|
26 |
|
#include "api/cpp/cvc5.h" |
27 |
|
|
28 |
|
using namespace cvc5::api; |
29 |
|
using namespace std; |
30 |
|
|
31 |
|
/** |
32 |
|
* Test function to validate that we *cannot* obtain the heap/nil expressions |
33 |
|
* when *not* using the separation logic theory |
34 |
|
*/ |
35 |
1 |
int validate_exception(void) |
36 |
|
{ |
37 |
2 |
Solver slv; |
38 |
|
|
39 |
|
/* |
40 |
|
* Setup some options for cvc5 -- we explictly want to use a simplistic |
41 |
|
* theory (e.g., QF_IDL) |
42 |
|
*/ |
43 |
1 |
slv.setLogic("QF_IDL"); |
44 |
1 |
slv.setOption("produce-models", "true"); |
45 |
1 |
slv.setOption("incremental", "false"); |
46 |
|
|
47 |
|
/* Our integer type */ |
48 |
2 |
Sort integer = slv.getIntegerSort(); |
49 |
|
|
50 |
|
/** we intentionally do not set the separation logic heap */ |
51 |
|
|
52 |
|
/* Our SMT constants */ |
53 |
2 |
Term x = slv.mkConst(integer, "x"); |
54 |
2 |
Term y = slv.mkConst(integer, "y"); |
55 |
|
|
56 |
|
/* y > x */ |
57 |
2 |
Term y_gt_x(slv.mkTerm(Kind::GT, y, x)); |
58 |
|
|
59 |
|
/* assert it */ |
60 |
1 |
slv.assertFormula(y_gt_x); |
61 |
|
|
62 |
|
/* check */ |
63 |
2 |
Result r(slv.checkSat()); |
64 |
|
|
65 |
|
/* If this is UNSAT, we have an issue; so bail-out */ |
66 |
1 |
if (!r.isSat()) |
67 |
|
{ |
68 |
|
return -1; |
69 |
|
} |
70 |
|
|
71 |
|
/* |
72 |
|
* We now try to obtain our separation logic expressions from the solver -- |
73 |
|
* we want to validate that we get our expected exceptions. |
74 |
|
*/ |
75 |
1 |
bool caught_on_heap(false); |
76 |
1 |
bool caught_on_nil(false); |
77 |
|
|
78 |
|
/* The exception message we expect to obtain */ |
79 |
|
std::string expected( |
80 |
|
"Cannot obtain separation logic expressions if not using the separation " |
81 |
2 |
"logic theory."); |
82 |
|
|
83 |
|
/* test the heap expression */ |
84 |
|
try |
85 |
|
{ |
86 |
1 |
Term heap_expr = slv.getSeparationHeap(); |
87 |
|
} |
88 |
2 |
catch (const CVC5ApiException& e) |
89 |
|
{ |
90 |
1 |
caught_on_heap = true; |
91 |
|
|
92 |
|
/* Check we get the correct exception string */ |
93 |
1 |
if (e.what() != expected) |
94 |
|
{ |
95 |
|
return -1; |
96 |
|
} |
97 |
|
} |
98 |
|
|
99 |
|
/* test the nil expression */ |
100 |
|
try |
101 |
|
{ |
102 |
1 |
Term nil_expr = slv.getSeparationNilTerm(); |
103 |
|
} |
104 |
2 |
catch (const CVC5ApiException& e) |
105 |
|
{ |
106 |
1 |
caught_on_nil = true; |
107 |
|
|
108 |
|
/* Check we get the correct exception string */ |
109 |
1 |
if (e.what() != expected) |
110 |
|
{ |
111 |
|
return -1; |
112 |
|
} |
113 |
|
} |
114 |
|
|
115 |
1 |
if (!caught_on_heap || !caught_on_nil) |
116 |
|
{ |
117 |
|
return -1; |
118 |
|
} |
119 |
|
|
120 |
|
/* All tests pass! */ |
121 |
1 |
return 0; |
122 |
|
} |
123 |
|
|
124 |
|
/** |
125 |
|
* Test function to demonstrate the use of, and validate the capability, of |
126 |
|
* obtaining the heap/nil expressions when using separation logic. |
127 |
|
*/ |
128 |
1 |
int validate_getters(void) |
129 |
|
{ |
130 |
2 |
Solver slv; |
131 |
|
|
132 |
|
/* Setup some options for cvc5 */ |
133 |
1 |
slv.setLogic("QF_ALL"); |
134 |
1 |
slv.setOption("produce-models", "true"); |
135 |
1 |
slv.setOption("incremental", "false"); |
136 |
|
|
137 |
|
/* Our integer type */ |
138 |
2 |
Sort integer = slv.getIntegerSort(); |
139 |
|
|
140 |
|
/** Declare the separation logic heap types */ |
141 |
1 |
slv.declareSeparationHeap(integer, integer); |
142 |
|
|
143 |
|
/* A "random" constant */ |
144 |
2 |
Term random_constant = slv.mkInteger(0xDEADBEEF); |
145 |
|
|
146 |
|
/* Another random constant */ |
147 |
2 |
Term expr_nil_val = slv.mkInteger(0xFBADBEEF); |
148 |
|
|
149 |
|
/* Our nil term */ |
150 |
2 |
Term nil = slv.mkSepNil(integer); |
151 |
|
|
152 |
|
/* Our SMT constants */ |
153 |
2 |
Term x = slv.mkConst(integer, "x"); |
154 |
2 |
Term y = slv.mkConst(integer, "y"); |
155 |
2 |
Term p1 = slv.mkConst(integer, "p1"); |
156 |
2 |
Term p2 = slv.mkConst(integer, "p2"); |
157 |
|
|
158 |
|
/* Constraints on x and y */ |
159 |
2 |
Term x_equal_const = slv.mkTerm(Kind::EQUAL, x, random_constant); |
160 |
2 |
Term y_gt_x = slv.mkTerm(Kind::GT, y, x); |
161 |
|
|
162 |
|
/* Points-to expressions */ |
163 |
2 |
Term p1_to_x = slv.mkTerm(Kind::SEP_PTO, p1, x); |
164 |
2 |
Term p2_to_y = slv.mkTerm(Kind::SEP_PTO, p2, y); |
165 |
|
|
166 |
|
/* Heap -- the points-to have to be "starred"! */ |
167 |
2 |
Term heap = slv.mkTerm(Kind::SEP_STAR, p1_to_x, p2_to_y); |
168 |
|
|
169 |
|
/* Constain "nil" to be something random */ |
170 |
2 |
Term fix_nil = slv.mkTerm(Kind::EQUAL, nil, expr_nil_val); |
171 |
|
|
172 |
|
/* Add it all to the solver! */ |
173 |
1 |
slv.assertFormula(x_equal_const); |
174 |
1 |
slv.assertFormula(y_gt_x); |
175 |
1 |
slv.assertFormula(heap); |
176 |
1 |
slv.assertFormula(fix_nil); |
177 |
|
|
178 |
|
/* |
179 |
|
* Incremental is disabled due to using separation logic, so don't query |
180 |
|
* twice! |
181 |
|
*/ |
182 |
2 |
Result r(slv.checkSat()); |
183 |
|
|
184 |
|
/* If this is UNSAT, we have an issue; so bail-out */ |
185 |
1 |
if (!r.isSat()) |
186 |
|
{ |
187 |
|
return -1; |
188 |
|
} |
189 |
|
|
190 |
|
/* Obtain our separation logic terms from the solver */ |
191 |
2 |
Term heap_expr = slv.getSeparationHeap(); |
192 |
2 |
Term nil_expr = slv.getSeparationNilTerm(); |
193 |
|
|
194 |
|
/* If the heap is not a separating conjunction, bail-out */ |
195 |
1 |
if (heap_expr.getKind() != Kind::SEP_STAR) |
196 |
|
{ |
197 |
|
return -1; |
198 |
|
} |
199 |
|
|
200 |
|
/* If nil is not a direct equality, bail-out */ |
201 |
1 |
if (nil_expr.getKind() != Kind::EQUAL) |
202 |
|
{ |
203 |
|
return -1; |
204 |
|
} |
205 |
|
|
206 |
|
/* Obtain the values for our "pointers" */ |
207 |
2 |
Term val_for_p1 = slv.getValue(p1); |
208 |
2 |
Term val_for_p2 = slv.getValue(p2); |
209 |
|
|
210 |
|
/* We need to make sure we find both pointers in the heap */ |
211 |
1 |
bool checked_p1(false); |
212 |
1 |
bool checked_p2(false); |
213 |
|
|
214 |
|
/* Walk all the children */ |
215 |
3 |
for (const Term& child : heap_expr) |
216 |
|
{ |
217 |
|
/* If we don't have a PTO operator, bail-out */ |
218 |
2 |
if (child.getKind() != Kind::SEP_PTO) |
219 |
|
{ |
220 |
|
return -1; |
221 |
|
} |
222 |
|
|
223 |
|
/* Find both sides of the PTO operator */ |
224 |
2 |
Term addr = slv.getValue(child[0]); |
225 |
2 |
Term value = slv.getValue(child[1]); |
226 |
|
|
227 |
|
/* If the current address is the value for p1 */ |
228 |
3 |
if (addr == val_for_p1) |
229 |
|
{ |
230 |
1 |
checked_p1 = true; |
231 |
|
|
232 |
|
/* If it doesn't match the random constant, we have a problem */ |
233 |
1 |
if (value != random_constant) |
234 |
|
{ |
235 |
|
return -1; |
236 |
|
} |
237 |
1 |
continue; |
238 |
|
} |
239 |
|
|
240 |
|
/* If the current address is the value for p2 */ |
241 |
2 |
if (addr == val_for_p2) |
242 |
|
{ |
243 |
1 |
checked_p2 = true; |
244 |
|
|
245 |
|
/* |
246 |
|
* Our earlier constraint was that what p2 points to must be *greater* |
247 |
|
* than the random constant -- if we get a value that is LTE, then |
248 |
|
* something has gone wrong! |
249 |
|
*/ |
250 |
2 |
if (std::stoll(value.toString()) |
251 |
1 |
<= std::stoll(random_constant.toString())) |
252 |
|
{ |
253 |
|
return -1; |
254 |
|
} |
255 |
1 |
continue; |
256 |
|
} |
257 |
|
|
258 |
|
/* |
259 |
|
* We should only have two addresses in heap, so if we haven't hit the |
260 |
|
* "continue" for p1 or p2, then bail-out |
261 |
|
*/ |
262 |
|
return -1; |
263 |
|
} |
264 |
|
|
265 |
|
/* |
266 |
|
* If we complete the loop and we haven't validated both p1 and p2, then we |
267 |
|
* have a problem |
268 |
|
*/ |
269 |
1 |
if (!checked_p1 || !checked_p2) |
270 |
|
{ |
271 |
|
return -1; |
272 |
|
} |
273 |
|
|
274 |
|
/* We now get our value for what nil is */ |
275 |
2 |
Term value_for_nil = slv.getValue(nil_expr[1]); |
276 |
|
|
277 |
|
/* |
278 |
|
* The value for nil from the solver should be the value we originally tied |
279 |
|
* nil to |
280 |
|
*/ |
281 |
1 |
if (value_for_nil != expr_nil_val) |
282 |
|
{ |
283 |
|
return -1; |
284 |
|
} |
285 |
|
|
286 |
|
/* All tests pass! */ |
287 |
1 |
return 0; |
288 |
|
} |
289 |
|
|
290 |
1 |
int main(void) |
291 |
|
{ |
292 |
|
/* check that we get an exception when we should */ |
293 |
1 |
int check_exception(validate_exception()); |
294 |
|
|
295 |
1 |
if (check_exception) |
296 |
|
{ |
297 |
|
return check_exception; |
298 |
|
} |
299 |
|
|
300 |
|
/* check the getters */ |
301 |
1 |
int check_getters(validate_getters()); |
302 |
|
|
303 |
1 |
if (check_getters) |
304 |
|
{ |
305 |
|
return check_getters; |
306 |
|
} |
307 |
|
|
308 |
1 |
return 0; |
309 |
3 |
} |