diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -260,6 +260,7 @@ friend class ASTWriter; friend class DeclContext; friend class LambdaExpr; + friend class ODRDiagsEmitter; friend void FunctionDecl::setPure(bool); friend void TagDecl::startDefinition(); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -9445,6 +9445,110 @@ PendingMergedDefinitionsToDeduplicate.clear(); } +namespace clang { +class ODRDiagsEmitter { +public: + ODRDiagsEmitter(DiagnosticsEngine &Diags, const ASTContext &Context, + const LangOptions &LangOpts) + : Diags(Diags), Context(Context), LangOpts(LangOpts) {} + + /// Diagnose ODR mismatch between 2 FunctionDecl. + /// + /// Returns true if found a mismatch and diagnosed it. + bool diagnoseMismatch(const FunctionDecl *FirstFunction, + const FunctionDecl *SecondFunction) const; + + /// Diagnose ODR mismatch between 2 EnumDecl. + /// + /// Returns true if found a mismatch and diagnosed it. + bool diagnoseMismatch(const EnumDecl *FirstEnum, + const EnumDecl *SecondEnum) const; + + /// Diagnose ODR mismatch between 2 CXXRecordDecl. + /// + /// Returns true if found a mismatch and diagnosed it. + /// To compare 2 declarations with merged and identical definition data + /// you need to provide pre-merge definition data in \p SecondDD. + bool + diagnoseMismatch(const CXXRecordDecl *FirstRecord, + const CXXRecordDecl *SecondRecord, + const struct CXXRecordDecl::DefinitionData *SecondDD) const; + +private: + using DeclHashes = llvm::SmallVector, 4>; + + // Used with err_module_odr_violation_mismatch_decl and + // note_module_odr_violation_mismatch_decl + // This list should be the same Decl's as in ODRHash::isDeclToBeProcessed + enum ODRMismatchDecl { + EndOfClass, + PublicSpecifer, + PrivateSpecifer, + ProtectedSpecifer, + StaticAssert, + Field, + CXXMethod, + TypeAlias, + TypeDef, + Var, + Friend, + FunctionTemplate, + Other + }; + + struct DiffResult { + const Decl *FirstDecl = nullptr, *SecondDecl = nullptr; + ODRMismatchDecl FirstDiffType = Other, SecondDiffType = Other; + }; + + // If there is a diagnoseable difference, FirstDiffType and + // SecondDiffType will not be Other and FirstDecl and SecondDecl will be + // filled in if not EndOfClass. + static DiffResult FindTypeDiffs(DeclHashes &FirstHashes, + DeclHashes &SecondHashes); + + DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) const { + return Diags.Report(Loc, DiagID); + } + + // Use this to diagnose that an unexpected Decl was encountered + // or no difference was detected. This causes a generic error + // message to be emitted. + void diagnoseSubMismatchUnexpected(DiffResult &DR, + const NamedDecl *FirstRecord, + StringRef FirstModule, + const NamedDecl *SecondRecord, + StringRef SecondModule) const; + + void diagnoseSubMismatchDifferentDeclKinds(DiffResult &DR, + const NamedDecl *FirstRecord, + StringRef FirstModule, + const NamedDecl *SecondRecord, + StringRef SecondModule) const; + + bool diagnoseSubMismatchField(const NamedDecl *FirstRecord, + StringRef FirstModule, StringRef SecondModule, + const FieldDecl *FirstField, + const FieldDecl *SecondField) const; + + bool diagnoseSubMismatchTypedef(const NamedDecl *FirstRecord, + StringRef FirstModule, StringRef SecondModule, + const TypedefNameDecl *FirstTD, + const TypedefNameDecl *SecondTD, + bool IsTypeAlias) const; + + bool diagnoseSubMismatchVar(const NamedDecl *FirstRecord, + StringRef FirstModule, StringRef SecondModule, + const VarDecl *FirstVD, + const VarDecl *SecondVD) const; + +private: + DiagnosticsEngine &Diags; + const ASTContext &Context; + const LangOptions &LangOpts; +}; +} // namespace clang + static unsigned computeODRHash(QualType Ty) { ODRHash Hasher; Hasher.AddQualType(Ty); @@ -9470,6 +9574,15 @@ return Hasher.CalculateHash(); } +static std::string getOwningModuleNameForDiagnostic(const Decl *D) { + // If we know the owning module, use it. + if (Module *M = D->getImportedOwningModule()) + return M->getFullModuleName(); + + // Not from a module. + return {}; +} + void ASTReader::diagnoseOdrViolations() { if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() && PendingFunctionOdrMergeFailures.empty() && @@ -9609,32 +9722,80 @@ // we're producing our diagnostics. Deserializing RecursionGuard(this); - // Used with err_module_odr_violation_mismatch_decl and - // note_module_odr_violation_mismatch_decl - // This list should be the same Decl's as in ODRHash::isDeclToBeProcessed - enum ODRMismatchDecl { - EndOfClass, - PublicSpecifer, - PrivateSpecifer, - ProtectedSpecifer, - StaticAssert, - Field, - CXXMethod, - TypeAlias, - TypeDef, - Var, - Friend, - FunctionTemplate, - Other - }; + // Issue any pending ODR-failure diagnostics. + for (auto &Merge : OdrMergeFailures) { + // If we've already pointed out a specific problem with this class, don't + // bother issuing a general "something's different" diagnostic. + if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) + continue; + + bool Diagnosed = false; + ODRDiagsEmitter DiagsEmitter(Diags, getContext(), + getPreprocessor().getLangOpts()); + CXXRecordDecl *FirstRecord = Merge.first; + for (auto &RecordPair : Merge.second) { + if (DiagsEmitter.diagnoseMismatch(FirstRecord, RecordPair.first, + RecordPair.second)) { + Diagnosed = true; + break; + } + } + + if (!Diagnosed) { + // All definitions are updates to the same declaration. This happens if a + // module instantiates the declaration of a class template specialization + // and two or more other modules instantiate its definition. + // + // FIXME: Indicate which modules had instantiations of this definition. + // FIXME: How can this even happen? + Diag(Merge.first->getLocation(), + diag::err_module_odr_violation_different_instantiations) + << Merge.first; + } + } + + // Issue ODR failures diagnostics for functions. + for (auto &Merge : FunctionOdrMergeFailures) { + ODRDiagsEmitter DiagsEmitter(Diags, getContext(), + getPreprocessor().getLangOpts()); + FunctionDecl *FirstFunction = Merge.first; + bool Diagnosed = false; + for (auto &SecondFunction : Merge.second) { + if (DiagsEmitter.diagnoseMismatch(FirstFunction, SecondFunction)) { + Diagnosed = true; + break; + } + } + (void)Diagnosed; + assert(Diagnosed && "Unable to emit ODR diagnostic."); + } + + // Issue ODR failures diagnostics for enums. + for (auto &Merge : EnumOdrMergeFailures) { + // If we've already pointed out a specific problem with this enum, don't + // bother issuing a general "something's different" diagnostic. + if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) + continue; + + ODRDiagsEmitter DiagsEmitter(Diags, getContext(), + getPreprocessor().getLangOpts()); + EnumDecl *FirstEnum = Merge.first; + bool Diagnosed = false; + for (auto &SecondEnum : Merge.second) { + if (DiagsEmitter.diagnoseMismatch(FirstEnum, SecondEnum)) { + Diagnosed = true; + break; + } + } + (void)Diagnosed; + assert(Diagnosed && "Unable to emit ODR diagnostic."); + } +} - // 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 ODRDiagField = [this](NamedDecl *FirstRecord, StringRef FirstModule, - StringRef SecondModule, - const FieldDecl *FirstField, - const FieldDecl *SecondField) { +// clang-format off + bool ODRDiagsEmitter::diagnoseSubMismatchField(const NamedDecl *FirstRecord, + StringRef FirstModule, StringRef SecondModule, + const FieldDecl *FirstField, const FieldDecl *SecondField) const { enum ODRFieldDifference { FieldName, FieldTypeName, @@ -9667,8 +9828,7 @@ return true; } - assert(getContext().hasSameType(FirstField->getType(), - SecondField->getType())); + assert(Context.hasSameType(FirstField->getType(), SecondField->getType())); QualType FirstType = FirstField->getType(); QualType SecondType = SecondField->getType(); @@ -9698,7 +9858,7 @@ } } - if (!PP.getLangOpts().CPlusPlus) + if (!LangOpts.CPlusPlus) return false; const bool IsFirstMutable = FirstField->isMutable(); @@ -9733,53 +9893,54 @@ } return false; - }; - - auto ODRDiagTypeDefOrAlias = - [this](NamedDecl *FirstRecord, StringRef FirstModule, - StringRef SecondModule, const TypedefNameDecl *FirstTD, - const TypedefNameDecl *SecondTD, bool IsTypeAlias) { - enum ODRTypedefDifference { - TypedefName, - TypedefType, - }; + } - auto DiagError = [FirstRecord, FirstTD, FirstModule, - this](ODRTypedefDifference DiffType) { - return Diag(FirstTD->getLocation(), - diag::err_module_odr_violation_typedef) - << FirstRecord << FirstModule.empty() << FirstModule - << FirstTD->getSourceRange() << DiffType; - }; - auto DiagNote = [SecondTD, SecondModule, - this](ODRTypedefDifference DiffType) { - return Diag(SecondTD->getLocation(), - diag::note_module_odr_violation_typedef) - << SecondModule << SecondTD->getSourceRange() << DiffType; - }; + bool ODRDiagsEmitter::diagnoseSubMismatchTypedef(const NamedDecl *FirstRecord, + StringRef FirstModule, StringRef SecondModule, + const TypedefNameDecl *FirstTD, const TypedefNameDecl *SecondTD, + bool IsTypeAlias) const { + enum ODRTypedefDifference { + TypedefName, + TypedefType, + }; - DeclarationName FirstName = FirstTD->getDeclName(); - DeclarationName SecondName = SecondTD->getDeclName(); - if (FirstName != SecondName) { - DiagError(TypedefName) << IsTypeAlias << FirstName; - DiagNote(TypedefName) << IsTypeAlias << SecondName; - return true; - } + auto DiagError = [FirstRecord, FirstTD, FirstModule, + this](ODRTypedefDifference DiffType) { + return Diag(FirstTD->getLocation(), + diag::err_module_odr_violation_typedef) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstTD->getSourceRange() << DiffType; + }; + auto DiagNote = [SecondTD, SecondModule, + this](ODRTypedefDifference DiffType) { + return Diag(SecondTD->getLocation(), + diag::note_module_odr_violation_typedef) + << SecondModule << SecondTD->getSourceRange() << DiffType; + }; - QualType FirstType = FirstTD->getUnderlyingType(); - QualType SecondType = SecondTD->getUnderlyingType(); - if (computeODRHash(FirstType) != computeODRHash(SecondType)) { - DiagError(TypedefType) << IsTypeAlias << FirstName << FirstType; - DiagNote(TypedefType) << IsTypeAlias << SecondName << SecondType; - return true; - } + DeclarationName FirstName = FirstTD->getDeclName(); + DeclarationName SecondName = SecondTD->getDeclName(); + if (FirstName != SecondName) { + DiagError(TypedefName) << IsTypeAlias << FirstName; + DiagNote(TypedefName) << IsTypeAlias << SecondName; + return true; + } - return false; - }; + QualType FirstType = FirstTD->getUnderlyingType(); + QualType SecondType = SecondTD->getUnderlyingType(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagError(TypedefType) << IsTypeAlias << FirstName << FirstType; + DiagNote(TypedefType) << IsTypeAlias << SecondName << SecondType; + return true; + } + return false; + } - auto ODRDiagVar = [this](NamedDecl *FirstRecord, StringRef FirstModule, - StringRef SecondModule, const VarDecl *FirstVD, - const VarDecl *SecondVD) { + bool ODRDiagsEmitter::diagnoseSubMismatchVar(const NamedDecl *FirstRecord, + StringRef FirstModule, + StringRef SecondModule, + const VarDecl *FirstVD, + const VarDecl *SecondVD) const { enum ODRVarDifference { VarName, VarType, @@ -9817,7 +9978,7 @@ return true; } - if (!PP.getLangOpts().CPlusPlus) + if (!LangOpts.CPlusPlus) return false; const Expr *FirstInit = FirstVD->getInit(); @@ -9849,28 +10010,12 @@ return true; } return false; - }; - - using DeclHashes = llvm::SmallVector, 4>; - auto PopulateHashes = [](DeclHashes &Hashes, RecordDecl *Record, - const DeclContext *DC) { - for (auto *D : Record->decls()) { - if (!ODRHash::isDeclToBeProcessed(D, DC)) - continue; - Hashes.emplace_back(D, computeODRHash(D)); - } - }; - - struct DiffResult { - Decl *FirstDecl = nullptr, *SecondDecl = nullptr; - ODRMismatchDecl FirstDiffType = Other, SecondDiffType = Other; - }; + } - // If there is a diagnoseable difference, FirstDiffType and - // SecondDiffType will not be Other and FirstDecl and SecondDecl will be - // filled in if not EndOfClass. - auto FindTypeDiffs = [](DeclHashes &FirstHashes, DeclHashes &SecondHashes) { - auto DifferenceSelector = [](Decl *D) { + ODRDiagsEmitter::DiffResult + ODRDiagsEmitter::FindTypeDiffs(DeclHashes &FirstHashes, + DeclHashes &SecondHashes) { + auto DifferenceSelector = [](const Decl *D) { assert(D && "valid Decl required"); switch (D->getKind()) { default: @@ -9930,15 +10075,11 @@ return DR; } return DR; - }; + } - // Use this to diagnose that an unexpected Decl was encountered - // or no difference was detected. This causes a generic error - // message to be emitted. - auto DiagnoseODRUnexpected = [this](DiffResult &DR, NamedDecl *FirstRecord, - StringRef FirstModule, - NamedDecl *SecondRecord, - StringRef SecondModule) { + void ODRDiagsEmitter::diagnoseSubMismatchUnexpected( + DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule, + const NamedDecl *SecondRecord, StringRef SecondModule) const { Diag(FirstRecord->getLocation(), diag::err_module_odr_violation_different_definitions) << FirstRecord << FirstModule.empty() << FirstModule; @@ -9956,12 +10097,11 @@ Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference) << DR.SecondDecl->getSourceRange(); } - }; + } - auto DiagnoseODRMismatch = [this](DiffResult &DR, NamedDecl *FirstRecord, - StringRef FirstModule, - NamedDecl *SecondRecord, - StringRef SecondModule) { + void ODRDiagsEmitter::diagnoseSubMismatchDifferentDeclKinds( + DiffResult &DR, const NamedDecl *FirstRecord, StringRef FirstModule, + const NamedDecl *SecondRecord, StringRef SecondModule) const { auto GetMismatchedDeclLoc = [](const NamedDecl *Container, ODRMismatchDecl DiffType, const Decl *D) { SourceLocation Loc; @@ -9986,1014 +10126,921 @@ GetMismatchedDeclLoc(SecondRecord, DR.SecondDiffType, DR.SecondDecl); Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl) << SecondModule << SecondDiagInfo.second << DR.SecondDiffType; - }; + } - // Issue any pending ODR-failure diagnostics. - for (auto &Merge : OdrMergeFailures) { - // If we've already pointed out a specific problem with this class, don't - // bother issuing a general "something's different" diagnostic. - if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) - continue; + bool ODRDiagsEmitter::diagnoseMismatch( + const CXXRecordDecl *FirstRecord, const CXXRecordDecl *SecondRecord, + const struct CXXRecordDecl::DefinitionData *SecondDD) const { + // Multiple different declarations got merged together; tell the user + // where they came from. + if (FirstRecord == SecondRecord) + return false; - bool Diagnosed = false; - CXXRecordDecl *FirstRecord = Merge.first; std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); - for (auto &RecordPair : Merge.second) { - CXXRecordDecl *SecondRecord = RecordPair.first; - // Multiple different declarations got merged together; tell the user - // where they came from. - if (FirstRecord == SecondRecord) - continue; - - std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); - - auto *FirstDD = FirstRecord->DefinitionData; - auto *SecondDD = RecordPair.second; - - assert(FirstDD && SecondDD && "Definitions without DefinitionData"); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); + + const struct CXXRecordDecl::DefinitionData *FirstDD = + FirstRecord->DefinitionData; + assert(FirstDD && SecondDD && "Definitions without DefinitionData"); + + // Diagnostics from DefinitionData are emitted here. + if (FirstDD != SecondDD) { + // Keep in sync with err_module_odr_violation_definition_data. + enum ODRDefinitionDataDifference { + NumBases, + NumVBases, + BaseType, + BaseVirtual, + BaseAccess, + }; + auto DiagBaseError = [FirstRecord, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRDefinitionDataDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_definition_data) + << FirstRecord << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto DiagBaseNote = [&SecondModule, + this](SourceLocation Loc, SourceRange Range, + ODRDefinitionDataDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_definition_data) + << SecondModule << Range << DiffType; + }; + auto GetSourceRange = [](const struct CXXRecordDecl::DefinitionData *DD) { + unsigned NumBases = DD->NumBases; + if (NumBases == 0) return SourceRange(); + ArrayRef bases = DD->bases(); + return SourceRange(bases[0].getBeginLoc(), + bases[NumBases - 1].getEndLoc()); + }; - // Diagnostics from DefinitionData are emitted here. - if (FirstDD != SecondDD) { - enum ODRDefinitionDataDifference { - NumBases, - NumVBases, - BaseType, - BaseVirtual, - BaseAccess, - }; - auto ODRDiagBaseError = [FirstRecord, &FirstModule, - this](SourceLocation Loc, SourceRange Range, - ODRDefinitionDataDifference DiffType) { - return Diag(Loc, diag::err_module_odr_violation_definition_data) - << FirstRecord << FirstModule.empty() << FirstModule << Range - << DiffType; - }; - auto ODRDiagBaseNote = [&SecondModule, - this](SourceLocation Loc, SourceRange Range, - ODRDefinitionDataDifference DiffType) { - return Diag(Loc, diag::note_module_odr_violation_definition_data) - << SecondModule << Range << DiffType; - }; - auto GetSourceRange = [](struct CXXRecordDecl::DefinitionData *DD) { - unsigned NumBases = DD->NumBases; - if (NumBases == 0) return SourceRange(); - ArrayRef bases = DD->bases(); - return SourceRange(bases[0].getBeginLoc(), - bases[NumBases - 1].getEndLoc()); - }; + unsigned FirstNumBases = FirstDD->NumBases; + unsigned FirstNumVBases = FirstDD->NumVBases; + unsigned SecondNumBases = SecondDD->NumBases; + unsigned SecondNumVBases = SecondDD->NumVBases; + if (FirstNumBases != SecondNumBases) { + DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), + NumBases) + << FirstNumBases; + DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), + NumBases) + << SecondNumBases; + return true; + } - unsigned FirstNumBases = FirstDD->NumBases; - unsigned FirstNumVBases = FirstDD->NumVBases; - unsigned SecondNumBases = SecondDD->NumBases; - unsigned SecondNumVBases = SecondDD->NumVBases; - if (FirstNumBases != SecondNumBases) { - ODRDiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), - NumBases) - << FirstNumBases; - ODRDiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), - NumBases) - << SecondNumBases; - Diagnosed = true; - break; - } + if (FirstNumVBases != SecondNumVBases) { + DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), + NumVBases) + << FirstNumVBases; + DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), + NumVBases) + << SecondNumVBases; + return true; + } - if (FirstNumVBases != SecondNumVBases) { - ODRDiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), - NumVBases) - << FirstNumVBases; - ODRDiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), - NumVBases) - << SecondNumVBases; - Diagnosed = true; - break; + ArrayRef FirstBases = FirstDD->bases(); + ArrayRef SecondBases = SecondDD->bases(); + for (unsigned I = 0; I < FirstNumBases; ++I) { + const CXXBaseSpecifier FirstBase = FirstBases[I]; + const CXXBaseSpecifier SecondBase = SecondBases[I]; + if (computeODRHash(FirstBase.getType()) != + computeODRHash(SecondBase.getType())) { + DiagBaseError(FirstRecord->getLocation(), + FirstBase.getSourceRange(), BaseType) + << (I + 1) << FirstBase.getType(); + DiagBaseNote(SecondRecord->getLocation(), + SecondBase.getSourceRange(), BaseType) + << (I + 1) << SecondBase.getType(); + return true; } - ArrayRef FirstBases = FirstDD->bases(); - ArrayRef SecondBases = SecondDD->bases(); - unsigned I = 0; - for (I = 0; I < FirstNumBases; ++I) { - const CXXBaseSpecifier FirstBase = FirstBases[I]; - const CXXBaseSpecifier SecondBase = SecondBases[I]; - if (computeODRHash(FirstBase.getType()) != - computeODRHash(SecondBase.getType())) { - ODRDiagBaseError(FirstRecord->getLocation(), - FirstBase.getSourceRange(), BaseType) - << (I + 1) << FirstBase.getType(); - ODRDiagBaseNote(SecondRecord->getLocation(), - SecondBase.getSourceRange(), BaseType) - << (I + 1) << SecondBase.getType(); - break; - } - - if (FirstBase.isVirtual() != SecondBase.isVirtual()) { - ODRDiagBaseError(FirstRecord->getLocation(), - FirstBase.getSourceRange(), BaseVirtual) - << (I + 1) << FirstBase.isVirtual() << FirstBase.getType(); - ODRDiagBaseNote(SecondRecord->getLocation(), - SecondBase.getSourceRange(), BaseVirtual) - << (I + 1) << SecondBase.isVirtual() << SecondBase.getType(); - break; - } - - if (FirstBase.getAccessSpecifierAsWritten() != - SecondBase.getAccessSpecifierAsWritten()) { - ODRDiagBaseError(FirstRecord->getLocation(), - FirstBase.getSourceRange(), BaseAccess) - << (I + 1) << FirstBase.getType() - << (int)FirstBase.getAccessSpecifierAsWritten(); - ODRDiagBaseNote(SecondRecord->getLocation(), - SecondBase.getSourceRange(), BaseAccess) - << (I + 1) << SecondBase.getType() - << (int)SecondBase.getAccessSpecifierAsWritten(); - break; - } + if (FirstBase.isVirtual() != SecondBase.isVirtual()) { + DiagBaseError(FirstRecord->getLocation(), + FirstBase.getSourceRange(), BaseVirtual) + << (I + 1) << FirstBase.isVirtual() << FirstBase.getType(); + DiagBaseNote(SecondRecord->getLocation(), + SecondBase.getSourceRange(), BaseVirtual) + << (I + 1) << SecondBase.isVirtual() << SecondBase.getType(); + return true; } - if (I != FirstNumBases) { - Diagnosed = true; - break; + if (FirstBase.getAccessSpecifierAsWritten() != + SecondBase.getAccessSpecifierAsWritten()) { + DiagBaseError(FirstRecord->getLocation(), + FirstBase.getSourceRange(), BaseAccess) + << (I + 1) << FirstBase.getType() + << (int)FirstBase.getAccessSpecifierAsWritten(); + DiagBaseNote(SecondRecord->getLocation(), + SecondBase.getSourceRange(), BaseAccess) + << (I + 1) << SecondBase.getType() + << (int)SecondBase.getAccessSpecifierAsWritten(); + return true; } } + } - const ClassTemplateDecl *FirstTemplate = - FirstRecord->getDescribedClassTemplate(); - const ClassTemplateDecl *SecondTemplate = - SecondRecord->getDescribedClassTemplate(); - - assert(!FirstTemplate == !SecondTemplate && - "Both pointers should be null or non-null"); - - if (FirstTemplate && SecondTemplate) { - DeclHashes FirstTemplateHashes; - DeclHashes SecondTemplateHashes; + const ClassTemplateDecl *FirstTemplate = + FirstRecord->getDescribedClassTemplate(); + const ClassTemplateDecl *SecondTemplate = + SecondRecord->getDescribedClassTemplate(); - auto PopulateTemplateParameterHashs = [](DeclHashes &Hashes, - const ClassTemplateDecl *TD) { - for (auto *D : TD->getTemplateParameters()->asArray()) { - Hashes.emplace_back(D, computeODRHash(D)); - } - }; + assert(!FirstTemplate == !SecondTemplate && + "Both pointers should be null or non-null"); - PopulateTemplateParameterHashs(FirstTemplateHashes, FirstTemplate); - PopulateTemplateParameterHashs(SecondTemplateHashes, SecondTemplate); + if (FirstTemplate && SecondTemplate) { + ArrayRef FirstTemplateParams = + FirstTemplate->getTemplateParameters()->asArray(); + ArrayRef SecondTemplateParams = + SecondTemplate->getTemplateParameters()->asArray(); + assert(FirstTemplateParams.size() == SecondTemplateParams.size() && + "Number of template parameters should be equal."); + for (auto Pair : llvm::zip(FirstTemplateParams, SecondTemplateParams)) { + const NamedDecl *FirstDecl = std::get<0>(Pair); + const NamedDecl *SecondDecl = std::get<1>(Pair); + if (computeODRHash(FirstDecl) == computeODRHash(SecondDecl)) + continue; - assert(FirstTemplateHashes.size() == SecondTemplateHashes.size() && - "Number of template parameters should be equal."); + assert(FirstDecl->getKind() == SecondDecl->getKind() && + "Parameter Decl's should be the same kind."); - auto FirstIt = FirstTemplateHashes.begin(); - auto FirstEnd = FirstTemplateHashes.end(); - auto SecondIt = SecondTemplateHashes.begin(); - for (; FirstIt != FirstEnd; ++FirstIt, ++SecondIt) { - if (FirstIt->second == SecondIt->second) - continue; + enum ODRTemplateDifference { + ParamEmptyName, + ParamName, + ParamSingleDefaultArgument, + ParamDifferentDefaultArgument, + }; - const NamedDecl* FirstDecl = cast(FirstIt->first); - const NamedDecl* SecondDecl = cast(SecondIt->first); + auto hasDefaultArg = [](const NamedDecl *D) { + if (auto *TTP = dyn_cast(D)) + return TTP->hasDefaultArgument() && + !TTP->defaultArgumentWasInherited(); + if (auto *NTTP = dyn_cast(D)) + return NTTP->hasDefaultArgument() && + !NTTP->defaultArgumentWasInherited(); + auto *TTP = cast(D); + return TTP->hasDefaultArgument() && + !TTP->defaultArgumentWasInherited(); + }; + bool hasFirstArg = hasDefaultArg(FirstDecl); + bool hasSecondArg = hasDefaultArg(SecondDecl); - assert(FirstDecl->getKind() == SecondDecl->getKind() && - "Parameter Decl's should be the same kind."); + ODRTemplateDifference ErrDiffType; + ODRTemplateDifference NoteDiffType; - enum ODRTemplateDifference { - ParamEmptyName, - ParamName, - ParamSingleDefaultArgument, - ParamDifferentDefaultArgument, - }; + DeclarationName FirstName = FirstDecl->getDeclName(); + DeclarationName SecondName = SecondDecl->getDeclName(); - auto hasDefaultArg = [](const NamedDecl *D) { - if (auto *TTP = dyn_cast(D)) - return TTP->hasDefaultArgument() && - !TTP->defaultArgumentWasInherited(); - if (auto *NTTP = dyn_cast(D)) - return NTTP->hasDefaultArgument() && - !NTTP->defaultArgumentWasInherited(); - auto *TTP = cast(D); - return TTP->hasDefaultArgument() && - !TTP->defaultArgumentWasInherited(); - }; - bool hasFirstArg = hasDefaultArg(FirstDecl); - bool hasSecondArg = hasDefaultArg(SecondDecl); - - ODRTemplateDifference ErrDiffType; - ODRTemplateDifference NoteDiffType; - - DeclarationName FirstName = FirstDecl->getDeclName(); - DeclarationName SecondName = SecondDecl->getDeclName(); - - if (FirstName != SecondName) { - bool FirstNameEmpty = - FirstName.isIdentifier() && !FirstName.getAsIdentifierInfo(); - bool SecondNameEmpty = SecondName.isIdentifier() && - !SecondName.getAsIdentifierInfo(); - ErrDiffType = FirstNameEmpty ? ParamEmptyName : ParamName; - NoteDiffType = SecondNameEmpty ? ParamEmptyName : ParamName; - } else if (hasFirstArg == hasSecondArg) - ErrDiffType = NoteDiffType = ParamDifferentDefaultArgument; - else - ErrDiffType = NoteDiffType = ParamSingleDefaultArgument; - - Diag(FirstDecl->getLocation(), - diag::err_module_odr_violation_template_parameter) - << FirstRecord << FirstModule.empty() << FirstModule - << FirstDecl->getSourceRange() << ErrDiffType << hasFirstArg - << FirstName; - Diag(SecondDecl->getLocation(), - diag::note_module_odr_violation_template_parameter) - << SecondModule << SecondDecl->getSourceRange() << NoteDiffType - << hasSecondArg << SecondName; - break; - } + if (FirstName != SecondName) { + bool FirstNameEmpty = + FirstName.isIdentifier() && !FirstName.getAsIdentifierInfo(); + bool SecondNameEmpty = SecondName.isIdentifier() && + !SecondName.getAsIdentifierInfo(); + ErrDiffType = FirstNameEmpty ? ParamEmptyName : ParamName; + NoteDiffType = SecondNameEmpty ? ParamEmptyName : ParamName; + } else if (hasFirstArg == hasSecondArg) + ErrDiffType = NoteDiffType = ParamDifferentDefaultArgument; + else + ErrDiffType = NoteDiffType = ParamSingleDefaultArgument; + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_template_parameter) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstDecl->getSourceRange() << ErrDiffType << hasFirstArg + << FirstName; + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_template_parameter) + << SecondModule << SecondDecl->getSourceRange() << NoteDiffType + << hasSecondArg << SecondName; + return true; + } + } - if (FirstIt != FirstEnd) { - Diagnosed = true; - break; - } + auto PopulateHashes = [](DeclHashes &Hashes, const RecordDecl *Record, + const DeclContext *DC) { + for (const Decl *D : Record->decls()) { + if (!ODRHash::isDeclToBeProcessed(D, DC)) + continue; + Hashes.emplace_back(D, computeODRHash(D)); } + }; + + DeclHashes FirstHashes; + DeclHashes SecondHashes; + const DeclContext *DC = FirstRecord; + PopulateHashes(FirstHashes, FirstRecord, DC); + PopulateHashes(SecondHashes, SecondRecord, DC); + + DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + const Decl *FirstDecl = DR.FirstDecl; + const Decl *SecondDecl = DR.SecondDecl; + + if (FirstDiffType == Other || SecondDiffType == Other) { + diagnoseSubMismatchUnexpected(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); + return true; + } - DeclHashes FirstHashes; - DeclHashes SecondHashes; - const DeclContext *DC = FirstRecord; - PopulateHashes(FirstHashes, FirstRecord, DC); - PopulateHashes(SecondHashes, SecondRecord, DC); + if (FirstDiffType != SecondDiffType) { + diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule, + SecondRecord, SecondModule); + return true; + } - DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes); - ODRMismatchDecl FirstDiffType = DR.FirstDiffType; - ODRMismatchDecl SecondDiffType = DR.SecondDiffType; - const Decl *FirstDecl = DR.FirstDecl; - const Decl *SecondDecl = DR.SecondDecl; + // Used with err_module_odr_violation_record and + // note_module_odr_violation_record + enum ODRCXXRecordDifference { + StaticAssertCondition, + StaticAssertMessage, + StaticAssertOnlyMessage, + MethodName, + MethodDeleted, + MethodDefaulted, + MethodVirtual, + MethodStatic, + MethodVolatile, + MethodConst, + MethodInline, + MethodNumberParameters, + MethodParameterType, + MethodParameterName, + MethodParameterSingleDefaultArgument, + MethodParameterDifferentDefaultArgument, + MethodNoTemplateArguments, + MethodDifferentNumberTemplateArguments, + MethodDifferentTemplateArgument, + MethodSingleBody, + MethodDifferentBody, + FriendTypeFunction, + FriendType, + FriendFunction, + FunctionTemplateDifferentNumberParameters, + FunctionTemplateParameterDifferentKind, + FunctionTemplateParameterName, + FunctionTemplateParameterSingleDefaultArgument, + FunctionTemplateParameterDifferentDefaultArgument, + FunctionTemplateParameterDifferentType, + FunctionTemplatePackParameter, + }; + auto DiagError = [FirstRecord, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRCXXRecordDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_record) + << FirstRecord << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto DiagNote = [&SecondModule, + this](SourceLocation Loc, SourceRange Range, + ODRCXXRecordDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_record) + << SecondModule << Range << DiffType; + }; - if (FirstDiffType == Other || SecondDiffType == Other) { - DiagnoseODRUnexpected(DR, FirstRecord, FirstModule, SecondRecord, - SecondModule); - Diagnosed = true; - break; + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + case Other: + case EndOfClass: + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + llvm_unreachable("Invalid diff type"); + + case StaticAssert: { + const StaticAssertDecl *FirstSA = cast(FirstDecl); + const StaticAssertDecl *SecondSA = cast(SecondDecl); + + const Expr *FirstExpr = FirstSA->getAssertExpr(); + const Expr *SecondExpr = SecondSA->getAssertExpr(); + unsigned FirstODRHash = computeODRHash(FirstExpr); + unsigned SecondODRHash = computeODRHash(SecondExpr); + if (FirstODRHash != SecondODRHash) { + DiagError(FirstExpr->getBeginLoc(), + FirstExpr->getSourceRange(), StaticAssertCondition); + DiagNote(SecondExpr->getBeginLoc(), + SecondExpr->getSourceRange(), StaticAssertCondition); + return true; } - if (FirstDiffType != SecondDiffType) { - DiagnoseODRMismatch(DR, FirstRecord, FirstModule, SecondRecord, - SecondModule); - Diagnosed = true; - break; + const StringLiteral *FirstStr = FirstSA->getMessage(); + const StringLiteral *SecondStr = SecondSA->getMessage(); + assert((FirstStr || SecondStr) && "Both messages cannot be empty"); + if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) { + SourceLocation FirstLoc, SecondLoc; + SourceRange FirstRange, SecondRange; + if (FirstStr) { + FirstLoc = FirstStr->getBeginLoc(); + FirstRange = FirstStr->getSourceRange(); + } else { + FirstLoc = FirstSA->getBeginLoc(); + FirstRange = FirstSA->getSourceRange(); + } + if (SecondStr) { + SecondLoc = SecondStr->getBeginLoc(); + SecondRange = SecondStr->getSourceRange(); + } else { + SecondLoc = SecondSA->getBeginLoc(); + SecondRange = SecondSA->getSourceRange(); + } + DiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage) + << (FirstStr == nullptr); + DiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage) + << (SecondStr == nullptr); + return true; } - // Used with err_module_odr_violation_record and - // note_module_odr_violation_record - enum ODRCXXRecordDifference { - StaticAssertCondition, - StaticAssertMessage, - StaticAssertOnlyMessage, - MethodName, - MethodDeleted, - MethodDefaulted, - MethodVirtual, - MethodStatic, - MethodVolatile, - MethodConst, - MethodInline, - MethodNumberParameters, - MethodParameterType, - MethodParameterName, - MethodParameterSingleDefaultArgument, - MethodParameterDifferentDefaultArgument, - MethodNoTemplateArguments, - MethodDifferentNumberTemplateArguments, - MethodDifferentTemplateArgument, - MethodSingleBody, - MethodDifferentBody, - FriendTypeFunction, - FriendType, - FriendFunction, - FunctionTemplateDifferentNumberParameters, - FunctionTemplateParameterDifferentKind, - FunctionTemplateParameterName, - FunctionTemplateParameterSingleDefaultArgument, - FunctionTemplateParameterDifferentDefaultArgument, - FunctionTemplateParameterDifferentType, - FunctionTemplatePackParameter, - }; - auto ODRDiagDeclError = [FirstRecord, &FirstModule, - this](SourceLocation Loc, SourceRange Range, - ODRCXXRecordDifference DiffType) { - return Diag(Loc, diag::err_module_odr_violation_record) - << FirstRecord << FirstModule.empty() << FirstModule << Range - << DiffType; + if (FirstStr && SecondStr && + FirstStr->getString() != SecondStr->getString()) { + DiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(), + StaticAssertMessage); + DiagNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(), + StaticAssertMessage); + return true; + } + break; + } + case Field: { + if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl))) + return true; + break; + } + case CXXMethod: { + enum { + DiagMethod, + DiagConstructor, + DiagDestructor, + } FirstMethodType, + SecondMethodType; + auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl* D) { + if (isa(D)) return DiagConstructor; + if (isa(D)) return DiagDestructor; + return DiagMethod; }; - auto ODRDiagDeclNote = [&SecondModule, - this](SourceLocation Loc, SourceRange Range, - ODRCXXRecordDifference DiffType) { - return Diag(Loc, diag::note_module_odr_violation_record) - << SecondModule << Range << DiffType; + const CXXMethodDecl *FirstMethod = cast(FirstDecl); + const CXXMethodDecl *SecondMethod = cast(SecondDecl); + FirstMethodType = GetMethodTypeForDiagnostics(FirstMethod); + SecondMethodType = GetMethodTypeForDiagnostics(SecondMethod); + DeclarationName FirstName = FirstMethod->getDeclName(); + DeclarationName SecondName = SecondMethod->getDeclName(); + auto DiagMethodError = [&DiagError, FirstMethod, FirstMethodType, + FirstName](ODRCXXRecordDifference DiffType) { + return DiagError(FirstMethod->getLocation(), + FirstMethod->getSourceRange(), DiffType) + << FirstMethodType << FirstName; + }; + auto DiagMethodNote = [&DiagNote, SecondMethod, SecondMethodType, + SecondName](ODRCXXRecordDifference DiffType) { + return DiagNote(SecondMethod->getLocation(), + SecondMethod->getSourceRange(), DiffType) + << SecondMethodType << SecondName; }; - assert(FirstDiffType == SecondDiffType); - switch (FirstDiffType) { - case Other: - case EndOfClass: - case PublicSpecifer: - case PrivateSpecifer: - case ProtectedSpecifer: - llvm_unreachable("Invalid diff type"); - - case StaticAssert: { - const StaticAssertDecl *FirstSA = cast(FirstDecl); - const StaticAssertDecl *SecondSA = cast(SecondDecl); - - const Expr *FirstExpr = FirstSA->getAssertExpr(); - const Expr *SecondExpr = SecondSA->getAssertExpr(); - unsigned FirstODRHash = computeODRHash(FirstExpr); - unsigned SecondODRHash = computeODRHash(SecondExpr); - if (FirstODRHash != SecondODRHash) { - ODRDiagDeclError(FirstExpr->getBeginLoc(), - FirstExpr->getSourceRange(), StaticAssertCondition); - ODRDiagDeclNote(SecondExpr->getBeginLoc(), - SecondExpr->getSourceRange(), StaticAssertCondition); - Diagnosed = true; - break; - } - - const StringLiteral *FirstStr = FirstSA->getMessage(); - const StringLiteral *SecondStr = SecondSA->getMessage(); - assert((FirstStr || SecondStr) && "Both messages cannot be empty"); - if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) { - SourceLocation FirstLoc, SecondLoc; - SourceRange FirstRange, SecondRange; - if (FirstStr) { - FirstLoc = FirstStr->getBeginLoc(); - FirstRange = FirstStr->getSourceRange(); - } else { - FirstLoc = FirstSA->getBeginLoc(); - FirstRange = FirstSA->getSourceRange(); - } - if (SecondStr) { - SecondLoc = SecondStr->getBeginLoc(); - SecondRange = SecondStr->getSourceRange(); - } else { - SecondLoc = SecondSA->getBeginLoc(); - SecondRange = SecondSA->getSourceRange(); - } - ODRDiagDeclError(FirstLoc, FirstRange, StaticAssertOnlyMessage) - << (FirstStr == nullptr); - ODRDiagDeclNote(SecondLoc, SecondRange, StaticAssertOnlyMessage) - << (SecondStr == nullptr); - Diagnosed = true; - break; - } - - if (FirstStr && SecondStr && - FirstStr->getString() != SecondStr->getString()) { - ODRDiagDeclError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(), - StaticAssertMessage); - ODRDiagDeclNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(), - StaticAssertMessage); - Diagnosed = true; - break; - } - break; - } - case Field: { - Diagnosed = ODRDiagField(FirstRecord, FirstModule, SecondModule, - cast(FirstDecl), - cast(SecondDecl)); - break; + if (FirstMethodType != SecondMethodType || FirstName != SecondName) { + DiagMethodError(MethodName); + DiagMethodNote(MethodName); + return true; } - case CXXMethod: { - enum { - DiagMethod, - DiagConstructor, - DiagDestructor, - } FirstMethodType, - SecondMethodType; - auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl* D) { - if (isa(D)) return DiagConstructor; - if (isa(D)) return DiagDestructor; - return DiagMethod; - }; - const CXXMethodDecl *FirstMethod = cast(FirstDecl); - const CXXMethodDecl *SecondMethod = cast(SecondDecl); - FirstMethodType = GetMethodTypeForDiagnostics(FirstMethod); - SecondMethodType = GetMethodTypeForDiagnostics(SecondMethod); - DeclarationName FirstName = FirstMethod->getDeclName(); - DeclarationName SecondName = SecondMethod->getDeclName(); - auto DiagMethodError = [&ODRDiagDeclError, FirstMethod, FirstMethodType, - FirstName](ODRCXXRecordDifference DiffType) { - return ODRDiagDeclError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), DiffType) - << FirstMethodType << FirstName; - }; - auto DiagMethodNote = [&ODRDiagDeclNote, SecondMethod, SecondMethodType, - SecondName](ODRCXXRecordDifference DiffType) { - return ODRDiagDeclNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), DiffType) - << SecondMethodType << SecondName; - }; - - if (FirstMethodType != SecondMethodType || FirstName != SecondName) { - DiagMethodError(MethodName); - DiagMethodNote(MethodName); - Diagnosed = true; - break; - } - - const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); - const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); - if (FirstDeleted != SecondDeleted) { - DiagMethodError(MethodDeleted) << FirstDeleted; - DiagMethodNote(MethodDeleted) << SecondDeleted; - Diagnosed = true; - break; - } - const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); - const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); - if (FirstDefaulted != SecondDefaulted) { - DiagMethodError(MethodDefaulted) << FirstDefaulted; - DiagMethodNote(MethodDefaulted) << SecondDefaulted; - Diagnosed = true; - break; - } - - const bool FirstVirtual = FirstMethod->isVirtualAsWritten(); - const bool SecondVirtual = SecondMethod->isVirtualAsWritten(); - const bool FirstPure = FirstMethod->isPure(); - const bool SecondPure = SecondMethod->isPure(); - if ((FirstVirtual || SecondVirtual) && - (FirstVirtual != SecondVirtual || FirstPure != SecondPure)) { - DiagMethodError(MethodVirtual) << FirstPure << FirstVirtual; - DiagMethodNote(MethodVirtual) << SecondPure << SecondVirtual; - Diagnosed = true; - break; - } + const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); + const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); + if (FirstDeleted != SecondDeleted) { + DiagMethodError(MethodDeleted) << FirstDeleted; + DiagMethodNote(MethodDeleted) << SecondDeleted; + return true; + } - // CXXMethodDecl::isStatic uses the canonical Decl. With Decl merging, - // FirstDecl is the canonical Decl of SecondDecl, so the storage - // class needs to be checked instead. - StorageClass FirstStorage = FirstMethod->getStorageClass(); - StorageClass SecondStorage = SecondMethod->getStorageClass(); - const bool FirstStatic = FirstStorage == SC_Static; - const bool SecondStatic = SecondStorage == SC_Static; - if (FirstStatic != SecondStatic) { - DiagMethodError(MethodStatic) << FirstStatic; - DiagMethodNote(MethodStatic) << SecondStatic; - Diagnosed = true; - break; - } + const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); + const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); + if (FirstDefaulted != SecondDefaulted) { + DiagMethodError(MethodDefaulted) << FirstDefaulted; + DiagMethodNote(MethodDefaulted) << SecondDefaulted; + return true; + } - const bool FirstVolatile = FirstMethod->isVolatile(); - const bool SecondVolatile = SecondMethod->isVolatile(); - if (FirstVolatile != SecondVolatile) { - DiagMethodError(MethodVolatile) << FirstVolatile; - DiagMethodNote(MethodVolatile) << SecondVolatile; - Diagnosed = true; - break; - } + const bool FirstVirtual = FirstMethod->isVirtualAsWritten(); + const bool SecondVirtual = SecondMethod->isVirtualAsWritten(); + const bool FirstPure = FirstMethod->isPure(); + const bool SecondPure = SecondMethod->isPure(); + if ((FirstVirtual || SecondVirtual) && + (FirstVirtual != SecondVirtual || FirstPure != SecondPure)) { + DiagMethodError(MethodVirtual) << FirstPure << FirstVirtual; + DiagMethodNote(MethodVirtual) << SecondPure << SecondVirtual; + return true; + } - const bool FirstConst = FirstMethod->isConst(); - const bool SecondConst = SecondMethod->isConst(); - if (FirstConst != SecondConst) { - DiagMethodError(MethodConst) << FirstConst; - DiagMethodNote(MethodConst) << SecondConst; - Diagnosed = true; - break; - } + // CXXMethodDecl::isStatic uses the canonical Decl. With Decl merging, + // FirstDecl is the canonical Decl of SecondDecl, so the storage + // class needs to be checked instead. + StorageClass FirstStorage = FirstMethod->getStorageClass(); + StorageClass SecondStorage = SecondMethod->getStorageClass(); + const bool FirstStatic = FirstStorage == SC_Static; + const bool SecondStatic = SecondStorage == SC_Static; + if (FirstStatic != SecondStatic) { + DiagMethodError(MethodStatic) << FirstStatic; + DiagMethodNote(MethodStatic) << SecondStatic; + return true; + } - const bool FirstInline = FirstMethod->isInlineSpecified(); - const bool SecondInline = SecondMethod->isInlineSpecified(); - if (FirstInline != SecondInline) { - DiagMethodError(MethodInline) << FirstInline; - DiagMethodNote(MethodInline) << SecondInline; - Diagnosed = true; - break; - } + const bool FirstVolatile = FirstMethod->isVolatile(); + const bool SecondVolatile = SecondMethod->isVolatile(); + if (FirstVolatile != SecondVolatile) { + DiagMethodError(MethodVolatile) << FirstVolatile; + DiagMethodNote(MethodVolatile) << SecondVolatile; + return true; + } - const unsigned FirstNumParameters = FirstMethod->param_size(); - const unsigned SecondNumParameters = SecondMethod->param_size(); - if (FirstNumParameters != SecondNumParameters) { - DiagMethodError(MethodNumberParameters) << FirstNumParameters; - DiagMethodNote(MethodNumberParameters) << SecondNumParameters; - Diagnosed = true; - break; - } + const bool FirstConst = FirstMethod->isConst(); + const bool SecondConst = SecondMethod->isConst(); + if (FirstConst != SecondConst) { + DiagMethodError(MethodConst) << FirstConst; + DiagMethodNote(MethodConst) << SecondConst; + return true; + } - // Need this status boolean to know when break out of the switch. - bool ParameterMismatch = false; - for (unsigned I = 0; I < FirstNumParameters; ++I) { - const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); - const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); - - QualType FirstParamType = FirstParam->getType(); - QualType SecondParamType = SecondParam->getType(); - if (FirstParamType != SecondParamType && - computeODRHash(FirstParamType) != - computeODRHash(SecondParamType)) { - if (const DecayedType *ParamDecayedType = - FirstParamType->getAs()) { - DiagMethodError(MethodParameterType) - << (I + 1) << FirstParamType << true - << ParamDecayedType->getOriginalType(); - } else { - DiagMethodError(MethodParameterType) - << (I + 1) << FirstParamType << false; - } + const bool FirstInline = FirstMethod->isInlineSpecified(); + const bool SecondInline = SecondMethod->isInlineSpecified(); + if (FirstInline != SecondInline) { + DiagMethodError(MethodInline) << FirstInline; + DiagMethodNote(MethodInline) << SecondInline; + return true; + } - if (const DecayedType *ParamDecayedType = - SecondParamType->getAs()) { - DiagMethodNote(MethodParameterType) - << (I + 1) << SecondParamType << true - << ParamDecayedType->getOriginalType(); - } else { - DiagMethodNote(MethodParameterType) - << (I + 1) << SecondParamType << false; - } - ParameterMismatch = true; - break; - } + const unsigned FirstNumParameters = FirstMethod->param_size(); + const unsigned SecondNumParameters = SecondMethod->param_size(); + if (FirstNumParameters != SecondNumParameters) { + DiagMethodError(MethodNumberParameters) << FirstNumParameters; + DiagMethodNote(MethodNumberParameters) << SecondNumParameters; + return true; + } - DeclarationName FirstParamName = FirstParam->getDeclName(); - DeclarationName SecondParamName = SecondParam->getDeclName(); - if (FirstParamName != SecondParamName) { - DiagMethodError(MethodParameterName) << (I + 1) << FirstParamName; - DiagMethodNote(MethodParameterName) << (I + 1) << SecondParamName; - ParameterMismatch = true; - break; - } + for (unsigned I = 0; I < FirstNumParameters; ++I) { + const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); + const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); - const Expr *FirstInit = FirstParam->getInit(); - const Expr *SecondInit = SecondParam->getInit(); - if ((FirstInit == nullptr) != (SecondInit == nullptr)) { - DiagMethodError(MethodParameterSingleDefaultArgument) - << (I + 1) << (FirstInit == nullptr) - << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); - DiagMethodNote(MethodParameterSingleDefaultArgument) - << (I + 1) << (SecondInit == nullptr) - << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); - ParameterMismatch = true; - break; + QualType FirstParamType = FirstParam->getType(); + QualType SecondParamType = SecondParam->getType(); + if (FirstParamType != SecondParamType && + computeODRHash(FirstParamType) != + computeODRHash(SecondParamType)) { + if (const DecayedType *ParamDecayedType = + FirstParamType->getAs()) { + DiagMethodError(MethodParameterType) + << (I + 1) << FirstParamType << true + << ParamDecayedType->getOriginalType(); + } else { + DiagMethodError(MethodParameterType) + << (I + 1) << FirstParamType << false; } - if (FirstInit && SecondInit && - computeODRHash(FirstInit) != computeODRHash(SecondInit)) { - DiagMethodError(MethodParameterDifferentDefaultArgument) - << (I + 1) << FirstInit->getSourceRange(); - DiagMethodNote(MethodParameterDifferentDefaultArgument) - << (I + 1) << SecondInit->getSourceRange(); - ParameterMismatch = true; - break; + if (const DecayedType *ParamDecayedType = + SecondParamType->getAs()) { + DiagMethodNote(MethodParameterType) + << (I + 1) << SecondParamType << true + << ParamDecayedType->getOriginalType(); + } else { + DiagMethodNote(MethodParameterType) + << (I + 1) << SecondParamType << false; } + return true; } - if (ParameterMismatch) { - Diagnosed = true; - break; + DeclarationName FirstParamName = FirstParam->getDeclName(); + DeclarationName SecondParamName = SecondParam->getDeclName(); + if (FirstParamName != SecondParamName) { + DiagMethodError(MethodParameterName) << (I + 1) << FirstParamName; + DiagMethodNote(MethodParameterName) << (I + 1) << SecondParamName; + return true; } - const TemplateArgumentList *FirstTemplateArgs = - FirstMethod->getTemplateSpecializationArgs(); - const TemplateArgumentList *SecondTemplateArgs = - SecondMethod->getTemplateSpecializationArgs(); - - if ((FirstTemplateArgs && !SecondTemplateArgs) || - (!FirstTemplateArgs && SecondTemplateArgs)) { - DiagMethodError(MethodNoTemplateArguments) - << (FirstTemplateArgs != nullptr); - DiagMethodNote(MethodNoTemplateArguments) - << (SecondTemplateArgs != nullptr); - Diagnosed = true; - break; + const Expr *FirstInit = FirstParam->getInit(); + const Expr *SecondInit = SecondParam->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + DiagMethodError(MethodParameterSingleDefaultArgument) + << (I + 1) << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + DiagMethodNote(MethodParameterSingleDefaultArgument) + << (I + 1) << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + return true; } - if (FirstTemplateArgs && SecondTemplateArgs) { - // Remove pack expansions from argument list. - auto ExpandTemplateArgumentList = - [](const TemplateArgumentList *TAL) { - llvm::SmallVector ExpandedList; - for (const TemplateArgument &TA : TAL->asArray()) { - if (TA.getKind() != TemplateArgument::Pack) { - ExpandedList.push_back(&TA); - continue; - } - llvm::append_range(ExpandedList, llvm::make_pointer_range( - TA.getPackAsArray())); - } - return ExpandedList; - }; - llvm::SmallVector FirstExpandedList = - ExpandTemplateArgumentList(FirstTemplateArgs); - llvm::SmallVector SecondExpandedList = - ExpandTemplateArgumentList(SecondTemplateArgs); - - if (FirstExpandedList.size() != SecondExpandedList.size()) { - DiagMethodError(MethodDifferentNumberTemplateArguments) - << (unsigned)FirstExpandedList.size(); - DiagMethodNote(MethodDifferentNumberTemplateArguments) - << (unsigned)SecondExpandedList.size(); - Diagnosed = true; - break; - } + if (FirstInit && SecondInit && + computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagMethodError(MethodParameterDifferentDefaultArgument) + << (I + 1) << FirstInit->getSourceRange(); + DiagMethodNote(MethodParameterDifferentDefaultArgument) + << (I + 1) << SecondInit->getSourceRange(); + return true; + } + } - bool TemplateArgumentMismatch = false; - for (unsigned i = 0, e = FirstExpandedList.size(); i != e; ++i) { - const TemplateArgument &FirstTA = *FirstExpandedList[i], - &SecondTA = *SecondExpandedList[i]; - if (computeODRHash(FirstTA) == computeODRHash(SecondTA)) { - continue; - } + const TemplateArgumentList *FirstTemplateArgs = + FirstMethod->getTemplateSpecializationArgs(); + const TemplateArgumentList *SecondTemplateArgs = + SecondMethod->getTemplateSpecializationArgs(); - DiagMethodError(MethodDifferentTemplateArgument) - << FirstTA << i + 1; - DiagMethodNote(MethodDifferentTemplateArgument) - << SecondTA << i + 1; - TemplateArgumentMismatch = true; - break; - } + if ((FirstTemplateArgs && !SecondTemplateArgs) || + (!FirstTemplateArgs && SecondTemplateArgs)) { + DiagMethodError(MethodNoTemplateArguments) + << (FirstTemplateArgs != nullptr); + DiagMethodNote(MethodNoTemplateArguments) + << (SecondTemplateArgs != nullptr); + return true; + } - if (TemplateArgumentMismatch) { - Diagnosed = true; - break; - } + if (FirstTemplateArgs && SecondTemplateArgs) { + // Remove pack expansions from argument list. + auto ExpandTemplateArgumentList = + [](const TemplateArgumentList *TAL) { + llvm::SmallVector ExpandedList; + for (const TemplateArgument &TA : TAL->asArray()) { + if (TA.getKind() != TemplateArgument::Pack) { + ExpandedList.push_back(&TA); + continue; + } + llvm::append_range(ExpandedList, llvm::make_pointer_range( + TA.getPackAsArray())); + } + return ExpandedList; + }; + llvm::SmallVector FirstExpandedList = + ExpandTemplateArgumentList(FirstTemplateArgs); + llvm::SmallVector SecondExpandedList = + ExpandTemplateArgumentList(SecondTemplateArgs); + + if (FirstExpandedList.size() != SecondExpandedList.size()) { + DiagMethodError(MethodDifferentNumberTemplateArguments) + << (unsigned)FirstExpandedList.size(); + DiagMethodNote(MethodDifferentNumberTemplateArguments) + << (unsigned)SecondExpandedList.size(); + return true; } - // Compute the hash of the method as if it has no body. - auto ComputeCXXMethodODRHash = [](const CXXMethodDecl *D) { - ODRHash Hasher; - Hasher.AddFunctionDecl(D, true /*SkipBody*/); - return Hasher.CalculateHash(); - }; + for (unsigned i = 0, e = FirstExpandedList.size(); i != e; ++i) { + const TemplateArgument &FirstTA = *FirstExpandedList[i], + &SecondTA = *SecondExpandedList[i]; + if (computeODRHash(FirstTA) == computeODRHash(SecondTA)) { + continue; + } - // Compare the hash generated to the hash stored. A difference means - // that a body was present in the original source. Due to merging, - // the standard way of detecting a body will not work. - const bool HasFirstBody = - ComputeCXXMethodODRHash(FirstMethod) != FirstMethod->getODRHash(); - const bool HasSecondBody = - ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash(); - - if (HasFirstBody != HasSecondBody) { - DiagMethodError(MethodSingleBody) << HasFirstBody; - DiagMethodNote(MethodSingleBody) << HasSecondBody; - Diagnosed = true; - break; + DiagMethodError(MethodDifferentTemplateArgument) + << FirstTA << i + 1; + DiagMethodNote(MethodDifferentTemplateArgument) + << SecondTA << i + 1; + return true; } + } - if (HasFirstBody && HasSecondBody) { - DiagMethodError(MethodDifferentBody); - DiagMethodNote(MethodDifferentBody); - Diagnosed = true; - break; - } + // Compute the hash of the method as if it has no body. + auto ComputeCXXMethodODRHash = [](const CXXMethodDecl *D) { + ODRHash Hasher; + Hasher.AddFunctionDecl(D, true /*SkipBody*/); + return Hasher.CalculateHash(); + }; - break; - } - case TypeAlias: - case TypeDef: { - Diagnosed = ODRDiagTypeDefOrAlias( - FirstRecord, FirstModule, SecondModule, - cast(FirstDecl), cast(SecondDecl), - FirstDiffType == TypeAlias); - break; + // Compare the hash generated to the hash stored. A difference means + // that a body was present in the original source. Due to merging, + // the standard way of detecting a body will not work. + const bool HasFirstBody = + ComputeCXXMethodODRHash(FirstMethod) != FirstMethod->getODRHash(); + const bool HasSecondBody = + ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash(); + + if (HasFirstBody != HasSecondBody) { + DiagMethodError(MethodSingleBody) << HasFirstBody; + DiagMethodNote(MethodSingleBody) << HasSecondBody; + return true; } - case Var: { - Diagnosed = - ODRDiagVar(FirstRecord, FirstModule, SecondModule, - cast(FirstDecl), cast(SecondDecl)); - break; + + if (HasFirstBody && HasSecondBody) { + DiagMethodError(MethodDifferentBody); + DiagMethodNote(MethodDifferentBody); + return true; } - case Friend: { - const FriendDecl *FirstFriend = cast(FirstDecl); - const FriendDecl *SecondFriend = cast(SecondDecl); - const NamedDecl *FirstND = FirstFriend->getFriendDecl(); - const NamedDecl *SecondND = SecondFriend->getFriendDecl(); + break; + } - TypeSourceInfo *FirstTSI = FirstFriend->getFriendType(); - TypeSourceInfo *SecondTSI = SecondFriend->getFriendType(); + case TypeAlias: + case TypeDef: { + if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl), + FirstDiffType == TypeAlias)) + return true; + break; + } + case Var: { + if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl))) + return true; + break; + } + case Friend: { + const FriendDecl *FirstFriend = cast(FirstDecl); + const FriendDecl *SecondFriend = cast(SecondDecl); - if (FirstND && SecondND) { - ODRDiagDeclError(FirstFriend->getFriendLoc(), - FirstFriend->getSourceRange(), FriendFunction) - << FirstND; - ODRDiagDeclNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendFunction) - << SecondND; - Diagnosed = true; - break; - } + const NamedDecl *FirstND = FirstFriend->getFriendDecl(); + const NamedDecl *SecondND = SecondFriend->getFriendDecl(); - if (FirstTSI && SecondTSI) { - QualType FirstFriendType = FirstTSI->getType(); - QualType SecondFriendType = SecondTSI->getType(); - assert(computeODRHash(FirstFriendType) != - computeODRHash(SecondFriendType)); - ODRDiagDeclError(FirstFriend->getFriendLoc(), - FirstFriend->getSourceRange(), FriendType) - << FirstFriendType; - ODRDiagDeclNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendType) - << SecondFriendType; - Diagnosed = true; - break; - } + TypeSourceInfo *FirstTSI = FirstFriend->getFriendType(); + TypeSourceInfo *SecondTSI = SecondFriend->getFriendType(); - ODRDiagDeclError(FirstFriend->getFriendLoc(), - FirstFriend->getSourceRange(), FriendTypeFunction) - << (FirstTSI == nullptr); - ODRDiagDeclNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendTypeFunction) - << (SecondTSI == nullptr); - Diagnosed = true; - break; + if (FirstND && SecondND) { + DiagError(FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendFunction) + << FirstND; + DiagNote(SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendFunction) + << SecondND; + return true; } - case FunctionTemplate: { - const FunctionTemplateDecl *FirstTemplate = - cast(FirstDecl); - const FunctionTemplateDecl *SecondTemplate = - cast(SecondDecl); - - TemplateParameterList *FirstTPL = - FirstTemplate->getTemplateParameters(); - TemplateParameterList *SecondTPL = - SecondTemplate->getTemplateParameters(); - - auto DiagTemplateError = [&ODRDiagDeclError, FirstTemplate]( - ODRCXXRecordDifference DiffType) { - return ODRDiagDeclError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), DiffType) - << FirstTemplate; - }; - auto DiagTemplateNote = [&ODRDiagDeclNote, SecondTemplate]( - ODRCXXRecordDifference DiffType) { - return ODRDiagDeclNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), DiffType) - << SecondTemplate; - }; - - if (FirstTPL->size() != SecondTPL->size()) { - DiagTemplateError(FunctionTemplateDifferentNumberParameters) - << FirstTPL->size(); - DiagTemplateNote(FunctionTemplateDifferentNumberParameters) - << SecondTPL->size(); - Diagnosed = true; - break; - } - bool ParameterMismatch = false; - for (unsigned i = 0, e = FirstTPL->size(); i != e; ++i) { - NamedDecl *FirstParam = FirstTPL->getParam(i); - NamedDecl *SecondParam = SecondTPL->getParam(i); + if (FirstTSI && SecondTSI) { + QualType FirstFriendType = FirstTSI->getType(); + QualType SecondFriendType = SecondTSI->getType(); + assert(computeODRHash(FirstFriendType) != + computeODRHash(SecondFriendType)); + DiagError(FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendType) + << FirstFriendType; + DiagNote(SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendType) + << SecondFriendType; + return true; + } - if (FirstParam->getKind() != SecondParam->getKind()) { - enum { - TemplateTypeParameter, - NonTypeTemplateParameter, - TemplateTemplateParameter, - }; - auto GetParamType = [](NamedDecl *D) { - switch (D->getKind()) { - default: - llvm_unreachable("Unexpected template parameter type"); - case Decl::TemplateTypeParm: - return TemplateTypeParameter; - case Decl::NonTypeTemplateParm: - return NonTypeTemplateParameter; - case Decl::TemplateTemplateParm: - return TemplateTemplateParameter; - } - }; + DiagError(FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendTypeFunction) + << (FirstTSI == nullptr); + DiagNote(SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendTypeFunction) + << (SecondTSI == nullptr); + return true; + } + case FunctionTemplate: { + const FunctionTemplateDecl *FirstTemplate = + cast(FirstDecl); + const FunctionTemplateDecl *SecondTemplate = + cast(SecondDecl); + + TemplateParameterList *FirstTPL = + FirstTemplate->getTemplateParameters(); + TemplateParameterList *SecondTPL = + SecondTemplate->getTemplateParameters(); + + auto DiagTemplateError = [&DiagError, FirstTemplate]( + ODRCXXRecordDifference DiffType) { + return DiagError(FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), DiffType) + << FirstTemplate; + }; + auto DiagTemplateNote = [&DiagNote, SecondTemplate]( + ODRCXXRecordDifference DiffType) { + return DiagNote(SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), DiffType) + << SecondTemplate; + }; - DiagTemplateError(FunctionTemplateParameterDifferentKind) - << (i + 1) << GetParamType(FirstParam); - DiagTemplateNote(FunctionTemplateParameterDifferentKind) - << (i + 1) << GetParamType(SecondParam); - ParameterMismatch = true; - break; - } + if (FirstTPL->size() != SecondTPL->size()) { + DiagTemplateError(FunctionTemplateDifferentNumberParameters) + << FirstTPL->size(); + DiagTemplateNote(FunctionTemplateDifferentNumberParameters) + << SecondTPL->size(); + return true; + } - if (FirstParam->getName() != SecondParam->getName()) { - DiagTemplateError(FunctionTemplateParameterName) - << (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam; - DiagTemplateNote(FunctionTemplateParameterName) - << (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam; - ParameterMismatch = true; - break; - } + for (unsigned i = 0, e = FirstTPL->size(); i != e; ++i) { + NamedDecl *FirstParam = FirstTPL->getParam(i); + NamedDecl *SecondParam = SecondTPL->getParam(i); - if (isa(FirstParam) && - isa(SecondParam)) { - TemplateTypeParmDecl *FirstTTPD = - cast(FirstParam); - TemplateTypeParmDecl *SecondTTPD = - cast(SecondParam); - bool HasFirstDefaultArgument = - FirstTTPD->hasDefaultArgument() && - !FirstTTPD->defaultArgumentWasInherited(); - bool HasSecondDefaultArgument = - SecondTTPD->hasDefaultArgument() && - !SecondTTPD->defaultArgumentWasInherited(); - if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) - << (i + 1) << HasFirstDefaultArgument; - DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) - << (i + 1) << HasSecondDefaultArgument; - ParameterMismatch = true; - break; + if (FirstParam->getKind() != SecondParam->getKind()) { + enum { + TemplateTypeParameter, + NonTypeTemplateParameter, + TemplateTemplateParameter, + }; + auto GetParamType = [](NamedDecl *D) { + switch (D->getKind()) { + default: + llvm_unreachable("Unexpected template parameter type"); + case Decl::TemplateTypeParm: + return TemplateTypeParameter; + case Decl::NonTypeTemplateParm: + return NonTypeTemplateParameter; + case Decl::TemplateTemplateParm: + return TemplateTemplateParameter; } + }; - if (HasFirstDefaultArgument && HasSecondDefaultArgument) { - QualType FirstType = FirstTTPD->getDefaultArgument(); - QualType SecondType = SecondTTPD->getDefaultArgument(); - if (computeODRHash(FirstType) != computeODRHash(SecondType)) { - DiagTemplateError( - FunctionTemplateParameterDifferentDefaultArgument) - << (i + 1) << FirstType; - DiagTemplateNote( - FunctionTemplateParameterDifferentDefaultArgument) - << (i + 1) << SecondType; - ParameterMismatch = true; - break; - } - } + DiagTemplateError(FunctionTemplateParameterDifferentKind) + << (i + 1) << GetParamType(FirstParam); + DiagTemplateNote(FunctionTemplateParameterDifferentKind) + << (i + 1) << GetParamType(SecondParam); + return true; + } - if (FirstTTPD->isParameterPack() != - SecondTTPD->isParameterPack()) { - DiagTemplateError(FunctionTemplatePackParameter) - << (i + 1) << FirstTTPD->isParameterPack(); - DiagTemplateNote(FunctionTemplatePackParameter) - << (i + 1) << SecondTTPD->isParameterPack(); - ParameterMismatch = true; - break; - } + if (FirstParam->getName() != SecondParam->getName()) { + DiagTemplateError(FunctionTemplateParameterName) + << (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam; + DiagTemplateNote(FunctionTemplateParameterName) + << (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam; + return true; + } + + if (isa(FirstParam) && + isa(SecondParam)) { + TemplateTypeParmDecl *FirstTTPD = + cast(FirstParam); + TemplateTypeParmDecl *SecondTTPD = + cast(SecondParam); + bool HasFirstDefaultArgument = + FirstTTPD->hasDefaultArgument() && + !FirstTTPD->defaultArgumentWasInherited(); + bool HasSecondDefaultArgument = + SecondTTPD->hasDefaultArgument() && + !SecondTTPD->defaultArgumentWasInherited(); + if (HasFirstDefaultArgument != HasSecondDefaultArgument) { + DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasFirstDefaultArgument; + DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasSecondDefaultArgument; + return true; } - if (isa(FirstParam) && - isa(SecondParam)) { - TemplateTemplateParmDecl *FirstTTPD = - cast(FirstParam); - TemplateTemplateParmDecl *SecondTTPD = - cast(SecondParam); - - TemplateParameterList *FirstTPL = - FirstTTPD->getTemplateParameters(); - TemplateParameterList *SecondTPL = - SecondTTPD->getTemplateParameters(); - - auto ComputeTemplateParameterListODRHash = - [](const TemplateParameterList *TPL) { - assert(TPL); - ODRHash Hasher; - Hasher.AddTemplateParameterList(TPL); - return Hasher.CalculateHash(); - }; - - if (ComputeTemplateParameterListODRHash(FirstTPL) != - ComputeTemplateParameterListODRHash(SecondTPL)) { - DiagTemplateError(FunctionTemplateParameterDifferentType) - << (i + 1); - DiagTemplateNote(FunctionTemplateParameterDifferentType) - << (i + 1); - ParameterMismatch = true; - break; + if (HasFirstDefaultArgument && HasSecondDefaultArgument) { + QualType FirstType = FirstTTPD->getDefaultArgument(); + QualType SecondType = SecondTTPD->getDefaultArgument(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagTemplateError( + FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << FirstType; + DiagTemplateNote( + FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << SecondType; + return true; } + } - bool HasFirstDefaultArgument = - FirstTTPD->hasDefaultArgument() && - !FirstTTPD->defaultArgumentWasInherited(); - bool HasSecondDefaultArgument = - SecondTTPD->hasDefaultArgument() && - !SecondTTPD->defaultArgumentWasInherited(); - if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) - << (i + 1) << HasFirstDefaultArgument; - DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) - << (i + 1) << HasSecondDefaultArgument; - ParameterMismatch = true; - break; - } + if (FirstTTPD->isParameterPack() != + SecondTTPD->isParameterPack()) { + DiagTemplateError(FunctionTemplatePackParameter) + << (i + 1) << FirstTTPD->isParameterPack(); + DiagTemplateNote(FunctionTemplatePackParameter) + << (i + 1) << SecondTTPD->isParameterPack(); + return true; + } + } - if (HasFirstDefaultArgument && HasSecondDefaultArgument) { - TemplateArgument FirstTA = - FirstTTPD->getDefaultArgument().getArgument(); - TemplateArgument SecondTA = - SecondTTPD->getDefaultArgument().getArgument(); - if (computeODRHash(FirstTA) != computeODRHash(SecondTA)) { - DiagTemplateError( - FunctionTemplateParameterDifferentDefaultArgument) - << (i + 1) << FirstTA; - DiagTemplateNote( - FunctionTemplateParameterDifferentDefaultArgument) - << (i + 1) << SecondTA; - ParameterMismatch = true; - break; - } - } + if (isa(FirstParam) && + isa(SecondParam)) { + TemplateTemplateParmDecl *FirstTTPD = + cast(FirstParam); + TemplateTemplateParmDecl *SecondTTPD = + cast(SecondParam); + + TemplateParameterList *FirstTPL = + FirstTTPD->getTemplateParameters(); + TemplateParameterList *SecondTPL = + SecondTTPD->getTemplateParameters(); + + auto ComputeTemplateParameterListODRHash = + [](const TemplateParameterList *TPL) { + assert(TPL); + ODRHash Hasher; + Hasher.AddTemplateParameterList(TPL); + return Hasher.CalculateHash(); + }; - if (FirstTTPD->isParameterPack() != - SecondTTPD->isParameterPack()) { - DiagTemplateError(FunctionTemplatePackParameter) - << (i + 1) << FirstTTPD->isParameterPack(); - DiagTemplateNote(FunctionTemplatePackParameter) - << (i + 1) << SecondTTPD->isParameterPack(); - ParameterMismatch = true; - break; - } + if (ComputeTemplateParameterListODRHash(FirstTPL) != + ComputeTemplateParameterListODRHash(SecondTPL)) { + DiagTemplateError(FunctionTemplateParameterDifferentType) + << (i + 1); + DiagTemplateNote(FunctionTemplateParameterDifferentType) + << (i + 1); + return true; } - if (isa(FirstParam) && - isa(SecondParam)) { - NonTypeTemplateParmDecl *FirstNTTPD = - cast(FirstParam); - NonTypeTemplateParmDecl *SecondNTTPD = - cast(SecondParam); + bool HasFirstDefaultArgument = + FirstTTPD->hasDefaultArgument() && + !FirstTTPD->defaultArgumentWasInherited(); + bool HasSecondDefaultArgument = + SecondTTPD->hasDefaultArgument() && + !SecondTTPD->defaultArgumentWasInherited(); + if (HasFirstDefaultArgument != HasSecondDefaultArgument) { + DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasFirstDefaultArgument; + DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasSecondDefaultArgument; + return true; + } - QualType FirstType = FirstNTTPD->getType(); - QualType SecondType = SecondNTTPD->getType(); - if (computeODRHash(FirstType) != computeODRHash(SecondType)) { - DiagTemplateError(FunctionTemplateParameterDifferentType) - << (i + 1); - DiagTemplateNote(FunctionTemplateParameterDifferentType) - << (i + 1); - ParameterMismatch = true; - break; + if (HasFirstDefaultArgument && HasSecondDefaultArgument) { + TemplateArgument FirstTA = + FirstTTPD->getDefaultArgument().getArgument(); + TemplateArgument SecondTA = + SecondTTPD->getDefaultArgument().getArgument(); + if (computeODRHash(FirstTA) != computeODRHash(SecondTA)) { + DiagTemplateError( + FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << FirstTA; + DiagTemplateNote( + FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << SecondTA; + return true; } + } - bool HasFirstDefaultArgument = - FirstNTTPD->hasDefaultArgument() && - !FirstNTTPD->defaultArgumentWasInherited(); - bool HasSecondDefaultArgument = - SecondNTTPD->hasDefaultArgument() && - !SecondNTTPD->defaultArgumentWasInherited(); - if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) - << (i + 1) << HasFirstDefaultArgument; - DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) - << (i + 1) << HasSecondDefaultArgument; - ParameterMismatch = true; - break; - } + if (FirstTTPD->isParameterPack() != + SecondTTPD->isParameterPack()) { + DiagTemplateError(FunctionTemplatePackParameter) + << (i + 1) << FirstTTPD->isParameterPack(); + DiagTemplateNote(FunctionTemplatePackParameter) + << (i + 1) << SecondTTPD->isParameterPack(); + return true; + } + } - if (HasFirstDefaultArgument && HasSecondDefaultArgument) { - Expr *FirstDefaultArgument = FirstNTTPD->getDefaultArgument(); - Expr *SecondDefaultArgument = SecondNTTPD->getDefaultArgument(); - if (computeODRHash(FirstDefaultArgument) != - computeODRHash(SecondDefaultArgument)) { - DiagTemplateError( - FunctionTemplateParameterDifferentDefaultArgument) - << (i + 1) << FirstDefaultArgument; - DiagTemplateNote( - FunctionTemplateParameterDifferentDefaultArgument) - << (i + 1) << SecondDefaultArgument; - ParameterMismatch = true; - break; - } - } + if (isa(FirstParam) && + isa(SecondParam)) { + NonTypeTemplateParmDecl *FirstNTTPD = + cast(FirstParam); + NonTypeTemplateParmDecl *SecondNTTPD = + cast(SecondParam); + + QualType FirstType = FirstNTTPD->getType(); + QualType SecondType = SecondNTTPD->getType(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagTemplateError(FunctionTemplateParameterDifferentType) + << (i + 1); + DiagTemplateNote(FunctionTemplateParameterDifferentType) + << (i + 1); + return true; + } + + bool HasFirstDefaultArgument = + FirstNTTPD->hasDefaultArgument() && + !FirstNTTPD->defaultArgumentWasInherited(); + bool HasSecondDefaultArgument = + SecondNTTPD->hasDefaultArgument() && + !SecondNTTPD->defaultArgumentWasInherited(); + if (HasFirstDefaultArgument != HasSecondDefaultArgument) { + DiagTemplateError(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasFirstDefaultArgument; + DiagTemplateNote(FunctionTemplateParameterSingleDefaultArgument) + << (i + 1) << HasSecondDefaultArgument; + return true; + } - if (FirstNTTPD->isParameterPack() != - SecondNTTPD->isParameterPack()) { - DiagTemplateError(FunctionTemplatePackParameter) - << (i + 1) << FirstNTTPD->isParameterPack(); - DiagTemplateNote(FunctionTemplatePackParameter) - << (i + 1) << SecondNTTPD->isParameterPack(); - ParameterMismatch = true; - break; + if (HasFirstDefaultArgument && HasSecondDefaultArgument) { + Expr *FirstDefaultArgument = FirstNTTPD->getDefaultArgument(); + Expr *SecondDefaultArgument = SecondNTTPD->getDefaultArgument(); + if (computeODRHash(FirstDefaultArgument) != + computeODRHash(SecondDefaultArgument)) { + DiagTemplateError( + FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << FirstDefaultArgument; + DiagTemplateNote( + FunctionTemplateParameterDifferentDefaultArgument) + << (i + 1) << SecondDefaultArgument; + return true; } } - } - if (ParameterMismatch) { - Diagnosed = true; - break; + if (FirstNTTPD->isParameterPack() != + SecondNTTPD->isParameterPack()) { + DiagTemplateError(FunctionTemplatePackParameter) + << (i + 1) << FirstNTTPD->isParameterPack(); + DiagTemplateNote(FunctionTemplatePackParameter) + << (i + 1) << SecondNTTPD->isParameterPack(); + return true; + } } - - break; - } } - - if (Diagnosed) - continue; - - Diag(FirstDecl->getLocation(), - diag::err_module_odr_violation_mismatch_decl_unknown) - << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType - << FirstDecl->getSourceRange(); - Diag(SecondDecl->getLocation(), - diag::note_module_odr_violation_mismatch_decl_unknown) - << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); - Diagnosed = true; + break; } - - if (!Diagnosed) { - // All definitions are updates to the same declaration. This happens if a - // module instantiates the declaration of a class template specialization - // and two or more other modules instantiate its definition. - // - // FIXME: Indicate which modules had instantiations of this definition. - // FIXME: How can this even happen? - Diag(Merge.first->getLocation(), - diag::err_module_odr_violation_different_instantiations) - << Merge.first; } + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); + return true; } - // Issue ODR failures diagnostics for functions. - for (auto &Merge : FunctionOdrMergeFailures) { + bool ODRDiagsEmitter::diagnoseMismatch( + const FunctionDecl *FirstFunction, + const FunctionDecl *SecondFunction) const { + if (FirstFunction == SecondFunction) + return false; + + // Keep in sync with select options in err_module_odr_violation_function. enum ODRFunctionDifference { ReturnType, ParameterName, @@ -11003,150 +11050,130 @@ FunctionBody, }; - FunctionDecl *FirstFunction = Merge.first; std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondFunction); + + auto DiagError = [FirstFunction, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRFunctionDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_function) + << FirstFunction << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto DiagNote = [&SecondModule, this](SourceLocation Loc, + SourceRange Range, + ODRFunctionDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_function) + << SecondModule << Range << DiffType; + }; - bool Diagnosed = false; - for (auto &SecondFunction : Merge.second) { - - if (FirstFunction == SecondFunction) - continue; - - std::string SecondModule = - getOwningModuleNameForDiagnostic(SecondFunction); - - auto ODRDiagError = [FirstFunction, &FirstModule, - this](SourceLocation Loc, SourceRange Range, - ODRFunctionDifference DiffType) { - return Diag(Loc, diag::err_module_odr_violation_function) - << FirstFunction << FirstModule.empty() << FirstModule << Range - << DiffType; - }; - auto ODRDiagNote = [&SecondModule, this](SourceLocation Loc, - SourceRange Range, - ODRFunctionDifference DiffType) { - return Diag(Loc, diag::note_module_odr_violation_function) - << SecondModule << Range << DiffType; - }; - - if (computeODRHash(FirstFunction->getReturnType()) != - computeODRHash(SecondFunction->getReturnType())) { - ODRDiagError(FirstFunction->getReturnTypeSourceRange().getBegin(), - FirstFunction->getReturnTypeSourceRange(), ReturnType) - << FirstFunction->getReturnType(); - ODRDiagNote(SecondFunction->getReturnTypeSourceRange().getBegin(), - SecondFunction->getReturnTypeSourceRange(), ReturnType) - << SecondFunction->getReturnType(); - Diagnosed = true; - break; - } - - assert(FirstFunction->param_size() == SecondFunction->param_size() && - "Merged functions with different number of parameters"); + if (computeODRHash(FirstFunction->getReturnType()) != + computeODRHash(SecondFunction->getReturnType())) { + DiagError(FirstFunction->getReturnTypeSourceRange().getBegin(), + FirstFunction->getReturnTypeSourceRange(), ReturnType) + << FirstFunction->getReturnType(); + DiagNote(SecondFunction->getReturnTypeSourceRange().getBegin(), + SecondFunction->getReturnTypeSourceRange(), ReturnType) + << SecondFunction->getReturnType(); + return true; + } - size_t ParamSize = FirstFunction->param_size(); - bool ParameterMismatch = false; - for (unsigned I = 0; I < ParamSize; ++I) { - const ParmVarDecl *FirstParam = FirstFunction->getParamDecl(I); - const ParmVarDecl *SecondParam = SecondFunction->getParamDecl(I); + assert(FirstFunction->param_size() == SecondFunction->param_size() && + "Merged functions with different number of parameters"); - assert(getContext().hasSameType(FirstParam->getType(), - SecondParam->getType()) && - "Merged function has different parameter types."); + size_t ParamSize = FirstFunction->param_size(); + for (unsigned I = 0; I < ParamSize; ++I) { + const ParmVarDecl *FirstParam = FirstFunction->getParamDecl(I); + const ParmVarDecl *SecondParam = SecondFunction->getParamDecl(I); - if (FirstParam->getDeclName() != SecondParam->getDeclName()) { - ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), - ParameterName) - << I + 1 << FirstParam->getDeclName(); - ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), - ParameterName) - << I + 1 << SecondParam->getDeclName(); - ParameterMismatch = true; - break; - }; - - QualType FirstParamType = FirstParam->getType(); - QualType SecondParamType = SecondParam->getType(); - if (FirstParamType != SecondParamType && - computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) { - if (const DecayedType *ParamDecayedType = - FirstParamType->getAs()) { - ODRDiagError(FirstParam->getLocation(), - FirstParam->getSourceRange(), ParameterType) - << (I + 1) << FirstParamType << true - << ParamDecayedType->getOriginalType(); - } else { - ODRDiagError(FirstParam->getLocation(), - FirstParam->getSourceRange(), ParameterType) - << (I + 1) << FirstParamType << false; - } + assert(Context.hasSameType(FirstParam->getType(), + SecondParam->getType()) && + "Merged function has different parameter types."); - if (const DecayedType *ParamDecayedType = - SecondParamType->getAs()) { - ODRDiagNote(SecondParam->getLocation(), - SecondParam->getSourceRange(), ParameterType) - << (I + 1) << SecondParamType << true - << ParamDecayedType->getOriginalType(); - } else { - ODRDiagNote(SecondParam->getLocation(), - SecondParam->getSourceRange(), ParameterType) - << (I + 1) << SecondParamType << false; - } - ParameterMismatch = true; - break; - } + if (FirstParam->getDeclName() != SecondParam->getDeclName()) { + DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterName) + << I + 1 << FirstParam->getDeclName(); + DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterName) + << I + 1 << SecondParam->getDeclName(); + return true; + }; - const Expr *FirstInit = FirstParam->getInit(); - const Expr *SecondInit = SecondParam->getInit(); - if ((FirstInit == nullptr) != (SecondInit == nullptr)) { - ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), - ParameterSingleDefaultArgument) - << (I + 1) << (FirstInit == nullptr) - << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); - ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), - ParameterSingleDefaultArgument) - << (I + 1) << (SecondInit == nullptr) - << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); - ParameterMismatch = true; - break; + QualType FirstParamType = FirstParam->getType(); + QualType SecondParamType = SecondParam->getType(); + if (FirstParamType != SecondParamType && + computeODRHash(FirstParamType) != computeODRHash(SecondParamType)) { + if (const DecayedType *ParamDecayedType = + FirstParamType->getAs()) { + DiagError(FirstParam->getLocation(), + FirstParam->getSourceRange(), ParameterType) + << (I + 1) << FirstParamType << true + << ParamDecayedType->getOriginalType(); + } else { + DiagError(FirstParam->getLocation(), + FirstParam->getSourceRange(), ParameterType) + << (I + 1) << FirstParamType << false; } - if (FirstInit && SecondInit && - computeODRHash(FirstInit) != computeODRHash(SecondInit)) { - ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), - ParameterDifferentDefaultArgument) - << (I + 1) << FirstInit->getSourceRange(); - ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), - ParameterDifferentDefaultArgument) - << (I + 1) << SecondInit->getSourceRange(); - ParameterMismatch = true; - break; + if (const DecayedType *ParamDecayedType = + SecondParamType->getAs()) { + DiagNote(SecondParam->getLocation(), + SecondParam->getSourceRange(), ParameterType) + << (I + 1) << SecondParamType << true + << ParamDecayedType->getOriginalType(); + } else { + DiagNote(SecondParam->getLocation(), + SecondParam->getSourceRange(), ParameterType) + << (I + 1) << SecondParamType << false; } + return true; + } - assert(computeODRHash(FirstParam) == computeODRHash(SecondParam) && - "Undiagnosed parameter difference."); + const Expr *FirstInit = FirstParam->getInit(); + const Expr *SecondInit = SecondParam->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterSingleDefaultArgument) + << (I + 1) << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterSingleDefaultArgument) + << (I + 1) << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + return true; } - if (ParameterMismatch) { - Diagnosed = true; - break; + if (FirstInit && SecondInit && + computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterDifferentDefaultArgument) + << (I + 1) << FirstInit->getSourceRange(); + DiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterDifferentDefaultArgument) + << (I + 1) << SecondInit->getSourceRange(); + return true; } - // If no error has been generated before now, assume the problem is in - // the body and generate a message. - ODRDiagError(FirstFunction->getLocation(), - FirstFunction->getSourceRange(), FunctionBody); - ODRDiagNote(SecondFunction->getLocation(), - SecondFunction->getSourceRange(), FunctionBody); - Diagnosed = true; - break; + assert(computeODRHash(FirstParam) == computeODRHash(SecondParam) && + "Undiagnosed parameter difference."); } - (void)Diagnosed; - assert(Diagnosed && "Unable to emit ODR diagnostic."); + + // If no error has been generated before now, assume the problem is in + // the body and generate a message. + DiagError(FirstFunction->getLocation(), + FirstFunction->getSourceRange(), FunctionBody); + DiagNote(SecondFunction->getLocation(), + SecondFunction->getSourceRange(), FunctionBody); + return true; } - // Issue ODR failures diagnostics for enums. - for (auto &Merge : EnumOdrMergeFailures) { + bool ODRDiagsEmitter::diagnoseMismatch(const EnumDecl *FirstEnum, + const EnumDecl *SecondEnum) const { + if (FirstEnum == SecondEnum) + return false; + + // Keep in sync with select options in err_module_odr_violation_enum. enum ODREnumDifference { SingleScopedEnum, EnumTagKeywordMismatch, @@ -11158,18 +11185,73 @@ EnumConstantDifferentInitializer, }; - // If we've already pointed out a specific problem with this enum, don't - // bother issuing a general "something's different" diagnostic. - if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) - continue; - - EnumDecl *FirstEnum = Merge.first; std::string FirstModule = getOwningModuleNameForDiagnostic(FirstEnum); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondEnum); + + auto DiagError = [FirstEnum, &FirstModule, + this](const auto *DiagAnchor, + ODREnumDifference DiffType) { + return Diag(DiagAnchor->getLocation(), + diag::err_module_odr_violation_enum) + << FirstEnum << FirstModule.empty() << FirstModule + << DiagAnchor->getSourceRange() << DiffType; + }; + auto DiagNote = [&SecondModule, this](const auto *DiagAnchor, + ODREnumDifference DiffType) { + return Diag(DiagAnchor->getLocation(), + diag::note_module_odr_violation_enum) + << SecondModule << DiagAnchor->getSourceRange() << DiffType; + }; + + if (FirstEnum->isScoped() != SecondEnum->isScoped()) { + DiagError(FirstEnum, SingleScopedEnum) << FirstEnum->isScoped(); + DiagNote(SecondEnum, SingleScopedEnum) << SecondEnum->isScoped(); + return true; + } + + if (FirstEnum->isScoped() && SecondEnum->isScoped()) { + if (FirstEnum->isScopedUsingClassTag() != + SecondEnum->isScopedUsingClassTag()) { + DiagError(FirstEnum, EnumTagKeywordMismatch) + << FirstEnum->isScopedUsingClassTag(); + DiagNote(SecondEnum, EnumTagKeywordMismatch) + << SecondEnum->isScopedUsingClassTag(); + return true; + } + } + + QualType FirstUnderlyingType = + FirstEnum->getIntegerTypeSourceInfo() + ? FirstEnum->getIntegerTypeSourceInfo()->getType() + : QualType(); + QualType SecondUnderlyingType = + SecondEnum->getIntegerTypeSourceInfo() + ? SecondEnum->getIntegerTypeSourceInfo()->getType() + : QualType(); + if (FirstUnderlyingType.isNull() != SecondUnderlyingType.isNull()) { + DiagError(FirstEnum, SingleSpecifiedType) + << !FirstUnderlyingType.isNull(); + DiagNote(SecondEnum, SingleSpecifiedType) + << !SecondUnderlyingType.isNull(); + return true; + } + + if (!FirstUnderlyingType.isNull() && !SecondUnderlyingType.isNull()) { + if (computeODRHash(FirstUnderlyingType) != + computeODRHash(SecondUnderlyingType)) { + DiagError(FirstEnum, DifferentSpecifiedTypes) + << FirstUnderlyingType; + DiagNote(SecondEnum, DifferentSpecifiedTypes) + << SecondUnderlyingType; + return true; + } + } + // Compare enum constants. using DeclHashes = - llvm::SmallVector, 4>; - auto PopulateHashes = [FirstEnum](DeclHashes &Hashes, EnumDecl *Enum) { - for (auto *D : Enum->decls()) { + llvm::SmallVector, 4>; + auto PopulateHashes = [FirstEnum](DeclHashes &Hashes, const EnumDecl *Enum) { + for (const Decl *D : Enum->decls()) { // Due to decl merging, the first EnumDecl is the parent of // Decls in both records. if (!ODRHash::isDeclToBeProcessed(D, FirstEnum)) @@ -11180,135 +11262,55 @@ }; DeclHashes FirstHashes; PopulateHashes(FirstHashes, FirstEnum); - bool Diagnosed = false; - for (auto &SecondEnum : Merge.second) { - - if (FirstEnum == SecondEnum) - continue; - - std::string SecondModule = - getOwningModuleNameForDiagnostic(SecondEnum); - - auto ODRDiagError = [FirstEnum, &FirstModule, - this](const auto *DiagAnchor, - ODREnumDifference DiffType) { - return Diag(DiagAnchor->getLocation(), - diag::err_module_odr_violation_enum) - << FirstEnum << FirstModule.empty() << FirstModule - << DiagAnchor->getSourceRange() << DiffType; - }; - auto ODRDiagNote = [&SecondModule, this](const auto *DiagAnchor, - ODREnumDifference DiffType) { - return Diag(DiagAnchor->getLocation(), - diag::note_module_odr_violation_enum) - << SecondModule << DiagAnchor->getSourceRange() << DiffType; - }; + DeclHashes SecondHashes; + PopulateHashes(SecondHashes, SecondEnum); + + if (FirstHashes.size() != SecondHashes.size()) { + DiagError(FirstEnum, DifferentNumberEnumConstants) + << (int)FirstHashes.size(); + DiagNote(SecondEnum, DifferentNumberEnumConstants) + << (int)SecondHashes.size(); + return true; + } - if (FirstEnum->isScoped() != SecondEnum->isScoped()) { - ODRDiagError(FirstEnum, SingleScopedEnum) << FirstEnum->isScoped(); - ODRDiagNote(SecondEnum, SingleScopedEnum) << SecondEnum->isScoped(); - Diagnosed = true; + for (unsigned I = 0, N = FirstHashes.size(); I < N; ++I) { + if (FirstHashes[I].second == SecondHashes[I].second) continue; + const EnumConstantDecl *FirstConstant = FirstHashes[I].first; + const EnumConstantDecl *SecondConstant = SecondHashes[I].first; + + if (FirstConstant->getDeclName() != SecondConstant->getDeclName()) { + DiagError(FirstConstant, EnumConstantName) + << I + 1 << FirstConstant; + DiagNote(SecondConstant, EnumConstantName) + << I + 1 << SecondConstant; + return true; } - if (FirstEnum->isScoped() && SecondEnum->isScoped()) { - if (FirstEnum->isScopedUsingClassTag() != - SecondEnum->isScopedUsingClassTag()) { - ODRDiagError(FirstEnum, EnumTagKeywordMismatch) - << FirstEnum->isScopedUsingClassTag(); - ODRDiagNote(SecondEnum, EnumTagKeywordMismatch) - << SecondEnum->isScopedUsingClassTag(); - Diagnosed = true; - continue; - } - } - - QualType FirstUnderlyingType = - FirstEnum->getIntegerTypeSourceInfo() - ? FirstEnum->getIntegerTypeSourceInfo()->getType() - : QualType(); - QualType SecondUnderlyingType = - SecondEnum->getIntegerTypeSourceInfo() - ? SecondEnum->getIntegerTypeSourceInfo()->getType() - : QualType(); - if (FirstUnderlyingType.isNull() != SecondUnderlyingType.isNull()) { - ODRDiagError(FirstEnum, SingleSpecifiedType) - << !FirstUnderlyingType.isNull(); - ODRDiagNote(SecondEnum, SingleSpecifiedType) - << !SecondUnderlyingType.isNull(); - Diagnosed = true; + const Expr *FirstInit = FirstConstant->getInitExpr(); + const Expr *SecondInit = SecondConstant->getInitExpr(); + if (!FirstInit && !SecondInit) continue; - } - - if (!FirstUnderlyingType.isNull() && !SecondUnderlyingType.isNull()) { - if (computeODRHash(FirstUnderlyingType) != - computeODRHash(SecondUnderlyingType)) { - ODRDiagError(FirstEnum, DifferentSpecifiedTypes) - << FirstUnderlyingType; - ODRDiagNote(SecondEnum, DifferentSpecifiedTypes) - << SecondUnderlyingType; - Diagnosed = true; - continue; - } - } - DeclHashes SecondHashes; - PopulateHashes(SecondHashes, SecondEnum); - - if (FirstHashes.size() != SecondHashes.size()) { - ODRDiagError(FirstEnum, DifferentNumberEnumConstants) - << (int)FirstHashes.size(); - ODRDiagNote(SecondEnum, DifferentNumberEnumConstants) - << (int)SecondHashes.size(); - Diagnosed = true; - continue; + if (!FirstInit || !SecondInit) { + DiagError(FirstConstant, EnumConstantSingleInitializer) + << I + 1 << FirstConstant << (FirstInit != nullptr); + DiagNote(SecondConstant, EnumConstantSingleInitializer) + << I + 1 << SecondConstant << (SecondInit != nullptr); + return true; } - for (unsigned I = 0; I < FirstHashes.size(); ++I) { - if (FirstHashes[I].second == SecondHashes[I].second) - continue; - const EnumConstantDecl *FirstConstant = FirstHashes[I].first; - const EnumConstantDecl *SecondConstant = SecondHashes[I].first; - - if (FirstConstant->getDeclName() != SecondConstant->getDeclName()) { - - ODRDiagError(FirstConstant, EnumConstantName) - << I + 1 << FirstConstant; - ODRDiagNote(SecondConstant, EnumConstantName) - << I + 1 << SecondConstant; - Diagnosed = true; - break; - } - - const Expr *FirstInit = FirstConstant->getInitExpr(); - const Expr *SecondInit = SecondConstant->getInitExpr(); - if (!FirstInit && !SecondInit) - continue; - - if (!FirstInit || !SecondInit) { - ODRDiagError(FirstConstant, EnumConstantSingleInitializer) - << I + 1 << FirstConstant << (FirstInit != nullptr); - ODRDiagNote(SecondConstant, EnumConstantSingleInitializer) - << I + 1 << SecondConstant << (SecondInit != nullptr); - Diagnosed = true; - break; - } - - if (computeODRHash(FirstInit) != computeODRHash(SecondInit)) { - ODRDiagError(FirstConstant, EnumConstantDifferentInitializer) - << I + 1 << FirstConstant; - ODRDiagNote(SecondConstant, EnumConstantDifferentInitializer) - << I + 1 << SecondConstant; - Diagnosed = true; - break; - } + if (computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagError(FirstConstant, EnumConstantDifferentInitializer) + << I + 1 << FirstConstant; + DiagNote(SecondConstant, EnumConstantDifferentInitializer) + << I + 1 << SecondConstant; + return true; } } - - (void)Diagnosed; - assert(Diagnosed && "Unable to emit ODR diagnostic."); + return false; } -} +// clang-format on void ASTReader::StartedDeserializing() { if (++NumCurrentElementsDeserializing == 1 && ReadTimer.get())