[clang] 160bc16 - [ODRHash] Hash `RecordDecl` and diagnose discovered mismatches.
Volodymyr Sapsai via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 19 13:59:56 PST 2023
Author: Volodymyr Sapsai
Date: 2023-01-19T15:57:48-06:00
New Revision: 160bc160b9b114069a8cb9b4cc887aa86e5ca7c4
URL: https://github.com/llvm/llvm-project/commit/160bc160b9b114069a8cb9b4cc887aa86e5ca7c4
DIFF: https://github.com/llvm/llvm-project/commit/160bc160b9b114069a8cb9b4cc887aa86e5ca7c4.diff
LOG: [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
Added:
clang/test/Modules/compare-record.c
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/Decl.h
clang/include/clang/AST/DeclBase.h
clang/include/clang/AST/ODRDiagsEmitter.h
clang/include/clang/AST/ODRHash.h
clang/include/clang/Serialization/ASTReader.h
clang/lib/AST/Decl.cpp
clang/lib/AST/ODRDiagsEmitter.cpp
clang/lib/AST/ODRHash.cpp
clang/lib/Serialization/ASTReader.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 541f8e1eb671d..9f9fdc5a97a79 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -192,6 +192,10 @@ code bases.
these definitions were allowed. Note that such definitions are ODR
violations if the header is included more than once.
+- Clang now diagnoses if structs/unions with the same name are
diff erent in
+
diff erent used modules. Behavior in C and Objective-C language modes now is
+ the same as in C++.
+
What's New in Clang |release|?
==============================
Some of the major new features and improvements to Clang are listed
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 7322d4bcbc7f3..863f6ac57f2aa 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -3998,6 +3998,7 @@ class RecordDecl : public TagDecl {
// to save some space. Use the provided accessors to access it.
public:
friend class DeclContext;
+ friend class ASTDeclReader;
/// Enum that represents the
diff erent 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
@@ -4253,9 +4254,16 @@ class RecordDecl : public TagDecl {
/// 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.ODRHash; }
+ void setODRHash(unsigned Hash) { RecordDeclBits.ODRHash = Hash; }
};
class FileScopeAsmDecl : public Decl {
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 8a5f75573095b..6134fdde8a2c9 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -1577,10 +1577,14 @@ class DeclContext {
/// Indicates whether this struct has had its field layout randomized.
uint64_t IsRandomized : 1;
+
+ /// True if a valid hash is stored in ODRHash. This should shave off some
+ /// extra storage and prevent CXXRecordDecl to store unused bits.
+ uint64_t ODRHash : 26;
};
/// Number of non-inherited bits in RecordDeclBitfields.
- enum { NumRecordDeclBits = 15 };
+ enum { NumRecordDeclBits = 41 };
/// Stores the bits used by OMPDeclareReductionDecl.
/// If modified NumOMPDeclareReductionDeclBits and the accessor
diff --git a/clang/include/clang/AST/ODRDiagsEmitter.h b/clang/include/clang/AST/ODRDiagsEmitter.h
index d3b7f5e6b4109..00a681b78a5eb 100644
--- a/clang/include/clang/AST/ODRDiagsEmitter.h
+++ b/clang/include/clang/AST/ODRDiagsEmitter.h
@@ -45,6 +45,12 @@ class ODRDiagsEmitter {
const CXXRecordDecl *SecondRecord,
const struct CXXRecordDecl::DefinitionData *SecondDD) const;
+ /// Diagnose ODR mismatch between 2 RecordDecl that are not CXXRecordDecl.
+ ///
+ /// Returns true if found a mismatch and diagnosed it.
+ bool diagnoseMismatch(const RecordDecl *FirstRecord,
+ const RecordDecl *SecondRecord) const;
+
/// Diagnose ODR mismatch between 2 ObjCProtocolDecl.
///
/// Returns true if found a mismatch and diagnosed it.
diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h
index 1ab20013a3670..a489bb73deb67 100644
--- a/clang/include/clang/AST/ODRHash.h
+++ b/clang/include/clang/AST/ODRHash.h
@@ -55,6 +55,10 @@ class ODRHash {
// 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
index cbac6b7bc51a3..5eb4834444ab6 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -1156,6 +1156,10 @@ class ASTReader
llvm::SmallDenseMap<CXXRecordDecl *, llvm::SmallVector<DataPointers, 2>, 2>
PendingOdrMergeFailures;
+ /// C/ObjC record definitions in which we found an ODR violation.
+ llvm::SmallDenseMap<RecordDecl *, llvm::SmallVector<RecordDecl *, 2>, 2>
+ PendingRecordOdrMergeFailures;
+
/// Function definitions in which we found an ODR violation.
llvm::SmallDenseMap<FunctionDecl *, llvm::SmallVector<FunctionDecl *, 2>, 2>
PendingFunctionOdrMergeFailures;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 49040f5cd317e..e60cc28f6e0f6 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 6e0f1226de721..b5888fc47a270 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 1a24bb24d59f6..6912f67daa8f8 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 2dbd4dd02ddef..77f29f6be4063 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
diff erent" 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 990806498c947..6c7198d7935a1 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 ca59dd69f4fd5..f7daa5076d2bb 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
diff --git a/clang/test/Modules/compare-record.c b/clang/test/Modules/compare-record.c
new file mode 100644
index 0000000000000..23dbe8191a343
--- /dev/null
+++ b/clang/test/Modules/compare-record.c
@@ -0,0 +1,418 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// Build first header file
+// RUN: echo "#define FIRST" >> %t/include/first.h
+// RUN: cat %t/test.c >> %t/include/first.h
+// RUN: echo "#undef FIRST" >> %t/include/first.h
+
+// Build second header file
+// RUN: echo "#define SECOND" >> %t/include/second.h
+// RUN: cat %t/test.c >> %t/include/second.h
+// RUN: echo "#undef SECOND" >> %t/include/second.h
+
+// Test that each header can compile
+// RUN: %clang_cc1 -fsyntax-only -x objective-c %t/include/first.h -fblocks -fobjc-arc
+// RUN: %clang_cc1 -fsyntax-only -x objective-c %t/include/second.h -fblocks -fobjc-arc
+
+// Run test
+// RUN: %clang_cc1 -I%t/include -verify %t/test.c -fblocks -fobjc-arc \
+// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache
+
+// Run tests for nested structs
+// DEFINE: %{filename} = test-nested-struct.c
+// DEFINE: %{macro_flag} = -DCASE1=1
+// DEFINE: %{command} = %clang_cc1 -I%t/include -verify %t/%{filename} -fblocks -fobjc-arc \
+// DEFINE: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache \
+// DEFINE: %{macro_flag} -emit-llvm -o %t/%{filename}.bc
+// RUN: %{command}
+// REDEFINE: %{macro_flag} = -DCASE2=1
+// RUN: %{command}
+// REDEFINE: %{macro_flag} = -DCASE3=1
+// RUN: %{command}
+
+// Test that we don't accept
diff erent structs and unions with the same name
+// from multiple modules but detect mismatches and provide actionable
+// diagnostic.
+
+//--- include/first-empty.h
+//--- include/module.modulemap
+module First {
+ module Empty {
+ header "first-empty.h"
+ }
+ module Hidden {
+ header "first.h"
+ header "first-nested-struct.h"
+ export *
+ }
+}
+module Second {
+ header "second.h"
+ header "second-nested-struct.h"
+ export *
+}
+
+//--- test.c
+#if !defined(FIRST) && !defined(SECOND)
+# include "first-empty.h"
+# include "second.h"
+#endif
+
+#if defined(FIRST)
+struct CompareForwardDeclaration1;
+struct CompareForwardDeclaration2 {};
+#elif defined(SECOND)
+struct CompareForwardDeclaration1 {};
+struct CompareForwardDeclaration2;
+#else
+struct CompareForwardDeclaration1 *compareForwardDeclaration1;
+struct CompareForwardDeclaration2 *compareForwardDeclaration2;
+#endif
+
+#if defined(FIRST)
+struct CompareMatchingFields {
+ int matchingFieldName;
+};
+
+struct CompareFieldPresence1 {
+ int fieldPresence1;
+};
+struct CompareFieldPresence2 {};
+
+struct CompareFieldName {
+ int fieldNameA;
+};
+
+struct CompareFieldOrder {
+ int fieldOrderX;
+ int fieldOrderY;
+};
+#elif defined(SECOND)
+struct CompareMatchingFields {
+ int matchingFieldName;
+};
+
+struct CompareFieldPresence1 {
+};
+struct CompareFieldPresence2 {
+ int fieldPresence2;
+};
+
+struct CompareFieldName {
+ int fieldNameB;
+};
+
+struct CompareFieldOrder {
+ int fieldOrderY;
+ int fieldOrderX;
+};
+#else
+struct CompareMatchingFields compareMatchingFields;
+struct CompareFieldPresence1 compareFieldPresence1;
+// expected-error at first.h:* {{'CompareFieldPresence1' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found field}}
+// expected-note at second.h:* {{but in 'Second' found end of class}}
+struct CompareFieldPresence2 compareFieldPresence2;
+// expected-error at second.h:* {{'CompareFieldPresence2::fieldPresence2' from module 'Second' is not present in definition of 'struct CompareFieldPresence2' in module 'First.Hidden'}}
+// expected-note at first.h:* {{definition has no member 'fieldPresence2'}}
+struct CompareFieldName compareFieldName;
+// expected-error at second.h:* {{'CompareFieldName::fieldNameB' from module 'Second' is not present in definition of 'struct CompareFieldName' in module 'First.Hidden'}}
+// expected-note at first.h:* {{definition has no member 'fieldNameB'}}
+struct CompareFieldOrder compareFieldOrder;
+// expected-error at first.h:* {{'CompareFieldOrder' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found field 'fieldOrderX'}}
+// expected-note at second.h:* {{but in 'Second' found field 'fieldOrderY'}}
+#endif
+
+#if defined(FIRST)
+struct CompareFieldType {
+ int fieldType;
+};
+
+typedef int FieldTypedefNameA;
+struct CompareFieldTypedefName {
+ FieldTypedefNameA fieldTypedefName;
+};
+
+typedef int TypedefUnderlyingType;
+struct CompareFieldTypeUnderlyingTypedef {
+ TypedefUnderlyingType fieldTypeUnderlyingTypedef;
+};
+
+typedef int TypedefFinal;
+struct CompareFieldTypedefChain {
+ TypedefFinal fieldTypeTypedefChain;
+};
+#elif defined(SECOND)
+struct CompareFieldType {
+ float fieldType;
+};
+
+typedef int FieldTypedefNameB;
+struct CompareFieldTypedefName {
+ FieldTypedefNameB fieldTypedefName;
+};
+
+struct CompareFieldTypeUnderlyingTypedef {
+ int fieldTypeUnderlyingTypedef;
+};
+
+typedef int TypedefIntermediate;
+typedef TypedefIntermediate TypedefFinal;
+struct CompareFieldTypedefChain {
+ TypedefFinal fieldTypeTypedefChain;
+};
+#else
+struct CompareFieldType compareFieldType;
+// expected-error at second.h:* {{'CompareFieldType::fieldType' from module 'Second' is not present in definition of 'struct CompareFieldType' in module 'First.Hidden'}}
+// expected-note at first.h:* {{declaration of 'fieldType' does not match}}
+struct CompareFieldTypedefName compareFieldTypedefName;
+// expected-error at first.h:* {{'CompareFieldTypedefName' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found field 'fieldTypedefName' with type 'FieldTypedefNameA' (aka 'int')}}
+// expected-note at second.h:* {{but in 'Second' found field 'fieldTypedefName' with type 'FieldTypedefNameB' (aka 'int')}}
+struct CompareFieldTypeUnderlyingTypedef compareFieldTypeUnderlyingTypedef;
+// expected-error at first.h:* {{'CompareFieldTypeUnderlyingTypedef' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found field 'fieldTypeUnderlyingTypedef' with type 'TypedefUnderlyingType' (aka 'int')}}
+// expected-note at second.h:* {{but in 'Second' found field 'fieldTypeUnderlyingTypedef' with type 'int'}}
+struct CompareFieldTypedefChain compareFieldTypedefChain;
+#endif
+
+#if defined(FIRST)
+struct CompareMatchingBitfields {
+ unsigned matchingBitfields : 3;
+};
+
+struct CompareBitfieldPresence1 {
+ unsigned bitfieldPresence1 : 1;
+};
+struct CompareBitfieldPresence2 {
+ unsigned bitfieldPresence2;
+};
+
+struct CompareBitfieldWidth {
+ unsigned bitfieldWidth : 2;
+};
+
+struct CompareBitfieldWidthExpression {
+ unsigned bitfieldWidthExpression : 1 + 1;
+};
+#elif defined(SECOND)
+struct CompareMatchingBitfields {
+ unsigned matchingBitfields : 3;
+};
+
+struct CompareBitfieldPresence1 {
+ unsigned bitfieldPresence1;
+};
+struct CompareBitfieldPresence2 {
+ unsigned bitfieldPresence2 : 1;
+};
+
+struct CompareBitfieldWidth {
+ unsigned bitfieldWidth : 1;
+};
+
+struct CompareBitfieldWidthExpression {
+ unsigned bitfieldWidthExpression : 2;
+};
+#else
+struct CompareMatchingBitfields compareMatchingBitfields;
+struct CompareBitfieldPresence1 compareBitfieldPresence1;
+// expected-error at first.h:* {{'CompareBitfieldPresence1' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found bitfield 'bitfieldPresence1'}}
+// expected-note at second.h:* {{but in 'Second' found non-bitfield 'bitfieldPresence1'}}
+struct CompareBitfieldPresence2 compareBitfieldPresence2;
+// expected-error at first.h:* {{'CompareBitfieldPresence2' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found non-bitfield 'bitfieldPresence2'}}
+// expected-note at second.h:* {{but in 'Second' found bitfield 'bitfieldPresence2'}}
+struct CompareBitfieldWidth compareBitfieldWidth;
+// expected-error at first.h:* {{'CompareBitfieldWidth' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found bitfield 'bitfieldWidth' with one width expression}}
+// expected-note at second.h:* {{but in 'Second' found bitfield 'bitfieldWidth' with
diff erent width expression}}
+struct CompareBitfieldWidthExpression compareBitfieldWidthExpression;
+// expected-error at first.h:* {{'CompareBitfieldWidthExpression' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found bitfield 'bitfieldWidthExpression' with one width expression}}
+// expected-note at second.h:* {{but in 'Second' found bitfield 'bitfieldWidthExpression' with
diff erent width expressio}}
+#endif
+
+#if defined(FIRST)
+struct CompareMatchingArrayFields {
+ int matchingArrayField[7];
+};
+
+struct CompareArrayLength {
+ int arrayLengthField[5];
+};
+
+struct CompareArrayType {
+ int arrayTypeField[5];
+};
+#elif defined(SECOND)
+struct CompareMatchingArrayFields {
+ int matchingArrayField[7];
+};
+
+struct CompareArrayLength {
+ int arrayLengthField[7];
+};
+
+struct CompareArrayType {
+ float arrayTypeField[5];
+};
+#else
+struct CompareMatchingArrayFields compareMatchingArrayFields;
+struct CompareArrayLength compareArrayLength;
+// expected-error at second.h:* {{'CompareArrayLength::arrayLengthField' from module 'Second' is not present in definition of 'struct CompareArrayLength' in module 'First.Hidden'}}
+// expected-note at first.h:* {{declaration of 'arrayLengthField' does not match}}
+struct CompareArrayType compareArrayType;
+// expected-error at second.h:* {{'CompareArrayType::arrayTypeField' from module 'Second' is not present in definition of 'struct CompareArrayType' in module 'First.Hidden'}}
+// expected-note at first.h:* {{declaration of 'arrayTypeField' does not match}}
+#endif
+
+#if defined(FIRST)
+struct CompareFieldAsForwardDeclaration {
+ struct FieldForwardDeclaration *fieldForwardDeclaration;
+};
+
+enum FieldEnumA { kFieldEnumValue };
+struct CompareFieldAsEnum {
+ enum FieldEnumA fieldEnum;
+};
+
+struct FieldStructA {};
+struct CompareFieldAsStruct {
+ struct FieldStructA fieldStruct;
+};
+#elif defined(SECOND)
+struct FieldForwardDeclaration {};
+struct CompareFieldAsForwardDeclaration {
+ struct FieldForwardDeclaration *fieldForwardDeclaration;
+};
+
+enum FieldEnumB { kFieldEnumValue };
+struct CompareFieldAsEnum {
+ enum FieldEnumB fieldEnum;
+};
+
+struct FieldStructB {};
+struct CompareFieldAsStruct {
+ struct FieldStructB fieldStruct;
+};
+#else
+struct CompareFieldAsForwardDeclaration compareFieldAsForwardDeclaration;
+struct CompareFieldAsEnum compareFieldAsEnum;
+// expected-error at second.h:* {{'CompareFieldAsEnum::fieldEnum' from module 'Second' is not present in definition of 'struct CompareFieldAsEnum' in module 'First.Hidden'}}
+// expected-note at first.h:* {{declaration of 'fieldEnum' does not match}}
+struct CompareFieldAsStruct compareFieldAsStruct;
+// expected-error at second.h:* {{'CompareFieldAsStruct::fieldStruct' from module 'Second' is not present in definition of 'struct CompareFieldAsStruct' in module 'First.Hidden'}}
+// expected-note at first.h:* {{declaration of 'fieldStruct' does not match}}
+#endif
+
+#if defined(FIRST)
+union CompareMatchingUnionFields {
+ int matchingFieldA;
+ float matchingFieldB;
+};
+
+union CompareUnionFieldOrder {
+ int unionFieldOrderA;
+ float unionFieldOrderB;
+};
+
+union CompareUnionFieldType {
+ int unionFieldType;
+};
+#elif defined(SECOND)
+union CompareMatchingUnionFields {
+ int matchingFieldA;
+ float matchingFieldB;
+};
+
+union CompareUnionFieldOrder {
+ float unionFieldOrderB;
+ int unionFieldOrderA;
+};
+
+union CompareUnionFieldType {
+ unsigned int unionFieldType;
+};
+#else
+union CompareMatchingUnionFields compareMatchingUnionFields;
+union CompareUnionFieldOrder compareUnionFieldOrder;
+// expected-error at first.h:* {{'CompareUnionFieldOrder' has
diff erent definitions in
diff erent modules; first
diff erence is definition in module 'First.Hidden' found field 'unionFieldOrderA'}}
+// expected-note at second.h:* {{but in 'Second' found field 'unionFieldOrderB'}}
+union CompareUnionFieldType compareUnionFieldType;
+// expected-error at second.h:* {{'CompareUnionFieldType::unionFieldType' from module 'Second' is not present in definition of 'union CompareUnionFieldType' in module 'First.Hidden'}}
+// expected-note at first.h:* {{declaration of 'unionFieldType' does not match}}
+#endif
+
+// Test that we find and compare definitions even if they are not the first encountered declaration in a module.
+#if defined(FIRST)
+struct CompareDefinitionsRegardlessForwardDeclarations {
+ int definitionField;
+};
+#elif defined(SECOND)
+struct CompareDefinitionsRegardlessForwardDeclarations;
+struct CompareDefinitionsRegardlessForwardDeclarations {
+ float definitionField;
+};
+#else
+struct CompareDefinitionsRegardlessForwardDeclarations compareDefinitions;
+// expected-error at second.h:* {{'CompareDefinitionsRegardlessForwardDeclarations::definitionField' from module 'Second' is not present in definition of 'struct CompareDefinitionsRegardlessForwardDeclarations' in module 'First.Hidden'}}
+// expected-note at first.h:* {{declaration of 'definitionField' does not match}}
+#endif
+
+//--- include/first-nested-struct.h
+struct CompareNestedStruct {
+ struct NestedLevel1 {
+ struct NestedLevel2 {
+ int a;
+ } y;
+ } x;
+};
+
+struct IndirectStruct {
+ int mismatchingField;
+};
+struct DirectStruct {
+ struct IndirectStruct indirectField;
+};
+struct CompareDifferentFieldInIndirectStruct {
+ struct DirectStruct directField;
+};
+struct CompareIndirectStructPointer {
+ struct DirectStruct *directFieldPointer;
+};
+
+//--- include/second-nested-struct.h
+struct CompareNestedStruct {
+ struct NestedLevel1 {
+ struct NestedLevel2 {
+ float b;
+ } y;
+ } x;
+};
+
+struct IndirectStruct {
+ float mismatchingField;
+};
+struct DirectStruct {
+ struct IndirectStruct indirectField;
+};
+struct CompareDifferentFieldInIndirectStruct {
+ struct DirectStruct directField;
+};
+struct CompareIndirectStructPointer {
+ struct DirectStruct *directFieldPointer;
+};
+
+//--- test-nested-struct.c
+#include "first-empty.h"
+#include "second-nested-struct.h"
+
+#if defined(CASE1)
+struct CompareNestedStruct compareNestedStruct;
+// expected-error at second-nested-struct.h:* {{'NestedLevel2::b' from module 'Second' is not present in definition of 'struct NestedLevel2' in module 'First.Hidden'}}
+// expected-note at first-nested-struct.h:* {{definition has no member 'b'}}
+#elif defined(CASE2)
+struct CompareDifferentFieldInIndirectStruct compareIndirectStruct;
+// expected-error at second-nested-struct.h:* {{'IndirectStruct::mismatchingField' from module 'Second' is not present in definition of 'struct IndirectStruct' in module 'First.Hidden'}}
+// expected-note at first-nested-struct.h:* {{declaration of 'mismatchingField' does not match}}
+#elif defined(CASE3)
+struct CompareIndirectStructPointer compareIndirectStructPointer;
+// expected-error at second-nested-struct.h:* {{'IndirectStruct::mismatchingField' from module 'Second' is not present in definition of 'struct IndirectStruct' in module 'First.Hidden'}}
+// expected-note at first-nested-struct.h:* {{declaration of 'mismatchingField' does not match}}
+#endif
More information about the cfe-commits
mailing list