Skip to content

Commit d411e97

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 d411e97

File tree

5 files changed

+76
-60
lines changed

5 files changed

+76
-60
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 & 50 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>
@@ -47,7 +48,6 @@ Date: February 2016
4748
#include "instrument_spec_assigns.h"
4849
#include "memory_predicates.h"
4950
#include "utils.h"
50-
5151
/// Decorator for \ref message_handlert that keeps track of warnings
5252
/// occuring when inlining a function.
5353
///
@@ -729,7 +729,7 @@ code_contractst::create_ensures_instruction(
729729
return std::make_pair(std::move(ensures_program), std::move(history));
730730
}
731731

732-
bool code_contractst::apply_function_contract(
732+
void code_contractst::apply_function_contract(
733733
const irep_idt &function,
734734
const source_locationt &location,
735735
goto_programt &function_body,
@@ -932,7 +932,6 @@ bool code_contractst::apply_function_contract(
932932

933933
// Add this function to the set of replaced functions.
934934
summarized.insert(target_function);
935-
return false;
936935
}
937936

938937
void code_contractst::apply_loop_contract(
@@ -1225,17 +1224,14 @@ goto_functionst &code_contractst::get_goto_functions()
12251224
return goto_functions;
12261225
}
12271226

1228-
bool code_contractst::check_frame_conditions_function(const irep_idt &function)
1227+
void code_contractst::check_frame_conditions_function(const irep_idt &function)
12291228
{
12301229
// Get the function object before instrumentation.
12311230
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-
}
1231+
1232+
INVARIANT(
1233+
function_obj != goto_functions.function_map.end(),
1234+
"Function '" + id2string(function) + "'must exist in the goto program");
12391235

12401236
const auto &goto_function = function_obj->second;
12411237
auto &function_body = function_obj->second.body;
@@ -1316,11 +1312,9 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
13161312
function_body.instructions.end(),
13171313
skip_function_paramst::YES,
13181314
cfg_info_opt);
1319-
1320-
return false;
13211315
}
13221316

1323-
bool code_contractst::enforce_contract(const irep_idt &function)
1317+
void code_contractst::enforce_contract(const irep_idt &function)
13241318
{
13251319
// Add statements to the source function
13261320
// to ensure assigns clause is respected.
@@ -1333,13 +1327,9 @@ bool code_contractst::enforce_contract(const irep_idt &function)
13331327
const irep_idt original(function);
13341328

13351329
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-
}
1330+
INVARIANT(
1331+
old_function != goto_functions.function_map.end(),
1332+
"Function to replace must exist in the program.");
13431333

13441334
std::swap(goto_functions.function_map[mangled], old_function->second);
13451335
goto_functions.function_map.erase(old_function);
@@ -1379,8 +1369,6 @@ bool code_contractst::enforce_contract(const irep_idt &function)
13791369
wrapper.parameter_identifiers = mangled_fun->second.parameter_identifiers;
13801370
wrapper.body.add(goto_programt::make_end_function(sl));
13811371
add_contract_check(original, mangled, wrapper.body);
1382-
1383-
return false;
13841372
}
13851373

13861374
void code_contractst::add_contract_check(
@@ -1536,12 +1524,29 @@ void code_contractst::add_contract_check(
15361524
dest.destructive_insert(dest.instructions.begin(), check);
15371525
}
15381526

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

1544-
bool fail = false;
1549+
check_all_functions_found(to_replace);
15451550

15461551
for(auto &goto_function : goto_functions.function_map)
15471552
{
@@ -1559,7 +1564,7 @@ bool code_contractst::replace_calls(const std::set<std::string> &to_replace)
15591564
if(found == to_replace.end())
15601565
continue;
15611566

1562-
fail |= apply_function_contract(
1567+
apply_function_contract(
15631568
goto_function.first,
15641569
ins->source_location(),
15651570
goto_function.second.body,
@@ -1568,15 +1573,10 @@ bool code_contractst::replace_calls(const std::set<std::string> &to_replace)
15681573
}
15691574
}
15701575

1571-
if(fail)
1572-
return true;
1573-
15741576
for(auto &goto_function : goto_functions.function_map)
15751577
remove_skip(goto_function.second.body);
15761578

15771579
goto_functions.update();
1578-
1579-
return false;
15801580
}
15811581

15821582
void code_contractst::apply_loop_contracts()
@@ -1585,27 +1585,15 @@ void code_contractst::apply_loop_contracts()
15851585
apply_loop_contract(goto_function.first, goto_function.second);
15861586
}
15871587

1588-
bool code_contractst::enforce_contracts(const std::set<std::string> &to_enforce)
1588+
void code_contractst::enforce_contracts(const std::set<std::string> &to_enforce)
15891589
{
15901590
if(to_enforce.empty())
1591-
return false;
1591+
return;
15921592

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

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-
}
1595+
check_all_functions_found(to_enforce);
16061596

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

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)