Skip to content

Commit 3a8b34d

Browse files
authored
Merge pull request #3926 from tautschnig/quantifier-fixes
Cleanup boolbv quantifier instantiation
2 parents 88f5a3f + 38fd81a commit 3a8b34d

File tree

10 files changed

+75
-80
lines changed

10 files changed

+75
-80
lines changed

regression/cbmc/Quantifiers-not-exists/main.c renamed to regression/cbmc-cover/Quantifiers-not-exists/main.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
int main()
22
{
3-
43
int a[2][2];
54
int b[2][2];
65
int c[2][2];
@@ -25,16 +24,15 @@ int main()
2524

2625
assert(0);
2726

28-
assert(a[0][0]>10);
29-
30-
assert( (b[0][0]<1||b[0][0]>10) && (b[0][1]<1||b[0][1]>10));
31-
assert( (b[1][0]<1||b[1][0]>10) && (b[1][1]<1||b[1][1]>10));
27+
assert(a[0][0] > 10);
3228

33-
assert(c[0][0]==0 || c[0][1]==0 || c[1][0]<=10 || c[1][1]<=10);
29+
assert((b[0][0] < 1 || b[0][0] > 10) && (b[0][1] < 1 || b[0][1] > 10));
30+
assert((b[1][0] < 1 || b[1][0] > 10) && (b[1][1] < 1 || b[1][1] > 10));
3431

35-
assert( ((d[0][0]<1||d[0][0]>10) || (d[0][1]<1||d[0][1]>10)) );
36-
assert( ((d[1][0]<1||d[1][0]>10) || (d[1][1]<1||d[1][1]>10)) );
32+
assert(c[0][0] == 0 || c[0][1] == 0 || c[1][0] <= 10 || c[1][1] <= 10);
3733

34+
assert(((d[0][0] < 1 || d[0][0] > 10) || (d[0][1] < 1 || d[0][1] > 10)));
35+
assert(((d[1][0] < 1 || d[1][0] > 10) || (d[1][1] < 1 || d[1][1] > 10)));
3836

3937
return 0;
4038
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--cover location
4+
^\*\* 1 of 46 covered \(2.2%\)
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring
9+
--
10+
All assertions are unreachable as each of the __CPROVER_assume evaluate to false
11+
(!exists i. i>=0 && i<2 ==> ... is equivalent to forall i. i>=0 && i<2 && ...,
12+
where neither i>=0 nor i<2 is actually true for all values of i).

regression/cbmc/Quantifiers-assertion/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
main.c
33

44
^\*\* Results:$

regression/cbmc/Quantifiers-if/main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ int main()
99
if(__CPROVER_forall { int i; (i>=0 && i<2) ==> a[i]>=1 && a[i]<=10 })
1010
__CPROVER_assert(0, "failure 1");
1111

12-
if(__CPROVER_exists { int i; (i>=0 && i<2) ==> a[i]>=1 && a[i]<=10 })
12+
if(__CPROVER_exists { int i; (i>=0 && i<2) && a[i]>=1 && a[i]<=10 })
1313
__CPROVER_assert(0, "failure 2");
1414

1515
if(__CPROVER_forall { int i; (i>=0 && i<2) ==> a[i]>=2 && a[i]<=10 })
1616
__CPROVER_assert(0, "success 1");
1717

18-
if(__CPROVER_exists { int i; (i>=0 && i<2) ==> a[i]>=2 && a[i]<=10 })
18+
if(__CPROVER_exists { int i; (i>=0 && i<2) && a[i]>=2 && a[i]<=10 })
1919
__CPROVER_assert(0, "failure 3");
2020

21-
if(__CPROVER_exists { int i; (i>=0 && i<2) ==> a[i]>=5 && a[i]<=10 })
21+
if(__CPROVER_exists { int i; (i>=0 && i<2) && a[i]>=5 && a[i]<=10 })
2222
__CPROVER_assert(0, "success 2");
2323
// clang-format on
2424

regression/cbmc/Quantifiers-invalid-var-range/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ int main()
44
int a[3][3];
55
// clang-format off
66
// clang-format would rewrite the "==>" as "== >"
7-
__CPROVER_assume(__CPROVER_forall { int i; (i>=0 && i<5) ==> __CPROVER_exists{int j; (j>=i && j<3) ==> a[i][j]==10} });
7+
// NOLINTNEXTLINE(whitespace/line_length)
8+
__CPROVER_assume(__CPROVER_forall { int i; (i>=0 && i<5) ==> __CPROVER_exists{int j; (j>=i && j<3) && a[i][j]==10} });
89
// clang-format on
910

1011
assert(a[0][0]==10||a[0][1]==10||a[0][2]==10);

regression/cbmc/Quantifiers-not-exists/test.desc

Lines changed: 0 additions & 20 deletions
This file was deleted.

regression/cbmc/Quantifiers-not/main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ int main()
99
if(!__CPROVER_forall { int i; (i>=0 && i<2) ==> a[i]>=1 && a[i]<=10 })
1010
__CPROVER_assert(0, "success 1");
1111

12-
if(!__CPROVER_exists { int i; (i>=0 && i<2) ==> a[i]>=1 && a[i]<=10 })
12+
if(!__CPROVER_exists { int i; (i>=0 && i<2) && a[i]>=1 && a[i]<=10 })
1313
__CPROVER_assert(0, "success 2");
1414

1515
if(!__CPROVER_forall { int i; (i>=0 && i<2) ==> a[i]>=2 && a[i]<=10 })
1616
__CPROVER_assert(0, "failure 1");
1717

18-
if(!__CPROVER_exists { int i; (i>=0 && i<2) ==> a[i]>=2 && a[i]<=10 })
18+
if(!__CPROVER_exists { int i; (i>=0 && i<2) && a[i]>=2 && a[i]<=10 })
1919
__CPROVER_assert(0, "success 3");
2020

21-
if(!__CPROVER_exists { int i; (i>=0 && i<2) ==> a[i]>=5 && a[i]<=10 })
21+
if(!__CPROVER_exists { int i; (i>=0 && i<2) && a[i]>=5 && a[i]<=10 })
2222
__CPROVER_assert(0, "failure 2");
2323
// clang-format on
2424

regression/cbmc/Quantifiers-two-dimension-array/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
main.c
33

44
^\*\* Results:$

src/solvers/flattening/boolbv.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,11 @@ class boolbvt:public arrayst
238238
class quantifiert
239239
{
240240
public:
241+
quantifiert(exprt _expr, literalt _l)
242+
: expr(std::move(_expr)), l(std::move(_l))
243+
{
244+
}
245+
241246
exprt expr;
242247
literalt l;
243248
};

src/solvers/flattening/boolbv_quantifier.cpp

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,23 @@ Author: Daniel Kroening, [email protected]
99
#include "boolbv.h"
1010

1111
#include <util/arith_tools.h>
12+
#include <util/expr_util.h>
1213
#include <util/invariant.h>
1314
#include <util/optional.h>
1415
#include <util/replace_expr.h>
1516
#include <util/simplify_expr.h>
1617

1718
/// A method to detect equivalence between experts that can contain typecast
18-
bool expr_eq(const exprt &expr1, const exprt &expr2)
19+
static bool expr_eq(const exprt &expr1, const exprt &expr2)
1920
{
20-
exprt e1=expr1, e2=expr2;
21-
if(expr1.id()==ID_typecast)
22-
e1=expr1.op0();
23-
if(expr2.id()==ID_typecast)
24-
e2=expr2.op0();
25-
return e1==e2;
21+
return skip_typecast(expr1) == skip_typecast(expr2);
2622
}
2723

2824
/// To obtain the min value for the quantifier variable of the specified
2925
/// forall/exists operator. The min variable is in the form of "!(var_expr >
3026
/// constant)".
31-
exprt get_quantifier_var_min(
32-
const exprt &var_expr,
33-
const exprt &quantifier_expr)
27+
static exprt
28+
get_quantifier_var_min(const exprt &var_expr, const exprt &quantifier_expr)
3429
{
3530
PRECONDITION(quantifier_expr.id() == ID_or || quantifier_expr.id() == ID_and);
3631

@@ -75,9 +70,8 @@ exprt get_quantifier_var_min(
7570

7671
/// To obtain the max value for the quantifier variable of the specified
7772
/// forall/exists operator.
78-
exprt get_quantifier_var_max(
79-
const exprt &var_expr,
80-
const exprt &quantifier_expr)
73+
static exprt
74+
get_quantifier_var_max(const exprt &var_expr, const exprt &quantifier_expr)
8175
{
8276
PRECONDITION(quantifier_expr.id() == ID_or || quantifier_expr.id() == ID_and);
8377
exprt res = false_exprt();
@@ -132,27 +126,25 @@ exprt get_quantifier_var_max(
132126
return res;
133127
}
134128

135-
optionalt<exprt>
129+
static optionalt<exprt>
136130
instantiate_quantifier(const quantifier_exprt &expr, const namespacet &ns)
137131
{
138-
PRECONDITION(expr.id() == ID_forall || expr.id() == ID_exists);
139-
140132
const symbol_exprt &var_expr = expr.symbol();
141133

142134
/**
143135
* We need to rewrite the forall/exists quantifier into
144136
* an OR/AND expr.
145137
**/
146138

147-
const exprt &re = simplify_expr(expr.where(), ns);
139+
const exprt re = simplify_expr(expr.where(), ns);
148140

149141
if(re.is_true() || re.is_false())
150142
{
151143
return re;
152144
}
153145

154-
const exprt &min_i = get_quantifier_var_min(var_expr, re);
155-
const exprt &max_i = get_quantifier_var_max(var_expr, re);
146+
const exprt min_i = get_quantifier_var_min(var_expr, re);
147+
const exprt max_i = get_quantifier_var_max(var_expr, re);
156148

157149
if(min_i.is_false() || max_i.is_false())
158150
return nullopt;
@@ -175,50 +167,57 @@ instantiate_quantifier(const quantifier_exprt &expr, const namespacet &ns)
175167

176168
if(expr.id()==ID_forall)
177169
{
178-
return conjunction(expr_insts);
170+
// maintain the domain constraint if it isn't guaranteed by the
171+
// instantiations (for a disjunction the domain constraint is implied by the
172+
// instantiations)
173+
if(re.id() == ID_and)
174+
{
175+
expr_insts.push_back(binary_predicate_exprt(
176+
var_expr, ID_gt, from_integer(lb, var_expr.type())));
177+
expr_insts.push_back(binary_predicate_exprt(
178+
var_expr, ID_le, from_integer(ub, var_expr.type())));
179+
}
180+
return simplify_expr(conjunction(expr_insts), ns);
179181
}
180182
else if(expr.id() == ID_exists)
181183
{
182-
return disjunction(expr_insts);
184+
// maintain the domain constraint if it isn't trivially satisfied by the
185+
// instantiations (for a conjunction the instantiations are stronger
186+
// constraints)
187+
if(re.id() == ID_or)
188+
{
189+
expr_insts.push_back(binary_predicate_exprt(
190+
var_expr, ID_gt, from_integer(lb, var_expr.type())));
191+
expr_insts.push_back(binary_predicate_exprt(
192+
var_expr, ID_le, from_integer(ub, var_expr.type())));
193+
}
194+
return simplify_expr(disjunction(expr_insts), ns);
183195
}
184196

185197
UNREACHABLE;
186-
return nullopt;
187198
}
188199

189200
literalt boolbvt::convert_quantifier(const quantifier_exprt &src)
190201
{
191202
PRECONDITION(src.id() == ID_forall || src.id() == ID_exists);
192203

193-
quantifier_exprt expr(src);
194-
const auto res = instantiate_quantifier(expr, ns);
204+
const auto res = instantiate_quantifier(src, ns);
195205

196-
if(!res)
197-
{
198-
return SUB::convert_rest(src);
199-
}
200-
201-
quantifiert quantifier;
202-
quantifier.expr = *res;
203-
quantifier_list.push_back(quantifier);
206+
if(res)
207+
return convert_bool(*res);
204208

205-
literalt l=prop.new_variable();
206-
quantifier_list.back().l=l;
209+
// we failed to instantiate here, need to pass to post-processing
210+
quantifier_list.emplace_back(quantifiert(src, prop.new_variable()));
207211

208-
return l;
212+
return quantifier_list.back().l;
209213
}
210214

211215
void boolbvt::post_process_quantifiers()
212216
{
213-
std::set<exprt> instances;
214-
215217
if(quantifier_list.empty())
216218
return;
217219

218-
for(auto it=quantifier_list.begin();
219-
it!=quantifier_list.end();
220-
++it)
221-
{
222-
prop.set_equal(convert_bool(it->expr), it->l);
223-
}
220+
// we do not yet have any elaborate post-processing
221+
for(const auto &q : quantifier_list)
222+
conversion_failed(q.expr);
224223
}

0 commit comments

Comments
 (0)