Skip to content

Commit e0da7e7

Browse files
committed
parsing, type checking & conversion of loop assigns clauses
In this commit we implement the parsing actions, type checking and conversion routines for assigns clauses on loops. The syntax rules along with regressions tests were introduced in an earlier PR: #6196.
1 parent 2499f8a commit e0da7e7

File tree

13 files changed

+84
-42
lines changed

13 files changed

+84
-42
lines changed

regression/contracts/assigns_enforce_address_of/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: illegal target in assigns clause$
6+
^.*error: non-lvalue target in assigns clause$
77
^CONVERSION ERROR$
88
--
99
--

regression/contracts/assigns_enforce_elvis_4/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: void-typed targets not permitted$
6+
^.*error: void-typed targets not permitted in assigns clause$
77
--
88
--
99
Check that Elvis operator expressions '*(cond ? if_true : if_false)' with different types in true and false branches are rejected.

regression/contracts/assigns_enforce_function_calls/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: illegal target in assigns clause$
6+
^.*error: non-lvalue target in assigns clause$
77
^CONVERSION ERROR$
88
--
99
--

regression/contracts/assigns_enforce_literal/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: illegal target in assigns clause$
6+
^.*error: non-lvalue target in assigns clause$
77
^CONVERSION ERROR$
88
--
99
--

regression/contracts/assigns_enforce_side_effects_1/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: void-typed targets not permitted$
6+
^.*error: void-typed targets not permitted in assigns clause$
77
^CONVERSION ERROR$
88
--
99
--

regression/contracts/assigns_enforce_side_effects_2/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: illegal target in assigns clause$
6+
^.*error: non-lvalue target in assigns clause$
77
^CONVERSION ERROR$
88
--
99
--

regression/contracts/assigns_enforce_side_effects_3/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: illegal target in assigns clause$
6+
^.*error: non-lvalue target in assigns clause$
77
^CONVERSION ERROR$
88
--
99
--

regression/contracts/assigns_type_checking_invalid_case_01/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ main.c
44
^EXIT=(1|64)$
55
^SIGNAL=0$
66
^CONVERSION ERROR$
7-
error: illegal target in assigns clause
7+
error: non-lvalue target in assigns clause
88
--
99
Checks whether type checking reports illegal target in assigns clause.

src/ansi-c/c_typecheck_base.cpp

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -739,33 +739,7 @@ void c_typecheck_baset::typecheck_declaration(
739739
CPROVER_PREFIX "loop_entry is not allowed in preconditions.");
740740
}
741741

742-
for(auto &target : code_type.assigns())
743-
{
744-
typecheck_expr(target);
745-
746-
if(target.type().id() == ID_empty)
747-
{
748-
error().source_location = target.source_location();
749-
error() << "void-typed targets not permitted" << eom;
750-
throw 0;
751-
}
752-
else if(target.id() == ID_pointer_object)
753-
{
754-
// skip
755-
}
756-
else if(!target.get_bool(ID_C_lvalue))
757-
{
758-
error().source_location = target.source_location();
759-
error() << "illegal target in assigns clause" << eom;
760-
throw 0;
761-
}
762-
else if(has_subexpr(target, ID_side_effect))
763-
{
764-
error().source_location = target.source_location();
765-
error() << "assigns clause is not side-effect free" << eom;
766-
throw 0;
767-
}
768-
}
742+
typecheck_spec_assigns(code_type.assigns());
769743

770744
if(!as_const(code_type).ensures().empty())
771745
{

src/ansi-c/c_typecheck_base.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ 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+
148+
// contracts
149+
virtual void typecheck_spec_assigns(exprt::operandst &targets);
147150
virtual void typecheck_spec_loop_invariant(codet &code);
148151
virtual void typecheck_spec_decreases(codet &code);
149152

src/ansi-c/c_typecheck_code.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Author: Daniel Kroening, [email protected]
1313

1414
#include <util/c_types.h>
1515
#include <util/config.h>
16+
#include <util/expr_util.h>
1617
#include <util/range.h>
1718
#include <util/string_constant.h>
1819

@@ -493,6 +494,12 @@ void c_typecheck_baset::typecheck_for(codet &code)
493494

494495
typecheck_spec_loop_invariant(code);
495496
typecheck_spec_decreases(code);
497+
498+
if(code.find(ID_C_spec_assigns).is_not_nil())
499+
{
500+
typecheck_spec_assigns(
501+
static_cast<unary_exprt &>(code.add(ID_C_spec_assigns)).op().operands());
502+
}
496503
}
497504

498505
void c_typecheck_baset::typecheck_label(code_labelt &code)
@@ -782,6 +789,12 @@ void c_typecheck_baset::typecheck_while(code_whilet &code)
782789

783790
typecheck_spec_loop_invariant(code);
784791
typecheck_spec_decreases(code);
792+
793+
if(code.find(ID_C_spec_assigns).is_not_nil())
794+
{
795+
typecheck_spec_assigns(
796+
static_cast<unary_exprt &>(code.add(ID_C_spec_assigns)).op().operands());
797+
}
785798
}
786799

787800
void c_typecheck_baset::typecheck_dowhile(code_dowhilet &code)
@@ -816,6 +829,43 @@ void c_typecheck_baset::typecheck_dowhile(code_dowhilet &code)
816829

