Skip to content

Commit c4b2bbb

Browse files
Long PhamLong Pham
Long Pham
authored and
Long Pham
committed
Implemented parsing and type checking for loop variants
1 parent f9be0eb commit c4b2bbb

File tree

20 files changed

+117
-15
lines changed

20 files changed

+117
-15
lines changed

regression/contracts/invar_check_break_pass/main.c

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

88
while(r > 0)
99
__CPROVER_loop_invariant(r >= 0)
10+
__CPROVER_decreases(r)
1011
{
1112
--r;
1213
if(r <= 1)

regression/contracts/invar_check_continue/main.c

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

88
while(r > 0)
99
__CPROVER_loop_invariant(r >= 0)
10+
__CPROVER_decreases(r)
1011
{
1112
--r;
1213
if(r < 5)

regression/contracts/invar_check_multiple_loops/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ int main()
77

88
for(r = 0; r < n; ++r)
99
__CPROVER_loop_invariant(0 <= r && r <= n && x == y + r)
10+
__CPROVER_decreases(n - r)
1011
{
1112
x++;
1213
}
1314
while(r > 0)
1415
__CPROVER_loop_invariant(r >= 0 && x == y + n + (n - r))
16+
__CPROVER_decreases(r)
1517
{
1618
y--;
1719
r--;

regression/contracts/invar_check_nested_loops/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ int main()
77

88
for(int i = 0; i < n; ++i)
99
__CPROVER_loop_invariant(i <= n && s == i)
10+
__CPROVER_decreases(n - i)
1011
{
1112
int a, b;
1213
__CPROVER_assume(b >= 0 && a == b);
1314

1415
while(a > 0)
1516
__CPROVER_loop_invariant(a >= 0 && s == i + (b - a))
17+
__CPROVER_decreases(a)
1618
{
1719
s++;
1820
a--;

regression/contracts/invar_check_pointer_modifies-01/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ void main()
99
unsigned i;
1010
while(i > 0)
1111
__CPROVER_loop_invariant(*data == 42)
12+
__CPROVER_decreases(i)
1213
{
1314
*data = 42;
1415
i--;

regression/contracts/invar_check_pointer_modifies-02/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ void main()
1111
unsigned i;
1212
while(i > 0)
1313
__CPROVER_loop_invariant(*data == 42)
14+
__CPROVER_decreases(i)
1415
{
1516
*data = 42;
1617
i--;

regression/contracts/invar_check_sufficiency/main.c

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

88
while(r > 0)
99
__CPROVER_loop_invariant(r >= 0)
10+
__CPROVER_decreases(r)
1011
{
1112
--r;
1213
}

regression/contracts/invar_loop_constant_no_modify/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ int main()
77
__CPROVER_assume(r >= 0);
88
while(r > 0)
99
__CPROVER_loop_invariant(r >= 0)
10+
__CPROVER_decreases(r)
1011
{
1112
r--;
1213
}

regression/contracts/invar_loop_constant_pass/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ int main()
66
__CPROVER_assume(r >= 0);
77
while(r > 0)
88
__CPROVER_loop_invariant(r >= 0 && s == 1)
9+
__CPROVER_decreases(r)
910
{
1011
s = 1;
1112
r--;

regression/contracts/invariant_side_effects/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ int main()
88
*a = 0;
99
while(*a < N)
1010
__CPROVER_loop_invariant((0 <= *a) && (*a <= N))
11+
__CPROVER_decreases(N - *a)
1112
{
1213
++(*a);
1314
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
int main()
3+
{
4+
int i = 0;
5+
int N = 10;
6+
while (i != N)
7+
__CPROVER_loop_invariant(0 <= i && i <= N)
8+
__CPROVER_decreases(int x = 0)
9+
{
10+
i++;
11+
}
12+
13+
return 0;
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^main.c: In function 'main':$
5+
^main.c:8:1: error: syntax error before 'int'$
6+
^ __CPROVER_decreases\(int x = 0\)$
7+
^PARSING ERROR$
8+
^EXIT=1$
9+
^SIGNAL=0$
10+
--
11+
--
12+
This test fails because the loop variant is a statement (more precisely,
13+
an assignment) rather than an expression (more precisely, an ACSL binding
14+
expression).
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
int main()
2+
{
3+
int i = 0;
4+
int N = 10;
5+
while (i != N)
6+
__CPROVER_loop_invariant(0 <= i && i <= N)
7+
__CPROVER_decreases(N - i, 42)
8+
{
9+
i++;
10+
}
11+
12+
return 0;
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^main.c: In function 'main':$
5+
^main.c:7:1: error: syntax error before ','$
6+
^ __CPROVER_decreases\(N - i, 42\)$
7+
^PARSING ERROR$
8+
^EXIT=1$
9+
^SIGNAL=0$
10+
--
11+
--
12+
This test fails because the loop variant has two arguments instead of just one.
13+
Currently, we only support loop variants of single arguments - we do not
14+
support loop variants of tuples (yet).

src/ansi-c/c_typecheck_base.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ class c_typecheck_baset:
144144
virtual void typecheck_while(code_whilet &code);
145145
virtual void typecheck_dowhile(code_dowhilet &code);
146146
virtual void typecheck_start_thread(codet &code);
147-
virtual void typecheck_spec_expr(codet &code, const irep_idt &spec);
147+
virtual void typecheck_loop_invariant(codet &code);
148+
virtual void typecheck_spec_decreases(codet &code);
148149

149150
bool break_is_allowed;
150151
bool continue_is_allowed;

src/ansi-c/c_typecheck_code.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,8 @@ void c_typecheck_baset::typecheck_for(codet &code)
484484
typecheck_code(code); // recursive call
485485
}
486486

487-
typecheck_spec_expr(code, ID_C_spec_loop_invariant);
487+
typecheck_loop_invariant(code);
488+
typecheck_spec_decreases(code);
488489
}
489490

490491
void c_typecheck_baset::typecheck_label(code_labelt &code)
@@ -772,7 +773,8 @@ void c_typecheck_baset::typecheck_while(code_whilet &code)
772773
break_is_allowed=old_break_is_allowed;
773774
continue_is_allowed=old_continue_is_allowed;
774775

775-
typecheck_spec_expr(code, ID_C_spec_loop_invariant);
776+
typecheck_loop_invariant(code);
777+
typecheck_spec_decreases(code);
776778
}
777779

778780
void c_typecheck_baset::typecheck_dowhile(code_dowhilet &code)
@@ -805,16 +807,28 @@ void c_typecheck_baset::typecheck_dowhile(code_dowhilet &code)
805807
break_is_allowed=old_break_is_allowed;
806808
continue_is_allowed=old_continue_is_allowed;
807809

808-
typecheck_spec_expr(code, ID_C_spec_loop_invariant);
810+
typecheck_loop_invariant(code);
811+
typecheck_spec_decreases(code);
809812
}
810813

811-
void c_typecheck_baset::typecheck_spec_expr(codet &code, const irep_idt &spec)
814+
void c_typecheck_baset::typecheck_loop_invariant(codet &code)
812815
{
813-
if(code.find(spec).is_not_nil())
816+
if(code.find(ID_C_spec_loop_invariant).is_not_nil())
814817
{
815-
exprt &constraint = static_cast<exprt &>(code.add(spec));
818+
exprt &invariant = static_cast<exprt &>(code.add(ID_C_spec_loop_invariant));
816819

817-
typecheck_expr(constraint);
818-
implicit_typecast_bool(constraint);
820+
typecheck_expr(invariant);
821+
implicit_typecast_bool(invariant);
822+
}
823+
}
824+
825+
void c_typecheck_baset::typecheck_spec_decreases(codet &code)
826+
{
827+
if(code.find(ID_C_spec_decreases).is_not_nil())
828+
{
829+
exprt &variant = static_cast<exprt &>(code.add(ID_C_spec_decreases));
830+
831+
typecheck_expr(variant);
832+
implicit_typecast_arithmetic(variant);
819833
}
820834
}

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ void c_typecheck_baset::typecheck_expr(exprt &expr)
5151
return;
5252
}
5353

54-
// fist do sub-nodes
54+
// first do sub-nodes
5555
typecheck_expr_operands(expr);
5656

5757
// now do case-split

src/ansi-c/parser.y

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ extern char *yyansi_ctext;
203203
%token TOK_CPROVER_FINALLY "__CPROVER_finally"
204204
%token TOK_CPROVER_ID "__CPROVER_ID"
205205
%token TOK_CPROVER_LOOP_INVARIANT "__CPROVER_loop_invariant"
206+
%token TOK_CPROVER_DECREASES "__CPROVER_decreases"
206207
%token TOK_CPROVER_REQUIRES "__CPROVER_requires"
207208
%token TOK_CPROVER_ENSURES "__CPROVER_ensures"
208209
%token TOK_CPROVER_ASSIGNS "__CPROVER_assigns"
@@ -497,6 +498,13 @@ loop_invariant_opt:
497498
{ $$=$3; }
498499
;
499500

501+
cprover_decreases_opt:
502+
/* nothing */
503+
{ init($$); parser_stack($$).make_nil(); }
504+
| TOK_CPROVER_DECREASES '(' ACSL_binding_expression ')'
505+
{ $$=$3; }
506+
;
507+
500508
statement_expression: '(' compound_statement ')'
501509
{
502510
$$=$1;
@@ -2418,24 +2426,31 @@ declaration_or_expression_statement:
24182426

24192427
iteration_statement:
24202428
TOK_WHILE '(' comma_expression_opt ')'
2421-
loop_invariant_opt statement
2429+
loop_invariant_opt cprover_decreases_opt
2430+
statement
24222431
{
24232432
$$=$1;
24242433
statement($$, ID_while);
2425-
parser_stack($$).add_to_operands(std::move(parser_stack($3)), std::move(parser_stack($6)));
2434+
parser_stack($$).add_to_operands(std::move(parser_stack($3)), std::move(parser_stack($7)));
24262435

24272436
if(parser_stack($5).is_not_nil())
24282437
parser_stack($$).add(ID_C_spec_loop_invariant).swap(parser_stack($5));
2438+
2439+
if(parser_stack($6).is_not_nil())
2440+
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($6));
24292441
}
24302442
| TOK_DO statement TOK_WHILE '(' comma_expression ')'
2431-
loop_invariant_opt ';'
2443+
loop_invariant_opt cprover_decreases_opt ';'
24322444
{
24332445
$$=$1;
24342446
statement($$, ID_dowhile);
24352447
parser_stack($$).add_to_operands(std::move(parser_stack($5)), std::move(parser_stack($2)));
24362448

24372449
if(parser_stack($7).is_not_nil())
24382450
parser_stack($$).add(ID_C_spec_loop_invariant).swap(parser_stack($7));
2451+
2452+
if(parser_stack($8).is_not_nil())
2453+
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($8));
24392454
}
24402455
| TOK_FOR
24412456
{
@@ -2449,7 +2464,7 @@ iteration_statement:
24492464
'(' declaration_or_expression_statement
24502465
comma_expression_opt ';'
24512466
comma_expression_opt ')'
2452-
loop_invariant_opt
2467+
loop_invariant_opt cprover_decreases_opt
24532468
statement
24542469
{
24552470
$$=$1;
@@ -2458,11 +2473,14 @@ iteration_statement:
24582473
mto($$, $4);
24592474
mto($$, $5);
24602475
mto($$, $7);
2461-
mto($$, $10);
2476+
mto($$, $11);
24622477

24632478
if(parser_stack($9).is_not_nil())
24642479
parser_stack($$).add(ID_C_spec_loop_invariant).swap(parser_stack($9));
24652480

2481+
if(parser_stack($10).is_not_nil())
2482+
parser_stack($$).add(ID_C_spec_decreases).swap(parser_stack($10));
2483+
24662484
if(PARSER.for_has_scope)
24672485
PARSER.pop_scope(); // remove the C99 for-scope
24682486
}

src/ansi-c/scanner.l

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,7 @@ __decltype { if(PARSER.cpp98 &&
12811281
{CPROVER_PREFIX}"finally" { loc(); return TOK_CPROVER_FINALLY; }
12821282
{CPROVER_PREFIX}"ID" { loc(); return TOK_CPROVER_ID; }
12831283
{CPROVER_PREFIX}"loop_invariant" { loc(); return TOK_CPROVER_LOOP_INVARIANT; }
1284+
{CPROVER_PREFIX}"decreases" { loc(); return TOK_CPROVER_DECREASES; }
12841285
{CPROVER_PREFIX}"requires" { loc(); return TOK_CPROVER_REQUIRES; }
12851286
{CPROVER_PREFIX}"ensures" { loc(); return TOK_CPROVER_ENSURES; }
12861287
{CPROVER_PREFIX}"assigns" { loc(); return TOK_CPROVER_ASSIGNS; }

src/util/irep_ids.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ IREP_ID_ONE(is_weak)
572572
IREP_ID_ONE(used)
573573
IREP_ID_ONE(is_used)
574574
IREP_ID_TWO(C_spec_loop_invariant, #spec_loop_invariant)
575+
IREP_ID_TWO(C_spec_decreases, #spec_decreases)
575576
IREP_ID_TWO(C_spec_requires, #spec_requires)
576577
IREP_ID_TWO(C_spec_ensures, #spec_ensures)
577578
IREP_ID_TWO(C_spec_assigns, #spec_assigns)

0 commit comments

Comments
 (0)