From f351ac8987fed7b99c840b4008c4dd42826f5bad Mon Sep 17 00:00:00 2001 From: Richard Trieu Date: Thu, 16 Feb 2017 04:53:40 +0000 Subject: [PATCH] Add better ODR checking for modules. Recommit r293585 that was reverted in r293611 with new fixes. The previous issue was determined to be an overly aggressive AST visitor from forward declared objects. The visitor will now only deeply visit certain Decl's and only do a shallow information extraction from all other Decl's. When objects are imported for modules, there is a chance that a name collision will cause an ODR violation. Previously, only a small number of such violations were detected. This patch provides a stronger check based on AST nodes. The information needed to uniquely identify an object is taken from the AST and put into a one-dimensional byte stream. This stream is then hashed to give a value to represent the object, which is stored with the other object data in the module. When modules are loaded, and Decl's are merged, the hash values of the two Decl's are compared. Only Decl's with matched hash values will be merged. Mismatch hashes will generate a module error, and if possible, point to the first difference between the two objects. The transform from AST to byte stream is a modified depth first algorithm. Due to references between some AST nodes, a pure depth first algorithm could generate loops. For Stmt nodes, a straight depth first processing occurs. For Type and Decl nodes, they are replaced with an index number and only on first visit will these nodes be processed. As an optimization, boolean values are saved and stored together in reverse order at the end of the byte stream to lower the ammount of data that needs to be hashed. Compile time impact was measured at 1.5-2.0% during module building, and negligible during builds without module building. Differential Revision: https://reviews.llvm.org/D21675 llvm-svn: 295284 --- clang/include/clang/AST/DeclCXX.h | 6 + clang/include/clang/AST/ODRHash.h | 82 ++ clang/include/clang/AST/Stmt.h | 3 + .../Basic/DiagnosticSerializationKinds.td | 64 + clang/lib/AST/CMakeLists.txt | 1 + clang/lib/AST/DeclCXX.cpp | 15 +- clang/lib/AST/ODRHash.cpp | 940 ++++++++++++++ clang/lib/AST/StmtProfile.cpp | 229 ++-- clang/lib/Sema/SemaDecl.cpp | 5 +- clang/lib/Serialization/ASTReader.cpp | 638 ++++++++- clang/lib/Serialization/ASTReaderDecl.cpp | 2 + clang/lib/Serialization/ASTWriter.cpp | 1 + clang/test/Modules/merge-using-decls.cpp | 4 + clang/test/Modules/odr_hash.cpp | 1156 +++++++++++++++++ 14 files changed, 3049 insertions(+), 97 deletions(-) create mode 100644 clang/include/clang/AST/ODRHash.h create mode 100644 clang/lib/AST/ODRHash.cpp create mode 100644 clang/test/Modules/odr_hash.cpp diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 020ebfb4af9b..c8db319f9d97 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -458,6 +458,9 @@ class CXXRecordDecl : public RecordDecl { /// \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 @@ public: return data().IsParsingBaseSpecifiers; } + void computeODRHash(); + unsigned getODRHash() const { return data().ODRHash; } + /// \brief Sets the base classes of this struct or class. void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases); diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h new file mode 100644 index 000000000000..f600a17865cd --- /dev/null +++ b/clang/include/clang/AST/ODRHash.h @@ -0,0 +1,82 @@ +//===-- ODRHash.h - Hashing to diagnose ODR failures ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the declaration of the ODRHash class, which calculates +/// a hash based on AST nodes, which is stable across different runs. +/// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Type.h" +#include "clang/AST/TemplateBase.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +class Decl; +class IdentifierInfo; +class NestedNameSpecifer; +class Stmt; +class TemplateParameterList; + +// ODRHash is used to calculate a hash based on AST node contents that +// does not rely on pointer addresses. This allows the hash to not vary +// between runs and is usable to detect ODR problems in modules. To use, +// construct an ODRHash object, then call Add* methods over the nodes that +// need to be hashed. Then call CalculateHash to get the hash value. +// Typically, only one Add* call is needed. clear can be called to reuse the +// object. +class ODRHash { + // Use DenseMaps to convert between Decl and Type pointers and an index value. + llvm::DenseMap DeclMap; + llvm::DenseMap TypeMap; + + // Save space by processing bools at the end. + llvm::SmallVector Bools; + + llvm::FoldingSetNodeID ID; + +public: + ODRHash() {} + + // Use this for ODR checking classes between modules. This method compares + // more information than the AddDecl class. + void AddCXXRecordDecl(const CXXRecordDecl *Record); + + // Process SubDecls of the main Decl. This method calls the DeclVisitor + // while AddDecl does not. + void AddSubDecl(const Decl *D); + + // Reset the object for reuse. + void clear(); + + // Add booleans to ID and uses it to calculate the hash. + unsigned CalculateHash(); + + // Add AST nodes that need to be processed. + void AddDecl(const Decl *D); + void AddType(const Type *T); + void AddQualType(QualType T); + void AddStmt(const Stmt *S); + void AddIdentifierInfo(const IdentifierInfo *II); + void AddNestedNameSpecifier(const NestedNameSpecifier *NNS); + void AddTemplateName(TemplateName Name); + void AddDeclarationName(DeclarationName Name); + void AddTemplateArgument(TemplateArgument TA); + void AddTemplateParameterList(const TemplateParameterList *TPL); + + // Save booleans until the end to lower the size of data to process. + void AddBoolean(bool value); +}; + +} // end namespace clang diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index e28675d6a828..defe6c2ac2f3 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -39,6 +39,7 @@ namespace clang { class Expr; class IdentifierInfo; class LabelDecl; + class ODRHash; class ParmVarDecl; class PrinterHelper; struct PrintingPolicy; @@ -436,6 +437,8 @@ public: /// written in the source. void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, bool Canonical) const; + + void ProcessODRHash(llvm::FoldingSetNodeID &ID, ODRHash& Hash) const; }; /// DeclStmt - Adaptor class for mixing declarations with statements and diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 066a1f5fa68f..9371b05c733a 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -117,6 +117,70 @@ def note_module_odr_violation_different_definitions : Note< def err_module_odr_violation_different_instantiations : Error< "instantiation of %q0 is different in different modules">; +def err_module_odr_violation_mismatch_decl : Error< + "%q0 has different definitions in different modules; first difference is " + "%select{definition in module '%2'|defined here}1 found " + "%select{end of class|public access specifier|private access specifier|" + "protected access specifier|friend declaration|enum|" + "static assert|typedef|type alias|method|constructor|destructor|" + "conversion operator|field|other}3">; +def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found " + "%select{end of class|public access specifier|private access specifier|" + "protected access specifier|friend declaration|enum|" + "static assert|typedef|type alias|method|constructor|destructor|" + "conversion operator|field|other}1">; + +def err_module_odr_violation_mismatch_decl_diff : Error< + "%q0 has different definitions in different modules; first difference is " + "%select{definition in module '%2'|defined here}1 found " + "%select{friend %4|enum %4|element %4 in enum %5|" + "element %4 in enum %5 with initializer|" + "element %4 in enum %5 with no initializer|" + "element %4 in enum %5 with initializer|" + "enum %4 has %5 element%s5|" + "static assert with condition|" + "static assert with message|" + "static assert with %select{|no}4 message|" + "%select{typedef|type alias}4 name %5|" + "method named %4|" + "method %4 is %select{non-|}5static|" + "method %4 is %select{not |}5inline|" + "method %4 is %select{not |}5const|" + "method %4 has %5 parameter%s5|" + "method %4 has %ordinal5 parameter %select{named %7|with no name}6|" + "method %4 has %ordinal5 parameter with type %6|" + "method %4 has %ordinal5 parameter with default argument|" + "method %4 has %ordinal5 parameter with %select{no |}6 default argument|" + "method %4 has %select{|no }5body|" + "method %4 has different body|" + "field %4|" + "%select{field|bitfield}5 %4|" + "%select{non-mutable|mutable}5 %4}3">; +def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found " + "%select{other friend %2|other enum %2|different element %2 in enum %3|" + "element %2 in enum %3 with initializer|" + "element %2 in enum %3 with no initializer|" + "element %2 in enum %3 with different initializer|" + "enum %2 has %3 element%s3|" + "static assert with different condition|" + "static assert with different message|" + "static assert with %select{|no}2 message|" + "different %select{typedef|type alias}2 name %3|" + "method named %2|" + "method %2 is %select{non-|}3static|" + "method %2 is %select{not |}3inline|" + "method %2 is %select{not |}3const|" + "method %2 has %3 parameter%s3|" + "method %2 has %ordinal3 parameter %select{named %5|with no name}4|" + "method %2 has %ordinal3 parameter with type %4|" + "method %2 has %ordinal3 parameter with different default argument|" + "method %2 has %ordinal3 parameter with %select{no |}4default argument|" + "method %2 has %select{|no }3body|" + "method %2 has different body|" + "field %2|" + "%select{field|bitfield}3 %2|" + "%select{non-mutable|mutable}3 %2}1">; + def warn_module_uses_date_time : Warning< "%select{precompiled header|module}0 uses __DATE__ or __TIME__">, InGroup>; diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index e28bd2e16d17..2e98f524da83 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -40,6 +40,7 @@ add_clang_library(clangAST MicrosoftMangle.cpp NestedNameSpecifier.cpp NSAPI.cpp + ODRHash.cpp OpenMPClause.cpp ParentMap.cpp RawCommentList.cpp diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index a9db65a51518..8fca00404c0f 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -18,6 +18,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/ADT/STLExtras.h" @@ -71,8 +72,8 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) 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,16 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, data().IsParsingBaseSpecifiers = false; } +void CXXRecordDecl::computeODRHash() { + if (!DefinitionData) + return; + + ODRHash Hash; + Hash.AddCXXRecordDecl(this); + + DefinitionData->ODRHash = Hash.CalculateHash(); +} + void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) { // C++11 [class.copy]p11: // A defaulted copy/move constructor for a class X is defined as diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp new file mode 100644 index 000000000000..870bf0ad9c17 --- /dev/null +++ b/clang/lib/AST/ODRHash.cpp @@ -0,0 +1,940 @@ +//===-- ODRHash.cpp - Hashing to diagnose ODR failures ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the ODRHash class, which calculates a hash based +/// on AST nodes, which is stable across different runs. +/// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ODRHash.h" + +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeVisitor.h" + +using namespace clang; + + +// Hashing for Stmt is with Stmt::Profile, since they derive from the same base +// class. +void ODRHash::AddStmt(const Stmt *S) { + assert(S && "Expecting non-null pointer."); + S->ProcessODRHash(ID, *this); +} + +void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) { + assert(II && "Expecting non-null pointer."); + ID.AddString(II->getName()); +} + +void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) { + assert(NNS && "Expecting non-null pointer."); + const auto *Prefix = NNS->getPrefix(); + AddBoolean(Prefix); + if (Prefix) + AddNestedNameSpecifier(Prefix); + + auto Kind = NNS->getKind(); + ID.AddInteger(Kind); + switch (Kind) { + case NestedNameSpecifier::Identifier: + AddIdentifierInfo(NNS->getAsIdentifier()); + break; + case NestedNameSpecifier::Namespace: + AddDecl(NNS->getAsNamespace()); + break; + case NestedNameSpecifier::NamespaceAlias: + AddDecl(NNS->getAsNamespaceAlias()); + break; + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + AddType(NNS->getAsType()); + break; + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Super: + break; + } +} + +void ODRHash::AddTemplateName(TemplateName Name) { + const auto Kind = Name.getKind(); + ID.AddInteger(Kind); + AddBoolean(Name.isDependent()); + AddBoolean(Name.isInstantiationDependent()); + switch (Kind) { + case TemplateName::Template: + AddDecl(Name.getAsTemplateDecl()); + break; + case TemplateName::OverloadedTemplate: { + const auto *Storage = Name.getAsOverloadedTemplate(); + ID.AddInteger(Storage->size()); + for (const auto *ND : *Storage) { + AddDecl(ND); + } + break; + } + case TemplateName::QualifiedTemplate: { + const auto *QTN = Name.getAsQualifiedTemplateName(); + AddNestedNameSpecifier(QTN->getQualifier()); + AddBoolean(QTN->hasTemplateKeyword()); + AddDecl(QTN->getDecl()); + break; + } + case TemplateName::DependentTemplate: { + const auto *DTN = Name.getAsDependentTemplateName(); + AddBoolean(DTN->isIdentifier()); + if (DTN->isIdentifier()) { + AddIdentifierInfo(DTN->getIdentifier()); + } else { + ID.AddInteger(DTN->getOperator()); + } + break; + } + case TemplateName::SubstTemplateTemplateParm: { + const auto *Storage = Name.getAsSubstTemplateTemplateParm(); + AddDecl(Storage->getParameter()); + AddTemplateName(Storage->getReplacement()); + break; + } + case TemplateName::SubstTemplateTemplateParmPack: { + const auto *Storage = Name.getAsSubstTemplateTemplateParmPack(); + AddDecl(Storage->getParameterPack()); + AddTemplateArgument(Storage->getArgumentPack()); + break; + } + } +} + +void ODRHash::AddDeclarationName(DeclarationName Name) { + AddBoolean(Name.isEmpty()); + if (Name.isEmpty()) + return; + + auto Kind = Name.getNameKind(); + ID.AddInteger(Kind); + switch (Kind) { + case DeclarationName::Identifier: + AddIdentifierInfo(Name.getAsIdentifierInfo()); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: { + Selector S = Name.getObjCSelector(); + AddBoolean(S.isNull()); + AddBoolean(S.isKeywordSelector()); + AddBoolean(S.isUnarySelector()); + unsigned NumArgs = S.getNumArgs(); + for (unsigned i = 0; i < NumArgs; ++i) { + AddIdentifierInfo(S.getIdentifierInfoForSlot(i)); + } + break; + } + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + AddQualType(Name.getCXXNameType()); + break; + case DeclarationName::CXXOperatorName: + ID.AddInteger(Name.getCXXOverloadedOperator()); + break; + case DeclarationName::CXXLiteralOperatorName: + AddIdentifierInfo(Name.getCXXLiteralIdentifier()); + break; + case DeclarationName::CXXConversionFunctionName: + AddQualType(Name.getCXXNameType()); + break; + case DeclarationName::CXXUsingDirective: + break; + case DeclarationName::CXXDeductionGuideName: { + auto *Template = Name.getCXXDeductionGuideTemplate(); + AddBoolean(Template); + if (Template) { + AddDecl(Template); + } + } + } +} + +void ODRHash::AddTemplateArgument(TemplateArgument TA) { + const auto Kind = TA.getKind(); + ID.AddInteger(Kind); + switch (Kind) { + case TemplateArgument::Null: + llvm_unreachable("Require valid TemplateArgument"); + case TemplateArgument::Type: + AddQualType(TA.getAsType()); + break; + case TemplateArgument::Declaration: + AddDecl(TA.getAsDecl()); + break; + case TemplateArgument::NullPtr: + AddQualType(TA.getNullPtrType()); + break; + case TemplateArgument::Integral: + TA.getAsIntegral().Profile(ID); + AddQualType(TA.getIntegralType()); + break; + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + AddTemplateName(TA.getAsTemplateOrTemplatePattern()); + break; + case TemplateArgument::Expression: + AddStmt(TA.getAsExpr()); + break; + case TemplateArgument::Pack: + ID.AddInteger(TA.pack_size()); + for (auto SubTA : TA.pack_elements()) + AddTemplateArgument(SubTA); + break; + } +} + +void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) { + assert(TPL && "Expecting non-null pointer."); + ID.AddInteger(TPL->size()); + for (auto *ND : TPL->asArray()) { + AddSubDecl(ND); + } +} + +void ODRHash::clear() { + DeclMap.clear(); + TypeMap.clear(); + Bools.clear(); + ID.clear(); +} + +unsigned ODRHash::CalculateHash() { + // Append the bools to the end of the data segment backwards. This allows + // for the bools data to be compressed 32 times smaller compared to using + // ID.AddBoolean + const unsigned unsigned_bits = sizeof(unsigned) * CHAR_BIT; + const unsigned size = Bools.size(); + const unsigned remainder = size % unsigned_bits; + const unsigned loops = size / unsigned_bits; + auto I = Bools.rbegin(); + unsigned value = 0; + for (unsigned i = 0; i < remainder; ++i) { + value <<= 1; + value |= *I; + ++I; + } + ID.AddInteger(value); + + for (unsigned i = 0; i < loops; ++i) { + value = 0; + for (unsigned j = 0; j < unsigned_bits; ++j) { + value <<= 1; + value |= *I; + ++I; + } + ID.AddInteger(value); + } + + assert(I == Bools.rend()); + Bools.clear(); + return ID.ComputeHash(); +} + +// Process a Decl pointer. Add* methods call back into ODRHash while Visit* +// methods process the relevant parts of the Decl. +class ODRDeclVisitor : public ConstDeclVisitor { + typedef ConstDeclVisitor Inherited; + llvm::FoldingSetNodeID &ID; + ODRHash &Hash; + +public: + ODRDeclVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash) + : ID(ID), Hash(Hash) {} + + void AddDecl(const Decl *D) { + Hash.AddBoolean(D); + if (D) { + Hash.AddDecl(D); + } + } + + void AddStmt(const Stmt *S) { + Hash.AddBoolean(S); + if (S) { + Hash.AddStmt(S); + } + } + + void AddQualType(QualType T) { + Hash.AddQualType(T); + } + + void AddIdentifierInfo(const IdentifierInfo *II) { + Hash.AddBoolean(II); + if (II) { + Hash.AddIdentifierInfo(II); + } + } + + void AddTemplateParameterList(TemplateParameterList *TPL) { + Hash.AddBoolean(TPL); + if (TPL) { + Hash.AddTemplateParameterList(TPL); + } + } + + void AddTemplateArgument(TemplateArgument TA) { + Hash.AddTemplateArgument(TA); + } + + void Visit(const Decl *D) { + if (!D) + return; + if (D->isImplicit()) + return; + if (D->isInvalidDecl()) + return; + ID.AddInteger(D->getKind()); + Hash.AddBoolean(D->hasAttrs()); + + Inherited::Visit(D); + } + + void VisitDecl(const Decl *D) { + Inherited::VisitDecl(D); + } + + void VisitLabelDecl(const LabelDecl *D) { + Inherited::VisitLabelDecl(D); + } + + void VisitEnumDecl(const EnumDecl *D) { + const bool isFixed = D->isFixed(); + Hash.AddBoolean(isFixed); + if (isFixed) + AddQualType(D->getIntegerType()); + Hash.AddBoolean(D->isScoped()); + Hash.AddBoolean(D->isScopedUsingClassTag()); + + // TODO: Enums should have their own ODR hash. + for (auto *SubDecl : D->decls()) { + Hash.AddSubDecl(SubDecl); + } + + Inherited::VisitEnumDecl(D); + } + + void VisitEnumConstantDecl(const EnumConstantDecl *D) { + auto *E = D->getInitExpr(); + AddStmt(E); + + Inherited::VisitEnumConstantDecl(D); + } + + void VisitNamedDecl(const NamedDecl *D) { + AddIdentifierInfo(D->getIdentifier()); + Inherited::VisitNamedDecl(D); + } + + void VisitValueDecl(const ValueDecl *D) { + AddQualType(D->getType()); + Inherited::VisitValueDecl(D); + } + + void VisitParmVarDecl(const ParmVarDecl *D) { + AddStmt(D->getDefaultArg()); + Inherited::VisitParmVarDecl(D); + } + + void VisitAccessSpecDecl(const AccessSpecDecl *D) { + ID.AddInteger(D->getAccess()); + Inherited::VisitAccessSpecDecl(D); + } + + void VisitFriendDecl(const FriendDecl *D) { + TypeSourceInfo *TSI = D->getFriendType(); + Hash.AddBoolean(TSI); + if (TSI) + AddQualType(TSI->getType()); + else + AddDecl(D->getFriendDecl()); + + unsigned NumLists = D->getFriendTypeNumTemplateParameterLists(); + ID.AddInteger(NumLists); + for (unsigned i = 0; i < NumLists; ++i) + AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i)); + Inherited::VisitFriendDecl(D); + } + + void VisitStaticAssertDecl(const StaticAssertDecl *D) { + AddStmt(D->getAssertExpr()); + AddStmt(D->getMessage()); + + Inherited::VisitStaticAssertDecl(D); + } + + void VisitTypedefNameDecl(const TypedefNameDecl *D) { + AddQualType(D->getUnderlyingType()); + + Inherited::VisitTypedefNameDecl(D); + } + + void VisitFunctionDecl(const FunctionDecl *D) { + // TODO: Functions should have their own ODR hashes. + AddStmt(D->getBody()); + + ID.AddInteger(D->getStorageClass()); + Hash.AddBoolean(D->isInlineSpecified()); + Hash.AddBoolean(D->isVirtualAsWritten()); + Hash.AddBoolean(D->isPure()); + Hash.AddBoolean(D->isDeletedAsWritten()); + ID.AddInteger(D->getOverloadedOperator()); + Inherited::VisitFunctionDecl(D); + } + + void VisitCXXMethodDecl(const CXXMethodDecl *D) { + Hash.AddBoolean(D->isStatic()); + Hash.AddBoolean(D->isInstance()); + Hash.AddBoolean(D->isConst()); + Hash.AddBoolean(D->isVolatile()); + Inherited::VisitCXXMethodDecl(D); + } + + void VisitCXXConstructorDecl(const CXXConstructorDecl *D) { + Hash.AddBoolean(D->isExplicitSpecified()); + unsigned NumCtorInits = 0; + llvm::SmallVector Initializers; + ID.AddInteger(D->getNumCtorInitializers()); + for (auto Initializer : D->inits()) { + if (Initializer->isWritten()) { + ++NumCtorInits; + Initializers.push_back(Initializer); + } + } + for (auto Initializer : Initializers) { + AddStmt(Initializer->getInit()); + } + + Inherited::VisitCXXConstructorDecl(D); + } + + void VisitCXXConversionDecl(const CXXConversionDecl *D) { + AddQualType(D->getConversionType()); + Hash.AddBoolean(D->isExplicitSpecified()); + Inherited::VisitCXXConversionDecl(D); + } + + void VisitFieldDecl(const FieldDecl *D) { + Hash.AddBoolean(D->isMutable()); + + const bool isBitField = D->isBitField(); + Hash.AddBoolean(isBitField); + if (isBitField) + AddStmt(D->getBitWidth()); + + AddStmt(D->getInClassInitializer()); + + Inherited::VisitFieldDecl(D); + } + + void VisitTemplateDecl(const TemplateDecl *D) { + AddDecl(D->getTemplatedDecl()); + + auto *Parameters = D->getTemplateParameters(); + ID.AddInteger(Parameters->size()); + for (auto *ND : *Parameters) + AddDecl(ND); + + Inherited::VisitTemplateDecl(D); + } + + void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { + Inherited::VisitFunctionTemplateDecl(D); + } + + void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) { + const bool hasDefaultArgument = D->hasDefaultArgument(); + Hash.AddBoolean(hasDefaultArgument); + if (hasDefaultArgument) + AddTemplateArgument(D->getDefaultArgument()); + + Inherited::VisitTemplateTypeParmDecl(D); + } + + void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) { + AddStmt(D->hasDefaultArgument() ? D->getDefaultArgument() : nullptr); + + Inherited::VisitNonTypeTemplateParmDecl(D); + } + + void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D) { + const bool hasDefaultArgument = D->hasDefaultArgument(); + Hash.AddBoolean(hasDefaultArgument); + if (hasDefaultArgument) + AddTemplateArgument(D->getDefaultArgument().getArgument()); + + Inherited::VisitTemplateTemplateParmDecl(D); + } + + void VisitCXXRecordDecl(const CXXRecordDecl *D) { + const bool hasDefinition = D->hasDefinition(); + Hash.AddBoolean(hasDefinition); + if (hasDefinition) { + ID.AddInteger(D->getODRHash()); + } + Inherited::VisitCXXRecordDecl(D); + }; +}; + + +void ODRHash::AddSubDecl(const Decl *D) { + assert(D && "Expecting non-null pointer."); + AddDecl(D); + ODRDeclVisitor(ID, *this).Visit(D); +} + +void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { + assert(Record && Record->hasDefinition() && + "Expected non-null record to be a definition."); + AddDecl(Record); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector Decls; + for (const Decl *SubDecl : Record->decls()) { + // Ignore implicit Decl's. + if (SubDecl->isImplicit()) { + continue; + } + // Ignore Decl's that are not in the context of the CXXRecordDecl. + if (SubDecl->getDeclContext() != Record) { + continue; + } + Decls.push_back(SubDecl); + } + + ID.AddInteger(Decls.size()); + for (auto SubDecl : Decls) { + AddSubDecl(SubDecl); + } + + ID.AddInteger(Record->getNumBases()); + for (auto base : Record->bases()) { + AddBoolean(base.isVirtual()); + AddQualType(base.getType()); + } + + const ClassTemplateDecl *TD = Record->getDescribedClassTemplate(); + AddBoolean(TD); + if (TD) { + AddTemplateParameterList(TD->getTemplateParameters()); + } +} + +void ODRHash::AddDecl(const Decl *D) { + assert(D && "Expecting non-null pointer."); + auto Result = DeclMap.insert(std::make_pair(D, DeclMap.size())); + ID.AddInteger(Result.first->second); + // On first encounter of a Decl pointer, process it. Every time afterwards, + // only the index value is needed. + if (!Result.second) { + return; + } + + // Unlike the DeclVisitor, this adds a limited amount of information to + // identify the Decl. + ID.AddInteger(D->getKind()); + + // Unlike other places where AddBoolean is used with possibly null pointers, + // the nullness of the following pointers is already encoded with the + // DeclKind value, so there is no ambiguity on what information is added. + if (const auto *ND = dyn_cast(D)) { + AddDeclarationName(ND->getDeclName()); + } + + if (const auto *Typedef = dyn_cast(D)) { + AddQualType(Typedef->getUnderlyingType()); + } + + if (const auto *Alias = dyn_cast(D)) { + AddDecl(Alias->getNamespace()); + } + + if (const auto *Template = dyn_cast(D)) { + AddTemplateParameterList(Template->getTemplateParameters()); + } +} + +// Process a Type pointer. Add* methods call back into ODRHash while Visit* +// methods process the relevant parts of the Type. +class ODRTypeVisitor : public TypeVisitor { + typedef TypeVisitor Inherited; + llvm::FoldingSetNodeID &ID; + ODRHash &Hash; + +public: + ODRTypeVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash) + : ID(ID), Hash(Hash) {} + + void AddType(const Type *T) { + Hash.AddType(T); + } + + void AddQualType(QualType T) { + Hash.AddQualType(T); + } + + void AddDecl(Decl *D) { + Hash.AddBoolean(D); + if (D) { + Hash.AddDecl(D); + } + } + + void AddTemplateArgument(TemplateArgument TA) { + Hash.AddTemplateArgument(TA); + } + + void AddStmt(Stmt *S) { + Hash.AddBoolean(S); + if (S) { + Hash.AddStmt(S); + } + } + + void AddNestedNameSpecifier(NestedNameSpecifier *NNS) { + Hash.AddBoolean(NNS); + if (NNS) { + Hash.AddNestedNameSpecifier(NNS); + } + } + void AddIdentiferInfo(const IdentifierInfo *II) { + Hash.AddBoolean(II); + if (II) { + Hash.AddIdentifierInfo(II); + } + } + + void AddTemplateName(TemplateName TN) { + Hash.AddTemplateName(TN); + } + + void VisitQualifiers(Qualifiers Quals) { + ID.AddInteger(Quals.getAsOpaqueValue()); + } + + void Visit(const Type *T) { + ID.AddInteger(T->getTypeClass()); + Inherited::Visit(T); + } + + void VisitType(const Type *T) {} + + void VisitAdjustedType(const AdjustedType *T) { + AddQualType(T->getOriginalType()); + AddQualType(T->getAdjustedType()); + VisitType(T); + } + + void VisitDecayedType(const DecayedType *T) { + AddQualType(T->getDecayedType()); + AddQualType(T->getPointeeType()); + VisitAdjustedType(T); + } + + void VisitArrayType(const ArrayType *T) { + AddQualType(T->getElementType()); + ID.AddInteger(T->getSizeModifier()); + VisitQualifiers(T->getIndexTypeQualifiers()); + VisitType(T); + } + void VisitConstantArrayType(const ConstantArrayType *T) { + T->getSize().Profile(ID); + VisitArrayType(T); + } + + void VisitDependentSizedArrayType(const DependentSizedArrayType *T) { + AddStmt(T->getSizeExpr()); + VisitArrayType(T); + } + + void VisitIncompleteArrayType(const IncompleteArrayType *T) { + VisitArrayType(T); + } + + void VisitVariableArrayType(const VariableArrayType *T) { + AddStmt(T->getSizeExpr()); + VisitArrayType(T); + } + + void VisitAtomicType(const AtomicType *T) { + AddQualType(T->getValueType()); + VisitType(T); + } + + void VisitAttributedType(const AttributedType *T) { + ID.AddInteger(T->getAttrKind()); + AddQualType(T->getModifiedType()); + AddQualType(T->getEquivalentType()); + VisitType(T); + } + + void VisitBlockPointerType(const BlockPointerType *T) { + AddQualType(T->getPointeeType()); + VisitType(T); + } + + void VisitBuiltinType(const BuiltinType *T) { + ID.AddInteger(T->getKind()); + VisitType(T); + } + + void VisitComplexType(const ComplexType *T) { + AddQualType(T->getElementType()); + VisitType(T); + } + + void VisitDecltypeType(const DecltypeType *T) { + AddQualType(T->getUnderlyingType()); + AddStmt(T->getUnderlyingExpr()); + VisitType(T); + } + + void VisitDependentSizedExtVectorType(const DependentSizedExtVectorType *T) { + AddQualType(T->getElementType()); + AddStmt(T->getSizeExpr()); + VisitType(T); + } + + void VisitFunctionType(const FunctionType *T) { + AddQualType(T->getReturnType()); + T->getExtInfo().Profile(ID); + Hash.AddBoolean(T->isConst()); + Hash.AddBoolean(T->isVolatile()); + Hash.AddBoolean(T->isRestrict()); + VisitType(T); + } + + void VisitFunctionNoProtoType(const FunctionNoProtoType *T) { + VisitFunctionType(T); + } + + void VisitFunctionProtoType(const FunctionProtoType *T) { + ID.AddInteger(T->getNumParams()); + for (auto ParamType : T->getParamTypes()) + AddQualType(ParamType); + + const auto &epi = T->getExtProtoInfo(); + ID.AddInteger(epi.Variadic); + ID.AddInteger(epi.TypeQuals); + ID.AddInteger(epi.RefQualifier); + ID.AddInteger(epi.ExceptionSpec.Type); + + if (epi.ExceptionSpec.Type == EST_Dynamic) { + for (QualType Ex : epi.ExceptionSpec.Exceptions) + AddQualType(Ex); + } else if (epi.ExceptionSpec.Type == EST_ComputedNoexcept && + epi.ExceptionSpec.NoexceptExpr) { + AddStmt(epi.ExceptionSpec.NoexceptExpr); + } else if (epi.ExceptionSpec.Type == EST_Uninstantiated || + epi.ExceptionSpec.Type == EST_Unevaluated) { + AddDecl(epi.ExceptionSpec.SourceDecl->getCanonicalDecl()); + } + if (epi.ExtParameterInfos) { + for (unsigned i = 0; i != T->getNumParams(); ++i) + ID.AddInteger(epi.ExtParameterInfos[i].getOpaqueValue()); + } + epi.ExtInfo.Profile(ID); + Hash.AddBoolean(epi.HasTrailingReturn); + + VisitFunctionType(T); + } + + void VisitInjectedClassNameType(const InjectedClassNameType *T) { + AddDecl(T->getDecl()); + VisitType(T); + } + + void VisitMemberPointerType(const MemberPointerType *T) { + AddQualType(T->getPointeeType()); + AddType(T->getClass()); + VisitType(T); + } + + void VisitObjCObjectPointerType(const ObjCObjectPointerType *T) { + AddQualType(T->getPointeeType()); + VisitType(T); + } + + void VisitObjCObjectType(const ObjCObjectType *T) { + QualType Base = T->getBaseType(); + Hash.AddBoolean(Base.getTypePtr() != T); + if (Base.getTypePtr() != T) + AddQualType(Base); + auto TypeArgs = T->getTypeArgsAsWritten(); + ID.AddInteger(TypeArgs.size()); + for (auto TypeArg : TypeArgs) + AddQualType(TypeArg); + ID.AddInteger(T->getNumProtocols()); + for (auto proto : T->quals()) + AddDecl(proto); + ID.AddInteger(T->isKindOfTypeAsWritten()); + VisitType(T); + } + + void VisitObjCInterfaceType(const ObjCInterfaceType *T) { + VisitObjCObjectType(T); + } + + void VisitObjCObjectTypeImpl(const ObjCObjectTypeImpl *T) { + VisitObjCObjectType(T); + } + + void VisitPackExpansionType(const PackExpansionType *T) { + AddQualType(T->getPattern()); + auto NumExpansions = T->getNumExpansions(); + Hash.AddBoolean(NumExpansions.hasValue()); + if (NumExpansions) + ID.AddInteger(*NumExpansions); + VisitType(T); + }; + + void VisitPointerType(const PointerType *T) { + AddQualType(T->getPointeeType()); + VisitType(T); + } + + void VisitReferenceType(const ReferenceType *T) { + AddQualType(T->getPointeeTypeAsWritten()); + VisitType(T); + } + + void VisitLValueReferenceType(const LValueReferenceType *T) { + VisitReferenceType(T); + } + + void VisitRValueReferenceType(const RValueReferenceType *T) { + VisitReferenceType(T); + } + + void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) { + AddQualType(T->getReplacementType()); + AddType(T->getReplacedParameter()); + VisitType(T); + } + + void + VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) { + AddType(T->getReplacedParameter()); + AddTemplateArgument(T->getArgumentPack()); + VisitType(T); + } + + void VisitTagType(const TagType *T) { + AddDecl(T->getDecl()); + Hash.AddBoolean(T->isBeingDefined()); + VisitType(T); + } + + void VisitEnumType(const EnumType *T) { + AddDecl(T->getDecl()); + VisitTagType(T); + } + + void VisitRecordType(const RecordType *T) { + AddDecl(T->getDecl()); + VisitTagType(T); + } + + void VisitTemplateSpecializationType(const TemplateSpecializationType *T) { + AddTemplateName(T->getTemplateName()); + ID.AddInteger(T->getNumArgs()); + for (auto I = T->begin(), E = T->end(); I != E; ++I) + AddTemplateArgument(*I); + VisitType(T); + } + + void VisitTemplateTypeParmType(const TemplateTypeParmType *T) { + ID.AddInteger(T->getDepth()); + ID.AddInteger(T->getIndex()); + Hash.AddBoolean(T->isParameterPack()); + AddDecl(T->getDecl()); + VisitType(T); + } + + void VisitTypedefType(const TypedefType *T) { + AddDecl(T->getDecl()); + VisitType(T); + } + + void VisitTypeOfExprType(const TypeOfExprType *T) { + AddStmt(T->getUnderlyingExpr()); + VisitType(T); + } + + void VisitDependentTypeOfExprType(const DependentTypeOfExprType *T) { + VisitTypeOfExprType(T); + } + + void VisitTypeWithKeyword(const TypeWithKeyword *T) { VisitType(T); } + + void VisitElaboratedType(const ElaboratedType *T) { + ID.AddInteger(T->getKeyword()); + AddNestedNameSpecifier(T->getQualifier()); + AddQualType(T->getNamedType()); + VisitTypeWithKeyword(T); + } + + void VisitUnaryTransformType(const UnaryTransformType *T) { + AddQualType(T->getBaseType()); + ID.AddInteger(T->getUTTKind()); + VisitType(T); + } + + void VisitDependentUnaryTransformType(const DependentUnaryTransformType *T) { + VisitUnaryTransformType(T); + } + + void VisitUnresolvedUsingType(const UnresolvedUsingType *T) { + AddDecl(T->getDecl()); + VisitType(T); + } + + void VisitVectorType(const VectorType *T) { + AddQualType(T->getElementType()); + ID.AddInteger(T->getNumElements()); + ID.AddInteger(T->getVectorKind()); + VisitType(T); + } + + void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); } +}; + +void ODRHash::AddType(const Type *T) { + assert(T && "Expecting non-null pointer."); + auto Result = TypeMap.insert(std::make_pair(T, TypeMap.size())); + ID.AddInteger(Result.first->second); + // On first encounter of a Type pointer, process it. Every time afterwards, + // only the index value is needed. + if (!Result.second) { + return; + } + + ODRTypeVisitor(ID, *this).Visit(T); +} + +void ODRHash::AddQualType(QualType T) { + AddBoolean(T.isNull()); + if (T.isNull()) + return; + SplitQualType split = T.split(); + ID.AddInteger(split.Quals.getAsOpaqueValue()); + AddType(split.Ty); +} + +void ODRHash::AddBoolean(bool Value) { + Bools.push_back(Value); +} diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 49e43de638a0..1ac2a5bf5e1d 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -19,20 +19,22 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/StmtVisitor.h" #include "llvm/ADT/FoldingSet.h" using namespace clang; 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 @@ namespace { /// \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,127 @@ namespace { /// \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 { + ODRHash &Hash; + public: + StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID, ODRHash &Hash) + : StmtProfiler(ID, false), Hash(Hash) {} + + private: + void VisitType(QualType T) override { + Hash.AddQualType(T); + } + + void VisitName(DeclarationName Name) override { + Hash.AddDeclarationName(Name); + } + void VisitIdentifierInfo(IdentifierInfo *II) override { + ID.AddBoolean(II); + if (II) { + Hash.AddIdentifierInfo(II); + } + } + void VisitDecl(const Decl *D) override { + ID.AddBoolean(D); + if (D) { + Hash.AddDecl(D); + } + } + void VisitTemplateName(TemplateName Name) override { + Hash.AddTemplateName(Name); + } + void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override { + ID.AddBoolean(NNS); + if (NNS) { + Hash.AddNestedNameSpecifier(NNS); + } + } + }; } void StmtProfiler::VisitStmt(const Stmt *S) { @@ -853,7 +979,7 @@ void StmtProfiler::VisitOffsetOfExpr(const OffsetOfExpr *S) { break; case OffsetOfNode::Identifier: - ID.AddPointer(ON.getFieldName()); + VisitIdentifierInfo(ON.getFieldName()); break; case OffsetOfNode::Base: @@ -861,7 +987,7 @@ void StmtProfiler::VisitOffsetOfExpr(const OffsetOfExpr *S) { break; } } - + VisitExpr(S); } @@ -1451,7 +1577,7 @@ StmtProfiler::VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *S) { if (S->getDestroyedTypeInfo()) VisitType(S->getDestroyedType()); else - ID.AddPointer(S->getDestroyedTypeIdentifier()); + VisitIdentifierInfo(S->getDestroyedTypeIdentifier()); } void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) { @@ -1701,77 +1827,6 @@ void StmtProfiler::VisitObjCAvailabilityCheckExpr( VisitExpr(S); } -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); @@ -1821,6 +1876,12 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) { 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::ProcessODRHash(llvm::FoldingSetNodeID &ID, + class ODRHash &Hash) const { + StmtProfilerWithoutPointers Profiler(ID, Hash); Profiler.Visit(this); } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 3905348d3ca0..27203c0296d5 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13775,8 +13775,11 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD, 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(); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 1b65ecbe98ab..e2486de4c23c 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -26,6 +26,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/RawCommentList.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLocVisitor.h" @@ -8895,21 +8896,638 @@ void ASTReader::diagnoseOdrViolations() { for (auto *RD : Merge.second) { // Multiple different declarations got merged together; tell the user // where they came from. - if (Merge.first != RD) { - // FIXME: Walk the definition, figure out what's different, - // and diagnose that. - if (!Diagnosed) { - std::string Module = getOwningModuleNameForDiagnostic(Merge.first); - Diag(Merge.first->getLocation(), - diag::err_module_odr_violation_different_definitions) - << Merge.first << Module.empty() << Module; - Diagnosed = true; + if (Merge.first == RD) + continue; + + llvm::SmallVector, 4> FirstHashes; + llvm::SmallVector, 4> SecondHashes; + ODRHash Hash; + for (auto D : Merge.first->decls()) { + if (D->isImplicit()) + continue; + Hash.clear(); + Hash.AddSubDecl(D); + FirstHashes.emplace_back(D, Hash.CalculateHash()); + } + for (auto D : RD->decls()) { + if (D->isImplicit()) + continue; + Hash.clear(); + Hash.AddSubDecl(D); + SecondHashes.emplace_back(D, Hash.CalculateHash()); + } + + // Used with err_module_odr_violation_mismatch_decl and + // note_module_odr_violation_mismatch_decl + enum { + EndOfClass, + PublicSpecifer, + PrivateSpecifer, + ProtectedSpecifer, + Friend, + Enum, + StaticAssert, + Typedef, + TypeAlias, + CXXMethod, + CXXConstructor, + CXXDestructor, + CXXConversion, + Field, + Other + } FirstDiffType = Other, + SecondDiffType = Other; + + auto DifferenceSelector = [](Decl *D) { + assert(D && "valid Decl required"); + switch (D->getKind()) { + default: + return Other; + case Decl::AccessSpec: + switch (D->getAccess()) { + case AS_public: + return PublicSpecifer; + case AS_private: + return PrivateSpecifer; + case AS_protected: + return ProtectedSpecifer; + case AS_none: + llvm_unreachable("Invalid access specifier"); + } + case Decl::Friend: + return Friend; + case Decl::Enum: + return Enum; + case Decl::StaticAssert: + return StaticAssert; + case Decl::Typedef: + return Typedef; + case Decl::TypeAlias: + return TypeAlias; + case Decl::CXXMethod: + return CXXMethod; + case Decl::CXXConstructor: + return CXXConstructor; + case Decl::CXXDestructor: + return CXXDestructor; + case Decl::CXXConversion: + return CXXConversion; + case Decl::Field: + return Field; } + }; + Decl *FirstDecl = nullptr; + Decl *SecondDecl = nullptr; + auto FirstIt = FirstHashes.begin(); + auto SecondIt = SecondHashes.begin(); + + // If there is a diagnoseable difference, FirstDiffType and + // SecondDiffType will not be Other and FirstDecl and SecondDecl will be + // filled in if not EndOfClass. + while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { + if (FirstIt->second == SecondIt->second) { + ++FirstIt; + ++SecondIt; + continue; + } + + FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; + SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; + + FirstDiffType = FirstDecl ? DifferenceSelector(FirstDecl) : EndOfClass; + SecondDiffType = + SecondDecl ? DifferenceSelector(SecondDecl) : EndOfClass; + + break; + } + + if (FirstDiffType == Other || SecondDiffType == Other) { + // Reaching this point means an unexpected Decl was encountered + // or no difference was detected. This causes a generic error + // message to be emitted. + std::string Module = getOwningModuleNameForDiagnostic(Merge.first); + Diag(Merge.first->getLocation(), + diag::err_module_odr_violation_different_definitions) + << Merge.first << Module.empty() << Module; + + Diag(RD->getLocation(), diag::note_module_odr_violation_different_definitions) - << getOwningModuleNameForDiagnostic(RD); + << getOwningModuleNameForDiagnostic(RD); + Diagnosed = true; + break; } + + std::string FirstModule = getOwningModuleNameForDiagnostic(Merge.first); + std::string SecondModule = getOwningModuleNameForDiagnostic(RD); + + if (FirstDiffType != SecondDiffType) { + SourceLocation FirstLoc; + SourceRange FirstRange; + if (FirstDiffType == EndOfClass) { + FirstLoc = Merge.first->getBraceRange().getEnd(); + } else { + FirstLoc = FirstIt->first->getLocation(); + FirstRange = FirstIt->first->getSourceRange(); + } + Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl) + << Merge.first << FirstModule.empty() << FirstModule << FirstRange + << FirstDiffType; + + SourceLocation SecondLoc; + SourceRange SecondRange; + if (SecondDiffType == EndOfClass) { + SecondLoc = RD->getBraceRange().getEnd(); + } else { + SecondLoc = SecondDecl->getLocation(); + SecondRange = SecondDecl->getSourceRange(); + } + Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl) + << SecondModule << SecondRange << SecondDiffType; + Diagnosed = true; + break; + } + + // Used with err_module_odr_violation_mismatch_decl_diff and + // note_module_odr_violation_mismatch_decl_diff + enum ODRDeclDifference{ + FriendName, + EnumName, + EnumConstantName, + EnumConstantInit, + EnumConstantNoInit, + EnumConstantDiffInit, + EnumNumberOfConstants, + StaticAssertCondition, + StaticAssertMessage, + StaticAssertOnlyMessage, + TypedefName, + MethodName, + MethodStatic, + MethodInline, + MethodConst, + MethodNumParams, + MethodParamName, + MethodParamType, + MethodDefaultArg, + MethodOnlyDefaultArg, + MethodOnlyBody, + MethodBody, + FieldName, + FieldSingleBitField, + FieldMutable, + }; + + // These lambdas have the common portions of the ODR diagnostics. This + // has the same return as Diag(), so addition parameters can be passed + // in with operator<< + auto ODRDiagError = [&Merge, &FirstModule, this]( + SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_mismatch_decl_diff) + << Merge.first << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto ODRDiagNote = [&SecondModule, this]( + SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << Range << DiffType; + }; + + auto ComputeODRHash = [&Hash](const Stmt* S) { + assert(S); + Hash.clear(); + Hash.AddStmt(S); + return Hash.CalculateHash(); + }; + + // At this point, both decls are of the same type. Dive down deeper into + // the Decl to determine where the first difference is located. + switch (FirstDiffType) { + case Friend: { + FriendDecl *FirstFriend = cast(FirstDecl); + FriendDecl *SecondFriend = cast(SecondDecl); + { + auto D = ODRDiagError(FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendName); + if (TypeSourceInfo *FirstTSI = FirstFriend->getFriendType()) + D << FirstTSI->getType(); + else + D << FirstFriend->getFriendDecl(); + } + { + auto D = ODRDiagNote(SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendName); + if (TypeSourceInfo *SecondTSI = SecondFriend->getFriendType()) + D << SecondTSI->getType(); + else + D << SecondFriend->getFriendDecl(); + } + Diagnosed = true; + break; + } + case Enum: { + EnumDecl *FirstEnum = cast(FirstDecl); + EnumDecl *SecondEnum = cast(SecondDecl); + if (FirstEnum->getName() != SecondEnum->getName()) { + ODRDiagError(FirstEnum->getLocStart(), FirstEnum->getSourceRange(), + EnumName) + << FirstEnum; + ODRDiagNote(SecondEnum->getLocStart(), SecondEnum->getSourceRange(), + EnumName) + << SecondEnum; + Diagnosed = true; + break; + } + + // Don't use EnumDecl::enumerator_{begin,end}. Decl merging can + // cause the iterators from them to be the same for both Decl's. + EnumDecl::enumerator_iterator FirstEnumIt(FirstEnum->decls_begin()); + EnumDecl::enumerator_iterator FirstEnumEnd(FirstEnum->decls_end()); + EnumDecl::enumerator_iterator SecondEnumIt(SecondEnum->decls_begin()); + EnumDecl::enumerator_iterator SecondEnumEnd(SecondEnum->decls_end()); + int NumElements = 0; + for (; FirstEnumIt != FirstEnumEnd && SecondEnumIt != SecondEnumEnd; + ++FirstEnumIt, ++SecondEnumIt, ++NumElements) { + if (FirstEnumIt->getName() != SecondEnumIt->getName()) { + ODRDiagError(FirstEnumIt->getLocStart(), + FirstEnumIt->getSourceRange(), EnumConstantName) + << *FirstEnumIt << FirstEnum; + ODRDiagNote(SecondEnumIt->getLocStart(), + SecondEnumIt->getSourceRange(), EnumConstantName) + << *SecondEnumIt << SecondEnum; + Diagnosed = true; + break; + } + Expr *FirstInit = FirstEnumIt->getInitExpr(); + Expr *SecondInit = SecondEnumIt->getInitExpr(); + + if (FirstInit && !SecondInit) { + ODRDiagError(FirstEnumIt->getLocStart(), + FirstEnumIt->getSourceRange(), EnumConstantInit) + << *FirstEnumIt << FirstEnum; + + ODRDiagNote(SecondEnumIt->getLocStart(), + SecondEnumIt->getSourceRange(), EnumConstantNoInit) + << *SecondEnumIt << SecondEnum; + Diagnosed = true; + break; + } + + if (!FirstInit && SecondInit) { + ODRDiagError(FirstEnumIt->getLocStart(), + FirstEnumIt->getSourceRange(), EnumConstantNoInit) + << *FirstEnumIt << FirstEnum; + ODRDiagNote(SecondEnumIt->getLocStart(), + SecondEnumIt->getSourceRange(), EnumConstantInit) + << *SecondEnumIt << SecondEnum; + Diagnosed = true; + break; + } + + if (FirstInit == SecondInit) + continue; + + unsigned FirstODRHash = ComputeODRHash(FirstInit); + unsigned SecondODRHash = ComputeODRHash(SecondInit); + + if (FirstODRHash != SecondODRHash) { + ODRDiagError(FirstEnumIt->getLocStart(), + FirstEnumIt->getSourceRange(), EnumConstantDiffInit) + << *FirstEnumIt << FirstEnum; + ODRDiagNote(SecondEnumIt->getLocStart(), + SecondEnumIt->getSourceRange(), EnumConstantDiffInit) + << *SecondEnumIt << SecondEnum; + Diagnosed = true; + break; + } + } + + if (FirstEnumIt == FirstEnumEnd && SecondEnumIt != SecondEnumEnd) { + unsigned FirstEnumSize = NumElements; + unsigned SecondEnumSize = NumElements; + for (; SecondEnumIt != SecondEnumEnd; ++SecondEnumIt) + ++SecondEnumSize; + ODRDiagError(FirstEnum->getLocStart(), FirstEnum->getSourceRange(), + EnumNumberOfConstants) + << FirstEnum << FirstEnumSize; + ODRDiagNote(SecondEnum->getLocStart(), SecondEnum->getSourceRange(), + EnumNumberOfConstants) + << SecondEnum << SecondEnumSize; + Diagnosed = true; + break; + } + + if (FirstEnumIt != FirstEnumEnd && SecondEnumIt == SecondEnumEnd) { + unsigned FirstEnumSize = NumElements; + unsigned SecondEnumSize = NumElements; + for (; FirstEnumIt != FirstEnumEnd; ++FirstEnumIt) + ++FirstEnumSize; + ODRDiagError(FirstEnum->getLocStart(), FirstEnum->getSourceRange(), + EnumNumberOfConstants) + << FirstEnum << FirstEnumSize; + ODRDiagNote(SecondEnum->getLocStart(), SecondEnum->getSourceRange(), + EnumNumberOfConstants) + << SecondEnum << SecondEnumSize; + Diagnosed = true; + break; + } + + break; + } + case StaticAssert: { + StaticAssertDecl *FirstSA = cast(FirstDecl); + StaticAssertDecl *SecondSA = cast(SecondDecl); + + Expr *FirstExpr = FirstSA->getAssertExpr(); + Expr *SecondExpr = SecondSA->getAssertExpr(); + unsigned FirstODRHash = ComputeODRHash(FirstExpr); + unsigned SecondODRHash = ComputeODRHash(SecondExpr); + if (FirstODRHash != SecondODRHash) { + ODRDiagError(FirstExpr->getLocStart(), FirstExpr->getSourceRange(), + StaticAssertCondition); + ODRDiagNote(SecondExpr->getLocStart(), + SecondExpr->getSourceRange(), StaticAssertCondition); + Diagnosed = true; + break; + } + + StringLiteral *FirstStr = FirstSA->getMessage(); + StringLiteral *SecondStr = SecondSA->getMessage(); + if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) { + SourceLocation FirstLoc, SecondLoc; + SourceRange FirstRange, SecondRange; + if (FirstStr) { + FirstLoc = FirstStr->getLocStart(); + FirstRange = FirstStr->getSourceRange(); + } else { + FirstLoc = FirstSA->getLocStart(); + FirstRange = FirstSA->getSourceRange(); + } + if (SecondStr) { + SecondLoc = SecondStr->getLocStart(); + SecondRange = SecondStr->getSourceRange(); + } else { + SecondLoc = SecondSA->getLocStart(); + SecondRange = SecondSA->getSourceRange(); + } + ODRDiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage) + << (FirstStr == nullptr); + ODRDiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage) + << (SecondStr == nullptr); + Diagnosed = true; + break; + } + + if (FirstStr && SecondStr && + FirstStr->getString() != SecondStr->getString()) { + ODRDiagError(FirstStr->getLocStart(), FirstStr->getSourceRange(), + StaticAssertMessage); + ODRDiagNote(SecondStr->getLocStart(), SecondStr->getSourceRange(), + StaticAssertMessage); + Diagnosed = true; + break; + } + break; + } + case Typedef: + case TypeAlias: { + TypedefNameDecl *FirstTD = cast(FirstDecl); + TypedefNameDecl *SecondTD = cast(SecondDecl); + IdentifierInfo *FirstII = FirstTD->getIdentifier(); + IdentifierInfo *SecondII = SecondTD->getIdentifier(); + if (FirstII && SecondII && FirstII->getName() != SecondII->getName()) { + ODRDiagError(FirstTD->getLocation(), FirstTD->getSourceRange(), + TypedefName) + << (FirstDiffType == TypeAlias) << FirstII; + ODRDiagNote(SecondTD->getLocation(), SecondTD->getSourceRange(), + TypedefName) + << (FirstDiffType == TypeAlias) << SecondII; + Diagnosed = true; + break; + } + break; + } + case CXXMethod: + case CXXConstructor: + case CXXConversion: + case CXXDestructor: { + // TODO: Merge with existing method diff logic. + CXXMethodDecl *FirstMD = cast(FirstDecl); + CXXMethodDecl *SecondMD = cast(SecondDecl); + IdentifierInfo *FirstII = FirstMD->getIdentifier(); + IdentifierInfo *SecondII = SecondMD->getIdentifier(); + if (FirstII && SecondII && FirstII->getName() != SecondII->getName()) { + ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(), + MethodName) + << FirstII; + ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(), + MethodName) + << SecondII; + Diagnosed = true; + break; + } + + bool FirstStatic = FirstMD->getStorageClass() == SC_Static; + bool SecondStatic = SecondMD->getStorageClass() == SC_Static; + if (FirstStatic != SecondStatic) { + ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(), + MethodStatic) + << FirstMD << FirstStatic; + ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(), + MethodStatic) + << SecondMD << SecondStatic; + Diagnosed = true; + break; + } + + bool FirstInline = FirstMD->isInlineSpecified(); + bool SecondInline = SecondMD->isInlineSpecified(); + if (FirstInline != SecondInline) { + ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(), + MethodInline) + << FirstMD << FirstInline; + ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(), + MethodInline) + << SecondMD << SecondInline; + Diagnosed = true; + break; + } + + bool FirstConst = FirstMD->isConst(); + bool SecondConst = SecondMD->isConst(); + if (FirstConst != SecondConst) { + ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(), + MethodConst) + << FirstMD << FirstInline; + ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(), + MethodConst) + << SecondMD << SecondInline; + Diagnosed = true; + break; + } + + if (FirstMD->getNumParams() != SecondMD->getNumParams()) { + ODRDiagError(FirstMD->getLocation(), FirstMD->getSourceRange(), + MethodNumParams) + << SecondMD << FirstMD->getNumParams(); + ODRDiagNote(SecondMD->getLocation(), SecondMD->getSourceRange(), + MethodNumParams) + << SecondMD << SecondMD->getNumParams(); + Diagnosed = true; + break; + } + + for (unsigned i = 0, e = FirstMD->getNumParams(); i < e; ++i) { + ParmVarDecl *FirstParam = FirstMD->getParamDecl(i); + ParmVarDecl *SecondParam = SecondMD->getParamDecl(i); + IdentifierInfo *FirstII = FirstParam->getIdentifier(); + IdentifierInfo *SecondII = SecondParam->getIdentifier(); + if ((!FirstII && SecondII) || (FirstII && !SecondII) || + (FirstII && SecondII && + FirstII->getName() != SecondII->getName())) { + ODRDiagError(FirstParam->getLocation(), + FirstParam->getSourceRange(), MethodParamName) + << SecondMD << i + 1 << (FirstII == nullptr) << FirstII; + ODRDiagNote(SecondParam->getLocation(), + SecondParam->getSourceRange(), MethodParamName) + << SecondMD << i + 1 << (SecondII == nullptr) << SecondII; + Diagnosed = true; + break; + } + + if (FirstParam->getType() != SecondParam->getType()) { + ODRDiagError(FirstParam->getLocation(), + FirstParam->getSourceRange(), MethodParamType) + << SecondMD << i + 1 << FirstParam->getType(); + ODRDiagNote(SecondParam->getLocation(), + SecondParam->getSourceRange(), MethodParamType) + << SecondMD << i + 1 << SecondParam->getType(); + Diagnosed = true; + break; + } + + Expr *FirstDefaultArg = FirstParam->getDefaultArg(); + Expr *SecondDefaultArg = SecondParam->getDefaultArg(); + if ((!FirstDefaultArg && SecondDefaultArg) || + (FirstDefaultArg && !SecondDefaultArg)) { + ODRDiagError(FirstParam->getLocation(), + FirstParam->getSourceRange(), MethodOnlyDefaultArg) + << SecondMD << i + 1 << (FirstDefaultArg != nullptr); + ODRDiagNote(SecondParam->getLocation(), + SecondParam->getSourceRange(), MethodOnlyDefaultArg) + << SecondMD << i + 1 << (SecondDefaultArg != nullptr); + Diagnosed = true; + break; + } + + if (FirstDefaultArg && SecondDefaultArg) { + unsigned FirstODRHash = ComputeODRHash(FirstDefaultArg); + unsigned SecondODRHash = ComputeODRHash(SecondDefaultArg); + if (FirstODRHash != SecondODRHash) { + ODRDiagError(FirstParam->getLocation(), + FirstParam->getSourceRange(), MethodDefaultArg) + << SecondMD << i + 1; + ODRDiagNote(SecondParam->getLocation(), + SecondParam->getSourceRange(), MethodDefaultArg) + << SecondMD << i + 1; + Diagnosed = true; + break; + } + } + } + + // TODO: Figure out how to diagnose different function bodies. + // Deserialization does not import the second function body. + + break; + } + case Field: { + // TODO: Merge with exising field diff logic. + FieldDecl *FirstField = cast(FirstDecl); + FieldDecl *SecondField = cast(SecondDecl); + IdentifierInfo *FirstII = FirstField->getIdentifier(); + IdentifierInfo *SecondII = SecondField->getIdentifier(); + if (FirstII->getName() != SecondII->getName()) { + ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), + FieldName) + << FirstII; + ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), + FieldName) + << SecondII; + Diagnosed = true; + break; + } + + // This case is handled elsewhere. + if (FirstField->getType() != SecondField->getType()) { + break; + } + + bool FirstBitField = FirstField->isBitField(); + bool SecondBitField = SecondField->isBitField(); + if (FirstBitField != SecondBitField) { + ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), + FieldSingleBitField) + << FirstII << FirstBitField; + ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), + FieldSingleBitField) + << SecondII << SecondBitField; + Diagnosed = true; + break; + } + + if (FirstBitField && SecondBitField) { + Expr* FirstWidth = FirstField->getBitWidth(); + Expr *SecondWidth = SecondField->getBitWidth(); + unsigned FirstODRHash = ComputeODRHash(FirstWidth); + unsigned SecondODRHash = ComputeODRHash(SecondWidth); + if (FirstODRHash != SecondODRHash) { + ODRDiagError(FirstField->getLocation(), + FirstField->getSourceRange(), FieldSingleBitField) + << FirstII << FirstBitField; + ODRDiagNote(SecondField->getLocation(), + SecondField->getSourceRange(), FieldSingleBitField) + << SecondII << SecondBitField; + Diagnosed = true; + break; + } + } + + bool FirstMutable = FirstField->isMutable(); + bool SecondMutable = SecondField->isMutable(); + if (FirstMutable != SecondMutable) { + ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), + FieldMutable) + << FirstII << FirstMutable; + ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), + FieldMutable) + << SecondII << SecondMutable; + Diagnosed = true; + break; + } + break; + } + case Other: + case EndOfClass: + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + llvm_unreachable("Invalid diff type"); + } + + if (Diagnosed == true) + continue; + + // Unable to find difference in Decl's, print simple different + // definitions diagnostic. + Diag(Merge.first->getLocation(), + diag::err_module_odr_violation_different_definitions) + << Merge.first << FirstModule.empty() << FirstModule; + Diag(RD->getLocation(), + diag::note_module_odr_violation_different_definitions) + << SecondModule; + Diagnosed = true; } if (!Diagnosed) { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 9be3981329a0..4871123772d1 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1527,6 +1527,7 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.ImplicitCopyAssignmentHasConstParam = Record.readInt(); Data.HasDeclaredCopyConstructorWithConstParam = Record.readInt(); Data.HasDeclaredCopyAssignmentWithConstParam = Record.readInt(); + Data.ODRHash = Record.readInt(); Data.NumBases = Record.readInt(); if (Data.NumBases) @@ -1657,6 +1658,7 @@ void ASTDeclReader::MergeDefinitionData( OR_FIELD(HasDeclaredCopyConstructorWithConstParam) OR_FIELD(HasDeclaredCopyAssignmentWithConstParam) MATCH_FIELD(IsLambda) + MATCH_FIELD(ODRHash) #undef OR_FIELD #undef MATCH_FIELD diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 5dee86c14190..5fe693ba8e15 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5707,6 +5707,7 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) { 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); diff --git a/clang/test/Modules/merge-using-decls.cpp b/clang/test/Modules/merge-using-decls.cpp index 98989d12f985..4153d14207c8 100644 --- a/clang/test/Modules/merge-using-decls.cpp +++ b/clang/test/Modules/merge-using-decls.cpp @@ -37,6 +37,10 @@ template int UseAll(); // Here, we're instantiating the definition from 'A' and merging the definition // from 'B' into it. +// expected-error@b.h:* {{'D::type' from module 'B' is not present in definition of 'D' in module 'A'}} +// expected-error@b.h:* {{'D::value' from module 'B' is not present in definition of 'D' in module 'A'}} + + // expected-error@b.h:* {{'E::value' from module 'B' is not present in definition of 'E' in module 'A'}} // expected-error@b.h:* {{'E::v' from module 'B' is not present in definition of 'E' in module 'A'}} diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp new file mode 100644 index 000000000000..8da3db95506d --- /dev/null +++ b/clang/test/Modules/odr_hash.cpp @@ -0,0 +1,1156 @@ +// Clear and create directories +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: mkdir %t/cache +// RUN: mkdir %t/Inputs + +// Build first header file +// RUN: echo "#define FIRST" >> %t/Inputs/first.h +// RUN: cat %s >> %t/Inputs/first.h + +// Build second header file +// RUN: echo "#define SECOND" >> %t/Inputs/second.h +// RUN: cat %s >> %t/Inputs/second.h + +// Build module map file +// RUN: echo "module first {" >> %t/Inputs/module.map +// RUN: echo " header \"first.h\"" >> %t/Inputs/module.map +// RUN: echo "}" >> %t/Inputs/module.map +// RUN: echo "module second {" >> %t/Inputs/module.map +// RUN: echo " header \"second.h\"" >> %t/Inputs/module.map +// RUN: echo "}" >> %t/Inputs/module.map + +// Run test +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++11 + +#if !defined(FIRST) && !defined(SECOND) +#include "first.h" +#include "second.h" +#endif + +#if defined(FIRST) +struct S1 { + public: +}; +#elif defined(SECOND) +struct S1 { + private: +}; +#else +S1 s1; +// expected-error@first.h:* {{'S1' has different definitions in different modules; first difference is definition in module 'first' found public access specifier}} +// expected-note@second.h:* {{but in 'second' found private access specifier}} +#endif + +#if defined(FIRST) +struct S2Friend2 {}; +struct S2 { + friend S2Friend2; +}; +#elif defined(SECOND) +struct S2Friend1 {}; +struct S2 { + friend S2Friend1; +}; +#else +S2 s2; +// expected-error@first.h:* {{'S2' has different definitions in different modules; first difference is definition in module 'first' found friend 'S2Friend2'}} +// expected-note@second.h:* {{but in 'second' found other friend 'S2Friend1'}} +#endif + +#if defined(FIRST) +template +struct S3Template {}; +struct S3 { + friend S3Template; +}; +#elif defined(SECOND) +template +struct S3Template {}; +struct S3 { + friend S3Template; +}; +#else +S3 s3; +// expected-error@first.h:* {{'S3' has different definitions in different modules; first difference is definition in module 'first' found friend 'S3Template'}} +// expected-note@second.h:* {{but in 'second' found other friend 'S3Template'}} +#endif + +#if defined(FIRST) +struct S4 { + static_assert(1 == 1, "First"); +}; +#elif defined(SECOND) +struct S4 { + static_assert(1 == 1, "Second"); +}; +#else +S4 s4; +// expected-error@first.h:* {{'S4' has different definitions in different modules; first difference is definition in module 'first' found static assert with message}} +// expected-note@second.h:* {{but in 'second' found static assert with different message}} +#endif + +#if defined(FIRST) +struct S5 { + static_assert(1 == 1, "Message"); +}; +#elif defined(SECOND) +struct S5 { + static_assert(2 == 2, "Message"); +}; +#else +S5 s5; +// expected-error@first.h:* {{'S5' has different definitions in different modules; first difference is definition in module 'first' found static assert with condition}} +// expected-note@second.h:* {{but in 'second' found static assert with different condition}} +#endif + +#if defined(FIRST) +struct S6 { + int First(); +}; +#elif defined(SECOND) +struct S6 { + int Second(); +}; +#else +S6 s6; +// expected-error@second.h:* {{'S6::Second' from module 'second' is not present in definition of 'S6' in module 'first'}} +// expected-note@first.h:* {{definition has no member 'Second'}} +#endif + +#if defined(FIRST) +struct S7 { + double foo(); +}; +#elif defined(SECOND) +struct S7 { + int foo(); +}; +#else +S7 s7; +// expected-error@second.h:* {{'S7::foo' from module 'second' is not present in definition of 'S7' in module 'first'}} +// expected-note@first.h:* {{declaration of 'foo' does not match}} +#endif + +#if defined(FIRST) +struct S8 { + void foo(); +}; +#elif defined(SECOND) +struct S8 { + void foo() {} +}; +#else +S8 s8; +// expected-error@first.h:* {{'S8' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S9 { + void foo() { int y = 5; } +}; +#elif defined(SECOND) +struct S9 { + void foo() { int x = 5; } +}; +#else +S9 s9; +// expected-error@first.h:* {{'S9' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S10 { + struct { + int x; + } a; +}; +#elif defined(SECOND) +struct S10 { + struct { + int x; + int y; + } a; +}; +#else +S10 s10; +// expected-error-re@second.h:* {{'S10::(anonymous struct)::y' from module 'second' is not present in definition of 'S10::(anonymous struct at {{.*}}first.h:{{[0-9]*}}:{{[0-9]*}})' in module 'first'}} +// expected-note@first.h:* {{definition has no member 'y'}} + +// expected-error@first.h:* {{'S10' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S11 { + void foo() { int y = sizeof(int); } +}; +#elif defined(SECOND) +struct S11 { + void foo() { int y = sizeof(double); } +}; +#else +S11 s11; +// expected-error@first.h:* {{'S11' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S12 { + int x = sizeof(x); + int y = sizeof(x); +}; +#elif defined(SECOND) +struct S12 { + int x = sizeof(x); + int y = sizeof(y); +}; +#else +S12 s12; +// expected-error@first.h:* {{'S12' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S13 { + template void foo(); +}; +#elif defined(SECOND) +struct S13 { + template void foo(); +}; +#else +S13 s13; +// expected-error@first.h:* {{'S13' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S14 { + template void foo(); +}; +#elif defined(SECOND) +struct S14 { + template void foo(); +}; +#else +S14 s14; +// expected-error@second.h:* {{'S14::foo' from module 'second' is not present in definition of 'S14' in module 'first'}} +// expected-note@first.h:* {{declaration of 'foo' does not match}} +#endif + +#if defined(FIRST) +template +struct S15 : T { + void foo() { + int x = __builtin_offsetof(T, first); + } +}; +#elif defined(SECOND) +template +struct S15 : T { + void foo() { + int x = __builtin_offsetof(T, second); + } +}; +#else +template +void Test15() { + S15 s15; +// expected-error@first.h:* {{'S15' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +} +#endif + +#if defined(FIRST) +struct S16 { + template class Y> + void foo() { + Y<> y; + } +}; +#elif defined(SECOND) +struct S16 { + template class Y> + void foo() { + Y<> y; + } +}; +#else +void TestS16() { + S16 s16; +} +// expected-error@first.h:* {{'S16' has different definitions in different modules; definition in module 'first' is here}} +// expected-note@second.h:* {{definition in module 'second' is here}} +#endif + +#if defined(FIRST) +struct S17 { + template