Skip to content

Commit dbe9e64

Browse files
author
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. Renamed loop_invariant to cprover_contract_loop_invariant. 5. Renamed loop_invariant_list to cprover_contract_loop_invariant_list. 6. Renamed loop_invariant_list_opt to cprover_contract_loop_invariant_list_opt. 7. Added cprover_contract_assigns_opt to iteration statement. 8. Created the regression test invar_assigns_alias_analysis (KNOWNBUG). 9. Created the regression test invar_assigns_opt (CORE). 10. Created the regression test invar_assigns_empty (CORE). This adds parser support for assigns contracts on loops.
1 parent 856a729 commit dbe9e64

File tree

11 files changed

+158
-29
lines changed

11 files changed

+158
-29
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
// clang-format off
16+
__CPROVER_assigns(b->data)
17+
__CPROVER_loop_invariant(i <= SIZE)
18+
// clang-format on
19+
{
20+
b->data[i] = 1;
21+
}
22+
23+
assert(b->data[5] == 0);
24+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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 that the current implementation only supports
13+
parsing and still uses alias analysis. Fixing this is a part
14+
of future work.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
int r;
6+
__CPROVER_assume(r >= 0);
7+
while(r > 0)
8+
__CPROVER_assigns()
9+
__CPROVER_loop_invariant(r >= 0)
10+
{
11+
r--;
12+
}
13+
assert(r == 0);
14+
15+
return 0;
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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 r == 0: SUCCESS$
9+
^VERIFICATION SUCCESSFUL$
10+
--
11+
--
12+
This test checks that empty assigns clause is supported.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
int r1, s1 = 1;
6+
__CPROVER_assume(r1 >= 0);
7+
while(r1 > 0)
8+
__CPROVER_loop_invariant(r1 >= 0 && s1 == 1)
9+
__CPROVER_decreases(r1)
10+
{
11+
s1 = 1;
12+
r1--;
13+
}
14+
assert(r1 == 0);
15+
assert(s1 == 1);
16+
17+
int r2, s2 = 1;
18+
__CPROVER_assume(r2 >= 0);
19+
while(r2 > 0)
20+
__CPROVER_assigns(r2, s2)
21+
__CPROVER_loop_invariant(r2 >= 0 && s2 == 1)
22+
__CPROVER_decreases(r2)
23+
{
24+
s2 = 1;
25+
r2--;
26+
}
27+
assert(r2 == 0);
28+
assert(s2 == 1);
29+
return 0;
30+
}
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.3\] .* Check decreases clause on loop iteration: SUCCESS$
9+
^\[main.assertion.1\] .* assertion r1 == 0: SUCCESS$
10+
^\[main.4\] .* Check loop invariant before entry: SUCCESS$
11+
^\[main.5\] .* Check that loop invariant is preserved: SUCCESS$
12+
^\[main.6\] .* Check decreases clause on loop iteration: SUCCESS$
13+
^\[main.assertion.2\] .* assertion r2 == 0: 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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ int main()
2727
assert(x == y + 2 * n);
2828

2929
return 0;
30-
}
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: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -491,25 +491,25 @@ quantifier_expression:
491491
}
492492
;
493493

494-
loop_invariant:
494+
cprover_contract_loop_invariant:
495495
TOK_CPROVER_LOOP_INVARIANT '(' ACSL_binding_expression ')'
496496
{ $$=$3; }
497497
;
498498

499-
loop_invariant_list:
500-
loop_invariant
499+
cprover_contract_loop_invariant_list:
500+
cprover_contract_loop_invariant
501501
{ init($$); mto($$, $1); }
502-
| loop_invariant_list loop_invariant
502+
| cprover_contract_loop_invariant_list cprover_contract_loop_invariant
503503
{ $$=$1; mto($$, $2); }
504504
;
505505

506-
loop_invariant_list_opt:
506+
cprover_contract_loop_invariant_list_opt:
507507
/* nothing */
508508
{ init($$); parser_stack($$).make_nil(); }
509-
| loop_invariant_list
509+
| cprover_contract_loop_invariant_list
510510
;
511511

512-
cprover_decreases_opt:
512+
cprover_contract_decreases_opt:
513513
/* nothing */
514514
{ init($$); parser_stack($$).make_nil(); }
515515
| TOK_CPROVER_DECREASES '(' ACSL_binding_expression ')'
@@ -2437,31 +2437,35 @@ declaration_or_expression_statement:
24372437

