Author: rsmith Date: Fri May 3 23:46:18 2019 New Revision: 359964 URL: http://llvm.org/viewvc/llvm-project?rev=359964&view=rev Log: [c++20] Implement tweaked __VA_OPT__ rules from P1042R1:
* __VA_OPT__ is expanded if the *expanded* __VA_ARGS__ is non-empty, not if the original argument contained no tokens. * Placemarkers at the start and end of __VA_OPT__ are retained just long enough to paste them with adjacent ## operators. We never paste "across" a discarded placemarker. Added: cfe/trunk/test/Preprocessor/macro_vaopt_p1042r1.cpp Modified: cfe/trunk/include/clang/Lex/MacroArgs.h cfe/trunk/include/clang/Lex/VariadicMacroSupport.h cfe/trunk/lib/Lex/MacroArgs.cpp cfe/trunk/lib/Lex/TokenLexer.cpp cfe/trunk/test/Preprocessor/macro_vaopt_expand.cpp cfe/trunk/www/cxx_status.html Modified: cfe/trunk/include/clang/Lex/MacroArgs.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/MacroArgs.h?rev=359964&r1=359963&r2=359964&view=diff ============================================================================== --- cfe/trunk/include/clang/Lex/MacroArgs.h (original) +++ cfe/trunk/include/clang/Lex/MacroArgs.h Fri May 3 23:46:18 2019 @@ -112,18 +112,19 @@ public: bool isVarargsElidedUse() const { return VarargsElided; } /// Returns true if the macro was defined with a variadic (ellipsis) parameter - /// AND was invoked with at least one token supplied as a variadic argument. + /// AND was invoked with at least one token supplied as a variadic argument + /// (after pre-expansion). /// /// \code /// #define F(a) a /// #define V(a, ...) __VA_OPT__(a) - /// F() <-- returns false on this invocation. - /// V(,a) <-- returns true on this invocation. - /// V(,) <-- returns false on this invocation. + /// F() <-- returns false on this invocation. + /// V(,a) <-- returns true on this invocation. + /// V(,) <-- returns false on this invocation. + /// V(,F()) <-- returns false on this invocation. /// \endcode /// - - bool invokedWithVariadicArgument(const MacroInfo *const MI) const; + bool invokedWithVariadicArgument(const MacroInfo *const MI, Preprocessor &PP); /// StringifyArgument - Implement C99 6.10.3.2p2, converting a sequence of /// tokens into the literal string token that should be produced by the C # Modified: cfe/trunk/include/clang/Lex/VariadicMacroSupport.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/VariadicMacroSupport.h?rev=359964&r1=359963&r2=359964&view=diff ============================================================================== --- cfe/trunk/include/clang/Lex/VariadicMacroSupport.h (original) +++ cfe/trunk/include/clang/Lex/VariadicMacroSupport.h Fri May 3 23:46:18 2019 @@ -113,6 +113,8 @@ namespace clang { UnmatchedOpeningParens.push_back(LParenLoc); } + /// Are we at the top level within the __VA_OPT__? + bool isAtTopLevel() const { return UnmatchedOpeningParens.size() == 1; } }; /// A class for tracking whether we're inside a VA_OPT during a @@ -135,7 +137,8 @@ namespace clang { unsigned StringifyBefore : 1; unsigned CharifyBefore : 1; - + unsigned BeginsWithPlaceholder : 1; + unsigned EndsWithPlaceholder : 1; bool hasStringifyBefore() const { assert(!isReset() && @@ -151,7 +154,8 @@ namespace clang { public: VAOptExpansionContext(Preprocessor &PP) : VAOptDefinitionContext(PP), LeadingSpaceForStringifiedToken(false), - StringifyBefore(false), CharifyBefore(false) { + StringifyBefore(false), CharifyBefore(false), + BeginsWithPlaceholder(false), EndsWithPlaceholder(false) { SyntheticEOFToken.startToken(); SyntheticEOFToken.setKind(tok::eof); } @@ -162,6 +166,8 @@ namespace clang { LeadingSpaceForStringifiedToken = false; StringifyBefore = false; CharifyBefore = false; + BeginsWithPlaceholder = false; + EndsWithPlaceholder = false; } const Token &getEOFTok() const { return SyntheticEOFToken; } @@ -174,7 +180,23 @@ namespace clang { LeadingSpaceForStringifiedToken = HasLeadingSpace; } + void hasPlaceholderAfterHashhashAtStart() { BeginsWithPlaceholder = true; } + void hasPlaceholderBeforeRParen() { + if (isAtTopLevel()) + EndsWithPlaceholder = true; + } + + bool beginsWithPlaceholder() const { + assert(!isReset() && + "Must only be called if the state has not been reset"); + return BeginsWithPlaceholder; + } + bool endsWithPlaceholder() const { + assert(!isReset() && + "Must only be called if the state has not been reset"); + return EndsWithPlaceholder; + } bool hasCharifyBefore() const { assert(!isReset() && Modified: cfe/trunk/lib/Lex/MacroArgs.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/MacroArgs.cpp?rev=359964&r1=359963&r2=359964&view=diff ============================================================================== --- cfe/trunk/lib/Lex/MacroArgs.cpp (original) +++ cfe/trunk/lib/Lex/MacroArgs.cpp Fri May 3 23:46:18 2019 @@ -135,15 +135,12 @@ const Token *MacroArgs::getUnexpArgument return Result; } -// This function assumes that the variadic arguments are the tokens -// corresponding to the last parameter (ellipsis) - and since tokens are -// separated by the 'eof' token, if that is the only token corresponding to that -// last parameter, we know no variadic arguments were supplied. -bool MacroArgs::invokedWithVariadicArgument(const MacroInfo *const MI) const { +bool MacroArgs::invokedWithVariadicArgument(const MacroInfo *const MI, + Preprocessor &PP) { if (!MI->isVariadic()) return false; const int VariadicArgIndex = getNumMacroArguments() - 1; - return getUnexpArgument(VariadicArgIndex)->isNot(tok::eof); + return getPreExpArgument(VariadicArgIndex, PP).front().isNot(tok::eof); } /// ArgNeedsPreexpansion - If we can prove that the argument won't be affected Modified: cfe/trunk/lib/Lex/TokenLexer.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/TokenLexer.cpp?rev=359964&r1=359963&r2=359964&view=diff ============================================================================== --- cfe/trunk/lib/Lex/TokenLexer.cpp (original) +++ cfe/trunk/lib/Lex/TokenLexer.cpp Fri May 3 23:46:18 2019 @@ -243,8 +243,7 @@ void TokenLexer::ExpandFunctionArguments // we install the newly expanded sequence as the new 'Tokens' list. bool MadeChange = false; - const bool CalledWithVariadicArguments = - ActualArgs->invokedWithVariadicArgument(Macro); + Optional<bool> CalledWithVariadicArguments; VAOptExpansionContext VCtx(PP); @@ -291,7 +290,12 @@ void TokenLexer::ExpandFunctionArguments // this token. Note sawClosingParen() returns true only if the r_paren matches // the closing r_paren of the __VA_OPT__. if (!Tokens[I].is(tok::r_paren) || !VCtx.sawClosingParen()) { - if (!CalledWithVariadicArguments) { + // Lazily expand __VA_ARGS__ when we see the first __VA_OPT__. + if (!CalledWithVariadicArguments.hasValue()) { + CalledWithVariadicArguments = + ActualArgs->invokedWithVariadicArgument(Macro, PP); + } + if (!*CalledWithVariadicArguments) { // Skip this token. continue; } @@ -314,8 +318,8 @@ void TokenLexer::ExpandFunctionArguments stringifyVAOPTContents(ResultToks, VCtx, /*ClosingParenLoc*/ Tokens[I].getLocation()); - } else if (/*No tokens within VAOPT*/ !( - ResultToks.size() - VCtx.getNumberOfTokensPriorToVAOpt())) { + } else if (/*No tokens within VAOPT*/ + ResultToks.size() == VCtx.getNumberOfTokensPriorToVAOpt()) { // Treat VAOPT as a placemarker token. Eat either the '##' before the // RHS/VAOPT (if one exists, suggesting that the LHS (if any) to that // hashhash was not a placemarker) or the '##' @@ -326,6 +330,26 @@ void TokenLexer::ExpandFunctionArguments } else if ((I + 1 != E) && Tokens[I + 1].is(tok::hashhash)) { ++I; // Skip the following hashhash. } + } else { + // If there's a ## before the __VA_OPT__, we might have discovered + // that the __VA_OPT__ begins with a placeholder. We delay action on + // that to now to avoid messing up our stashed count of tokens before + // __VA_OPT__. + if (VCtx.beginsWithPlaceholder()) { + assert(VCtx.getNumberOfTokensPriorToVAOpt() > 0 && + ResultToks.size() >= VCtx.getNumberOfTokensPriorToVAOpt() && + ResultToks[VCtx.getNumberOfTokensPriorToVAOpt() - 1].is( + tok::hashhash) && + "no token paste before __VA_OPT__"); + ResultToks.erase(ResultToks.begin() + + VCtx.getNumberOfTokensPriorToVAOpt() - 1); + } + // If the expansion of __VA_OPT__ ends with a placeholder, eat any + // following '##' token. + if (VCtx.endsWithPlaceholder() && I + 1 != E && + Tokens[I + 1].is(tok::hashhash)) { + ++I; + } } VCtx.reset(); // We processed __VA_OPT__'s closing paren (and the exit out of @@ -386,6 +410,7 @@ void TokenLexer::ExpandFunctionArguments !ResultToks.empty() && ResultToks.back().is(tok::hashhash); bool PasteBefore = I != 0 && Tokens[I-1].is(tok::hashhash); bool PasteAfter = I+1 != E && Tokens[I+1].is(tok::hashhash); + bool RParenAfter = I+1 != E && Tokens[I+1].is(tok::r_paren); assert((!NonEmptyPasteBefore || PasteBefore || VCtx.isInVAOpt()) && "unexpected ## in ResultToks"); @@ -470,6 +495,18 @@ void TokenLexer::ExpandFunctionArguments NextTokGetsSpace); ResultToks[FirstResult].setFlagValue(Token::StartOfLine, false); NextTokGetsSpace = false; + } else { + // We're creating a placeholder token. Usually this doesn't matter, + // but it can affect paste behavior when at the start or end of a + // __VA_OPT__. + if (NonEmptyPasteBefore) { + // We're imagining a placeholder token is inserted here. If this is + // the first token in a __VA_OPT__ after a ##, delete the ##. + assert(VCtx.isInVAOpt() && "should only happen inside a __VA_OPT__"); + VCtx.hasPlaceholderAfterHashhashAtStart(); + } + if (RParenAfter) + VCtx.hasPlaceholderBeforeRParen(); } continue; } @@ -534,6 +571,9 @@ void TokenLexer::ExpandFunctionArguments continue; } + if (RParenAfter) + VCtx.hasPlaceholderBeforeRParen(); + // If this is on the RHS of a paste operator, we've already copied the // paste operator to the ResultToks list, unless the LHS was empty too. // Remove it. @@ -547,6 +587,8 @@ void TokenLexer::ExpandFunctionArguments if (!VCtx.isInVAOpt() || ResultToks.size() > VCtx.getNumberOfTokensPriorToVAOpt()) ResultToks.pop_back(); + else + VCtx.hasPlaceholderAfterHashhashAtStart(); } // If this is the __VA_ARGS__ token, and if the argument wasn't provided, Modified: cfe/trunk/test/Preprocessor/macro_vaopt_expand.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Preprocessor/macro_vaopt_expand.cpp?rev=359964&r1=359963&r2=359964&view=diff ============================================================================== --- cfe/trunk/test/Preprocessor/macro_vaopt_expand.cpp (original) +++ cfe/trunk/test/Preprocessor/macro_vaopt_expand.cpp Fri May 3 23:46:18 2019 @@ -129,8 +129,8 @@ #define G(a,...) __VA_OPT__(B a) ## 1 26: F(,1) 26_1: G(,1) -// CHECK: 26: B1 -// CHECK: 26_1: B1 +// CHECK: 26: B 1 +// CHECK: 26_1: B 1 #undef F #undef G @@ -140,9 +140,9 @@ 27: F(,1) 27_1: F(A0,1) 28: G(,1) -// CHECK: 27: B11 +// CHECK: 27: B 11 // CHECK: 27_1: BexpandedA0 11 -// CHECK: 28: B11 +// CHECK: 28: B 11 #undef F #undef G Added: cfe/trunk/test/Preprocessor/macro_vaopt_p1042r1.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Preprocessor/macro_vaopt_p1042r1.cpp?rev=359964&view=auto ============================================================================== --- cfe/trunk/test/Preprocessor/macro_vaopt_p1042r1.cpp (added) +++ cfe/trunk/test/Preprocessor/macro_vaopt_p1042r1.cpp Fri May 3 23:46:18 2019 @@ -0,0 +1,30 @@ + RUN: %clang_cc1 -E %s -pedantic -std=c++2a | FileCheck -strict-whitespace %s + +#define LPAREN() ( +#define G(Q) 42 +#define F1(R, X, ...) __VA_OPT__(G R X) ) +1: int x = F1(LPAREN(), 0, <:-); +// CHECK: 1: int x = 42; + +#define F2(...) f(0 __VA_OPT__(,) __VA_ARGS__) +#define EMP +2: F2(EMP) +// CHECK: 2: f(0 ) + +#define H3(X, ...) #__VA_OPT__(X##X X##X) +3: H3(, 0) +// CHECK: 3: "" + +#define H4(X, ...) __VA_OPT__(a X ## X) ## b +4: H4(, 1) +// CHECK: 4: a b + +#define H4B(X, ...) a ## __VA_OPT__(X ## X b) +4B: H4B(, 1) +// CHECK: 4B: a b + +#define H5A(...) __VA_OPT__()/**/__VA_OPT__() +#define H5B(X) a ## X ## b +#define H5C(X) H5B(X) +5: H5C(H5A()) +// CHECK: 5: ab Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=359964&r1=359963&r2=359964&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Fri May 3 23:46:18 2019 @@ -858,7 +858,7 @@ as the draft C++2a standard evolves. </tr> <tr> <!-- from Rapperswil --> <td><a href="http://wg21.link/p1042r1">P1042R1</a></td> - <td class="partial" align="center">Partial</td> + <td class="svn" align="center">SVN</td> </tr> <tr> <td>Designated initializers</td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits