aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
authorVolodymyr Sapsai <vsapsai@apple.com>2022-12-01 18:39:23 -0800
committerVolodymyr Sapsai <vsapsai@apple.com>2023-01-19 15:57:48 -0600
commit160bc160b9b114069a8cb9b4cc887aa86e5ca7c4 (patch)
tree2bf5e66cafb5a616d21d4a856ab1fbc0bba62f58 /clang/lib
parent9bdcf8778a5c6f57e6d05308e9dde655bba97698 (diff)
downloadllvm-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.cpp14
-rw-r--r--clang/lib/AST/ODRDiagsEmitter.cpp95
-rw-r--r--clang/lib/AST/ODRHash.cpp18
-rw-r--r--clang/lib/Serialization/ASTReader.cpp35
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp3
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp6
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