diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3716,6 +3716,7 @@ // to save some space. Use the provided accessors to access it. public: friend class DeclContext; + friend class ASTDeclReader; /// Enum that represents the different ways arguments are passed to and /// returned from function calls. This takes into account the target-specific /// and version-specific rules along with the rules determined by the @@ -3960,9 +3961,19 @@ /// nullptr is returned if no named data member exists. const FieldDecl *findFirstNamedDataMember() const; + /// Get precomputed ODRHash or add a new one. + unsigned getODRHash(); + private: /// Deserialize just the fields. void LoadFieldsFromExternalStorage() const; + + /// True if a valid hash is stored in ODRHash. + bool hasODRHash() const { return RecordDeclBits.HasODRHash; } + void setHasODRHash(bool Hash = true) { RecordDeclBits.HasODRHash = Hash; } + + /// Store the ODR hash for this decl. + unsigned ODRHash; }; class FileScopeAsmDecl : public Decl { diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1452,10 +1452,13 @@ /// Represents the way this type is passed to a function. uint64_t ArgPassingRestrictions : 2; + + /// True if a valid hash is stored in ODRHash. + uint64_t HasODRHash : 1; }; /// Number of non-inherited bits in RecordDeclBitfields. - enum { NumRecordDeclBits = 14 }; + enum { NumRecordDeclBits = 15 }; /// Stores the bits used by OMPDeclareReductionDecl. /// If modified NumOMPDeclareReductionDeclBits and the accessor diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h --- a/clang/include/clang/AST/ODRHash.h +++ b/clang/include/clang/AST/ODRHash.h @@ -55,6 +55,10 @@ // more information than the AddDecl class. void AddCXXRecordDecl(const CXXRecordDecl *Record); + // Use this for ODR checking records in C/Objective-C between modules. This + // method compares more information than the AddDecl class. + void AddRecordDecl(const RecordDecl *Record); + // Use this for ODR checking functions between modules. This method compares // more information than the AddDecl class. SkipBody will process the // hash as if the function has no body. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1107,6 +1107,10 @@ llvm::SmallDenseMap, 2> PendingEnumOdrMergeFailures; + /// C/ObjC definitions in which the structural equivalence check fails + llvm::SmallDenseMap, 2> + PendingRecordOdrMergeFailures; + /// DeclContexts in which we have diagnosed an ODR violation. llvm::SmallPtrSet DiagnosedOdrMergeFailures; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4360,6 +4360,8 @@ setHasNonTrivialToPrimitiveCopyCUnion(false); setParamDestroyedInCallee(false); setArgPassingRestrictions(APK_CanPassInRegs); + setHasODRHash(false); + ODRHash = 0; } RecordDecl *RecordDecl::Create(const ASTContext &C, TagKind TK, DeclContext *DC, @@ -4507,6 +4509,19 @@ return nullptr; } +unsigned RecordDecl::getODRHash() { + if (hasODRHash()) + return ODRHash; + + // Only calculate hash on first call of getODRHash per record. + class ODRHash Hash; + Hash.AddRecordDecl(this); + setHasODRHash(); + ODRHash = Hash.CalculateHash(); + + return ODRHash; +} + //===----------------------------------------------------------------------===// // BlockDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -484,7 +484,7 @@ return DefinitionData->ODRHash; // Only calculate hash on first call of getODRHash per record. - ODRHash Hash; + class ODRHash Hash; Hash.AddCXXRecordDecl(getDefinition()); DefinitionData->HasODRHash = true; DefinitionData->ODRHash = Hash.CalculateHash(); diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -464,6 +464,21 @@ ODRDeclVisitor(ID, *this).Visit(D); } +void ODRHash::AddRecordDecl(const RecordDecl *Record) { + AddDecl(Record); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector Decls; + for (Decl *SubDecl : Record->decls()) + if (isWhitelistedDecl(SubDecl, Record)) + Decls.push_back(SubDecl); + + ID.AddInteger(Decls.size()); + for (auto SubDecl : Decls) + AddSubDecl(SubDecl); +} + void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { assert(Record && Record->hasDefinition() && "Expected non-null record to be a definition."); 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 @@ -9267,7 +9267,8 @@ void ASTReader::diagnoseOdrViolations() { if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() && PendingFunctionOdrMergeFailures.empty() && - PendingEnumOdrMergeFailures.empty()) + PendingEnumOdrMergeFailures.empty() && + PendingRecordOdrMergeFailures.empty()) return; // Trigger the import of the full definition of each class that had any @@ -9289,6 +9290,15 @@ } } + // Trigger the import of the full definition of each record in C/ObjC. + auto RecordOdrMergeFailures = std::move(PendingRecordOdrMergeFailures); + PendingRecordOdrMergeFailures.clear(); + for (auto &Merge : RecordOdrMergeFailures) { + Merge.first->decls_begin(); + for (auto &D : Merge.second) + D->decls_begin(); + } + // Trigger the import of functions. auto FunctionOdrMergeFailures = std::move(PendingFunctionOdrMergeFailures); PendingFunctionOdrMergeFailures.clear(); @@ -9331,6 +9341,11 @@ DeclContext *CanonDef = D->getDeclContext(); + // Skip ODR checking for structs without a definition for C/ObjC mode. + if (RecordDecl *RD = dyn_cast(CanonDef)) + if (!RD->isCompleteDefinition()) + continue; + bool Found = false; const Decl *DCanon = D->getCanonicalDecl(); @@ -9396,7 +9411,7 @@ } if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty() && - EnumOdrMergeFailures.empty()) + EnumOdrMergeFailures.empty() && RecordOdrMergeFailures.empty()) return; // Ensure we don't accidentally recursively enter deserialization while @@ -9439,6 +9454,435 @@ return Hash.CalculateHash(); }; + // 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::isWhiteListedDecl + enum RecordDiffType { + EndOfClass, + PublicSpecifer, + PrivateSpecifer, + ProtectedSpecifer, + StaticAssert, + Field, + CXXMethod, + TypeAlias, + TypeDef, + Var, + Friend, + FunctionTemplate, + Other + }; + + // Used with err_module_odr_violation_mismatch_decl_diff and + // note_module_odr_violation_mismatch_decl_diff + enum ODRDeclDifference { + StaticAssertCondition, + StaticAssertMessage, + StaticAssertOnlyMessage, + FieldName, + FieldTypeName, + FieldSingleBitField, + FieldDifferentWidthBitField, + FieldSingleMutable, + FieldSingleInitializer, + FieldDifferentInitializers, + MethodName, + MethodDeleted, + MethodDefaulted, + MethodVirtual, + MethodStatic, + MethodVolatile, + MethodConst, + MethodInline, + MethodNumberParameters, + MethodParameterType, + MethodParameterName, + MethodParameterSingleDefaultArgument, + MethodParameterDifferentDefaultArgument, + MethodNoTemplateArguments, + MethodDifferentNumberTemplateArguments, + MethodDifferentTemplateArgument, + MethodSingleBody, + MethodDifferentBody, + TypedefName, + TypedefType, + VarName, + VarType, + VarSingleInitializer, + VarDifferentInitializer, + VarConstexpr, + FriendTypeFunction, + FriendType, + FriendFunction, + FunctionTemplateDifferentNumberParameters, + FunctionTemplateParameterDifferentKind, + FunctionTemplateParameterName, + FunctionTemplateParameterSingleDefaultArgument, + FunctionTemplateParameterDifferentDefaultArgument, + FunctionTemplateParameterDifferentType, + FunctionTemplatePackParameter, + }; + + // 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 ODRDiagDeclError = [this](NamedDecl *FirstRecord, StringRef FirstModule, + SourceLocation Loc, SourceRange Range, + ODRDeclDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_mismatch_decl_diff) + << FirstRecord << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto ODRDiagDeclNote = [this](StringRef SecondModule, SourceLocation Loc, + SourceRange Range, ODRDeclDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << Range << DiffType; + }; + + auto ODRDiagField = [this, &ODRDiagDeclError, &ODRDiagDeclNote, + &ComputeQualTypeODRHash, &ComputeODRHash]( + NamedDecl *FirstRecord, StringRef FirstModule, + StringRef SecondModule, FieldDecl *FirstField, + FieldDecl *SecondField) { + IdentifierInfo *FirstII = FirstField->getIdentifier(); + IdentifierInfo *SecondII = SecondField->getIdentifier(); + if (FirstII->getName() != SecondII->getName()) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldName) + << FirstII; + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldName) + << SecondII; + + return true; + } + + assert(getContext().hasSameType(FirstField->getType(), + SecondField->getType())); + + QualType FirstType = FirstField->getType(); + QualType SecondType = SecondField->getType(); + if (ComputeQualTypeODRHash(FirstType) != + ComputeQualTypeODRHash(SecondType)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldTypeName) + << FirstII << FirstType; + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldTypeName) + << SecondII << SecondType; + + return true; + } + + const bool IsFirstBitField = FirstField->isBitField(); + const bool IsSecondBitField = SecondField->isBitField(); + if (IsFirstBitField != IsSecondBitField) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldSingleBitField) + << FirstII << IsFirstBitField; + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldSingleBitField) + << SecondII << IsSecondBitField; + return true; + } + + if (IsFirstBitField && IsSecondBitField) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), + FieldDifferentWidthBitField) + << FirstII << FirstField->getBitWidth()->getSourceRange(); + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), + FieldDifferentWidthBitField) + << SecondII << SecondField->getBitWidth()->getSourceRange(); + return true; + } + + if (!PP.getLangOpts().CPlusPlus) + return false; + + const bool IsFirstMutable = FirstField->isMutable(); + const bool IsSecondMutable = SecondField->isMutable(); + if (IsFirstMutable != IsSecondMutable) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldSingleMutable) + << FirstII << IsFirstMutable; + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldSingleMutable) + << SecondII << IsSecondMutable; + return true; + } + + const Expr *FirstInitializer = FirstField->getInClassInitializer(); + const Expr *SecondInitializer = SecondField->getInClassInitializer(); + if ((!FirstInitializer && SecondInitializer) || + (FirstInitializer && !SecondInitializer)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldSingleInitializer) + << FirstII << (FirstInitializer != nullptr); + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldSingleInitializer) + << SecondII << (SecondInitializer != nullptr); + return true; + } + + if (FirstInitializer && SecondInitializer) { + unsigned FirstInitHash = ComputeODRHash(FirstInitializer); + unsigned SecondInitHash = ComputeODRHash(SecondInitializer); + if (FirstInitHash != SecondInitHash) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), + FieldDifferentInitializers) + << FirstII << FirstInitializer->getSourceRange(); + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), + FieldDifferentInitializers) + << SecondII << SecondInitializer->getSourceRange(); + return true; + } + } + + return false; + }; + + auto ODRDiagTypeDefOrAlias = + [&ODRDiagDeclError, &ODRDiagDeclNote, &ComputeQualTypeODRHash]( + NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule, + TypedefNameDecl *FirstTD, TypedefNameDecl *SecondTD, + bool IsTypeAlias) { + auto FirstName = FirstTD->getDeclName(); + auto SecondName = SecondTD->getDeclName(); + if (FirstName != SecondName) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstTD->getLocation(), + FirstTD->getSourceRange(), TypedefName) + << IsTypeAlias << FirstName; + ODRDiagDeclNote(SecondModule, SecondTD->getLocation(), + SecondTD->getSourceRange(), TypedefName) + << IsTypeAlias << SecondName; + return true; + } + + QualType FirstType = FirstTD->getUnderlyingType(); + QualType SecondType = SecondTD->getUnderlyingType(); + if (ComputeQualTypeODRHash(FirstType) != + ComputeQualTypeODRHash(SecondType)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstTD->getLocation(), + FirstTD->getSourceRange(), TypedefType) + << IsTypeAlias << FirstName << FirstType; + ODRDiagDeclNote(SecondModule, SecondTD->getLocation(), + SecondTD->getSourceRange(), TypedefType) + << IsTypeAlias << SecondName << SecondType; + return true; + } + + return false; + }; + + auto ODRDiagVar = [&ODRDiagDeclError, &ODRDiagDeclNote, + &ComputeQualTypeODRHash, &ComputeODRHash, + this](NamedDecl *FirstRecord, StringRef FirstModule, + StringRef SecondModule, VarDecl *FirstVD, + VarDecl *SecondVD) { + auto FirstName = FirstVD->getDeclName(); + auto SecondName = SecondVD->getDeclName(); + if (FirstName != SecondName) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarName) + << FirstName; + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarName) + << SecondName; + return true; + } + + QualType FirstType = FirstVD->getType(); + QualType SecondType = SecondVD->getType(); + if (ComputeQualTypeODRHash(FirstType) != + ComputeQualTypeODRHash(SecondType)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarType) + << FirstName << FirstType; + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarType) + << SecondName << SecondType; + return true; + } + + if (!PP.getLangOpts().CPlusPlus) + return false; + + const Expr *FirstInit = FirstVD->getInit(); + const Expr *SecondInit = SecondVD->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarSingleInitializer) + << FirstName << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarSingleInitializer) + << SecondName << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + return true; + } + + if (FirstInit && SecondInit && + ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarDifferentInitializer) + << FirstName << FirstInit->getSourceRange(); + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarDifferentInitializer) + << SecondName << SecondInit->getSourceRange(); + return true; + } + + const bool FirstIsConstexpr = FirstVD->isConstexpr(); + const bool SecondIsConstexpr = SecondVD->isConstexpr(); + if (FirstIsConstexpr != SecondIsConstexpr) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarConstexpr) + << FirstName << FirstIsConstexpr; + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarConstexpr) + << SecondName << SecondIsConstexpr; + return true; + } + return false; + }; + + 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; + } + }; + + using DeclHashes = llvm::SmallVector, 4>; + auto PopulateHashes = [&ComputeSubDeclODRHash](DeclHashes &Hashes, + RecordDecl *Record, + const DeclContext *DC) { + for (auto *D : Record->decls()) { + if (!ODRHash::isWhitelistedDecl(D, DC)) + continue; + Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); + } + }; + + struct DiffResult { + Decl *FirstDecl = nullptr, *SecondDecl = nullptr; + RecordDiffType FirstDiffType = Other, SecondDiffType = Other; + }; + + // If there is a diagnoseable difference, FirstDiffType and + // SecondDiffType will not be Other and FirstDecl and SecondDecl will be + // filled in if not EndOfClass. + auto FindTypeDiffs = [&DifferenceSelector](DeclHashes &FirstHashes, + DeclHashes &SecondHashes) { + 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; + } + + DR.FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; + DR.SecondDecl = + SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; + + DR.FirstDiffType = + DR.FirstDecl ? DifferenceSelector(DR.FirstDecl) : EndOfClass; + DR.SecondDiffType = + DR.SecondDecl ? DifferenceSelector(DR.SecondDecl) : EndOfClass; + return DR; + } + return DR; + }; + + 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; + + if (DR.FirstDecl) { + Diag(DR.FirstDecl->getLocation(), diag::note_first_module_difference) + << FirstRecord << DR.FirstDecl->getSourceRange(); + } + + Diag(SecondRecord->getLocation(), + diag::note_module_odr_violation_different_definitions) + << SecondModule; + + if (DR.SecondDecl) { + Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference) + << DR.SecondDecl->getSourceRange(); + } + }; + + auto DiagnoseODRMismatch = + [this](DiffResult &DR, TagDecl *FirstRecord, StringRef FirstModule, + TagDecl *SecondRecord, StringRef SecondModule) { + SourceLocation FirstLoc; + SourceRange FirstRange; + if (DR.FirstDiffType == EndOfClass) { + FirstLoc = FirstRecord->getBraceRange().getEnd(); + } else { + FirstLoc = DR.FirstDecl->getLocation(); + FirstRange = DR.FirstDecl->getSourceRange(); + } + Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl) + << FirstRecord << FirstModule.empty() << FirstModule << FirstRange + << DR.FirstDiffType; + + SourceLocation SecondLoc; + SourceRange SecondRange; + if (DR.SecondDiffType == EndOfClass) { + SecondLoc = SecondRecord->getBraceRange().getEnd(); + } else { + SecondLoc = DR.SecondDecl->getLocation(); + SecondRange = DR.SecondDecl->getSourceRange(); + } + Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl) + << SecondModule << SecondRange << DR.SecondDiffType; + }; + // Issue any pending ODR-failure diagnostics. for (auto &Merge : OdrMergeFailures) { // If we've already pointed out a specific problem with this class, don't @@ -9472,16 +9916,16 @@ BaseVirtual, BaseAccess, }; - auto ODRDiagError = [FirstRecord, &FirstModule, - this](SourceLocation Loc, SourceRange Range, - ODRDefinitionDataDifference DiffType) { + 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 ODRDiagNote = [&SecondModule, - this](SourceLocation Loc, SourceRange Range, - ODRDefinitionDataDifference DiffType) { + auto ODRDiagBaseNote = [&SecondModule, + this](SourceLocation Loc, SourceRange Range, + ODRDefinitionDataDifference DiffType) { return Diag(Loc, diag::note_module_odr_violation_definition_data) << SecondModule << Range << DiffType; }; @@ -9500,22 +9944,22 @@ }; if (FirstNumBases != SecondNumBases) { - ODRDiagError(FirstRecord->getLocation(), GetSourceRange(FirstDD), - NumBases) + ODRDiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), + NumBases) << FirstNumBases; - ODRDiagNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), - NumBases) + ODRDiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), + NumBases) << SecondNumBases; Diagnosed = true; break; } if (FirstNumVBases != SecondNumVBases) { - ODRDiagError(FirstRecord->getLocation(), GetSourceRange(FirstDD), - NumVBases) + ODRDiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), + NumVBases) << FirstNumVBases; - ODRDiagNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), - NumVBases) + ODRDiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), + NumVBases) << SecondNumVBases; Diagnosed = true; break; @@ -9529,33 +9973,33 @@ auto SecondBase = SecondBases[i]; if (ComputeQualTypeODRHash(FirstBase.getType()) != ComputeQualTypeODRHash(SecondBase.getType())) { - ODRDiagError(FirstRecord->getLocation(), FirstBase.getSourceRange(), - BaseType) + ODRDiagBaseError(FirstRecord->getLocation(), + FirstBase.getSourceRange(), BaseType) << (i + 1) << FirstBase.getType(); - ODRDiagNote(SecondRecord->getLocation(), - SecondBase.getSourceRange(), BaseType) + ODRDiagBaseNote(SecondRecord->getLocation(), + SecondBase.getSourceRange(), BaseType) << (i + 1) << SecondBase.getType(); break; } if (FirstBase.isVirtual() != SecondBase.isVirtual()) { - ODRDiagError(FirstRecord->getLocation(), FirstBase.getSourceRange(), - BaseVirtual) + ODRDiagBaseError(FirstRecord->getLocation(), + FirstBase.getSourceRange(), BaseVirtual) << (i + 1) << FirstBase.isVirtual() << FirstBase.getType(); - ODRDiagNote(SecondRecord->getLocation(), - SecondBase.getSourceRange(), BaseVirtual) + ODRDiagBaseNote(SecondRecord->getLocation(), + SecondBase.getSourceRange(), BaseVirtual) << (i + 1) << SecondBase.isVirtual() << SecondBase.getType(); break; } if (FirstBase.getAccessSpecifierAsWritten() != SecondBase.getAccessSpecifierAsWritten()) { - ODRDiagError(FirstRecord->getLocation(), FirstBase.getSourceRange(), - BaseAccess) + ODRDiagBaseError(FirstRecord->getLocation(), + FirstBase.getSourceRange(), BaseAccess) << (i + 1) << FirstBase.getType() << (int)FirstBase.getAccessSpecifierAsWritten(); - ODRDiagNote(SecondRecord->getLocation(), - SecondBase.getSourceRange(), BaseAccess) + ODRDiagBaseNote(SecondRecord->getLocation(), + SecondBase.getSourceRange(), BaseAccess) << (i + 1) << SecondBase.getType() << (int)SecondBase.getAccessSpecifierAsWritten(); break; @@ -9568,8 +10012,6 @@ } } - using DeclHashes = llvm::SmallVector, 4>; - const ClassTemplateDecl *FirstTemplate = FirstRecord->getDescribedClassTemplate(); const ClassTemplateDecl *SecondTemplate = @@ -9610,16 +10052,16 @@ if (FirstIt->second == SecondIt->second) continue; - auto ODRDiagError = [FirstRecord, &FirstModule, - this](SourceLocation Loc, SourceRange Range, - ODRTemplateDifference DiffType) { + auto ODRDiagTemplateError = [FirstRecord, &FirstModule, this]( + SourceLocation Loc, SourceRange Range, + ODRTemplateDifference DiffType) { return Diag(Loc, diag::err_module_odr_violation_template_parameter) << FirstRecord << FirstModule.empty() << FirstModule << Range << DiffType; }; - auto ODRDiagNote = [&SecondModule, - this](SourceLocation Loc, SourceRange Range, - ODRTemplateDifference DiffType) { + auto ODRDiagTemplateNote = [&SecondModule, this]( + SourceLocation Loc, SourceRange Range, + ODRTemplateDifference DiffType) { return Diag(Loc, diag::note_module_odr_violation_template_parameter) << SecondModule << Range << DiffType; }; @@ -9640,11 +10082,13 @@ SecondName.isIdentifier() && !SecondName.getAsIdentifierInfo(); assert((!FirstNameEmpty || !SecondNameEmpty) && "Both template parameters cannot be unnamed."); - ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(), - FirstNameEmpty ? ParamEmptyName : ParamName) + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + FirstNameEmpty ? ParamEmptyName : ParamName) << FirstName; - ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(), - SecondNameEmpty ? ParamEmptyName : ParamName) + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + SecondNameEmpty ? ParamEmptyName : ParamName) << SecondName; break; } @@ -9663,13 +10107,13 @@ !SecondParam->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstDecl->getLocation(), - FirstDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasFirstDefaultArgument; - ODRDiagNote(SecondDecl->getLocation(), - SecondDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasSecondDefaultArgument; break; } @@ -9677,10 +10121,12 @@ assert(HasFirstDefaultArgument && HasSecondDefaultArgument && "Expecting default arguments."); - ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(), - ParamDifferentDefaultArgument); - ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(), - ParamDifferentDefaultArgument); + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamDifferentDefaultArgument); + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamDifferentDefaultArgument); break; } @@ -9695,13 +10141,13 @@ !SecondParam->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstDecl->getLocation(), - FirstDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasFirstDefaultArgument; - ODRDiagNote(SecondDecl->getLocation(), - SecondDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasSecondDefaultArgument; break; } @@ -9709,10 +10155,12 @@ assert(HasFirstDefaultArgument && HasSecondDefaultArgument && "Expecting default arguments."); - ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(), - ParamDifferentDefaultArgument); - ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(), - ParamDifferentDefaultArgument); + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamDifferentDefaultArgument); + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamDifferentDefaultArgument); break; } @@ -9728,13 +10176,13 @@ !SecondParam->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstDecl->getLocation(), - FirstDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasFirstDefaultArgument; - ODRDiagNote(SecondDecl->getLocation(), - SecondDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasSecondDefaultArgument; break; } @@ -9742,10 +10190,12 @@ assert(HasFirstDefaultArgument && HasSecondDefaultArgument && "Expecting default arguments."); - ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(), - ParamDifferentDefaultArgument); - ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(), - ParamDifferentDefaultArgument); + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamDifferentDefaultArgument); + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamDifferentDefaultArgument); break; } @@ -9762,224 +10212,35 @@ DeclHashes FirstHashes; DeclHashes SecondHashes; - - auto PopulateHashes = [&ComputeSubDeclODRHash, FirstRecord]( - DeclHashes &Hashes, CXXRecordDecl *Record) { - for (auto *D : Record->decls()) { - // Due to decl merging, the first CXXRecordDecl is the parent of - // Decls in both records. - if (!ODRHash::isWhitelistedDecl(D, FirstRecord)) - continue; - Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); - } - }; - PopulateHashes(FirstHashes, FirstRecord); - PopulateHashes(SecondHashes, SecondRecord); - - // 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::isWhiteListedDecl - enum { - EndOfClass, - PublicSpecifer, - PrivateSpecifer, - ProtectedSpecifer, - StaticAssert, - Field, - CXXMethod, - TypeAlias, - TypeDef, - Var, - Friend, - FunctionTemplate, - Other - } FirstDiffType = Other, - SecondDiffType = Other; - - auto DifferenceSelector = [](Decl *D) { - assert(D && "valid Decl required"); - switch (D->getKind()) { - default: - return Other; - case Decl::AccessSpec: - switch (D->getAccess()) { - case AS_public: - return PublicSpecifer; - case AS_private: - return PrivateSpecifer; - case AS_protected: - return ProtectedSpecifer; - case AS_none: - 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; - } - }; - - Decl *FirstDecl = nullptr; - Decl *SecondDecl = nullptr; - auto FirstIt = FirstHashes.begin(); - auto SecondIt = SecondHashes.begin(); - - // If there is a diagnoseable difference, FirstDiffType and - // SecondDiffType will not be Other and FirstDecl and SecondDecl will be - // filled in if not EndOfClass. - while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { - if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() && - FirstIt->second == SecondIt->second) { - ++FirstIt; - ++SecondIt; - continue; - } - - FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; - SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; - - FirstDiffType = FirstDecl ? DifferenceSelector(FirstDecl) : EndOfClass; - SecondDiffType = - SecondDecl ? DifferenceSelector(SecondDecl) : EndOfClass; - - break; - } - + const DeclContext *DC = FirstRecord; + PopulateHashes(FirstHashes, FirstRecord, DC); + PopulateHashes(SecondHashes, SecondRecord, DC); + + auto DR = FindTypeDiffs(FirstHashes, SecondHashes); + RecordDiffType FirstDiffType = DR.FirstDiffType; + RecordDiffType SecondDiffType = DR.SecondDiffType; + Decl *FirstDecl = DR.FirstDecl; + Decl *SecondDecl = DR.SecondDecl; + + // Reaching this point means an unexpected Decl was encountered + // or no difference was detected. This causes a generic error + // message to be emitted. if (FirstDiffType == Other || SecondDiffType == Other) { - // Reaching this point means an unexpected Decl was encountered - // or no difference was detected. This causes a generic error - // message to be emitted. - Diag(FirstRecord->getLocation(), - diag::err_module_odr_violation_different_definitions) - << FirstRecord << FirstModule.empty() << FirstModule; - - if (FirstDecl) { - Diag(FirstDecl->getLocation(), diag::note_first_module_difference) - << FirstRecord << FirstDecl->getSourceRange(); - } - - Diag(SecondRecord->getLocation(), - diag::note_module_odr_violation_different_definitions) - << SecondModule; - - if (SecondDecl) { - Diag(SecondDecl->getLocation(), diag::note_second_module_difference) - << SecondDecl->getSourceRange(); - } - + DiagnoseODRUnexpected(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); Diagnosed = true; break; } if (FirstDiffType != SecondDiffType) { - SourceLocation FirstLoc; - SourceRange FirstRange; - if (FirstDiffType == EndOfClass) { - FirstLoc = FirstRecord->getBraceRange().getEnd(); - } else { - FirstLoc = FirstIt->first->getLocation(); - FirstRange = FirstIt->first->getSourceRange(); - } - Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl) - << FirstRecord << FirstModule.empty() << FirstModule << FirstRange - << FirstDiffType; - - SourceLocation SecondLoc; - SourceRange SecondRange; - if (SecondDiffType == EndOfClass) { - SecondLoc = SecondRecord->getBraceRange().getEnd(); - } else { - SecondLoc = SecondDecl->getLocation(); - SecondRange = SecondDecl->getSourceRange(); - } - Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl) - << SecondModule << SecondRange << SecondDiffType; + DiagnoseODRMismatch(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); Diagnosed = true; break; } assert(FirstDiffType == SecondDiffType); - // Used with err_module_odr_violation_mismatch_decl_diff and - // note_module_odr_violation_mismatch_decl_diff - enum ODRDeclDifference { - StaticAssertCondition, - StaticAssertMessage, - StaticAssertOnlyMessage, - FieldName, - FieldTypeName, - FieldSingleBitField, - FieldDifferentWidthBitField, - FieldSingleMutable, - FieldSingleInitializer, - FieldDifferentInitializers, - MethodName, - MethodDeleted, - MethodDefaulted, - MethodVirtual, - MethodStatic, - MethodVolatile, - MethodConst, - MethodInline, - MethodNumberParameters, - MethodParameterType, - MethodParameterName, - MethodParameterSingleDefaultArgument, - MethodParameterDifferentDefaultArgument, - MethodNoTemplateArguments, - MethodDifferentNumberTemplateArguments, - MethodDifferentTemplateArgument, - MethodSingleBody, - MethodDifferentBody, - TypedefName, - TypedefType, - VarName, - VarType, - VarSingleInitializer, - VarDifferentInitializer, - VarConstexpr, - FriendTypeFunction, - FriendType, - FriendFunction, - FunctionTemplateDifferentNumberParameters, - FunctionTemplateParameterDifferentKind, - FunctionTemplateParameterName, - FunctionTemplateParameterSingleDefaultArgument, - FunctionTemplateParameterDifferentDefaultArgument, - FunctionTemplateParameterDifferentType, - FunctionTemplatePackParameter, - }; - - // These lambdas have the common portions of the ODR diagnostics. This - // has the same return as Diag(), so addition parameters can be passed - // in with operator<< - auto ODRDiagError = [FirstRecord, &FirstModule, this]( - SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) { - return Diag(Loc, diag::err_module_odr_violation_mismatch_decl_diff) - << FirstRecord << FirstModule.empty() << FirstModule << Range - << DiffType; - }; - auto ODRDiagNote = [&SecondModule, this]( - SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) { - return Diag(Loc, diag::note_module_odr_violation_mismatch_decl_diff) - << SecondModule << Range << DiffType; - }; - switch (FirstDiffType) { case Other: case EndOfClass: @@ -9997,10 +10258,10 @@ unsigned FirstODRHash = ComputeODRHash(FirstExpr); unsigned SecondODRHash = ComputeODRHash(SecondExpr); if (FirstODRHash != SecondODRHash) { - ODRDiagError(FirstExpr->getBeginLoc(), FirstExpr->getSourceRange(), - StaticAssertCondition); - ODRDiagNote(SecondExpr->getBeginLoc(), SecondExpr->getSourceRange(), - StaticAssertCondition); + ODRDiagDeclError(FirstRecord, FirstModule, FirstExpr->getBeginLoc(), + FirstExpr->getSourceRange(), StaticAssertCondition); + ODRDiagDeclNote(SecondModule, SecondExpr->getBeginLoc(), + SecondExpr->getSourceRange(), StaticAssertCondition); Diagnosed = true; break; } @@ -10025,9 +10286,11 @@ SecondLoc = SecondSA->getBeginLoc(); SecondRange = SecondSA->getSourceRange(); } - ODRDiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage) + ODRDiagDeclError(FirstRecord, FirstModule, FirstLoc, FirstRange, + StaticAssertOnlyMessage) << (FirstStr == nullptr); - ODRDiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage) + ODRDiagDeclNote(SecondModule, SecondLoc, SecondRange, + StaticAssertOnlyMessage) << (SecondStr == nullptr); Diagnosed = true; break; @@ -10035,118 +10298,19 @@ if (FirstStr && SecondStr && FirstStr->getString() != SecondStr->getString()) { - ODRDiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(), - StaticAssertMessage); - ODRDiagNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(), - StaticAssertMessage); + ODRDiagDeclError(FirstRecord, FirstModule, FirstStr->getBeginLoc(), + FirstStr->getSourceRange(), StaticAssertMessage); + ODRDiagDeclNote(SecondModule, SecondStr->getBeginLoc(), + SecondStr->getSourceRange(), StaticAssertMessage); Diagnosed = true; break; } break; } case Field: { - FieldDecl *FirstField = cast(FirstDecl); - FieldDecl *SecondField = cast(SecondDecl); - IdentifierInfo *FirstII = FirstField->getIdentifier(); - IdentifierInfo *SecondII = SecondField->getIdentifier(); - if (FirstII->getName() != SecondII->getName()) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldName) - << FirstII; - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldName) - << SecondII; - - Diagnosed = true; - break; - } - - assert(getContext().hasSameType(FirstField->getType(), - SecondField->getType())); - - QualType FirstType = FirstField->getType(); - QualType SecondType = SecondField->getType(); - if (ComputeQualTypeODRHash(FirstType) != - ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldTypeName) - << FirstII << FirstType; - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldTypeName) - << SecondII << SecondType; - - Diagnosed = true; - break; - } - - const bool IsFirstBitField = FirstField->isBitField(); - const bool IsSecondBitField = SecondField->isBitField(); - if (IsFirstBitField != IsSecondBitField) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldSingleBitField) - << FirstII << IsFirstBitField; - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldSingleBitField) - << SecondII << IsSecondBitField; - Diagnosed = true; - break; - } - - if (IsFirstBitField && IsSecondBitField) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldDifferentWidthBitField) - << FirstII << FirstField->getBitWidth()->getSourceRange(); - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldDifferentWidthBitField) - << SecondII << SecondField->getBitWidth()->getSourceRange(); - Diagnosed = true; - break; - } - - const bool IsFirstMutable = FirstField->isMutable(); - const bool IsSecondMutable = SecondField->isMutable(); - if (IsFirstMutable != IsSecondMutable) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldSingleMutable) - << FirstII << IsFirstMutable; - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldSingleMutable) - << SecondII << IsSecondMutable; - Diagnosed = true; - break; - } - - const Expr *FirstInitializer = FirstField->getInClassInitializer(); - const Expr *SecondInitializer = SecondField->getInClassInitializer(); - if ((!FirstInitializer && SecondInitializer) || - (FirstInitializer && !SecondInitializer)) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldSingleInitializer) - << FirstII << (FirstInitializer != nullptr); - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldSingleInitializer) - << SecondII << (SecondInitializer != nullptr); - Diagnosed = true; - break; - } - - if (FirstInitializer && SecondInitializer) { - unsigned FirstInitHash = ComputeODRHash(FirstInitializer); - unsigned SecondInitHash = ComputeODRHash(SecondInitializer); - if (FirstInitHash != SecondInitHash) { - ODRDiagError(FirstField->getLocation(), - FirstField->getSourceRange(), - FieldDifferentInitializers) - << FirstII << FirstInitializer->getSourceRange(); - ODRDiagNote(SecondField->getLocation(), - SecondField->getSourceRange(), - FieldDifferentInitializers) - << SecondII << SecondInitializer->getSourceRange(); - Diagnosed = true; - break; - } - } - + Diagnosed = ODRDiagField(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); break; } case CXXMethod: { @@ -10168,11 +10332,11 @@ auto FirstName = FirstMethod->getDeclName(); auto SecondName = SecondMethod->getDeclName(); if (FirstMethodType != SecondMethodType || FirstName != SecondName) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodName) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodName) << FirstMethodType << FirstName; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodName) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodName) << SecondMethodType << SecondName; Diagnosed = true; @@ -10182,12 +10346,12 @@ const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); if (FirstDeleted != SecondDeleted) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodDeleted) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodDeleted) << FirstMethodType << FirstName << FirstDeleted; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodDeleted) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodDeleted) << SecondMethodType << SecondName << SecondDeleted; Diagnosed = true; break; @@ -10196,12 +10360,12 @@ const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); if (FirstDefaulted != SecondDefaulted) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodDefaulted) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodDefaulted) << FirstMethodType << FirstName << FirstDefaulted; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodDefaulted) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodDefaulted) << SecondMethodType << SecondName << SecondDefaulted; Diagnosed = true; break; @@ -10213,11 +10377,11 @@ const bool SecondPure = SecondMethod->isPure(); if ((FirstVirtual || SecondVirtual) && (FirstVirtual != SecondVirtual || FirstPure != SecondPure)) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodVirtual) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodVirtual) << FirstMethodType << FirstName << FirstPure << FirstVirtual; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodVirtual) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodVirtual) << SecondMethodType << SecondName << SecondPure << SecondVirtual; Diagnosed = true; break; @@ -10231,11 +10395,11 @@ const bool FirstStatic = FirstStorage == SC_Static; const bool SecondStatic = SecondStorage == SC_Static; if (FirstStatic != SecondStatic) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodStatic) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodStatic) << FirstMethodType << FirstName << FirstStatic; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodStatic) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodStatic) << SecondMethodType << SecondName << SecondStatic; Diagnosed = true; break; @@ -10244,11 +10408,11 @@ const bool FirstVolatile = FirstMethod->isVolatile(); const bool SecondVolatile = SecondMethod->isVolatile(); if (FirstVolatile != SecondVolatile) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodVolatile) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodVolatile) << FirstMethodType << FirstName << FirstVolatile; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodVolatile) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodVolatile) << SecondMethodType << SecondName << SecondVolatile; Diagnosed = true; break; @@ -10257,11 +10421,11 @@ const bool FirstConst = FirstMethod->isConst(); const bool SecondConst = SecondMethod->isConst(); if (FirstConst != SecondConst) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodConst) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodConst) << FirstMethodType << FirstName << FirstConst; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodConst) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodConst) << SecondMethodType << SecondName << SecondConst; Diagnosed = true; break; @@ -10270,11 +10434,11 @@ const bool FirstInline = FirstMethod->isInlineSpecified(); const bool SecondInline = SecondMethod->isInlineSpecified(); if (FirstInline != SecondInline) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodInline) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodInline) << FirstMethodType << FirstName << FirstInline; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodInline) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodInline) << SecondMethodType << SecondName << SecondInline; Diagnosed = true; break; @@ -10283,11 +10447,13 @@ const unsigned FirstNumParameters = FirstMethod->param_size(); const unsigned SecondNumParameters = SecondMethod->param_size(); if (FirstNumParameters != SecondNumParameters) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodNumberParameters) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodNumberParameters) << FirstMethodType << FirstName << FirstNumParameters; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodNumberParameters) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodNumberParameters) << SecondMethodType << SecondName << SecondNumParameters; Diagnosed = true; break; @@ -10306,27 +10472,31 @@ ComputeQualTypeODRHash(SecondParamType)) { if (const DecayedType *ParamDecayedType = FirstParamType->getAs()) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodParameterType) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterType) << FirstMethodType << FirstName << (I + 1) << FirstParamType << true << ParamDecayedType->getOriginalType(); } else { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodParameterType) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterType) << FirstMethodType << FirstName << (I + 1) << FirstParamType << false; } if (const DecayedType *ParamDecayedType = SecondParamType->getAs()) { - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodParameterType) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodParameterType) << SecondMethodType << SecondName << (I + 1) << SecondParamType << true << ParamDecayedType->getOriginalType(); } else { - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodParameterType) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodParameterType) << SecondMethodType << SecondName << (I + 1) << SecondParamType << false; } @@ -10337,11 +10507,12 @@ DeclarationName FirstParamName = FirstParam->getDeclName(); DeclarationName SecondParamName = SecondParam->getDeclName(); if (FirstParamName != SecondParamName) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodParameterName) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterName) << FirstMethodType << FirstName << (I + 1) << FirstParamName; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodParameterName) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodParameterName) << SecondMethodType << SecondName << (I + 1) << SecondParamName; ParameterMismatch = true; break; @@ -10350,15 +10521,16 @@ const Expr *FirstInit = FirstParam->getInit(); const Expr *SecondInit = SecondParam->getInit(); if ((FirstInit == nullptr) != (SecondInit == nullptr)) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), - MethodParameterSingleDefaultArgument) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodParameterSingleDefaultArgument) << FirstMethodType << FirstName << (I + 1) << (FirstInit == nullptr) << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), - MethodParameterSingleDefaultArgument) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodParameterSingleDefaultArgument) << SecondMethodType << SecondName << (I + 1) << (SecondInit == nullptr) << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); @@ -10368,14 +10540,15 @@ if (FirstInit && SecondInit && ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), - MethodParameterDifferentDefaultArgument) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodParameterDifferentDefaultArgument) << FirstMethodType << FirstName << (I + 1) << FirstInit->getSourceRange(); - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), - MethodParameterDifferentDefaultArgument) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodParameterDifferentDefaultArgument) << SecondMethodType << SecondName << (I + 1) << SecondInit->getSourceRange(); ParameterMismatch = true; @@ -10396,11 +10569,13 @@ if ((FirstTemplateArgs && !SecondTemplateArgs) || (!FirstTemplateArgs && SecondTemplateArgs)) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodNoTemplateArguments) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodNoTemplateArguments) << FirstMethodType << FirstName << (FirstTemplateArgs != nullptr); - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodNoTemplateArguments) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodNoTemplateArguments) << SecondMethodType << SecondName << (SecondTemplateArgs != nullptr); @@ -10430,14 +10605,15 @@ ExpandTemplateArgumentList(SecondTemplateArgs); if (FirstExpandedList.size() != SecondExpandedList.size()) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), - MethodDifferentNumberTemplateArguments) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodDifferentNumberTemplateArguments) << FirstMethodType << FirstName << (unsigned)FirstExpandedList.size(); - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), - MethodDifferentNumberTemplateArguments) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodDifferentNumberTemplateArguments) << SecondMethodType << SecondName << (unsigned)SecondExpandedList.size(); @@ -10454,13 +10630,13 @@ continue; } - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), - MethodDifferentTemplateArgument) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodDifferentTemplateArgument) << FirstMethodType << FirstName << FirstTA << i + 1; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), - MethodDifferentTemplateArgument) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodDifferentTemplateArgument) << SecondMethodType << SecondName << SecondTA << i + 1; TemplateArgumentMismatch = true; @@ -10489,22 +10665,22 @@ ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash(); if (HasFirstBody != HasSecondBody) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodSingleBody) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodSingleBody) << FirstMethodType << FirstName << HasFirstBody; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodSingleBody) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodSingleBody) << SecondMethodType << SecondName << HasSecondBody; Diagnosed = true; break; } if (HasFirstBody && HasSecondBody) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodDifferentBody) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodDifferentBody) << FirstMethodType << FirstName; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodDifferentBody) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodDifferentBody) << SecondMethodType << SecondName; Diagnosed = true; break; @@ -10514,105 +10690,16 @@ } case TypeAlias: case TypeDef: { - TypedefNameDecl *FirstTD = cast(FirstDecl); - TypedefNameDecl *SecondTD = cast(SecondDecl); - auto FirstName = FirstTD->getDeclName(); - auto SecondName = SecondTD->getDeclName(); - if (FirstName != SecondName) { - ODRDiagError(FirstTD->getLocation(), FirstTD->getSourceRange(), - TypedefName) - << (FirstDiffType == TypeAlias) << FirstName; - ODRDiagNote(SecondTD->getLocation(), SecondTD->getSourceRange(), - TypedefName) - << (FirstDiffType == TypeAlias) << SecondName; - Diagnosed = true; - break; - } - - QualType FirstType = FirstTD->getUnderlyingType(); - QualType SecondType = SecondTD->getUnderlyingType(); - if (ComputeQualTypeODRHash(FirstType) != - ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstTD->getLocation(), FirstTD->getSourceRange(), - TypedefType) - << (FirstDiffType == TypeAlias) << FirstName << FirstType; - ODRDiagNote(SecondTD->getLocation(), SecondTD->getSourceRange(), - TypedefType) - << (FirstDiffType == TypeAlias) << SecondName << SecondType; - Diagnosed = true; - break; - } + Diagnosed = ODRDiagTypeDefOrAlias( + FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), cast(SecondDecl), + FirstDiffType == TypeAlias); break; } case Var: { - VarDecl *FirstVD = cast(FirstDecl); - VarDecl *SecondVD = cast(SecondDecl); - auto FirstName = FirstVD->getDeclName(); - auto SecondName = SecondVD->getDeclName(); - if (FirstName != SecondName) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarName) - << FirstName; - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarName) - << SecondName; - Diagnosed = true; - break; - } - - QualType FirstType = FirstVD->getType(); - QualType SecondType = SecondVD->getType(); - if (ComputeQualTypeODRHash(FirstType) != - ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarType) - << FirstName << FirstType; - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarType) - << SecondName << SecondType; - Diagnosed = true; - break; - } - - const Expr *FirstInit = FirstVD->getInit(); - const Expr *SecondInit = SecondVD->getInit(); - if ((FirstInit == nullptr) != (SecondInit == nullptr)) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarSingleInitializer) - << FirstName << (FirstInit == nullptr) - << (FirstInit ? FirstInit->getSourceRange(): SourceRange()); - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarSingleInitializer) - << SecondName << (SecondInit == nullptr) - << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); - Diagnosed = true; - break; - } - - if (FirstInit && SecondInit && - ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarDifferentInitializer) - << FirstName << FirstInit->getSourceRange(); - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarDifferentInitializer) - << SecondName << SecondInit->getSourceRange(); - Diagnosed = true; - break; - } - - const bool FirstIsConstexpr = FirstVD->isConstexpr(); - const bool SecondIsConstexpr = SecondVD->isConstexpr(); - if (FirstIsConstexpr != SecondIsConstexpr) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarConstexpr) - << FirstName << FirstIsConstexpr; - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarConstexpr) - << SecondName << SecondIsConstexpr; - Diagnosed = true; - break; - } + Diagnosed = + ODRDiagVar(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), cast(SecondDecl)); break; } case Friend: { @@ -10626,11 +10713,12 @@ TypeSourceInfo *SecondTSI = SecondFriend->getFriendType(); if (FirstND && SecondND) { - ODRDiagError(FirstFriend->getFriendLoc(), - FirstFriend->getSourceRange(), FriendFunction) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendFunction) << FirstND; - ODRDiagNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendFunction) + ODRDiagDeclNote(SecondModule, SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendFunction) << SecondND; Diagnosed = true; @@ -10642,21 +10730,22 @@ QualType SecondFriendType = SecondTSI->getType(); assert(ComputeQualTypeODRHash(FirstFriendType) != ComputeQualTypeODRHash(SecondFriendType)); - ODRDiagError(FirstFriend->getFriendLoc(), - FirstFriend->getSourceRange(), FriendType) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendType) << FirstFriendType; - ODRDiagNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendType) + ODRDiagDeclNote(SecondModule, SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendType) << SecondFriendType; Diagnosed = true; break; } - ODRDiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(), - FriendTypeFunction) + ODRDiagDeclError(FirstRecord, FirstModule, FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendTypeFunction) << (FirstTSI == nullptr); - ODRDiagNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendTypeFunction) + ODRDiagDeclNote(SecondModule, SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendTypeFunction) << (SecondTSI == nullptr); Diagnosed = true; @@ -10674,14 +10763,15 @@ SecondTemplate->getTemplateParameters(); if (FirstTPL->size() != SecondTPL->size()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateDifferentNumberParameters) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateDifferentNumberParameters) << FirstTemplate << FirstTPL->size(); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateDifferentNumberParameters) - << SecondTemplate << SecondTPL->size(); + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateDifferentNumberParameters) + << SecondTemplate << SecondTPL->size(); Diagnosed = true; break; @@ -10711,13 +10801,14 @@ } }; - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentKind) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentKind) << FirstTemplate << (i + 1) << GetParamType(FirstParam); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentKind) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentKind) << SecondTemplate << (i + 1) << GetParamType(SecondParam); ParameterMismatch = true; @@ -10725,14 +10816,14 @@ } if (FirstParam->getName() != SecondParam->getName()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterName) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), FunctionTemplateParameterName) << FirstTemplate << (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterName) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterName) << SecondTemplate << (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam; ParameterMismatch = true; @@ -10752,13 +10843,14 @@ SecondTTPD->hasDefaultArgument() && !SecondTTPD->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << FirstTemplate << (i + 1) << HasFirstDefaultArgument; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << SecondTemplate << (i + 1) << HasSecondDefaultArgument; ParameterMismatch = true; break; @@ -10769,13 +10861,15 @@ QualType SecondType = SecondTTPD->getDefaultArgument(); if (ComputeQualTypeODRHash(FirstType) != ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << FirstTemplate << (i + 1) << FirstType; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclNote( + SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << SecondTemplate << (i + 1) << SecondType; ParameterMismatch = true; break; @@ -10784,13 +10878,14 @@ if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplatePackParameter) << FirstTemplate << (i + 1) << FirstTTPD->isParameterPack(); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplatePackParameter) << SecondTemplate << (i + 1) << SecondTTPD->isParameterPack(); ParameterMismatch = true; break; @@ -10811,13 +10906,14 @@ if (ComputeTemplateParameterListODRHash(FirstTPL) != ComputeTemplateParameterListODRHash(SecondTPL)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentType) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentType) << FirstTemplate << (i + 1); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentType) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentType) << SecondTemplate << (i + 1); ParameterMismatch = true; break; @@ -10830,13 +10926,14 @@ SecondTTPD->hasDefaultArgument() && !SecondTTPD->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << FirstTemplate << (i + 1) << HasFirstDefaultArgument; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << SecondTemplate << (i + 1) << HasSecondDefaultArgument; ParameterMismatch = true; break; @@ -10849,13 +10946,15 @@ SecondTTPD->getDefaultArgument().getArgument(); if (ComputeTemplateArgumentODRHash(FirstTA) != ComputeTemplateArgumentODRHash(SecondTA)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << FirstTemplate << (i + 1) << FirstTA; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclNote( + SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << SecondTemplate << (i + 1) << SecondTA; ParameterMismatch = true; break; @@ -10864,13 +10963,14 @@ if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplatePackParameter) << FirstTemplate << (i + 1) << FirstTTPD->isParameterPack(); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplatePackParameter) << SecondTemplate << (i + 1) << SecondTTPD->isParameterPack(); ParameterMismatch = true; break; @@ -10888,13 +10988,14 @@ QualType SecondType = SecondNTTPD->getType(); if (ComputeQualTypeODRHash(FirstType) != ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentType) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentType) << FirstTemplate << (i + 1); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentType) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentType) << SecondTemplate << (i + 1); ParameterMismatch = true; break; @@ -10907,13 +11008,14 @@ SecondNTTPD->hasDefaultArgument() && !SecondNTTPD->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << FirstTemplate << (i + 1) << HasFirstDefaultArgument; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << SecondTemplate << (i + 1) << HasSecondDefaultArgument; ParameterMismatch = true; break; @@ -10924,13 +11026,15 @@ Expr *SecondDefaultArgument = SecondNTTPD->getDefaultArgument(); if (ComputeODRHash(FirstDefaultArgument) != ComputeODRHash(SecondDefaultArgument)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << FirstTemplate << (i + 1) << FirstDefaultArgument; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclNote( + SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << SecondTemplate << (i + 1) << SecondDefaultArgument; ParameterMismatch = true; break; @@ -10939,13 +11043,14 @@ if (FirstNTTPD->isParameterPack() != SecondNTTPD->isParameterPack()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplatePackParameter) << FirstTemplate << (i + 1) << FirstNTTPD->isParameterPack(); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplatePackParameter) << SecondTemplate << (i + 1) << SecondNTTPD->isParameterPack(); ParameterMismatch = true; @@ -10989,6 +11094,110 @@ } } + // Issue any pending ODR-failure (for structural equivalence checks) + // diagnostics for RecordDecl in C/ObjC, note that in C++ this is + // done as paert of CXXRecordDecl ODR checking. + for (auto &Merge : RecordOdrMergeFailures) { + // If we've already pointed out a specific problem with this class, don't + // bother issuing a general "something's different" diagnostic. + if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) + continue; + + bool Diagnosed = false; + RecordDecl *FirstRecord = Merge.first; + if (!FirstRecord->isCompleteDefinition()) + continue; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); + for (auto *SecondRecord : Merge.second) { + // Multiple different declarations got merged together; tell the user + // where they came from. + if (FirstRecord == SecondRecord) + continue; + if (!SecondRecord->isCompleteDefinition()) + continue; + + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); + DeclHashes FirstHashes; + DeclHashes SecondHashes; + + const DeclContext *DC = FirstRecord; + PopulateHashes(FirstHashes, FirstRecord, DC); + PopulateHashes(SecondHashes, SecondRecord, DC); + + auto DR = FindTypeDiffs(FirstHashes, SecondHashes); + RecordDiffType FirstDiffType = DR.FirstDiffType; + RecordDiffType SecondDiffType = DR.SecondDiffType; + Decl *FirstDecl = DR.FirstDecl; + Decl *SecondDecl = DR.SecondDecl; + + // Reaching this point means an unexpected Decl was encountered + // or no difference was detected. This causes a generic error + // message to be emitted. + if (FirstDiffType == Other || SecondDiffType == Other) { + DiagnoseODRUnexpected(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); + Diagnosed = true; + break; + } + + if (FirstDiffType != SecondDiffType) { + DiagnoseODRMismatch(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); + Diagnosed = true; + break; + } + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + case EndOfClass: + case Other: + // C++ only, invalid in this context. + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case StaticAssert: + case CXXMethod: + case TypeAlias: + case Friend: + case FunctionTemplate: + llvm_unreachable("Invalid diff type"); + + case Field: { + Diagnosed = ODRDiagField(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); + break; + } + case TypeDef: { + Diagnosed = ODRDiagTypeDefOrAlias( + FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), cast(SecondDecl), + false /* IsTypeAlias */); + break; + } + case Var: { + Diagnosed = + ODRDiagVar(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), cast(SecondDecl)); + break; + } + } + + if (Diagnosed) + continue; + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); + Diagnosed = true; + } + } + // Issue ODR failures diagnostics for functions. for (auto &Merge : FunctionOdrMergeFailures) { enum ODRFunctionDifference { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -723,8 +723,11 @@ llvm_unreachable("unexpected tag info kind"); } - if (!isa(TD)) - mergeRedeclarable(TD, Redecl); + if (isa(TD)) + return Redecl; + + // Handle merging in C and Objective-C + mergeRedeclarable(TD, Redecl); return Redecl; } @@ -794,6 +797,22 @@ RD->setHasNonTrivialToPrimitiveCopyCUnion(Record.readInt()); RD->setParamDestroyedInCallee(Record.readInt()); RD->setArgPassingRestrictions((RecordDecl::ArgPassingKind)Record.readInt()); + RD->setHasODRHash(true); + RD->ODRHash = Record.readInt(); + + // C++ applies ODR checking in VisitCXXRecordDecl instead. Note that + // structural equivalence is the usual way to check for ODR-like semantics + // in ObjC/C, but using ODRHash is prefered if possible because of better + // performance. + if (!Reader.getContext().getLangOpts().CPlusPlus) { + RecordDecl *Canon = static_cast(RD->getCanonicalDecl()); + if (RD == Canon || Canon->getODRHash() == RD->getODRHash()) + return Redecl; + Reader.PendingRecordOdrMergeFailures[Canon].push_back(RD); + // Track that we merged the definitions. + Reader.MergedDeclContexts.insert(std::make_pair(RD, Canon)); + } + return Redecl; } @@ -2561,7 +2580,7 @@ if (!ND) return false; // TODO: implement merge for other necessary decls. - if (isa(ND)) + if (isa(ND) || isa(ND)) return true; return false; } @@ -3197,6 +3216,10 @@ return ED->getASTContext().getLangOpts().CPlusPlus? ED->getDefinition() : nullptr; + if (auto *RD = dyn_cast(DC)) + if (!RD->getASTContext().getLangOpts().CPlusPlus) + return RD->getCanonicalDecl()->getDefinition(); + // We can see the TU here only if we have no Sema object. In that case, // there's no TU scope to look in, so using the DC alone is sufficient. if (auto *TU = dyn_cast(DC)) diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -483,6 +483,9 @@ Record.push_back(D->hasNonTrivialToPrimitiveCopyCUnion()); Record.push_back(D->isParamDestroyedInCallee()); Record.push_back(D->getArgPassingRestrictions()); + // Only compute this for C/Objective-C, in C++ this is computed as part + // of CXXRecordDecl. + Record.push_back(Writer.getLangOpts().CPlusPlus ? 0UL : D->getODRHash()); if (D->getDeclContext() == D->getLexicalDeclContext() && !D->hasAttrs() && @@ -2046,6 +2049,8 @@ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // getArgPassingRestrictions Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); + // ODRHash + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // DC Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset diff --git a/clang/test/Modules/odr_hash-record.c b/clang/test/Modules/odr_hash-record.c new file mode 100644 --- /dev/null +++ b/clang/test/Modules/odr_hash-record.c @@ -0,0 +1,293 @@ +// Clear and create directories +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: mkdir %t/cache +// RUN: mkdir %t/Inputs + +// Build first header file +// RUN: echo "#define FIRST" >> %t/Inputs/first.h +// RUN: cat %s >> %t/Inputs/first.h + +// Build second header file +// RUN: echo "#define SECOND" >> %t/Inputs/second.h +// RUN: cat %s >> %t/Inputs/second.h + +// Test that each header can compile +// RUN: %clang_cc1 -fsyntax-only -x c %t/Inputs/first.h +// RUN: %clang_cc1 -fsyntax-only -x c %t/Inputs/second.h + +// Build module map file +// RUN: echo "module FirstModule {" >> %t/Inputs/module.map +// RUN: echo " header \"first.h\"" >> %t/Inputs/module.map +// RUN: echo "}" >> %t/Inputs/module.map +// RUN: echo "module SecondModule {" >> %t/Inputs/module.map +// RUN: echo " header \"second.h\"" >> %t/Inputs/module.map +// RUN: echo "}" >> %t/Inputs/module.map + +// Run test +// RUN: %clang_cc1 -triple x86_64-linux-gnu -x c \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ +// RUN: -I%t/Inputs -verify %s + +#if !defined(FIRST) && !defined(SECOND) +#include "first.h" +#include "second.h" +#endif + +#if defined(FIRST) +struct S1 {}; +struct S1 s1a; +#elif defined(SECOND) +struct S1 {}; +#else +struct S1 s1; +#endif + +#if defined(FIRST) +struct S2 { + int x; + int y; +}; +#elif defined(SECOND) +struct S2 { + int y; + int x; +}; +#else +struct S2 s2; +// expected-error@first.h:* {{'S2' has different definitions in different modules; first difference is definition in module 'FirstModule' found field 'x'}} +// expected-note@second.h:* {{but in 'SecondModule' found field 'y'}} +#endif + +#if defined(FIRST) +struct S3 { + double x; +}; +#elif defined(SECOND) +struct S3 { + int x; +}; +#else +struct S3 s3; +// expected-error@second.h:* {{'S3::x' from module 'SecondModule' is not present in definition of 'struct S3' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +#if defined(FIRST) +typedef int A; +struct S4 { + A x; +}; + +struct S5 { + A x; +}; +#elif defined(SECOND) +typedef int B; +struct S4 { + B x; +}; + +struct S5 { + int x; +}; +#else +struct S4 s4; +// expected-error@first.h:* {{'S4' has different definitions in different modules; first difference is definition in module 'FirstModule' found field 'x' with type 'A' (aka 'int')}} +// expected-note@second.h:* {{but in 'SecondModule' found field 'x' with type 'B' (aka 'int')}} + +struct S5 s5; +// expected-error@first.h:* {{'S5' has different definitions in different modules; first difference is definition in module 'FirstModule' found field 'x' with type 'A' (aka 'int')}} +// expected-note@second.h:* {{but in 'SecondModule' found field 'x' with type 'int'}} +#endif + +#if defined(FIRST) +struct S6 { + unsigned x; +}; +#elif defined(SECOND) +struct S6 { + unsigned x : 1; +}; +#else +struct S6 s6; +// expected-error@first.h:* {{'S6' has different definitions in different modules; first difference is definition in module 'FirstModule' found non-bitfield 'x'}} +// expected-note@second.h:* {{but in 'SecondModule' found bitfield 'x'}} +#endif + +#if defined(FIRST) +struct S7 { + unsigned x : 2; +}; +#elif defined(SECOND) +struct S7 { + unsigned x : 1; +}; +#else +struct S7 s7; +// expected-error@first.h:* {{'S7' has different definitions in different modules; first difference is definition in module 'FirstModule' found bitfield 'x' with one width expression}} +// expected-note@second.h:* {{but in 'SecondModule' found bitfield 'x' with different width expression}} +#endif + +#if defined(FIRST) +struct S8 { + unsigned x : 2; +}; +#elif defined(SECOND) +struct S8 { + unsigned x : 1 + 1; +}; +#else +struct S8 s8; +// expected-error@first.h:* {{'S8' has different definitions in different modules; first difference is definition in module 'FirstModule' found bitfield 'x' with one width expression}} +// expected-note@second.h:* {{but in 'SecondModule' found bitfield 'x' with different width expression}} +#endif + +#if defined(FIRST) +struct S12 { + unsigned x[5]; +}; +#elif defined(SECOND) +struct S12 { + unsigned x[7]; +}; +#else +struct S12 s12; +// expected-error@second.h:* {{'S12::x' from module 'SecondModule' is not present in definition of 'struct S12' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +#if defined(FIRST) +struct S13 { + unsigned x[7]; +}; +#elif defined(SECOND) +struct S13 { + double x[7]; +}; +#else +struct S13 s13; +// expected-error@second.h:* {{'S13::x' from module 'SecondModule' is not present in definition of 'struct S13' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +#if defined(FIRST) +struct B1 {}; +struct SS1 { + struct B1 x; +}; +#elif defined(SECOND) +struct A1 {}; +struct SS1 { + struct A1 x; +}; +#else +struct SS1 ss1; +// expected-error@second.h:* {{'SS1::x' from module 'SecondModule' is not present in definition of 'struct SS1' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +#if defined(FIRST) +enum E1 { x42 }; +struct SE1 { + enum E1 x; +}; +#elif defined(SECOND) +enum E2 { x42 }; +struct SE1 { + enum E2 x; +}; +#else +struct SE1 se1; +// expected-error@second.h:* {{'SE1::x' from module 'SecondModule' is not present in definition of 'struct SE1' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +// struct with forward declaration +#if defined(FIRST) +struct P {}; +struct S { + struct P *ptr; +}; +#elif defined(SECOND) +struct S { + struct P *ptr; +}; +#else +struct S s; +#endif + +// struct with forward declaration and no definition +#if defined(FIRST) +struct PA; +struct SA { + struct PA *ptr; +}; +#elif defined(SECOND) +struct SA { + struct PA *ptr; +}; +#else +struct SA sa; +#endif + +// struct with multiple typedefs +#if defined(FIRST) +typedef int BB1; +typedef BB1 AA1; +struct TS1 { + AA1 x; +}; +#elif defined(SECOND) +typedef int AA1; +struct TS1 { + AA1 x; +}; +#else +struct TS1 ts1; +#endif + +#if defined(FIRST) +struct T2 { + int x; +}; +typedef struct T2 B2; +typedef struct B2 A2; +struct TS2 { + struct T2 x; +}; +#elif defined(SECOND) +struct T2 { + int x; +}; +typedef struct T2 A2; +struct TS2 { + struct T2 x; +}; +#else +struct TS2 ts2; +#endif + +#if defined(FIRST) +struct T3; +struct TS3 { + struct T3 *t; +}; +#elif defined(SECOND) +typedef struct T3 { +} T3; +struct TS3 { + struct T3 *t; +}; +#else +struct TS3 ts3; +#endif + +// Keep macros contained to one file. +#ifdef FIRST +#undef FIRST +#endif + +#ifdef SECOND +#undef SECOND +#endif