Skip to content

Commit 86cb295

Browse files
ArenBabikianSaswatPadhi
authored andcommitted
Add support for quantifiers in loop contracts
1 parent ef9e076 commit 86cb295

File tree

5 files changed

+100
-6
lines changed

5 files changed

+100
-6
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <assert.h>
2+
3+
void main()
4+
{
5+
int N = 64, a[64];
6+
7+
for(int i = 0; i < N; ++i)
8+
// clang-format off
9+
__CPROVER_loop_invariant(
10+
(0 <= i) && (i <= N) &&
11+
__CPROVER_forall {
12+
int k;
13+
(0 <= k && k < N) ==> 1 == 1
14+
}
15+
)
16+
// clang-format on
17+
{
18+
a[i] = 1;
19+
}
20+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.1\] line .* Check loop invariant before entry: SUCCESS
7+
^\[main.2\] line .* Check that loop invariant is preserved: SUCCESS
8+
^VERIFICATION SUCCESSFUL$
9+
--
10+
--
11+
This test case checks the handling of a `forall` quantifier within a loop invariant.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <assert.h>
2+
3+
void main()
4+
{
5+
int N, a[64];
6+
__CPROVER_assume(0 <= N && N < 64);
7+
8+
for(int i = 0; i < N; ++i)
9+
// clang-format off
10+
__CPROVER_loop_invariant(
11+
(0 <= i) && (i <= N) &&
12+
__CPROVER_forall {
13+
int k;
14+
(0 <= k && k < i) ==> a[k] == 1
15+
}
16+
)
17+
// clang-format on
18+
{
19+
a[i] = 1;
20+
}
21+
22+
// clang-format off
23+
assert(__CPROVER_forall {
24+
int k;
25+
(0 <= k && k < N) ==> a[k] == 1
26+
});
27+
// clang-format on
28+
29+
int k;
30+
__CPROVER_assume(0 <= k && k < N);
31+
assert(a[k] == 1);
32+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
CORE --smt-backend --broken-cprover-smt-backend
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.1\] line .* Check loop invariant before entry: SUCCESS
7+
^\[main.2\] line .* Check that loop invariant is preserved: SUCCESS
8+
^\[main.assertion.1\] line .* assertion __CPROVER_forall \{ int k; \(0 <= k && k < N\) ==> a\[k\] == 1 \}: SUCCESS
9+
^VERIFICATION SUCCESSFUL$
10+
--
11+
--
12+
This test case checks the handling of a `forall` quantifier, with a symbolic
13+
upper bound, within a loop invariant.
14+
15+
The test is tagged:
16+
- `--smt-backend`:
17+
because the SAT backend does not support (simply ignores) `forall` in negative (e.g. assume) contexts.
18+
- `--broken-cprover-smt-backend`:
19+
because the CPROVER SMT2 solver cannot handle (errors out on) `forall` in negative (e.g. assume) contexts.

src/goto-instrument/code_contracts.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,19 @@ void code_contractst::check_apply_invariants(
114114
// build the havocking code
115115
goto_programt havoc_code;
116116

117+
// process quantified variables correctly (introduce a fresh temporary)
118+
// and return a copy of the invariant
119+
const auto &invariant_expr = [&]() {
120+
auto invariant_copy = invariant;
121+
replace_symbolt replace;
122+
code_contractst::add_quantified_variable(invariant_copy, replace, mode);
123+
replace(invariant_copy);
124+
return invariant_copy;
125+
};
126+
117127
// assert the invariant
118128
{
119-
code_assertt assertion{invariant};
129+
code_assertt assertion{invariant_expr()};
120130
assertion.add_source_location() = loop_head->source_location;
121131
converter.goto_convert(assertion, havoc_code, mode);
122132
havoc_code.instructions.back().source_location.set_comment(
@@ -127,9 +137,11 @@ void code_contractst::check_apply_invariants(
127137
build_havoc_code(loop_head, modifies, havoc_code);
128138

129139
// assume the invariant
130-
code_assumet assumption{invariant};
131-
assumption.add_source_location() = loop_head->source_location;
132-
converter.goto_convert(assumption, havoc_code, mode);
140+
{
141+
code_assumet assumption{invariant_expr()};
142+
assumption.add_source_location() = loop_head->source_location;
143+
converter.goto_convert(assumption, havoc_code, mode);
144+
}
133145

134146
// non-deterministically skip the loop if it is a do-while loop
135147
if(!loop_head->is_goto())
@@ -145,7 +157,7 @@ void code_contractst::check_apply_invariants(
145157

146158
// assert the invariant at the end of the loop body
147159
{
148-
code_assertt assertion{invariant};
160+
code_assertt assertion{invariant_expr()};
149161
assertion.add_source_location() = loop_end->source_location;
150162
converter.goto_convert(assertion, havoc_code, mode);
151163
havoc_code.instructions.back().source_location.set_comment(
@@ -354,7 +366,7 @@ bool code_contractst::apply_function_contract(
354366

355367
// Create a replace_symbolt object, for replacing expressions in the callee
356368
// with expressions from the call site (e.g. the return value).
357-
// This object tracks replacements that are common to both ENSURES and REQUIRES.
369+
// This object tracks replacements that are common to ENSURES and REQUIRES.
358370
replace_symbolt common_replace;
359371
if(type.return_type() != empty_typet())
360372
{

0 commit comments

Comments
 (0)