r221991 - Complete support for the SD-6 standing document (based off N4200) with support for __has_cpp_attribute.

Aaron Ballman aaron at aaronballman.com
Fri Nov 14 05:44:03 PST 2014


Author: aaronballman
Date: Fri Nov 14 07:44:02 2014
New Revision: 221991

URL: http://llvm.org/viewvc/llvm-project?rev=221991&view=rev
Log:
Complete support for the SD-6 standing document (based off N4200) with support for __has_cpp_attribute.

Added:
    cfe/trunk/test/Preprocessor/has_attribute.cpp
Modified:
    cfe/trunk/docs/LanguageExtensions.rst
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/Attributes.h
    cfe/trunk/include/clang/Lex/Preprocessor.h
    cfe/trunk/lib/Basic/Attributes.cpp
    cfe/trunk/lib/Lex/PPMacroExpansion.cpp
    cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp
    cfe/trunk/www/cxx_status.html

Modified: cfe/trunk/docs/LanguageExtensions.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LanguageExtensions.rst?rev=221991&r1=221990&r2=221991&view=diff
==============================================================================
--- cfe/trunk/docs/LanguageExtensions.rst (original)
+++ cfe/trunk/docs/LanguageExtensions.rst Fri Nov 14 07:44:02 2014
@@ -109,6 +109,36 @@ following ``__`` (double underscore) to
 the same name.  For instance, ``__cxx_rvalue_references__`` can be used instead
 of ``cxx_rvalue_references``.
 
+``__has_cpp_attribute``
+-------------------
+
+This function-like macro takes a single argument that is the name of a
+C++11-style attribute. The argument can either be a single identifier, or a
+scoped identifier. If the attribute is supported, a nonzero value is returned.
+If the attribute is a standards-based attribute, this macro returns a nonzero
+value based on the year and month in which the attribute was voted into the
+working draft. If the attribute is not supported by the current compliation
+target, this macro evaluates to 0.  It can be used like this:
+
+.. code-block:: c++
+
+  #ifndef __has_cpp_attribute         // Optional of course.
+    #define __has_cpp_attribute(x) 0  // Compatibility with non-clang compilers.
+  #endif
+
+  ...
+  #if __has_cpp_attribute(clang::fallthrough)
+  #define FALLTHROUGH [[clang::fallthrough]]
+  #else
+  #define FALLTHROUGH
+  #endif
+  ...
+
+The attribute identifier (but not scope) can also be specified with a preceding
+and following ``__`` (double underscore) to avoid interference from a macro with
+the same name.  For instance, ``gnu::__const__`` can be used instead of
+``gnu::const``.
+
 ``__has_attribute``
 -------------------
 

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=221991&r1=221990&r2=221991&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Fri Nov 14 07:44:02 2014
@@ -186,10 +186,11 @@ class Spelling<string name, string varie
 
 class GNU<string name> : Spelling<name, "GNU">;
 class Declspec<string name> : Spelling<name, "Declspec">;
-class CXX11<string namespace, string name> : Spelling<name, "CXX11"> {
+class CXX11<string namespace, string name, int version = 1>
+    : Spelling<name, "CXX11"> {
   string Namespace = namespace;
-}
-class Keyword<string name> : Spelling<name, "Keyword">;
+  int Version = version;
+} class Keyword<string name> : Spelling<name, "Keyword">;
 class Pragma<string namespace, string name> : Spelling<name, "Pragma"> {
   string Namespace = namespace;
 }
@@ -452,7 +453,8 @@ def Bounded : IgnoredAttr {
 }
 
 def CarriesDependency : InheritableParamAttr {
-  let Spellings = [GNU<"carries_dependency">, CXX11<"","carries_dependency">];
+  let Spellings = [GNU<"carries_dependency">,
+                   CXX11<"","carries_dependency", 200809>];
   let Subjects = SubjectList<[ParmVar, ObjCMethod, Function], ErrorDiag>;
   let Documentation = [CarriesDependencyDocs];
 }
@@ -593,7 +595,7 @@ def C11NoReturn : InheritableAttr {
 }
 
 def CXX11NoReturn : InheritableAttr {
-  let Spellings = [CXX11<"","noreturn">];
+  let Spellings = [CXX11<"","noreturn", 200809>];
   let Subjects = SubjectList<[Function], ErrorDiag>;
   let Documentation = [CXX11NoReturnDocs];
 }
@@ -642,7 +644,7 @@ def OpenCLConstantAddressSpace : TypeAtt
 
 def Deprecated : InheritableAttr {
   let Spellings = [GCC<"deprecated">, Declspec<"deprecated">,
-                   CXX11<"","deprecated">];
+                   CXX11<"","deprecated", 201309>];
   let Args = [StringArgument<"Message", 1>];
   let Documentation = [Undocumented];
 }

