Skip to content

Commit 63b2af3

Browse files
author
klaas
committed
Adds havocking for function calls
This commit changes the behaviour of --apply-code-contracts on function calls. Previously, when applying contracts, a function call would be replaced by an assertion that the precondition holds, followed by an assumption that the postcondition holds. This change inserts between that assert/assume pair code that sets all variables that could be modified by the function being called to be nondeterministic.
1 parent 02eb459 commit 63b2af3

File tree

1 file changed

+32
-17
lines changed

1 file changed

+32
-17
lines changed

src/goto-instrument/code_contracts.cpp

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Date: February 2016
2323
#include <linking/static_lifetime_init.h>
2424

2525
#include "loop_utils.h"
26+
#include "function_modifies.h"
2627

2728
enum class contract_opst { APPLY, CHECK };
2829

@@ -52,6 +53,7 @@ class code_contractst
5253

5354
void apply_contract(
5455
goto_programt &goto_program,
56+
const local_may_aliast &local_may_alias,
5557
goto_programt::targett target);
5658

5759
void apply_invariant(
@@ -78,6 +80,7 @@ class code_contractst
7880

7981
void code_contractst::apply_contract(
8082
goto_programt &goto_program,
83+
const local_may_aliast &local_may_alias,
8184
goto_programt::targett target)
8285
{
8386
const code_function_callt &call=to_code_function_call(target->code);
@@ -109,6 +112,23 @@ void code_contractst::apply_contract(
109112
}
110113
}
111114

115+
// find out what can be written by the function
116+
// TODO Use a better write-set analysis.
117+
modifiest modifies;
118+
function_modifiest function_modifies(goto_functions);
119+
120+
// Handle return value of the function
121+
if(call.lhs().is_not_nil())
122+
{
123+
function_modifies.get_modifies_lhs(local_may_alias, target,
124+
call.lhs(), modifies);
125+
}
126+
function_modifies(call.function(), modifies);
127+
128+
// build the havocking code
129+
goto_programt havoc_code;
130+
build_havoc_code(target, modifies, havoc_code);
131+
112132
// replace formal parameters by arguments, replace return
113133
replace_symbolt replace;
114134

@@ -140,7 +160,9 @@ void code_contractst::apply_contract(
140160
goto_program.insert_before_swap(target, a);
141161
++target;
142162
}
143-
// TODO: Havoc write set of the function between assert and ensure.
163+
164+
// TODO some sort of replacement on havoc code
165+
goto_program.destructive_insert(target, havoc_code);
144166

145167
target->make_assumption(ensures);
146168
}
@@ -151,7 +173,7 @@ void code_contractst::apply_invariant(
151173
const goto_programt::targett loop_head,
152174
const loopt &loop)
153175
{
154-
assert(!loop.empty());
176+
PRECONDITION(!loop.empty());
155177

156178
// find the last back edge
157179
goto_programt::targett loop_end=loop_head;
@@ -172,8 +194,6 @@ void code_contractst::apply_invariant(
172194
return;
173195
}
174196

175-
// TODO: Allow for not havocking in the for/while case
176-
177197
// change H: loop; E: ...
178198
// to
179199
// H: assert(invariant);
@@ -210,8 +230,6 @@ void code_contractst::apply_invariant(
210230
}
211231

212232
// assume !guard
213-
// TODO: consider breaks and how they're implemented.
214-
// TODO: Also consider continues and whether they jump to loop end or head
215233
{
216234
goto_programt::targett assume = havoc_code.add_instruction(ASSUME);
217235
if(loop_head->is_goto())
@@ -246,7 +264,7 @@ void code_contractst::check_contract(
246264
goto_functionst::goto_functiont &goto_function,
247265
goto_programt &dest)
248266
{
249-
assert(!dest.instructions.empty());
267+
PRECONDITION(!dest.instructions.empty());
250268

251269
exprt requires =
252270
static_cast<const exprt&>(goto_function.type.find(ID_C_spec_requires));
@@ -321,16 +339,17 @@ void code_contractst::check_contract(
321339
replace.insert(p_it.get_identifier(), p);
322340
}
323341

342+
// rewrite any use of parameters
343+
replace(requires);
344+
replace(ensures);
345+
324346
// assume(requires)
325347
if(requires.is_not_nil())
326348
{
327349
goto_programt::targett a=check.add_instruction();
328350
a->make_assumption(requires);
329351
a->function=skip->function;
330352
a->source_location=requires.source_location();
331-
332-
// rewrite any use of parameters
333-
replace(a->guard);
334353
}
335354

336355
// ret=function(parameter1, ...)
@@ -345,9 +364,6 @@ void code_contractst::check_contract(
345364
a->function=skip->function;
346365
a->source_location=ensures.source_location();
347366

348-
// rewrite any use of __CPROVER_return_value
349-
replace(a->guard);
350-
351367
// prepend the new code to dest
352368
check.destructive_append(tmp_skip);
353369
dest.destructive_insert(dest.instructions.begin(), check);
@@ -359,7 +375,7 @@ void code_contractst::check_apply_invariant(
359375
const goto_programt::targett loop_head,
360376
const loopt &loop)
361377
{
362-
assert(!loop.empty());
378+
PRECONDITION(!loop.empty());
363379

364380
// find the last back edge
365381
goto_programt::targett loop_end=loop_head;
@@ -478,7 +494,7 @@ void code_contractst::apply_code_contracts()
478494
{
479495
if(it->is_function_call())
480496
{
481-
apply_contract(goto_function.body, it);
497+
apply_contract(goto_function.body, local_may_alias, it);
482498
}
483499
}
484500
}
@@ -512,7 +528,7 @@ void code_contractst::check_code_contracts()
512528
{
513529
if(it->is_function_call())
514530
{
515-
apply_contract(goto_function.body, it);
531+
apply_contract(goto_function.body, local_may_alias, it);
516532
}
517533
}
518534
}
@@ -522,7 +538,6 @@ void code_contractst::check_code_contracts()
522538
check_contract(it->first, it->second, i_it->second.body);
523539
}
524540

525-
// remove skips
526541
remove_skip(i_it->second.body);
527542

528543
goto_functions.update();

0 commit comments

Comments
 (0)