diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -50,6 +50,7 @@ // FIXME: These are properties of the attribute kind, not state for this // instance of the attribute. unsigned IsLateParsed : 1; + unsigned IsODRHashable : 1; unsigned InheritEvenIfAlreadyPresent : 1; void *operator new(size_t bytes) noexcept { @@ -71,10 +72,10 @@ protected: Attr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, - attr::Kind AK, bool IsLateParsed) + attr::Kind AK, bool IsLateParsed, bool IsODRHashable) : AttributeCommonInfo(CommonInfo), AttrKind(AK), Inherited(false), IsPackExpansion(false), Implicit(false), IsLateParsed(IsLateParsed), - InheritEvenIfAlreadyPresent(false) {} + IsODRHashable(IsODRHashable), InheritEvenIfAlreadyPresent(false) {} public: attr::Kind getKind() const { return static_cast(AttrKind); } @@ -101,17 +102,25 @@ bool isLateParsed() const { return IsLateParsed; } + bool isODRHashable() const { return IsODRHashable; } + // Pretty print this attribute. void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const; + /// Hash the attribute and its salient arguments. + /// + /// It is an implementation detail and for obtaining a hash you should use + /// ODRHash, specifically ODRHash::AddAttr. + void processODRHash(llvm::FoldingSetNodeID &ID, class ODRHash &Hash) const; + static StringRef getDocumentation(attr::Kind); }; class TypeAttr : public Attr { protected: TypeAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, - attr::Kind AK, bool IsLateParsed) - : Attr(Context, CommonInfo, AK, IsLateParsed) {} + attr::Kind AK, bool IsLateParsed, bool IsODRHashable) + : Attr(Context, CommonInfo, AK, IsLateParsed, IsODRHashable) {} public: static bool classof(const Attr *A) { @@ -123,8 +132,8 @@ class StmtAttr : public Attr { protected: StmtAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, - attr::Kind AK, bool IsLateParsed) - : Attr(Context, CommonInfo, AK, IsLateParsed) {} + attr::Kind AK, bool IsLateParsed, bool IsODRHashable) + : Attr(Context, CommonInfo, AK, IsLateParsed, IsODRHashable) {} public: static bool classof(const Attr *A) { @@ -136,9 +145,9 @@ class InheritableAttr : public Attr { protected: InheritableAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, - attr::Kind AK, bool IsLateParsed, + attr::Kind AK, bool IsLateParsed, bool IsODRHashable, bool InheritEvenIfAlreadyPresent) - : Attr(Context, CommonInfo, AK, IsLateParsed) { + : Attr(Context, CommonInfo, AK, IsLateParsed, IsODRHashable) { this->InheritEvenIfAlreadyPresent = InheritEvenIfAlreadyPresent; } @@ -161,9 +170,9 @@ class DeclOrStmtAttr : public InheritableAttr { protected: DeclOrStmtAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, - attr::Kind AK, bool IsLateParsed, + attr::Kind AK, bool IsLateParsed, bool IsODRHashable, bool InheritEvenIfAlreadyPresent) - : InheritableAttr(Context, CommonInfo, AK, IsLateParsed, + : InheritableAttr(Context, CommonInfo, AK, IsLateParsed, IsODRHashable, InheritEvenIfAlreadyPresent) {} public: @@ -177,8 +186,9 @@ protected: InheritableParamAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, attr::Kind AK, - bool IsLateParsed, bool InheritEvenIfAlreadyPresent) - : InheritableAttr(Context, CommonInfo, AK, IsLateParsed, + bool IsLateParsed, bool IsODRHashable, + bool InheritEvenIfAlreadyPresent) + : InheritableAttr(Context, CommonInfo, AK, IsLateParsed, IsODRHashable, InheritEvenIfAlreadyPresent) {} public: @@ -192,9 +202,9 @@ class HLSLAnnotationAttr : public InheritableAttr { protected: HLSLAnnotationAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, - attr::Kind AK, bool IsLateParsed, + attr::Kind AK, bool IsLateParsed, bool IsODRHashable, bool InheritEvenIfAlreadyPresent) - : InheritableAttr(Context, CommonInfo, AK, IsLateParsed, + : InheritableAttr(Context, CommonInfo, AK, IsLateParsed, IsODRHashable, InheritEvenIfAlreadyPresent) {} public: @@ -210,10 +220,10 @@ class ParameterABIAttr : public InheritableParamAttr { protected: ParameterABIAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, - attr::Kind AK, bool IsLateParsed, + attr::Kind AK, bool IsLateParsed, bool IsODRHashable, bool InheritEvenIfAlreadyPresent) : InheritableParamAttr(Context, CommonInfo, AK, IsLateParsed, - InheritEvenIfAlreadyPresent) {} + IsODRHashable, InheritEvenIfAlreadyPresent) {} public: ParameterABI getABI() const { diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -549,6 +549,8 @@ list Accessors = []; // Set to true for attributes with arguments which require delayed parsing. bit LateParsed = 0; + // Set to true for attributes that should participate in ODR hashing. + bit IsODRHashable = 0; // Set to false to prevent an attribute from being propagated from a template // to the instantiation. bit Clone = 1; @@ -669,6 +671,7 @@ let Args = [VariadicStringArgument<"Tags">]; let Subjects = SubjectList<[Struct, Var, Function, Namespace], ErrorDiag>; let MeaningfulToClassTemplateDefinition = 1; + let IsODRHashable = 1; let Documentation = [AbiTagsDocs]; } @@ -710,6 +713,7 @@ Accessor<"isAlignas", [Keyword<"alignas">, Keyword<"_Alignas">]>, Accessor<"isDeclspec",[Declspec<"align">]>]; + let IsODRHashable = 1; let Documentation = [Undocumented]; } @@ -730,6 +734,7 @@ ]; let Args = [ExprArgument<"Alignment">]; let Subjects = SubjectList<[Var, TypedefName]>; + let IsODRHashable = 1; let Documentation = [AlignValueDocs]; } @@ -1497,6 +1502,7 @@ let Subjects = SubjectList<[Enum]>; let Args = [EnumArgument<"Extensibility", "Kind", ["closed", "open"], ["Closed", "Open"]>]; + let IsODRHashable = 1; let Documentation = [EnumExtensibilityDocs]; } @@ -1719,6 +1725,7 @@ def Naked : InheritableAttr { let Spellings = [GCC<"naked">, Declspec<"naked">]; let Subjects = SubjectList<[Function]>; + let IsODRHashable = 1; let Documentation = [Undocumented]; } @@ -2320,6 +2327,7 @@ def OptimizeNone : InheritableAttr { let Spellings = [Clang<"optnone">]; let Subjects = SubjectList<[Function, ObjCMethod]>; + let IsODRHashable = 1; let Documentation = [OptnoneDocs]; } @@ -2361,6 +2369,7 @@ def Packed : InheritableAttr { let Spellings = [GCC<"packed">]; // let Subjects = [Tag, Field]; + let IsODRHashable = 1; let Documentation = [Undocumented]; } @@ -2861,6 +2870,7 @@ C2x<"", "maybe_unused", 202106>]; let Subjects = SubjectList<[Var, ObjCIvar, Type, Enum, EnumConstant, Label, Field, ObjCMethod, FunctionLike]>; + let IsODRHashable = 1; let Documentation = [WarnMaybeUnusedDocs]; } diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Expr.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/Type.h" using namespace clang; diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -539,7 +539,7 @@ return; llvm::copy_if(D->attrs(), std::back_inserter(HashableAttrs), - [](const Attr *A) { return !A->isImplicit(); }); + [](const Attr *A) { return A->isODRHashable(); }); } void ODRHash::AddAttrs(const Decl *D) { @@ -551,39 +551,7 @@ } void ODRHash::AddAttr(const Attr *A) { - ID.AddInteger(A->getKind()); - - // FIXME: This should be auto-generated as part of Attr.td - switch (A->getKind()) { - case attr::Aligned: { - auto *M = cast(A); - ID.AddBoolean(M->isAlignmentExpr()); - if (M->isAlignmentExpr()) { - Expr *AlignmentExpr = M->getAlignmentExpr(); - ID.AddBoolean(AlignmentExpr); - if (AlignmentExpr) - AddStmt(AlignmentExpr); - } else { - ID.AddString(M->getAlignmentType()->getType().getAsString()); - } - break; - } - case attr::AlignValue: { - auto *M = cast(A); - Expr *AlignmentExpr = M->getAlignment(); - ID.AddBoolean(AlignmentExpr); - if (AlignmentExpr) - AddStmt(AlignmentExpr); - break; - } - case attr::EnumExtensibility: { - auto *M = cast(A); - ID.AddInteger(M->getExtensibility()); - break; - } - default: - break; - } + A->processODRHash(ID, *this); } void ODRHash::AddSubDecl(const Decl *D) { 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 @@ -249,6 +249,7 @@ virtual void writeDump(raw_ostream &OS) const = 0; virtual void writeDumpChildren(raw_ostream &OS) const {} virtual void writeHasChildren(raw_ostream &OS) const { OS << "false"; } + virtual void writeODRHashing(raw_ostream &OS) const {} virtual bool isEnumArg() const { return false; } virtual bool isVariadicEnumArg() const { return false; } @@ -647,6 +648,20 @@ void writeHasChildren(raw_ostream &OS) const override { OS << "SA->is" << getUpperName() << "Expr()"; } + + void writeODRHashing(raw_ostream &OS) const override { + OS << " ID.AddBoolean(is" << getUpperName() << "Expr());\n"; + OS << " if (is" << getUpperName() << "Expr()) {\n"; + OS << " const Expr *" << getUpperName() << "Expr = " + << "get" << getUpperName() << "Expr();\n"; + OS << " ID.AddBoolean(" << getUpperName() << "Expr);\n"; + OS << " if (" << getUpperName() << "Expr)\n" + << " Hash.AddStmt(" << getUpperName() << "Expr);\n"; + OS << " } else {\n"; + OS << " ID.AddString(get" << getUpperName() + << "Type()->getType().getAsString());\n"; + OS << " }\n"; + } }; class VariadicArgument : public Argument { @@ -1006,6 +1021,10 @@ << " llvm_unreachable(\"No enumerator with that value\");\n" << "}\n"; } + + void writeODRHashing(raw_ostream &OS) const override { + OS << " ID.AddInteger(get" << getUpperName() << "());\n"; + } }; class VariadicEnumArgument: public VariadicArgument { @@ -1230,6 +1249,14 @@ } void writeHasChildren(raw_ostream &OS) const override { OS << "true"; } + + void writeODRHashing(raw_ostream &OS) const override { + OS << " const Expr *" << getUpperName() << "Expr = " + << "get" << getUpperName() << "();\n"; + OS << " ID.AddBoolean(" << getUpperName() << "Expr);\n"; + OS << " if (" << getUpperName() << "Expr)\n" + << " Hash.AddStmt(" << getUpperName() << "Expr);\n"; + } }; class VariadicExprArgument : public VariadicArgument { @@ -1624,6 +1651,27 @@ OS << "}\n\n"; } +static void +writeProcessODRHashFunction(const Record &R, + const std::vector> &Args, + raw_ostream &OS) { + OS << "void " << R.getName() << "Attr::processODRHash(\n" + << " llvm::FoldingSetNodeID &ID, ODRHash &Hash) const {\n"; + if (!R.getValueAsBit("IsODRHashable")) { + OS << " // Not ODR-hashable\n}\n\n"; + return; + } + + OS << " ID.AddInteger(Attr::getKind());\n"; + for (const auto &Arg : Args) { + if (Arg->isFake()) + continue; + Arg->writeODRHashing(OS); + } + // End of the processODRHash function. + OS << "}\n\n"; +} + /// Return the index of a spelling in a spelling list. static unsigned getSpellingListIndex(const std::vector &SpellingList, @@ -2683,7 +2731,8 @@ } OS << "\n : " << SuperName << "(Ctx, CommonInfo, "; OS << "attr::" << R.getName() << ", " - << (R.getValueAsBit("LateParsed") ? "true" : "false"); + << (R.getValueAsBit("LateParsed") ? "true" : "false") << ", " + << (R.getValueAsBit("IsODRHashable") ? "true" : "false"); if (Inheritable) { OS << ", " << (R.getValueAsBit("InheritEvenIfAlreadyPresent") ? "true" @@ -2746,6 +2795,8 @@ OS << " void printPretty(raw_ostream &OS,\n" << " const PrintingPolicy &Policy) const;\n"; OS << " const char *getSpelling() const;\n"; + OS << " void processODRHash(llvm::FoldingSetNodeID &ID,\n" + << " class ODRHash &Hash) const;\n"; } if (!ElideSpelling) { @@ -2820,6 +2871,7 @@ writePrettyPrintFunction(R, Args, OS); writeGetSpellingFunction(R, OS); + writeProcessODRHashFunction(R, Args, OS); } } } @@ -2870,6 +2922,10 @@ OS << "void Attr::printPretty(raw_ostream &OS, " "const PrintingPolicy &Policy) const {\n"; EmitFunc("printPretty(OS, Policy)"); + + OS << "void Attr::processODRHash(llvm::FoldingSetNodeID &ID, " + << "class ODRHash &Hash) const {"; + EmitFunc("processODRHash(ID, Hash)"); } static void emitAttrList(raw_ostream &OS, StringRef Class,