Skip to content

Commit 51f2a9f

Browse files
committed
allow contracts on function declarations
This changes the grammar of the C frontend to allow contracts on function declarations. This also removes the requirement that the contract clauses appear in any particular order.
1 parent 5e145e4 commit 51f2a9f

File tree

3 files changed

+58
-35
lines changed

3 files changed

+58
-35
lines changed

src/ansi-c/ansi_c_convert_type.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,24 @@ void ansi_c_convert_typet::read_rec(const typet &type)
245245
{
246246
UNREACHABLE;
247247
}
248+
else if(type.id() == ID_C_spec_requires)
249+
{
250+
const exprt &as_expr =
251+
static_cast<const exprt &>(static_cast<const irept &>(type));
252+
requires = to_unary_expr(as_expr).op();
253+
}
254+
else if(type.id() == ID_C_spec_ensures)
255+
{
256+
const exprt &as_expr =
257+
static_cast<const exprt &>(static_cast<const irept &>(type));
258+
ensures = to_unary_expr(as_expr).op();
259+
}
260+
else if(type.id() == ID_C_spec_assigns)
261+
{
262+
const exprt &as_expr =
263+
static_cast<const exprt &>(static_cast<const irept &>(type));
264+
assigns = to_unary_expr(as_expr).op();
265+
}
248266
else
249267
other.push_back(type);
250268
}
@@ -290,6 +308,16 @@ void ansi_c_convert_typet::write(typet &type)
290308

291309
type.swap(other.front());
292310

311+
// the contract expressions are meant for function types only
312+
if(requires.is_not_nil())
313+
type.add(ID_C_spec_requires) = std::move(requires);
314+
315+
if(assigns.is_not_nil())
316+
type.add(ID_C_spec_assigns) = std::move(assigns);
317+
318+
if(ensures.is_not_nil())
319+
type.add(ID_C_spec_ensures) = std::move(ensures);
320+
293321
if(constructor || destructor)
294322
{
295323
if(constructor && destructor)

src/ansi-c/ansi_c_convert_type.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class ansi_c_convert_typet:public messaget
4545
exprt msc_based; // this is Visual Studio
4646
bool constructor, destructor;
4747

48+
// contracts
49+
exprt requires, assigns, ensures;
50+
4851
// storage spec
4952
c_storage_spect c_storage_spec;
5053

@@ -82,6 +85,10 @@ class ansi_c_convert_typet:public messaget
8285
msc_based.make_nil();
8386
gcc_attribute_mode.make_nil();
8487

88+
requires.make_nil();
89+
ensures.make_nil();
90+
ensures.make_nil();
91+
8592
packed=aligned=constructor=destructor=false;
8693

8794
other.clear();

src/ansi-c/parser.y

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -497,27 +497,6 @@ loop_invariant_opt:
497497
{ $$=$3; }
498498
;
499499

500-
requires_opt:
501-
/* nothing */
502-
{ init($$); parser_stack($$).make_nil(); }
503-
| TOK_CPROVER_REQUIRES '(' ACSL_binding_expression ')'
504-
{ $$=$3; }
505-
;
506-
507-
ensures_opt:
508-
/* nothing */
509-
{ init($$); parser_stack($$).make_nil(); }
510-
| TOK_CPROVER_ENSURES '(' ACSL_binding_expression ')'
511-
{ $$=$3; }
512-
;
513-
514-
assigns_opt:
515-
/* nothing */
516-
{ init($$); parser_stack($$).make_nil(); }
517-
| TOK_CPROVER_ASSIGNS '(' target_list ')'
518-
{ $$=$3; }
519-
;
520-
521500
target_list:
522501
target
523502
{
@@ -1008,6 +987,7 @@ post_declarator_attribute:
1008987
parser_stack($$).set(ID_flavor, ID_gcc);
1009988
parser_stack($$).operands().swap(parser_stack($4).operands());
1010989
}
990+
| cprover_contract
1011991
| gcc_attribute_specifier
1012992
;
1013993

@@ -2901,29 +2881,16 @@ asm_definition:
29012881

29022882
function_definition:
29032883
function_head
2904-
assigns_opt
2905-
requires_opt
2906-
ensures_opt
29072884
function_body
29082885
{
2909-
2910-
// Capture assigns clause
2911-
if(parser_stack($2).is_not_nil())
2912-
parser_stack($1).add(ID_C_spec_assigns).swap(parser_stack($2));
2913-
2914-
// Capture code contract
2915-
if(parser_stack($3).is_not_nil())
2916-
parser_stack($1).add(ID_C_spec_requires).swap(parser_stack($3));
2917-
if(parser_stack($4).is_not_nil())
2918-
parser_stack($1).add(ID_C_spec_ensures).swap(parser_stack($4));
29192886
// The head is a declaration with one declarator,
29202887
// and the body becomes the 'value'.
29212888
$$=$1;
29222889
ansi_c_declarationt &ansi_c_declaration=
29232890
to_ansi_c_declaration(parser_stack($$));
29242891

29252892
assert(ansi_c_declaration.declarators().size()==1);
2926-
ansi_c_declaration.add_initializer(parser_stack($5));
2893+
ansi_c_declaration.add_initializer(parser_stack($2));
29272894

29282895
// Kill the scope that 'function_head' creates.
29292896
PARSER.pop_scope();
@@ -3285,6 +3252,27 @@ parameter_abstract_declarator:
32853252
| parameter_postfix_abstract_declarator
32863253
;
32873254

3255+
cprover_contract:
3256+
TOK_CPROVER_ENSURES '(' ACSL_binding_expression ')'
3257+
{
3258+
$$=$1;
3259+
set($$, ID_C_spec_ensures);
3260+
mto($$, $3);
3261+
}
3262+
| TOK_CPROVER_REQUIRES '(' ACSL_binding_expression ')'
3263+
{
3264+
$$=$1;
3265+
set($$, ID_C_spec_requires);
3266+
mto($$, $3);
3267+
}
3268+
| TOK_CPROVER_ASSIGNS '(' ACSL_binding_expression ')'
3269+
{
3270+
$$=$1;
3271+
set($$, ID_C_spec_assigns);
3272+
mto($$, $3);
3273+
}
3274+
;
3275+
32883276
postfixing_abstract_declarator:
32893277
parameter_postfixing_abstract_declarator
32903278
/* The following two rules implement K&R headers! */

0 commit comments

Comments
 (0)