Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -164,6 +164,35 @@ (double underscore) to avoid interference from a macro with the same name. For instance, ``__always_inline__`` can be used instead of ``always_inline``. +``__has_attribute_enhancement`` +------------------------------- + +This function-like macro takes two arguments: the name of a GNU-style attribute, +and the name of an enhancement that was made to the attribute since its +introduction. It evaluates to 1 if clang supports this enhancement, and 0 if +not. + +For example, clang's ``overloadable`` attribute has existed since before Clang +3.5, but in Clang 5.0 it gained was modified to support so-called "unmarked +overloads". One can use ``__has_attribute_enhancement`` to query whether clang +supports this upgraded version of ``overloadable`` like so: + +.. code-block:: c++ + + #ifndef __has_attribute_enhancement // Optional. + #define __has_attribute_enhancement(x, y) 0 // Compatibility with non-clang + // compilers. + #endif + + #if !__has_attribute_enhancement(overloadable, unmarked_overloads) + #error "This library requires unmarked overload support." + #endif + +Since enhancements are attribute-specific, please consult the documentation to +see what enhancements each attribute has, if any. + +Much like in ``__has_attribute``, if either the first or second argument to +this macro has a leading and trailing ``__``, clang will ignore them. ``__has_declspec_attribute`` ---------------------------- Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -435,6 +435,10 @@ // attribute may be documented under multiple categories, more than one // Documentation entry may be listed. list Documentation; + // Any features that this attribute supports that were added since its + // introduction. These can be queried via the __has_attribute_enhancement + // builtin macro. + list Enhancements = []; } /// A type attribute is not processed on a declaration or a statement. @@ -1581,6 +1585,7 @@ let Spellings = [GNU<"overloadable">]; let Subjects = SubjectList<[Function], ErrorDiag>; let Documentation = [OverloadableDocs]; + let Enhancements = ["unmarked_overloads"]; } def Override : InheritableAttr { Index: include/clang/Basic/Attributes.h =================================================================== --- include/clang/Basic/Attributes.h +++ include/clang/Basic/Attributes.h @@ -34,7 +34,8 @@ /// recognize and implement the attribute specified by the given information. int hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope, const IdentifierInfo *Attr, const TargetInfo &Target, - const LangOptions &LangOpts); + const LangOptions &LangOpts, + const IdentifierInfo *Enhancement = nullptr); } // end namespace clang Index: include/clang/Lex/Preprocessor.h =================================================================== --- include/clang/Lex/Preprocessor.h +++ include/clang/Lex/Preprocessor.h @@ -121,27 +121,29 @@ llvm::BumpPtrAllocator BP; /// Identifiers for builtin macros and other builtins. - IdentifierInfo *Ident__LINE__, *Ident__FILE__; // __LINE__, __FILE__ - IdentifierInfo *Ident__DATE__, *Ident__TIME__; // __DATE__, __TIME__ - IdentifierInfo *Ident__INCLUDE_LEVEL__; // __INCLUDE_LEVEL__ - IdentifierInfo *Ident__BASE_FILE__; // __BASE_FILE__ - IdentifierInfo *Ident__TIMESTAMP__; // __TIMESTAMP__ - IdentifierInfo *Ident__COUNTER__; // __COUNTER__ - IdentifierInfo *Ident_Pragma, *Ident__pragma; // _Pragma, __pragma - IdentifierInfo *Ident__identifier; // __identifier - IdentifierInfo *Ident__VA_ARGS__; // __VA_ARGS__ - IdentifierInfo *Ident__has_feature; // __has_feature - IdentifierInfo *Ident__has_extension; // __has_extension - IdentifierInfo *Ident__has_builtin; // __has_builtin - IdentifierInfo *Ident__has_attribute; // __has_attribute - IdentifierInfo *Ident__has_include; // __has_include - IdentifierInfo *Ident__has_include_next; // __has_include_next - IdentifierInfo *Ident__has_warning; // __has_warning - IdentifierInfo *Ident__is_identifier; // __is_identifier - IdentifierInfo *Ident__building_module; // __building_module - IdentifierInfo *Ident__MODULE__; // __MODULE__ - IdentifierInfo *Ident__has_cpp_attribute; // __has_cpp_attribute - IdentifierInfo *Ident__has_declspec; // __has_declspec_attribute + IdentifierInfo *Ident__LINE__, *Ident__FILE__; // __LINE__, __FILE__ + IdentifierInfo *Ident__DATE__, *Ident__TIME__; // __DATE__, __TIME__ + IdentifierInfo *Ident__INCLUDE_LEVEL__; // __INCLUDE_LEVEL__ + IdentifierInfo *Ident__BASE_FILE__; // __BASE_FILE__ + IdentifierInfo *Ident__TIMESTAMP__; // __TIMESTAMP__ + IdentifierInfo *Ident__COUNTER__; // __COUNTER__ + IdentifierInfo *Ident_Pragma, *Ident__pragma; // _Pragma, __pragma + IdentifierInfo *Ident__identifier; // __identifier + IdentifierInfo *Ident__VA_ARGS__; // __VA_ARGS__ + IdentifierInfo *Ident__has_feature; // __has_feature + IdentifierInfo *Ident__has_extension; // __has_extension + IdentifierInfo *Ident__has_builtin; // __has_builtin + IdentifierInfo *Ident__has_attribute; // __has_attribute + IdentifierInfo + *Ident__has_attribute_enhancement; // __has_attribute_enhancement + IdentifierInfo *Ident__has_include; // __has_include + IdentifierInfo *Ident__has_include_next; // __has_include_next + IdentifierInfo *Ident__has_warning; // __has_warning + IdentifierInfo *Ident__is_identifier; // __is_identifier + IdentifierInfo *Ident__building_module; // __building_module + IdentifierInfo *Ident__MODULE__; // __MODULE__ + IdentifierInfo *Ident__has_cpp_attribute; // __has_cpp_attribute + IdentifierInfo *Ident__has_declspec; // __has_declspec_attribute SourceLocation DATELoc, TIMELoc; unsigned CounterValue; // Next __COUNTER__ value. Index: lib/Basic/Attributes.cpp =================================================================== --- lib/Basic/Attributes.cpp +++ lib/Basic/Attributes.cpp @@ -4,13 +4,20 @@ #include "llvm/ADT/StringSwitch.h" using namespace clang; +static StringRef stripSurroundingUnderscores(StringRef R) { + if (R.size() >= 4 && R.startswith("__") && R.endswith("__")) + return R.drop_front(2).drop_back(2); + return R; +} + int clang::hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope, const IdentifierInfo *Attr, const TargetInfo &Target, - const LangOptions &LangOpts) { - StringRef Name = Attr->getName(); - // Normalize the attribute name, __foo__ becomes foo. - if (Name.size() >= 4 && Name.startswith("__") && Name.endswith("__")) - Name = Name.substr(2, Name.size() - 4); + const LangOptions &LangOpts, + const IdentifierInfo *Enhancement) { + StringRef Name = stripSurroundingUnderscores(Attr->getName()); + StringRef EnhancementName; + if (Enhancement) + EnhancementName = stripSurroundingUnderscores(Enhancement->getName()); #include "clang/Basic/AttrHasAttributeImpl.inc" Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -351,9 +351,9 @@ Ident__has_cpp_attribute = nullptr; // GCC Extensions. - Ident__BASE_FILE__ = RegisterBuiltinMacro(*this, "__BASE_FILE__"); + Ident__BASE_FILE__ = RegisterBuiltinMacro(*this, "__BASE_FILE__"); Ident__INCLUDE_LEVEL__ = RegisterBuiltinMacro(*this, "__INCLUDE_LEVEL__"); - Ident__TIMESTAMP__ = RegisterBuiltinMacro(*this, "__TIMESTAMP__"); + Ident__TIMESTAMP__ = RegisterBuiltinMacro(*this, "__TIMESTAMP__"); // Microsoft Extensions. if (LangOpts.MicrosoftExt) { @@ -365,15 +365,17 @@ } // Clang Extensions. - Ident__has_feature = RegisterBuiltinMacro(*this, "__has_feature"); - Ident__has_extension = RegisterBuiltinMacro(*this, "__has_extension"); - Ident__has_builtin = RegisterBuiltinMacro(*this, "__has_builtin"); - Ident__has_attribute = RegisterBuiltinMacro(*this, "__has_attribute"); + Ident__has_feature = RegisterBuiltinMacro(*this, "__has_feature"); + Ident__has_extension = RegisterBuiltinMacro(*this, "__has_extension"); + Ident__has_builtin = RegisterBuiltinMacro(*this, "__has_builtin"); + Ident__has_attribute = RegisterBuiltinMacro(*this, "__has_attribute"); + Ident__has_attribute_enhancement = + RegisterBuiltinMacro(*this, "__has_attribute_enhancement"); Ident__has_declspec = RegisterBuiltinMacro(*this, "__has_declspec_attribute"); - Ident__has_include = RegisterBuiltinMacro(*this, "__has_include"); + Ident__has_include = RegisterBuiltinMacro(*this, "__has_include"); Ident__has_include_next = RegisterBuiltinMacro(*this, "__has_include_next"); - Ident__has_warning = RegisterBuiltinMacro(*this, "__has_warning"); - Ident__is_identifier = RegisterBuiltinMacro(*this, "__is_identifier"); + Ident__has_warning = RegisterBuiltinMacro(*this, "__has_warning"); + Ident__is_identifier = RegisterBuiltinMacro(*this, "__is_identifier"); // Modules. Ident__building_module = RegisterBuiltinMacro(*this, "__building_module"); @@ -1470,14 +1472,15 @@ return EvaluateHasIncludeCommon(Tok, II, PP, Lookup, LookupFromFile); } -/// \brief Process single-argument builtin feature-like macros that return -/// integer values. +/// \brief Process builtin feature-like macros that return integer values. Any +/// multi-arg handling should be done from `Op`. static void EvaluateFeatureLikeBuiltinMacro(llvm::raw_svector_ostream& OS, Token &Tok, IdentifierInfo *II, Preprocessor &PP, llvm::function_ref< int(Token &Tok, - bool &HasLexedNextTok)> Op) { + bool &HasLexedNextTok, + bool &SuppressDiags)> Op) { // Parse the initial '('. PP.LexUnexpandedToken(Tok); if (Tok.isNot(tok::l_paren)) { @@ -1550,7 +1553,9 @@ break; bool HasLexedNextToken = false; - Result = Op(Tok, HasLexedNextToken); + bool NoMoreDiags = false; + Result = Op(Tok, HasLexedNextToken, NoMoreDiags); + SuppressDiagnostic |= NoMoreDiags; ResultTok = Tok; if (HasLexedNextToken) goto already_lexed; @@ -1721,21 +1726,21 @@ Tok.setKind(tok::numeric_constant); } else if (II == Ident__has_feature) { EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, - [this](Token &Tok, bool &HasLexedNextToken) -> int { + [this](Token &Tok, bool &HasLexedNextToken, bool &SuppressDiags) -> int { IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, diag::err_feature_check_malformed); return II && HasFeature(*this, II->getName()); }); } else if (II == Ident__has_extension) { EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, - [this](Token &Tok, bool &HasLexedNextToken) -> int { + [this](Token &Tok, bool &HasLexedNextToken, bool &SuppressDiags) -> int { IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, diag::err_feature_check_malformed); return II && HasExtension(*this, II->getName()); }); } else if (II == Ident__has_builtin) { EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, - [this](Token &Tok, bool &HasLexedNextToken) -> int { + [this](Token &Tok, bool &HasLexedNextToken, bool &SuppressDiags) -> int { IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, diag::err_feature_check_malformed); if (!II) @@ -1753,20 +1758,58 @@ }); } else if (II == Ident__is_identifier) { EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, - [](Token &Tok, bool &HasLexedNextToken) -> int { + [](Token &Tok, bool &HasLexedNextToken, bool &SuppressDiags) -> int { return Tok.is(tok::identifier); }); } else if (II == Ident__has_attribute) { EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, - [this](Token &Tok, bool &HasLexedNextToken) -> int { + [this](Token &Tok, bool &HasLexedNextToken, bool &SuppressDiags) -> int { IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, diag::err_feature_check_malformed); return II ? hasAttribute(AttrSyntax::GNU, nullptr, II, getTargetInfo(), getLangOpts()) : 0; }); + } else if (II == Ident__has_attribute_enhancement) { + EvaluateFeatureLikeBuiltinMacro( + OS, Tok, II, *this, + [this](Token &Tok, bool &HasLexedNextToken, + bool &SuppressDiags) -> int { + IdentifierInfo *AttrName = ExpectFeatureIdentifierInfo( + Tok, *this, diag::err_feature_check_malformed); + + if (!AttrName) + return 0; + + IdentifierInfo *EnhancementName = nullptr; + SourceLocation InitialIdentLoc = Tok.getLocation(); + LexUnexpandedToken(Tok); + if (Tok.isNot(tok::comma)) { + if (Tok.is(tok::r_paren)) + Diag(Tok.getLocation(), diag::err_too_few_args_in_macro_invoc); + else + Diag(Tok.getLocation(), diag::err_pp_expected_after) + << AttrName << tok::comma << InitialIdentLoc; + HasLexedNextToken = true; + SuppressDiags = true; + return 0; + } + + LexUnexpandedToken(Tok); + EnhancementName = ExpectFeatureIdentifierInfo( + Tok, *this, diag::err_pp_expected_ident_in_arg_list); + // If the enhancement name is invalid, pretend we don't have it. + if (!EnhancementName) { + SuppressDiags = true; + HasLexedNextToken = true; + return 0; + } + + return hasAttribute(AttrSyntax::GNU, nullptr, AttrName, + getTargetInfo(), getLangOpts(), EnhancementName); + }); } else if (II == Ident__has_declspec) { EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, - [this](Token &Tok, bool &HasLexedNextToken) -> int { + [this](Token &Tok, bool &HasLexedNextToken, bool &SuppressDiags) -> int { IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, diag::err_feature_check_malformed); return II ? hasAttribute(AttrSyntax::Declspec, nullptr, II, @@ -1774,7 +1817,7 @@ }); } else if (II == Ident__has_cpp_attribute) { EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, - [this](Token &Tok, bool &HasLexedNextToken) -> int { + [this](Token &Tok, bool &HasLexedNextToken, bool &SuppressDiags) -> int { IdentifierInfo *ScopeII = nullptr; IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, diag::err_feature_check_malformed); @@ -1814,7 +1857,7 @@ } else if (II == Ident__has_warning) { // The argument should be a parenthesized string literal. EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, - [this](Token &Tok, bool &HasLexedNextToken) -> int { + [this](Token &Tok, bool &HasLexedNextToken, bool &SuppressDiags) -> int { std::string WarningName; SourceLocation StrStartLoc = Tok.getLocation(); @@ -1845,7 +1888,7 @@ // builtin evaluates to 1 when that identifier names the module we are // currently building. EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, - [this](Token &Tok, bool &HasLexedNextToken) -> int { + [this](Token &Tok, bool &HasLexedNextToken, bool &SuppressDiags) -> int { IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this, diag::err_expected_id_building_module); return getLangOpts().isCompilingModule() && II && Index: test/Preprocessor/has_attribute.c =================================================================== --- test/Preprocessor/has_attribute.c +++ test/Preprocessor/has_attribute.c @@ -56,3 +56,59 @@ #if __has_cpp_attribute(selectany) // expected-error {{function-like macro '__has_cpp_attribute' is not defined}} #endif + +#if !defined(__has_attribute_enhancement) +#error "No extended has_attribute support?" +#endif + +#if __has_attribute_enhancement(attr_does_not_exist) // expected-error{{too few arguments}} +#error "We have an enhancement on an attr that doesn't exist?" +#endif + +#if __has_attribute_enhancement(attr_does_not_exist thing) // expected-error{{missing ',' after 'attr_does_not_exist'}} +#error "We have an enhancement on an attr that doesn't exist?" +#endif + +#if __has_attribute_enhancement(attr_does_not_exist, ____) +#error "We have an enhancement on an attr that doesn't exist?" +#endif + +#if __has_attribute_enhancement(always_inline, ____) +#error "Empty enhancement exists on always_inline?" +#endif + +#if __has_attribute_enhancement(always_inline, enhancement_does_not_exist) +#error "Enhancement checks on enhancementless attributes seem broken" +#endif + +#if !__has_attribute(overloadable) +#error "The following tests depend on having overloadable" +#endif + +#if __has_attribute_enhancement(overloadable,) // expected-error{{expected identifier}} +#error "Should assume false" +#endif + +#if __has_attribute_enhancement(overloadable, not a valid enhancement) // expected-error{{missing ')' after 'not'}} expected-note{{to match this}} +#error "Should assume false" +#endif + +#if __has_attribute_enhancement(overloadable, not_an_existing_enhancement) +#error "Should assume false" +#endif + +#if __has_attribute_enhancement(overloadable, __not_an_existing_enhancement__) +#error "Should assume false" +#endif + +#if !__has_attribute_enhancement(overloadable, unmarked_overloads) +#error "Unmarked overloads are supported" +#endif + +#if __has_attribute_enhancement(overloadable, __unmarked_overloads_) +#error "Underscores should be all-or-nothing" +#endif + +#if !__has_attribute_enhancement(overloadable, __unmarked_overloads__) +#error "Unmarked overloads are supported" +#endif Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -2664,6 +2664,7 @@ static void GenerateHasAttrSpellingStringSwitch( const std::vector &Attrs, raw_ostream &OS, const std::string &Variety = "", const std::string &Scope = "") { + const StringRef CaseIndent = " "; 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 @@ -2700,15 +2701,35 @@ // present in the caller. Test = "LangOpts.CPlusPlus11"; + // Any enhancements must be checked for, as well. + if (!Test.empty()) + Test += " && "; + + std::vector Enhancements = + Attr->getValueAsListOfStrings("Enhancements"); + if (Enhancements.empty()) { + Test += "!Enhancement"; + } else { + Test += "(!Enhancement || llvm::StringSwitch(EnhancementName)"; + + std::string Preamble = ("\n" + CaseIndent + CaseIndent).str(); + for (const auto &Enhancement : Enhancements) { + assert(Enhancement.find('"') == std::string::npos && + "Quotes aren't allowed in enhancements!"); + Test += (Preamble + ".Case(\"" + Enhancement + "\", true)").str(); + } + Test += Preamble + ".Default(false))"; + } + std::string TestStr = !Test.empty() ? Test + " ? " + llvm::itostr(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() << "\", " << TestStr << ")\n"; + OS << CaseIndent << ".Case(\"" << S.name() << "\", " << TestStr << ")\n"; } - OS << " .Default(0);\n"; + OS << CaseIndent << ".Default(0);\n"; } // Emits the list of spellings for attributes.