Skip to content

Commit 73198ac

Browse files
author
Remi Delmas
committed
CONTRACTS: allow NULL function pointer contracts
Adds a thrid optional NULL parameter to requires_contract and ensures_contract clauses, allows a pointer to a function to either satisfy a given contract or to be NULL.
1 parent 0060417 commit 73198ac

File tree

4 files changed

+147
-82
lines changed

4 files changed

+147
-82
lines changed

src/ansi-c/c_expr.h

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -329,11 +329,11 @@ class function_pointer_obeys_contract_exprt : public exprt
329329
public:
330330
explicit function_pointer_obeys_contract_exprt(
331331
exprt _function_pointer,
332-
exprt _contract)
332+
exprt _contract_pointers)
333333
: exprt(ID_function_pointer_obeys_contract, empty_typet{})
334334
{
335335
add_to_operands(std::move(_function_pointer));
336-
add_to_operands(std::move(_contract));
336+
add_to_operands(std::move(_contract_pointers));
337337
}
338338

339339
static void check(
@@ -344,6 +344,13 @@ class function_pointer_obeys_contract_exprt : public exprt
344344
vm,
345345
expr.operands().size() == 2,
346346
"function pointer obeys contract expression must have two operands");
347+
348+
DATA_CHECK(
349+
vm,
350+
expr.operands()[1].id() == ID_expression_list,
351+
"function pointer obeys contract expression second operand must be an "
352+
"ID_expression_list expression, found " +
353+
id2string(expr.operands()[1].id()));
347354
}
348355

349356
static void validate(
@@ -364,24 +371,14 @@ class function_pointer_obeys_contract_exprt : public exprt
364371
return op0();
365372
}
366373

367-
const symbol_exprt &contract_symbol_expr() const
368-
{
369-
return to_symbol_expr(op1().operands().at(0));
370-
}
371-
372-
symbol_exprt &contract_symbol_expr()
374+
const exprt::operandst &contract_pointers() const
373375
{
374-
return to_symbol_expr(op1().operands().at(0));
375-
}
376-
377-
const exprt &address_of_contract() const
378-
{
379-
return op1();
376+
return op1().operands();
380377
}
381378

382-
exprt &address_of_contract()
379+
exprt::operandst &contract_pointers()
383380
{
384-
return op1();
381+
return op1().operands();
385382
}
386383
};
387384

src/ansi-c/c_typecheck_code.cpp

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ Author: Daniel Kroening, [email protected]
99
/// \file
1010
/// C Language Type Checking
1111

12-
#include "c_typecheck_base.h"
13-
1412
#include <util/arith_tools.h>
1513
#include <util/c_types.h>
1614
#include <util/config.h>
1715
#include <util/expr_util.h>
1816
#include <util/range.h>
17+
#include <util/simplify_expr.h>
1918
#include <util/string_constant.h>
2019

2120
#include "ansi_c_declaration.h"
2221
#include "c_expr.h"
22+
#include "c_typecheck_base.h"
2323