24382438
iteration_statement:
24392439
TOK_WHILE '(' comma_expression_opt ')'
2440-
loop_invariant_list_opt cprover_decreases_opt
2440+
cprover_contract_assigns_opt
2441+
cprover_contract_loop_invariant_list_opt
2442+
cprover_contract_decreases_opt
24412443
statement
24422444
{
24432445
$$=$1;
24442446
statement($$, ID_while);
2445-
parser_stack($$).add_to_operands(std::move(parser_stack($3)), std::move(parser_stack($7)));
2447+
parser_stack($$).add_to_operands(std::move(parser_stack($3)), std::move(parser_stack($8)));
24462448

2447-
if(!parser_stack($5).operands().empty())
2448-
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($5).operands());
2449+
if(!parser_stack($6).operands().empty())
2450+
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($6).operands());
24492451

2450-
if(parser_stack($6).is_not_nil())
2451-
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($6));
2452+
if(parser_stack($7).is_not_nil())
2453+
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($7));
24522454
}
24532455
| TOK_DO statement TOK_WHILE '(' comma_expression ')'
2454-
loop_invariant_list_opt cprover_decreases_opt ';'
2456+
cprover_contract_assigns_opt
2457+
cprover_contract_loop_invariant_list_opt
2458+
cprover_contract_decreases_opt ';'
24552459
{
24562460
$$=$1;
24572461
statement($$, ID_dowhile);
24582462
parser_stack($$).add_to_operands(std::move(parser_stack($5)), std::move(parser_stack($2)));
24592463

2460-
if(!parser_stack($7).operands().empty())
2461-
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($7).operands());
2464+
if(!parser_stack($8).operands().empty())
2465+
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($8).operands());
24622466

2463-
if(parser_stack($8).is_not_nil())
2464-
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($8));
2467+
if(parser_stack($9).is_not_nil())
2468+
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($9));
24652469
}
24662470
| TOK_FOR
24672471
{
@@ -2475,7 +2479,9 @@ iteration_statement:
24752479
'(' declaration_or_expression_statement
24762480
comma_expression_opt ';'
24772481
comma_expression_opt ')'
2478-
loop_invariant_list_opt cprover_decreases_opt
2482+
cprover_contract_assigns_opt
2483+
cprover_contract_loop_invariant_list_opt
2484+
cprover_contract_decreases_opt
24792485
statement
24802486
{
24812487
$$=$1;
@@ -2484,13 +2490,13 @@ iteration_statement:
24842490
mto($$, $4);
24852491
mto($$, $5);
24862492
mto($$, $7);
2487-
mto($$, $11);
2493+
mto($$, $12);
24882494

2489-
if(!parser_stack($9).operands().empty())
2490-
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($9).operands());
2495+
if(!parser_stack($10).operands().empty())
2496+
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($10).operands());
24912497

2492-
if(parser_stack($10).is_not_nil())
2493-
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($10));
2498+
if(parser_stack($11).is_not_nil())
2499+
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($11));
24942500

24952501
if(PARSER.for_has_scope)
24962502
PARSER.pop_scope(); // remove the C99 for-scope
@@ -3272,10 +3278,10 @@ cprover_function_contract:
32723278
set($$, ID_C_spec_requires);
32733279
mto($$, $3);
32743280
}
3275-
| cprover_contract_assigns_opt
3281+
| cprover_contract_assigns
32763282
;
32773283

3278-
cprover_contract_assigns_opt:
3284+
cprover_contract_assigns:
32793285
TOK_CPROVER_ASSIGNS '(' argument_expression_list ')'
32803286
{
32813287
$$=$1;
@@ -3291,6 +3297,12 @@ cprover_contract_assigns_opt:
32913297
}
32923298
;
32933299

3300+
cprover_contract_assigns_opt:
3301+
/* nothing */
3302+
{ init($$); parser_stack($$).make_nil(); }
3303+
| cprover_contract_assigns
3304+
;
3305+
32943306
cprover_function_contract_sequence:
32953307
cprover_function_contract
32963308
| cprover_function_contract_sequence cprover_function_contract

0 commit comments

Comments
 (0)