Skip to content

Commit 64b5182

Browse files
martin-csAalok Thakkar
authored and
Aalok Thakkar
committed
Parser support for assigns contracts on loops
1. Renamed cprover_contract_assigns_opt to cprover_contract_assigns. 2. Created cprover_contract_assigns_opt which has a unit production and cprover_contract_assigns. 3. Renamed cprover_decreases_opt to cprover_contract_decreases_opt. 4. Added cprover_contract_assigns_opt to iteration statement. This adds parser support for assigns contracts on loops.
1 parent 845343e commit 64b5182

File tree

9 files changed

+129
-24
lines changed

9 files changed

+129
-24
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
#define SIZE 8
5+
6+
struct blob { char *data; };
7+
8+
void main()
9+
{
10+
struct blob *b = malloc(sizeof(struct blob));
11+
b->data = malloc(SIZE);
12+
13+
b->data[5] = 0;
14+
for (unsigned i = 0; i < SIZE; i++)
15+
__CPROVER_assigns(b->data)
16+
__CPROVER_loop_invariant(i <= SIZE)
17+
{
18+
b->data[i] = 1;
19+
}
20+
21+
assert(b->data[5] == 0);
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
KNOWNBUG
2+
main.c
3+
--apply-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.2\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.assertion.1\] .* assertion b->data[5] == 0: FAILURE$
9+
^VERIFICATION FAILED$
10+
--
11+
--
12+
This test shows a known bug in the current implementation
13+
for assigns clause. Fixing this is a part of future work.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
int r1, s1 = 1;
6+
__CPROVER_assume(r1 >= 0);
7+
while(r1 > 0)
8+
// clang-format off
9+
__CPROVER_loop_invariant(r1 >= 0 && s1 == 1)
10+
__CPROVER_decreases(r1)
11+
// clang-format on
12+
{
13+
s1 = 1;
14+
r1--;
15+
}
16+
assert(r1 == 0);
17+
assert(s1 == 1);
18+
19+
int r2, s2 = 1;
20+
__CPROVER_assume(r2 >= 0);
21+
while(r2 > 0)
22+
// clang-format off
23+
__CPROVER_assigns(r2, s2)
24+
__CPROVER_loop_invariant(r2 >= 0 && s2 == 1)
25+
__CPROVER_decreases(r2)
26+
// clang-format on
27+
{
28+
s2 = 1;
29+
r2--;
30+
}
31+
assert(r2 == 0);
32+
assert(s2 == 1);
33+
34+
return 0;
35+
36+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.2\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.assertion.1\] .* assertion r1 == 0: SUCCESS$
9+
^\[main.assertion.2\] .* assertion s1 == 1: SUCCESS$
10+
^\[main.3\] .* Check loop invariant before entry: SUCCESS$
11+
^\[main.4\] .* Check that loop invariant is preserved: SUCCESS$
12+
^\[main.assertion.3\] .* assertion r2 == 0: SUCCESS$
13+
^\[main.assertion.4\] .* assertion s2 == 1: SUCCESS$
14+
^VERIFICATION SUCCESSFUL$
15+
--
16+
--
17+
This test checks that adding assigns clause is optional
18+
and that if a proof does not require it, then adding an
19+
appropriate assigns clause does not lead to any
20+
unexpected behavior.

regression/contracts/invar_check_multiple_loops/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ int main()
77

88
for(r = 0; r < n; ++r)
99
// clang-format off
10+
__CPROVER_assigns(x)
1011
__CPROVER_loop_invariant(0 <= r && r <= n && x == y + r)
1112
__CPROVER_decreases(n - r)
1213
// clang-format on
@@ -26,4 +27,4 @@ int main()
2627
assert(x == y + 2 * n);
2728

2829
return 0;
29-
}
30+
}

regression/contracts/invar_check_nested_loops/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ int main()
1616

1717
while(a > 0)
1818
// clang-format off
19+
__CPROVER_assigns(s, a)
1920
__CPROVER_loop_invariant(a >= 0 && s == i + (b - a))
2021
__CPROVER_decreases(a)
2122
// clang-format on

regression/contracts/invar_dynamic_struct_member/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ void main()
99
t->x = 0;
1010

1111
unsigned n;
12-
for(unsigned i = 0; i < n; ++i)
12+
for(unsigned i = 0; i < n; ++i)
1313
__CPROVER_loop_invariant(i <= n)
1414
{
1515
switch(i % 4)

regression/contracts/invar_loop_constant_pass/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ main.c
1010
^VERIFICATION SUCCESSFUL$
1111
--
1212
This test checks that the invariant is correctly used to satisfy
13-
all assertions that follow a loop.
13+
all assertions that follow a loop.

src/ansi-c/parser.y

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -491,14 +491,14 @@ quantifier_expression:
491491
}
492492
;
493493

