Skip to content

Commit d4f9c45

Browse files
authored
Merge pull request #6502 from remi-delmas-3000/contract-replacement-havoc-return-value
havoc call return value when replacing a call by its contract
2 parents a1472ae + 320a9f8 commit d4f9c45

File tree

3 files changed

+74
-10
lines changed

3 files changed

+74
-10
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
int cmp(int i1, int i2)
2+
// clang-format off
3+
__CPROVER_ensures((i1 == i2) ==> (__CPROVER_return_value == 0))
4+
__CPROVER_ensures((i1 != i2) ==> (__CPROVER_return_value == -1))
5+
// clang-format on
6+
{
7+
if(i1 == i2)
8+
return 0;
9+
else
10+
return -1;
11+
}
12+
13+
int main()
14+
{
15+
int ret = -1;
16+
ret = cmp(0, 0);
17+
__CPROVER_assert(ret == 0, "expecting SUCCESS");
18+
ret = cmp(0, 1);
19+
__CPROVER_assert(ret == 0, "expecting FAILURE");
20+
__CPROVER_assert(ret == -1, "expecting SUCCESS");
21+
__CPROVER_assert(0, "expecting FAILURE");
22+
return 0;
23+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CORE
2+
main.c
3+
--replace-call-with-contract cmp
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main\.assertion\.1\] line \d+ expecting SUCCESS: SUCCESS$
7+
^\[main\.assertion\.2\] line \d+ expecting FAILURE: FAILURE$
8+
^\[main\.assertion\.3\] line \d+ expecting SUCCESS: SUCCESS$
9+
^\[main\.assertion\.4\] line \d+ expecting FAILURE: FAILURE$
10+
^\*\* 2 of 4 failed
11+
^VERIFICATION FAILED$
12+
--
13+
--
14+
This test checks that the return value of a replaced function call is made
15+
nondet at each replacement site.
16+
The replaced function is called twice. Each call is expected to have a different
17+
return value. If the return value of the call is not made nondet at each
18+
replacement, it would be subject to contradictory constraints
19+
(from the post conditions) and the assertions expected to fail would
20+
be vacuously satisfied.

src/goto-instrument/contracts/contracts.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Date: February 2016
2323
#include <goto-instrument/havoc_utils.h>
2424

2525
#include <goto-programs/goto_inline.h>
26+
#include <goto-programs/goto_program.h>
2627
#include <goto-programs/remove_skip.h>
2728

2829
#include <langapi/language_util.h>
@@ -631,6 +632,10 @@ bool code_contractst::apply_function_contract(
631632
// with expressions from the call site (e.g. the return value).
632633
// This object tracks replacements that are common to ENSURES and REQUIRES.
633634
replace_symbolt common_replace;
635+
636+
// keep track of the call's return expression to make it nondet later
637+
optionalt<exprt> call_ret_opt = {};
638+
634639
if(type.return_type() != empty_typet())
635640
{
636641
// Check whether the function's return value is not disregarded.
@@ -640,9 +645,10 @@ bool code_contractst::apply_function_contract(
640645
// For example, if foo() ensures that its return value is > 5, then
641646
// rewrite calls to foo as follows:
642647
// x = foo() -> assume(__CPROVER_return_value > 5) -> assume(x > 5)
643-
symbol_exprt ret_val(
644-
CPROVER_PREFIX "return_value", const_target->call_lhs().type());
645-
common_replace.insert(ret_val, const_target->call_lhs());
648+
auto &lhs_expr = const_target->call_lhs();
649+
call_ret_opt = lhs_expr;
650+
symbol_exprt ret_val(CPROVER_PREFIX "return_value", lhs_expr.type());
651+
common_replace.insert(ret_val, lhs_expr);
646652
}
647653
else
648654
{
@@ -663,7 +669,9 @@ bool code_contractst::apply_function_contract(
663669
ns,
664670
symbol_table);
665671
symbol_exprt ret_val(CPROVER_PREFIX "return_value", type.return_type());
666-
common_replace.insert(ret_val, fresh.symbol_expr());
672+
auto fresh_sym_expr = fresh.symbol_expr();
673+
common_replace.insert(ret_val, fresh_sym_expr);
674+
call_ret_opt = fresh_sym_expr;
667675
}
668676
}
669677
}
@@ -736,8 +744,10 @@ bool code_contractst::apply_function_contract(
736744
targets.add_to_operands(std::move(target));
737745
common_replace(targets);
738746

739-
// Create a series of non-deterministic assignments to havoc the variables
740-
// in the assigns clause.
747+
// Create a sequence of non-deterministic assignments...
748+
goto_programt havoc_instructions;
749+
750+
// ...for assigns clause targets
741751
if(!assigns.empty())
742752
{
743753
assigns_clauset assigns_clause(
@@ -747,14 +757,25 @@ bool code_contractst::apply_function_contract(
747757
modifiest modifies;
748758
modifies.insert(targets.operands().cbegin(), targets.operands().cend());
749759

750-
goto_programt assigns_havoc;
751760
havoc_assigns_targetst havoc_gen(modifies, ns);
752-
havoc_gen.append_full_havoc_code(location, assigns_havoc);
761+
havoc_gen.append_full_havoc_code(location, havoc_instructions);
762+
}
753763

754-
// Insert the non-deterministic assignment immediately before the call site.
755-
insert_before_swap_and_advance(function_body, target, assigns_havoc);
764+
// ...for the return value
765+
if(call_ret_opt.has_value())
766+
{
767+
auto &call_ret = call_ret_opt.value();
768+
auto &loc = call_ret.source_location();
769+
auto &type = call_ret.type();
770+
side_effect_expr_nondett expr(type, location);
771+
auto target = havoc_instructions.add(
772+
goto_programt::make_assignment(call_ret, expr, loc));
773+
target->code_nonconst().add_source_location() = loc;
756774
}
757775

776+
// Insert havoc instructions immediately before the call site.
777+
insert_before_swap_and_advance(function_body, target, havoc_instructions);
778+
758779
// To remove the function call, insert statements related to the assumption.
759780
// Then, replace the function call with a SKIP statement.
760781
if(!ensures.is_false())

0 commit comments

Comments
 (0)