[llvm-branch-commits] [clang] ea99c88 - Permit __VA_OPT__ in all language modes and allow it to be detected with #ifdef.

Richard Smith via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Jan 27 15:52:47 PST 2021


Author: Richard Smith
Date: 2021-01-27T15:52:31-08:00
New Revision: ea99c885a63de9af673a5e5cd51f44fb70c83c1b

URL: https://github.com/llvm/llvm-project/commit/ea99c885a63de9af673a5e5cd51f44fb70c83c1b
DIFF: https://github.com/llvm/llvm-project/commit/ea99c885a63de9af673a5e5cd51f44fb70c83c1b.diff

LOG: 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.

(cherry picked from commit 0436ec2128c9775ba13b0308937238fc79673fdd)

Added: 
    

Modified: 
    clang/include/clang/Lex/Preprocessor.h
    clang/include/clang/Lex/VariadicMacroSupport.h
    clang/lib/Lex/PPDirectives.cpp
    clang/lib/Lex/PPExpressions.cpp
    clang/lib/Lex/PPMacroExpansion.cpp
    clang/lib/Lex/Preprocessor.cpp
    clang/test/Preprocessor/macro_vaopt_check.cpp
    clang/test/Preprocessor/macro_vaopt_expand.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 68139cb24b31..ba8bdaa23c4c 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -447,6 +447,25 @@ class Preprocessor {
           ElseLoc(ElseLoc) {}
   };
 
+  class IfdefMacroNameScopeRAII {
+    Preprocessor &PP;
+    bool VAOPTWasPoisoned;
+
+  public:
+    IfdefMacroNameScopeRAII(Preprocessor &PP)
+        : PP(PP), VAOPTWasPoisoned(PP.Ident__VA_OPT__->isPoisoned()) {
+      PP.Ident__VA_OPT__->setIsPoisoned(false);
+    }
+    IfdefMacroNameScopeRAII(const IfdefMacroNameScopeRAII&) = delete;
+    IfdefMacroNameScopeRAII &operator=(const IfdefMacroNameScopeRAII&) = delete;
+    ~IfdefMacroNameScopeRAII() { Exit(); }
+
+    void Exit() {
+      if (VAOPTWasPoisoned)
+        PP.Ident__VA_OPT__->setIsPoisoned(true);
+    }
+  };
+
 private:
   friend class ASTReader;
   friend class MacroArgs;

