Skip to content

Commit e6402ec

Browse files
author
Remi Delmas
committed
CONTRACTS: Error-out on unknown functions (replace/enforce).
We dropped the pattern of returning boolean error flags and accumulating errors in `replace_calls` and `enforce_contracts`. We are now checking that all functions exist upfront and throwing exceptions from `util/exception_utils.h` as soon as the first unknown function is found. Subsequent checks of the same conditions are now INVARIANTS.
1 parent 6ca79bf commit e6402ec

File tree

5 files changed

+76
-59
lines changed

5 files changed

+76
-59
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--enforce-contract goo
4+
^Function 'goo' was not found in the GOTO program.$
5+
^EXIT=6$
6+
^SIGNAL=0$
7+
--
8+
--
9+
Checks that attempting to enforce the contract of an unknown function creates
10+
an error.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
int main()
2+
{
3+
return 0;
4+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--replace-call-with-contract goo
4+
^Function 'goo' was not found in the GOTO program.$
5+
^EXIT=6$
6+
^SIGNAL=0$
7+
--
8+
--
9+
Checks that attempting call replacement with an unknown function creates an
10+
error.

src/goto-instrument/contracts/contracts.cpp

Lines changed: 38 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Date: February 2016
3030
#include <langapi/language_util.h>
3131

3232
#include <util/c_types.h>
33+
#include <util/exception_utils.h>
3334
#include <util/expr_util.h>
3435
#include <util/find_symbols.h>
3536
#include <util/format_expr.h>
@@ -729,7 +730,7 @@ code_contractst::create_ensures_instruction(
729730
return std::make_pair(std::move(ensures_program), std::move(history));
730731
}
731732

732-
bool code_contractst::apply_function_contract(
733+
void code_contractst::apply_function_contract(
733734
const irep_idt &function,
734735
const source_locationt &location,
735736
goto_programt &function_body,
@@ -932,7 +933,6 @@ bool code_contractst::apply_function_contract(
932933

933934
// Add this function to the set of replaced functions.
934935
summarized.insert(target_function);
935-
return false;
936936
}
937937

938938
void code_contractst::apply_loop_contract(
@@ -1225,17 +1225,14 @@ goto_functionst &code_contractst::get_goto_functions()
12251225
return goto_functions;
12261226
}
12271227

1228-
bool code_contractst::check_frame_conditions_function(const irep_idt &function)
1228+
void code_contractst::check_frame_conditions_function(const irep_idt &function)
12291229
{
12301230
// Get the function object before instrumentation.
12311231
auto function_obj = goto_functions.function_map.find(function);
1232-
if(function_obj == goto_functions.function_map.end())
1233-
{
1234-
log.error() << "Could not find function '" << function
1235-
<< "' in goto-program; not enforcing contracts."
1236-
<< messaget::eom;
1237-
return true;
1238-
}
1232+
1233+
INVARIANT(
1234+
function_obj != goto_functions.function_map.end(),
1235+
"Function '" + id2string(function) + "'must exist in the goto program");
12391236

12401237
const auto &goto_function = function_obj->second;
12411238
auto &function_body = function_obj->second.body;
@@ -1316,11 +1313,9 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
13161313
function_body.instructions.end(),
13171314
skip_function_paramst::YES,
13181315
cfg_info_opt);
1319-
1320-
return false;
13211316
}
13221317

1323-
bool code_contractst::enforce_contract(const irep_idt &function)
1318+
void code_contractst::enforce_contract(const irep_idt &function)
13241319
{
13251320
// Add statements to the source function
13261321
// to ensure assigns clause is respected.
@@ -1333,13 +1328,9 @@ bool code_contractst::enforce_contract(const irep_idt &function)
13331328
const irep_idt original(function);
13341329

13351330
auto old_function = goto_functions.function_map.find(original);
1336-
if(old_function == goto_functions.function_map.end())
1337-
{
1338-
log.error() << "Could not find function '" << function
1339-
<< "' in goto-program; not enforcing contracts."
1340-
<< messaget::eom;
1341-
return true;
1342-
}
1331+
INVARIANT(
1332+
old_function != goto_functions.function_map.end(),
1333+
"Function to replace must exist in the program.");
13431334

13441335
std::swap(goto_functions.function_map[mangled], old_function->second);
13451336
goto_functions.function_map.erase(old_function);
@@ -1379,8 +1370,6 @@ bool code_contractst::enforce_contract(const irep_idt &function)
13791370
wrapper.parameter_identifiers = mangled_fun->second.parameter_identifiers;
13801371
wrapper.body.add(goto_programt::make_end_function(sl));
13811372
add_contract_check(original, mangled, wrapper.body);
1382-
1383-
return false;
13841373
}
13851374

13861375
void code_contractst::add_contract_check(
@@ -1536,12 +1525,29 @@ void code_contractst::add_contract_check(
15361525
dest.destructive_insert(dest.instructions.begin(), check);
15371526
}
15381527

1539-
bool code_contractst::replace_calls(const std::set<std::string> &to_replace)
1528+
void code_contractst::check_all_functions_found(
1529+
const std::set<std::string> &functions) const
1530+
{
1531+
for(const auto &function : functions)
1532+
{
1533+
if(
1534+
goto_functions.function_map.find(function) ==
1535+
goto_functions.function_map.end())
1536+
{
1537+
throw invalid_input_exceptiont(
1538+
"Function '" + function + "' was not found in the GOTO program.");
1539+
}
1540+
}
1541+
}
1542+
1543+
void code_contractst::replace_calls(const std::set<std::string> &to_replace)
15401544
{
15411545
if(to_replace.empty())
1542-
return false;
1546+
return;
1547+
1548+
log.status() << "Replacing function calls with contracts" << messaget::eom;
15431549

1544-
bool fail = false;
1550+
check_all_functions_found(to_replace);
15451551

15461552
for(auto &goto_function : goto_functions.function_map)
15471553
{
@@ -1559,7 +1565,7 @@ bool code_contractst::replace_calls(const std::set<std::string> &to_replace)
15591565
if(found == to_replace.end())
15601566
continue;
15611567

1562-
fail |= apply_function_contract(
1568+
apply_function_contract(
15631569
goto_function.first,
15641570
ins->source_location(),
15651571
goto_function.second.body,
@@ -1568,15 +1574,10 @@ bool code_contractst::replace_calls(const std::set<std::string> &to_replace)
15681574
}
15691575
}
15701576

1571-
if(fail)
1572-
return true;
1573-
15741577
for(auto &goto_function : goto_functions.function_map)
15751578
remove_skip(goto_function.second.body);
15761579

15771580
goto_functions.update();
1578-
1579-
return false;
15801581
}
15811582

15821583
void code_contractst::apply_loop_contracts()
@@ -1585,27 +1586,15 @@ void code_contractst::apply_loop_contracts()
15851586
apply_loop_contract(goto_function.first, goto_function.second);
15861587
}
15871588

1588-
bool code_contractst::enforce_contracts(const std::set<std::string> &to_enforce)
1589+
void code_contractst::enforce_contracts(const std::set<std::string> &to_enforce)
15891590
{
15901591
if(to_enforce.empty())
1591-
return false;
1592+
return;
15921593

1593-
bool fail = false;
1594+
log.status() << "Enforcing contracts" << messaget ::eom;
15941595

1595-
for(const auto &function : to_enforce)
1596-
{
1597-
auto goto_function = goto_functions.function_map.find(function);
1598-
if(goto_function == goto_functions.function_map.end())
1599-
{
1600-
fail = true;
1601-
log.error() << "Could not find function '" << function
1602-
<< "' in goto-program; not enforcing contracts."
1603-
<< messaget::eom;
1604-
continue;
1605-
}
1596+
check_all_functions_found(to_enforce);
16061597

1607-
if(!fail)
1608-
fail = enforce_contract(function);
1609-
}
1610-
return fail;
1598+
for(const auto &function : to_enforce)
1599+
enforce_contract(function);
16111600
}

src/goto-instrument/contracts/contracts.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,13 @@ class code_contractst
6262
{
6363
}
6464

65-
/// \brief Replace all calls to each function in the list with that function's
66-
/// contract
65+
/// Throws an exception if some function `functions` is found in the program.
66+
void check_all_functions_found(const std::set<std::string> &functions) const;
67+
68+
/// \brief Replace all calls to each function in the `to_replace` set
69+
/// with that function's contract
70+
///
71+
/// Throws an exception if some `to_replace` functions are not found.
6772
///
6873
/// Use this function when proving code that calls into an expensive function,
6974
/// `F`. You can write a contract for F using __CPROVER_requires and
@@ -73,13 +78,13 @@ class code_contractst
7378
/// actually abides by its `ensures` and `requires` clauses, you should
7479
/// separately call `code_constractst::enforce_contracts()` on `F` and verify
7580
/// it using `cbmc --function F`.
76-
///
77-
/// \return `true` on failure, `false` otherwise
78-
bool replace_calls(const std::set<std::string> &);
81+
void replace_calls(const std::set<std::string> &to_replace);
7982

8083
/// \brief Turn requires & ensures into assumptions and assertions for each of
8184
/// the named functions
8285
///
86+
/// Throws an exception if some `to_enforce` functions are not found.
87+
///
8388
/// Use this function to prove the correctness of a function F independently
8489
/// of its calling context. If you have proved that F is correct, then you can
8590
/// soundly replace all calls to F with F's contract using the
@@ -92,8 +97,7 @@ class code_contractst
9297
/// function called `F` that assumes `CF`'s `requires` clause, calls `CF`, and
9398
/// then asserts `CF`'s `ensures` clause.
9499
///
95-
/// \return `true` on failure, `false` otherwise
96-
bool enforce_contracts(const std::set<std::string> &functions);
100+
void enforce_contracts(const std::set<std::string> &to_enforce);
97101

98102
void apply_loop_contracts();
99103

@@ -125,10 +129,10 @@ class code_contractst
125129
std::unordered_set<irep_idt> summarized;
126130

127131
/// \brief Enforce contract of a single function
128-
bool enforce_contract(const irep_idt &function);
132+
void enforce_contract(const irep_idt &function);
129133

130134
/// Instrument functions to check frame conditions.
131-
bool check_frame_conditions_function(const irep_idt &function);
135+
void check_frame_conditions_function(const irep_idt &function);
132136

133137
/// Apply loop contracts, whenever available, to all loops in `function`.
134138
/// Loop invariants, loop variants, and loop assigns clauses.
@@ -139,7 +143,7 @@ class code_contractst
139143
/// Replaces function calls with assertions based on requires clauses,
140144
/// non-deterministic assignments for the write set, and assumptions
141145
/// based on ensures clauses.
142-
bool apply_function_contract(
146+
void apply_function_contract(
143147
const irep_idt &function,
144148
const source_locationt &location,
145149
goto_programt &function_body,

0 commit comments

Comments
 (0)