Skip to content

Commit 9573f2b

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 9573f2b

6 files changed

+91
-86
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_assigns)
255+
{
256+
const exprt &as_expr =
257+
static_cast<const exprt &>(static_cast<const irept &>(type));
258+
assigns = to_unary_expr(as_expr).op();
259+
}
260+
else if(type.id() == ID_C_spec_ensures)
261+
{
262+
const exprt &as_expr =
263+
static_cast<const exprt &>(static_cast<const irept &>(type));
264+
ensures = 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+
assigns.make_nil();
90+
ensures.make_nil();
91+
8592
packed=aligned=constructor=destructor=false;
8693

8794
other.clear();

src/ansi-c/c_typecheck_base.cpp

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -644,19 +644,6 @@ void c_typecheck_baset::typecheck_declaration(
644644
// mark as 'already typechecked'
645645
already_typechecked_typet::make_already_typechecked(declaration.type());
646646

647-
irept contract;
648-
649-
{
650-
exprt spec_assigns = declaration.spec_assigns();
651-
contract.add(ID_C_spec_assigns).swap(spec_assigns);
652-
653-
exprt spec_ensures = declaration.spec_ensures();
654-
contract.add(ID_C_spec_ensures).swap(spec_ensures);
655-
656-
exprt spec_requires = declaration.spec_requires();
657-
contract.add(ID_C_spec_requires).swap(spec_requires);
658-
}
659-
660647
// Now do declarators, if any.
661648
for(auto &declarator : declaration.declarators())
662649
{
@@ -728,45 +715,7 @@ void c_typecheck_baset::typecheck_declaration(
728715
irep_idt identifier=symbol.name;
729716
declarator.set_name(identifier);
730717

731-
// If the declarator is for a function definition, typecheck it.
732-
if(can_cast_type<code_typet>(declarator.type()))
733-
{
734-
typecheck_assigns(to_code_type(declarator.type()), contract);
735-
}
736-
737718
typecheck_symbol(symbol);
738-
739-
// add code contract (if any); we typecheck this after the
740-
// function body done above, so as to have parameter symbols
741-
// available
742-
symbolt &new_symbol = symbol_table.get_writeable_ref(identifier);
743-
744-
typecheck_assigns_exprs(
745-
static_cast<codet &>(contract), ID_C_spec_assigns);
746-
typecheck_spec_expr(static_cast<codet &>(contract), ID_C_spec_requires);
747-
748-
typet ret_type = void_type();
749-
if(new_symbol.type.id()==ID_code)
750-
ret_type=to_code_type(new_symbol.type).return_type();
751-
assert(parameter_map.empty());
752-
if(ret_type.id()!=ID_empty)
753-
parameter_map[CPROVER_PREFIX "return_value"] = ret_type;
754-
typecheck_spec_expr(static_cast<codet &>(contract), ID_C_spec_ensures);
755-
parameter_map.clear();
756-
757-
exprt assigns_to_add =
758-
static_cast<const exprt &>(contract.find(ID_C_spec_assigns));
759-
if(assigns_to_add.is_not_nil())
760-
to_code_with_contract_type(new_symbol.type).assigns() = assigns_to_add;
761-
exprt requires_to_add =
762-
static_cast<const exprt &>(contract.find(ID_C_spec_requires));
763-
if(requires_to_add.is_not_nil())
764-
to_code_with_contract_type(new_symbol.type).requires() =
765-
requires_to_add;
766-
exprt ensures_to_add =
767-
static_cast<const exprt &>(contract.find(ID_C_spec_ensures));
768-
if(ensures_to_add.is_not_nil())
769-
to_code_with_contract_type(new_symbol.type).ensures() = ensures_to_add;
770719
}
771720
}
772721
}

src/ansi-c/c_typecheck_type.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,27 @@ void c_typecheck_baset::typecheck_code_type(code_typet &type)
515515
error() << "function must not return function type" << eom;
516516
throw 0;
517517
}
518+
519+
// check the contract, if any
520+
if(to_code_with_contract_type(as_const(type)).requires().is_not_nil())
521+
{
522+
auto &requires = to_code_with_contract_type(type).requires();
523+
typecheck_expr(requires);
524+
implicit_typecast_bool(requires);
525+
}
526+
527+
if(to_code_with_contract_type(as_const(type)).assigns().is_not_nil())
528+
{
529+
for(auto &op : to_code_with_contract_type(type).assigns().operands())
530+
typecheck_expr(op);
531+
}
532+
533+
if(to_code_with_contract_type(as_const(type)).ensures().is_not_nil())
534+
{
535+
auto &ensures = to_code_with_contract_type(type).ensures();
536+
typecheck_expr(ensures);
537+
implicit_typecast_bool(ensures);
538+
}
518539
}
519540

520541
void c_typecheck_baset::typecheck_array_type(array_typet &type)

src/ansi-c/expr2c.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,16 @@ std::string expr2ct::convert_rec(
550550
dest.resize(dest.size()-1);
551551
}
552552

553+
// contract, if any
554+
if(to_code_with_contract_type(src).requires().is_not_nil())
555+
dest += " [[requires " + convert(to_code_with_contract_type(src).requires()) + "]]";
556+
557+
if(!to_code_with_contract_type(src).assigns().operands().empty())
558+
dest += " [[assigns " + convert(to_code_with_contract_type(src).assigns()) + "]]";
559+
560+
if(to_code_with_contract_type(src).ensures().is_not_nil())
561+
dest += " [[ensures " + convert(to_code_with_contract_type(src).ensures()) + "]]";
562+
553563
return dest;
554564
}
555565
else if(src.id()==ID_vector)

src/ansi-c/parser.y

Lines changed: 25 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();
@@ -3065,13 +3032,15 @@ function_head:
30653032
{
30663033
init($$, ID_declaration);
30673034
parser_stack($$).type().swap(parser_stack($1));
3035+
$2=merge($3, $2);
30683036
PARSER.add_declarator(parser_stack($$), parser_stack($2));
30693037
create_function_scope($$);
30703038
}
30713039
| type_specifier declarator post_declarator_attributes_opt
30723040
{
30733041
init($$, ID_declaration);
30743042
parser_stack($$).type().swap(parser_stack($1));
3043+
$2=merge($3, $2);
30753044
PARSER.add_declarator(parser_stack($$), parser_stack($2));
30763045
create_function_scope($$);
30773046
}
@@ -3285,6 +3254,27 @@ parameter_abstract_declarator:
32853254
| parameter_postfix_abstract_declarator
32863255
;
32873256

3257+
cprover_contract:
3258+
TOK_CPROVER_ENSURES '(' ACSL_binding_expression ')'
3259+
{
3260+
$$=$1;
3261+
set($$, ID_C_spec_ensures);
3262+
mto($$, $3);
3263+
}
3264+
| TOK_CPROVER_REQUIRES '(' ACSL_binding_expression ')'
3265+
{
3266+
$$=$1;
3267+
set($$, ID_C_spec_requires);
3268+
mto($$, $3);
3269+
}
3270+
| TOK_CPROVER_ASSIGNS '(' target_list ')'
3271+
{
3272+
$$=$1;
3273+
set($$, ID_C_spec_assigns);
3274+
parser_stack($$).operands() = std::move(parser_stack($3).operands());
3275+
}
3276+
;
3277+
32883278
postfixing_abstract_declarator:
32893279
parameter_postfixing_abstract_declarator
32903280
/* The following two rules implement K&R headers! */

0 commit comments

Comments
 (0)