-
Notifications
You must be signed in to change notification settings - Fork 274
Cprover enum is in range #5808
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
Cprover enum is in range #5808
Conversation
TGWDB
commented
Feb 9, 2021
- Each commit message has a non-empty body, explaining why the change was made.
- Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.
- The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/
- Regression or unit tests are included, or existing tests cover the modified code (in this case I have detailed which ones those are in the commit message).
- My commit message includes data points confirming performance improvements (if claimed).
- My PR is restricted to a single feature or bugfix.
- White-space or formatting changes outside the feature-related changed lines are in commits of their own.
src/ansi-c/c_typecheck_expr.cpp
Outdated
<< expr.arguments().size() << " were provided"; | ||
throw invalid_source_file_exceptiont{error_message.str()}; | ||
} | ||
// Replace expr with giant boolean that checks all the values |
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 think the translation into a condition should be done here, but rather later as a pass on the goto program.
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.
@peterschrammel Could you elaborate where exactly you'd like this to happen?
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 it would fit well into the goto_check pass. Then you can even reuse the logic for generating the conditons that is already there.
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.
@peterschrammel This should work without enum-range-check being enabled as it's more generally useful though. Or do you mean just have it as something that happens during goto check? Because I feel that'd be slightly confusing as it's not a check.
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.
@peterschrammel It would be nice to be able to reuse the same check logic for both kinds of check. However we don't think it fits into goto_check
because it is replacing an existing expression rather than adding new checks. Would you be OK with us adding it in src/goto-programs/builtin_functions.cpp
, function goto_convertt::do_function_call_symbol
? This seems to be the place where the other built in functions are implemented / replaced.
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.
builtin_functions
is certainly a better place than c_typecheck_expr
.
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.
@peterschrammel This is addressed now.
Codecov Report
@@ Coverage Diff @@
## develop #5808 +/- ##
===========================================
+ Coverage 69.54% 73.60% +4.05%
===========================================
Files 1243 1491 +248
Lines 100701 179270 +78569
===========================================
+ Hits 70037 131958 +61921
- Misses 30664 47312 +16648
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
src/ansi-c/c_typecheck_expr.cpp
Outdated
<< expr.arguments().size() << " were provided"; | ||
throw invalid_source_file_exceptiont{error_message.str()}; | ||
} | ||
// Replace expr with giant boolean that checks all the values |
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.
I think this is great work, and would just ask for
- @peterschrammel's comment to be addressed and
- the commit pile to be cleaned up.
src/ansi-c/c_typecheck_expr.cpp
Outdated
std::vector<exprt> disjuncts; | ||
for(const auto &enum_value : enum_values) | ||
{ | ||
const constant_exprt val{enum_value.get_value(), *c_enum_tag_type}; |
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.
Rather than making this const
I'd even opt to std::move
it in the construction of the equal_exprt
below.
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.
Done
src/ansi-c/c_typecheck_expr.cpp
Outdated
error().source_location = expr.source_location(); | ||
error() << identifier << " expects one argument, " | ||
<< expr.arguments().size() << " were provided" << eom; | ||
throw 0; |
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.
Is it worth adding a test also covering this error condition? You've got a bunch of tests already, adding one more would help tick another box :-)
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.
Done
doc/cprover-manual/api.md
Outdated
|
||
The function **\_\_CPROVER\_enum\_is\_in\_range** returns true | ||
if their one argument (an `enum`) is one of the defined values for | ||
the enumeration. For example |
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 would probably rewrite that as
in this example,
__CPROVER_enum_is_in_range(ev1)
would return true, while in that example...
Because otherwise we have a block of code and it's a bit unclear 'what' returns true/false. Otherwise, maybe just say something like
In this example the assertion will pass
while in this example it will fail.
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.
Improved 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.
I think the code is mostly OK, withholding approval for commit / error handling cleanup.
src/ansi-c/c_typecheck_expr.cpp
Outdated
<< expr.arguments().size() << " were provided"; | ||
throw invalid_source_file_exceptiont{error_message.str()}; | ||
} | ||
// Replace expr with giant boolean that checks all the values |
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.
@peterschrammel Could you elaborate where exactly you'd like this to happen?
src/ansi-c/c_typecheck_expr.cpp
Outdated
{ | ||
// Can't enum range check a non-enum | ||
error().source_location = expr.source_location(); | ||
error() << identifier << " expects enum argument type" << 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.
Again, would be better if the error message also stated what we got instead of what it expected.
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.
@tautschnig We seem to be disagreeing on the error handling. To avoid us giving conflicting advice in the future, we should probably come to an agreement on what we do with error handling. My view was that we should avoid adding additional unstructured exceptions/remove unstructured exceptions when we come across them. Do you disagree with that on principle, or do you only have a problem with it in this particular case? |
My apologies if I caused confusion here. I'm all for structured exceptions in general, except the C front-end does things differently (but is consistent in doing it in such a way). I'd thus ask for this to remain consistent, not ruling out that we change this at a later point - but then we should make a wholesale change. Edit: to be very explicit: my comment is just about this particular case. |
To also explain at a more technical level why the C front-end (rightly) does |
1f8f445
to
8412bdf
Compare
const c_enum_typet &c_enum_type = ns.follow_tag(*enum_type); | ||
const c_enum_typet::memberst enum_values = c_enum_type.members(); | ||
|
||
std::vector<exprt> disjuncts; |
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::vector<exprt> disjuncts; | |
exprt::operandst disjuncts; |
@@ -589,6 +589,11 @@ class goto_convertt:public messaget | |||
exprt get_constant(const exprt &expr); | |||
|
|||
// some built-in functions | |||
void do_enum_is_in_range( |
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.
⛏️ Some docs would be appreciated.
64124d7
to
955fd76
Compare
@hannes-steffenhagen-diffblue Can you please (re)review this PR now it's been redone taking into account above comments (and also tidied up). |
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 would ask for the changes I've suggested to be addressed, but no longer have blocking concerns. Also, please add Fixes: #5473
to one of the commit messages.
const auto disjunction = ::disjunction(disjuncts); | ||
|
||
code_assignt assignment(lhs, disjunction); |
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 picking: I'd do code_assignt assignment{lhs, disjunction(disjuncts)};
to avoid constructing (and then deleting) a temporary.
/// | ||
/// Note that the check for the range of values is done by creating a | ||
/// disjunction comparing the expression with each possible valid value. | ||
/// \param lhs The destination for the generated assignment. |
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 picking: I believe the coding guidelines suggest to use a colon after the parameter's name.
^\[main.assertion.1\] line \d+ assertion __CPROVER_enum_is_in_range\(i\): FAILURE$ | ||
^\*\*\*\* WARNING: no body for function __CPROVER_enum_is_in_range$ | ||
-- | ||
This test the type checking of argument for __CPROVER_num_is_in_range |
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.
s/_num_/_enum_/
^\[main.assertion.1\] line \d+ assertion __CPROVER_enum_is_in_range\(.*\): FAILURE$ | ||
^\*\*\*\* WARNING: no body for function __CPROVER_enum_is_in_range$ | ||
-- | ||
This test the type checking of argument for __CPROVER_num_is_in_range |
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.
s/_num_/_enum_
|
||
^EXIT=6$ | ||
^SIGNAL=0$ | ||
^file enum_test10.c line \d+ function main: __CPROVER_enum_is_in_range expects enum, but \(i\) has type `signed int`$ |
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 test ought to live in the regression/ansi-c/
folder, I'd say. This will require modifying EXIT=6
to EXIT=(1|64)
|
||
^EXIT=6$ | ||
^SIGNAL=0$ | ||
^file enum_test12.c line \d+ function main: __CPROVER_enum_is_in_range takes exactly 1 argument, but 2 were 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.
Should also go in regression/ansi-c/
.
This adds a new builtin function that allows user to explicitly check that an enum has a valid value (and also this function in an assert).
955fd76
to
b953457
Compare
Adds regression tests for enum_is_in_range to test positive, negative and error outcomes on both named and typedef'd enumerations.
Adds documentation of how to use the CPROVER_enum_is_in_range function to explain to a user how to use this function.
b953457
to
baab734
Compare