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