diff options
author | Volodymyr Sapsai <vsapsai@apple.com> | 2022-12-01 18:39:23 -0800 |
---|---|---|
committer | Volodymyr Sapsai <vsapsai@apple.com> | 2023-01-19 15:57:48 -0600 |
commit | 160bc160b9b114069a8cb9b4cc887aa86e5ca7c4 (patch) | |
tree | 2bf5e66cafb5a616d21d4a856ab1fbc0bba62f58 /clang/lib | |
parent | 9bdcf8778a5c6f57e6d05308e9dde655bba97698 (diff) | |
download | llvm-160bc160b9b114069a8cb9b4cc887aa86e5ca7c4.zip llvm-160bc160b9b114069a8cb9b4cc887aa86e5ca7c4.tar.gz llvm-160bc160b9b114069a8cb9b4cc887aa86e5ca7c4.tar.bz2 |
[ODRHash] Hash `RecordDecl` and diagnose discovered mismatches.
When two modules contain struct/union with the same name, check the
definitions are equivalent and diagnose if they are not. This is similar
to `CXXRecordDecl` where we already discover and diagnose mismatches.
rdar://problem/56764293
Differential Revision: https://reviews.llvm.org/D71734
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/Decl.cpp | 14 | ||||
-rw-r--r-- | clang/lib/AST/ODRDiagsEmitter.cpp | 95 | ||||
-rw-r--r-- | clang/lib/AST/ODRHash.cpp | 18 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReader.cpp | 35 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReaderDecl.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriterDecl.cpp | 6 |
6 files changed, 169 insertions, 2 deletions
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 49040f5..e60cc28 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4711,6 +4711,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C, setParamDestroyedInCallee(false); setArgPassingRestrictions(APK_CanPassInRegs); setIsRandomized(false); + setODRHash(0); } RecordDecl *RecordDecl::Create(const ASTContext &C, TagKind TK, DeclContext *DC, @@ -4885,6 +4886,19 @@ const FieldDecl *RecordDecl::findFirstNamedDataMember() const { return nullptr; } +unsigned RecordDecl::getODRHash() { + if (hasODRHash()) + return RecordDeclBits.ODRHash; + + // Only calculate hash on first call of getODRHash per record. + ODRHash Hash; + Hash.AddRecordDecl(this); + // For RecordDecl the ODRHash is stored in the remaining 26 + // bit of RecordDeclBits, adjust the hash to accomodate. + setODRHash(Hash.CalculateHash() >> 6); + return RecordDeclBits.ODRHash; +} + //===----------------------------------------------------------------------===// // BlockDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ODRDiagsEmitter.cpp b/clang/lib/AST/ODRDiagsEmitter.cpp index 6e0f122..b5888fc 100644 --- a/clang/lib/AST/ODRDiagsEmitter.cpp +++ b/clang/lib/AST/ODRDiagsEmitter.cpp @@ -1547,6 +1547,101 @@ bool ODRDiagsEmitter::diagnoseMismatch( return true; } +bool ODRDiagsEmitter::diagnoseMismatch(const RecordDecl *FirstRecord, + const RecordDecl *SecondRecord) const { + if (FirstRecord == SecondRecord) + return false; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); + + auto PopulateHashes = [](DeclHashes &Hashes, const RecordDecl *Record, + const DeclContext *DC) { + for (const Decl *D : Record->decls()) { + if (!ODRHash::isSubDeclToBeProcessed(D, DC)) + continue; + Hashes.emplace_back(D, computeODRHash(D)); + } + }; + + DeclHashes FirstHashes; + DeclHashes SecondHashes; + const DeclContext *DC = FirstRecord; + PopulateHashes(FirstHashes, FirstRecord, DC); + PopulateHashes(SecondHashes, SecondRecord, DC); + + DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + const Decl *FirstDecl = DR.FirstDecl; + const Decl *SecondDecl = DR.SecondDecl; + + if (FirstDiffType == Other || SecondDiffType == Other) { + diagnoseSubMismatchUnexpected(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); + return true; + } + + if (FirstDiffType != SecondDiffType) { + diagnoseSubMismatchDifferentDeclKinds(DR, FirstRecord, FirstModule, + SecondRecord, SecondModule); + return true; + } + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + // Already handled. + 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: + // Cannot be contained by RecordDecl, invalid in this context. + case ObjCMethod: + case ObjCProperty: + llvm_unreachable("Invalid diff type"); + + case Field: { + if (diagnoseSubMismatchField(FirstRecord, FirstModule, SecondModule, + cast<FieldDecl>(FirstDecl), + cast<FieldDecl>(SecondDecl))) + return true; + break; + } + case TypeDef: { + if (diagnoseSubMismatchTypedef(FirstRecord, FirstModule, SecondModule, + cast<TypedefNameDecl>(FirstDecl), + cast<TypedefNameDecl>(SecondDecl), + /*IsTypeAlias=*/false)) + return true; + break; + } + case Var: { + if (diagnoseSubMismatchVar(FirstRecord, FirstModule, SecondModule, + cast<VarDecl>(FirstDecl), + cast<VarDecl>(SecondDecl))) + return true; + break; + } + } + + 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.empty() << SecondModule << FirstDiffType + << SecondDecl->getSourceRange(); + return true; +} + bool ODRDiagsEmitter::diagnoseMismatch( const FunctionDecl *FirstFunction, const FunctionDecl *SecondFunction) const { diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 1a24bb2..6912f67 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -595,6 +595,24 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { } } +void ODRHash::AddRecordDecl(const RecordDecl *Record) { + assert(!isa<CXXRecordDecl>(Record) && + "For CXXRecordDecl should call AddCXXRecordDecl."); + AddDecl(Record); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector<const Decl *, 16> Decls; + for (Decl *SubDecl : Record->decls()) { + if (isSubDeclToBeProcessed(SubDecl, Record)) + Decls.push_back(SubDecl); + } + + ID.AddInteger(Decls.size()); + for (const Decl *SubDecl : Decls) + AddSubDecl(SubDecl); +} + void ODRHash::AddFunctionDecl(const FunctionDecl *Function, bool SkipBody) { assert(Function && "Expecting non-null pointer."); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 2dbd4dd..77f29f6 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -9504,6 +9504,7 @@ void ASTReader::finishPendingActions() { void ASTReader::diagnoseOdrViolations() { if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() && + PendingRecordOdrMergeFailures.empty() && PendingFunctionOdrMergeFailures.empty() && PendingEnumOdrMergeFailures.empty() && PendingObjCProtocolOdrMergeFailures.empty()) @@ -9528,6 +9529,15 @@ void ASTReader::diagnoseOdrViolations() { } } + // 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(); @@ -9645,8 +9655,9 @@ void ASTReader::diagnoseOdrViolations() { } } - if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty() && - EnumOdrMergeFailures.empty() && ObjCProtocolOdrMergeFailures.empty()) + if (OdrMergeFailures.empty() && RecordOdrMergeFailures.empty() && + FunctionOdrMergeFailures.empty() && EnumOdrMergeFailures.empty() && + ObjCProtocolOdrMergeFailures.empty()) return; // Ensure we don't accidentally recursively enter deserialization while @@ -9685,6 +9696,26 @@ void ASTReader::diagnoseOdrViolations() { } } + // Issue any pending ODR-failure diagnostics for RecordDecl in C/ObjC. Note + // that in C++ this is done as a part 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; + + RecordDecl *FirstRecord = Merge.first; + bool Diagnosed = false; + for (auto *SecondRecord : Merge.second) { + if (DiagsEmitter.diagnoseMismatch(FirstRecord, SecondRecord)) { + Diagnosed = true; + break; + } + } + (void)Diagnosed; + assert(Diagnosed && "Unable to emit ODR diagnostic."); + } + // Issue ODR failures diagnostics for functions. for (auto &Merge : FunctionOdrMergeFailures) { FunctionDecl *FirstFunction = Merge.first; diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 9908064..6c7198d 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -829,6 +829,7 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) { void ASTDeclReader::VisitRecordDecl(RecordDecl *RD) { VisitRecordDeclImpl(RD); + RD->setODRHash(Record.readInt()); // Maintain the invariant of a redeclaration chain containing only // a single definition. @@ -849,6 +850,8 @@ void ASTDeclReader::VisitRecordDecl(RecordDecl *RD) { Reader.MergedDeclContexts.insert(std::make_pair(RD, OldDef)); RD->demoteThisDefinitionToDeclaration(); Reader.mergeDefinitionVisibility(OldDef, RD); + if (OldDef->getODRHash() != RD->getODRHash()) + Reader.PendingRecordOdrMergeFailures[OldDef].push_back(RD); } else { OldDef = RD; } diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index ca59dd6..f7daa50 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -491,6 +491,10 @@ void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) { 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. + if (!isa<CXXRecordDecl>(D)) + Record.push_back(D->getODRHash()); if (D->getDeclContext() == D->getLexicalDeclContext() && !D->hasAttrs() && @@ -2127,6 +2131,8 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // getArgPassingRestrictions Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); + // ODRHash + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 26)); // DC Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset |