-
Notifications
You must be signed in to change notification settings - Fork 274
Adding #pragma CPROVER enable "named-check"
#6450
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
Adding #pragma CPROVER enable "named-check"
#6450
Conversation
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.
Nice :) Mostly minor comments below.
Can we add at least one regression test to check nesting of both disable and enable pragmas, something like:
int n;
#pragma CPROVER check push
#pragma CPROVER check enable "signed-overflow"
// generate overflow checks for the following addition
int x = n + n;
#pragma CPROVER check push
#pragma CPROVER check disable "signed-overflow"
// do not generate overflow checks for the following increment operations
++n;
n++;
#pragma CPROVER check pop
// generate overflow checks for the following addition
int b = n + n;
#pragma CPROVER check pop
src/analyses/goto_check.cpp
Outdated
|
||
/// when true causes a forced collection of all allocations | ||
/// (needed to allow selectively enabling checks per instruction) | ||
bool do_collect_allocations; |
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.
It seems like we are setting this to true
in the constructor and no other method sets it to false
, so wouldn't this be always true
? And if that's the case, can we completely remove this and also remove the if(!... & !...) return;
check in collect_allocations
?
Or did I miss a code path where it won't be true
?
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.
+1
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 variable was removed and collection is now always on
src/analyses/goto_check.cpp
Outdated
{ | ||
log.warning() << "Found more than one CPROVER enable/disable check " | ||
"pragma on a same goto instruction" | ||
<< messaget::eom; |
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 think the error message could be improved a bit. Having "more than one CPROVER enable/disable check" is fine as long as they are different checks.
I think we want to say that we the same check being enabled/disabled multiple times times at a goto instruction.
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.
message updated to include the name of the check
^SIGNAL=0$ | ||
-- | ||
^\[main.pointer_primitives.\d+\] line 17 | ||
-- |
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.
[nit] missing newline. Also in a few other files.
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.
fixed
#pragma CPROVER check enable "signed-overflow" | ||
// generate assertions for the following statement | ||
x = x + y[1]; | ||
// pop all annotations |
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.
// pop all annotations | |
// pop all checks added since the last push |
[nit] also in other test files
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.
+1
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.
comment removed
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.
Only minor comments (modulo Saswat's comments).
doc/cprover-manual/properties.md
Outdated
use `#pragma CPROVER check disable "<name_of_check>"`, which remains in effect | ||
until a `#pragma CPROVER check pop` (to re-enable all properties | ||
disabled before or since the last `#pragma CPROVER check push`) is provided. | ||
disabled or enabled before or since the last `#pragma CPROVER check push`) is provided. |
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.
(to re-enable all properties disabled or enabled before or since the last `#pragma CPROVER check push`)
This wording seems confusing. Could you re-write it?
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.
documentation updated
doc/cprover-manual/properties.md
Outdated
until a `#pragma CPROVER check pop` (to re-enable all properties | ||
disabled or enabled before or since the last `#pragma CPROVER check push`) is provided. |
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.
Same as above.
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.
documentation updated
#pragma CPROVER check enable "signed-overflow" | ||
// generate assertions for the following statement | ||
x = x + y[1]; | ||
// pop all annotations |
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.
+1
^SIGNAL=0$ | ||
-- | ||
-- | ||
Checks that we can selectively activate checks using pragmas. |
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.
Add newline.
src/analyses/goto_check.cpp
Outdated
|
||
/// when true causes a forced collection of all allocations | ||
/// (needed to allow selectively enabling checks per instruction) | ||
bool do_collect_allocations; |
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.
+1
Codecov Report
@@ Coverage Diff @@
## develop #6450 +/- ##
===========================================
+ Coverage 76.01% 76.06% +0.05%
===========================================
Files 1527 1546 +19
Lines 164465 165584 +1119
===========================================
+ Hits 125013 125951 +938
- Misses 39452 39633 +181
Continue to review full report at Codecov.
|
Just a general git housekeeping comment - it's generally better to use |
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.
Functionality looks ok, but I think this PR needs some cleanup to remove as much noise as possible.
843b48e
to
9686089
Compare
4e8f4e2
to
702bbd9
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.
A few more comments; nothing major
src/analyses/goto_check.cpp
Outdated
if(d.first == "disable:bounds-check") | ||
flag_resetter.set_flag(enable_bounds_check, false); | ||
flag_resetter.set_flag(enable_bounds_check, false, err_msg); |
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 am not sure if I understand why err_msg
needs to be a lambda function. It seems like it's called immediately (at the INVARIANT
check) within the set_flag
call. Can we not just pass a string
here instead?
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 lambda is used to defer the creation of the error string until it is actually needed (if ever).
src/analyses/goto_check.cpp
Outdated
return "no_enum_check flag disabled and enabled at \n" + | ||
i.source_location().pretty(); | ||
}; | ||
no_enum_check_flag_resetter.set_flag(no_enum_check, true, err_msg); |
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.
Same comment as above.
Can we not just pass a string
here instead?
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.
now passing flag name and instructions to set_flag
src/analyses/goto_check.cpp
Outdated
/// \brief True iff flag has already been seen by \ref set_flag | ||
bool seen(bool &flag) | ||
{ | ||
return seen_flags.find(&flag) != seen_flags.end(); |
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.
Hmm, we seem to be taking the address of flag
here. So if we have two flag
s with the same content but different addresses then this would break?
Do we have some mechanism to guard against it? Or if there's some other reason as to why this is a non-issue, we should mention that in the comments above this function.
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 class flag_resett
is file-local so as long as the discipline is respected in this file it should be ok.
702bbd9
to
1018f34
Compare
565af96
to
c8cd39e
Compare
src/analyses/goto_check.cpp
Outdated
if(flag != new_value) | ||
{ | ||
flags_to_reset.emplace_back(&flag, flag); | ||
flags_to_reset.insert(std::pair<bool *, bool>(&flag, flag)); |
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 might be wrong, but I believe you can shorten this to flags_to_reset.insert({&flag, flag});
with C++11.
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.
Or just flags_to_reset.emplace(&flag, flag);
to avoid creating a temporary pair.
src/analyses/goto_check.cpp
Outdated
~flag_resett() | ||
{ | ||
for(const auto &flag_pair : flags_to_reset) | ||
*flag_pair.first = flag_pair.second; | ||
} | ||
|
||
private: | ||
std::list<std::pair<bool *, bool>> flags_to_reset; | ||
/// \brief True iff flag has already been seen by \ref set_flag | ||
bool seen(bool *flag) |
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.
Not that it matters a whole lot, but the parameter type should be const bool *
to make clear that no modification will occur. Also, the method should be marked const
.
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.
method removed
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.
Approving modulo Michael's remaining comments and one minor suggestion:
std::string check_name = id2string(pragma.first); | ||
std::string full_name = | ||
(pragma.second ? "enable:" : "disable:") + check_name; |
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.
std::string check_name = id2string(pragma.first); | |
std::string full_name = | |
(pragma.second ? "enable:" : "disable:") + check_name; | |
auto check_name = id2string(pragma.first); | |
auto full_name = | |
(pragma.second ? "enable:" : "disable:") + check_name; |
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.
scanner.l
uses std::string
everywhere so I'll keep this as is for consistency
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.
Yes, but not sure why it has to be kept this way here though ... Using auto
is not only more succinct but also makes it future proof (disconnects this from the underlying type, which is base std::string
today)
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 would only be patching two lines out of several dozen that could use auto
the whole lexer would need refactoring to be effectively future proof.
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.
Up to you 🙂
0f77949
to
b48db7c
Compare
@peterschrammel @martin-cs could you take a look at this PR? It's missing code owner approval. |
#pragma CPROVER enable "named-check"
#pragma CPROVER enable "named-check"
d5c6659
to
0008223
Compare
0008223
to
21897d5
Compare
@feliperodri Apologies for the lag; I missed this one. |
This would be easier to review if it were split up into multiple commits. Parsing, implementation, testing would seem a reasonable start. |
Adding enable and disable pragmas for a same check at a same pragma stack level triggers an error. Enable and disable pragmas for a same check in different stack levels is allowed, and shadowing is managed when flattening the stack.
No change in behaviour expected. No need for specific tests.
Needed so that the data required to express pointer checks is always available to process a enable pragmas.
…ble updates with opposite values.
21897d5
to
68e8888
Compare
@martin-cs @feliperodri @tautschnig I reworked the commit history it should be good for the final review now |
Thank you @remi-delmas-3000 it makes reviewing so much easier! |
This PR adds the possibility to selectively activate checks :
#pragma CPROVER enable "named-check"
source_location.add_pragma("enable:named-check")
It is possible to enable/disable a check, to push a level on the stack and override the check status using another pragma enable/disable pragma.
Attempts to enable and disable a same check at the same stack level are detected and cause a PARSING_ERROR in the C front end and a INVARIANT violation in
goto_check
.Modifications:
goto_checkt::collect_allocation
is now always onmessaget log
attribute is added togoto_checkt
to be able to log warnings in case a same check is enabled/disabled more than once on a given GOTO instructionflag_resett
class is extended to detect multiple 'set_flag' operations on a same flag#pragma CPROVER enable "named check"
and flag shadowingregression/cbmc/pragma_cprover_enable*
to test the featureThis feature is needed as part of the function and loop contracts verification, to be able to selectively activate checks on instructions used for assigns-clause checking, in order to guarantee the soundness of the analysis, even if these checks are not globally activated in the current or ulterior
goto-instrument
orcbmc
passes