Skip to content

Commit 0436ec2

Browse files
committed
Permit __VA_OPT__ in all language modes and allow it to be detected with #ifdef.
These changes are intended to give code a path to move away from the GNU ,##__VA_ARGS__ extension, which is non-conforming in some situations and which we'd like to disable in our conforming mode in those cases.
1 parent 9f2c7ef commit 0436ec2

File tree

8 files changed

+78
-21
lines changed

8 files changed

+78
-21
lines changed

clang/include/clang/Lex/Preprocessor.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,25 @@ class Preprocessor {
447447
ElseLoc(ElseLoc) {}
448448
};
449449

450+
class IfdefMacroNameScopeRAII {
451+
Preprocessor &PP;
452+
bool VAOPTWasPoisoned;
453+
454+
public:
455+
IfdefMacroNameScopeRAII(Preprocessor &PP)
456+
: PP(PP), VAOPTWasPoisoned(PP.Ident__VA_OPT__->isPoisoned()) {
457+
PP.Ident__VA_OPT__->setIsPoisoned(false);
458+
}
459+
IfdefMacroNameScopeRAII(const IfdefMacroNameScopeRAII&) = delete;
460+
IfdefMacroNameScopeRAII &operator=(const IfdefMacroNameScopeRAII&) = delete;
461+
~IfdefMacroNameScopeRAII() { Exit(); }
462+
463+
void Exit() {
464+
if (VAOPTWasPoisoned)
465+
PP.Ident__VA_OPT__->setIsPoisoned(true);
466+
}
467+
};
468+
450469
private:
451470
friend class ASTReader;
452471
friend class MacroArgs;

