Skip to content

Commit 3778757

Browse files
author
Aalok Thakkar
committed
Added support for loop_entry history variable.
1. Created a new predicate __CPROVER_loop_entry which can be used in the loop invariant to keep track of the value of a variable at the start of the loop. 2. Modified the code contract to support this history variable.
1 parent e4369b6 commit 3778757

File tree

16 files changed

+192
-26
lines changed

16 files changed

+192
-26
lines changed

regression/contracts/history-pointer-replace-03/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ main.c
33
--replace-all-calls-with-contracts
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6+
^main.c.* error: __CPROVER_old expressions are not allowed in this context$
67
^CONVERSION ERROR$
7-
error: __CPROVER_old expressions are not allowed in __CPROVER_requires clauses
88
--
99
--
1010
Verification:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
typedef struct
5+
{
6+
int *n;
7+
} s;
8+
9+
void main()
10+
{
11+
int *x1, y1, z1;
12+
*x1 = &z1;
13+
14+
while(y1 > 0)
15+
__CPROVER_loop_invariant(*x1 == __CPROVER_loop_entry(*x1))
16+
{
17+
--y1;
18+
*x1 = *x1 + 1;
19+
*x1 = *x1 - 1;
20+
}
21+
assert(*x1 == &z1);
22+
23+
int x2, y2, z2;
24+
x2 = z2;
25+
26+
while(y2 > 0)
27+
__CPROVER_loop_invariant(x2 == __CPROVER_loop_entry(x2))
28+
{
29+
--y2;
30+
x2 = x2 + 1;
31+
x2 = x2 - 1;
32+
}
33+
assert(x2 == z2);
34+
35+
int y3;
36+
s *s1, *s2;
37+
s2->n = malloc(sizeof(int));
38+
s1->n = s2->n;
39+
40+
while(y3 > 0)
41+
__CPROVER_loop_invariant(*s1 == __CPROVER_loop_entry(*s1))
42+
{
43+
--y3;
44+
s1->n = s1->n + 1;
45+
s1->n = s1->n - 1;
46+
}
47+
48+
assert(s1->n == s2->n);
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.2\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.assertion.1\] .* assertion \*x1 == &z1: SUCCESS$
9+
^\[main.3\] .* Check loop invariant before entry: SUCCESS$
10+
^\[main.4\] .* Check that loop invariant is preserved: SUCCESS$
11+
^\[main.assertion.2\] .* assertion x2 == z2: SUCCESS$
12+
^\[main.5\] .* Check loop invariant before entry: SUCCESS$
13+
^\[main.6\] .* Check that loop invariant is preserved: SUCCESS$
14+
^\[main.assertion.3\] .* assertion s1->n == s2->n: SUCCESS$
15+
^VERIFICATION SUCCESSFUL$
16+
--
17+
--
18+
This test checks that __CPROVER_loop_entry is supported.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <assert.h>
2+
3+
void main()
4+
{
5+
int *x, y, z;
6+
7+
*x = &z;
8+
9+
while(y > 0)
10+
__CPROVER_loop_invariant(*x == __CPROVER_old(*x))
11+
{
12+
--y;
13+
*x = *x + 1;
14+
*x = *x - 1;
15+
}
16+
assert(*x == &z);
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts
4+
^main.c.* error: __CPROVER_old expressions are not allowed in this context$
5+
^CONVERSION ERROR$
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This test ensures that __CPROVER_old cannot be used for loop contracts.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void foo(int *x) __CPROVER_assigns(*x)
2+
__CPROVER_ensures(*x == __CPROVER_loop_entry(*x) + 5)
3+
{
4+
*x = *x + 5;
5+
}
6+
7+
int main()
8+
{
9+
int n;
10+
foo(&n);
11+
12+
return 0;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^main.c.* error: __CPROVER_loop_entry expressions are not allowed in this context$
5+
^CONVERSION ERROR$
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This test ensures that __CPROVER_loop_entry cannot be
11+
used for function contracts.

src/ansi-c/c_expr.h

+8-5
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,11 @@ inline side_effect_expr_overflowt &to_side_effect_expr_overflow(exprt &expr)
202202

203203
/// \brief A class for an expression that indicates the pre-function-call
204204
/// value of an expression passed as a parameter to a function
205-
class old_exprt : public unary_exprt
205+
class history_exprt : public unary_exprt
206206
{
207207
public:
208-
explicit old_exprt(exprt variable) : unary_exprt(ID_old, std::move(variable))
208+
explicit history_exprt(exprt variable, const irep_idt &id)
209+
: unary_exprt(id, std::move(variable))
209210
{
210211
}
211212

@@ -215,10 +216,12 @@ class old_exprt : public unary_exprt
215216
}
216217
};
217218

218-
inline const old_exprt &to_old_expr(const exprt &expr)
219+
inline const history_exprt &
220+
to_history_expr(const exprt &expr, const irep_idt &id)
219221
{
220-
PRECONDITION(expr.id() == ID_old);
221-
auto &ret = static_cast<const old_exprt &>(expr);
222+
PRECONDITION(id == ID_old || id == ID_loop_entry);
223+
PRECONDITION(expr.id() == id);
224+
auto &ret = static_cast<const history_exprt &>(expr);
222225
validate_expr(ret);
223226
return ret;
224227
}

src/ansi-c/c_typecheck_base.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,8 @@ void c_typecheck_baset::typecheck_declaration(
730730
{
731731
typecheck_expr(requires);
732732
implicit_typecast_bool(requires);
733-
disallow_history_variables(requires);
733+
disallow_history_variables(requires, ID_old);
734+
disallow_history_variables(requires, ID_loop_entry);
734735
}
735736
}
736737

@@ -751,6 +752,7 @@ void c_typecheck_baset::typecheck_declaration(
751752
{
752753
typecheck_expr(ensures);
753754
implicit_typecast_bool(ensures);
755+
disallow_history_variables(ensures, ID_loop_entry);
754756
}
755757

756758
if(return_type.id() != ID_empty)

src/ansi-c/c_typecheck_base.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ class c_typecheck_baset:
206206
const symbol_exprt &function_symbol);
207207
virtual exprt
208208
typecheck_shuffle_vector(const side_effect_expr_function_callt &expr);
209-
void disallow_history_variables(const exprt &) const;
209+
void disallow_history_variables(const exprt &, const irep_idt &) const;
210210

211211
virtual void make_index_type(exprt &expr);
212212
virtual void make_constant(exprt &expr);

src/ansi-c/c_typecheck_code.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,7 @@ void c_typecheck_baset::typecheck_spec_loop_invariant(codet &code)
820820
{
821821
typecheck_expr(invariant);
822822
implicit_typecast_bool(invariant);
823+
disallow_history_variables(invariant, ID_old);
823824
}
824825
}
825826
}