817830
typecheck_spec_loop_invariant(code);
818831
typecheck_spec_decreases(code);
832+
833+
if(code.find(ID_C_spec_assigns).is_not_nil())
834+
{
835+
typecheck_spec_assigns(
836+
static_cast<unary_exprt &>(code.add(ID_C_spec_assigns)).op().operands());
837+
}
838+
}
839+
840+
void c_typecheck_baset::typecheck_spec_assigns(exprt::operandst &targets)
841+
{
842+
for(auto &target : targets)
843+
{
844+
typecheck_expr(target);
845+
846+
if(target.type().id() == ID_empty)
847+
{
848+
error().source_location = target.source_location();
849+
error() << "void-typed targets not permitted in assigns clause" << eom;
850+
throw 0;
851+
}
852+
else if(target.id() == ID_pointer_object)
853+
{
854+
// skip
855+
}
856+
else if(!target.get_bool(ID_C_lvalue))
857+
{
858+
error().source_location = target.source_location();
859+
error() << "non-lvalue target in assigns clause" << eom;
860+
throw 0;
861+
}
862+
else if(has_subexpr(target, ID_side_effect))
863+
{
864+
error().source_location = target.source_location();
865+
error() << "assigns clause is not side-effect free" << eom;
866+
throw 0;
867+
}
868+
}
819869
}
820870

821871
void c_typecheck_baset::typecheck_spec_loop_invariant(codet &code)

src/ansi-c/parser.y

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2453,6 +2453,9 @@ iteration_statement:
24532453
statement($$, ID_while);
24542454
parser_stack($$).add_to_operands(std::move(parser_stack($3)), std::move(parser_stack($8)));
24552455

2456+
if(!parser_stack($5).operands().empty())
2457+
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_assigns)).operands().swap(parser_stack($5).operands());
2458+
24562459
if(!parser_stack($6).operands().empty())
24572460
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($6).operands());
24582461

@@ -2468,6 +2471,9 @@ iteration_statement:
24682471
statement($$, ID_dowhile);
24692472
parser_stack($$).add_to_operands(std::move(parser_stack($5)), std::move(parser_stack($2)));
24702473

2474+
if(!parser_stack($7).operands().empty())
2475+
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_assigns)).operands().swap(parser_stack($7).operands());
2476+
24712477
if(!parser_stack($8).operands().empty())
24722478
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($8).operands());
24732479

@@ -2499,6 +2505,9 @@ iteration_statement:
24992505
mto($$, $7);
25002506
mto($$, $12);
25012507

2508+
if(!parser_stack($9).operands().empty())
2509+
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_assigns)).operands().swap(parser_stack($9).operands());
2510+
25022511
if(!parser_stack($10).operands().empty())
25032512
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($10).operands());
25042513

src/goto-programs/goto_convert.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -863,11 +863,15 @@ void goto_convertt::convert_loop_contracts(
863863
goto_programt::targett loop,
864864
const irep_idt &mode)
865865
{
866-
exprt invariant =
867-
static_cast<const exprt &>(code.find(ID_C_spec_loop_invariant));
868-
exprt decreases_clause =
869-
static_cast<const exprt &>(code.find(ID_C_spec_decreases));
866+
auto assigns = static_cast<const unary_exprt &>(code.find(ID_C_spec_assigns));
867+
if(assigns.is_not_nil())
868+
{
869+
PRECONDITION(loop->is_goto());
870+
loop->guard.add(ID_C_spec_assigns).swap(assigns.op());
871+
}
870872

873+
auto invariant =
874+
static_cast<const exprt &>(code.find(ID_C_spec_loop_invariant));
871875
if(!invariant.is_nil())
872876
{
873877
if(has_subexpr(invariant, ID_side_effect))
@@ -880,6 +884,8 @@ void goto_convertt::convert_loop_contracts(
880884
loop->condition_nonconst().add(ID_C_spec_loop_invariant).swap(invariant);
881885
}
882886

887+
auto decreases_clause =
888+
static_cast<const exprt &>(code.find(ID_C_spec_decreases));
883889
if(!decreases_clause.is_nil())
884890
{
885891
if(has_subexpr(decreases_clause, ID_side_effect))
@@ -972,7 +978,7 @@ void goto_convertt::convert_for(
972978
goto_programt::targett y = tmp_y.add(
973979
goto_programt::make_goto(u, true_exprt(), code.source_location()));
974980

975-
// loop invariant and decreases clause
981+
// assigns clause, loop invariant and decreases clause
976982
convert_loop_contracts(code, y, mode);
977983

978984
dest.destructive_append(sideeffects);
@@ -1030,7 +1036,7 @@ void goto_convertt::convert_while(
10301036
goto_programt tmp_x;
10311037
convert(code.body(), tmp_x, mode);
10321038

1033-
// loop invariant and decreases clause
1039+
// assigns clause, loop invariant and decreases clause
10341040
convert_loop_contracts(code, y, mode);
10351041

10361042
dest.destructive_append(tmp_branch);
@@ -1099,7 +1105,7 @@ void goto_convertt::convert_dowhile(
10991105
// y: if(c) goto w;
11001106
y->complete_goto(w);
11011107

1102-
// loop invariant and decreases clause
1108+
// assigns_clause, loop invariant and decreases clause
11031109
convert_loop_contracts(code, y, mode);
11041110

11051111
dest.destructive_append(tmp_w);

0 commit comments

Comments
 (0)