Skip to content

Commit

Permalink
Add an attribute registry so plugins can add attributes
Browse files Browse the repository at this point in the history
When constructing a ParsedAttr the ParsedAttrInfo gets looked up in the
AttrInfoMap, which is auto-generated using tablegen. If that lookup fails then
we look through the ParsedAttrInfos that plugins have added to the registry and
check if any has a spelling that matches.

Differential Revision: https://reviews.llvm.org/D31338
  • Loading branch information
john-brawn-arm committed Feb 27, 2020
1 parent ddd1127 commit 75d4d4b
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 78 deletions.
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/AttributeCommonInfo.h
Expand Up @@ -134,6 +134,11 @@ class AttributeCommonInfo {
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; }

Expand Down
88 changes: 81 additions & 7 deletions clang/include/clang/Sema/ParsedAttr.h
Expand Up @@ -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 <cassert>
#include <cstddef>
Expand All @@ -37,6 +38,72 @@ class Decl;
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<Spelling> 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<std::pair<attr::SubjectMatchRule, bool>> &Rules,
const LangOptions &LangOpts) const {
}

static const ParsedAttrInfo &get(const AttributeCommonInfo &A);
};

typedef llvm::Registry<ParsedAttrInfo> ParsedAttrInfoRegistry;

/// Represents information about a change in availability for
/// an entity, which is part of the encoding of the 'availability'
Expand Down Expand Up @@ -181,6 +248,8 @@ class ParsedAttr final

const Expr *MessageExpr;

const ParsedAttrInfo &Info;

ArgsUnion *getArgsBuffer() { return getTrailingObjects<ArgsUnion>(); }
ArgsUnion const *getArgsBuffer() const {
return getTrailingObjects<ArgsUnion>();
Expand All @@ -207,7 +276,8 @@ class ParsedAttr final
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));
}
Expand All @@ -225,7 +295,8 @@ class ParsedAttr final
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(
Expand All @@ -242,7 +313,7 @@ class ParsedAttr final
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;
Expand All @@ -259,7 +330,7 @@ class ParsedAttr final
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();
Expand All @@ -277,7 +348,7 @@ class ParsedAttr final
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);
}

Expand All @@ -291,7 +362,7 @@ class ParsedAttr final
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);
}

Expand Down Expand Up @@ -534,7 +605,10 @@ class ParsedAttr final
}
}

AttributeCommonInfo::Kind getKind() const { return getParsedKind(); }
AttributeCommonInfo::Kind getKind() const {
return AttributeCommonInfo::Kind(Info.AttrKind);
}
const ParsedAttrInfo &getInfo() const { return Info; }
};

class AttributePool;
Expand Down
49 changes: 30 additions & 19 deletions clang/lib/Basic/Attributes.cpp
Expand Up @@ -36,10 +36,14 @@ const char *attr::getSubjectMatchRuleSpelling(attr::SubjectMatchRule Rule) {
}

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__")
Expand All @@ -50,7 +54,7 @@ normalizeAttrScopeName(StringRef ScopeName,
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
Expand All @@ -61,6 +65,7 @@ static StringRef normalizeAttrName(StringRef AttrName,
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);
Expand All @@ -74,35 +79,41 @@ bool AttributeCommonInfo::isGNUScope() const {

#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<std::string>(
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<AttributeCommonInfo::Syntax>(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"
}

0 comments on commit 75d4d4b

Please sign in to comment.