File tree 7 files changed +289
-84
lines changed
loop_guard_with_side_effects
loop_guard_with_side_effects_fail
nonvacuous_loop_contracts
src/goto-instrument/contracts 7 files changed +289
-84
lines changed Original file line number Diff line number Diff line change
1
+ #include <assert.h>
2
+ #include <limits.h>
3
+ #include <stdbool.h>
4
+
5
+ bool check (unsigned * i , unsigned * j , unsigned * k )
6
+ {
7
+ (* j )++ ;
8
+ return * i < * k ;
9
+ }
10
+
11
+ int main ()
12
+ {
13
+ unsigned i , j , k ;
14
+ __CPROVER_assume (k < UINT_MAX );
15
+
16
+ i = j = 0 ;
17
+
18
+ while (check (& i , & j , & k ))
19
+ // clang-format off
20
+ __CPROVER_assigns (i , j )
21
+ __CPROVER_loop_invariant (0 <= i && i == j && i <= k )
22
+ // clang-format on
23
+ {
24
+ i ++ ;
25
+ }
26
+
27
+ assert (i == k );
28
+ assert (j == k + 1 );
29
+ }
Original file line number Diff line number Diff line change
1
+ CORE
2
+ main.c
3
+ --apply-loop-contracts _ --unsigned-overflow-check
4
+ \[check.assigns\.\d+\] line \d+ Check that \*j is assignable: SUCCESS$
5
+ \[check.overflow\.\d+\] line \d+ arithmetic overflow on unsigned \+ in \*j \+ 1u: SUCCESS
6
+ \[check.overflow\.\d+\] line \d+ arithmetic overflow on unsigned \+ in \*j \+ 1u: SUCCESS
7
+ \[check.assigns\.\d+\] line \d+ Check that return_value_check is assignable: SUCCESS$
8
+ \[main\.\d+\] line \d+ Check loop invariant before entry: SUCCESS$
9
+ \[main\.\d+\] line \d+ Check that loop invariant is preserved: SUCCESS$
10
+ \[main.assigns\.\d+\] line \d+ Check that i is assignable: SUCCESS$
11
+ \[main.assigns\.\d+\] line \d+ Check that j is assignable: SUCCESS$
12
+ \[main.assigns\.\d+\] line \d+ Check that k is assignable: SUCCESS$
13
+ \[main.assigns\.\d+\] line \d+ Check that i is valid: SUCCESS$
14
+ \[main.assigns\.\d+\] line \d+ Check that j is valid: SUCCESS$
15
+ \[main.assigns\.\d+\] line \d+ Check that i is assignable: SUCCESS$
16
+ \[main.overflow\.\d+\] line \d+ arithmetic overflow on unsigned \+ in i \+ 1u: SUCCESS
17
+ \[main.assertion\.\d+\] line \d+ assertion i == k: SUCCESS$
18
+ \[main.overflow\.\d+\] line \d+ arithmetic overflow on unsigned \+ in k \+ \(unsigned int\)1: SUCCESS
19
+ \[main.assertion\.\d+\] line \d+ assertion j == k \+ 1: SUCCESS$
20
+ ^EXIT=0$
21
+ ^SIGNAL=0$
22
+ --
23
+ --
24
+ This test demonstrates a case where the loop guard has side effects.
25
+ The loop contracts must specify the state of all modified variables,
26
+ including those in the loop guard.
Original file line number Diff line number Diff line change
1
+ #include <assert.h>
2
+ #include <stdbool.h>
3
+
4
+ bool check (unsigned * i , unsigned * j , unsigned * k )
5
+ {
6
+ (* j )++ ;
7
+ return * i < * k ;
8
+ }
9
+
10
+ int main ()
11
+ {
12
+ unsigned i , j , k ;
13
+
14
+ i = j = 0 ;
15
+
16
+ while (check (& i , & j , & k ))
17
+ // clang-format off
18
+ __CPROVER_assigns (i )
19
+ __CPROVER_loop_invariant (0 <= i && i <= k )
20
+ // clang-format on
21
+ {
22
+ i ++ ;
23
+ }
24
+
25
+ assert (i == k );
26
+ }
Original file line number Diff line number Diff line change
1
+ CORE
2
+ main.c
3
+ --apply-loop-contracts _ --unsigned-overflow-check
4
+ \[check.assigns\.\d+\] line \d+ Check that \*j is assignable: FAILURE$
5
+ \[check.assigns\.\d+\] line \d+ Check that return_value_check is assignable: SUCCESS$
6
+ \[main\.\d+\] line \d+ Check loop invariant before entry: SUCCESS$
7
+ \[main\.\d+\] line \d+ Check that loop invariant is preserved: SUCCESS$
8
+ \[main.assigns\.\d+\] line \d+ Check that i is assignable: SUCCESS$
9
+ \[main.assigns\.\d+\] line \d+ Check that j is assignable: SUCCESS$
10
+ \[main.assigns\.\d+\] line \d+ Check that k is assignable: SUCCESS$
11
+ \[main.assigns\.\d+\] line \d+ Check that i is valid: SUCCESS$
12
+ \[main.assigns\.\d+\] line \d+ Check that i is assignable: SUCCESS$
13
+ \[main.assertion\.\d+\] line \d+ assertion i == k: SUCCESS$
14
+ ^EXIT=(6|10)$
15
+ ^SIGNAL=0$
16
+ --
17
+ --
18
+ This test demonstrates a case where the loop guard has side effects.
19
+ The writes performed in the guard must also be specified
20
+ in the assigns clause of the loop.
Original file line number Diff line number Diff line change
1
+ #include <assert.h>
2
+ #include <stdlib.h>
3
+
4
+ #define MAX_SIZE 1000000
5
+
6
+ int main ()
7
+ {
8
+ size_t size ;
9
+ __CPROVER_assume (size <= MAX_SIZE );
10
+
11
+ char * buf = malloc (size );
12
+ char c ;
13
+
14
+ size_t start ;
15
+ size_t end = start ;
16
+
17
+ while (end < size )
18
+ __CPROVER_loop_invariant (start <= end && end <= size )
19
+ __CPROVER_decreases (size - end )
20
+ {
21
+ if (buf [end ] != c )
22
+ break ;
23
+ end ++ ;
24
+ }
25
+
26
+ if (start > size )
27
+ {
28
+ assert (end == start );
29
+ }
30
+ else
31
+ {
32
+ assert (start <= end && end <= size );
33
+ }
34
+ }
Original file line number Diff line number Diff line change
1
+ CORE
2
+ main.c
3
+ --apply-loop-contracts _ --signed-overflow-check --unsigned-overflow-check
4
+ \[main.\d+\] line \d+ Check loop invariant before entry: SUCCESS$
5
+ \[main.\d+\] line \d+ Check that loop invariant is preserved: SUCCESS$
6
+ \[main.assigns.\d+\] line \d+ Check that end is valid: SUCCESS$
7
+ \[main.assigns.\d+\] line \d+ Check that end is assignable: SUCCESS$
8
+ \[main.assertion.\d+\] line \d+ assertion end == start: SUCCESS$
9
+ \[main.assertion.\d+\] line \d+ assertion start <= end && end <= size: SUCCESS$
10
+ ^EXIT=0$
11
+ ^SIGNAL=0$
12
+ --
13
+ --
14
+ This test demonstrates a simplification that is now possible on loop contracts.
15
+
16
+ Prior to this commit, loop contracts were unconditionally applied on loops,
17
+ and thus had to also consider the case when the loop doesn't run even once.
18
+
19
+ The contracts that were previously necessary were:
20
+ __CPROVER_loop_invariant(
21
+ (start > size && end == start) || (start <= end && end <= size)
22
+ )
23
+ __CPROVER_decreases(
24
+ max(start, size) - end
25
+ )
You can’t perform that action at this time.
0 commit comments