src/ansi-c/c_typecheck_expr.cpp

+28-5
Original file line numberDiff line numberDiff line change
@@ -2704,7 +2704,21 @@ exprt c_typecheck_baset::do_special_functions(
27042704
throw 0;
27052705
}
27062706

2707-
old_exprt old_expr(expr.arguments()[0]);
2707+
history_exprt old_expr(expr.arguments()[0], ID_old);
2708+
old_expr.add_source_location() = source_location;
2709+
2710+
return std::move(old_expr);
2711+
}
2712+
else if(identifier == CPROVER_PREFIX "loop_entry")
2713+
{
2714+
if(expr.arguments().size() != 1)
2715+
{
2716+
error().source_location = f_op.source_location();
2717+
error() << identifier << " expects one operand" << eom;
2718+
throw 0;
2719+
}
2720+
2721+
history_exprt old_expr(expr.arguments()[0], ID_loop_entry);
27082722
old_expr.add_source_location() = source_location;
27092723

27102724
return std::move(old_expr);
@@ -3957,18 +3971,27 @@ void c_typecheck_baset::make_constant_index(exprt &expr)
39573971
}
39583972
}
39593973

3960-
void c_typecheck_baset::disallow_history_variables(const exprt &expr) const
3974+
void c_typecheck_baset::disallow_history_variables(
3975+
const exprt &expr,
3976+
const irep_idt &id) const
39613977
{
39623978
for(auto &op : expr.operands())
39633979
{
3964-
disallow_history_variables(op);
3980+
disallow_history_variables(op, id);
39653981
}
39663982

3967-
if(expr.id() == ID_old)
3983+
if(expr.id() == ID_old && id == ID_old)
3984+
{
3985+
error().source_location = expr.source_location();
3986+
error() << CPROVER_PREFIX "old expressions are not allowed in this context"
3987+
<< eom;
3988+
throw 0;
3989+
}
3990+
else if(expr.id() == ID_loop_entry && id == ID_loop_entry)
39683991
{
39693992
error().source_location = expr.source_location();
39703993
error() << CPROVER_PREFIX
3971-
"old expressions are not allowed in " CPROVER_PREFIX "requires clauses"
3994+
"loop_entry expressions are not allowed in this context"
39723995
<< eom;
39733996
throw 0;
39743997
}

src/ansi-c/cprover_builtin_headers.h

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ void __CPROVER_fence(const char *kind, ...);
3737
// contract-related functions
3838
__CPROVER_bool __CPROVER_is_fresh(const void *mem, __CPROVER_size_t size);
3939
void __CPROVER_old(const void *);
40+
void __CPROVER_loop_entry(const void *);
4041

4142
// pointers
4243
__CPROVER_size_t __CPROVER_POINTER_OBJECT(const void *);

src/goto-instrument/contracts/contracts.cpp

+27-11
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ void code_contractst::check_apply_loop_contracts(
147147
// H: loop;
148148
// E: ...
149149
// to
150+
// process history variables;
151+
// declare and assign loop_entry variables;
150152
// H: assert(invariant);
151153
// havoc;
152154
// assume(invariant);
@@ -176,6 +178,20 @@ void code_contractst::check_apply_loop_contracts(
176178
return invariant_copy;
177179
};
178180

181+
// Process "loop_entry" history variables
182+
// Find and replace "loop_entry" expression in the "invariant" expression.
183+
std::map<exprt, exprt> parameter2history;
184+
replace_history_parameter(
185+
invariant,
186+
parameter2history,
187+
loop_head->source_location,
188+
mode,
189+
havoc_code,
190+
ID_loop_entry);
191+
192+
// Create 'loop_entry' history variables
193+
insert_before_swap_and_advance(goto_function.body, loop_head, havoc_code);
194+
179195
// Generate: assert(invariant) just before the loop
180196
// We use a block scope to create a temporary assertion,
181197
// and immediately convert it to goto instructions.
@@ -377,24 +393,23 @@ void code_contractst::add_quantified_variable(
377393
}
378394
}
379395

380-
void code_contractst::replace_old_parameter(
396+
void code_contractst::replace_history_parameter(
381397
exprt &expr,
382398
std::map<exprt, exprt> &parameter2history,
383399
source_locationt location,
384400
const irep_idt &mode,
385-
goto_programt &history)
401+
goto_programt &history,
402+
const irep_idt &id)
386403
{
387404
for(auto &op : expr.operands())
388405
{
389-
replace_old_parameter(op, parameter2history, location, mode, history);
406+
replace_history_parameter(
407+
op, parameter2history, location, mode, history, id);
390408
}
391409

392-
if(expr.id() == ID_old)
410+
if(expr.id() == ID_old || expr.id() == ID_loop_entry)
393411
{
394-
DATA_INVARIANT(
395-
expr.operands().size() == 1, CPROVER_PREFIX "old must have one operand");
396-
397-
const auto &parameter = to_old_expr(expr).expression();
412+
const auto &parameter = to_history_expr(expr, id).expression();
398413

399414
if(
400415
parameter.id() == ID_dereference || parameter.id() == ID_member ||
@@ -428,8 +443,8 @@ void code_contractst::replace_old_parameter(
428443
}
429444
else
430445
{
431-
log.error() << CPROVER_PREFIX "old does not currently support "
432-
<< parameter.id() << " expressions." << messaget::eom;
446+
log.error() << "History variables do not support " << parameter.id()
447+
<< " expressions." << messaget::eom;
433448
throw 0;
434449
}
435450
}
@@ -445,7 +460,8 @@ code_contractst::create_ensures_instruction(
445460
goto_programt history;
446461

447462
// Find and replace "old" expression in the "expression" variable
448-
replace_old_parameter(expression, parameter2history, location, mode, history);
463+
replace_history_parameter(
464+
expression, parameter2history, location, mode, history, ID_old);
449465

450466
// Create instructions corresponding to the ensures clause
451467
goto_programt ensures_program;

src/goto-instrument/contracts/contracts.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,13 @@ class code_contractst
219219

220220
/// This function recursively identifies the "old" expressions within expr
221221
/// and replaces them with correspoding history variables.
222-
void replace_old_parameter(
222+
void replace_history_parameter(
223223
exprt &expr,
224224
std::map<exprt, exprt> &parameter2history,
225225
source_locationt location,
226226
const irep_idt &mode,
227-
goto_programt &history);
227+
goto_programt &history,
228+
const irep_idt &id);
228229

229230
/// This function creates and returns an instruction that corresponds to the
230231
/// ensures clause. It also returns a list of instructions related to

src/util/irep_ids.def

+1
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ IREP_ID_ONE(r_ok)
691691
IREP_ID_ONE(w_ok)
692692
IREP_ID_ONE(rw_ok)
693693
IREP_ID_ONE(old)
694+
IREP_ID_ONE(loop_entry)
694695
IREP_ID_ONE(super_class)
695696
IREP_ID_ONE(exceptions_thrown_list)
696697
IREP_ID_TWO(C_java_method_type, #java_method_type)

0 commit comments

Comments
 (0)