Skip to content

Commit 6b36ce6

Browse files
authored
Merge pull request #5946 from diffblue/contract_grammar
allow contracts on function declarations
2 parents c923c3d + 4a50ac1 commit 6b36ce6

9 files changed

+157
-185
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/ansi_c_declaration.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,11 @@ void ansi_c_declarationt::to_symbol(
140140
symbol.is_weak=get_is_weak();
141141

142142
// is it a function?
143+
const typet &type = symbol.type.id() == ID_merged_type
144+
? to_merged_type(symbol.type).last_type()
145+
: symbol.type;
143146

144-
if(symbol.type.id()==ID_code && !symbol.is_type)
147+
if(type.id() == ID_code && !symbol.is_type)
145148
{
146149
symbol.is_static_lifetime=false;
147150
symbol.is_thread_local=false;

src/ansi-c/c_typecheck_base.cpp

Lines changed: 35 additions & 48 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,45 @@ 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);
738719

739-
// add code contract (if any); we typecheck this after the
740-
// function body done above, so as to have parameter symbols
741-
// available
720+
// check the contract, if any
742721
symbolt &new_symbol = symbol_table.get_writeable_ref(identifier);
722+
if(new_symbol.type.id() == ID_code)
723+
{
724+
// We typecheck this after the
725+
// function body done above, so as to have parameter symbols
726+
// available
727+
auto &code_type = to_code_with_contract_type(new_symbol.type);
728+
729+
if(as_const(code_type).requires().is_not_nil())
730+
{
731+
auto &requires = code_type.requires();
732+
typecheck_expr(requires);
733+
implicit_typecast_bool(requires);
734+
}
735+
736+
if(as_const(code_type).assigns().is_not_nil())
737+
{
738+
for(auto &op : code_type.assigns().operands())
739+
typecheck_expr(op);
740+
}
743741

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;
742+
if(as_const(code_type).ensures().is_not_nil())
743+
{
744+
const auto &return_type = code_type.return_type();
745+
746+
if(return_type.id() != ID_empty)
747+
parameter_map[CPROVER_PREFIX "return_value"] = return_type;
748+
749+
auto &ensures = code_type.ensures();
750+
typecheck_expr(ensures);
751+
implicit_typecast_bool(ensures);
752+
753+
if(return_type.id() != ID_empty)
754+
parameter_map.erase(CPROVER_PREFIX "return_value");
755+
}
756+
}
770757
}
771758
}
772759
}

src/ansi-c/c_typecheck_base.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,6 @@ class c_typecheck_baset:
145145
virtual void typecheck_dowhile(code_dowhilet &code);
146146
virtual void typecheck_start_thread(codet &code);
147147
virtual void typecheck_spec_expr(codet &code, const irep_idt &spec);
148-
virtual void
149-
typecheck_assigns(const code_typet &function_declarator, const irept &spec);
150-
virtual void
151-
typecheck_assigns(const ansi_c_declaratort &declarator, const exprt &assigns);
152-
virtual void typecheck_assigns_exprs(codet &code, const irep_idt &spec);
153148

154149
bool break_is_allowed;
155150
bool continue_is_allowed;

src/ansi-c/c_typecheck_code.cpp

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -819,65 +819,3 @@ void c_typecheck_baset::typecheck_spec_expr(codet &code, const irep_idt &spec)
819819
implicit_typecast_bool(constraint);
820820
}
821821
}
822-
823-
void c_typecheck_baset::typecheck_assigns(
824-
const code_typet &function_declarator,
825-
const irept &contract)
826-
{
827-
exprt assigns = static_cast<const exprt &>(contract.find(ID_C_spec_assigns));
828-
829-
// Make sure there is an assigns clause to check
830-
if(assigns.is_not_nil())
831-
{
832-
for(const auto &curr_param : function_declarator.parameters())
833-
{
834-
if(curr_param.id() == ID_declaration)
835-
{
836-
const ansi_c_declarationt &param_declaration =
837-
to_ansi_c_declaration(curr_param);
838-
839-
for(const auto &decl : param_declaration.declarators())
840-
{
841-
typecheck_assigns(decl, assigns);
842-
}
843-
}
844-
}
845-
}
846-
}
847-
848-
void c_typecheck_baset::typecheck_assigns(
849-
const ansi_c_declaratort &declarator,
850-
const exprt &assigns)
851-
{
852-
for(exprt curr_op : assigns.operands())
853-
{
854-
if(curr_op.id() != ID_symbol)
855-
{
856-
continue;
857-
}
858-
const symbol_exprt &symbol_op = to_symbol_expr(curr_op);
859-
860-
if(symbol_op.get(ID_C_base_name) == declarator.get_base_name())
861-
{
862-
error().source_location = declarator.source_location();
863-
error() << "Formal parameter " << declarator.get_name() << " without "
864-
<< "dereference appears in ASSIGNS clause. Assigning this "
865-
<< "parameter will never affect the state of the calling context."
866-
<< " Did you mean to write *" << declarator.get_name() << "?"
867-
<< eom;
868-
throw 0;
869-
}
870-
}
871-
}
872-
873-
void c_typecheck_baset::typecheck_assigns_exprs(
874-
codet &code,
875-
const irep_idt &spec)
876-
{
877-
if(code.find(spec).is_not_nil())
878-
{
879-
exprt &constraint = static_cast<exprt &>(code.add(spec));
880-
881-
typecheck_expr(constraint);
882-
}
883-
}

src/ansi-c/expr2c.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,25 @@ 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+
{
556+
dest += " [[requires " +
557+
convert(to_code_with_contract_type(src).requires()) + "]]";
558+
}
559+
560+
if(!to_code_with_contract_type(src).assigns().operands().empty())
561+
{
562+
dest += " [[assigns " +
563+
convert(to_code_with_contract_type(src).assigns()) + "]]";
564+
}
565+
566+
if(to_code_with_contract_type(src).ensures().is_not_nil())
567+
{
568+
dest += " [[ensures " +
569+
convert(to_code_with_contract_type(src).ensures()) + "]]";
570+
}
571+
553572
return dest;
554573
}
555574
else if(src.id()==ID_vector)

0 commit comments

Comments
 (0)