File tree Expand file tree Collapse file tree 12 files changed +485
-191
lines changed
loop_guard_with_side_effects_fail
loop_guard_with_side_effects
nonvacuous_loop_contracts
variant_missing_invariant_warning
variant_multi_instruction_loop_head
src/goto-instrument/contracts Expand file tree Collapse file tree 12 files changed +485
-191
lines changed Original file line number Diff line number Diff line change 9
9
^\[main\.\d+\] .* Check loop invariant before entry: SUCCESS$
10
10
^\[main\.\d+\] .* Check that loop invariant is preserved: SUCCESS$
11
11
^\[main\.\d+\] .* Check decreases clause on loop iteration: SUCCESS$
12
- ^\[main.assigns.\d+] line 23 Check that s is assignable: SUCCESS$
13
- ^\[main.assigns.\d+] line 24 Check that a is assignable: SUCCESS$
14
- ^\[main.assigns.\d+] line 27 Check that s is assignable: SUCCESS$
12
+ ^\[main.assigns.\d+] .* line 23 Check that s is assignable: SUCCESS$
13
+ ^\[main.assigns.\d+] .* line 24 Check that a is assignable: SUCCESS$
14
+ ^\[main.assigns.\d+] .* line 27 Check that s is assignable: SUCCESS$
15
15
^\[main\.assertion.\d+\] .* assertion s == n: SUCCESS$
16
16
^VERIFICATION SUCCESSFUL$
17
17
--
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
+ int main ()
5
+ {
6
+ size_t size ;
7
+
8
+ char * buf = malloc (size );
9
+ char c ;
10
+
11
+ size_t start ;
12
+ size_t end = start ;
13
+
14
+ while (end < size )
15
+ // clang-format off
16
+ __CPROVER_loop_invariant (start <= end && end <= size )
17
+ __CPROVER_decreases (size - end )
18
+ // clang-format on
19
+ {
20
+ if (buf [end ] != c )
21
+ break ;
22
+ end ++ ;
23
+ }
24
+
25
+ if (start > size )
26
+ {
27
+ assert (end == start );
28
+ }
29
+ else
30
+ {
31
+ assert (start <= end && end <= size );
32
+ }
33
+ }
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
+ )
Original file line number Diff line number Diff line change 2
2
main.c
3
3
--apply-loop-contracts
4
4
activate-multi-line-match
5
- ^The loop at file main.c line 4 function main does not have an invariant.*.\nHence, a default invariant \('true'\) is being used to prove those .$
5
+ ^The loop at file main.c line 4 function main does not have an invariant.*.\nHence, a default invariant \('true'\) is being used.* .$
6
6
^\[main\.\d+\] .* Check loop invariant before entry: SUCCESS$
7
7
^\[main\.\d+\] .* Check that loop invariant is preserved: SUCCESS$
8
8
^\[main\.\d+\] .* Check decreases clause on loop iteration: SUCCESS$
Original file line number Diff line number Diff line change @@ -5,7 +5,7 @@ int main()
5
5
6
6
while (* y <= 0 && * y < 10 )
7
7
// clang-format off
8
- __CPROVER_loop_invariant (1 == 1 )
8
+ __CPROVER_loop_invariant (0 <= * y && * y <= 10 )
9
9
__CPROVER_decreases (10 - x )
10
10
// clang-format on
11
11
{
Original file line number Diff line number Diff line change 1
1
CORE
2
2
main.c
3
3
--apply-loop-contracts
4
- ^VERIFICATION FAILED$
5
- ^EXIT=10$
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\.\d+\] line \d+ Check decreases clause on loop iteration: SUCCESS$
7
+ \[main\.\d+\] line \d+ Check that loop instrumentation was not truncated: SUCCESS$
8
+ \[main\.assigns\.\d+\] line \d+ Check that x is valid: SUCCESS$
9
+ \[main\.assigns\.\d+\] line \d+ Check that x is assignable: SUCCESS$
10
+ ^VERIFICATION SUCCESSFUL$
11
+ ^EXIT=0$
6
12
^SIGNAL=0$
7
13
--
8
14
--
9
- This test fails even without the fix proposed in the commit, so it should be improved.
10
- It is expected to fail because the proposed invariant isn't strong enough to help prove
11
- termination using the specified variant.
12
-
13
- The test highlights a case where a C loop guard is compiled to multiple GOTO instructions.
14
- Therefore the loop_head pointer needs to be advanced multiple times to get to the loop body,
15
- where the initial value of the loop variant (/ ranking function) must be recorded.
16
-
17
- The proposed fix advances the pointer until the source_location differs from the original
18
- loop_head's source_location. However, this might still not work if the loop guard in the
19
- original C code was split across multiple lines in the first place.
15
+ This test checks that decreases clause is properly initialized
16
+ when the loop head and loop invariant compilation emit several goto instructions.
You can’t perform that action at this time.
0 commit comments