r337978 - [ODRHash] Support hashing enums.
Richard Trieu via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 25 15:52:05 PDT 2018
Author: rtrieu
Date: Wed Jul 25 15:52:05 2018
New Revision: 337978
URL: http://llvm.org/viewvc/llvm-project?rev=337978&view=rev
Log:
[ODRHash] Support hashing enums.
Modified:
cfe/trunk/include/clang/AST/Decl.h
cfe/trunk/include/clang/AST/ODRHash.h
cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
cfe/trunk/include/clang/Serialization/ASTReader.h
cfe/trunk/lib/AST/Decl.cpp
cfe/trunk/lib/AST/ODRHash.cpp
cfe/trunk/lib/Serialization/ASTReader.cpp
cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
cfe/trunk/test/Modules/odr_hash.cpp
Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Wed Jul 25 15:52:05 2018
@@ -3335,6 +3335,10 @@ class EnumDecl : public TagDecl {
/// information.
MemberSpecializationInfo *SpecializationInfo = nullptr;
+ /// Store the ODRHash after first calculation.
+ unsigned HasODRHash : 1;
+ unsigned ODRHash;
+
EnumDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
SourceLocation IdLoc, IdentifierInfo *Id, EnumDecl *PrevDecl,
bool Scoped, bool ScopedUsingClassTag, bool Fixed)
@@ -3346,6 +3350,8 @@ class EnumDecl : public TagDecl {
IsScoped = Scoped;
IsScopedUsingClassTag = ScopedUsingClassTag;
IsFixed = Fixed;
+ HasODRHash = false;
+ ODRHash = 0;
}
void anchor() override;
@@ -3496,6 +3502,8 @@ public:
return IsFixed;
}
+ unsigned getODRHash();
+
/// Returns true if this can be considered a complete type.
bool isComplete() const {
// IntegerType is set for fixed type enums and non-fixed but implicitly
Modified: cfe/trunk/include/clang/AST/ODRHash.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ODRHash.h?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ODRHash.h (original)
+++ cfe/trunk/include/clang/AST/ODRHash.h Wed Jul 25 15:52:05 2018
@@ -58,6 +58,10 @@ public:
// hash as if the function has no body.
void AddFunctionDecl(const FunctionDecl *Function, bool SkipBody = false);
+ // Use this for ODR checking enums between modules. This method compares
+ // more information than the AddDecl class.
+ void AddEnumDecl(const EnumDecl *Enum);
+
// Process SubDecls of the main Decl. This method calls the DeclVisitor
// while AddDecl does not.
void AddSubDecl(const Decl *D);
@@ -83,7 +87,7 @@ public:
// Save booleans until the end to lower the size of data to process.
void AddBoolean(bool value);
- static bool isWhitelistedDecl(const Decl* D, const CXXRecordDecl *Record);
+ static bool isWhitelistedDecl(const Decl* D, const DeclContext *Parent);
};
} // end namespace clang
Modified: cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td Wed Jul 25 15:52:05 2018
@@ -341,6 +341,33 @@ def note_module_odr_violation_function :
"a different body"
"}1">;
+def err_module_odr_violation_enum : Error<
+ "%q0 has different definitions in different modules; "
+ "%select{definition in module '%2'|defined here}1 "
+ "first difference is "
+ "%select{"
+ "enum that is %select{not scoped|scoped}4|"
+ "enum scoped with keyword %select{struct|class}4|"
+ "enum %select{without|with}4 specified type|"
+ "enum with specified type %4|"
+ "enum with %4 element%s4|"
+ "%ordinal4 element has name %5|"
+ "%ordinal4 element %5 %select{has|does not have}6 an initilizer|"
+ "%ordinal4 element %5 has an initializer|"
+ "}3">;
+
+def note_module_odr_violation_enum : Note<"but in '%0' found "
+ "%select{"
+ "enum that is %select{not scoped|scoped}2|"
+ "enum scoped with keyword %select{struct|class}2|"
+ "enum %select{without|with}2 specified type|"
+ "enum with specified type %2|"
+ "enum with %2 element%s2|"
+ "%ordinal2 element has name %3|"
+ "%ordinal2 element %3 %select{has|does not have}4 an initializer|"
+ "%ordinal2 element %3 has different initializer|"
+ "}1">;
+
def err_module_odr_violation_mismatch_decl_unknown : Error<
"%q0 %select{with definition in module '%2'|defined here}1 has different "
"definitions in different modules; first difference is this "
Modified: cfe/trunk/include/clang/Serialization/ASTReader.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTReader.h?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/ASTReader.h (original)
+++ cfe/trunk/include/clang/Serialization/ASTReader.h Wed Jul 25 15:52:05 2018
@@ -1099,6 +1099,10 @@ private:
llvm::SmallDenseMap<FunctionDecl *, llvm::SmallVector<FunctionDecl *, 2>, 2>
PendingFunctionOdrMergeFailures;
+ /// Enum definitions in which we found an ODR violation.
+ llvm::SmallDenseMap<EnumDecl *, llvm::SmallVector<EnumDecl *, 2>, 2>
+ PendingEnumOdrMergeFailures;
+
/// DeclContexts in which we have diagnosed an ODR violation.
llvm::SmallPtrSet<DeclContext*, 2> DiagnosedOdrMergeFailures;
Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Wed Jul 25 15:52:05 2018
@@ -3973,6 +3973,17 @@ void EnumDecl::setInstantiationOfMemberE
SpecializationInfo = new (C) MemberSpecializationInfo(ED, TSK);
}
+unsigned EnumDecl::getODRHash() {
+ if (HasODRHash)
+ return ODRHash;
+
+ class ODRHash Hash;
+ Hash.AddEnumDecl(this);
+ HasODRHash = true;
+ ODRHash = Hash.CalculateHash();
+ return ODRHash;
+}
+
//===----------------------------------------------------------------------===//
// RecordDecl Implementation
//===----------------------------------------------------------------------===//
Modified: cfe/trunk/lib/AST/ODRHash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ODRHash.cpp?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ODRHash.cpp (original)
+++ cfe/trunk/lib/AST/ODRHash.cpp Wed Jul 25 15:52:05 2018
@@ -407,12 +407,17 @@ public:
AddDecl(D->getTemplatedDecl());
Inherited::VisitFunctionTemplateDecl(D);
}
+
+ void VisitEnumConstantDecl(const EnumConstantDecl *D) {
+ AddStmt(D->getInitExpr());
+ Inherited::VisitEnumConstantDecl(D);
+ }
};
} // namespace
// Only allow a small portion of Decl's to be processed. Remove this once
// all Decl's can be handled.
-bool ODRHash::isWhitelistedDecl(const Decl *D, const CXXRecordDecl *Parent) {
+bool ODRHash::isWhitelistedDecl(const Decl *D, const DeclContext *Parent) {
if (D->isImplicit()) return false;
if (D->getDeclContext() != Parent) return false;
@@ -423,6 +428,7 @@ bool ODRHash::isWhitelistedDecl(const De
case Decl::CXXConstructor:
case Decl::CXXDestructor:
case Decl::CXXMethod:
+ case Decl::EnumConstant: // Only found in EnumDecl's.
case Decl::Field:
case Decl::Friend:
case Decl::FunctionTemplate:
@@ -554,6 +560,34 @@ void ODRHash::AddFunctionDecl(const Func
}
}
+void ODRHash::AddEnumDecl(const EnumDecl *Enum) {
+ assert(Enum);
+ AddDeclarationName(Enum->getDeclName());
+
+ AddBoolean(Enum->isScoped());
+ if (Enum->isScoped())
+ AddBoolean(Enum->isScopedUsingClassTag());
+
+ if (Enum->getIntegerTypeSourceInfo())
+ AddQualType(Enum->getIntegerType());
+
+ // 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 : Enum->decls()) {
+ if (isWhitelistedDecl(SubDecl, Enum)) {
+ assert(isa<EnumConstantDecl>(SubDecl) && "Unexpected Decl");
+ Decls.push_back(SubDecl);
+ }
+ }
+
+ ID.AddInteger(Decls.size());
+ for (auto SubDecl : Decls) {
+ AddSubDecl(SubDecl);
+ }
+
+}
+
void ODRHash::AddDecl(const Decl *D) {
assert(D && "Expecting non-null pointer.");
D = D->getCanonicalDecl();
Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Wed Jul 25 15:52:05 2018
@@ -9443,7 +9443,8 @@ void ASTReader::finishPendingActions() {
void ASTReader::diagnoseOdrViolations() {
if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() &&
- PendingFunctionOdrMergeFailures.empty())
+ PendingFunctionOdrMergeFailures.empty() &&
+ PendingEnumOdrMergeFailures.empty())
return;
// Trigger the import of the full definition of each class that had any
@@ -9479,6 +9480,16 @@ void ASTReader::diagnoseOdrViolations()
}
}
+ // Trigger the import of enums.
+ auto EnumOdrMergeFailures = std::move(PendingEnumOdrMergeFailures);
+ PendingEnumOdrMergeFailures.clear();
+ for (auto &Merge : EnumOdrMergeFailures) {
+ Merge.first->decls_begin();
+ for (auto &Enum : Merge.second) {
+ Enum->decls_begin();
+ }
+ }
+
// For each declaration from a merged context, check that the canonical
// definition of that context also contains a declaration of the same
// entity.
@@ -9561,7 +9572,8 @@ void ASTReader::diagnoseOdrViolations()
}
}
- if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty())
+ if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty() &&
+ EnumOdrMergeFailures.empty())
return;
// Ensure we don't accidentally recursively enter deserialization while
@@ -11308,6 +11320,194 @@ void ASTReader::diagnoseOdrViolations()
(void)Diagnosed;
assert(Diagnosed && "Unable to emit ODR diagnostic.");
}
+
+ // Issue ODR failures diagnostics for enums.
+ for (auto &Merge : EnumOdrMergeFailures) {
+ enum ODREnumDifference {
+ SingleScopedEnum,
+ EnumTagKeywordMismatch,
+ SingleSpecifiedType,
+ DifferentSpecifiedTypes,
+ DifferentNumberEnumConstants,
+ EnumConstantName,
+ EnumConstantSingleInitilizer,
+ EnumConstantDifferentInitilizer,
+ };
+
+ // If we've already pointed out a specific problem with this enum, don't
+ // bother issuing a general "something's different" diagnostic.
+ if (!DiagnosedOdrMergeFailures.insert(Merge.first).second)
+ continue;
+
+ EnumDecl *FirstEnum = Merge.first;
+ std::string FirstModule = getOwningModuleNameForDiagnostic(FirstEnum);
+
+ using DeclHashes =
+ llvm::SmallVector<std::pair<EnumConstantDecl *, unsigned>, 4>;
+ auto PopulateHashes = [&ComputeSubDeclODRHash, FirstEnum](
+ DeclHashes &Hashes, EnumDecl *Enum) {
+ for (auto *D : Enum->decls()) {
+ // Due to decl merging, the first EnumDecl is the parent of
+ // Decls in both records.
+ if (!ODRHash::isWhitelistedDecl(D, FirstEnum))
+ continue;
+ assert(isa<EnumConstantDecl>(D) && "Unexpected Decl kind");
+ Hashes.emplace_back(cast<EnumConstantDecl>(D),
+ ComputeSubDeclODRHash(D));
+ }
+ };
+ DeclHashes FirstHashes;
+ PopulateHashes(FirstHashes, FirstEnum);
+ bool Diagnosed = false;
+ for (auto &SecondEnum : Merge.second) {
+
+ if (FirstEnum == SecondEnum)
+ continue;
+
+ std::string SecondModule =
+ getOwningModuleNameForDiagnostic(SecondEnum);
+
+ auto ODRDiagError = [FirstEnum, &FirstModule,
+ this](SourceLocation Loc, SourceRange Range,
+ ODREnumDifference DiffType) {
+ return Diag(Loc, diag::err_module_odr_violation_enum)
+ << FirstEnum << FirstModule.empty() << FirstModule << Range
+ << DiffType;
+ };
+ auto ODRDiagNote = [&SecondModule, this](SourceLocation Loc,
+ SourceRange Range,
+ ODREnumDifference DiffType) {
+ return Diag(Loc, diag::note_module_odr_violation_enum)
+ << SecondModule << Range << DiffType;
+ };
+
+ if (FirstEnum->isScoped() != SecondEnum->isScoped()) {
+ ODRDiagError(FirstEnum->getLocation(), FirstEnum->getSourceRange(),
+ SingleScopedEnum)
+ << FirstEnum->isScoped();
+ ODRDiagNote(SecondEnum->getLocation(), SecondEnum->getSourceRange(),
+ SingleScopedEnum)
+ << SecondEnum->isScoped();
+ Diagnosed = true;
+ continue;
+ }
+
+ if (FirstEnum->isScoped() && SecondEnum->isScoped()) {
+ if (FirstEnum->isScopedUsingClassTag() !=
+ SecondEnum->isScopedUsingClassTag()) {
+ ODRDiagError(FirstEnum->getLocation(), FirstEnum->getSourceRange(),
+ EnumTagKeywordMismatch)
+ << FirstEnum->isScopedUsingClassTag();
+ ODRDiagNote(SecondEnum->getLocation(), SecondEnum->getSourceRange(),
+ EnumTagKeywordMismatch)
+ << SecondEnum->isScopedUsingClassTag();
+ Diagnosed = true;
+ continue;
+ }
+ }
+
+ QualType FirstUnderlyingType =
+ FirstEnum->getIntegerTypeSourceInfo()
+ ? FirstEnum->getIntegerTypeSourceInfo()->getType()
+ : QualType();
+ QualType SecondUnderlyingType =
+ SecondEnum->getIntegerTypeSourceInfo()
+ ? SecondEnum->getIntegerTypeSourceInfo()->getType()
+ : QualType();
+ if (FirstUnderlyingType.isNull() != SecondUnderlyingType.isNull()) {
+ ODRDiagError(FirstEnum->getLocation(), FirstEnum->getSourceRange(),
+ SingleSpecifiedType)
+ << !FirstUnderlyingType.isNull();
+ ODRDiagNote(SecondEnum->getLocation(), SecondEnum->getSourceRange(),
+ SingleSpecifiedType)
+ << !SecondUnderlyingType.isNull();
+ Diagnosed = true;
+ continue;
+ }
+
+ if (!FirstUnderlyingType.isNull() && !SecondUnderlyingType.isNull()) {
+ if (ComputeQualTypeODRHash(FirstUnderlyingType) !=
+ ComputeQualTypeODRHash(SecondUnderlyingType)) {
+ ODRDiagError(FirstEnum->getLocation(), FirstEnum->getSourceRange(),
+ DifferentSpecifiedTypes)
+ << FirstUnderlyingType;
+ ODRDiagNote(SecondEnum->getLocation(), SecondEnum->getSourceRange(),
+ DifferentSpecifiedTypes)
+ << SecondUnderlyingType;
+ Diagnosed = true;
+ continue;
+ }
+ }
+
+ DeclHashes SecondHashes;
+ PopulateHashes(SecondHashes, SecondEnum);
+
+ if (FirstHashes.size() != SecondHashes.size()) {
+ ODRDiagError(FirstEnum->getLocation(), FirstEnum->getSourceRange(),
+ DifferentNumberEnumConstants)
+ << (int)FirstHashes.size();
+ ODRDiagNote(SecondEnum->getLocation(), SecondEnum->getSourceRange(),
+ DifferentNumberEnumConstants)
+ << (int)SecondHashes.size();
+ Diagnosed = true;
+ continue;
+ }
+
+ for (unsigned I = 0; I < FirstHashes.size(); ++I) {
+ if (FirstHashes[I].second == SecondHashes[I].second)
+ continue;
+ const EnumConstantDecl *FirstEnumConstant = FirstHashes[I].first;
+ const EnumConstantDecl *SecondEnumConstant = SecondHashes[I].first;
+
+ if (FirstEnumConstant->getDeclName() !=
+ SecondEnumConstant->getDeclName()) {
+
+ ODRDiagError(FirstEnumConstant->getLocation(),
+ FirstEnumConstant->getSourceRange(), EnumConstantName)
+ << I + 1 << FirstEnumConstant;
+ ODRDiagNote(SecondEnumConstant->getLocation(),
+ SecondEnumConstant->getSourceRange(), EnumConstantName)
+ << I + 1 << SecondEnumConstant;
+ Diagnosed = true;
+ break;
+ }
+
+ const Expr *FirstInit = FirstEnumConstant->getInitExpr();
+ const Expr *SecondInit = SecondEnumConstant->getInitExpr();
+ if (!FirstInit && !SecondInit)
+ continue;
+
+ if (!FirstInit || !SecondInit) {
+ ODRDiagError(FirstEnumConstant->getLocation(),
+ FirstEnumConstant->getSourceRange(),
+ EnumConstantSingleInitilizer)
+ << I + 1 << FirstEnumConstant << (FirstInit != nullptr);
+ ODRDiagNote(SecondEnumConstant->getLocation(),
+ SecondEnumConstant->getSourceRange(),
+ EnumConstantSingleInitilizer)
+ << I + 1 << SecondEnumConstant << (SecondInit != nullptr);
+ Diagnosed = true;
+ break;
+ }
+
+ if (ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) {
+ ODRDiagError(FirstEnumConstant->getLocation(),
+ FirstEnumConstant->getSourceRange(),
+ EnumConstantDifferentInitilizer)
+ << I + 1 << FirstEnumConstant;
+ ODRDiagNote(SecondEnumConstant->getLocation(),
+ SecondEnumConstant->getSourceRange(),
+ EnumConstantDifferentInitilizer)
+ << I + 1 << SecondEnumConstant;
+ Diagnosed = true;
+ break;
+ }
+ }
+ }
+
+ (void)Diagnosed;
+ assert(Diagnosed && "Unable to emit ODR diagnostic.");
+ }
}
void ASTReader::StartedDeserializing() {
Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Wed Jul 25 15:52:05 2018
@@ -746,6 +746,9 @@ void ASTDeclReader::VisitEnumDecl(EnumDe
ED->IsScopedUsingClassTag = Record.readInt();
ED->IsFixed = Record.readInt();
+ ED->HasODRHash = true;
+ ED->ODRHash = Record.readInt();
+
// If this is a definition subject to the ODR, and we already have a
// definition, merge this one into it.
if (ED->IsCompleteDefinition &&
@@ -766,6 +769,8 @@ void ASTDeclReader::VisitEnumDecl(EnumDe
Reader.MergedDeclContexts.insert(std::make_pair(ED, OldDef));
ED->IsCompleteDefinition = false;
Reader.mergeDefinitionVisibility(OldDef, ED);
+ if (OldDef->getODRHash() != ED->getODRHash())
+ Reader.PendingEnumOdrMergeFailures[OldDef].push_back(ED);
} else {
OldDef = ED;
}
Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Wed Jul 25 15:52:05 2018
@@ -431,6 +431,8 @@ void ASTDeclWriter::VisitEnumDecl(EnumDe
Record.push_back(D->isScoped());
Record.push_back(D->isScopedUsingClassTag());
Record.push_back(D->isFixed());
+ Record.push_back(D->getODRHash());
+
if (MemberSpecializationInfo *MemberInfo = D->getMemberSpecializationInfo()) {
Record.AddDeclRef(MemberInfo->getInstantiatedFrom());
Record.push_back(MemberInfo->getTemplateSpecializationKind());
@@ -1865,6 +1867,7 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isScoped
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isScopedUsingClassTag
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isFixed
+ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32));// ODRHash
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // InstantiatedMembEnum
// DC
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset
Modified: cfe/trunk/test/Modules/odr_hash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/odr_hash.cpp?rev=337978&r1=337977&r2=337978&view=diff
==============================================================================
--- cfe/trunk/test/Modules/odr_hash.cpp (original)
+++ cfe/trunk/test/Modules/odr_hash.cpp Wed Jul 25 15:52:05 2018
@@ -3146,6 +3146,177 @@ Invalid1 i1;
#undef DECLS
}
+namespace Enums {
+#if defined(FIRST)
+enum E1 { x11 };
+#elif defined(SECOND)
+enum E1 {};
+#else
+E1 e1;
+// expected-error at first.h:* {{'Enums::x11' from module 'FirstModule' is not present in definition of 'Enums::E1' in module 'SecondModule'}}
+// expected-note at second.h:* {{definition has no member 'x11'}}
+#endif
+
+#if defined(FIRST)
+enum E2 {};
+#elif defined(SECOND)
+enum E2 { x21 };
+#else
+E2 e2;
+// expected-error at second.h:* {{'Enums::E2' has different definitions in different modules; definition in module 'SecondModule' first difference is enum with 1 element}}
+// expected-note at first.h:* {{but in 'FirstModule' found enum with 0 elements}}
+#endif
+
+#if defined(FIRST)
+enum E3 { x31 };
+#elif defined(SECOND)
+enum E3 { x32 };
+#else
+E3 e3;
+// expected-error at first.h:* {{'Enums::x31' from module 'FirstModule' is not present in definition of 'Enums::E3' in module 'SecondModule'}}
+// expected-note at second.h:* {{definition has no member 'x31'}}
+#endif
+
+#if defined(FIRST)
+enum E4 { x41 };
+#elif defined(SECOND)
+enum E4 { x41, x42 };
+#else
+E4 e4;
+// expected-error at second.h:* {{'Enums::E4' has different definitions in different modules; definition in module 'SecondModule' first difference is enum with 2 elements}}
+// expected-note at first.h:* {{but in 'FirstModule' found enum with 1 element}}
+#endif
+
+#if defined(FIRST)
+enum E5 { x51, x52 };
+#elif defined(SECOND)
+enum E5 { x51 };
+#else
+E5 e5;
+// expected-error at first.h:* {{'Enums::x52' from module 'FirstModule' is not present in definition of 'Enums::E5' in module 'SecondModule'}}
+// expected-note at second.h:* {{definition has no member 'x52'}}
+#endif
+
+#if defined(FIRST)
+enum E6 { x61, x62 };
+#elif defined(SECOND)
+enum E6 { x62, x61 };
+#else
+E6 e6;
+// expected-error at second.h:* {{'Enums::E6' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st element has name 'x62'}}
+// expected-note at first.h:* {{but in 'FirstModule' found 1st element has name 'x61'}}
+#endif
+
+#if defined(FIRST)
+enum E7 { x71 = 0 };
+#elif defined(SECOND)
+enum E7 { x71 };
+#else
+E7 e7;
+// expected-error at second.h:* {{'Enums::E7' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st element 'x71' has an initilizer}}
+// expected-note at first.h:* {{but in 'FirstModule' found 1st element 'x71' does not have an initializer}}
+#endif
+
+#if defined(FIRST)
+enum E8 { x81 };
+#elif defined(SECOND)
+enum E8 { x81 = 0 };
+#else
+E8 e8;
+// expected-error at second.h:* {{'Enums::E8' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st element 'x81' does not have an initilizer}}
+// expected-note at first.h:* {{but in 'FirstModule' found 1st element 'x81' has an initializer}}
+#endif
+
+#if defined(FIRST)
+enum E9 { x91 = 0, x92 = 1 };
+#elif defined(SECOND)
+enum E9 { x91 = 0, x92 = 2 - 1 };
+#else
+E9 e9;
+// expected-error at second.h:* {{'Enums::E9' has different definitions in different modules; definition in module 'SecondModule' first difference is 2nd element 'x92' has an initializer}}
+// expected-note at first.h:* {{but in 'FirstModule' found 2nd element 'x92' has different initializer}}
+#endif
+
+#if defined(FIRST)
+enum class E10 : int {};
+#elif defined(SECOND)
+enum class E10 {};
+#else
+E10 e10;
+// expected-error at second.h:* {{'Enums::E10' has different definitions in different modules; definition in module 'SecondModule' first difference is enum without specified type}}
+// expected-note at first.h:* {{but in 'FirstModule' found enum with specified type}}
+#endif
+
+#if defined(FIRST)
+enum E11 {};
+#elif defined(SECOND)
+enum E11 : int {};
+#else
+E11 e11;
+// expected-error at second.h:* {{'Enums::E11' has different definitions in different modules; definition in module 'SecondModule' first difference is enum with specified type}}
+// expected-note at first.h:* {{but in 'FirstModule' found enum without specified type}}
+#endif
+
+#if defined(FIRST)
+enum struct E12 : long {};
+#elif defined(SECOND)
+enum struct E12 : int {};
+#else
+E12 e12;
+// expected-error at second.h:* {{'Enums::E12' has different definitions in different modules; definition in module 'SecondModule' first difference is enum with specified type 'int'}}
+// expected-note at first.h:* {{but in 'FirstModule' found enum with specified type 'long'}}
+#endif
+
+#if defined(FIRST)
+enum struct E13 {};
+#elif defined(SECOND)
+enum E13 {};
+#else
+E13 e13;
+// expected-error at second.h:* {{'Enums::E13' has different definitions in different modules; definition in module 'SecondModule' first difference is enum that is not scoped}}
+// expected-note at first.h:* {{but in 'FirstModule' found enum that is scoped}}
+#endif
+
+#if defined(FIRST)
+enum E14 {};
+#elif defined(SECOND)
+enum struct E14 {};
+#else
+E14 e14;
+// expected-error at second.h:* {{'Enums::E14' has different definitions in different modules; definition in module 'SecondModule' first difference is enum that is scoped}}
+// expected-note at first.h:* {{but in 'FirstModule' found enum that is not scoped}}
+#endif
+
+#if defined(FIRST)
+enum class E15 {};
+#elif defined(SECOND)
+enum struct E15 {};
+#else
+E15 e15;
+// expected-error at second.h:* {{'Enums::E15' has different definitions in different modules; definition in module 'SecondModule' first difference is enum scoped with keyword struct}}
+// expected-note at first.h:* {{but in 'FirstModule' found enum scoped with keyword class}}
+#endif
+
+#if defined(FIRST)
+enum struct E16 {};
+#elif defined(SECOND)
+enum class E16 {};
+#else
+E16 e16;
+// expected-error at second.h:* {{'Enums::E16' has different definitions in different modules; definition in module 'SecondModule' first difference is enum scoped with keyword class}}
+// expected-note at first.h:* {{but in 'FirstModule' found enum scoped with keyword struct}}
+#endif
+
+#if defined(FIRST)
+enum Valid { v1 = (struct S*)0 == (struct S*)0 };
+#elif defined(SECOND)
+struct S {};
+enum Valid { v1 = (struct S*)0 == (struct S*)0 };
+#else
+Valid V;
+#endif
+} // namespace Enums
+
// Collection of interesting cases below.
// Naive parsing of AST can lead to cycles in processing. Ensure
More information about the cfe-commits
mailing list