Modified: cfe/trunk/include/clang/Basic/Attributes.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attributes.h?rev=221991&r1=221990&r2=221991&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attributes.h (original)
+++ cfe/trunk/include/clang/Basic/Attributes.h Fri Nov 14 07:44:02 2014
@@ -30,11 +30,11 @@ enum class AttrSyntax {
   Pragma
 };
 
-/// \brief Return true if we recognize and implement the attribute specified by
-/// the given information.
-bool hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope,
-                  const IdentifierInfo *Attr, const llvm::Triple &T,
-                  const LangOptions &LangOpts);
+/// \brief Return the version number associated with the attribute if we
+/// recognize and implement the attribute specified by the given information.
+int hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope,
+                 const IdentifierInfo *Attr, const llvm::Triple &T,
+                 const LangOptions &LangOpts);
 
 } // end namespace clang
 

Modified: cfe/trunk/include/clang/Lex/Preprocessor.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/Preprocessor.h?rev=221991&r1=221990&r2=221991&view=diff
==============================================================================
--- cfe/trunk/include/clang/Lex/Preprocessor.h (original)
+++ cfe/trunk/include/clang/Lex/Preprocessor.h Fri Nov 14 07:44:02 2014
@@ -135,6 +135,7 @@ class Preprocessor : public RefCountedBa
   IdentifierInfo *Ident__is_identifier;            // __is_identifier
   IdentifierInfo *Ident__building_module;          // __building_module
   IdentifierInfo *Ident__MODULE__;                 // __MODULE__
+  IdentifierInfo *Ident__has_cpp_attribute;        // __has_cpp_attribute
 
   SourceLocation DATELoc, TIMELoc;
   unsigned CounterValue;  // Next __COUNTER__ value.

Modified: cfe/trunk/lib/Basic/Attributes.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Attributes.cpp?rev=221991&r1=221990&r2=221991&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/Attributes.cpp (original)
+++ cfe/trunk/lib/Basic/Attributes.cpp Fri Nov 14 07:44:02 2014
@@ -3,7 +3,7 @@
 #include "llvm/ADT/StringSwitch.h"
 using namespace clang;
 
