From a0344c5d7b9b748a2244dce393ceb55e83b495d6 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Fri, 14 Nov 2014 13:44:02 +0000 Subject: [PATCH] Complete support for the SD-6 standing document (based off N4200) with support for __has_cpp_attribute. llvm-svn: 221991 --- clang/docs/LanguageExtensions.rst | 30 +++++++++++++++++ clang/include/clang/Basic/Attr.td | 14 ++++---- clang/include/clang/Basic/Attributes.h | 10 +++--- clang/include/clang/Lex/Preprocessor.h | 1 + clang/lib/Basic/Attributes.cpp | 4 +-- clang/lib/Lex/PPMacroExpansion.cpp | 30 ++++++++++++++--- clang/test/Preprocessor/has_attribute.cpp | 53 +++++++++++++++++++++++++++++++ clang/utils/TableGen/ClangAttrEmitter.cpp | 39 +++++++++++++++++------ clang/www/cxx_status.html | 8 ++--- 9 files changed, 156 insertions(+), 33 deletions(-) create mode 100644 clang/test/Preprocessor/has_attribute.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index bf41ef5..d45d15e 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 4fbbc66..5ab73e6 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 5783b3b..982e12d 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 3bf65e5..d16f212 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 a05ad05..da9ac79 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 eb14176..225a3fa 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 0000000..27c48d0 --- /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 a8e1b07..71b5c36 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 5239c20..cdb05a1 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. -

- -- 2.7.4