diff options
-rw-r--r-- | clang/include/clang/Lex/Preprocessor.h | 40 | ||||
-rw-r--r-- | clang/lib/Lex/PPCaching.cpp | 51 | ||||
-rw-r--r-- | clang/lib/Lex/PPDirectives.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Lex/Pragma.cpp | 125 | ||||
-rw-r--r-- | clang/lib/Lex/Preprocessor.cpp | 3 | ||||
-rw-r--r-- | clang/test/Preprocessor/_Pragma-in-macro-arg.cpp | 22 |
6 files changed, 125 insertions, 122 deletions
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 6f7ab0b01fd..6ab8c5d899d 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -323,6 +323,14 @@ class Preprocessor { /// to avoid hitting the same error over and over again. bool HasReachedMaxIncludeDepth = false; + /// The number of currently-active calls to Lex. + /// + /// Lex is reentrant, and asking for an (end-of-phase-4) token can often + /// require asking for multiple additional tokens. This counter makes it + /// possible for Lex to detect whether it's producing a token for the end + /// of phase 4 of translation or for some other situation. + unsigned LexLevel = 0; + public: struct PreambleSkipInfo { SourceLocation HashTokenLoc; @@ -1244,24 +1252,6 @@ public: /// Disable the last EnableBacktrackAtThisPos call. void CommitBacktrackedTokens(); - struct CachedTokensRange { - CachedTokensTy::size_type Begin, End; - }; - -private: - /// A range of cached tokens that should be erased after lexing - /// when backtracking requires the erasure of such cached tokens. - Optional<CachedTokensRange> CachedTokenRangeToErase; - -public: - /// Returns the range of cached tokens that were lexed since - /// EnableBacktrackAtThisPos() was previously called. - CachedTokensRange LastCachedTokenRange(); - - /// Erase the range of cached tokens that were lexed since - /// EnableBacktrackAtThisPos() was previously called. - void EraseCachedTokens(CachedTokensRange TokenRange); - /// Make Preprocessor re-lex the tokens that were lexed since /// EnableBacktrackAtThisPos() was previously called. void Backtrack(); @@ -1353,6 +1343,7 @@ public: /// tokens after phase 5. As such, it is equivalent to using /// 'Lex', not 'LexUnexpandedToken'. const Token &LookAhead(unsigned N) { + assert(LexLevel == 0 && "cannot use lookahead while lexing"); if (CachedLexPos + N < CachedTokens.size()) return CachedTokens[CachedLexPos+N]; else @@ -1379,8 +1370,16 @@ public: /// If BackTrack() is called afterwards, the token will remain at the /// insertion point. void EnterToken(const Token &Tok) { - EnterCachingLexMode(); - CachedTokens.insert(CachedTokens.begin()+CachedLexPos, Tok); + if (LexLevel) { + // It's not correct in general to enter caching lex mode while in the + // middle of a nested lexing action. + auto TokCopy = llvm::make_unique<Token[]>(1); + TokCopy[0] = Tok; + EnterTokenStream(std::move(TokCopy), 1, true); + } else { + EnterCachingLexMode(); + CachedTokens.insert(CachedTokens.begin()+CachedLexPos, Tok); + } } /// We notify the Preprocessor that if it is caching tokens (because @@ -2062,6 +2061,7 @@ private: } void EnterCachingLexMode(); + void EnterCachingLexModeUnchecked(); void ExitCachingLexMode() { if (InCachingLexMode()) diff --git a/clang/lib/Lex/PPCaching.cpp b/clang/lib/Lex/PPCaching.cpp index 929d35ad51b..90238de8a48 100644 --- a/clang/lib/Lex/PPCaching.cpp +++ b/clang/lib/Lex/PPCaching.cpp @@ -23,6 +23,7 @@ using namespace clang; // be called multiple times and CommitBacktrackedTokens/Backtrack calls will // be combined with the EnableBacktrackAtThisPos calls in reverse order. void Preprocessor::EnableBacktrackAtThisPos() { + assert(LexLevel == 0 && "cannot use lookahead while lexing"); BacktrackPositions.push_back(CachedLexPos); EnterCachingLexMode(); } @@ -34,29 +35,6 @@ void Preprocessor::CommitBacktrackedTokens() { BacktrackPositions.pop_back(); } -Preprocessor::CachedTokensRange Preprocessor::LastCachedTokenRange() { - assert(isBacktrackEnabled()); - auto PrevCachedLexPos = BacktrackPositions.back(); - return CachedTokensRange{PrevCachedLexPos, CachedLexPos}; -} - -void Preprocessor::EraseCachedTokens(CachedTokensRange TokenRange) { - assert(TokenRange.Begin <= TokenRange.End); - if (CachedLexPos == TokenRange.Begin && TokenRange.Begin != TokenRange.End) { - // We have backtracked to the start of the token range as we want to consume - // them again. Erase the tokens only after consuming then. - assert(!CachedTokenRangeToErase); - CachedTokenRangeToErase = TokenRange; - return; - } - // The cached tokens were committed, so they should be erased now. - assert(TokenRange.End == CachedLexPos); - CachedTokens.erase(CachedTokens.begin() + TokenRange.Begin, - CachedTokens.begin() + TokenRange.End); - CachedLexPos = TokenRange.Begin; - ExitCachingLexMode(); -} - // Make Preprocessor re-lex the tokens that were lexed since // EnableBacktrackAtThisPos() was previously called. void Preprocessor::Backtrack() { @@ -71,15 +49,12 @@ void Preprocessor::CachingLex(Token &Result) { if (!InCachingLexMode()) return; + // The assert in EnterCachingLexMode should prevent this from happening. + assert(LexLevel == 1 && + "should not use token caching within the preprocessor"); + if (CachedLexPos < CachedTokens.size()) { Result = CachedTokens[CachedLexPos++]; - // Erase the some of the cached tokens after they are consumed when - // asked to do so. - if (CachedTokenRangeToErase && - CachedTokenRangeToErase->End == CachedLexPos) { - EraseCachedTokens(*CachedTokenRangeToErase); - CachedTokenRangeToErase = None; - } return; } @@ -88,14 +63,14 @@ void Preprocessor::CachingLex(Token &Result) { if (isBacktrackEnabled()) { // Cache the lexed token. - EnterCachingLexMode(); + EnterCachingLexModeUnchecked(); CachedTokens.push_back(Result); ++CachedLexPos; return; } if (CachedLexPos < CachedTokens.size()) { - EnterCachingLexMode(); + EnterCachingLexModeUnchecked(); } else { // All cached tokens were consumed. CachedTokens.clear(); @@ -104,11 +79,23 @@ void Preprocessor::CachingLex(Token &Result) { } void Preprocessor::EnterCachingLexMode() { + // The caching layer sits on top of all the other lexers, so it's incorrect + // to cache tokens while inside a nested lex action. The cached tokens would + // be retained after returning to the enclosing lex action and, at best, + // would appear at the wrong position in the token stream. + assert(LexLevel == 0 && + "entered caching lex mode while lexing something else"); + if (InCachingLexMode()) { assert(CurLexerKind == CLK_CachingLexer && "Unexpected lexer kind"); return; } + EnterCachingLexModeUnchecked(); +} + +void Preprocessor::EnterCachingLexModeUnchecked() { + assert(CurLexerKind != CLK_CachingLexer && "already in caching lex mode"); PushIncludeMacroStack(); CurLexerKind = CLK_CachingLexer; } diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 3aae6ae245f..216ae39632d 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -829,10 +829,10 @@ void Preprocessor::HandleSkippedDirectiveWhileUsingPCH(Token &Result, return HandleIncludeDirective(HashLoc, Result); } if (SkippingUntilPragmaHdrStop && II->getPPKeywordID() == tok::pp_pragma) { - Token P = LookAhead(0); - auto *II = P.getIdentifierInfo(); + Lex(Result); + auto *II = Result.getIdentifierInfo(); if (II && II->getName() == "hdrstop") - return HandlePragmaDirective(HashLoc, PIK_HashPragma); + return HandlePragmaHdrstop(Result); } } DiscardUntilEndOfDirective(); diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 286b863b354..1c87f3ecc09 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -144,84 +144,72 @@ void Preprocessor::HandlePragmaDirective(SourceLocation IntroducerLoc, DiscardUntilEndOfDirective(); } -namespace { - -/// Helper class for \see Preprocessor::Handle_Pragma. -class LexingFor_PragmaRAII { - Preprocessor &PP; - bool InMacroArgPreExpansion; - bool Failed = false; - Token &OutTok; - Token PragmaTok; - -public: - LexingFor_PragmaRAII(Preprocessor &PP, bool InMacroArgPreExpansion, - Token &Tok) - : PP(PP), InMacroArgPreExpansion(InMacroArgPreExpansion), OutTok(Tok) { - if (InMacroArgPreExpansion) { - PragmaTok = OutTok; - PP.EnableBacktrackAtThisPos(); - } - } - - ~LexingFor_PragmaRAII() { - if (InMacroArgPreExpansion) { - // When committing/backtracking the cached pragma tokens in a macro - // argument pre-expansion we want to ensure that either the tokens which - // have been committed will be removed from the cache or that the tokens - // over which we just backtracked won't remain in the cache after they're - // consumed and that the caching will stop after consuming them. - // Otherwise the caching will interfere with the way macro expansion - // works, because we will continue to cache tokens after consuming the - // backtracked tokens, which shouldn't happen when we're dealing with - // macro argument pre-expansion. - auto CachedTokenRange = PP.LastCachedTokenRange(); - if (Failed) { - PP.CommitBacktrackedTokens(); - } else { - PP.Backtrack(); - OutTok = PragmaTok; - } - PP.EraseCachedTokens(CachedTokenRange); - } - } - - void failed() { - Failed = true; - } -}; - -} // namespace - /// Handle_Pragma - Read a _Pragma directive, slice it up, process it, then /// return the first token after the directive. The _Pragma token has just /// been read into 'Tok'. void Preprocessor::Handle_Pragma(Token &Tok) { - // This works differently if we are pre-expanding a macro argument. - // In that case we don't actually "activate" the pragma now, we only lex it - // until we are sure it is lexically correct and then we backtrack so that - // we activate the pragma whenever we encounter the tokens again in the token - // stream. This ensures that we will activate it in the correct location - // or that we will ignore it if it never enters the token stream, e.g: + // C11 6.10.3.4/3: + // all pragma unary operator expressions within [a completely + // macro-replaced preprocessing token sequence] are [...] processed [after + // rescanning is complete] // - // #define EMPTY(x) - // #define INACTIVE(x) EMPTY(x) - // INACTIVE(_Pragma("clang diagnostic ignored \"-Wconversion\"")) + // This means that we execute _Pragma operators in two cases: + // + // 1) on token sequences that would otherwise be produced as the output of + // phase 4 of preprocessing, and + // 2) on token sequences formed as the macro-replaced token sequence of a + // macro argument + // + // Case #2 appears to be a wording bug: only _Pragmas that would survive to + // the end of phase 4 should actually be executed. Discussion on the WG14 + // mailing list suggests that a _Pragma operator is notionally checked early, + // but only pragmas that survive to the end of phase 4 should be executed. + // + // In Case #2, we check the syntax now, but then put the tokens back into the + // token stream for later consumption. + + struct TokenCollector { + Preprocessor &Self; + bool Collect; + SmallVector<Token, 3> Tokens; + Token &Tok; + + void lex() { + if (Collect) + Tokens.push_back(Tok); + Self.Lex(Tok); + } + + void revert() { + assert(Collect && "did not collect tokens"); + assert(!Tokens.empty() && "collected unexpected number of tokens"); + + // Push the ( "string" ) tokens into the token stream. + auto Toks = llvm::make_unique<Token[]>(Tokens.size()); + std::copy(Tokens.begin() + 1, Tokens.end(), Toks.get()); + Toks[Tokens.size() - 1] = Tok; + Self.EnterTokenStream(std::move(Toks), Tokens.size(), + /*DisableMacroExpansion*/ true); + + // ... and return the _Pragma token unchanged. + Tok = *Tokens.begin(); + } + }; - LexingFor_PragmaRAII _PragmaLexing(*this, InMacroArgPreExpansion, Tok); + TokenCollector Toks = {*this, InMacroArgPreExpansion, {}, Tok}; // Remember the pragma token location. SourceLocation PragmaLoc = Tok.getLocation(); // Read the '('. - Lex(Tok); + Toks.lex(); if (Tok.isNot(tok::l_paren)) { Diag(PragmaLoc, diag::err__Pragma_malformed); - return _PragmaLexing.failed(); + return; } // Read the '"..."'. - Lex(Tok); + Toks.lex(); if (!tok::isStringLiteral(Tok.getKind())) { Diag(PragmaLoc, diag::err__Pragma_malformed); // Skip bad tokens, and the ')', if present. @@ -233,7 +221,7 @@ void Preprocessor::Handle_Pragma(Token &Tok) { Lex(Tok); if (Tok.is(tok::r_paren)) Lex(Tok); - return _PragmaLexing.failed(); + return; } if (Tok.hasUDSuffix()) { @@ -242,21 +230,24 @@ void Preprocessor::Handle_Pragma(Token &Tok) { Lex(Tok); if (Tok.is(tok::r_paren)) Lex(Tok); - return _PragmaLexing.failed(); + return; } // Remember the string. Token StrTok = Tok; // Read the ')'. - Lex(Tok); + Toks.lex(); if (Tok.isNot(tok::r_paren)) { Diag(PragmaLoc, diag::err__Pragma_malformed); - return _PragmaLexing.failed(); + return; } - if (InMacroArgPreExpansion) + // If we're expanding a macro argument, put the tokens back. + if (InMacroArgPreExpansion) { + Toks.revert(); return; + } SourceLocation RParenLoc = Tok.getLocation(); std::string StrVal = getSpelling(StrTok); diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index ff040a28d4b..d1c02c942da 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -862,6 +862,8 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) { } void Preprocessor::Lex(Token &Result) { + ++LexLevel; + // We loop here until a lex function returns a token; this avoids recursion. bool ReturnedToken; do { @@ -893,6 +895,7 @@ void Preprocessor::Lex(Token &Result) { } LastTokenWasAt = Result.is(tok::at); + --LexLevel; } /// Lex a header-name token (including one formed from header-name-tokens if diff --git a/clang/test/Preprocessor/_Pragma-in-macro-arg.cpp b/clang/test/Preprocessor/_Pragma-in-macro-arg.cpp new file mode 100644 index 00000000000..0d2dcd05124 --- /dev/null +++ b/clang/test/Preprocessor/_Pragma-in-macro-arg.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 %s -verify -Wconversion + +#define P(X) _Pragma(#X) +#define V(X) X + +#define X \ + P(clang diagnostic push) \ + P(clang diagnostic ignored "-Wconversion") \ + ) = 1.2; \ + P(clang diagnostic pop) + +void f() { + int a = 1.2; // expected-warning {{changes value}} + + // Note, we intentionally enter a tentatively-parsed context here to trigger + // regular use of lookahead. This would go wrong if _Pragma checking in macro + // argument pre-expansion also tries to use token lookahead. + int (b + V(X) + + int c = 1.2; // expected-warning {{changes value}} +} |