r295800 - Add more ODR checking.

Richard Trieu via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 21 17:11:26 PST 2017


Author: rtrieu
Date: Tue Feb 21 19:11:25 2017
New Revision: 295800

URL: http://llvm.org/viewvc/llvm-project?rev=295800&view=rev
Log:
Add more ODR checking.

Add the basics for the ODRHash class, which will only process Decl's from
a whitelist, which currently only has AccessSpecDecl.  Different access
specifiers in merged classes can now be detected.

Differential Revision: https://reviews.llvm.org/D21675

Added:
    cfe/trunk/include/clang/AST/ODRHash.h
    cfe/trunk/lib/AST/ODRHash.cpp
    cfe/trunk/test/Modules/odr_hash.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
    cfe/trunk/lib/AST/CMakeLists.txt
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/Serialization/ASTReader.cpp

Added: cfe/trunk/include/clang/AST/ODRHash.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ODRHash.h?rev=295800&view=auto
==============================================================================
--- cfe/trunk/include/clang/AST/ODRHash.h (added)
+++ cfe/trunk/include/clang/AST/ODRHash.h Tue Feb 21 19:11:25 2017
@@ -0,0 +1,84 @@
+//===-- ODRHash.h - Hashing to diagnose ODR failures ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declaration of the ODRHash class, which calculates
+/// a hash based on AST nodes, which is stable across different runs.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TemplateBase.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+
+class Decl;
+class IdentifierInfo;
+class NestedNameSpecifer;
+class Stmt;
+class TemplateParameterList;
+
+// ODRHash is used to calculate a hash based on AST node contents that
+// does not rely on pointer addresses.  This allows the hash to not vary
+// between runs and is usable to detect ODR problems in modules.  To use,
+// construct an ODRHash object, then call Add* methods over the nodes that
+// need to be hashed.  Then call CalculateHash to get the hash value.
+// Typically, only one Add* call is needed.  clear can be called to reuse the
+// object.
+class ODRHash {
+  // Use DenseMaps to convert between Decl and Type pointers and an index value.
+  llvm::DenseMap<const Decl*, unsigned> DeclMap;
+  llvm::DenseMap<const Type*, unsigned> TypeMap;
+
+  // Save space by processing bools at the end.
+  llvm::SmallVector<bool, 128> Bools;
+
+  llvm::FoldingSetNodeID ID;
+
+public:
+  ODRHash() {}
+
+  // Use this for ODR checking classes between modules.  This method compares
+  // more information than the AddDecl class.
+  void AddCXXRecordDecl(const CXXRecordDecl *Record);
+
+  // Process SubDecls of the main Decl.  This method calls the DeclVisitor
+  // while AddDecl does not.
+  void AddSubDecl(const Decl *D);
+
+  // Reset the object for reuse.
+  void clear();
+
+  // Add booleans to ID and uses it to calculate the hash.
+  unsigned CalculateHash();
+
+  // Add AST nodes that need to be processed.
+  void AddDecl(const Decl *D);
+  void AddType(const Type *T);
+  void AddQualType(QualType T);
+  void AddStmt(const Stmt *S);
+  void AddIdentifierInfo(const IdentifierInfo *II);
+  void AddNestedNameSpecifier(const NestedNameSpecifier *NNS);
+  void AddTemplateName(TemplateName Name);
+  void AddDeclarationName(DeclarationName Name);
+  void AddTemplateArgument(TemplateArgument TA);
+  void AddTemplateParameterList(const TemplateParameterList *TPL);
+
+  // 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);
+};
+
+}  // 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=295800&r1=295799&r2=295800&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td Tue Feb 21 19:11:25 2017
@@ -117,6 +117,15 @@ def note_module_odr_violation_different_
 def err_module_odr_violation_different_instantiations : Error<
   "instantiation of %q0 is different in different modules">;
 
+def err_module_odr_violation_mismatch_decl : Error<
+  "%q0 has different definitions in different modules; first difference is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{end of class|public access specifier|private access specifier|"
+  "protected access specifier}3">;
+def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
+  "%select{end of class|public access specifier|private access specifier|"
+  "protected access specifier}1">;
+
 def warn_module_uses_date_time : Warning<
   "%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
   InGroup<DiagGroup<"pch-date-time">>;