2424
void c_typecheck_baset::start_typecheck_code()
2525
{
@@ -1101,11 +1101,11 @@ void c_typecheck_baset::typecheck_spec_function_pointer_obeys_contract(
11011101
{
11021102
if(!can_cast_expr<function_pointer_obeys_contract_exprt>(expr))
11031103
{
1104-
error().source_location = expr.source_location();
1105-
error() << "expected ID_function_pointer_obeys_contract expression in "
1106-
"requires_contract/ensures_contract clause, found "
1107-
<< expr.id() << eom;
1108-
throw 0;
1104+
throw invalid_source_file_exceptiont(
1105+
"expected ID_function_pointer_obeys_contract expression in "
1106+
"requires_contract/ensures_contract clause, found " +
1107+
id2string(expr.id()),
1108+
expr.source_location());
11091109
}
11101110

11111111
auto &obeys_expr = to_function_pointer_obeys_contract_expr(expr);
@@ -1121,60 +1121,73 @@ void c_typecheck_baset::typecheck_spec_function_pointer_obeys_contract(
11211121
function_pointer.type().id() != ID_pointer ||
11221122
to_pointer_type(function_pointer.type()).base_type().id() != ID_code)
11231123
{
1124-
error().source_location = expr.source_location();
1125-
error() << "the first parameter of the clause must be a function pointer "
1126-
"expression"
1127-
<< eom;
1128-
throw 0;
1124+
throw invalid_source_file_exceptiont(
1125+
"the first parameter of the clause must be a function pointer expression",
1126+
expr.source_location());
11291127
}
11301128

11311129
if(!function_pointer.get_bool(ID_C_lvalue))
11321130
{
1133-
error().source_location = function_pointer.source_location();
1134-
error() << "first parameter of the clause must be an lvalue" << eom;
1135-
throw 0;
1131+
throw invalid_source_file_exceptiont(
1132+
"the first parameter of the clause must be an lvalue",
1133+
function_pointer.source_location());
11361134
}
11371135

11381136
if(has_subexpr(function_pointer, ID_side_effect))
11391137
{
1140-
error().source_location = function_pointer.source_location();
1141-
error() << "first parameter of the clause must have no side-effects" << eom;
1142-
throw 0;
1138+
throw invalid_source_file_exceptiont(
1139+
"the first parameter of the clause must have no side-effects",
1140+
function_pointer.source_location());
11431141
}
11441142

11451143
if(has_subexpr(function_pointer, ID_if))
11461144
{
1147-
error().source_location = function_pointer.source_location();
1148-
error() << "first parameter of the clause must have no ternary operator"
1149-
<< eom;
1150-
throw 0;
1145+
throw invalid_source_file_exceptiont(
1146+
"the first parameter of the clause must have no ternary operator",
1147+
function_pointer.source_location());
11511148
}
11521149

1153-
// second parameter must be the address of a function symbol
1154-
auto &address_of_contract = obeys_expr.address_of_contract();
1155-
typecheck_expr(address_of_contract);
1150+
// second parameter must be a list of size 1 or 2 with:
1151+
// - a function symbol with compatible type
1152+
// - a null value
1153+
auto contract_pointers_size = obeys_expr.contract_pointers().size();
1154+
if(contract_pointers_size < 1 || contract_pointers_size > 2)
1155+
{
1156+
throw invalid_source_file_exceptiont(
1157+
"requires_contract/ensures_contract clauses must have two or three "
1158+
"parameters",
1159+
obeys_expr.source_location());
1160+
}
11561161

1162+
auto &first_target = obeys_expr.contract_pointers().at(0);
1163+
typecheck_expr(first_target);
11571164
if(
1158-
address_of_contract.id() != ID_address_of ||
1159-
to_address_of_expr(address_of_contract).object().id() != ID_symbol ||
1160-
address_of_contract.type().id() != ID_pointer ||
1161-
to_pointer_type(address_of_contract.type()).base_type().id() != ID_code)
1162-
{
1163-
error().source_location = expr.source_location();
1164-
error() << "the second parameter of the requires_contract/ensures_contract "
1165-
"clause must be a function symbol"
1166-
<< eom;
1167-
throw 0;
1165+
first_target.id() != ID_address_of ||
1166+
to_address_of_expr(first_target).object().id() != ID_symbol ||
1167+
first_target.type().id() != ID_pointer ||
1168+
to_pointer_type(first_target.type()).base_type().id() != ID_code ||
1169+
function_pointer.type() != first_target.type())
1170+
{
1171+
throw invalid_source_file_exceptiont(
1172+
"the second parameter of the clause must be a pointer to a contract "
1173+
"symbol with the same type as the first parameter",
1174+
first_target.source_location());
11681175
}
11691176

1170-
if(function_pointer.type() != address_of_contract.type())
1177+
if(contract_pointers_size == 2)
11711178
{
1172-
error().source_location = expr.source_location();
1173-
error() << "the first and second parameter of the "
1174-
"requires_contract/ensures_contract clause must have the same "
1175-
"function pointer type "
1176-
<< eom;
1177-
throw 0;
1179+
auto &second_target = obeys_expr.contract_pointers().at(1);
1180+
typecheck_expr(second_target);
1181+
auto simplified = simplify_expr(second_target, namespacet(symbol_table));
1182+
if(
1183+
!simplified.is_constant() ||
1184+
!is_null_pointer(to_constant_expr(simplified)))
1185+
throw invalid_source_file_exceptiont(
1186+
"the (optional) third parameter of the clause must be NULL",
1187+
second_target.source_location());
1188+
// add typecast around NULL
1189+
obeys_expr.contract_pointers()[1] =
1190+
typecast_exprt(simplified, function_pointer.type());
11781191
}
11791192
}
11801193

src/ansi-c/parser.y

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3299,7 +3299,7 @@ cprover_function_contract:
32993299
set($$, ID_C_spec_requires);
33003300
mto($$, $3);
33013301
}
3302-
| TOK_CPROVER_ENSURES_CONTRACT '(' unary_expression ',' unary_expression ')'
3302+
| TOK_CPROVER_ENSURES_CONTRACT '(' unary_expression ',' unary_expression_list ')'
33033303
{
33043304
$$=$1;
33053305
set($$, ID_C_spec_ensures_contract);
@@ -3309,7 +3309,7 @@ cprover_function_contract:
33093309
tmp.add_source_location()=parser_stack($$).source_location();
33103310
parser_stack($$).add_to_operands(std::move(tmp));
33113311
}
3312-
| TOK_CPROVER_REQUIRES_CONTRACT '(' unary_expression ',' unary_expression ')'
3312+
| TOK_CPROVER_REQUIRES_CONTRACT '(' unary_expression ',' unary_expression_list ')'
33133313
{
33143314
$$=$1;
33153315
set($$, ID_C_spec_requires_contract);

src/goto-instrument/contracts/dynamic-frames/dfcc_wrapper_program.cpp

Lines changed: 77 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -877,22 +877,38 @@ void dfcc_wrapper_programt::assert_function_pointer_obeys_contract(
877877
const irep_idt &property_class,
878878
goto_programt &dest)
879879
{
880-
function_pointer_contracts.insert(
881-
expr.contract_symbol_expr().get_identifier());
882880
source_locationt loc(expr.source_location());
883881
loc.set_property_class(property_class);
884882
std::stringstream comment;
885883
comment << "Assert function pointer '"
886884
<< from_expr_using_mode(
887885
ns, contract_symbol.mode, expr.function_pointer())
888-
<< "' obeys contract '"
889-
<< from_expr_using_mode(
890-
ns, contract_symbol.mode, expr.address_of_contract())
891-
<< "'";
886+
<< "' obeys contract ";
887+
888+
exprt::operandst disjuncts;
889+
890+
for(std::size_t i = 0; i < expr.contract_pointers().size(); i++)
891+
{
892+
const exprt &contract_pointer = expr.contract_pointers().at(i);
893+
if(contract_pointer.id() == ID_address_of)
894+
{
895+
function_pointer_contracts.insert(
896+
to_symbol_expr(to_address_of_expr(contract_pointer).object())
897+
.get_identifier());
898+
}
899+
if(i > 0)
900+
comment << " or ";
901+
902+
comment << "'"
903+
<< from_expr_using_mode(ns, contract_symbol.mode, contract_pointer)
904+
<< "'";
905+
disjuncts.push_back(equal_exprt{expr.function_pointer(), contract_pointer});
906+
}
892907
loc.set_comment(comment.str());
893-
code_assertt assert_expr(
894-
equal_exprt{expr.function_pointer(), expr.address_of_contract()});
908+
909+
code_assertt assert_expr{or_exprt{disjuncts}};
895910
assert_expr.add_source_location() = loc;
911+
896912
goto_programt instructions;
897913
converter.goto_convert(assert_expr, instructions, contract_symbol.mode);
898914
dest.destructive_append(instructions);
@@ -902,21 +918,60 @@ void dfcc_wrapper_programt::assume_function_pointer_obeys_contract(
902918
const function_pointer_obeys_contract_exprt &expr,
903919
goto_programt &dest)
904920
{
905-
function_pointer_contracts.insert(
906-
expr.contract_symbol_expr().get_identifier());
921+
for(std::size_t i = 0; i < expr.contract_pointers().size(); i++)
922+
{
923+
const exprt &contract_pointer = expr.contract_pointers().at(i);
907924

908-
source_locationt loc(expr.source_location());
909-
std::stringstream comment;
910-
comment << "Assume function pointer '"
911-
<< from_expr_using_mode(
912-
ns, contract_symbol.mode, expr.function_pointer())
913-
<< "' obeys contract '"
914-
<< from_expr_using_mode(
915-
ns, contract_symbol.mode, expr.contract_symbol_expr())
916-
<< "'";
917-
loc.set_comment(comment.str());
918-
dest.add(goto_programt::make_assignment(
919-
expr.function_pointer(), expr.address_of_contract(), loc));
925+
if(contract_pointer.id() == ID_address_of)
926+
{
927+
function_pointer_contracts.insert(
928+
to_symbol_expr(to_address_of_expr(contract_pointer).object())
929+
.get_identifier());
930+
}
931+
932+
const auto &loc = contract_pointer.source_location();
933+
if(i == 0)
934+
{
935+
// First is assignment unconditional
936+
// ```
937+
// function_pointer := contract_pointer;
938+
// ```
939+
dest.add(goto_programt::make_assignment(
940+
expr.function_pointer(), contract_pointer, loc));
941+
}
942+
else
943+
{
944+
// Remaining are nondet
945+
// ```
946+
// DECL skip: bool
947+
// ASSIGN skip = nondet_bool;
948+
// IF skip goto skip_label;
949+
// function_pointer = contract_pointer;
950+
// skip_label:
951+
// DEAD skip;
952+
// ```
953+
const auto skip_var = utils
954+
.create_symbol(
955+
bool_typet(),
956+
wrapped_symbol.name,
957+
"skip",
958+
loc,
959+
wrapper_symbol.mode,
960+
wrapper_symbol.module,
961+
false)
962+
.symbol_expr();
963+
dest.add(goto_programt::make_decl(skip_var, loc));
964+
dest.add(goto_programt::make_assignment(
965+
skip_var, side_effect_expr_nondett(bool_typet(), loc), loc));
966+
auto goto_instr =
967+
dest.add(goto_programt::make_incomplete_goto(skip_var, loc));
968+
dest.add(goto_programt::make_assignment(
969+
expr.function_pointer(), contract_pointer, loc));
970+
auto skip_target = dest.add(goto_programt::make_skip(loc));
971+
goto_instr->complete_goto(skip_target);
972+
dest.add(goto_programt::make_dead(skip_var, loc));
973+
}
974+
}
920975
}
921976

922977
void dfcc_wrapper_programt::encode_function_call()

0 commit comments

Comments
 (0)