Skip to content

Add a __CPROVER_postcondition builtin #4921

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

Merged
merged 4 commits into from
Aug 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions doc/cprover-manual/cbmc-assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,34 @@ for(i=0; i<100; i++)
assert(a[i]==0);
```

CPROVER also supports writing function pre and postconditions, using
the built-in functions `__CPROVER_precondition` and
`__CPROVER_postcondition`. They can be used to express intent, and at
the moment they are just transformed to assertions in the goto
program. As such, they can be used as simple assertions in
code. However, it is advised to use `__CPROVER_precondition` at the
beginning of a function's body, and `__CPROVER_postcondition` before
the exit points in a function (either the return statements, or the
end of the body if the function returns void). The following is an
example usage:

```C
int foo(int a, int b) {
__CPROVER_precondition(a >= 0);
__CPROVER_precondition(b > 0);

int rval = a / b;

__CPROVER_postcondition(rval >= 0);
return rval;
}
```

A future release of CPROVER will support using these pre and
postconditions to create a function contract, which can be used for
modular verification.


Future CPROVER releases will support explicit quantifiers with a syntax
that resembles Spec\#:

Expand Down
14 changes: 14 additions & 0 deletions regression/cbmc/cprover_postcondition/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
int func(int a)
{
__CPROVER_precondition(a > 10, "Argument should be larger than 10");
int rval = a - 10;
__CPROVER_postcondition(rval > 1, "Return value should be positive");
return rval;
}

int main()
{
int a = 11;
int rval = func(a);
return 0;
}
7 changes: 7 additions & 0 deletions regression/cbmc/cprover_postcondition/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CORE
main.c

^EXIT=10$
^SIGNAL=0$
^VERIFICATION FAILED$
--
13 changes: 13 additions & 0 deletions regression/cbmc/cprover_precondition/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
void func(int a)
{
__CPROVER_precondition(a > 10, "Argument should be larger than 10.");
}

int main()
{
int a = 10;

func(a);

return 0;
}
7 changes: 7 additions & 0 deletions regression/cbmc/cprover_precondition/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CORE
main.c

^EXIT=10$
^SIGNAL=0$
^VERIFICATION FAILED$
--
1 change: 1 addition & 0 deletions src/ansi-c/cprover_builtin_headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ void __CPROVER_assume(__CPROVER_bool assumption);
void __VERIFIER_assume(__CPROVER_bool assumption);
void __CPROVER_assert(__CPROVER_bool assertion, const char *description);
void __CPROVER_precondition(__CPROVER_bool precondition, const char *description);
void __CPROVER_postcondition(__CPROVER_bool assertion, const char *description);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Random place for a comment: I think this strictly requires documentation. When should users make use of this? I note that the commit message doesn't have a body either, so it's really hard to see when that should be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Where is a good place to add documentation for this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed elsewhere: doc/cprover-manual/cbmc-assertions.md is likely the best place for this. @kroening Would you be able to contribute some documentation of __CPROVER_precondition, which AFAIK never got documented?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would structure this differently.
__CPROVER_precondition was introduced as a means to add preconditions to library functions, i.e., it was never meant to be used by anyone but CBMC developers.

The new feature, which is contract checking, is a different use-case, and externalises this to users of CBMC. My experience is that users like manuals that are structured by use-case. Thus, I would suggest to add a new chapter, say titled "Modular Verification using Code Contracts", or the like, to discuss this use-case separately.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[...] never meant to be used by anyone but CBMC developers.

While this is a good intention, I don't think it will remain as constrained if people find it useful in other places as well. Anything that can be used will be used at some point...

void __CPROVER_havoc_object(void *);
__CPROVER_bool __CPROVER_equal();
__CPROVER_bool __CPROVER_same_object(const void *, const void *);
Expand Down
1 change: 1 addition & 0 deletions src/ansi-c/library/cprover.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ extern const void *__CPROVER_memory_leak;
void __CPROVER_assume(__CPROVER_bool assumption) __attribute__((__noreturn__));
void __CPROVER_assert(__CPROVER_bool assertion, const char *description);
void __CPROVER_precondition(__CPROVER_bool assertion, const char *description);
void __CPROVER_postcondition(__CPROVER_bool assertion, const char *description);

__CPROVER_bool __CPROVER_is_zero_string(const void *);
__CPROVER_size_t __CPROVER_zero_string_length(const void *);
Expand Down
1 change: 1 addition & 0 deletions src/cpp/library/cprover.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ extern const void *__CPROVER_memory_leak;
void __CPROVER_assume(__CPROVER_bool assumption) __attribute__((__noreturn__));
void __CPROVER_assert(__CPROVER_bool assertion, const char *description);
void __CPROVER_precondition(__CPROVER_bool assertion, const char *description);
void __CPROVER_postcondition(__CPROVER_bool assertion, const char *description);

// NOLINTNEXTLINE(build/deprecated)
void __CPROVER_input(const char *description, ...);
Expand Down
11 changes: 9 additions & 2 deletions src/goto-programs/builtin_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,8 +735,10 @@ void goto_convertt::do_function_call_symbol(
throw 0;
}
}
else if(identifier==CPROVER_PREFIX "assert" ||
identifier==CPROVER_PREFIX "precondition")
else if(
identifier == CPROVER_PREFIX "assert" ||
identifier == CPROVER_PREFIX "precondition" ||
identifier == CPROVER_PREFIX "postcondition")
{
if(arguments.size()!=2)
{
Expand All @@ -747,6 +749,7 @@ void goto_convertt::do_function_call_symbol(

bool is_precondition=
identifier==CPROVER_PREFIX "precondition";
bool is_postcondition = identifier == CPROVER_PREFIX "postcondition";

const irep_idt description=
get_string_constant(arguments[1]);
Expand All @@ -760,6 +763,10 @@ void goto_convertt::do_function_call_symbol(
{
t->source_location.set_property_class(ID_precondition);
}
else if(is_postcondition)
{
t->source_location.set_property_class(ID_postcondition);
}
else
{
t->source_location.set(
Expand Down
1 change: 1 addition & 0 deletions src/util/irep_ids.def
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ IREP_ID_ONE(assume)
IREP_ID_ONE(assert)
IREP_ID_ONE(assertion)
IREP_ID_ONE(precondition)
IREP_ID_ONE(postcondition)
IREP_ID_ONE(precondition_instance)
IREP_ID_ONE(goto)
IREP_ID_ONE(gcc_computed_goto)
Expand Down