diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index bf41ef5feaf2..d45d15e444e7 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -109,6 +109,36 @@ following ``__`` (double underscore) to avoid interference from a macro with
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``
-------------------
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 4fbbc6684800..5ab73e64deb8 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -186,10 +186,11 @@ class Spelling {
class GNU : Spelling;
class Declspec : Spelling;
-class CXX11 : Spelling {
+class CXX11
+ : Spelling {
string Namespace = namespace;
-}
-class Keyword : Spelling;
+ int Version = version;
+} class Keyword : Spelling;
class Pragma : Spelling {
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 : TypeAttr {
def Deprecated : InheritableAttr {
let Spellings = [GCC<"deprecated">, Declspec<"deprecated">,
- CXX11<"","deprecated">];
+ CXX11<"","deprecated", 201309>];
let Args = [StringArgument<"Message", 1>];
let Documentation = [Undocumented];
}
diff --git a/clang/include/clang/Basic/Attributes.h b/clang/include/clang/Basic/Attributes.h
index 5783b3bff510..982e12d9f24f 100644
--- a/clang/include/clang/Basic/Attributes.h
+++ b/clang/include/clang/Basic/Attributes.h
@@ -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
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 3bf65e5f87dd..d16f2128164c 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -135,6 +135,7 @@ class Preprocessor : public RefCountedBase {
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.
diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp
index a05ad053db04..da9ac793f163 100644
--- a/clang/lib/Basic/Attributes.cpp
+++ b/clang/lib/Basic/Attributes.cpp
@@ -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 Syntax, const IdentifierInfo *Scope,
#include "clang/Basic/AttrHasAttributeImpl.inc"
- return false;
+ return 0;
}
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index eb1417660e27..225a3fafd3c6 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -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(Token &Tok) {
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(Token &Tok) {
// 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(Token &Tok) {
} 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(Token &Tok) {
Value = HasFeature(*this, FeatureII);
}
- OS << (int)Value;
+ OS << Value;
if (IsValid)
Tok.setKind(tok::numeric_constant);
} else if (II == Ident__has_include ||
diff --git a/clang/test/Preprocessor/has_attribute.cpp b/clang/test/Preprocessor/has_attribute.cpp
new file mode 100644
index 000000000000..27c48d077cec
--- /dev/null
+++ b/clang/test/Preprocessor/has_attribute.cpp
@@ -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
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index a8e1b077d5bc..71b5c3650f34 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -1820,6 +1820,27 @@ static void GenerateHasAttrSpellingStringSwitch(
const std::vector &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 Spellings = Attr->getValueAsListOfDefs("Spellings");
+ for (const auto &Spelling : Spellings) {
+ if (Spelling->getValueAsString("Variety") == "CXX11") {
+ Version = static_cast(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 GenerateHasAttrSpellingStringSwitch(
// 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 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(RecordKeeper &Records, raw_ostream &OS) {
OS << "switch (Syntax) {\n";
OS << "case AttrSyntax::Generic:\n";
- OS << " return llvm::StringSwitch(Name)\n";
+ OS << " return llvm::StringSwitch(Name)\n";
GenerateHasAttrSpellingStringSwitch(Attrs, OS);
OS << "case AttrSyntax::GNU:\n";
- OS << " return llvm::StringSwitch(Name)\n";
+ OS << " return llvm::StringSwitch(Name)\n";
GenerateHasAttrSpellingStringSwitch(GNU, OS, "GNU");
OS << "case AttrSyntax::Declspec:\n";
- OS << " return llvm::StringSwitch(Name)\n";
+ OS << " return llvm::StringSwitch(Name)\n";
GenerateHasAttrSpellingStringSwitch(Declspec, OS, "Declspec");
OS << "case AttrSyntax::Pragma:\n";
- OS << " return llvm::StringSwitch(Name)\n";
+ OS << " return llvm::StringSwitch(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(RecordKeeper &Records, raw_ostream &OS) {
OS << "if (!Scope || Scope->getName() == \"\") {\n";
else
OS << "if (Scope->getName() == \"" << I->first << "\") {\n";
- OS << " return llvm::StringSwitch(Name)\n";
+ OS << " return llvm::StringSwitch(Name)\n";
GenerateHasAttrSpellingStringSwitch(I->second, OS, "CXX11", I->first);
OS << "}";
}
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 5239c2030bbf..cdb05a14403a 100644
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -590,8 +590,8 @@ Clang version they became available:
-
- SVN (N4200): Partial (1)
+ |
+ SVN (N4200)
|
@@ -611,10 +611,6 @@ Clang version they became available:
-
-(1): __has_cpp_attribute
is not yet supported.
-
-