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,1705 +9722,1558 @@ // 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 - }; - - // 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) { - enum ODRFieldDifference { - FieldName, - FieldTypeName, - FieldSingleBitField, - FieldDifferentWidthBitField, - FieldSingleMutable, - FieldSingleInitializer, - FieldDifferentInitializers, - }; - - auto DiagError = [FirstRecord, FirstField, FirstModule, - this](ODRFieldDifference DiffType) { - return Diag(FirstField->getLocation(), - diag::err_module_odr_violation_field) - << FirstRecord << FirstModule.empty() << FirstModule - << FirstField->getSourceRange() << DiffType; - }; - auto DiagNote = [SecondField, SecondModule, - this](ODRFieldDifference DiffType) { - return Diag(SecondField->getLocation(), - diag::note_module_odr_violation_field) - << SecondModule << SecondField->getSourceRange() << DiffType; - }; - - IdentifierInfo *FirstII = FirstField->getIdentifier(); - IdentifierInfo *SecondII = SecondField->getIdentifier(); - if (FirstII->getName() != SecondII->getName()) { - DiagError(FieldName) << FirstII; - DiagNote(FieldName) << SecondII; - return true; - } - - assert(getContext().hasSameType(FirstField->getType(), - SecondField->getType())); + // 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; - QualType FirstType = FirstField->getType(); - QualType SecondType = SecondField->getType(); - if (computeODRHash(FirstType) != computeODRHash(SecondType)) { - DiagError(FieldTypeName) << FirstII << FirstType; - DiagNote(FieldTypeName) << SecondII << SecondType; - return true; + 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; + } } - const bool IsFirstBitField = FirstField->isBitField(); - const bool IsSecondBitField = SecondField->isBitField(); - if (IsFirstBitField != IsSecondBitField) { - DiagError(FieldSingleBitField) << FirstII << IsFirstBitField; - DiagNote(FieldSingleBitField) << SecondII << IsSecondBitField; - return true; + 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; } + } - if (IsFirstBitField && IsSecondBitField) { - unsigned FirstBitWidthHash = computeODRHash(FirstField->getBitWidth()); - unsigned SecondBitWidthHash = computeODRHash(SecondField->getBitWidth()); - if (FirstBitWidthHash != SecondBitWidthHash) { - DiagError(FieldDifferentWidthBitField) - << FirstII << FirstField->getBitWidth()->getSourceRange(); - DiagNote(FieldDifferentWidthBitField) - << SecondII << SecondField->getBitWidth()->getSourceRange(); - return true; + // 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."); + } - if (!PP.getLangOpts().CPlusPlus) - return false; - - const bool IsFirstMutable = FirstField->isMutable(); - const bool IsSecondMutable = SecondField->isMutable(); - if (IsFirstMutable != IsSecondMutable) { - DiagError(FieldSingleMutable) << FirstII << IsFirstMutable; - DiagNote(FieldSingleMutable) << SecondII << IsSecondMutable; - return true; - } - - const Expr *FirstInitializer = FirstField->getInClassInitializer(); - const Expr *SecondInitializer = SecondField->getInClassInitializer(); - if ((!FirstInitializer && SecondInitializer) || - (FirstInitializer && !SecondInitializer)) { - DiagError(FieldSingleInitializer) - << FirstII << (FirstInitializer != nullptr); - DiagNote(FieldSingleInitializer) - << SecondII << (SecondInitializer != nullptr); - return true; - } + // 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; - if (FirstInitializer && SecondInitializer) { - unsigned FirstInitHash = computeODRHash(FirstInitializer); - unsigned SecondInitHash = computeODRHash(SecondInitializer); - if (FirstInitHash != SecondInitHash) { - DiagError(FieldDifferentInitializers) - << FirstII << FirstInitializer->getSourceRange(); - DiagNote(FieldDifferentInitializers) - << SecondII << SecondInitializer->getSourceRange(); - return true; + 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."); + } +} - return false; +bool ODRDiagsEmitter::diagnoseSubMismatchField( + const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule, + const FieldDecl *FirstField, const FieldDecl *SecondField) const { + enum ODRFieldDifference { + FieldName, + FieldTypeName, + FieldSingleBitField, + FieldDifferentWidthBitField, + FieldSingleMutable, + FieldSingleInitializer, + FieldDifferentInitializers, }; - 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; - }; - - DeclarationName FirstName = FirstTD->getDeclName(); - DeclarationName SecondName = SecondTD->getDeclName(); - if (FirstName != SecondName) { - DiagError(TypedefName) << IsTypeAlias << FirstName; - DiagNote(TypedefName) << IsTypeAlias << SecondName; - return true; - } + auto DiagError = [FirstRecord, FirstField, FirstModule, + this](ODRFieldDifference DiffType) { + return Diag(FirstField->getLocation(), diag::err_module_odr_violation_field) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstField->getSourceRange() << DiffType; + }; + auto DiagNote = [SecondField, SecondModule, + this](ODRFieldDifference DiffType) { + return Diag(SecondField->getLocation(), + diag::note_module_odr_violation_field) + << SecondModule << SecondField->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; - } + IdentifierInfo *FirstII = FirstField->getIdentifier(); + IdentifierInfo *SecondII = SecondField->getIdentifier(); + if (FirstII->getName() != SecondII->getName()) { + DiagError(FieldName) << FirstII; + DiagNote(FieldName) << SecondII; + return true; + } - return false; - }; + assert(Context.hasSameType(FirstField->getType(), SecondField->getType())); - auto ODRDiagVar = [this](NamedDecl *FirstRecord, StringRef FirstModule, - StringRef SecondModule, const VarDecl *FirstVD, - const VarDecl *SecondVD) { - enum ODRVarDifference { - VarName, - VarType, - VarSingleInitializer, - VarDifferentInitializer, - VarConstexpr, - }; + QualType FirstType = FirstField->getType(); + QualType SecondType = SecondField->getType(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagError(FieldTypeName) << FirstII << FirstType; + DiagNote(FieldTypeName) << SecondII << SecondType; + return true; + } - auto DiagError = [FirstRecord, FirstVD, FirstModule, - this](ODRVarDifference DiffType) { - return Diag(FirstVD->getLocation(), - diag::err_module_odr_violation_variable) - << FirstRecord << FirstModule.empty() << FirstModule - << FirstVD->getSourceRange() << DiffType; - }; - auto DiagNote = [SecondVD, SecondModule, this](ODRVarDifference DiffType) { - return Diag(SecondVD->getLocation(), - diag::note_module_odr_violation_variable) - << SecondModule << SecondVD->getSourceRange() << DiffType; - }; + const bool IsFirstBitField = FirstField->isBitField(); + const bool IsSecondBitField = SecondField->isBitField(); + if (IsFirstBitField != IsSecondBitField) { + DiagError(FieldSingleBitField) << FirstII << IsFirstBitField; + DiagNote(FieldSingleBitField) << SecondII << IsSecondBitField; + return true; + } - DeclarationName FirstName = FirstVD->getDeclName(); - DeclarationName SecondName = SecondVD->getDeclName(); - if (FirstName != SecondName) { - DiagError(VarName) << FirstName; - DiagNote(VarName) << SecondName; + if (IsFirstBitField && IsSecondBitField) { + unsigned FirstBitWidthHash = computeODRHash(FirstField->getBitWidth()); + unsigned SecondBitWidthHash = computeODRHash(SecondField->getBitWidth()); + if (FirstBitWidthHash != SecondBitWidthHash) { + DiagError(FieldDifferentWidthBitField) + << FirstII << FirstField->getBitWidth()->getSourceRange(); + DiagNote(FieldDifferentWidthBitField) + << SecondII << SecondField->getBitWidth()->getSourceRange(); return true; } + } - QualType FirstType = FirstVD->getType(); - QualType SecondType = SecondVD->getType(); - if (computeODRHash(FirstType) != computeODRHash(SecondType)) { - DiagError(VarType) << FirstName << FirstType; - DiagNote(VarType) << SecondName << SecondType; - return true; - } + if (!LangOpts.CPlusPlus) + return false; - if (!PP.getLangOpts().CPlusPlus) - return false; + const bool IsFirstMutable = FirstField->isMutable(); + const bool IsSecondMutable = SecondField->isMutable(); + if (IsFirstMutable != IsSecondMutable) { + DiagError(FieldSingleMutable) << FirstII << IsFirstMutable; + DiagNote(FieldSingleMutable) << SecondII << IsSecondMutable; + return true; + } - const Expr *FirstInit = FirstVD->getInit(); - const Expr *SecondInit = SecondVD->getInit(); - if ((FirstInit == nullptr) != (SecondInit == nullptr)) { - DiagError(VarSingleInitializer) - << FirstName << (FirstInit == nullptr) - << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); - DiagNote(VarSingleInitializer) - << SecondName << (SecondInit == nullptr) - << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); - return true; - } + const Expr *FirstInitializer = FirstField->getInClassInitializer(); + const Expr *SecondInitializer = SecondField->getInClassInitializer(); + if ((!FirstInitializer && SecondInitializer) || + (FirstInitializer && !SecondInitializer)) { + DiagError(FieldSingleInitializer) + << FirstII << (FirstInitializer != nullptr); + DiagNote(FieldSingleInitializer) + << SecondII << (SecondInitializer != nullptr); + return true; + } - if (FirstInit && SecondInit && - computeODRHash(FirstInit) != computeODRHash(SecondInit)) { - DiagError(VarDifferentInitializer) - << FirstName << FirstInit->getSourceRange(); - DiagNote(VarDifferentInitializer) - << SecondName << SecondInit->getSourceRange(); + if (FirstInitializer && SecondInitializer) { + unsigned FirstInitHash = computeODRHash(FirstInitializer); + unsigned SecondInitHash = computeODRHash(SecondInitializer); + if (FirstInitHash != SecondInitHash) { + DiagError(FieldDifferentInitializers) + << FirstII << FirstInitializer->getSourceRange(); + DiagNote(FieldDifferentInitializers) + << SecondII << SecondInitializer->getSourceRange(); return true; } + } - const bool FirstIsConstexpr = FirstVD->isConstexpr(); - const bool SecondIsConstexpr = SecondVD->isConstexpr(); - if (FirstIsConstexpr != SecondIsConstexpr) { - DiagError(VarConstexpr) << FirstName << FirstIsConstexpr; - DiagNote(VarConstexpr) << SecondName << SecondIsConstexpr; - return true; - } - return false; - }; + 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)); - } +bool ODRDiagsEmitter::diagnoseSubMismatchTypedef( + const NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule, + const TypedefNameDecl *FirstTD, const TypedefNameDecl *SecondTD, + bool IsTypeAlias) const { + enum ODRTypedefDifference { + TypedefName, + TypedefType, }; - struct DiffResult { - Decl *FirstDecl = nullptr, *SecondDecl = nullptr; - ODRMismatchDecl FirstDiffType = Other, SecondDiffType = Other; + 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; }; - // 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) { - assert(D && "valid Decl required"); - switch (D->getKind()) { - default: - return Other; - case Decl::AccessSpec: - switch (D->getAccess()) { - case AS_public: - return PublicSpecifer; - case AS_private: - return PrivateSpecifer; - case AS_protected: - return ProtectedSpecifer; - case AS_none: - break; - } - llvm_unreachable("Invalid access specifier"); - case Decl::StaticAssert: - return StaticAssert; - case Decl::Field: - return Field; - case Decl::CXXMethod: - case Decl::CXXConstructor: - case Decl::CXXDestructor: - return CXXMethod; - case Decl::TypeAlias: - return TypeAlias; - case Decl::Typedef: - return TypeDef; - case Decl::Var: - return Var; - case Decl::Friend: - return Friend; - case Decl::FunctionTemplate: - return FunctionTemplate; - } - }; + DeclarationName FirstName = FirstTD->getDeclName(); + DeclarationName SecondName = SecondTD->getDeclName(); + if (FirstName != SecondName) { + DiagError(TypedefName) << IsTypeAlias << FirstName; + DiagNote(TypedefName) << IsTypeAlias << SecondName; + return true; + } - DiffResult DR; - auto FirstIt = FirstHashes.begin(); - auto SecondIt = SecondHashes.begin(); - while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { - if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() && - FirstIt->second == SecondIt->second) { - ++FirstIt; - ++SecondIt; - continue; - } + 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; +} - DR.FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; - DR.SecondDecl = - SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; +bool ODRDiagsEmitter::diagnoseSubMismatchVar(const NamedDecl *FirstRecord, + StringRef FirstModule, + StringRef SecondModule, + const VarDecl *FirstVD, + const VarDecl *SecondVD) const { + enum ODRVarDifference { + VarName, + VarType, + VarSingleInitializer, + VarDifferentInitializer, + VarConstexpr, + }; - DR.FirstDiffType = - DR.FirstDecl ? DifferenceSelector(DR.FirstDecl) : EndOfClass; - DR.SecondDiffType = - DR.SecondDecl ? DifferenceSelector(DR.SecondDecl) : EndOfClass; - return DR; - } - return DR; + auto DiagError = [FirstRecord, FirstVD, FirstModule, + this](ODRVarDifference DiffType) { + return Diag(FirstVD->getLocation(), diag::err_module_odr_violation_variable) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstVD->getSourceRange() << DiffType; + }; + auto DiagNote = [SecondVD, SecondModule, this](ODRVarDifference DiffType) { + return Diag(SecondVD->getLocation(), + diag::note_module_odr_violation_variable) + << SecondModule << SecondVD->getSourceRange() << DiffType; }; - // 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) { - Diag(FirstRecord->getLocation(), - diag::err_module_odr_violation_different_definitions) - << FirstRecord << FirstModule.empty() << FirstModule; + DeclarationName FirstName = FirstVD->getDeclName(); + DeclarationName SecondName = SecondVD->getDeclName(); + if (FirstName != SecondName) { + DiagError(VarName) << FirstName; + DiagNote(VarName) << SecondName; + return true; + } - if (DR.FirstDecl) { - Diag(DR.FirstDecl->getLocation(), diag::note_first_module_difference) - << FirstRecord << DR.FirstDecl->getSourceRange(); - } + QualType FirstType = FirstVD->getType(); + QualType SecondType = SecondVD->getType(); + if (computeODRHash(FirstType) != computeODRHash(SecondType)) { + DiagError(VarType) << FirstName << FirstType; + DiagNote(VarType) << SecondName << SecondType; + return true; + } - Diag(SecondRecord->getLocation(), - diag::note_module_odr_violation_different_definitions) - << SecondModule; + if (!LangOpts.CPlusPlus) + return false; - if (DR.SecondDecl) { - Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference) - << DR.SecondDecl->getSourceRange(); - } - }; + const Expr *FirstInit = FirstVD->getInit(); + const Expr *SecondInit = SecondVD->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + DiagError(VarSingleInitializer) + << FirstName << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + DiagNote(VarSingleInitializer) + << SecondName << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + return true; + } - auto DiagnoseODRMismatch = [this](DiffResult &DR, NamedDecl *FirstRecord, - StringRef FirstModule, - NamedDecl *SecondRecord, - StringRef SecondModule) { - auto GetMismatchedDeclLoc = [](const NamedDecl *Container, - ODRMismatchDecl DiffType, const Decl *D) { - SourceLocation Loc; - SourceRange Range; - auto *Tag = dyn_cast(Container); - if (DiffType == EndOfClass && Tag) { - Loc = Tag->getBraceRange().getEnd(); - } else { - Loc = D->getLocation(); - Range = D->getSourceRange(); - } - return std::make_pair(Loc, Range); - }; + if (FirstInit && SecondInit && + computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagError(VarDifferentInitializer) + << FirstName << FirstInit->getSourceRange(); + DiagNote(VarDifferentInitializer) + << SecondName << SecondInit->getSourceRange(); + return true; + } - auto FirstDiagInfo = - GetMismatchedDeclLoc(FirstRecord, DR.FirstDiffType, DR.FirstDecl); - Diag(FirstDiagInfo.first, diag::err_module_odr_violation_mismatch_decl) - << FirstRecord << FirstModule.empty() << FirstModule - << FirstDiagInfo.second << DR.FirstDiffType; + const bool FirstIsConstexpr = FirstVD->isConstexpr(); + const bool SecondIsConstexpr = SecondVD->isConstexpr(); + if (FirstIsConstexpr != SecondIsConstexpr) { + DiagError(VarConstexpr) << FirstName << FirstIsConstexpr; + DiagNote(VarConstexpr) << SecondName << SecondIsConstexpr; + return true; + } + return false; +} - auto SecondDiagInfo = - GetMismatchedDeclLoc(SecondRecord, DR.SecondDiffType, DR.SecondDecl); - Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl) - << SecondModule << SecondDiagInfo.second << DR.SecondDiffType; +ODRDiagsEmitter::DiffResult +ODRDiagsEmitter::FindTypeDiffs(DeclHashes &FirstHashes, + DeclHashes &SecondHashes) { + auto DifferenceSelector = [](const Decl *D) { + assert(D && "valid Decl required"); + switch (D->getKind()) { + default: + return Other; + case Decl::AccessSpec: + switch (D->getAccess()) { + case AS_public: + return PublicSpecifer; + case AS_private: + return PrivateSpecifer; + case AS_protected: + return ProtectedSpecifer; + case AS_none: + break; + } + llvm_unreachable("Invalid access specifier"); + case Decl::StaticAssert: + return StaticAssert; + case Decl::Field: + return Field; + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + return CXXMethod; + case Decl::TypeAlias: + return TypeAlias; + case Decl::Typedef: + return TypeDef; + case Decl::Var: + return Var; + case Decl::Friend: + return Friend; + case Decl::FunctionTemplate: + return FunctionTemplate; + } }; - // 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) + DiffResult DR; + auto FirstIt = FirstHashes.begin(); + auto SecondIt = SecondHashes.begin(); + while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { + if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() && + FirstIt->second == SecondIt->second) { + ++FirstIt; + ++SecondIt; continue; + } - 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; + DR.FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; + DR.SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; - std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); + DR.FirstDiffType = + DR.FirstDecl ? DifferenceSelector(DR.FirstDecl) : EndOfClass; + DR.SecondDiffType = + DR.SecondDecl ? DifferenceSelector(DR.SecondDecl) : EndOfClass; + return DR; + } + return DR; +} - auto *FirstDD = FirstRecord->DefinitionData; - auto *SecondDD = RecordPair.second; +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; - assert(FirstDD && SecondDD && "Definitions without DefinitionData"); + if (DR.FirstDecl) { + Diag(DR.FirstDecl->getLocation(), diag::note_first_module_difference) + << FirstRecord << DR.FirstDecl->getSourceRange(); + } - // 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()); - }; + Diag(SecondRecord->getLocation(), + diag::note_module_odr_violation_different_definitions) + << SecondModule; - 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 (DR.SecondDecl) { + Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference) + << DR.SecondDecl->getSourceRange(); + } +} - 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(); - 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 (I != FirstNumBases) { - Diagnosed = true; - break; - } - } +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; + SourceRange Range; + auto *Tag = dyn_cast(Container); + if (DiffType == EndOfClass && Tag) { + Loc = Tag->getBraceRange().getEnd(); + } else { + Loc = D->getLocation(); + Range = D->getSourceRange(); + } + return std::make_pair(Loc, Range); + }; - const ClassTemplateDecl *FirstTemplate = - FirstRecord->getDescribedClassTemplate(); - const ClassTemplateDecl *SecondTemplate = - SecondRecord->getDescribedClassTemplate(); + auto FirstDiagInfo = + GetMismatchedDeclLoc(FirstRecord, DR.FirstDiffType, DR.FirstDecl); + Diag(FirstDiagInfo.first, diag::err_module_odr_violation_mismatch_decl) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstDiagInfo.second << DR.FirstDiffType; - assert(!FirstTemplate == !SecondTemplate && - "Both pointers should be null or non-null"); + auto SecondDiagInfo = + GetMismatchedDeclLoc(SecondRecord, DR.SecondDiffType, DR.SecondDecl); + Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl) + << SecondModule << SecondDiagInfo.second << DR.SecondDiffType; +} - if (FirstTemplate && SecondTemplate) { - DeclHashes FirstTemplateHashes; - DeclHashes SecondTemplateHashes; +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; - auto PopulateTemplateParameterHashs = [](DeclHashes &Hashes, - const ClassTemplateDecl *TD) { - for (auto *D : TD->getTemplateParameters()->asArray()) { - Hashes.emplace_back(D, computeODRHash(D)); - } - }; + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); + 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()); + }; - PopulateTemplateParameterHashs(FirstTemplateHashes, FirstTemplate); - PopulateTemplateParameterHashs(SecondTemplateHashes, SecondTemplate); + 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; + } - assert(FirstTemplateHashes.size() == SecondTemplateHashes.size() && - "Number of template parameters should be equal."); + if (FirstNumVBases != SecondNumVBases) { + DiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), + NumVBases) + << FirstNumVBases; + DiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), + NumVBases) + << SecondNumVBases; + return true; + } - auto FirstIt = FirstTemplateHashes.begin(); - auto FirstEnd = FirstTemplateHashes.end(); - auto SecondIt = SecondTemplateHashes.begin(); - for (; FirstIt != FirstEnd; ++FirstIt, ++SecondIt) { - if (FirstIt->second == SecondIt->second) - continue; + 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; + } - const NamedDecl* FirstDecl = cast(FirstIt->first); - const NamedDecl* SecondDecl = cast(SecondIt->first); - - assert(FirstDecl->getKind() == SecondDecl->getKind() && - "Parameter Decl's should be the same kind."); - - enum ODRTemplateDifference { - ParamEmptyName, - ParamName, - ParamSingleDefaultArgument, - ParamDifferentDefaultArgument, - }; - - 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 (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 (FirstIt != FirstEnd) { - 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; } + } + } - DeclHashes FirstHashes; - DeclHashes SecondHashes; - const DeclContext *DC = FirstRecord; - PopulateHashes(FirstHashes, FirstRecord, DC); - PopulateHashes(SecondHashes, SecondRecord, DC); + const ClassTemplateDecl *FirstTemplate = + FirstRecord->getDescribedClassTemplate(); + const ClassTemplateDecl *SecondTemplate = + SecondRecord->getDescribedClassTemplate(); - DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes); - ODRMismatchDecl FirstDiffType = DR.FirstDiffType; - ODRMismatchDecl SecondDiffType = DR.SecondDiffType; - const Decl *FirstDecl = DR.FirstDecl; - const Decl *SecondDecl = DR.SecondDecl; + assert(!FirstTemplate == !SecondTemplate && + "Both pointers should be null or non-null"); - if (FirstDiffType == Other || SecondDiffType == Other) { - DiagnoseODRUnexpected(DR, FirstRecord, FirstModule, SecondRecord, - SecondModule); - Diagnosed = true; - break; - } + 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; - if (FirstDiffType != SecondDiffType) { - DiagnoseODRMismatch(DR, FirstRecord, FirstModule, SecondRecord, - SecondModule); - Diagnosed = true; - break; - } + assert(FirstDecl->getKind() == SecondDecl->getKind() && + "Parameter Decl's should be the same kind."); - // 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; - }; - auto ODRDiagDeclNote = [&SecondModule, - this](SourceLocation Loc, SourceRange Range, - ODRCXXRecordDifference DiffType) { - return Diag(Loc, diag::note_module_odr_violation_record) - << SecondModule << Range << DiffType; + enum ODRTemplateDifference { + ParamEmptyName, + ParamName, + ParamSingleDefaultArgument, + ParamDifferentDefaultArgument, }; - 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; - } + 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; - 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; - } - 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; - }; + 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 (FirstMethodType != SecondMethodType || FirstName != SecondName) { - DiagMethodError(MethodName); - DiagMethodNote(MethodName); - 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)); + } + }; - const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); - const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); - if (FirstDeleted != SecondDeleted) { - DiagMethodError(MethodDeleted) << FirstDeleted; - DiagMethodNote(MethodDeleted) << SecondDeleted; - Diagnosed = true; - break; - } + 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; + } - const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); - const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); - if (FirstDefaulted != SecondDefaulted) { - DiagMethodError(MethodDefaulted) << FirstDefaulted; - DiagMethodNote(MethodDefaulted) << SecondDefaulted; - Diagnosed = true; - break; - } + if (FirstDiffType != SecondDiffType) { + diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule, + SecondRecord, SecondModule); + return true; + } - 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; - } + // 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; + }; - // 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; - } + 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; + } - const bool FirstVolatile = FirstMethod->isVolatile(); - const bool SecondVolatile = SecondMethod->isVolatile(); - if (FirstVolatile != SecondVolatile) { - DiagMethodError(MethodVolatile) << FirstVolatile; - DiagMethodNote(MethodVolatile) << SecondVolatile; - 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; + } - const bool FirstConst = FirstMethod->isConst(); - const bool SecondConst = SecondMethod->isConst(); - if (FirstConst != SecondConst) { - DiagMethodError(MethodConst) << FirstConst; - DiagMethodNote(MethodConst) << SecondConst; - Diagnosed = true; - break; - } + if (FirstStr && SecondStr && + FirstStr->getString() != SecondStr->getString()) { + DiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(), + StaticAssertMessage); + DiagNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(), + StaticAssertMessage); + return true; + } + break; + } - const bool FirstInline = FirstMethod->isInlineSpecified(); - const bool SecondInline = SecondMethod->isInlineSpecified(); - if (FirstInline != SecondInline) { - DiagMethodError(MethodInline) << FirstInline; - DiagMethodNote(MethodInline) << SecondInline; - Diagnosed = true; - break; - } + case Field: { + if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl))) + return true; + break; + } - const unsigned FirstNumParameters = FirstMethod->param_size(); - const unsigned SecondNumParameters = SecondMethod->param_size(); - if (FirstNumParameters != SecondNumParameters) { - DiagMethodError(MethodNumberParameters) << FirstNumParameters; - DiagMethodNote(MethodNumberParameters) << SecondNumParameters; - Diagnosed = 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; + }; + 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; + }; - // 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; - } - - if (const DecayedType *ParamDecayedType = - SecondParamType->getAs()) { - DiagMethodNote(MethodParameterType) - << (I + 1) << SecondParamType << true - << ParamDecayedType->getOriginalType(); - } else { - DiagMethodNote(MethodParameterType) - << (I + 1) << SecondParamType << false; - } - ParameterMismatch = true; - break; - } + if (FirstMethodType != SecondMethodType || FirstName != SecondName) { + DiagMethodError(MethodName); + DiagMethodNote(MethodName); + 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; - } + const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); + const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); + if (FirstDeleted != SecondDeleted) { + DiagMethodError(MethodDeleted) << FirstDeleted; + DiagMethodNote(MethodDeleted) << SecondDeleted; + return true; + } - 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; - } + const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); + const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); + if (FirstDefaulted != SecondDefaulted) { + DiagMethodError(MethodDefaulted) << FirstDefaulted; + DiagMethodNote(MethodDefaulted) << SecondDefaulted; + return true; + } - if (FirstInit && SecondInit && - computeODRHash(FirstInit) != computeODRHash(SecondInit)) { - DiagMethodError(MethodParameterDifferentDefaultArgument) - << (I + 1) << FirstInit->getSourceRange(); - DiagMethodNote(MethodParameterDifferentDefaultArgument) - << (I + 1) << SecondInit->getSourceRange(); - ParameterMismatch = 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; + } - if (ParameterMismatch) { - 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 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 bool FirstVolatile = FirstMethod->isVolatile(); + const bool SecondVolatile = SecondMethod->isVolatile(); + if (FirstVolatile != SecondVolatile) { + DiagMethodError(MethodVolatile) << FirstVolatile; + DiagMethodNote(MethodVolatile) << SecondVolatile; + 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; - } + const bool FirstConst = FirstMethod->isConst(); + const bool SecondConst = SecondMethod->isConst(); + if (FirstConst != SecondConst) { + DiagMethodError(MethodConst) << FirstConst; + DiagMethodNote(MethodConst) << SecondConst; + 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; - } - - DiagMethodError(MethodDifferentTemplateArgument) - << FirstTA << i + 1; - DiagMethodNote(MethodDifferentTemplateArgument) - << SecondTA << i + 1; - TemplateArgumentMismatch = true; - break; - } + const bool FirstInline = FirstMethod->isInlineSpecified(); + const bool SecondInline = SecondMethod->isInlineSpecified(); + if (FirstInline != SecondInline) { + DiagMethodError(MethodInline) << FirstInline; + DiagMethodNote(MethodInline) << SecondInline; + return true; + } - if (TemplateArgumentMismatch) { - Diagnosed = 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; + } - // 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; I < FirstNumParameters; ++I) { + const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); + const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); - // 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; + 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 (HasFirstBody && HasSecondBody) { - DiagMethodError(MethodDifferentBody); - DiagMethodNote(MethodDifferentBody); - Diagnosed = 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; + } - break; + DeclarationName FirstParamName = FirstParam->getDeclName(); + DeclarationName SecondParamName = SecondParam->getDeclName(); + if (FirstParamName != SecondParamName) { + DiagMethodError(MethodParameterName) << (I + 1) << FirstParamName; + DiagMethodNote(MethodParameterName) << (I + 1) << SecondParamName; + return true; } - case TypeAlias: - case TypeDef: { - Diagnosed = ODRDiagTypeDefOrAlias( - FirstRecord, FirstModule, SecondModule, - cast(FirstDecl), cast(SecondDecl), - FirstDiffType == TypeAlias); - 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; } - case Var: { - Diagnosed = - ODRDiagVar(FirstRecord, FirstModule, SecondModule, - cast(FirstDecl), cast(SecondDecl)); - break; + + if (FirstInit && SecondInit && + computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagMethodError(MethodParameterDifferentDefaultArgument) + << (I + 1) << FirstInit->getSourceRange(); + DiagMethodNote(MethodParameterDifferentDefaultArgument) + << (I + 1) << SecondInit->getSourceRange(); + return true; } - case Friend: { - const FriendDecl *FirstFriend = cast(FirstDecl); - const FriendDecl *SecondFriend = cast(SecondDecl); + } - const NamedDecl *FirstND = FirstFriend->getFriendDecl(); - const NamedDecl *SecondND = SecondFriend->getFriendDecl(); + const TemplateArgumentList *FirstTemplateArgs = + FirstMethod->getTemplateSpecializationArgs(); + const TemplateArgumentList *SecondTemplateArgs = + SecondMethod->getTemplateSpecializationArgs(); - TypeSourceInfo *FirstTSI = FirstFriend->getFriendType(); - TypeSourceInfo *SecondTSI = SecondFriend->getFriendType(); + if ((FirstTemplateArgs && !SecondTemplateArgs) || + (!FirstTemplateArgs && SecondTemplateArgs)) { + DiagMethodError(MethodNoTemplateArguments) + << (FirstTemplateArgs != nullptr); + DiagMethodNote(MethodNoTemplateArguments) + << (SecondTemplateArgs != nullptr); + return true; + } - if (FirstND && SecondND) { - ODRDiagDeclError(FirstFriend->getFriendLoc(), - FirstFriend->getSourceRange(), FriendFunction) - << FirstND; - ODRDiagDeclNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendFunction) - << SecondND; - 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; + } - 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; - } + for (unsigned i = 0, e = FirstExpandedList.size(); i != e; ++i) { + const TemplateArgument &FirstTA = *FirstExpandedList[i], + &SecondTA = *SecondExpandedList[i]; + if (computeODRHash(FirstTA) == computeODRHash(SecondTA)) + continue; - ODRDiagDeclError(FirstFriend->getFriendLoc(), - FirstFriend->getSourceRange(), FriendTypeFunction) - << (FirstTSI == nullptr); - ODRDiagDeclNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendTypeFunction) - << (SecondTSI == nullptr); - Diagnosed = true; - break; + DiagMethodError(MethodDifferentTemplateArgument) << FirstTA << i + 1; + DiagMethodNote(MethodDifferentTemplateArgument) << SecondTA << i + 1; + 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; - } + // 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(); + }; - bool ParameterMismatch = false; - for (unsigned i = 0, e = FirstTPL->size(); i != e; ++i) { - NamedDecl *FirstParam = FirstTPL->getParam(i); - NamedDecl *SecondParam = SecondTPL->getParam(i); + // 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; + } - 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 (HasFirstBody && HasSecondBody) { + DiagMethodError(MethodDifferentBody); + DiagMethodNote(MethodDifferentBody); + return true; + } - DiagTemplateError(FunctionTemplateParameterDifferentKind) - << (i + 1) << GetParamType(FirstParam); - DiagTemplateNote(FunctionTemplateParameterDifferentKind) - << (i + 1) << GetParamType(SecondParam); - ParameterMismatch = true; - break; - } + break; + } - if (FirstParam->getName() != SecondParam->getName()) { - DiagTemplateError(FunctionTemplateParameterName) - << (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam; - DiagTemplateNote(FunctionTemplateParameterName) - << (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam; - ParameterMismatch = true; - break; - } + 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 (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 (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; - } - } - - if (FirstTTPD->isParameterPack() != - SecondTTPD->isParameterPack()) { - DiagTemplateError(FunctionTemplatePackParameter) - << (i + 1) << FirstTTPD->isParameterPack(); - DiagTemplateNote(FunctionTemplatePackParameter) - << (i + 1) << SecondTTPD->isParameterPack(); - ParameterMismatch = true; - break; - } - } + const NamedDecl *FirstND = FirstFriend->getFriendDecl(); + const NamedDecl *SecondND = SecondFriend->getFriendDecl(); - 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; - } - - 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 (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 (FirstTTPD->isParameterPack() != - SecondTTPD->isParameterPack()) { - DiagTemplateError(FunctionTemplatePackParameter) - << (i + 1) << FirstTTPD->isParameterPack(); - DiagTemplateNote(FunctionTemplatePackParameter) - << (i + 1) << SecondTTPD->isParameterPack(); - ParameterMismatch = true; - break; - } - } + TypeSourceInfo *FirstTSI = FirstFriend->getFriendType(); + TypeSourceInfo *SecondTSI = SecondFriend->getFriendType(); - 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); - ParameterMismatch = true; - break; - } - - 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 (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 (FirstNTTPD->isParameterPack() != - SecondNTTPD->isParameterPack()) { - DiagTemplateError(FunctionTemplatePackParameter) - << (i + 1) << FirstNTTPD->isParameterPack(); - DiagTemplateNote(FunctionTemplatePackParameter) - << (i + 1) << SecondNTTPD->isParameterPack(); - ParameterMismatch = true; - break; - } - } - } + if (FirstND && SecondND) { + DiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(), + FriendFunction) + << FirstND; + DiagNote(SecondFriend->getFriendLoc(), SecondFriend->getSourceRange(), + FriendFunction) + << SecondND; + return true; + } - if (ParameterMismatch) { - Diagnosed = true; - break; - } + 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; + } - break; - } - } + 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); - if (Diagnosed) - continue; + TemplateParameterList *FirstTPL = FirstTemplate->getTemplateParameters(); + TemplateParameterList *SecondTPL = SecondTemplate->getTemplateParameters(); - 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; - } + 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; + }; - 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; + if (FirstTPL->size() != SecondTPL->size()) { + DiagTemplateError(FunctionTemplateDifferentNumberParameters) + << FirstTPL->size(); + DiagTemplateNote(FunctionTemplateDifferentNumberParameters) + << SecondTPL->size(); + return true; } - } - // Issue ODR failures diagnostics for functions. - for (auto &Merge : FunctionOdrMergeFailures) { - enum ODRFunctionDifference { - ReturnType, - ParameterName, - ParameterType, - ParameterSingleDefaultArgument, - ParameterDifferentDefaultArgument, - FunctionBody, - }; + for (unsigned i = 0, e = FirstTPL->size(); i != e; ++i) { + NamedDecl *FirstParam = FirstTPL->getParam(i); + NamedDecl *SecondParam = SecondTPL->getParam(i); - FunctionDecl *FirstFunction = Merge.first; - std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction); + 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; + } + }; - bool Diagnosed = false; - for (auto &SecondFunction : Merge.second) { + DiagTemplateError(FunctionTemplateParameterDifferentKind) + << (i + 1) << GetParamType(FirstParam); + DiagTemplateNote(FunctionTemplateParameterDifferentKind) + << (i + 1) << GetParamType(SecondParam); + return true; + } - if (FirstFunction == SecondFunction) - continue; + if (FirstParam->getName() != SecondParam->getName()) { + DiagTemplateError(FunctionTemplateParameterName) + << (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam; + DiagTemplateNote(FunctionTemplateParameterName) + << (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam; + return true; + } - std::string SecondModule = - getOwningModuleNameForDiagnostic(SecondFunction); + 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; + } - 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 (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; + } + } - 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; + if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { + DiagTemplateError(FunctionTemplatePackParameter) + << (i + 1) << FirstTTPD->isParameterPack(); + DiagTemplateNote(FunctionTemplatePackParameter) + << (i + 1) << SecondTTPD->isParameterPack(); + return true; + } } - assert(FirstFunction->param_size() == SecondFunction->param_size() && - "Merged functions with different number of parameters"); + if (isa(FirstParam) && + isa(SecondParam)) { + TemplateTemplateParmDecl *FirstTTPD = + cast(FirstParam); + TemplateTemplateParmDecl *SecondTTPD = + cast(SecondParam); - 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); + TemplateParameterList *FirstTPL = FirstTTPD->getTemplateParameters(); + TemplateParameterList *SecondTPL = SecondTTPD->getTemplateParameters(); - assert(getContext().hasSameType(FirstParam->getType(), - SecondParam->getType()) && - "Merged function has different parameter types."); + auto ComputeTemplateParameterListODRHash = + [](const TemplateParameterList *TPL) { + assert(TPL); + ODRHash Hasher; + Hasher.AddTemplateParameterList(TPL); + return Hasher.CalculateHash(); + }; - 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; - }; + if (ComputeTemplateParameterListODRHash(FirstTPL) != + ComputeTemplateParameterListODRHash(SecondTPL)) { + DiagTemplateError(FunctionTemplateParameterDifferentType) << (i + 1); + DiagTemplateNote(FunctionTemplateParameterDifferentType) << (i + 1); + return true; + } - 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; - } + 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 (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; + 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; } - ParameterMismatch = true; - break; } - 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; + if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { + DiagTemplateError(FunctionTemplatePackParameter) + << (i + 1) << FirstTTPD->isParameterPack(); + DiagTemplateNote(FunctionTemplatePackParameter) + << (i + 1) << SecondTTPD->isParameterPack(); + return true; } + } - 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 (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; } - assert(computeODRHash(FirstParam) == computeODRHash(SecondParam) && - "Undiagnosed parameter difference."); - } + 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 (ParameterMismatch) { - Diagnosed = 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 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; + if (FirstNTTPD->isParameterPack() != SecondNTTPD->isParameterPack()) { + DiagTemplateError(FunctionTemplatePackParameter) + << (i + 1) << FirstNTTPD->isParameterPack(); + DiagTemplateNote(FunctionTemplatePackParameter) + << (i + 1) << SecondNTTPD->isParameterPack(); + return true; + } + } } - (void)Diagnosed; - assert(Diagnosed && "Unable to emit ODR diagnostic."); + break; + } } - // Issue ODR failures diagnostics for enums. - for (auto &Merge : EnumOdrMergeFailures) { - enum ODREnumDifference { - SingleScopedEnum, - EnumTagKeywordMismatch, - SingleSpecifiedType, - DifferentSpecifiedTypes, - DifferentNumberEnumConstants, - EnumConstantName, - EnumConstantSingleInitializer, - EnumConstantDifferentInitializer, - }; + 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; +} - // 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; +bool ODRDiagsEmitter::diagnoseMismatch( + const FunctionDecl *FirstFunction, + const FunctionDecl *SecondFunction) const { + if (FirstFunction == SecondFunction) + return false; - EnumDecl *FirstEnum = Merge.first; - std::string FirstModule = getOwningModuleNameForDiagnostic(FirstEnum); - - using DeclHashes = - llvm::SmallVector, 4>; - auto PopulateHashes = [FirstEnum](DeclHashes &Hashes, EnumDecl *Enum) { - for (auto *D : Enum->decls()) { - // Due to decl merging, the first EnumDecl is the parent of - // Decls in both records. - if (!ODRHash::isDeclToBeProcessed(D, FirstEnum)) - continue; - assert(isa(D) && "Unexpected Decl kind"); - Hashes.emplace_back(cast(D), computeODRHash(D)); - } - }; - DeclHashes FirstHashes; - PopulateHashes(FirstHashes, FirstEnum); - bool Diagnosed = false; - for (auto &SecondEnum : Merge.second) { + // Keep in sync with select options in err_module_odr_violation_function. + enum ODRFunctionDifference { + ReturnType, + ParameterName, + ParameterType, + ParameterSingleDefaultArgument, + ParameterDifferentDefaultArgument, + FunctionBody, + }; - if (FirstEnum == SecondEnum) - continue; + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondFunction); - std::string SecondModule = - getOwningModuleNameForDiagnostic(SecondEnum); + 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; + }; - 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; - }; + 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; + } - if (FirstEnum->isScoped() != SecondEnum->isScoped()) { - ODRDiagError(FirstEnum, SingleScopedEnum) << FirstEnum->isScoped(); - ODRDiagNote(SecondEnum, SingleScopedEnum) << SecondEnum->isScoped(); - Diagnosed = true; - continue; - } + assert(FirstFunction->param_size() == SecondFunction->param_size() && + "Merged functions with different number of parameters"); - if (FirstEnum->isScoped() && SecondEnum->isScoped()) { - if (FirstEnum->isScopedUsingClassTag() != - SecondEnum->isScopedUsingClassTag()) { - ODRDiagError(FirstEnum, EnumTagKeywordMismatch) - << FirstEnum->isScopedUsingClassTag(); - ODRDiagNote(SecondEnum, EnumTagKeywordMismatch) - << SecondEnum->isScopedUsingClassTag(); - Diagnosed = true; - continue; - } - } + 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); - 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; - continue; + assert(Context.hasSameType(FirstParam->getType(), SecondParam->getType()) && + "Merged function has different parameter types."); + + 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; + }; + + 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 (!FirstUnderlyingType.isNull() && !SecondUnderlyingType.isNull()) { - if (computeODRHash(FirstUnderlyingType) != - computeODRHash(SecondUnderlyingType)) { - ODRDiagError(FirstEnum, DifferentSpecifiedTypes) - << FirstUnderlyingType; - ODRDiagNote(SecondEnum, DifferentSpecifiedTypes) - << SecondUnderlyingType; - Diagnosed = true; - continue; - } + 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; + } - DeclHashes SecondHashes; - PopulateHashes(SecondHashes, SecondEnum); + 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 (FirstHashes.size() != SecondHashes.size()) { - ODRDiagError(FirstEnum, DifferentNumberEnumConstants) - << (int)FirstHashes.size(); - ODRDiagNote(SecondEnum, DifferentNumberEnumConstants) - << (int)SecondHashes.size(); - Diagnosed = true; - continue; - } + 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; + } - 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; + assert(computeODRHash(FirstParam) == computeODRHash(SecondParam) && + "Undiagnosed parameter difference."); + } - if (FirstConstant->getDeclName() != SecondConstant->getDeclName()) { + // 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; +} - ODRDiagError(FirstConstant, EnumConstantName) - << I + 1 << FirstConstant; - ODRDiagNote(SecondConstant, EnumConstantName) - << I + 1 << SecondConstant; - Diagnosed = true; - break; - } +bool ODRDiagsEmitter::diagnoseMismatch(const EnumDecl *FirstEnum, + const EnumDecl *SecondEnum) const { + if (FirstEnum == SecondEnum) + return false; - const Expr *FirstInit = FirstConstant->getInitExpr(); - const Expr *SecondInit = SecondConstant->getInitExpr(); - if (!FirstInit && !SecondInit) - continue; + // Keep in sync with select options in err_module_odr_violation_enum. + enum ODREnumDifference { + SingleScopedEnum, + EnumTagKeywordMismatch, + SingleSpecifiedType, + DifferentSpecifiedTypes, + DifferentNumberEnumConstants, + EnumConstantName, + EnumConstantSingleInitializer, + EnumConstantDifferentInitializer, + }; - if (!FirstInit || !SecondInit) { - ODRDiagError(FirstConstant, EnumConstantSingleInitializer) - << I + 1 << FirstConstant << (FirstInit != nullptr); - ODRDiagNote(SecondConstant, EnumConstantSingleInitializer) - << I + 1 << SecondConstant << (SecondInit != nullptr); - Diagnosed = true; - break; - } + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstEnum); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondEnum); - if (computeODRHash(FirstInit) != computeODRHash(SecondInit)) { - ODRDiagError(FirstConstant, EnumConstantDifferentInitializer) - << I + 1 << FirstConstant; - ODRDiagNote(SecondConstant, EnumConstantDifferentInitializer) - << I + 1 << SecondConstant; - Diagnosed = true; - break; - } - } + 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; } + } - (void)Diagnosed; - assert(Diagnosed && "Unable to emit ODR diagnostic."); + 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, 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)) + continue; + assert(isa(D) && "Unexpected Decl kind"); + Hashes.emplace_back(cast(D), computeODRHash(D)); + } + }; + DeclHashes FirstHashes; + PopulateHashes(FirstHashes, FirstEnum); + DeclHashes SecondHashes; + PopulateHashes(SecondHashes, SecondEnum); + + if (FirstHashes.size() != SecondHashes.size()) { + DiagError(FirstEnum, DifferentNumberEnumConstants) + << (int)FirstHashes.size(); + DiagNote(SecondEnum, DifferentNumberEnumConstants) + << (int)SecondHashes.size(); + return 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; + } + + const Expr *FirstInit = FirstConstant->getInitExpr(); + const Expr *SecondInit = SecondConstant->getInitExpr(); + if (!FirstInit && !SecondInit) + continue; + + if (!FirstInit || !SecondInit) { + DiagError(FirstConstant, EnumConstantSingleInitializer) + << I + 1 << FirstConstant << (FirstInit != nullptr); + DiagNote(SecondConstant, EnumConstantSingleInitializer) + << I + 1 << SecondConstant << (SecondInit != nullptr); + return true; + } + + if (computeODRHash(FirstInit) != computeODRHash(SecondInit)) { + DiagError(FirstConstant, EnumConstantDifferentInitializer) + << I + 1 << FirstConstant; + DiagNote(SecondConstant, EnumConstantDifferentInitializer) + << I + 1 << SecondConstant; + return true; + } + } + return false; } void ASTReader::StartedDeserializing() {