Skip to content

Commit 9ec901a

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 25ed175 commit 9ec901a

File tree

8 files changed

+84
-4
lines changed

8 files changed

+84
-4
lines changed
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_loop_entry(*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,11 @@
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+
^VERIFICATION SUCCESSFUL$
9+
--
10+
--
11+
This test checks that the history variable for loop entry is supported.

src/ansi-c/c_typecheck_base.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ void c_typecheck_baset::typecheck_declaration(
731731
typecheck_expr(requires);
732732
implicit_typecast_bool(requires);
733733
disallow_history_variables(requires);
734+
disallow_loop_history_variables(requires);
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_loop_history_variables(ensures);
754756
}
755757

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

src/ansi-c/c_typecheck_base.h

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ class c_typecheck_baset:
207207
virtual exprt
208208
typecheck_shuffle_vector(const side_effect_expr_function_callt &expr);
209209
void disallow_history_variables(const exprt &) const;
210+
void disallow_loop_history_variables(const exprt &) const;
210211

211212
virtual void make_index_type(exprt &expr);
212213
virtual void make_constant(exprt &expr);

src/ansi-c/c_typecheck_expr.cpp

+19-1
Original file line numberDiff line numberDiff line change
@@ -2647,7 +2647,8 @@ exprt c_typecheck_baset::do_special_functions(
26472647

26482648
return std::move(ok_expr);
26492649
}
2650-
else if(identifier == CPROVER_PREFIX "old")
2650+
else if(identifier == CPROVER_PREFIX "old" ||
2651+
identifier == CPROVER_PREFIX "loop_entry")
26512652
{
26522653
if(expr.arguments().size() != 1)
26532654
{
@@ -3925,3 +3926,20 @@ void c_typecheck_baset::disallow_history_variables(const exprt &expr) const
39253926
throw 0;
39263927
}
39273928
}
3929+
3930+
void c_typecheck_baset::disallow_loop_history_variables(const exprt &expr) const
3931+
{
3932+
for(auto &op : expr.operands())
3933+
{
3934+
disallow_loop_history_variables(op);
3935+
}
3936+
3937+
if(expr.id() == ID_loop_entry)
3938+
{
3939+
error().source_location = expr.source_location();
3940+
error() << CPROVER_PREFIX
3941+
"loop_entry expressions are not allowed in function contracts"
3942+
<< eom;
3943+
throw 0;
3944+
}
3945+
}

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

+32-3
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ void code_contractst::check_apply_loop_contracts(
145145
// H: loop;
146146
// E: ...
147147
// to
148+
// process history variables;
148149
// H: assert(invariant);
149150
// havoc;
150151
// assume(invariant);
@@ -174,6 +175,16 @@ void code_contractst::check_apply_loop_contracts(
174175
return invariant_copy;
175176
};
176177

178+
// Process "loop_entry" history variables
179+
std::map<exprt, exprt> parameter2history;
180+
goto_programt history;
181+
182+
// Find and replace "loop_entry" expression in the "invariant" expression.
183+
replace_old_parameter(invariant, parameter2history, loop_head->source_location, mode, history);
184+
185+
// Create 'loop_entry' history variables
186+
insert_before_swap_and_advance(goto_function.body, loop_head, history);
187+
177188
// Generate: assert(invariant) just before the loop
178189
// We use a block scope to create a temporary assertion,
179190
// and immediately convert it to goto instructions.
@@ -387,10 +398,19 @@ void code_contractst::replace_old_parameter(
387398
replace_old_parameter(op, parameter2history, location, mode, history);
388399
}
389400

390-
if(expr.id() == ID_old)
401+
if(expr.id() == ID_old ||
402+
expr.id() == ID_loop_entry)
391403
{
392-
DATA_INVARIANT(
404+
if (expr.id() == ID_old)
405+
{
406+
DATA_INVARIANT(
393407
expr.operands().size() == 1, CPROVER_PREFIX "old must have one operand");
408+
}
409+
if (expr.id() == ID_loop_entry)
410+
{
411+
DATA_INVARIANT(
412+
expr.operands().size() == 1, CPROVER_PREFIX "loop_entry must have one operand");
413+
}
394414

395415
const auto &parameter = to_old_expr(expr).expression();
396416

@@ -427,9 +447,18 @@ void code_contractst::replace_old_parameter(
427447
}
428448
else
429449
{
430-
log.error() << CPROVER_PREFIX "old does not currently support "
450+
if (expr.id() == ID_old)
451+
{
452+
log.error() << CPROVER_PREFIX "old does not currently support "
431453
<< parameter.id() << " expressions." << messaget::eom;
432454
throw 0;
455+
}
456+
if (expr.id() == ID_loop_entry)
457+
{
458+
log.error() << CPROVER_PREFIX "loop_entry does not currently support "
459+
<< parameter.id() << " expressions." << messaget::eom;
460+
throw 0;
461+
}
433462
}
434463
}
435464
}

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)