-
Notifications
You must be signed in to change notification settings - Fork 277
Unify havocing codegen within code contracts #6292
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
Conversation
0cbf501
to
d84d2b4
Compare
Codecov Report
@@ Coverage Diff @@
## develop #6292 +/- ##
===========================================
- Coverage 75.98% 75.98% -0.01%
===========================================
Files 1510 1512 +2
Lines 163467 163471 +4
===========================================
- Hits 124213 124209 -4
- Misses 39254 39262 +8
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for implementing this fix!! Just a few comments.
void append_havoc_code( | ||
const source_locationt source_location, | ||
const modifiest &modifies, | ||
goto_programt &dest); | ||
|
||
void append_object_havoc_code_for_expr( | ||
const source_locationt source_location, | ||
const exprt &expr, | ||
goto_programt &dest); | ||
|
||
void append_scalar_havoc_code_for_expr( | ||
const source_locationt source_location, | ||
const exprt &expr, | ||
goto_programt &dest); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the suffix "append"? Perhaps we can come up with more intuitive names. What do you think about havoc_code
, havoc_object
, and havoc_scalar
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original prefix was build
and it was inserting goto statements at the end of the dest
goto program. I was always confused about whether it adds the statements at the beginning or at the end, so I just changed build
-> append
. What part of the name is unintuitive?
The names havoc_code
, havoc_object
, and havoc_scalar
seem okay but lead to the same confusion as build
. Also notice that append_havoc_code
takes an entire modifies
set
, but the append_*_havoc_code_for_expr
functions take exprt
.
The editor autocompletes anyway, so I just use longer function names that are more explicit about their effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a blocker, since it is probably just a style matter. For me, clear and concise method names yields clear code.
I was always confused about whether it adds the statements at the beginning or at the end, so I just changed
build
->append
.
I'd add this information to a header comment in each method. This will also help us to build document for this file using Doxygen. Some editors also pick up this information. For instance, take a look at this method:
cbmc/src/goto-instrument/contracts/contracts.h
Lines 74 to 87 in 0bbbe19
/// \brief Replace all calls to each function in the list with that function's | |
/// contract | |
/// | |
/// Use this function when proving code that calls into an expensive function, | |
/// `F`. You can write a contract for F using __CPROVER_requires and | |
/// __CPROVER_ensures, and then use this function to replace all calls to `F` | |
/// with an assertion that the `requires` clause holds followed by an | |
/// assumption that the `ensures` clause holds. In order to ensure that `F` | |
/// actually abides by its `ensures` and `requires` clauses, you should | |
/// separately call `code_constractst::enforce_contracts()` on `F` and verify | |
/// it using `cbmc --function F`. | |
/// | |
/// \return `true` on failure, `false` otherwise | |
bool replace_calls(const std::set<std::string> &functions); |
Also notice that
append_havoc_code
takes an entiremodifies
set, but theappend_*_havoc_code_for_expr
functions takeexprt
.
This is the type of information that could easily go to a comment header or inferred just by looking at the parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I agree I should add some comments here regardless, but for now I think I will leave append_*_havoc_code_for_expr
and append_havoc_code
, unless there is something specific that's incorrect / confusing about these names.
What do you think about
havoc_code
,havoc_object
, andhavoc_scalar
?
I thought a bit more about these names, but havoc_code
seems incorrect -- it doesn't havoc a codet
but a set<exprt>
.
I guess we can revisit this later when we add comments & documentation for our functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I like the append_*_
naming :-)
modifiest modifies; | ||
std::for_each( | ||
targets.begin(), | ||
targets.end(), | ||
[&modifies](const assigns_clause_targett *t) { | ||
modifies.insert(to_address_of_expr(t->get_direct_pointer()).object()); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very clean 👏 nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😄 Thanks!
While writing this transformation, I noticed that targets
is a vector
, unlike modifies
, which is a set
. If we write the same expression twice in the __CPROVER_assigns
clause, would we end up havocing / enforcing it twice? Or, does the parser / typechecker somehow guard against this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we write the same expression twice in the
__CPROVER_assigns
clause, would we end up havocing / enforcing it twice? Or, does the parser / typechecker somehow guard against this?
We don't have any regression tests that cover this behavior. Could you write a regression test that cover this scenario for both loop contracts and function contracts?
While writing this transformation, I noticed that
targets
is avector
, unlikemodifies
, which is aset
.
Could you turn targets
into a set
too? Regardless of our type-checking procedure, set
would be a more appropriate data structure here.
These modifications might be part of a separate PR. I'm happy to implement it too if that's the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently this is not a concern for loops because we don't allow manual assigns
clause annotation yet.
I would separate this refactor (vector
-> set
) into another PR though. I'll open an internal ticket to track it.
209f403
to
64fe788
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have any more blocking comments. 🚀
This change fixes multiple known issues: - (function contracts) entire arrays were not being havoced - (loop contracts) pointers not checked for nullness before havocing
Resolves #6235.
In this PR, we unify the havocing code generation for both loop & function contracts.
This fixes multiple known issues:
The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/My commit message includes data points confirming performance improvements (if claimed).