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 #
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
unsigned StringifyBefore : 1;
unsigned CharifyBefore : 1;
-
+ unsigned BeginsWithPlaceholder : 1;
+ unsigned EndsWithPlaceholder : 1;
bool hasStringifyBefore() const {
assert(!isReset() &&
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);
}
LeadingSpaceForStringifiedToken = false;
StringifyBefore = false;
CharifyBefore = false;
+ BeginsWithPlaceholder = false;
+ EndsWithPlaceholder = false;
}
const Token &getEOFTok() const { return SyntheticEOFToken; }
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() &&
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
// 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);
// 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;
}
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 '##'
} 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
!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");
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;
}
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.
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,
#define G(a,...) __VA_OPT__(B a) ## 1\r
26: F(,1)\r
26_1: G(,1)\r
-// CHECK: 26: B1\r
-// CHECK: 26_1: B1\r
+// CHECK: 26: B 1\r
+// CHECK: 26_1: B 1\r
#undef F\r
#undef G\r
\r
27: F(,1)\r
27_1: F(A0,1)\r
28: G(,1)\r
-// CHECK: 27: B11\r
+// CHECK: 27: B 11\r
// CHECK: 27_1: BexpandedA0 11\r
-// CHECK: 28: B11\r
+// CHECK: 28: B 11\r
\r
#undef F\r
#undef G\r
--- /dev/null
+ RUN: %clang_cc1 -E %s -pedantic -std=c++2a | FileCheck -strict-whitespace %s\r
+\r
+#define LPAREN() (\r
+#define G(Q) 42\r
+#define F1(R, X, ...) __VA_OPT__(G R X) )\r
+1: int x = F1(LPAREN(), 0, <:-);\r
+// CHECK: 1: int x = 42;\r
+\r
+#define F2(...) f(0 __VA_OPT__(,) __VA_ARGS__)\r
+#define EMP\r
+2: F2(EMP)\r
+// CHECK: 2: f(0 )\r
+\r
+#define H3(X, ...) #__VA_OPT__(X##X X##X)\r
+3: H3(, 0)\r
+// CHECK: 3: ""\r
+\r
+#define H4(X, ...) __VA_OPT__(a X ## X) ## b\r
+4: H4(, 1)\r
+// CHECK: 4: a b\r
+\r
+#define H4B(X, ...) a ## __VA_OPT__(X ## X b)\r
+4B: H4B(, 1)\r
+// CHECK: 4B: a b\r
+\r
+#define H5A(...) __VA_OPT__()/**/__VA_OPT__()\r
+#define H5B(X) a ## X ## b\r
+#define H5C(X) H5B(X)\r
+5: H5C(H5A())\r
+// CHECK: 5: ab\r
</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>