diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -134,6 +134,11 @@ const IdentifierInfo *getScopeName() const { return ScopeName; } SourceLocation getScopeLoc() const { return ScopeLoc; } + /// Gets the normalized full name, which consists of both scope and name and + /// with surrounding underscores removed as appropriate (e.g. + /// __gnu__::__attr__ will be normalized to gnu::attr). + std::string getNormalizedFullName() const; + bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec; } bool isMicrosoftAttribute() const { return SyntaxUsed == AS_Microsoft; } diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -24,6 +24,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/Allocator.h" +#include "llvm/Support/Registry.h" #include "llvm/Support/VersionTuple.h" #include #include @@ -37,6 +38,72 @@ class Expr; class IdentifierInfo; class LangOptions; +class ParsedAttr; +class Sema; + +struct ParsedAttrInfo { + /// Corresponds to the Kind enum. + unsigned AttrKind : 16; + /// The number of required arguments of this attribute. + unsigned NumArgs : 4; + /// The number of optional arguments of this attributes. + unsigned OptArgs : 4; + /// True if the parsing does not match the semantic content. + unsigned HasCustomParsing : 1; + /// True if this attribute is only available for certain targets. + unsigned IsTargetSpecific : 1; + /// True if this attribute applies to types. + unsigned IsType : 1; + /// True if this attribute applies to statements. + unsigned IsStmt : 1; + /// True if this attribute has any spellings that are known to gcc. + unsigned IsKnownToGCC : 1; + /// True if this attribute is supported by #pragma clang attribute. + unsigned IsSupportedByPragmaAttribute : 1; + /// The syntaxes supported by this attribute and how they're spelled. + struct Spelling { + AttributeCommonInfo::Syntax Syntax; + std::string NormalizedFullName; + }; + std::vector Spellings; + + ParsedAttrInfo(AttributeCommonInfo::Kind AttrKind = + AttributeCommonInfo::UnknownAttribute) + : AttrKind(AttrKind), NumArgs(0), OptArgs(0), HasCustomParsing(0), + IsTargetSpecific(0), IsType(0), IsStmt(0), IsKnownToGCC(0), + IsSupportedByPragmaAttribute(0) {} + + virtual ~ParsedAttrInfo() = default; + + /// Check if this attribute appertains to D, and issue a diagnostic if not. + virtual bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr, + const Decl *D) const { + return true; + } + /// Check if this attribute is allowed by the language we are compiling, and + /// issue a diagnostic if not. + virtual bool diagLangOpts(Sema &S, const ParsedAttr &Attr) const { + return true; + } + /// Check if this attribute is allowed when compiling for the given target. + virtual bool existsInTarget(const TargetInfo &Target) const { + return true; + } + /// Convert the spelling index of Attr to a semantic spelling enum value. + virtual unsigned + spellingIndexToSemanticSpelling(const ParsedAttr &Attr) const { + return UINT_MAX; + } + /// Populate Rules with the match rules of this attribute. + virtual void getPragmaAttributeMatchRules( + llvm::SmallVectorImpl> &Rules, + const LangOptions &LangOpts) const { + } + + static const ParsedAttrInfo &get(const AttributeCommonInfo &A); +}; + +typedef llvm::Registry ParsedAttrInfoRegistry; /// Represents information about a change in availability for /// an entity, which is part of the encoding of the 'availability' @@ -181,6 +248,8 @@ const Expr *MessageExpr; + const ParsedAttrInfo &Info; + ArgsUnion *getArgsBuffer() { return getTrailingObjects(); } ArgsUnion const *getArgsBuffer() const { return getTrailingObjects(); @@ -207,7 +276,8 @@ EllipsisLoc(ellipsisLoc), NumArgs(numArgs), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - HasProcessingCache(false), IsPragmaClangAttribute(false) { + HasProcessingCache(false), IsPragmaClangAttribute(false), + Info(ParsedAttrInfo::get(*this)) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); } @@ -225,7 +295,8 @@ NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), IsPragmaClangAttribute(false), - UnavailableLoc(unavailable), MessageExpr(messageExpr) { + UnavailableLoc(unavailable), MessageExpr(messageExpr), + Info(ParsedAttrInfo::get(*this)) { ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (getAvailabilityData()) detail::AvailabilityData( @@ -242,7 +313,7 @@ NumArgs(3), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false) { + IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { ArgsUnion *Args = getArgsBuffer(); Args[0] = Parm1; Args[1] = Parm2; @@ -259,7 +330,7 @@ NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false) { + IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { ArgsUnion PVal(ArgKind); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); detail::TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); @@ -277,7 +348,7 @@ NumArgs(0), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true), HasProcessingCache(false), - IsPragmaClangAttribute(false) { + IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { new (&getTypeBuffer()) ParsedType(typeArg); } @@ -291,7 +362,7 @@ NumArgs(0), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false) { + IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { new (&getPropertyDataBuffer()) detail::PropertyData(getterId, setterId); } @@ -534,7 +605,10 @@ } } - AttributeCommonInfo::Kind getKind() const { return getParsedKind(); } + AttributeCommonInfo::Kind getKind() const { + return AttributeCommonInfo::Kind(Info.AttrKind); + } + const ParsedAttrInfo &getInfo() const { return Info; } }; class AttributePool; diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp --- a/clang/lib/Basic/Attributes.cpp +++ b/clang/lib/Basic/Attributes.cpp @@ -36,10 +36,14 @@ } static StringRef -normalizeAttrScopeName(StringRef ScopeName, +normalizeAttrScopeName(const IdentifierInfo *Scope, AttributeCommonInfo::Syntax SyntaxUsed) { + if (!Scope) + return ""; + // Normalize the "__gnu__" scope name to be "gnu" and the "_Clang" scope name // to be "clang". + StringRef ScopeName = Scope->getName(); if (SyntaxUsed == AttributeCommonInfo::AS_CXX11 || SyntaxUsed == AttributeCommonInfo::AS_C2x) { if (ScopeName == "__gnu__") @@ -50,7 +54,7 @@ return ScopeName; } -static StringRef normalizeAttrName(StringRef AttrName, +static StringRef normalizeAttrName(const IdentifierInfo *Name, StringRef NormalizedScopeName, AttributeCommonInfo::Syntax SyntaxUsed) { // Normalize the attribute name, __foo__ becomes foo. This is only allowable @@ -61,6 +65,7 @@ SyntaxUsed == AttributeCommonInfo::AS_C2x) && (NormalizedScopeName.empty() || NormalizedScopeName == "gnu" || NormalizedScopeName == "clang")); + StringRef AttrName = Name->getName(); if (ShouldNormalize && AttrName.size() >= 4 && AttrName.startswith("__") && AttrName.endswith("__")) AttrName = AttrName.slice(2, AttrName.size() - 2); @@ -74,35 +79,41 @@ #include "clang/Sema/AttrParsedAttrKinds.inc" -AttributeCommonInfo::Kind -AttributeCommonInfo::getParsedKind(const IdentifierInfo *Name, - const IdentifierInfo *ScopeName, - Syntax SyntaxUsed) { - StringRef AttrName = Name->getName(); - - SmallString<64> FullName; - if (ScopeName) - FullName += normalizeAttrScopeName(ScopeName->getName(), SyntaxUsed); - - AttrName = normalizeAttrName(AttrName, FullName, SyntaxUsed); +static SmallString<64> normalizeName(const IdentifierInfo *Name, + const IdentifierInfo *Scope, + AttributeCommonInfo::Syntax SyntaxUsed) { + StringRef ScopeName = normalizeAttrScopeName(Scope, SyntaxUsed); + StringRef AttrName = normalizeAttrName(Name, ScopeName, SyntaxUsed); // Ensure that in the case of C++11 attributes, we look for '::foo' if it is // unscoped. - if (ScopeName || SyntaxUsed == AS_CXX11 || SyntaxUsed == AS_C2x) + SmallString<64> FullName = ScopeName; + if (Scope || SyntaxUsed == AttributeCommonInfo::AS_CXX11 || + SyntaxUsed == AttributeCommonInfo::AS_C2x) FullName += "::"; FullName += AttrName; - return ::getAttrKind(FullName, SyntaxUsed); + return FullName; +} + +AttributeCommonInfo::Kind +AttributeCommonInfo::getParsedKind(const IdentifierInfo *Name, + const IdentifierInfo *ScopeName, + Syntax SyntaxUsed) { + return ::getAttrKind(normalizeName(Name, ScopeName, SyntaxUsed), SyntaxUsed); +} + +std::string AttributeCommonInfo::getNormalizedFullName() const { + return static_cast( + normalizeName(getAttrName(), getScopeName(), getSyntax())); } unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const { // Both variables will be used in tablegen generated // attribute spell list index matching code. auto Syntax = static_cast(getSyntax()); - StringRef Scope = - getScopeName() ? normalizeAttrScopeName(getScopeName()->getName(), Syntax) - : ""; - StringRef Name = normalizeAttrName(getAttrName()->getName(), Scope, Syntax); + StringRef Scope = normalizeAttrScopeName(getScopeName(), Syntax); + StringRef Name = normalizeAttrName(getAttrName(), Scope, Syntax); #include "clang/Sema/AttrSpellingListIndex.inc" } diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp --- a/clang/lib/Sema/ParsedAttr.cpp +++ b/clang/lib/Sema/ParsedAttr.cpp @@ -25,6 +25,8 @@ using namespace clang; +LLVM_INSTANTIATE_REGISTRY(ParsedAttrInfoRegistry) + IdentifierLoc *IdentifierLoc::create(ASTContext &Ctx, SourceLocation Loc, IdentifierInfo *Ident) { IdentifierLoc *Result = new (Ctx) IdentifierLoc; @@ -100,66 +102,58 @@ pool.Attrs.clear(); } -struct ParsedAttrInfo { - unsigned NumArgs : 4; - unsigned OptArgs : 4; - unsigned HasCustomParsing : 1; - unsigned IsTargetSpecific : 1; - unsigned IsType : 1; - unsigned IsStmt : 1; - unsigned IsKnownToGCC : 1; - unsigned IsSupportedByPragmaAttribute : 1; - - virtual ~ParsedAttrInfo() = default; - - virtual bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr, - const Decl *) const { - return true; - } - virtual bool diagLangOpts(Sema &S, const ParsedAttr &Attr) const { - return true; - } - virtual bool existsInTarget(const TargetInfo &Target) const { - return true; - } - virtual unsigned - spellingIndexToSemanticSpelling(const ParsedAttr &Attr) const { - return UINT_MAX; - } - virtual void getPragmaAttributeMatchRules( - llvm::SmallVectorImpl> &Rules, - const LangOptions &LangOpts) const { - } -}; - namespace { #include "clang/Sema/AttrParsedAttrImpl.inc" } // namespace -static const ParsedAttrInfo &getInfo(const ParsedAttr &A) { - // If we have a ParsedAttrInfo for this ParsedAttr then return that, - // otherwise return a default ParsedAttrInfo. - if (A.getKind() < llvm::array_lengthof(AttrInfoMap)) - return *AttrInfoMap[A.getKind()]; - +const ParsedAttrInfo &ParsedAttrInfo::get(const AttributeCommonInfo &A) { + // If we have a ParsedAttrInfo for this ParsedAttr then return that. + if (A.getParsedKind() < llvm::array_lengthof(AttrInfoMap)) + return *AttrInfoMap[A.getParsedKind()]; + + // If this is an ignored attribute then return an appropriate ParsedAttrInfo. + static ParsedAttrInfo IgnoredParsedAttrInfo( + AttributeCommonInfo::IgnoredAttribute); + if (A.getParsedKind() == AttributeCommonInfo::IgnoredAttribute) + return IgnoredParsedAttrInfo; + + // Otherwise this may be an attribute defined by a plugin. First instantiate + // all plugin attributes if we haven't already done so. + static std::list> PluginAttrInstances; + if (PluginAttrInstances.empty()) + for (auto It : ParsedAttrInfoRegistry::entries()) + PluginAttrInstances.emplace_back(It.instantiate()); + + // Search for a ParsedAttrInfo whose name and syntax match. + std::string FullName = A.getNormalizedFullName(); + AttributeCommonInfo::Syntax SyntaxUsed = A.getSyntax(); + if (SyntaxUsed == AttributeCommonInfo::AS_ContextSensitiveKeyword) + SyntaxUsed = AttributeCommonInfo::AS_Keyword; + + for (auto &Ptr : PluginAttrInstances) + for (auto &S : Ptr->Spellings) + if (S.Syntax == SyntaxUsed && S.NormalizedFullName == FullName) + return *Ptr; + + // If we failed to find a match then return a default ParsedAttrInfo. static ParsedAttrInfo DefaultParsedAttrInfo; return DefaultParsedAttrInfo; } -unsigned ParsedAttr::getMinArgs() const { return getInfo(*this).NumArgs; } +unsigned ParsedAttr::getMinArgs() const { return getInfo().NumArgs; } unsigned ParsedAttr::getMaxArgs() const { - return getMinArgs() + getInfo(*this).OptArgs; + return getMinArgs() + getInfo().OptArgs; } bool ParsedAttr::hasCustomParsing() const { - return getInfo(*this).HasCustomParsing; + return getInfo().HasCustomParsing; } bool ParsedAttr::diagnoseAppertainsTo(Sema &S, const Decl *D) const { - return getInfo(*this).diagAppertainsToDecl(S, *this, D); + return getInfo().diagAppertainsToDecl(S, *this, D); } bool ParsedAttr::appliesToDecl(const Decl *D, @@ -171,33 +165,33 @@ const LangOptions &LangOpts, SmallVectorImpl> &MatchRules) const { - return getInfo(*this).getPragmaAttributeMatchRules(MatchRules, LangOpts); + return getInfo().getPragmaAttributeMatchRules(MatchRules, LangOpts); } bool ParsedAttr::diagnoseLangOpts(Sema &S) const { - return getInfo(*this).diagLangOpts(S, *this); + return getInfo().diagLangOpts(S, *this); } bool ParsedAttr::isTargetSpecificAttr() const { - return getInfo(*this).IsTargetSpecific; + return getInfo().IsTargetSpecific; } -bool ParsedAttr::isTypeAttr() const { return getInfo(*this).IsType; } +bool ParsedAttr::isTypeAttr() const { return getInfo().IsType; } -bool ParsedAttr::isStmtAttr() const { return getInfo(*this).IsStmt; } +bool ParsedAttr::isStmtAttr() const { return getInfo().IsStmt; } bool ParsedAttr::existsInTarget(const TargetInfo &Target) const { - return getInfo(*this).existsInTarget(Target); + return getInfo().existsInTarget(Target); } -bool ParsedAttr::isKnownToGCC() const { return getInfo(*this).IsKnownToGCC; } +bool ParsedAttr::isKnownToGCC() const { return getInfo().IsKnownToGCC; } bool ParsedAttr::isSupportedByPragmaAttribute() const { - return getInfo(*this).IsSupportedByPragmaAttribute; + return getInfo().IsSupportedByPragmaAttribute; } unsigned ParsedAttr::getSemanticSpelling() const { - return getInfo(*this).spellingIndexToSemanticSpelling(*this); + return getInfo().spellingIndexToSemanticSpelling(*this); } bool ParsedAttr::hasVariadicArg() const { @@ -205,5 +199,5 @@ // claim that as being variadic. If we someday get an attribute that // legitimately bumps up against that maximum, we can use another bit to track // whether it's truly variadic or not. - return getInfo(*this).OptArgs == 15; + return getInfo().OptArgs == 15; } diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -3625,9 +3625,11 @@ // We need to generate struct instances based off ParsedAttrInfo from // ParsedAttr.cpp. + const std::string &AttrName = I->first; const Record &Attr = *I->second; OS << "struct ParsedAttrInfo" << I->first << " : public ParsedAttrInfo {\n"; OS << " ParsedAttrInfo" << I->first << "() {\n"; + OS << " AttrKind = ParsedAttr::AT_" << AttrName << ";\n"; emitArgInfo(Attr, OS); OS << " HasCustomParsing = "; OS << Attr.getValueAsBit("HasCustomParsing") << ";\n"; @@ -3642,6 +3644,20 @@ OS << IsKnownToGCC(Attr) << ";\n"; OS << " IsSupportedByPragmaAttribute = "; OS << PragmaAttributeSupport.isAttributedSupported(*I->second) << ";\n"; + for (const auto &S : GetFlattenedSpellings(Attr)) { + const std::string &RawSpelling = S.name(); + std::string Spelling; + if (S.variety() == "CXX11" || S.variety() == "C2x") { + Spelling += S.nameSpace(); + Spelling += "::"; + } + if (S.variety() == "GNU") + Spelling += NormalizeGNUAttrSpelling(RawSpelling); + else + Spelling += RawSpelling; + OS << " Spellings.push_back({AttributeCommonInfo::AS_" << S.variety(); + OS << ",\"" << Spelling << "\"});\n"; + } OS << " }\n"; GenerateAppertainsTo(Attr, OS); GenerateLangOptRequirements(Attr, OS);