Skip to content

Commit f9f9581

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.# Changes to be committed:
1 parent f55f636 commit f9f9581

File tree

15 files changed

+358
-13
lines changed

15 files changed

+358
-13
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,9 @@
1+
CORE
2+
quantifier-with-function-call.c
3+
^EXIT=134$
4+
^SIGNAL=0$
5+
--- begin invariant violation report ---
6+
Invariant check failed
7+
^Reason: quantifier must not contain side effects
8+
--
9+
^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,9 @@
11
CORE
22
quantifier-with-side-effect.c
3-
4-
^EXIT=6$
3+
^EXIT=134$
54
^SIGNAL=0$
6-
^file .* line 10 function main: quantifier must not contain side effects$
5+
--- begin invariant violation report ---
6+
Invariant check failed
7+
^Reason: quantifier must not contain side effects
78
--
89
^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.

src/ansi-c/c_typecheck_expr.cpp

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

319-
if(has_subexpr(where, ID_side_effect))
319+
if(expr.id() == ID_lambda && has_subexpr(where, ID_side_effect))
320320
{
321321
error().source_location = expr.source_location();
322-
error() << "quantifier must not contain side effects" << eom;
322+
error() << "lambda expressions must not contain side effects" << eom;
323323
throw 0;
324324
}
325325

0 commit comments

Comments
 (0)