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