494-
loop_invariant_opt:
494+
cprover_contract_invariant_opt:
495495
/* nothing */
496496
{ init($$); parser_stack($$).make_nil(); }
497497
| TOK_CPROVER_LOOP_INVARIANT '(' ACSL_binding_expression ')'
498498
{ $$=$3; }
499499
;
500500

501-
cprover_decreases_opt:
501+
cprover_contract_decreases_opt:
502502
/* nothing */
503503
{ init($$); parser_stack($$).make_nil(); }
504504
| TOK_CPROVER_DECREASES '(' ACSL_binding_expression ')'
@@ -2426,31 +2426,35 @@ declaration_or_expression_statement:
24262426

24272427
iteration_statement:
24282428
TOK_WHILE '(' comma_expression_opt ')'
2429-
loop_invariant_opt cprover_decreases_opt
2429+
cprover_contract_assigns_opt
2430+
cprover_contract_invariant_opt
2431+
cprover_contract_decreases_opt
24302432
statement
24312433
{
24322434
$$=$1;
24332435
statement($$, ID_while);
2434-
parser_stack($$).add_to_operands(std::move(parser_stack($3)), std::move(parser_stack($7)));
2435-
2436-
if(parser_stack($5).is_not_nil())
2437-
parser_stack($$).add(ID_C_spec_loop_invariant).swap(parser_stack($5));
2436+
parser_stack($$).add_to_operands(std::move(parser_stack($3)), std::move(parser_stack($8)));
24382437

24392438
if(parser_stack($6).is_not_nil())
2440-
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($6));
2439+
parser_stack($$).add(ID_C_spec_loop_invariant).swap(parser_stack($6));
2440+
2441+
if(parser_stack($7).is_not_nil())
2442+
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($7));
24412443
}
24422444
| TOK_DO statement TOK_WHILE '(' comma_expression ')'
2443-
loop_invariant_opt cprover_decreases_opt ';'
2445+
cprover_contract_assigns_opt
2446+
cprover_contract_invariant_opt
2447+
cprover_contract_decreases_opt ';'
24442448
{
24452449
$$=$1;
24462450
statement($$, ID_dowhile);
24472451
parser_stack($$).add_to_operands(std::move(parser_stack($5)), std::move(parser_stack($2)));
24482452

2449-
if(parser_stack($7).is_not_nil())
2450-
parser_stack($$).add(ID_C_spec_loop_invariant).swap(parser_stack($7));
2451-
24522453
if(parser_stack($8).is_not_nil())
2453-
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($8));
2454+
parser_stack($$).add(ID_C_spec_loop_invariant).swap(parser_stack($8));
2455+
2456+
if(parser_stack($9).is_not_nil())
2457+
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($9));
24542458
}
24552459
| TOK_FOR
24562460
{
@@ -2464,7 +2468,9 @@ iteration_statement:
24642468
'(' declaration_or_expression_statement
24652469
comma_expression_opt ';'
24662470
comma_expression_opt ')'
2467-
loop_invariant_opt cprover_decreases_opt
2471+
cprover_contract_assigns_opt
2472+
cprover_contract_invariant_opt
2473+
cprover_contract_decreases_opt
24682474
statement
24692475
{
24702476
$$=$1;
@@ -2473,13 +2479,13 @@ iteration_statement:
24732479
mto($$, $4);
24742480
mto($$, $5);
24752481
mto($$, $7);
2476-
mto($$, $11);
2477-
2478-
if(parser_stack($9).is_not_nil())
2479-
parser_stack($$).add(ID_C_spec_loop_invariant).swap(parser_stack($9));
2482+
mto($$, $12);
24802483

24812484
if(parser_stack($10).is_not_nil())
2482-
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($10));
2485+
parser_stack($$).add(ID_C_spec_loop_invariant).swap(parser_stack($10));
2486+
2487+
if(parser_stack($11).is_not_nil())
2488+
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($11));
24832489

24842490
if(PARSER.for_has_scope)
24852491
PARSER.pop_scope(); // remove the C99 for-scope
@@ -3261,10 +3267,10 @@ cprover_function_contract:
32613267
set($$, ID_C_spec_requires);
32623268
mto($$, $3);
32633269
}
3264-
| cprover_contract_assigns_opt
3270+
| cprover_contract_assigns
32653271
;
32663272

3267-
cprover_contract_assigns_opt:
3273+
cprover_contract_assigns:
32683274
TOK_CPROVER_ASSIGNS '(' argument_expression_list ')'
32693275
{
32703276
$$=$1;
@@ -3274,6 +3280,12 @@ cprover_contract_assigns_opt:
32743280
}
32753281
;
32763282

3283+
cprover_contract_assigns_opt:
3284+
/* nothing */
3285+
{ init($$); parser_stack($$).make_nil(); }
3286+
| cprover_contract_assigns
3287+
;
3288+
32773289
cprover_function_contract_sequence:
32783290
cprover_function_contract
32793291
| cprover_function_contract_sequence cprover_function_contract

0 commit comments

Comments
 (0)