diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 15 | ||||
-rw-r--r-- | clang/lib/AST/ODRHash.cpp | 935 | ||||
-rw-r--r-- | clang/lib/AST/StmtProfile.cpp | 229 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 5 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReader.cpp | 638 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReaderDecl.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriter.cpp | 1 |
8 files changed, 1729 insertions, 97 deletions
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index e28bd2e16d1..2e98f524da8 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 a9db65a5151..8fca00404c0 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 00000000000..7ad76df8a77 --- /dev/null +++ b/clang/lib/AST/ODRHash.cpp @@ -0,0 +1,935 @@ +//===-- 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<ODRDeclVisitor> { + typedef ConstDeclVisitor<ODRDeclVisitor> 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()); + + 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->hasBody() ? D->getBody() : nullptr); + + 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<CXXCtorInitializer *, 4> 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<const Decl *, 16> 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<NamedDecl>(D)) { + AddDeclarationName(ND->getDeclName()); + } + + if (const auto *Typedef = dyn_cast<TypedefNameDecl>(D)) { + AddQualType(Typedef->getUnderlyingType()); + } + + if (const auto *Alias = dyn_cast<NamespaceAliasDecl>(D)) { + AddDecl(Alias->getNamespace()); + } +} + +// Process a Type pointer. Add* methods call back into ODRHash while Visit* +// methods process the relevant parts of the Type. +class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> { + typedef TypeVisitor<ODRTypeVisitor> 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 49e43de638a..1ac2a5bf5e1 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<StmtProfiler> { + 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<NonTypeTemplateParmDecl>(D)) { + ID.AddInteger(NTTP->getDepth()); + ID.AddInteger(NTTP->getIndex()); + ID.AddBoolean(NTTP->isParameterPack()); + VisitType(NTTP->getType()); + return; + } + + if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(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<TemplateTypeParmDecl>(D)) { + ID.AddInteger(TTP->getDepth()); + ID.AddInteger(TTP->getIndex()); + ID.AddBoolean(TTP->isParameterPack()); + return; + } + + if (const TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(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<NonTypeTemplateParmDecl>(D)) { - ID.AddInteger(NTTP->getDepth()); - ID.AddInteger(NTTP->getIndex()); - ID.AddBoolean(NTTP->isParameterPack()); - VisitType(NTTP->getType()); - return; - } - - if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(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<TemplateTypeParmDecl>(D)) { - ID.AddInteger(TTP->getDepth()); - ID.AddInteger(TTP->getIndex()); - ID.AddBoolean(TTP->isParameterPack()); - return; - } - - if (const TemplateTemplateParmDecl *TTP = - dyn_cast<TemplateTemplateParmDecl>(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 3905348d3ca..27203c0296d 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<CXXRecordDecl>(Tag)) + if (auto *RD = dyn_cast<CXXRecordDecl>(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 452f5410a8c..46ea00e0e8f 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" @@ -8892,21 +8893,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<std::pair<Decl *, unsigned>, 4> FirstHashes; + llvm::SmallVector<std::pair<Decl *, unsigned>, 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<FriendDecl>(FirstDecl); + FriendDecl *SecondFriend = cast<FriendDecl>(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<EnumDecl>(FirstDecl); + EnumDecl *SecondEnum = cast<EnumDecl>(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<StaticAssertDecl>(FirstDecl); + StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(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<TypedefNameDecl>(FirstDecl); + TypedefNameDecl *SecondTD = cast<TypedefNameDecl>(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<CXXMethodDecl>(FirstDecl); + CXXMethodDecl *SecondMD = cast<CXXMethodDecl>(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 (!Context.hasSameType(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<FieldDecl>(FirstDecl); + FieldDecl *SecondField = cast<FieldDecl>(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 (!Context.hasSameType(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 9be3981329a..4871123772d 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 5dee86c1419..5fe693ba8e1 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); |