-
Notifications
You must be signed in to change notification settings - Fork 274
Add support for assigns clauses on loops #6249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c2f986f
b5b8a5a
2499f8a
e0da7e7
49a75bf
cec18ba
2a9e3e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#include <assert.h> | ||
#include <stdlib.h> | ||
|
||
#define SIZE 8 | ||
|
||
struct blob | ||
{ | ||
char *data; | ||
}; | ||
|
||
void main() | ||
{ | ||
struct blob *b = malloc(sizeof(struct blob)); | ||
b->data = malloc(SIZE); | ||
|
||
b->data[5] = 0; | ||
for(unsigned i = 0; i < SIZE; i++) | ||
// clang-format off | ||
__CPROVER_assigns(i, __CPROVER_POINTER_OBJECT(b->data)) | ||
__CPROVER_loop_invariant(i <= SIZE) | ||
// clang-format on | ||
{ | ||
b->data[i] = 1; | ||
} | ||
|
||
assert(b->data[5] == 0); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
CORE | ||
main.c | ||
--apply-loop-contracts | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$ | ||
^\[main.\d+\] .* Check that i is assignable: SUCCESS$ | ||
^\[main.\d+\] .* Check that b->data\[(.*)i\] is assignable: SUCCESS$ | ||
^\[main.\d+\] .* Check that loop invariant is preserved: SUCCESS$ | ||
^\[main.assertion.\d+\] .* assertion b->data\[5\] == 0: FAILURE$ | ||
^VERIFICATION FAILED$ | ||
-- | ||
-- | ||
This test (taken from #6021) shows the need for assigns clauses on loops. | ||
The alias analysis in this case returns `unknown`, | ||
and so we must manually annotate an assigns clause on the loop. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#include <assert.h> | ||
#include <stdlib.h> | ||
|
||
#define SIZE 8 | ||
|
||
struct blob | ||
{ | ||
char *data; | ||
}; | ||
|
||
void main() | ||
{ | ||
struct blob *b = malloc(sizeof(struct blob)); | ||
b->data = malloc(SIZE); | ||
|
||
b->data[5] = 0; | ||
for(unsigned i = 0; i < SIZE; i++) | ||
// clang-format off | ||
__CPROVER_assigns(i, b->data[i]) | ||
__CPROVER_loop_invariant(i <= SIZE) | ||
// clang-format on | ||
{ | ||
b->data[i] = 1; | ||
} | ||
|
||
assert(b->data[5] == 0); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
CORE | ||
main.c | ||
--apply-loop-contracts | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$ | ||
^\[main.\d+\] .* Check that i is assignable: SUCCESS$ | ||
^\[main.\d+\] .* Check that b->data\[(.*)i\] is assignable: FAILURE$ | ||
^\[main.\d+\] .* Check that loop invariant is preserved: SUCCESS$ | ||
^\[main.assertion.\d+\] .* assertion b->data\[5\] == 0: FAILURE$ | ||
^VERIFICATION FAILED$ | ||
-- | ||
-- | ||
This test (taken from #6021) shows the need for assigns clauses on loops. | ||
The alias analysis in this case returns `unknown`, | ||
and so we must manually annotate an assigns clause on the loop. | ||
|
||
Note that the annotated assigns clause in this case is an underapproximation, | ||
per the current semantics of the assigns clause -- it must model ALL memory | ||
being assigned to by the loop, not just a single symbolic iteration. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#include <assert.h> | ||
#include <stdlib.h> | ||
|
||
#define SIZE 8 | ||
|
||
struct blob | ||
{ | ||
char *data; | ||
}; | ||
|
||
void main() | ||
{ | ||
struct blob *b = malloc(sizeof(struct blob)); | ||
b->data = malloc(SIZE); | ||
|
||
b->data[5] = 0; | ||
for(unsigned i = 0; i < SIZE; i++) | ||
// clang-format off | ||
__CPROVER_assigns(__CPROVER_POINTER_OBJECT(b->data)) | ||
__CPROVER_loop_invariant(i <= SIZE) | ||
// clang-format on | ||
{ | ||
b->data[i] = 1; | ||
} | ||
|
||
assert(b->data[5] == 0); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
CORE | ||
main.c | ||
--apply-loop-contracts | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$ | ||
^\[main.\d+\] .* Check that i is assignable: FAILURE$ | ||
^\[main.\d+\] .* Check that b->data\[(.*)i\] is assignable: SUCCESS$ | ||
^VERIFICATION FAILED$ | ||
-- | ||
-- | ||
This test (taken from #6021) shows the need for assigns clauses on loops. | ||
The alias analysis in this case returns `unknown`, | ||
and so we must manually annotate an assigns clause on the loop. | ||
|
||
Note that the annotated assigns clause in this case is an underapproximation, | ||
martin-cs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
because `i` is also assigned in the loop and should be marked as assignable. | ||
Comment on lines
+16
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but again, isn't that part of the "local write set"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that the initialization of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moreover the information that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh that clarifies this a bit more. So a simple loop like this for(int i = 0; i < 2; i++)
assert(i < 2); is converted to something like this
So the declaration of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Local variables (variables declared inside the loop / function body) are completely unrelated here. Loop-local variables need not be specified in the assigns clause -- they aren't even visible at the enclosing scope. I think the main confusion here is that Before C99 standard, declarations within {
...
for ( init-statement ; condition ; iteration-expression ) statement
..
} is equivalent to: (comments added by me) {
// This is the function-body scope
...
{
// This is NOT the loop-body scope
init-statement
while ( condition )
// This is where the invariant goes
{
// This is the loop-body scope
statement
iteration-expression ;
}
}
...
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you so much for adding a bit more context here! If there is a "loop scope" that encapsulates the entire loop and loop body, why are we focusing only in the loop body and not the most general scope? The only goal I'm trying to active with this discussion is: decrease the burden of annotation for users (this is not a blocker for this PR). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your previous comment respond my previous question: #6249 (comment). If you could grab all declarations from the initialization clause of a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes exactly. I'll try to push that change in this PR if it works (still playing with it), or would make a separate PR for it later. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#include <assert.h> | ||
#include <stdlib.h> | ||
|
||
#define SIZE 8 | ||
|
||
struct blob | ||
{ | ||
char *data; | ||
}; | ||
|
||
void main() | ||
{ | ||
int y; | ||
struct blob *b = malloc(sizeof(struct blob)); | ||
b->data = malloc(SIZE); | ||
|
||
b->data[5] = 0; | ||
for(unsigned i = 0; i < SIZE; i++) | ||
// clang-format off | ||
__CPROVER_assigns(i, __CPROVER_POINTER_OBJECT(b->data)) | ||
__CPROVER_loop_invariant(i <= SIZE) | ||
// clang-format on | ||
{ | ||
b->data[i] = 1; | ||
|
||
int x; | ||
for(unsigned j = 0; j < i; j++) | ||
// clang-format off | ||
// y is not assignable by outer loop, so this should be flagged | ||
SaswatPadhi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
__CPROVER_assigns(j, y, x, b->data[i]) | ||
__CPROVER_loop_invariant(j <= i) | ||
// clang-format on | ||
{ | ||
x = b->data[j] * b->data[j]; | ||
b->data[i] += x; | ||
y += j; | ||
} | ||
} | ||
|
||
assert(b->data[5] == 0); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.