Modified: cfe/trunk/lib/AST/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CMakeLists.txt?rev=295800&r1=295799&r2=295800&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CMakeLists.txt (original)
+++ cfe/trunk/lib/AST/CMakeLists.txt Tue Feb 21 19:11:25 2017
@@ -40,6 +40,7 @@ add_clang_library(clangAST
   MicrosoftMangle.cpp
   NestedNameSpecifier.cpp
   NSAPI.cpp
+  ODRHash.cpp
   OpenMPClause.cpp
   ParentMap.cpp
   RawCommentList.cpp

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=295800&r1=295799&r2=295800&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Tue Feb 21 19:11:25 2017
@@ -18,6 +18,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/ODRHash.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "llvm/ADT/STLExtras.h"
@@ -371,7 +372,15 @@ CXXRecordDecl::setBases(CXXBaseSpecifier
   data().IsParsingBaseSpecifiers = false;
 }
 
-void CXXRecordDecl::computeODRHash() {}
+void CXXRecordDecl::computeODRHash() {
+  if (!DefinitionData)
+    return;
+
+  ODRHash Hash;
+  Hash.AddCXXRecordDecl(this);
+
+  DefinitionData->ODRHash = Hash.CalculateHash();
+}
 
 void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
   // C++11 [class.copy]p11:

Added: cfe/trunk/lib/AST/ODRHash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ODRHash.cpp?rev=295800&view=auto
==============================================================================
--- cfe/trunk/lib/AST/ODRHash.cpp (added)
+++ cfe/trunk/lib/AST/ODRHash.cpp Tue Feb 21 19:11:25 2017
@@ -0,0 +1,152 @@
+//===-- ODRHash.cpp - Hashing to diagnose ODR failures ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the ODRHash class, which calculates a hash based
+/// on AST nodes, which is stable across different runs.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ODRHash.h"
+
+#include "clang/AST/DeclVisitor.h"
+#include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/TypeVisitor.h"
+
+using namespace clang;
+
+void ODRHash::AddStmt(const Stmt *S) {}
+void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) {}
+void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {}
+void ODRHash::AddTemplateName(TemplateName Name) {}
+void ODRHash::AddDeclarationName(DeclarationName Name) {}
+void ODRHash::AddTemplateArgument(TemplateArgument TA) {}
+void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {}
+
+void ODRHash::clear() {
+  DeclMap.clear();
+  TypeMap.clear();
+  Bools.clear();
+  ID.clear();
+}
+
+unsigned ODRHash::CalculateHash() {
+  // Append the bools to the end of the data segment backwards.  This allows
+  // for the bools data to be compressed 32 times smaller compared to using
+  // ID.AddBoolean
+  const unsigned unsigned_bits = sizeof(unsigned) * CHAR_BIT;
+  const unsigned size = Bools.size();
+  const unsigned remainder = size % unsigned_bits;
+  const unsigned loops = size / unsigned_bits;
+  auto I = Bools.rbegin();
+  unsigned value = 0;
+  for (unsigned i = 0; i < remainder; ++i) {
+    value <<= 1;
+    value |= *I;
+    ++I;
+  }
+  ID.AddInteger(value);
+
+  for (unsigned i = 0; i < loops; ++i) {
+    value = 0;
+    for (unsigned j = 0; j < unsigned_bits; ++j) {
+      value <<= 1;
+      value |= *I;
+      ++I;
+    }
+    ID.AddInteger(value);
+  }
+
+  assert(I == Bools.rend());
+  Bools.clear();
+  return ID.ComputeHash();
+}
+
+// Process a Decl pointer.  Add* methods call back into ODRHash while Visit*
+// methods process the relevant parts of the Decl.
+class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
+  typedef ConstDeclVisitor<ODRDeclVisitor> Inherited;
+  llvm::FoldingSetNodeID &ID;
+  ODRHash &Hash;
+
+public:
+  ODRDeclVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
+      : ID(ID), Hash(Hash) {}
+
+  void Visit(const Decl *D) {
+    ID.AddInteger(D->getKind());
+    Inherited::Visit(D);
+  }
+
+  void VisitAccessSpecDecl(const AccessSpecDecl *D) {
+    ID.AddInteger(D->getAccess());
+    Inherited::VisitAccessSpecDecl(D);
+  }
+};
+
+// 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) {
+  if (D->isImplicit()) return false;
+  if (D->getDeclContext() != Parent) return false;
+
+  switch (D->getKind()) {
+    default:
+      return false;
+    case Decl::AccessSpec:
+      return true;
+  }
+}
+
+void ODRHash::AddSubDecl(const Decl *D) {
+  assert(D && "Expecting non-null pointer.");
+  AddDecl(D);
+
+  ODRDeclVisitor(ID, *this).Visit(D);
+}
+
+void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
+  assert(Record && Record->hasDefinition() &&
+         "Expected non-null record to be a definition.");
+  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 (const Decl *SubDecl : Record->decls()) {
+    if (isWhitelistedDecl(SubDecl, Record)) {
+      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.");
+  auto Result = DeclMap.insert(std::make_pair(D, DeclMap.size()));
+  ID.AddInteger(Result.first->second);
+  // On first encounter of a Decl pointer, process it.  Every time afterwards,
+  // only the index value is needed.
+  if (!Result.second) {
+    return;
+  }
+
+  ID.AddInteger(D->getKind());
+}
+
+void ODRHash::AddType(const Type *T) {}
+void ODRHash::AddQualType(QualType T) {}
+void ODRHash::AddBoolean(bool Value) {
+  Bools.push_back(Value);
+}

Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=295800&r1=295799&r2=295800&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Tue Feb 21 19:11:25 2017
@@ -26,6 +26,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/ODRHash.h"
 #include "clang/AST/RawCommentList.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLocVisitor.h"
@@ -8918,24 +8919,143 @@ void ASTReader::diagnoseOdrViolations()
       continue;
 
     bool Diagnosed = false;
-    for (auto *RD : Merge.second) {
+    CXXRecordDecl *FirstRecord = Merge.first;
+    std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord);
+    for (CXXRecordDecl *SecondRecord : Merge.second) {
       // Multiple different declarations got merged together; tell the user
       // where they came from.
-      if (Merge.first != RD) {
-        // FIXME: Walk the definition, figure out what's different,
-        // and diagnose that.
-        if (!Diagnosed) {
-          std::string Module = getOwningModuleNameForDiagnostic(Merge.first);
-          Diag(Merge.first->getLocation(),
-               diag::err_module_odr_violation_different_definitions)
-            << Merge.first << Module.empty() << Module;
-          Diagnosed = true;
+      if (FirstRecord == SecondRecord)
+        continue;
+
+      std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord);
+      using DeclHashes = llvm::SmallVector<std::pair<Decl *, unsigned>, 4>;
+      DeclHashes FirstHashes;
+      DeclHashes SecondHashes;
+      ODRHash Hash;
+
+      auto PopulateHashes = [&Hash, FirstRecord](DeclHashes &Hashes,
+                                                 CXXRecordDecl *Record) {
+        for (auto *D : Record->decls()) {
+          // Due to decl merging, the first CXXRecordDecl is the parent of
+          // Decls in both records.
+          if (!ODRHash::isWhitelistedDecl(D, FirstRecord))
+            continue;
+          Hash.clear();
+          Hash.AddSubDecl(D);
+          Hashes.emplace_back(D, Hash.CalculateHash());
+        }
+      };
+      PopulateHashes(FirstHashes, FirstRecord);
+      PopulateHashes(SecondHashes, SecondRecord);
+
+      // Used with err_module_odr_violation_mismatch_decl and
+      // note_module_odr_violation_mismatch_decl
+      enum {
+        EndOfClass,
+        PublicSpecifer,
+        PrivateSpecifer,
+        ProtectedSpecifer,
+        Other
+      } FirstDiffType = Other,
+        SecondDiffType = Other;
+
+      auto DifferenceSelector = [](Decl *D) {
+        assert(D && "valid Decl required");
+        switch (D->getKind()) {
+        default:
+          return Other;
+        case Decl::AccessSpec:
+          switch (D->getAccess()) {
+          case AS_public:
+            return PublicSpecifer;
+          case AS_private:
+            return PrivateSpecifer;
+          case AS_protected:
+            return ProtectedSpecifer;
+          case AS_none:
+            llvm_unreachable("Invalid access specifier");
+          }
+        }
+      };
+
+      Decl *FirstDecl = nullptr;
+      Decl *SecondDecl = nullptr;
+      auto FirstIt = FirstHashes.begin();
+      auto SecondIt = SecondHashes.begin();
+
+      // If there is a diagnoseable difference, FirstDiffType and
+      // SecondDiffType will not be Other and FirstDecl and SecondDecl will be
+      // filled in if not EndOfClass.
+      while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) {
+        if (FirstIt->second == SecondIt->second) {
+          ++FirstIt;
+          ++SecondIt;
+          continue;
         }
 
-        Diag(RD->getLocation(),
+        FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first;
+        SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first;
+
+        FirstDiffType = FirstDecl ? DifferenceSelector(FirstDecl) : EndOfClass;
+        SecondDiffType =
+            SecondDecl ? DifferenceSelector(SecondDecl) : EndOfClass;
+
+        break;
+      }
+
+      if (FirstDiffType == Other || SecondDiffType == Other) {
+        // Reaching this point means an unexpected Decl was encountered
+        // or no difference was detected.  This causes a generic error
+        // message to be emitted.
+        Diag(FirstRecord->getLocation(),
+             diag::err_module_odr_violation_different_definitions)
+            << FirstRecord << FirstModule.empty() << FirstModule;
+
+        Diag(SecondRecord->getLocation(),
              diag::note_module_odr_violation_different_definitions)
-          << getOwningModuleNameForDiagnostic(RD);
+            << SecondModule;
+        Diagnosed = true;
+        break;
       }
+
+      if (FirstDiffType != SecondDiffType) {
+        SourceLocation FirstLoc;
+        SourceRange FirstRange;
+        if (FirstDiffType == EndOfClass) {
+          FirstLoc = FirstRecord->getBraceRange().getEnd();
+        } else {
+          FirstLoc = FirstIt->first->getLocation();
+          FirstRange = FirstIt->first->getSourceRange();
+        }
+        Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl)
+            << FirstRecord << FirstModule.empty() << FirstModule << FirstRange
+            << FirstDiffType;
+
+        SourceLocation SecondLoc;
+        SourceRange SecondRange;
+        if (SecondDiffType == EndOfClass) {
+          SecondLoc = SecondRecord->getBraceRange().getEnd();
+        } else {
+          SecondLoc = SecondDecl->getLocation();
+          SecondRange = SecondDecl->getSourceRange();
+        }
+        Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl)
+            << SecondModule << SecondRange << SecondDiffType;
+        Diagnosed = true;
+        break;
+      }
+
+      if (Diagnosed == true)
+        continue;
+
+      Diag(FirstRecord->getLocation(),
+           diag::err_module_odr_violation_different_definitions)
+          << FirstRecord << FirstModule.empty() << FirstModule;
+
+      Diag(SecondRecord->getLocation(),
+           diag::note_module_odr_violation_different_definitions)
+          << SecondModule;
+      Diagnosed = true;
     }
 
     if (!Diagnosed) {

Added: cfe/trunk/test/Modules/odr_hash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/odr_hash.cpp?rev=295800&view=auto
==============================================================================
--- cfe/trunk/test/Modules/odr_hash.cpp (added)
+++ cfe/trunk/test/Modules/odr_hash.cpp Tue Feb 21 19:11:25 2017
@@ -0,0 +1,412 @@
+// Clear and create directories
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: mkdir %t/cache
+// RUN: mkdir %t/Inputs
+
+// Build first header file
+// RUN: echo "#define FIRST" >> %t/Inputs/first.h
+// RUN: cat %s               >> %t/Inputs/first.h
+
+// Build second header file
+// RUN: echo "#define SECOND" >> %t/Inputs/second.h
+// RUN: cat %s                >> %t/Inputs/second.h
+
+// Build module map file
+// RUN: echo "module FirstModule {"     >> %t/Inputs/module.map
+// RUN: echo "    header \"first.h\""   >> %t/Inputs/module.map
+// RUN: echo "}"                        >> %t/Inputs/module.map
+// RUN: echo "module SecondModule {"    >> %t/Inputs/module.map
+// RUN: echo "    header \"second.h\""  >> %t/Inputs/module.map
+// RUN: echo "}"                        >> %t/Inputs/module.map
+
+// Run test
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++11
+
+#if !defined(FIRST) && !defined(SECOND)
+#include "first.h"
+#include "second.h"
+#endif
+
+namespace AccessSpecifiers {
+#if defined(FIRST)
+struct S1 {
+};
+#elif defined(SECOND)
+struct S1 {
+  private:
+};
+#else
+S1 s1;
+// expected-error at second.h:* {{'AccessSpecifiers::S1' has different definitions in different modules; first difference is definition in module 'SecondModule' found private access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found end of class}}
+#endif
+
+#if defined(FIRST)
+struct S2 {
+  public:
+};
+#elif defined(SECOND)
+struct S2 {
+  protected:
+};
+#else
+S2 s2;
+// expected-error at second.h:* {{'AccessSpecifiers::S2' has different definitions in different modules; first difference is definition in module 'SecondModule' found protected access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found public access specifier}}
+#endif
+} // namespace AccessSpecifiers
+
+// Naive parsing of AST can lead to cycles in processing.  Ensure
+// self-references don't trigger an endless cycles of AST node processing.
+namespace SelfReference {
+#if defined(FIRST)
+template <template <int> class T> class Wrapper {};
+
+template <int N> class S {
+  S(Wrapper<::SelfReference::S> &Ref) {}
+};
+
+struct Xx {
+  struct Yy {
+  };
+};
+
+Xx::Xx::Xx::Yy yy;
+
+namespace NNS {
+template <typename> struct Foo;
+template <template <class> class T = NNS::Foo>
+struct NestedNamespaceSpecifier {};
+}
+#endif
+}  // namespace SelfReference
+
+// Interesting cases that should not cause errors.  struct S should not error
+// while struct T should error at the access specifier mismatch at the end.
+namespace AllDecls {
+#if defined(FIRST)
+struct S {
+  public:
+  private:
+  protected:
+};
+#elif defined(SECOND)
+struct S {
+  public:
+  private:
+  protected:
+};
+#else
+S s;
+#endif
+
+#if defined(FIRST)
+struct T {
+  public:
+  private:
+  protected:
+
+  private:
+};
+#elif defined(SECOND)
+struct T {
+  public:
+  private:
+  protected:
+
+  public:
+};
+#else
+T t;
+// expected-error at second.h:* {{'AllDecls::T' has different definitions in different modules; first difference is definition in module 'SecondModule' found public access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found private access specifier}}
+#endif
+}
+
+namespace FriendFunction {
+#if defined(FIRST)
+void F(int = 0);
+struct S { friend void F(int); };
+#elif defined(SECOND)
+void F(int);
+struct S { friend void F(int); };
+#else
+S s;
+#endif
+
+#if defined(FIRST)
+void G(int = 0);
+struct T {
+  friend void G(int);
+
+  private:
+};
+#elif defined(SECOND)
+void G(int);
+struct T {
+  friend void G(int);
+
+  public:
+};
+#else
+T t;
+// expected-error at second.h:* {{'FriendFunction::T' has different definitions in different modules; first difference is definition in module 'SecondModule' found public access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found private access specifier}}
+#endif
+}  // namespace FriendFunction
+
+namespace ImplicitDecl {
+#if defined(FIRST)
+struct S { };
+void S_Constructors() {
+  // Trigger creation of implicit contructors
+  S foo;
+  S bar = foo;
+  S baz(bar);
+}
+#elif defined(SECOND)
+struct S { };
+#else
+S s;
+#endif
+
+#if defined(FIRST)
+struct T {
+  private:
+};
+void T_Constructors() {
+  // Trigger creation of implicit contructors
+  T foo;
+  T bar = foo;
+  T baz(bar);
+}
+#elif defined(SECOND)
+struct T {
+  public:
+};
+#else
+T t;
+// expected-error at first.h:* {{'ImplicitDecl::T' has different definitions in different modules; first difference is definition in module 'FirstModule' found private access specifier}}
+// expected-note at second.h:* {{but in 'SecondModule' found public access specifier}}
+#endif
+
+}  // namespace ImplicitDelc
+
+namespace TemplatedClass {
+#if defined(FIRST)
+template <class>
+struct S {};
+#elif defined(SECOND)
+template <class>
+struct S {};
+#else
+S<int> s;
+#endif
+
+#if defined(FIRST)
+template <class>
+struct T {
+  private:
+};
+#elif defined(SECOND)
+template <class>
+struct T {
+  public:
+};
+#else
+T<int> t;
+// expected-error at second.h:* {{'TemplatedClass::T' has different definitions in different modules; first difference is definition in module 'SecondModule' found public access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found private access specifier}}
+#endif
+}  // namespace TemplatedClass
+
+namespace TemplateClassWithField {
+#if defined(FIRST)
+template <class A>
+struct S {
+  A a;
+};
+#elif defined(SECOND)
+template <class A>
+struct S {
+  A a;
+};
+#else
+S<int> s;
+#endif
+
+#if defined(FIRST)
+template <class A>
+struct T {
+  A a;
+
+  private:
+};
+#elif defined(SECOND)
+template <class A>
+struct T {
+  A a;
+
+  public:
+};
+#else
+T<int> t;
+// expected-error at second.h:* {{'TemplateClassWithField::T' has different definitions in different modules; first difference is definition in module 'SecondModule' found public access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found private access specifier}}
+#endif
+}  // namespace TemplateClassWithField
+
+namespace TemplateClassWithTemplateField {
+#if defined(FIRST)
+template <class A>
+class WrapperS;
+template <class A>
+struct S {
+  WrapperS<A> a;
+};
+#elif defined(SECOND)
+template <class A>
+class WrapperS;
+template <class A>
+struct S {
+  WrapperS<A> a;
+};
+#else
+template <class A>
+class WrapperS{};
+S<int> s;
+#endif
+
+#if defined(FIRST)
+template <class A>
+class WrapperT;
+template <class A>
+struct T {
+  WrapperT<A> a;
+
+  public:
+};
+#elif defined(SECOND)
+template <class A>
+class WrapperT;
+template <class A>
+struct T {
+  WrapperT<A> a;
+
+  private:
+};
+#else
+template <class A>
+class WrapperT{};
+T<int> t;
+// expected-error at second.h:* {{'TemplateClassWithTemplateField::T' has different definitions in different modules; first difference is definition in module 'SecondModule' found private access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found public access specifier}}
+#endif
+}  // namespace TemplateClassWithTemplateField
+
+namespace EnumWithForwardDeclaration {
+#if defined(FIRST)
+enum E : int;
+struct S {
+  void get(E) {}
+};
+#elif defined(SECOND)
+enum E : int { A, B };
+struct S {
+  void get(E) {}
+};
+#else
+S s;
+#endif
+
+#if defined(FIRST)
+struct T {
+  void get(E) {}
+  public:
+};
+#elif defined(SECOND)
+struct T {
+  void get(E) {}
+  private:
+};
+#else
+T t;
+// expected-error at second.h:* {{'EnumWithForwardDeclaration::T' has different definitions in different modules; first difference is definition in module 'SecondModule' found private access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found public access specifier}}
+#endif
+}  // namespace EnumWithForwardDeclaration
+
+namespace StructWithForwardDeclaration {
+#if defined(FIRST)
+struct P {};
+struct S {
+  struct P *ptr;
+};
+#elif defined(SECOND)
+struct S {
+  struct P *ptr;
+};
+#else
+S s;
+#endif
+
+#if defined(FIRST)
+struct Q {};
+struct T {
+  struct Q *ptr;
+  public:
+};
+#elif defined(SECOND)
+struct T {
+  struct Q *ptr;
+  private:
+};
+#else
+T t;
+// expected-error at second.h:* {{'StructWithForwardDeclaration::T' has different definitions in different modules; first difference is definition in module 'SecondModule' found private access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found public access specifier}}
+#endif
+}  // namespace StructWithForwardDeclaration
+
+namespace StructWithForwardDeclarationNoDefinition {
+#if defined(FIRST)
+struct P;
+struct S {
+  struct P *ptr;
+};
+#elif defined(SECOND)
+struct S {
+  struct P *ptr;
+};
+#else
+S s;
+#endif
+
+#if defined(FIRST)
+struct Q;
+struct T {
+  struct Q *ptr;
+
+  public:
+};
+#elif defined(SECOND)
+struct T {
+  struct Q *ptr;
+
+  private:
+};
+#else
+T t;
+// expected-error at second.h:* {{'StructWithForwardDeclarationNoDefinition::T' has different definitions in different modules; first difference is definition in module 'SecondModule' found private access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found public access specifier}}
+#endif
+}  // namespace StructWithForwardDeclarationNoDefinition
+
+// Keep macros contained to one file.
+#ifdef FIRST
+#undef FIRST
+#endif
+#ifdef SECOND
+#undef SECOND
+#endif




More information about the cfe-commits mailing list