-bool clang::hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope,
+int clang::hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope,
                          const IdentifierInfo *Attr, const llvm::Triple &T,
                          const LangOptions &LangOpts) {
   StringRef Name = Attr->getName();
@@ -13,5 +13,5 @@ bool clang::hasAttribute(AttrSyntax Synt
 
 #include "clang/Basic/AttrHasAttributeImpl.inc"
 
-  return false;
+  return 0;
 }

Modified: cfe/trunk/lib/Lex/PPMacroExpansion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPMacroExpansion.cpp?rev=221991&r1=221990&r2=221991&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/PPMacroExpansion.cpp (original)
+++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp Fri Nov 14 07:44:02 2014
@@ -96,6 +96,9 @@ void Preprocessor::RegisterBuiltinMacros
   Ident__COUNTER__ = RegisterBuiltinMacro(*this, "__COUNTER__");
   Ident_Pragma  = RegisterBuiltinMacro(*this, "_Pragma");
 
+  // C++ Standing Document Extensions.
+  Ident__has_cpp_attribute = RegisterBuiltinMacro(*this, "__has_cpp_attribute");
+
   // GCC Extensions.
   Ident__BASE_FILE__     = RegisterBuiltinMacro(*this, "__BASE_FILE__");
   Ident__INCLUDE_LEVEL__ = RegisterBuiltinMacro(*this, "__INCLUDE_LEVEL__");
@@ -1374,12 +1377,14 @@ void Preprocessor::ExpandBuiltinMacro(To
              II == Ident__has_extension ||
              II == Ident__has_builtin   ||
              II == Ident__is_identifier ||
-             II == Ident__has_attribute) {
+             II == Ident__has_attribute ||
+             II == Ident__has_cpp_attribute) {
     // The argument to these builtins should be a parenthesized identifier.
     SourceLocation StartLoc = Tok.getLocation();
 
     bool IsValid = false;
     IdentifierInfo *FeatureII = nullptr;
+    IdentifierInfo *ScopeII = nullptr;
 
     // Read the '('.
     LexUnexpandedToken(Tok);
@@ -1387,14 +1392,26 @@ void Preprocessor::ExpandBuiltinMacro(To
       // Read the identifier
       LexUnexpandedToken(Tok);
       if ((FeatureII = Tok.getIdentifierInfo())) {
-        // Read the ')'.
+        // If we're checking __has_cpp_attribute, it is possible to receive a
+        // scope token. Read the "::", if it's available.
         LexUnexpandedToken(Tok);
-        if (Tok.is(tok::r_paren))
+        bool IsScopeValid = true;
+        if (II == Ident__has_cpp_attribute && Tok.is(tok::coloncolon)) {
+          LexUnexpandedToken(Tok);
+          // The first thing we read was not the feature, it was the scope.
+          ScopeII = FeatureII;
+          if (FeatureII = Tok.getIdentifierInfo())
+            LexUnexpandedToken(Tok);
+          else
+            IsScopeValid = false;          
+        }
+        // Read the closing paren.
+        if (IsScopeValid && Tok.is(tok::r_paren))
           IsValid = true;
       }
     }
 
-    bool Value = false;
+    int Value = 0;
     if (!IsValid)
       Diag(StartLoc, diag::err_feature_check_malformed);
     else if (II == Ident__is_identifier)
@@ -1405,6 +1422,9 @@ void Preprocessor::ExpandBuiltinMacro(To
     } else if (II == Ident__has_attribute)
       Value = hasAttribute(AttrSyntax::Generic, nullptr, FeatureII,
                            getTargetInfo().getTriple(), getLangOpts());
+    else if (II == Ident__has_cpp_attribute)
+      Value = hasAttribute(AttrSyntax::CXX, ScopeII, FeatureII,
+                           getTargetInfo().getTriple(), getLangOpts());
     else if (II == Ident__has_extension)
       Value = HasExtension(*this, FeatureII);
     else {
@@ -1412,7 +1432,7 @@ void Preprocessor::ExpandBuiltinMacro(To
       Value = HasFeature(*this, FeatureII);
     }
 
-    OS << (int)Value;
+    OS << Value;
     if (IsValid)
       Tok.setKind(tok::numeric_constant);
   } else if (II == Ident__has_include ||

Added: cfe/trunk/test/Preprocessor/has_attribute.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Preprocessor/has_attribute.cpp?rev=221991&view=auto
==============================================================================
--- cfe/trunk/test/Preprocessor/has_attribute.cpp (added)
+++ cfe/trunk/test/Preprocessor/has_attribute.cpp Fri Nov 14 07:44:02 2014
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -triple i386-unknown-unknown -std=c++11 -E %s -o - | FileCheck %s
+
+// CHECK: has_cxx11_carries_dep
+#if __has_cpp_attribute(carries_dependency)
+  int has_cxx11_carries_dep();
+#endif
+
+// CHECK: has_clang_fallthrough
+#if __has_cpp_attribute(clang::fallthrough)
+  int has_clang_fallthrough();
+#endif
+
+// CHECK: does_not_have_selectany
+#if !__has_cpp_attribute(selectany)
+  int does_not_have_selectany();
+#endif
+
+// The attribute name can be bracketed with double underscores.
+// CHECK: has_clang_fallthrough_2
+#if __has_cpp_attribute(clang::__fallthrough__)
+  int has_clang_fallthrough_2();
+#endif
+
+// The scope cannot be bracketed with double underscores.
+// CHECK: does_not_have___clang___fallthrough
+#if !__has_cpp_attribute(__clang__::fallthrough)
+  int does_not_have___clang___fallthrough();
+#endif
+
+// Test that C++11, target-specific attributes behave properly.
+
+// CHECK: does_not_have_mips16
+#if !__has_cpp_attribute(gnu::mips16)
+  int does_not_have_mips16();
+#endif
+
+// Test that the version numbers of attributes listed in SD-6 are supported
+// correctly.
+
+// CHECK: has_cxx11_carries_dep_vers
+#if __has_cpp_attribute(carries_dependency) == 200809
+  int has_cxx11_carries_dep_vers();
+#endif
+
+// CHECK: has_cxx11_noreturn_vers
+#if __has_cpp_attribute(noreturn) == 200809
+  int has_cxx11_noreturn_vers();
+#endif
+
+// CHECK: has_cxx14_deprecated_vers
+#if __has_cpp_attribute(deprecated) == 201309
+  int has_cxx14_deprecated_vers();
+#endif

Modified: cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp?rev=221991&r1=221990&r2=221991&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp (original)
+++ cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp Fri Nov 14 07:44:02 2014
@@ -1820,6 +1820,27 @@ static void GenerateHasAttrSpellingStrin
     const std::vector<Record *> &Attrs, raw_ostream &OS,
     const std::string &Variety = "", const std::string &Scope = "") {
   for (const auto *Attr : Attrs) {
+    // C++11-style attributes have specific version information associated with
+    // them. If the attribute has no scope, the version information must not
+    // have the default value (1), as that's incorrect. Instead, the unscoped
+    // attribute version information should be taken from the SD-6 standing
+    // document, which can be found at: 
+    // https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations
+    int Version = 1;
+
+    if (Variety == "CXX11") {
+        std::vector<Record *> Spellings = Attr->getValueAsListOfDefs("Spellings");
+        for (const auto &Spelling : Spellings) {
+          if (Spelling->getValueAsString("Variety") == "CXX11") {
+            Version = static_cast<int>(Spelling->getValueAsInt("Version"));
+            if (Scope.empty() && Version == 1)
+              PrintError(Spelling->getLoc(), "C++ standard attributes must "
+              "have valid version information.");
+            break;
+          }
+      }
+    }
+
     // It is assumed that there will be an llvm::Triple object named T within
     // scope that can be used to determine whether the attribute exists in
     // a given target.
@@ -1858,16 +1879,16 @@ static void GenerateHasAttrSpellingStrin
       // C++11 mode should be checked against LangOpts, which is presumed to be
       // present in the caller.
       Test = "LangOpts.CPlusPlus11";
-    else
-      Test = "true";
 
+    std::string TestStr =
+        !Test.empty() ? Test + " ? " + std::to_string(Version) + " : 0" : "1";
     std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr);
     for (const auto &S : Spellings)
       if (Variety.empty() || (Variety == S.variety() &&
                               (Scope.empty() || Scope == S.nameSpace())))
-        OS << "    .Case(\"" << S.name() << "\", " << Test << ")\n";
+        OS << "    .Case(\"" << S.name() << "\", " << TestStr << ")\n";
   }
-  OS << "    .Default(false);\n";
+  OS << "    .Default(0);\n";
 }
 
 // Emits the list of spellings for attributes.
@@ -1899,16 +1920,16 @@ void EmitClangAttrHasAttrImpl(RecordKeep
 
   OS << "switch (Syntax) {\n";
   OS << "case AttrSyntax::Generic:\n";
-  OS << "  return llvm::StringSwitch<bool>(Name)\n";
+  OS << "  return llvm::StringSwitch<int>(Name)\n";
   GenerateHasAttrSpellingStringSwitch(Attrs, OS);
   OS << "case AttrSyntax::GNU:\n";
-  OS << "  return llvm::StringSwitch<bool>(Name)\n";
+  OS << "  return llvm::StringSwitch<int>(Name)\n";
   GenerateHasAttrSpellingStringSwitch(GNU, OS, "GNU");
   OS << "case AttrSyntax::Declspec:\n";
-  OS << "  return llvm::StringSwitch<bool>(Name)\n";
+  OS << "  return llvm::StringSwitch<int>(Name)\n";
   GenerateHasAttrSpellingStringSwitch(Declspec, OS, "Declspec");
   OS << "case AttrSyntax::Pragma:\n";
-  OS << "  return llvm::StringSwitch<bool>(Name)\n";
+  OS << "  return llvm::StringSwitch<int>(Name)\n";
   GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma");
   OS << "case AttrSyntax::CXX: {\n";
   // C++11-style attributes are further split out based on the Scope.
@@ -1921,7 +1942,7 @@ void EmitClangAttrHasAttrImpl(RecordKeep
       OS << "if (!Scope || Scope->getName() == \"\") {\n";
     else
       OS << "if (Scope->getName() == \"" << I->first << "\") {\n";
-    OS << "  return llvm::StringSwitch<bool>(Name)\n";
+    OS << "  return llvm::StringSwitch<int>(Name)\n";
     GenerateHasAttrSpellingStringSwitch(I->second, OS, "CXX11", I->first);
     OS << "}";
   }

Modified: cfe/trunk/www/cxx_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=221991&r1=221990&r2=221991&view=diff
==============================================================================
--- cfe/trunk/www/cxx_status.html (original)
+++ cfe/trunk/www/cxx_status.html Fri Nov 14 07:44:02 2014
@@ -590,8 +590,8 @@ Clang version they became available:</p>
       </td>
     </tr>
     <tr>
-      <td class="partial" align="center">
-        SVN (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4200">N4200</a>): Partial <a href="#n4200">(1)</a>
+      <td class="full" align="center">
+        SVN (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4200">N4200</a>)</a>
       </td>
     </tr>
     <tr>
@@ -611,10 +611,6 @@ Clang version they became available:</p>
     </tr>
 </table>
 
-<p>
-<span id="n4200">(1): <code>__has_cpp_attribute</code> is not yet supported.</span>
-</p>
-
 </div>
 </body>
 </html>





More information about the cfe-commits mailing list