Skip to content

Commit e190b7f

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 665abf7 commit e190b7f

File tree

7 files changed

+153
-26
lines changed

7 files changed

+153
-26
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
#define SIZE 8
5+
6+
struct blob
7+
{
8+
char *data;
9+
};
10+
11+
void main()
12+
{
13+
struct blob *b = malloc(sizeof(struct blob));
14+
b->data = malloc(SIZE);
15+
16+
b->data[5] = 0;
17+
for(unsigned i = 0; i < SIZE; i++)
18+
// clang-format off
19+
__CPROVER_assigns(b->data)
20+
__CPROVER_loop_invariant(i <= SIZE)
21+
// clang-format on
22+
{
23+
b->data[i] = 1;
24+
}
25+
26+
assert(b->data[5] == 0);
27+
}
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 the need for assigns clauses. Currently we only
13+
parse the assigns clause for loops, but there is no implementation
14+
to actually enforce them. In the future, we will add this feature.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
int r;
6+
__CPROVER_assume(r >= 0);
7+
while(r > 0)
8+
__CPROVER_assigns() __CPROVER_loop_invariant(r >= 0)
9+
{
10+
r--;
11+
}
12+
assert(r == 0);
13+
14+
return 0;
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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
13+
in loop contracts.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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) __CPROVER_decreases(r1)
9+
{
10+
s1 = 1;
11+
r1--;
12+
}
13+
assert(r1 == 0);
14+
15+
int r2, s2 = 1;
16+
__CPROVER_assume(r2 >= 0);
17+
while(r2 > 0)
18+
__CPROVER_assigns(r2, s2) __CPROVER_loop_invariant(r2 >= 0 && s2 == 1)
19+
__CPROVER_decreases(r2)
20+
{
21+
s2 = 1;
22+
r2--;
23+
}
24+
assert(r2 == 0);
25+
return 0;
26+
}
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.

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)