r359964 - [c++20] Implement tweaked __VA_OPT__ rules from P1042R1:
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Fri May 3 23:46:18 PDT 2019
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>
More information about the cfe-commits
mailing list