Skip to content

Commit 2b3c737

Browse files
authored
Merge pull request #6278 from aalok-thakkar/history-variables
Added support for loop_entry history variables in loop invariants.
2 parents a023d0f + f8cca6c commit 2b3c737

File tree

20 files changed

+244
-41
lines changed

20 files changed

+244
-41
lines changed
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,10 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^main.c.* error: __CPROVER_loop_entry is not allowed in postconditions.$
5+
^CONVERSION ERROR$
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This test ensures that __CPROVER_loop_entry cannot be used within ensures clause.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void bar(int *x) __CPROVER_assigns(*x)
2+
__CPROVER_requires(*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,10 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^main.c.* error: __CPROVER_loop_entry is not allowed in preconditions.$
5+
^CONVERSION ERROR$
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This test ensures that __CPROVER_loop_entry cannot be used within requires clause.
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
CORE
22
main.c
33
--replace-all-calls-with-contracts
4+
^main.c.* error: __CPROVER_old is not allowed in preconditions.$
5+
^CONVERSION ERROR$
46
^EXIT=(1|64)$
57
^SIGNAL=0$
6-
^CONVERSION ERROR$
7-
error: __CPROVER_old expressions are not allowed in __CPROVER_requires clauses
88
--
99
--
10-
Verification:
1110
This test checks that history variables cannot be used as part of the
12-
pre-condition contract. In this case, verification should fail.
11+
pre-condition (requires) contract.
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 is not allowed in loop invariants.$
5+
^CONVERSION ERROR$
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This test ensures that __CPROVER_old cannot be used within loop contracts.
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->n == __CPROVER_loop_entry(s1->n))
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,15 @@
1+
#include <assert.h>
2+
3+
void main()
4+
{
5+
int x, y, z;
6+
x = z;
7+
8+
while(y > 0)
9+
__CPROVER_loop_invariant(x == __CPROVER_loop_entry(x))
10+
{
11+
--y;
12+
x = x + 1;
13+
x = x - 2;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts
4+
^EXIT=(10|6)$
5+
^SIGNAL=0$
6+
^\[main.1\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.2\] .* Check that loop invariant is preserved: FAILURE$
8+
^VERIFICATION FAILED$
9+
--
10+
--
11+
This test ensures that __CPROVER_loop_entry violations are checked.

src/ansi-c/c_expr.h

+9-7
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,12 @@ inline side_effect_expr_overflowt &to_side_effect_expr_overflow(exprt &expr)
200200
return static_cast<side_effect_expr_overflowt &>(side_effect_expr);
201201
}
202202

203-
/// \brief A class for an expression that indicates the pre-function-call
204-
/// value of an expression passed as a parameter to a function
205-
class old_exprt : public unary_exprt
203+
/// \brief A class for an expression that indicates a history variable
204+
class history_exprt : public unary_exprt
206205
{
207206
public:
208-
explicit old_exprt(exprt variable) : unary_exprt(ID_old, std::move(variable))
207+
explicit history_exprt(exprt variable, const irep_idt &id)
208+
: unary_exprt(id, std::move(variable))
209209
{
210210
}
211211

@@ -215,10 +215,12 @@ class old_exprt : public unary_exprt
215215
}
216216
};
217217

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

src/ansi-c/c_typecheck_base.cpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,14 @@ void c_typecheck_baset::typecheck_declaration(
730730
{
731731
typecheck_expr(requires);
732732
implicit_typecast_bool(requires);
733-
disallow_history_variables(requires);
733+
disallow_subexpr_by_id(
734+
requires,
735+
ID_old,
736+
CPROVER_PREFIX "old is not allowed in preconditions.");
737+
disallow_subexpr_by_id(
738+
requires,
739+
ID_loop_entry,
740+
CPROVER_PREFIX "loop_entry is not allowed in preconditions.");
734741
}
735742
}
736743

@@ -751,6 +758,10 @@ void c_typecheck_baset::typecheck_declaration(
751758
{
752759
typecheck_expr(ensures);
753760
implicit_typecast_bool(ensures);
761+
disallow_subexpr_by_id(
762+
ensures,
763+
ID_loop_entry,
764+
CPROVER_PREFIX "loop_entry is not allowed in postconditions.");
754765
}
755766

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

src/ansi-c/c_typecheck_base.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,10 @@ 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_subexpr_by_id(
210+
const exprt &,
211+
const irep_idt &,
212+
const std::string &) const;
210213

211214
virtual void make_index_type(exprt &expr);
212215
virtual void make_constant(exprt &expr);

src/ansi-c/c_typecheck_code.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,10 @@ void c_typecheck_baset::typecheck_spec_loop_invariant(codet &code)
820820
{
821821
typecheck_expr(invariant);
822822
implicit_typecast_bool(invariant);
823+
disallow_subexpr_by_id(
824+
invariant,
825+
ID_old,
826+
CPROVER_PREFIX "old is not allowed in loop invariants.");
823827
}
824828
}
825829
}

src/ansi-c/c_typecheck_expr.cpp

+15-15
Original file line numberDiff line numberDiff line change
@@ -2695,7 +2695,9 @@ exprt c_typecheck_baset::do_special_functions(
26952695

26962696
return std::move(ok_expr);
26972697
}
2698-
else if(identifier == CPROVER_PREFIX "old")
2698+
else if(
2699+
(identifier == CPROVER_PREFIX "old") ||
2700+
(identifier == CPROVER_PREFIX "loop_entry"))
26992701
{
27002702
if(expr.arguments().size() != 1)
27012703
{
@@ -2704,7 +2706,9 @@ exprt c_typecheck_baset::do_special_functions(
27042706
throw 0;
27052707
}
27062708

2707-
old_exprt old_expr(expr.arguments()[0]);
2709+
irep_idt id = identifier == CPROVER_PREFIX "old" ? ID_old : ID_loop_entry;
2710+
2711+
history_exprt old_expr(expr.arguments()[0], id);
27082712
old_expr.add_source_location() = source_location;
27092713

27102714
return std::move(old_expr);
@@ -3957,19 +3961,15 @@ void c_typecheck_baset::make_constant_index(exprt &expr)
39573961
}
39583962
}
39593963

3960-
void c_typecheck_baset::disallow_history_variables(const exprt &expr) const
3964+
void c_typecheck_baset::disallow_subexpr_by_id(
3965+
const exprt &expr,
3966+
const irep_idt &id,
3967+
const std::string &message) const
39613968
{
3962-
for(auto &op : expr.operands())
3963-
{
3964-
disallow_history_variables(op);
3965-
}
3969+
if(!has_subexpr(expr, id))
3970+
return;
39663971

3967-
if(expr.id() == ID_old)
3968-
{
3969-
error().source_location = expr.source_location();
3970-
error() << CPROVER_PREFIX
3971-
"old expressions are not allowed in " CPROVER_PREFIX "requires clauses"
3972-
<< eom;
3973-
throw 0;
3974-
}
3972+
error().source_location = expr.source_location();
3973+
error() << message << eom;
3974+
throw 0;
39753975
}

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 *);

0 commit comments

Comments
 (0)