Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -457,6 +457,9 @@ /// \brief Whether we are currently parsing base specifiers. unsigned IsParsingBaseSpecifiers : 1; + /// \brief A hash of parts of the class to help in ODR checking. + unsigned ODRHash; + /// \brief The number of base class specifiers in Bases. unsigned NumBases; @@ -703,6 +706,9 @@ return data().IsParsingBaseSpecifiers; } + void computeODRHash(); + unsigned getODRHash() { return data().ODRHash; } + /// \brief Sets the base classes of this struct or class. void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases); Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -422,6 +422,7 @@ /// written in the source. void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, bool Canonical) const; + void ODRProfile(llvm::FoldingSetNodeID &ID) const; }; /// DeclStmt - Adaptor class for mixing declarations with statements and Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -16,6 +16,7 @@ #include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/TypeLoc.h" @@ -71,8 +72,8 @@ ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false), - IsParsingBaseSpecifiers(false), NumBases(0), NumVBases(0), Bases(), - VBases(), Definition(D), FirstFriend() {} + IsParsingBaseSpecifiers(false), ODRHash(0), NumBases(0), NumVBases(0), + Bases(), VBases(), Definition(D), FirstFriend() {} CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { return Bases.get(Definition->getASTContext().getExternalSource()); @@ -371,6 +372,253 @@ data().IsParsingBaseSpecifiers = false; } +// This DeclVisitor computes a hash for the common Decl types +// encountered from a CXXRecordDecl. +class ODRDeclVisitor : public DeclVisitor { + typedef DeclVisitor Inherited; + llvm::FoldingSetNodeID &ID; + +public: + ODRDeclVisitor(llvm::FoldingSetNodeID &ID) + : ID(ID) {} + + unsigned ComputeHash() { return ID.ComputeHash(); } + + void Visit(Decl *D) { + if (!D) + return; + if (D->isImplicit()) + return; + if (D->isInvalidDecl()) + return; + ID.AddInteger(D->getKind()); + ID.AddInteger(D->hasAttrs()); + + if (auto *DC = dyn_cast(D)) + for (auto *SubD : DC->decls()) + Visit(SubD); + + Inherited::Visit(D); + } + + void VisitLabelDecl(LabelDecl *D) { + Inherited::VisitLabelDecl(D); + } + + void VisitEnumDecl(EnumDecl *D) { + ID.AddInteger(D->isFixed()); + if (D->isFixed()) + VisitType(D->getIntegerType()); + ID.AddInteger(D->isScoped()); + ID.AddInteger(D->isScopedUsingClassTag()); + + Inherited::VisitEnumDecl(D); + } + + void VisitEnumConstantDecl(EnumConstantDecl *D) { + if (auto *E = D->getInitExpr()) + VisitStmt(E); + + Inherited::VisitEnumConstantDecl(D); + } + + void VisitNamedDecl(NamedDecl *D) { + if (IdentifierInfo *II = D->getIdentifier()) { + ID.AddString(II->getName()); + } + Inherited::VisitNamedDecl(D); + } + + void VisitValueDecl(ValueDecl *D) { + VisitType(D->getType()); + Inherited::VisitValueDecl(D); + } + + void VisitAccessSpecDecl(AccessSpecDecl *D) { + ID.AddInteger(D->getAccess()); + Inherited::VisitAccessSpecDecl(D); + } + + void VisitFriendDecl(FriendDecl *D) { + if (TypeSourceInfo *TSI = D->getFriendType()) + VisitType(TSI->getType()); + unsigned NumLists = D->getFriendTypeNumTemplateParameterLists(); + ID.AddInteger(NumLists); + for (unsigned i = 0; i < NumLists; ++i) + VisitTemplateParameterList(D->getFriendTypeTemplateParameterList(i)); + Inherited::VisitFriendDecl(D); + } + + void VisitStaticAssertDecl(StaticAssertDecl *D) { + VisitStmt(D->getAssertExpr()); + ID.AddString(D->getMessage()->getString()); + Inherited::VisitStaticAssertDecl(D); + } + + void VisitTypedefNameDecl(TypedefNameDecl *D) { + VisitType(D->getUnderlyingType()); + + Inherited::VisitTypedefNameDecl(D); + } + + void VisitFunctionDecl(FunctionDecl *D) { + if (D->hasBody()) + VisitStmt(D->getBody()); + else + ID.AddInteger(false); + + ID.AddInteger(D->getStorageClass()); + ID.AddInteger(D->isInlineSpecified()); + ID.AddInteger(D->isVirtualAsWritten()); + ID.AddInteger(D->isPure()); + ID.AddInteger(D->isDeletedAsWritten()); + + Inherited::VisitFunctionDecl(D); + } + + void VisitCXXMethodDecl(CXXMethodDecl *D) { + ID.AddInteger(D->isStatic()); + ID.AddInteger(D->isInstance()); + ID.AddInteger(D->isConst()); + ID.AddInteger(D->isVolatile()); + Inherited::VisitCXXMethodDecl(D); + } + + void VisitCXXConstructorDecl(CXXConstructorDecl *D) { + ID.AddInteger(D->isExplicitSpecified()); + ID.AddInteger(D->getNumCtorInitializers()); + for (auto Initializer : D->inits()) { + if (Initializer->isWritten()) { + Initializer->getInit()->ODRProfile(ID); + } + } + Inherited::VisitCXXConstructorDecl(D); + } + + + void VisitCXXConversionDecl(CXXConversionDecl *D) { + VisitType(D->getConversionType()); + ID.AddInteger(D->isExplicitSpecified()); + Inherited::VisitCXXConversionDecl(D); + } + + void VisitFieldDecl(FieldDecl *D) { + ID.AddInteger(D->isMutable()); + ID.AddInteger(D->isBitField()); + if (D->isBitField()) + D->getBitWidth()->ODRProfile(ID); + Inherited::VisitFieldDecl(D); + } + + void VisitTemplateDecl(TemplateDecl *D) { + if (NamedDecl *ND = D->getTemplatedDecl()) + Visit(ND); + auto *Parameters = D->getTemplateParameters(); + ID.AddInteger(Parameters->size()); + for (auto *ND : *Parameters) + Visit(ND); + Inherited::VisitTemplateDecl(D); + } + + void VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { + Inherited::VisitFunctionTemplateDecl(D); + } + + void VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + if (D->hasDefaultArgument()) + VisitType(D->getDefaultArgument()); + Inherited::VisitTemplateTypeParmDecl(D); + } + + void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { + if (D->hasDefaultArgument()) + D->getDefaultArgument()->ODRProfile(ID); + Inherited::VisitNonTypeTemplateParmDecl(D); + } + + void VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { + if (D->hasDefaultArgument()) + VisitTemplateArgument(D->getDefaultArgument().getArgument()); + Inherited::VisitTemplateTemplateParmDecl(D); + } + + void VisitStmt(Stmt *S) { + if (!S) + return; + S->ODRProfile(ID); + } + + void VisitTemplateParameterList(TemplateParameterList *TPL) { + if (!TPL) + return; + for (auto * ND : TPL->asArray()) { + Visit(ND); + } + } + + void VisitTemplateArgument(const TemplateArgument& TA) { + switch (TA.getKind()) { + case TemplateArgument::Null: + llvm_unreachable("Require valid TemplateArgument"); + case TemplateArgument::Type: + VisitType(TA.getAsType()); + return; + case TemplateArgument::Declaration: + Visit(TA.getAsDecl()); + return; + case TemplateArgument::NullPtr: + ID.AddInteger(0); + return; + case TemplateArgument::Integral: + TA.getAsIntegral().Profile(ID); + VisitType(TA.getIntegralType()); + break; + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + Visit(TA.getAsTemplateOrTemplatePattern().getAsTemplateDecl()); + break; + case TemplateArgument::Expression: + TA.getAsExpr()->ODRProfile(ID); + break; + case TemplateArgument::Pack: + ID.AddInteger(TA.pack_size()); + for (auto SubTA : TA.pack_elements()) + VisitTemplateArgument(SubTA); + break; + } + } + + void VisitType(QualType T) { + // TODO: String type is used instead of a proper hash for the type. + // Eventually, a hash built from a type visitor should be used. + LangOptions options; + PrintingPolicy policy(options); + policy.AnonymousTagLocations = false; + ID.AddString(T.getAsString(policy)); + } +}; + +void CXXRecordDecl::computeODRHash() { + if (!DefinitionData) + return; + llvm::FoldingSetNodeID ID; + ODRDeclVisitor Visitor(ID); + for (auto *D : decls()) { + Visitor.Visit(D); + } + + for (auto base : bases()) { + ID.AddInteger(base.isVirtual()); + Visitor.VisitType(base.getType()); + } + + if (ClassTemplateDecl *TD = getDescribedClassTemplate()) { + Visitor.VisitTemplateParameterList(TD->getTemplateParameters()); + } + + DefinitionData->ODRHash = ID.ComputeHash(); +} + void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) { // C++11 [class.copy]p11: // A defaulted copy/move constructor for a class X is defined as Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -15,6 +15,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" @@ -25,14 +26,15 @@ namespace { class StmtProfiler : public ConstStmtVisitor { + protected: llvm::FoldingSetNodeID &ID; - const ASTContext &Context; bool Canonical; public: - StmtProfiler(llvm::FoldingSetNodeID &ID, const ASTContext &Context, - bool Canonical) - : ID(ID), Context(Context), Canonical(Canonical) { } + StmtProfiler(llvm::FoldingSetNodeID &ID, bool Canonical) + : ID(ID), Canonical(Canonical) {} + + virtual ~StmtProfiler() {} void VisitStmt(const Stmt *S); @@ -41,22 +43,25 @@ /// \brief Visit a declaration that is referenced within an expression /// or statement. - void VisitDecl(const Decl *D); + virtual void VisitDecl(const Decl *D) = 0; /// \brief Visit a type that is referenced within an expression or /// statement. - void VisitType(QualType T); + virtual void VisitType(QualType T) = 0; /// \brief Visit a name that occurs within an expression or statement. - void VisitName(DeclarationName Name); + virtual void VisitName(DeclarationName Name) = 0; + + /// \brief Visit identifiers that are not in Decl's or Type's. + virtual void VisitIdentifierInfo(IdentifierInfo *II) = 0; /// \brief Visit a nested-name-specifier that occurs within an expression /// or statement. - void VisitNestedNameSpecifier(NestedNameSpecifier *NNS); + virtual void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) = 0; /// \brief Visit a template name that occurs within an expression or /// statement. - void VisitTemplateName(TemplateName Name); + virtual void VisitTemplateName(TemplateName Name) = 0; /// \brief Visit template arguments that occur within an expression or /// statement. @@ -66,6 +71,134 @@ /// \brief Visit a single template argument. void VisitTemplateArgument(const TemplateArgument &Arg); }; + + class StmtProfilerWithPointers : public StmtProfiler { + const ASTContext &Context; + + public: + StmtProfilerWithPointers(llvm::FoldingSetNodeID &ID, + const ASTContext &Context, bool Canonical) + : StmtProfiler(ID, Canonical), Context(Context) {} + private: + void VisitDecl(const Decl *D) override { + ID.AddInteger(D ? D->getKind() : 0); + + if (Canonical && D) { + if (const NonTypeTemplateParmDecl *NTTP = + dyn_cast(D)) { + ID.AddInteger(NTTP->getDepth()); + ID.AddInteger(NTTP->getIndex()); + ID.AddBoolean(NTTP->isParameterPack()); + VisitType(NTTP->getType()); + return; + } + + if (const ParmVarDecl *Parm = dyn_cast(D)) { + // The Itanium C++ ABI uses the type, scope depth, and scope + // index of a parameter when mangling expressions that involve + // function parameters, so we will use the parameter's type for + // establishing function parameter identity. That way, our + // definition of "equivalent" (per C++ [temp.over.link]) is at + // least as strong as the definition of "equivalent" used for + // name mangling. + VisitType(Parm->getType()); + ID.AddInteger(Parm->getFunctionScopeDepth()); + ID.AddInteger(Parm->getFunctionScopeIndex()); + return; + } + + if (const TemplateTypeParmDecl *TTP = + dyn_cast(D)) { + ID.AddInteger(TTP->getDepth()); + ID.AddInteger(TTP->getIndex()); + ID.AddBoolean(TTP->isParameterPack()); + return; + } + + if (const TemplateTemplateParmDecl *TTP = + dyn_cast(D)) { + ID.AddInteger(TTP->getDepth()); + ID.AddInteger(TTP->getIndex()); + ID.AddBoolean(TTP->isParameterPack()); + return; + } + } + + ID.AddPointer(D ? D->getCanonicalDecl() : nullptr); + } + + void VisitType(QualType T) override { + if (Canonical) + T = Context.getCanonicalType(T); + + ID.AddPointer(T.getAsOpaquePtr()); + } + + void VisitName(DeclarationName Name) override { + ID.AddPointer(Name.getAsOpaquePtr()); + } + + void VisitIdentifierInfo(IdentifierInfo *II) override { + ID.AddPointer(II); + } + + void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override { + if (Canonical) + NNS = Context.getCanonicalNestedNameSpecifier(NNS); + ID.AddPointer(NNS); + } + + void VisitTemplateName(TemplateName Name) override { + if (Canonical) + Name = Context.getCanonicalTemplateName(Name); + + Name.Profile(ID); + } + }; + + class StmtProfilerWithoutPointers : public StmtProfiler { + public: + StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID) + : StmtProfiler(ID, false) {} + + private: + void VisitType(QualType T) override { + // TODO: String of type is used instead of a proper hash for the type. + // Eventually, a hash built from a type visitor should be used. + LangOptions options; + PrintingPolicy policy(options); + policy.AnonymousTagLocations = false; + ID.AddString(T.getAsString(policy)); + } + void VisitName(DeclarationName Name) override { + if (Name.isEmpty()) { + ID.AddInteger(0); + return; + } + auto Kind = Name.getNameKind(); + ID.AddInteger(Kind); + // TODO: Hash the relevant parts of DeclarationName instead of using + // the string. + SmallString<64> Buffer; + llvm::raw_svector_ostream StrOS(Buffer); + Name.print(StrOS, PrintingPolicy(LangOptions())); + ID.AddString(Buffer); + } + void VisitIdentifierInfo(IdentifierInfo *II) override { + ID.AddString(II->getName()); + } + void VisitDecl(const Decl *D) override { + ID.AddInteger(D ? D->getKind() : 0); + if (!D) { + return; + } + if (auto *ND = dyn_cast(D)) { + ID.AddString(ND->getNameAsString()); + } + } + void VisitTemplateName(TemplateName Name) override { } + void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override { } + }; } void StmtProfiler::VisitStmt(const Stmt *S) { @@ -775,7 +908,7 @@ break; case OffsetOfNode::Identifier: - ID.AddPointer(ON.getFieldName()); + VisitIdentifierInfo(ON.getFieldName()); break; case OffsetOfNode::Base: @@ -1355,7 +1488,7 @@ if (S->getDestroyedTypeInfo()) VisitType(S->getDestroyedType()); else - ID.AddPointer(S->getDestroyedTypeIdentifier()); + VisitIdentifierInfo(S->getDestroyedTypeIdentifier()); } void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) { @@ -1600,77 +1733,6 @@ ID.AddBoolean(S->getBridgeKind()); } -void StmtProfiler::VisitDecl(const Decl *D) { - ID.AddInteger(D? D->getKind() : 0); - - if (Canonical && D) { - if (const NonTypeTemplateParmDecl *NTTP = - dyn_cast(D)) { - ID.AddInteger(NTTP->getDepth()); - ID.AddInteger(NTTP->getIndex()); - ID.AddBoolean(NTTP->isParameterPack()); - VisitType(NTTP->getType()); - return; - } - - if (const ParmVarDecl *Parm = dyn_cast(D)) { - // The Itanium C++ ABI uses the type, scope depth, and scope - // index of a parameter when mangling expressions that involve - // function parameters, so we will use the parameter's type for - // establishing function parameter identity. That way, our - // definition of "equivalent" (per C++ [temp.over.link]) is at - // least as strong as the definition of "equivalent" used for - // name mangling. - VisitType(Parm->getType()); - ID.AddInteger(Parm->getFunctionScopeDepth()); - ID.AddInteger(Parm->getFunctionScopeIndex()); - return; - } - - if (const TemplateTypeParmDecl *TTP = - dyn_cast(D)) { - ID.AddInteger(TTP->getDepth()); - ID.AddInteger(TTP->getIndex()); - ID.AddBoolean(TTP->isParameterPack()); - return; - } - - if (const TemplateTemplateParmDecl *TTP = - dyn_cast(D)) { - ID.AddInteger(TTP->getDepth()); - ID.AddInteger(TTP->getIndex()); - ID.AddBoolean(TTP->isParameterPack()); - return; - } - } - - ID.AddPointer(D? D->getCanonicalDecl() : nullptr); -} - -void StmtProfiler::VisitType(QualType T) { - if (Canonical) - T = Context.getCanonicalType(T); - - ID.AddPointer(T.getAsOpaquePtr()); -} - -void StmtProfiler::VisitName(DeclarationName Name) { - ID.AddPointer(Name.getAsOpaquePtr()); -} - -void StmtProfiler::VisitNestedNameSpecifier(NestedNameSpecifier *NNS) { - if (Canonical) - NNS = Context.getCanonicalNestedNameSpecifier(NNS); - ID.AddPointer(NNS); -} - -void StmtProfiler::VisitTemplateName(TemplateName Name) { - if (Canonical) - Name = Context.getCanonicalTemplateName(Name); - - Name.Profile(ID); -} - void StmtProfiler::VisitTemplateArguments(const TemplateArgumentLoc *Args, unsigned NumArgs) { ID.AddInteger(NumArgs); @@ -1720,6 +1782,11 @@ void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, bool Canonical) const { - StmtProfiler Profiler(ID, Context, Canonical); + StmtProfilerWithPointers Profiler(ID, Context, Canonical); + Profiler.Visit(this); +} + +void Stmt::ODRProfile(llvm::FoldingSetNodeID &ID) const { + StmtProfilerWithoutPointers Profiler(ID); Profiler.Visit(this); } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -13142,8 +13142,11 @@ RD->completeDefinition(); } - if (isa(Tag)) + if (auto *RD = dyn_cast(Tag)) { FieldCollector->FinishClass(); + if (Context.getLangOpts().Modules) + RD->computeODRHash(); + } // Exit this scope of this tag's definition. PopDeclContext(); Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1489,6 +1489,7 @@ Data.ImplicitCopyAssignmentHasConstParam = Record[Idx++]; Data.HasDeclaredCopyConstructorWithConstParam = Record[Idx++]; Data.HasDeclaredCopyAssignmentWithConstParam = Record[Idx++]; + Data.ODRHash = Record[Idx++]; Data.NumBases = Record[Idx++]; if (Data.NumBases) @@ -1619,6 +1620,7 @@ OR_FIELD(HasDeclaredCopyConstructorWithConstParam) OR_FIELD(HasDeclaredCopyAssignmentWithConstParam) MATCH_FIELD(IsLambda) + MATCH_FIELD(ODRHash) #undef OR_FIELD #undef MATCH_FIELD Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -5511,6 +5511,7 @@ Record->push_back(Data.ImplicitCopyAssignmentHasConstParam); Record->push_back(Data.HasDeclaredCopyConstructorWithConstParam); Record->push_back(Data.HasDeclaredCopyAssignmentWithConstParam); + Record->push_back(Data.ODRHash); // IsLambda bit is already saved. Record->push_back(Data.NumBases); Index: test/Modules/Inputs/odr_hash/first.h =================================================================== --- test/Modules/Inputs/odr_hash/first.h +++ test/Modules/Inputs/odr_hash/first.h @@ -0,0 +1,204 @@ +struct S1 { + public: +}; + +struct S2Friend2 {}; +struct S2 { + friend S2Friend2; +}; + +template +struct S3Template {}; + +struct S3 { + friend S3Template; +}; + +struct S4 { + static_assert(1 == 1, "First"); +}; + +struct S5 { + static_assert(1 == 1, "Message"); +}; + +struct S6 { + int First(); +}; + +struct S7 { + double foo(); +}; + +struct S8 { + void foo(); +}; + +struct S9 { + void foo() { int y = 5; } +}; + +struct S10 { + struct { + int x; + } a; +}; + +struct S11 { + void foo() { int y = sizeof(int); } +}; + +struct S12 { + int x = sizeof(x); + int y = sizeof(x); +}; + +struct S13 { + template void foo(); +}; + +struct S14 { + template void foo(); +}; + +template +struct S15 : T { + void foo() { + int x = __builtin_offsetof(T, first); + } +}; + +struct S16 { + template class Y> + void foo() { + Y<> y; + } +}; + +struct S17 { + template