clang/include/clang/Lex/VariadicMacroSupport.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,14 @@ namespace clang {
3939
assert(Ident__VA_ARGS__->isPoisoned() && "__VA_ARGS__ should be poisoned "
4040
"outside an ISO C/C++ variadic "
4141
"macro definition!");
42-
assert(
43-
!Ident__VA_OPT__ ||
44-
(Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!"));
42+
assert(Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!");
4543
}
4644

4745
/// Client code should call this function just before the Preprocessor is
4846
/// about to Lex tokens from the definition of a variadic (ISO C/C++) macro.
4947
void enterScope() {
5048
Ident__VA_ARGS__->setIsPoisoned(false);
51-
if (Ident__VA_OPT__)
52-
Ident__VA_OPT__->setIsPoisoned(false);
49+
Ident__VA_OPT__->setIsPoisoned(false);
5350
}
5451

5552
/// Client code should call this function as soon as the Preprocessor has
@@ -58,8 +55,7 @@ namespace clang {
5855
/// (might be explicitly called, and then reinvoked via the destructor).
5956
void exitScope() {
6057
Ident__VA_ARGS__->setIsPoisoned(true);
61-
if (Ident__VA_OPT__)
62-
Ident__VA_OPT__->setIsPoisoned(true);
58+
Ident__VA_OPT__->setIsPoisoned(true);
6359
}
6460

6561
~VariadicMacroScopeGuard() { exitScope(); }

clang/lib/Lex/PPDirectives.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2928,9 +2928,14 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
29282928
++NumIf;
29292929
Token DirectiveTok = Result;
29302930

2931+
// __VA_OPT__ is allowed as the operand of #if[n]def.
2932+
IfdefMacroNameScopeRAII IfdefMacroNameScope(*this);
2933+
29312934
Token MacroNameTok;
29322935
ReadMacroName(MacroNameTok);
29332936

2937+
IfdefMacroNameScope.Exit();
2938+
29342939
// Error reading macro name? If so, diagnostic already issued.
29352940
if (MacroNameTok.is(tok::eod)) {
29362941
// Skip code until we get to #endif. This helps with recovery by not

clang/lib/Lex/PPExpressions.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
104104
SourceLocation beginLoc(PeekTok.getLocation());
105105
Result.setBegin(beginLoc);
106106

107+
// __VA_OPT__ is allowed as the operand of 'defined'.
108+
Preprocessor::IfdefMacroNameScopeRAII IfdefMacroNameScope(PP);
109+
107110
// Get the next token, don't expand it.
108111
PP.LexUnexpandedNonComment(PeekTok);
109112

@@ -122,6 +125,8 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
122125
PP.LexUnexpandedNonComment(PeekTok);
123126
}
124127

128+
IfdefMacroNameScope.Exit();
129+
125130
// If we don't have a pp-identifier now, this is an error.
126131
if (PP.CheckMacroName(PeekTok, MU_Other))
127132
return true;

clang/lib/Lex/PPMacroExpansion.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,16 @@ void Preprocessor::dumpMacroInfo(const IdentifierInfo *II) {
323323

324324
/// RegisterBuiltinMacro - Register the specified identifier in the identifier
325325
/// table and mark it as a builtin macro to be expanded.
326-
static IdentifierInfo *RegisterBuiltinMacro(Preprocessor &PP, const char *Name){
326+
static IdentifierInfo *RegisterBuiltinMacro(Preprocessor &PP, const char *Name,
327+
bool Disabled = false) {
327328
// Get the identifier.
328329
IdentifierInfo *Id = PP.getIdentifierInfo(Name);
329330

330331
// Mark it as being a macro that is builtin.
331332
MacroInfo *MI = PP.AllocateMacroInfo(SourceLocation());
332333
MI->setIsBuiltinMacro();
334+
if (Disabled)
335+
MI->DisableMacro();
333336
PP.appendDefMacroDirective(Id, MI);
334337
return Id;
335338
}
@@ -343,6 +346,7 @@ void Preprocessor::RegisterBuiltinMacros() {
343346
Ident__TIME__ = RegisterBuiltinMacro(*this, "__TIME__");
344347
Ident__COUNTER__ = RegisterBuiltinMacro(*this, "__COUNTER__");
345348
Ident_Pragma = RegisterBuiltinMacro(*this, "_Pragma");
349+
Ident__VA_OPT__ = RegisterBuiltinMacro(*this, "__VA_OPT__", true);
346350

347351
// C++ Standing Document Extensions.
348352
if (getLangOpts().CPlusPlus)

clang/lib/Lex/Preprocessor.cpp

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -115,23 +115,20 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
115115

116116
BuiltinInfo = std::make_unique<Builtin::Context>();
117117

118-
// "Poison" __VA_ARGS__, __VA_OPT__ which can only appear in the expansion of
119-
// a macro. They get unpoisoned where it is allowed.
120-
(Ident__VA_ARGS__ = getIdentifierInfo("__VA_ARGS__"))->setIsPoisoned();
121-
SetPoisonReason(Ident__VA_ARGS__,diag::ext_pp_bad_vaargs_use);
122-
if (getLangOpts().CPlusPlus20) {
123-
(Ident__VA_OPT__ = getIdentifierInfo("__VA_OPT__"))->setIsPoisoned();
124-
SetPoisonReason(Ident__VA_OPT__,diag::ext_pp_bad_vaopt_use);
125-
} else {
126-
Ident__VA_OPT__ = nullptr;
127-
}
128-
129118
// Initialize the pragma handlers.
130119
RegisterBuiltinPragmas();
131120

132121
// Initialize builtin macros like __LINE__ and friends.
133122
RegisterBuiltinMacros();
134123

124+
// "Poison" __VA_ARGS__, __VA_OPT__ which can only appear in the expansion of
125+
// a macro. They get unpoisoned where it is allowed. Note that we model
126+
// __VA_OPT__ as a builtin macro to allow #ifdef and friends to detect it.
127+
(Ident__VA_ARGS__ = getIdentifierInfo("__VA_ARGS__"))->setIsPoisoned();
128+
SetPoisonReason(Ident__VA_ARGS__, diag::ext_pp_bad_vaargs_use);
129+
Ident__VA_OPT__->setIsPoisoned();
130+
SetPoisonReason(Ident__VA_OPT__, diag::ext_pp_bad_vaopt_use);
131+
135132
if(LangOpts.Borland) {
136133
Ident__exception_info = getIdentifierInfo("_exception_info");
137134
Ident___exception_info = getIdentifierInfo("__exception_info");

clang/test/Preprocessor/macro_vaopt_check.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -pedantic -std=c++2a
1+
// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -pedantic -std=c++20
2+
// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -pedantic -std=c++11
3+
// RUN: %clang_cc1 -x c %s -Eonly -verify -Wno-all -pedantic -std=c99
4+
5+
// Check that support for __VA_OPT__ can be detected by #ifdef.
6+
#ifndef __VA_OPT__
7+
#error should be defined
8+
#endif
9+
10+
#ifdef __VA_OPT__
11+
#else
12+
#error should be defined
13+
#endif
14+
15+
#if !defined(__VA_OPT__)
16+
#error should be defined
17+
#endif
218

319
//expected-error@+1{{missing '('}}
420
#define V1(...) __VA_OPT__
@@ -62,3 +78,16 @@
6278
#define V1(...) __VA_OPT__ ((())
6379
#undef V1
6480

81+
// __VA_OPT__ can't appear anywhere else.
82+
#if __VA_OPT__ // expected-warning {{__VA_OPT__ can only appear in the expansion of a variadic macro}}
83+
#endif
84+
85+
#define BAD __VA_OPT__ // expected-warning {{__VA_OPT__ can only appear in the expansion of a variadic macro}}
86+
87+
// Check defined(__VA_OPT__) doesn't leave __VA_OPT__ poisoned.
88+
#define Z(...) (0 __VA_OPT__(|| 1))
89+
#if defined(__VA_OPT__) && Z(hello)
90+
// OK
91+
#else
92+
#error bad
93+
#endif

clang/test/Preprocessor/macro_vaopt_expand.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// RUN: %clang_cc1 -E %s -pedantic -std=c++2a | FileCheck -strict-whitespace %s
1+
// RUN: %clang_cc1 -E %s -pedantic -std=c++20 | FileCheck -strict-whitespace %s
2+
// RUN: %clang_cc1 -E %s -pedantic -std=c++11 | FileCheck -strict-whitespace %s
3+
// RUN: %clang_cc1 -E -x c %s -pedantic -std=c99 | FileCheck -strict-whitespace %s
24

35
#define LPAREN (
46
#define RPAREN )

0 commit comments

Comments
 (0)