Skip to content

Commit 5cdfb0b

Browse files
committed
Fix: Handle quantifiers with statement expressions in CBMC
This commit addresses the issue of handling quantifiers that contain statement expressions in CBMC. Previously, quantifiers without side effects were not supported. Changes include: - Added logic to handle quantifiers with statement expressions in `goto_clean_expr.cpp`. - Updated `c_typecheck_expr.cpp` to allow quantifiers with statement expressions. - Introduced new tests to verify the correct handling of quantifiers with statement expressions.
1 parent f55f636 commit 5cdfb0b

File tree

16 files changed

+440
-14
lines changed

16 files changed

+440
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
int main()
2+
{
3+
// clang-format off
4+
// no side effects!
5+
int j = 0;
6+
//assert(j++);
7+
//assert(({int i = 0; while(i <3) i++; i <3;}));
8+
int a[5] = {0 , 0, 0, 0, 0};
9+
assert(__CPROVER_forall { int i; ({ int j = i; i=i; if(i < 0 || i >4) i = 1; ( a[i] < 5); }) });
10+
// clang-format on
11+
12+
return 0;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
int main()
2+
{
3+
int b[2];
4+
int c[2];
5+
6+
// clang-format off
7+
// clang-format would rewrite the "==>" as "== >"
8+
__CPROVER_assume( __CPROVER_forall { char i; ({ _Bool flag = (i>=0 && i<2); flag ==> b[i]>=10 && b[i]<=10; }) } );
9+
__CPROVER_assume( __CPROVER_forall { unsigned i; ({ _Bool flag = (i>=0 && i<2); flag ==> c[i]>=10 && c[i]<=10; }) } );
10+
// clang-format on
11+
12+
assert(b[0] == 10 && b[1] == 10);
13+
assert(c[0] == 10 && c[1] == 10);
14+
15+
return 0;
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE no-new-smt
2+
main.c
3+
4+
^\*\* Results:$
5+
^\[main.assertion.1\] line 12 assertion b\[.*0\] == 10 && b\[.*1\] == 10: SUCCESS$
6+
^\[main.assertion.2\] line 13 assertion c\[.*0\] == 10 && c\[.*1\] == 10: SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
8+
^EXIT=0$
9+
^SIGNAL=0$
10+
--
11+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
int func()
2+
{
3+
return 1;
4+
}
5+
6+
int main()
7+
{
8+
// clang-format off
9+
// no side effects!
10+
assert(__CPROVER_forall { int i; func() });
11+
// clang-format on
12+
13+
return 0;
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
quantifier-with-function-call.c
3+
4+
^EXIT=6$
5+
^SIGNAL=0$
6+
quantifier must not contain function calls
7+
--
8+
^warning: ignoring

regression/cbmc/Quantifiers1/quantifier-with-side-effect.c

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
int func()
2-
{
3-
return 1;
4-
}
5-
61
int main()
72
{
3+
int j = 0;
84
// clang-format off
95
// no side effects!
10-
assert(__CPROVER_forall { int i; func() });
6+
assert(__CPROVER_forall { int i; ({ j = 1; j; }) });
117
// clang-format on
128

139
return 0;
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
CORE
22
quantifier-with-side-effect.c
33

4-
^EXIT=6$
4+
^EXIT=(127|134)$
55
^SIGNAL=0$
6-
^file .* line 10 function main: quantifier must not contain side effects$
6+
^Invariant check failed
7+
^Reason: quantifier must not contain side effects
78
--
8-
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// clang-format off
2+
int f1(int *arr)
3+
__CPROVER_requires(__CPROVER_exists {
4+
int i;
5+
({(0 <= i && i < 8) && arr[i] == 0;})
6+
})
7+
__CPROVER_ensures(__CPROVER_exists {
8+
int i;
9+
({(0 <= i && i < 8) && arr[i] == 0;})
10+
})
11+
// clang-format on
12+
{
13+
return 0;
14+
}
15+
16+
int main()
17+
{
18+
int arr[8];
19+
f1(arr);
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CORE
2+
with_statement_expression.c
3+
--dfcc main --enforce-contract f1
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[f1.postcondition.\d+\] line \d+ Check ensures clause( of contract contract::f1 for function f1)?: SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
^warning: ignoring
10+
--
11+
The purpose of this test is to ensure that we can safely use __CPROVER_exists within both
12+
positive and negative contexts (ENSURES and REQUIRES clauses).
13+
14+
With the SAT backend existential quantifiers in a positive context,
15+
e.g., the ENSURES clause being enforced in this case,
16+
are supported only if the quantifier is bound to a constant range.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include <stdlib.h>
2+
3+
#define MAX_LEN 8
4+
5+
// clang-format off
6+
int f1(int *arr, int len)
7+
__CPROVER_requires(
8+
len > 0 ==> __CPROVER_exists {
9+
int i;
10+
// constant bounds for explicit unrolling with SAT backend
11+
({ (0 <= i && i < MAX_LEN) && (
12+
// actual symbolic bound for `i`
13+
i < len && arr[i] == 0
14+
); })
15+
}
16+
)
17+
__CPROVER_ensures(
18+
len > 0 ==> __CPROVER_exists {
19+
int i;
20+
// constant bounds for explicit unrolling with SAT backend
21+
({ (0 <= i && i < MAX_LEN) && (
22+
// actual symbolic bound for `i`
23+
i < len && arr[i] == 0
24+
); })
25+
}
26+
)
27+
// clang-format on
28+
{
29+
return 0;
30+
}
31+
32+
int main()
33+
{
34+
int len;
35+
__CPROVER_assume(0 <= len && len <= MAX_LEN);
36+
37+
int *arr = malloc(len);
38+
if(len > 0)
39+
arr[0] = 0;
40+
41+
f1(arr, len);
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CORE
2+
with_statement_expression.c
3+
--no-malloc-may-fail --dfcc main --replace-call-with-contract f1 _ --no-standard-checks
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[f1.precondition.\d+\] line \d+ Check requires clause of (contract contract::f1 for function f1|f1 in main): SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
^warning: ignoring
10+
--
11+
The purpose of this test is to ensure that we can safely use __CPROVER_exists within both
12+
positive and negative contexts (ENSURES and REQUIRES clauses).
13+
14+
With the SAT backend existential quantifiers in a positive context,
15+
e.g., the REQUIRES clause being replaced in this case,
16+
are supported only if the quantifier is bound to a constant range.

regression/validate-trace-xml-schema/check.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
# the SAT back-end only
7575
['integer-assignments1', 'test.desc'],
7676
# this test is expected to abort, thus producing invalid XML
77-
['String_Abstraction17', 'test.desc']
77+
['String_Abstraction17', 'test.desc'],
78+
['Quantifiers1', 'quantifier-with-side-effect.desc']
7879
]))
7980

8081
# TODO maybe consider looking them up on PATH, but direct paths are

src/ansi-c/c_typecheck_expr.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,16 @@ void c_typecheck_baset::typecheck_expr_main(exprt &expr)
316316
}
317317
}
318318

319-
if(has_subexpr(where, ID_side_effect))
319+
if(has_subexpr(
320+
where,
321+
[&](const exprt &subexpr)
322+
{
323+
return can_cast_expr<side_effect_exprt>(subexpr) &&
324+
can_cast_expr<side_effect_expr_function_callt>(subexpr);
325+
}))
320326
{
321327
error().source_location = expr.source_location();
322-
error() << "quantifier must not contain side effects" << eom;
328+
error() << "quantifier must not contain function calls" << eom;
323329
throw 0;
324330
}
325331

0 commit comments

Comments
 (0)