Skip to content

Commit c65d0ff

Browse files
author
Remi Delmas
committed
Function contracts: add missing DECL/DEAD instructions for ignored_return_value variables introduced by contract replacement (these would previously appear as global variables).
1 parent 9060de1 commit c65d0ff

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
int bar(int l, int r) __CPROVER_requires(0 <= l && l <= 10)
5+
__CPROVER_requires(0 <= r && r <= 10) __CPROVER_assigns() __CPROVER_ensures(
6+
__CPROVER_return_value == __CPROVER_old(l) + __CPROVER_old(r))
7+
{
8+
return l + r;
9+
}
10+
11+
int *baz(int l, int r) __CPROVER_requires(0 <= l && l <= 10)
12+
__CPROVER_requires(0 <= r && r <= 10) __CPROVER_assigns() __CPROVER_ensures(
13+
*__CPROVER_return_value == __CPROVER_old(l) + __CPROVER_old(r))
14+
{
15+
int *res = malloc(sizeof(int));
16+
*res = l + r;
17+
return res;
18+
}
19+
20+
int foo(int l, int r) __CPROVER_requires(0 <= l && l <= 10)
21+
__CPROVER_requires(0 <= r && r <= 10) __CPROVER_assigns() __CPROVER_ensures(
22+
__CPROVER_return_value == __CPROVER_old(l) + __CPROVER_old(r))
23+
{
24+
bar(l, r);
25+
bar(l, r);
26+
baz(l, r);
27+
baz(l, r);
28+
return l + r;
29+
}
30+
31+
int main()
32+
{
33+
int l;
34+
int r;
35+
foo(l, r);
36+
return 0;
37+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--replace-call-with-contract bar --replace-call-with-contract baz --enforce-contract foo
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^[.*] Check that .*ignored_return_value.* is assignable: FAILURE
9+
--
10+
This test checks that when replacing a call by a contract for a call that
11+
ignores the return value of the function, the temporary introduced to
12+
receive the call result does not trigger errors with assigns clause Checking
13+
in the function under verification.

src/goto-instrument/contracts/contracts.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,9 @@ bool code_contractst::apply_function_contract(
672672
// keep track of the call's return expression to make it nondet later
673673
optionalt<exprt> call_ret_opt = {};
674674

675+
// if true, the call return variable variable was created during replacement
676+
bool call_ret_is_fresh_var = false;
677+
675678
if(type.return_type() != empty_typet())
676679
{
677680
// Check whether the function's return value is not disregarded.
@@ -696,6 +699,7 @@ bool code_contractst::apply_function_contract(
696699
{
697700
// The postcondition does mention __CPROVER_return_value, so mint a
698701
// fresh variable to replace __CPROVER_return_value with.
702+
call_ret_is_fresh_var = true;
699703
const symbolt &fresh = get_fresh_aux_symbol(
700704
type.return_type(),
701705
id2string(target_function),
@@ -803,6 +807,12 @@ bool code_contractst::apply_function_contract(
803807
auto &call_ret = call_ret_opt.value();
804808
auto &loc = call_ret.source_location();
805809
auto &type = call_ret.type();
810+
811+
// Declare if fresh
812+
if(call_ret_is_fresh_var)
813+
havoc_instructions.add(
814+
goto_programt::make_decl(to_symbol_expr(call_ret), loc));
815+
806816
side_effect_expr_nondett expr(type, location);
807817
auto target = havoc_instructions.add(
808818
goto_programt::make_assignment(call_ret, expr, loc));
@@ -819,6 +829,17 @@ bool code_contractst::apply_function_contract(
819829
is_fresh.update_ensures(ensures_pair.first);
820830
insert_before_swap_and_advance(function_body, target, ensures_pair.first);
821831
}
832+
833+
// Kill return value variable if fresh
834+
if(call_ret_is_fresh_var)
835+
{
836+
function_body.output_instruction(ns, "", log.warning(), *target);
837+
auto dead_inst =
838+
goto_programt::make_dead(to_symbol_expr(call_ret_opt.value()), location);
839+
function_body.insert_before_swap(target, dead_inst);
840+
++target;
841+
}
842+
822843
*target = goto_programt::make_skip();
823844

824845
// Add this function to the set of replaced functions.

0 commit comments

Comments
 (0)