Skip to content

Commit 7a2e934

Browse files
authored
Merge pull request #6074 from feliperodri/empty-assigns-clause
Enforce purity on missing assigns clause
2 parents 56adbb2 + c29b69a commit 7a2e934

File tree

9 files changed

+104
-64
lines changed

9 files changed

+104
-64
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
CORE
22
main.c
33
--enforce-all-contracts
4-
^EXIT=10$
4+
^EXIT=0$
55
^SIGNAL=0$
6-
^VERIFICATION FAILED$
6+
^\[f1.1\] line \d+ .*: SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
78
--
89
--
9-
This test checks that verification fails when a function with an assigns clause calls a function without an assigns clause.
10+
This test checks that verification succeeds when enforcing a contract
11+
for functions, without assigns clauses, that don't modify anything.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
int global = 0;
2+
3+
int foo(int *x) __CPROVER_assigns(*x)
4+
__CPROVER_ensures(__CPROVER_return_value == *x + 5)
5+
{
6+
int z = 5;
7+
int y = bar(&z);
8+
return *x + 5;
9+
}
10+
11+
int bar(int *a) __CPROVER_ensures(__CPROVER_return_value >= 5)
12+
{
13+
*a = *a + 2;
14+
return *a;
15+
}
16+
17+
int baz() __CPROVER_ensures(__CPROVER_return_value == global)
18+
{
19+
global = global + 1;
20+
return global;
21+
}
22+
23+
int main()
24+
{
25+
int n;
26+
n = foo(&n);
27+
n = baz();
28+
return 0;
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[bar.1\] line \d+ .*: FAILURE$
7+
^\[baz.1\] line \d+ .*: FAILURE$
8+
^VERIFICATION FAILED$
9+
--
10+
--
11+
Checks whether verification fails when enforcing a contract
12+
for functions, without assigns clauses, that modify an input.

regression/contracts/function_check_02/main.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
// A known bug (resolved in PR #2278) causes the use of quantifiers
55
// in ensures to fail.
66

7+
// clang-format off
78
int initialize(int *arr)
9+
__CPROVER_assigns(*arr)
810
__CPROVER_ensures(
9-
__CPROVER_forall {int i; (0 <= i && i < 10) ==> arr[i] == i}
11+
__CPROVER_forall {
12+
int i;
13+
(0 <= i && i < 10) ==> arr[i] == i
14+
}
1015
)
16+
// clang-format on
1117
{
1218
for(int i = 0; i < 10; i++)
1319
{

regression/contracts/quantifiers-exists-ensures-01/main.c

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// clang-format off
2-
int f1(int *arr) __CPROVER_ensures(__CPROVER_exists {
3-
int i;
4-
(0 <= i && i < 10) ==> arr[i] == i
5-
})
2+
int f1(int *arr)
3+
__CPROVER_assigns(*arr)
4+
__CPROVER_ensures(__CPROVER_exists {
5+
int i;
6+
(0 <= i && i < 10) ==> arr[i] == i
7+
})
68
// clang-format on
79
{
810
for(int i = 0; i < 10; i++)

regression/contracts/quantifiers-forall-ensures-01/main.c

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// clang-format off
2-
int f1(int *arr) __CPROVER_ensures(__CPROVER_forall {
3-
int i;
4-
(0 <= i && i < 10) ==> arr[i] == 0
5-
})
2+
int f1(int *arr)
3+
__CPROVER_assigns(*arr)
4+
__CPROVER_ensures(__CPROVER_forall {
5+
int i;
6+
(0 <= i && i < 10) ==> arr[i] == 0
7+
})
68
// clang-format on
79
{
810
for(int i = 0; i < 10; i++)

regression/contracts/quantifiers-nested-01/main.c

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// clang-format off
2-
int f1(int *arr) __CPROVER_ensures(__CPROVER_forall {
3-
int i;
4-
__CPROVER_forall
5-
{
6-
int j;
7-
(0 <= i && i < 10 && i <= j && j < 10) ==> arr[i] <= arr[j]
8-
}
9-
})
2+
int f1(int *arr)
3+
__CPROVER_assigns(*arr)
4+
__CPROVER_ensures(__CPROVER_forall {
5+
int i;
6+
__CPROVER_forall
7+
{
8+
int j;
9+
(0 <= i && i < 10 && i <= j && j < 10) ==> arr[i] <= arr[j]
10+
}
11+
})
1012
// clang-format on
1113
{
1214
for(int i = 0; i < 10; i++)

regression/contracts/quantifiers-nested-03/main.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
int f1(int *arr)
1+
int f1(int *arr) __CPROVER_assigns(*arr)
22
__CPROVER_ensures(__CPROVER_return_value == 0 && __CPROVER_exists {
33
int i;
44
(0 <= i && i < 10) && arr[i] == i

src/goto-instrument/code_contracts.cpp

+28-43
Original file line numberDiff line numberDiff line change
@@ -513,50 +513,38 @@ void code_contractst::instrument_call_statement(
513513

514514
exprt called_assigns =
515515
to_code_with_contract_type(called_symbol.type).assigns();
516-
if(called_assigns.is_nil()) // Called function has no assigns clause
517-
{
518-
// Create a false assertion, so the analysis
519-
// will fail if this function is called.
520-
goto_programt failing_assertion;
521-
failing_assertion.add(goto_programt::make_assertion(
522-
false_exprt(), instruction_iterator->source_location));
523-
program.insert_before_swap(instruction_iterator, failing_assertion);
524-
++instruction_iterator;
525-
526-
return;
527-
}
528-
else // Called function has assigns clause
516+
if(!called_assigns.is_nil()) // Called function has assigns clause
517+
{
518+
replace_symbolt replace;
519+
// Replace formal parameters
520+
code_function_callt::argumentst::const_iterator a_it =
521+
call.arguments().begin();
522+
for(code_typet::parameterst::const_iterator p_it =
523+
called_type.parameters().begin();
524+
p_it != called_type.parameters().end() &&
525+
a_it != call.arguments().end();
526+
++p_it, ++a_it)
529527
{
530-
replace_symbolt replace;
531-
// Replace formal parameters
532-
code_function_callt::argumentst::const_iterator a_it =
533-
call.arguments().begin();
534-
for(code_typet::parameterst::const_iterator p_it =
535-
called_type.parameters().begin();
536-
p_it != called_type.parameters().end() &&
537-
a_it != call.arguments().end();
538-
++p_it, ++a_it)
528+
if(!p_it->get_identifier().empty())
539529
{
540-
if(!p_it->get_identifier().empty())
541-
{
542-
symbol_exprt p(p_it->get_identifier(), p_it->type());
543-
replace.insert(p, *a_it);
544-
}
530+
symbol_exprt p(p_it->get_identifier(), p_it->type());
531+
replace.insert(p, *a_it);
545532
}
546-
547-
replace(called_assigns);
548-
549-
// check compatibility of assigns clause with the called function
550-
assigns_clauset called_assigns_clause(
551-
called_assigns, *this, function_id, log);
552-
exprt compatible =
553-
assigns_clause.compatible_expression(called_assigns_clause);
554-
goto_programt alias_assertion;
555-
alias_assertion.add(goto_programt::make_assertion(
556-
compatible, instruction_iterator->source_location));
557-
program.insert_before_swap(instruction_iterator, alias_assertion);
558-
++instruction_iterator;
559533
}
534+
535+
replace(called_assigns);
536+
537+
// check compatibility of assigns clause with the called function
538+
assigns_clauset called_assigns_clause(
539+
called_assigns, *this, function_id, log);
540+
exprt compatible =
541+
assigns_clause.compatible_expression(called_assigns_clause);
542+
goto_programt alias_assertion;
543+
alias_assertion.add(goto_programt::make_assertion(
544+
compatible, instruction_iterator->source_location));
545+
program.insert_before_swap(instruction_iterator, alias_assertion);
546+
++instruction_iterator;
547+
}
560548
}
561549

562550
bool code_contractst::check_for_looped_mallocs(const goto_programt &program)
@@ -634,9 +622,6 @@ bool code_contractst::add_pointer_checks(const std::string &function_name)
634622
const auto &type = to_code_with_contract_type(function_symbol.type);
635623

636624
exprt assigns_expr = type.assigns();
637-
// Return if there are no reference checks to perform.
638-
if(assigns_expr.is_nil())
639-
return false;
640625

641626
assigns_clauset assigns(assigns_expr, *this, function_id, log);
642627

0 commit comments

Comments
 (0)