diff  --git a/clang/include/clang/Lex/VariadicMacroSupport.h b/clang/include/clang/Lex/VariadicMacroSupport.h
index 989e0ac703c9..119f02201fc6 100644
--- a/clang/include/clang/Lex/VariadicMacroSupport.h
+++ b/clang/include/clang/Lex/VariadicMacroSupport.h
@@ -39,17 +39,14 @@ namespace clang {
       assert(Ident__VA_ARGS__->isPoisoned() && "__VA_ARGS__ should be poisoned "
                                               "outside an ISO C/C++ variadic "
                                               "macro definition!");
-      assert(
-          !Ident__VA_OPT__ ||
-          (Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!"));
+      assert(Ident__VA_OPT__->isPoisoned() && "__VA_OPT__ should be poisoned!");
     }
 
     /// Client code should call this function just before the Preprocessor is
     /// about to Lex tokens from the definition of a variadic (ISO C/C++) macro.
     void enterScope() {
       Ident__VA_ARGS__->setIsPoisoned(false);
-      if (Ident__VA_OPT__)
-        Ident__VA_OPT__->setIsPoisoned(false);
+      Ident__VA_OPT__->setIsPoisoned(false);
     }
 
     /// Client code should call this function as soon as the Preprocessor has
@@ -58,8 +55,7 @@ namespace clang {
     /// (might be explicitly called, and then reinvoked via the destructor).
     void exitScope() {
       Ident__VA_ARGS__->setIsPoisoned(true);
-      if (Ident__VA_OPT__)
-        Ident__VA_OPT__->setIsPoisoned(true);
+      Ident__VA_OPT__->setIsPoisoned(true);
     }
 
     ~VariadicMacroScopeGuard() { exitScope(); }

diff  --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index d6b03d85913d..e2aa93455ea5 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2928,9 +2928,14 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
   ++NumIf;
   Token DirectiveTok = Result;
 
+  // __VA_OPT__ is allowed as the operand of #if[n]def.
+  IfdefMacroNameScopeRAII IfdefMacroNameScope(*this);
+
   Token MacroNameTok;
   ReadMacroName(MacroNameTok);
 
+  IfdefMacroNameScope.Exit();
+
   // Error reading macro name?  If so, diagnostic already issued.
   if (MacroNameTok.is(tok::eod)) {
     // Skip code until we get to #endif.  This helps with recovery by not

diff  --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp
index 8c120c13d7d2..952fb8f121dc 100644
--- a/clang/lib/Lex/PPExpressions.cpp
+++ b/clang/lib/Lex/PPExpressions.cpp
@@ -104,6 +104,9 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
   SourceLocation beginLoc(PeekTok.getLocation());
   Result.setBegin(beginLoc);
 
+  // __VA_OPT__ is allowed as the operand of 'defined'.
+  Preprocessor::IfdefMacroNameScopeRAII IfdefMacroNameScope(PP);
+
   // Get the next token, don't expand it.
   PP.LexUnexpandedNonComment(PeekTok);
 
@@ -122,6 +125,8 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
     PP.LexUnexpandedNonComment(PeekTok);
   }
 
+  IfdefMacroNameScope.Exit();
+
   // If we don't have a pp-identifier now, this is an error.
   if (PP.CheckMacroName(PeekTok, MU_Other))
     return true;

diff  --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index 43d31d6c5732..f6ca04defeb9 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -323,13 +323,16 @@ void Preprocessor::dumpMacroInfo(const IdentifierInfo *II) {
 
 /// RegisterBuiltinMacro - Register the specified identifier in the identifier
 /// table and mark it as a builtin macro to be expanded.
-static IdentifierInfo *RegisterBuiltinMacro(Preprocessor &PP, const char *Name){
+static IdentifierInfo *RegisterBuiltinMacro(Preprocessor &PP, const char *Name,
+                                            bool Disabled = false) {
   // Get the identifier.
   IdentifierInfo *Id = PP.getIdentifierInfo(Name);
 
   // Mark it as being a macro that is builtin.
   MacroInfo *MI = PP.AllocateMacroInfo(SourceLocation());
   MI->setIsBuiltinMacro();
+  if (Disabled)
+    MI->DisableMacro();
   PP.appendDefMacroDirective(Id, MI);
   return Id;
 }
@@ -343,6 +346,7 @@ void Preprocessor::RegisterBuiltinMacros() {
   Ident__TIME__ = RegisterBuiltinMacro(*this, "__TIME__");
   Ident__COUNTER__ = RegisterBuiltinMacro(*this, "__COUNTER__");
   Ident_Pragma  = RegisterBuiltinMacro(*this, "_Pragma");
+  Ident__VA_OPT__ = RegisterBuiltinMacro(*this, "__VA_OPT__", true);
 
   // C++ Standing Document Extensions.
   if (getLangOpts().CPlusPlus)

diff  --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 94f1ce91f884..9baba204b324 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -115,23 +115,20 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
 
   BuiltinInfo = std::make_unique<Builtin::Context>();
 
-  // "Poison" __VA_ARGS__, __VA_OPT__ which can only appear in the expansion of
-  // a macro. They get unpoisoned where it is allowed.
-  (Ident__VA_ARGS__ = getIdentifierInfo("__VA_ARGS__"))->setIsPoisoned();
-  SetPoisonReason(Ident__VA_ARGS__,diag::ext_pp_bad_vaargs_use);
-  if (getLangOpts().CPlusPlus20) {
-    (Ident__VA_OPT__ = getIdentifierInfo("__VA_OPT__"))->setIsPoisoned();
-    SetPoisonReason(Ident__VA_OPT__,diag::ext_pp_bad_vaopt_use);
-  } else {
-    Ident__VA_OPT__ = nullptr;
-  }
-
   // Initialize the pragma handlers.
   RegisterBuiltinPragmas();
 
   // Initialize builtin macros like __LINE__ and friends.
   RegisterBuiltinMacros();
 
+  // "Poison" __VA_ARGS__, __VA_OPT__ which can only appear in the expansion of
+  // a macro. They get unpoisoned where it is allowed. Note that we model
+  // __VA_OPT__ as a builtin macro to allow #ifdef and friends to detect it.
+  (Ident__VA_ARGS__ = getIdentifierInfo("__VA_ARGS__"))->setIsPoisoned();
+  SetPoisonReason(Ident__VA_ARGS__, diag::ext_pp_bad_vaargs_use);
+  Ident__VA_OPT__->setIsPoisoned();
+  SetPoisonReason(Ident__VA_OPT__, diag::ext_pp_bad_vaopt_use);
+
   if(LangOpts.Borland) {
     Ident__exception_info        = getIdentifierInfo("_exception_info");
     Ident___exception_info       = getIdentifierInfo("__exception_info");

diff  --git a/clang/test/Preprocessor/macro_vaopt_check.cpp b/clang/test/Preprocessor/macro_vaopt_check.cpp
index fb52e9946af3..84f3b85871dd 100644
--- a/clang/test/Preprocessor/macro_vaopt_check.cpp
+++ b/clang/test/Preprocessor/macro_vaopt_check.cpp
@@ -1,4 +1,20 @@
-// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -pedantic -std=c++2a
+// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -pedantic -std=c++20
+// RUN: %clang_cc1 %s -Eonly -verify -Wno-all -pedantic -std=c++11
+// RUN: %clang_cc1 -x c %s -Eonly -verify -Wno-all -pedantic -std=c99
+
+// Check that support for __VA_OPT__ can be detected by #ifdef.
+#ifndef __VA_OPT__
+#error should be defined
+#endif
+
+#ifdef __VA_OPT__
+#else
+#error should be defined
+#endif
+
+#if !defined(__VA_OPT__)
+#error should be defined
+#endif
 
 //expected-error at +1{{missing '('}}
 #define V1(...) __VA_OPT__  
@@ -62,3 +78,16 @@
 #define V1(...) __VA_OPT__  ((())
 #undef V1
 
+// __VA_OPT__ can't appear anywhere else.
+#if __VA_OPT__ // expected-warning {{__VA_OPT__ can only appear in the expansion of a variadic macro}}
+#endif
+
+#define BAD __VA_OPT__ // expected-warning {{__VA_OPT__ can only appear in the expansion of a variadic macro}}
+
+// Check defined(__VA_OPT__) doesn't leave __VA_OPT__ poisoned.
+#define Z(...) (0 __VA_OPT__(|| 1))
+#if defined(__VA_OPT__) && Z(hello)
+// OK
+#else
+#error bad
+#endif

diff  --git a/clang/test/Preprocessor/macro_vaopt_expand.cpp b/clang/test/Preprocessor/macro_vaopt_expand.cpp
index 7ec4f6128cfa..5eb0facb83f7 100644
--- a/clang/test/Preprocessor/macro_vaopt_expand.cpp
+++ b/clang/test/Preprocessor/macro_vaopt_expand.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -E %s -pedantic -std=c++2a | FileCheck -strict-whitespace %s
+// RUN: %clang_cc1 -E %s -pedantic -std=c++20 | FileCheck -strict-whitespace %s
+// RUN: %clang_cc1 -E %s -pedantic -std=c++11 | FileCheck -strict-whitespace %s
+// RUN: %clang_cc1 -E -x c %s -pedantic -std=c99 | FileCheck -strict-whitespace %s
 
 #define LPAREN ( 
 #define RPAREN ) 


        


More information about the llvm-branch-commits mailing list