-
Notifications
You must be signed in to change notification settings - Fork 117
c-parser: add support for _Static_assert statements #917
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
base: master
Are you sure you want to change the base?
Conversation
The kernel AST now produces statements like:
##StaticAssertion: long_is_32bits
StaticAssertion [
"long_is_32bits"
BinOp [
"=="
SizeofTy [
Unsigned [
"long"
],
],
Constant [
NUMCONST [
"4"
""
"DEC"
],
],
],
],
This should allow the horrible array size hack to be removed from
the kernel source code, i.e. [include/assert.h](
https://github.com/seL4/seL4/blob/13.0.0/include/assert.h#L45-L69).
Signed-off-by: julia <midnight@trainwit.ch>
|
Hm, I'm not sure about this one. On the one hand it'd be nice to not just fail on For one, it immediately throws up the question that you hit what to do with those assertions. Do we prove them? Do we evaluate them in the parser? Do we error on the parse because a static assertion failed? Compilation is quick, translating seL4 into Isabelle is not quick, so failing can become extremely annoying. I guess the most consistent approach would be to allow the parse, and then ignore the assert. If I understand the PR correctly, this is what it is currently doing (parse as declaration, but not adding any types). (Also, I'm impressed that you managed to add this without too much trouble, people seem to be very afraid of touching this code in general :)) @Xaphiosis @corlewis What are your opinions? |
| (* nothing to be done *) | ||
| (* XXX: like process_enumdecl, use const_eval on the condition to check it? *) | ||
| | StaticAssertion {...} => (puredecl d, e) |
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 guess this is the main question we have to decide on. I think we should stay with the "do nothing", but I'm keen to hear arguments for/against.
| _Static_assert(2 + 3 == 5, "Hello"); | ||
|
|
||
| // struct hello { | ||
| // _Static_assert(4 + 4 == 8, "Goodbye"); | ||
| // } |
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'd like to see a few more tests, since the parser is correctness critical for architectures without binary verification.
If this can happen anywhere where we can have a declaration, we should at least add one inside a function body. Anything else that would make sense?
We're generally not so good with negative tests for the parser, but it would be worth adding a few to the testfiles/errors directory for this one. E.g. one where the _Static_assert has content that does not parse (in either of the arguments, maybe also 0, 1, and > 2 arguments), and one where _Static_assert as a whole occurs in a position it is not supposed to occur.
| context static_assert | ||
| begin | ||
|
|
||
| end | ||
| 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.
If we're adding a function body to the test, then we can add a thm f_body_def statement here to check that the function body has been defined. In this case it even make sense to state a lemma that shows that it is the expected function body so that the test confirms that the assert has been ignored (with a comment that this is what it does).
|
If we add this, we should also add a remark to |
Does clang support the same attribute the same way? Our own requirements are GCC first, clang later, but there are other people using the parser mostly on clang code. We don't have much attribute support currently, but if we add more that might become relevant. |
Yeah, it does (or at least recent-ish clang do); the API is basically that of the C11 offsetof header. Broadly speaking, yeah I agree with your concerns. I mostly did this for fun, but it would be nice to bring in more of the used subset into the parser; this is the only use of CONFIG_VERIFICATION_BUILD in seL4's c code itself (iirc) And I think there's an open issue regarding what to support on the parser. we have quirks like tb SEL4_LONG_ENUM which aren't technically c99 compatible, but there's some things regarding "do the compilers support it anyway" that was discussed. But I don't think that was resolved. |
| (* constant-expression is modelled as rexpression (c.f. "enumerator") *) | ||
| static_assert_declaration | ||
| : STATIC_ASSERT LPAREN rexpression YCOMMA cstring_literal RPAREN YSEMI | ||
| (wrap(StaticAssertion { condition = rexpression, message = cstring_literal }, eleft rexpression, eright rexpression)) |
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 just realised that I forgot to go back and change this, the span for this should probably be STATIC_ASSERTleft and RPARENright not eleft/eright of the rexpression.
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.
You're quoting from a C standard; you should say which one, otherwise people will expect C99.
Also, lines too long; this file tries to be 80 wide modulo minor spillage.
|
Impressive you felt like doing this, and even more so that you got something to work. Gerwin's covered the main areas, especially tests. Nitpicks: when dealing with other code bases, try to blend in with comment style etc, and if we're going to keep this and the tests, it would be good if they were more targeted/informative (e.g. "False assertion in global scope" / "Commented out" instead of "Hello"). The main thing to discuss is what this gives us. We'd be including a part of C11 into the C parser, arguing that in C99 gcc/clang implement it already, and doing so most likely so that verification can ignore it. The benefit is that an seL4 include file won't need the funky We have had (sometimes fiery) discussions about the C parser supporting a subset of C99 vs becoming some kind of Frankenstein's monster, with C99 usually winning. We do have the When it comes to what do we do with the assertions, I'm on the side of discarding them in verification. The "ideal" thing to have is the C parser checking them during a parse (also so that In summary, I'm reluctantly ok with this if it's gated behind an option and we don't need to prove or step over function-scope static asserts, provided it's properly documented+tested. For offsetof it's going to be the same story (sanctifying an implementation-defined macro with a specific definition not in the standard). The C parser should have all the type info, but I'd want to make sure it's quite stable, e.g. that it doesn't barf on a forward-declared struct etc, and we'd need to put in a proof or two to show it actually calculates the correct result. |
| sdecls) | ||
| end) | ||
| (* per the C11 spec this should be allowed but let's not for simplicity *) | ||
| (* | static_assert_declaration () *) |
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.
What is the issue here? If we're putting functionality in, but not putting in all of it, would be good to know why.
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.
Nothing really, and it's probably pretty easy to do.
I was leaving it out because the kernel doesn't use _Static_assert in these positions (i.e. inside a struct) so I didn't see the point (I only learned that this was even possible reading the C spec last year or so, it's not commonly known either).
The kernel AST now produces statements like:
This should allow the horrible array size hack to be removed from the kernel source code, i.e. include/assert.h.
Was this necessary? No
Was this interesting? Yes
Was this fun? Uhhh well I want to do more of it
It doesn't seem like any of the kernel proofs actually proved anything against the static asserts, so this should be fine. (I ran the proofs locally after making all the static assertions false and they still succeeded, so I think only the compiler checks these).
I've no idea how to write an appropriate test, aside from that it parses.
I also have a similar one for offsetof, but I don't want to trigger the CI builds unnecessarily by making another PR (for the moment, at least).