[cfe-commits] [PATCH] GCC Preprocessor Assertions
Andrew Craik
andrew.craik at oracle.com
Sat Dec 3 14:33:20 PST 2011
Hi Doug,
Thanks for the detailed feedback on the code style and control flow; all
your comments seem very reasonable and I will go and rework the patch. I
will see if I can add another patch to do the
serialization/de-serialization.
Andrew
On 04-Dec-11 06:32, Douglas Gregor wrote:
> On Dec 1, 2011, at 11:46 PM, Andrew Craik wrote:
>
>> Hello,
>>
>> At the LLVM Developers' Meeting a couple of weeks ago I mentioned that I had a patch to add support to the pre-processor to handle GCC-style pre-processor assertions.
>> Please find attached two patches:
>> pp_assert_impl.patch - this adds handling of assertions to the pre-processor
>> pp_assert_default.patch - this adds default cpu and system assertions to Targets.cpp
>>
>> I look forward to hearing from the community and hopefully getting these patches integrated.
>
> Thanks. A bunch of comments below, but generally things are look very good!
>
> Starting with pp_assert_impl.patch:
>
> Index: clang/include/clang/Lex/Preprocessor.h
> ===================================================================
> --- clang.orig/include/clang/Lex/Preprocessor.h 2011-11-30 11:39:19.000000000 +1000
> +++ clang/include/clang/Lex/Preprocessor.h 2011-12-02 12:23:53.246940900 +1000
> @@ -15,6 +15,7 @@
> #define LLVM_CLANG_LEX_PREPROCESSOR_H
>
> #include "clang/Lex/MacroInfo.h"
> +#include "clang/Lex/AssertionInfo.h"
> #include "clang/Lex/Lexer.h"
> #include "clang/Lex/PTHLexer.h"
> #include "clang/Lex/PPCallbacks.h"
> @@ -240,6 +241,10 @@
> /// to the actual definition of the macro.
> llvm::DenseMap<IdentifierInfo*, MacroInfo*> Macros;
>
> + /// Assertions - For each IdentifierInfo that has appeared as an asserted
> + /// predicate, we store the values for which it was asserted
> + llvm::DenseMap<IdentifierInfo*, std::vector<AssertionInfo*> > AssertedValues;
>
> I'm guessing that the common case is that there is only one asserted value for each assertion, so I suggest replacing the std::vector with a UsuallyTinyPtrVector (which will need to be boosted from AST to Basic).
>
> @@ -440,6 +459,30 @@
> macro_iterator macro_begin(bool IncludeExternalMacros = true) const;
> macro_iterator macro_end(bool IncludeExternalMacros = true) const;
>
> + /// AddAssertionValue - assert the specified identifier to the
> + /// set of asserted values for the given assertion key
> + void AddAssertionValue(IdentifierInfo *II, AssertionInfo *AI);
> +
> + /// IsAsserted - test if the specified identifier has been asserted as a
> + /// value of the given assertion key returns true iff it has
> + bool IsAsserted(IdentifierInfo *II, AssertionInfo *AI);
> +
> + /// RemoveAssertionValue - remove the specified identifier from the set of
> + /// asserted values for the given assertion key
> + void RemoveAssertionValue(IdentifierInfo *II, AssertionInfo *AI);
> +
> + /// RemoveAssertion - remove the specified assertion and all of its asserted
> + /// values
> + void RemoveAssertion(IdentifierInfo *II);
>
> A quick request hereā¦ the LLVM coding standards clarified the naming of functions a few months ago (http://llvm.org/docs/CodingStandards.html#ll_naming), so please name these addAssertionValue, isAsserted, and so on.
> The same comment applies throughout the patch, because we want new code to follow this convention and existing code to eventually migrate over.
>
>
> + void Destory() {
> + ValueTokens.clear();
> + this->~AssertionInfo();
> + }
>
> Typo "Destory", propagated throughout.
>
> +/// ReadAssertionPredicate - Lex and validate an assertion predicate, which
> +/// occurs after an #assert or #unassert. This sets the token kind to eod and
> +/// discards the rest of the line if the assert is invalid.
> +/// isAssertUnassert is 1 if this is due to an #assert, 2 if #unassert
> +/// directive, 0 if it is something else
> +void Preprocessor::ReadAssertionPredicate(Token&AssertionPredTok,
> + char isAssertUnassert) {
>
> Please use an enumeration type rather than 0/1/2 here.
>
> Also, why not indicate failure by returning 'true' and success by returning 'false', as we do elsewhere in Clang?
>
> + IdentifierInfo *II = AssertionPredTok.getIdentifierInfo();
> + if (II == 0) {
> + bool Invalid = false;
> + std::string Spelling = getSpelling(AssertionPredTok,&Invalid);
> + if (Invalid) {
> + return;
> + }
> +
> + const IdentifierInfo&Info = Identifiers.get(Spelling);
> + if (Info.isCPlusPlusOperatorKeyword()) {
> + Diag(AssertionPredTok, diag::err_pp_operator_used_as_assert_predicate)
> +<< Spelling;
> + } else {
> + Diag(AssertionPredTok, diag::err_pp_predicate_not_identifier);
> + }
> + // Fall through on error.
> + } else if (isAssertUnassert&& II->getPPKeywordID() == tok::pp_assert) {
> + // Error if using assert as a predicate since using it would require
> + // #assert
> + Diag(AssertionPredTok, diag::err_assert_predicate_name);
> + } else {
> + // Okay, we got a good identifier node. Return it.
> + return;
> + }
>
> If you did switch the return value to 'bool', this code could be de-nested and clarified a bit:
>
> if (IdentifierInfo *II = AssertionPredTok.getIdentifierInfo()) {
> if (isAssertUnassert&& II->getPPKeywordID() == tok::pp_assert)
> return Diag(AssertionPredTok, diag::err_assert_predicate_name);
>
> return false;
> }
>
> // diagnose non-identifiers.
>
> +//===----------------------------------------------------------------------===//
> +// Preprocessor Assertion Directive Handling.
> +//===----------------------------------------------------------------------===//
> +void Preprocessor::HandleAssertDirective(Token&DTok) {
> + Token AssertionPredicateTok;
> + ReadAssertionPredicate(AssertionPredicateTok, 1);
> +
> + if (AssertionPredicateTok.is(tok::eod)) {
> + return;
> + }
> +
> + AssertionInfo *AI =
> + AllocateAssertionInfo(AssertionPredicateTok.getLocation());
> + Token Tok;
> + LexUnexpandedToken(Tok);
> + ReadAssertionValue(Tok, AI);
> +
> + // add to AI
> + if (AI != 0) {
>
> I find this interface to ReadAssertionValue to be a bit odd. How about having ReadAssertionValue lex all of the tokens it needs to, then return an allocated AssertionInfo* on success or null to indicate failure? It would simplify control flow for its callers.
>
> +void Preprocessor::HandleUnassertDirective(Token&DTok) {
> + Token AssertionPredicateTok;
> + ReadAssertionPredicate(AssertionPredicateTok, 2);
> +
> + // check to see if we read eod which indicates an error has occurred and
> + // an appropriate diagnostic has already been generated
> + if (AssertionPredicateTok.is(tok::eod)) {
> + return;
> + }
> +
> + Token Tok;
> + LexUnexpandedToken(Tok);
> +
> + IdentifierInfo *II = AssertionPredicateTok.getIdentifierInfo();
> +
> + // we have a valid identifier so we need to create an AssertionInfo to hold
> + // the value to associate with the predicate
> + assertion_iterator iter = AssertedValues.find(II);
> +
> + // we don't have an entry in the assertion table for the predicate, this is a
> + // noop so escape early
> + if (iter == assertion_end()) {
> + return;
> + }
>
> Shouldn't we still parse the rest of the #unassert to make sure it's well-formed? Or, at the very least, skip to the end of the directive?
>
> +void Preprocessor::ReadAssertionValue(Token&Tok, AssertionInfo *AI) {
> + if (Tok.isNot(tok::l_paren)) {
> + // emit diag
> + ReleaseAssertionInfo(AI);
> + AI = 0;
> + return DiscardUntilEndOfDirective();
> + }
>
> You're missing the diagnostic here.
>
> + while (true) {
> + LexUnexpandedToken(Tok);
> + if (Tok.is(tok::r_paren) || Tok.is(tok::eod)) {
> + break;
> + }
> + AI->AddToken(Tok);
> + }
>
> Could you add a comment saying that it's intentional that parentheses don't nest?It surprised me, but I see that's the documented behavior, and I don't want someone coming through later and "fixing" it.
>
> + // check for the closing ), if we don't have it relase the AssetionInfo and
> + // discard the rest of the directive
> + if (!Tok.is(tok::r_paren)) {
> + ReleaseAssertionInfo(AI);
> + AI = 0;
> + return DiscardUntilEndOfDirective();
> + }
>
> Comment typo "relase". Also, shouldn't we emit a diagnostic here?
>
> + // check there is nothing after the closing ), if we have anything release
> + // the Assertioninfo and idscard the rest of the directive
> + LexUnexpandedToken(Tok);
> + if (!Tok.is(tok::eod)) {
> + ReleaseAssertionInfo(AI);
> + AI = 0;
> + return DiscardUntilEndOfDirective();
> + }
>
> Diagnostic here?
>
> + while (true) {
> + PP.LexUnexpandedToken(PeekTok);
> + if (PeekTok.is(tok::r_paren) || PeekTok.is(tok::eod)) {
> + break;
> + }
> + AI->AddToken(PeekTok);
> + }
>
> Same request for a comment about non-nesting parens.
>
> + // check for the closing ), if we don't have it relase the AssertionInfo
> + // and discard the rest of the directive
> + if (!PeekTok.is(tok::r_paren)) {
> + PP.Diag(PredicateTok, diag::err_pp_expr_bad_token_start_expr);
> + PP.ReleaseAssertionInfo(AI);
> + AI = 0;
> + return true;
> + }
>
> "relase" typo in comment.
>
> This patch is looking really good. One non-trivial piece that's still missing is that the AssertedValues map will need to be serialized to the AST file and (lazily) deserialized, so that this feature works with precompiled headers.
>
> pp_assert_default.patch looks good to me.
>
> Thanks for working on this!
>
> - Doug
More information about the cfe-commits
mailing list