]> granicus.if.org Git - clang/commitdiff
[ODRHash] Extend hash to support all Type's.
authorRichard Trieu <rtrieu@google.com>
Tue, 4 Sep 2018 22:53:19 +0000 (22:53 +0000)
committerRichard Trieu <rtrieu@google.com>
Tue, 4 Sep 2018 22:53:19 +0000 (22:53 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@341421 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ODRHash.h
lib/AST/ODRHash.cpp
lib/AST/StmtProfile.cpp
test/Modules/odr_hash-blocks.cpp
test/Modules/odr_hash-gnu.cpp [new file with mode: 0644]
test/Modules/odr_hash-vector.cpp [new file with mode: 0644]
test/Modules/odr_hash.cl [new file with mode: 0644]
test/Modules/odr_hash.cpp
test/Modules/odr_hash.mm

index 0e52d3d8543610f98372932e52636f7fb3e9b931..0298887d1b1d7133515268dadd6e04c2a4bbff27 100644 (file)
@@ -83,7 +83,7 @@ public:
   void AddIdentifierInfo(const IdentifierInfo *II);
   void AddNestedNameSpecifier(const NestedNameSpecifier *NNS);
   void AddTemplateName(TemplateName Name);
-  void AddDeclarationName(DeclarationName Name);
+  void AddDeclarationName(DeclarationName Name, bool TreatAsDecl = false);
   void AddTemplateArgument(TemplateArgument TA);
   void AddTemplateParameterList(const TemplateParameterList *TPL);
 
index e710d3780337ebe31573212c9ff09f2b2446f308..1624468079a0ad153fe7a630e8676e0e8daa2097 100644 (file)
@@ -32,7 +32,10 @@ void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) {
   ID.AddString(II->getName());
 }
 
-void ODRHash::AddDeclarationName(DeclarationName Name) {
+void ODRHash::AddDeclarationName(DeclarationName Name, bool TreatAsDecl) {
+  if (TreatAsDecl)
+    AddBoolean(true);
+
   // Index all DeclarationName and use index numbers to refer to them.
   auto Result = DeclNameMap.insert(std::make_pair(Name, DeclNameMap.size()));
   ID.AddInteger(Result.first->second);
@@ -88,6 +91,9 @@ void ODRHash::AddDeclarationName(DeclarationName Name) {
     }
   }
   }
+
+  if (TreatAsDecl)
+    AddBoolean(false);
 }
 
 void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {
@@ -405,6 +411,7 @@ public:
 
   void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
     AddDecl(D->getTemplatedDecl());
+    ID.AddInteger(D->getTemplatedDecl()->getODRHash());
     Inherited::VisitFunctionTemplateDecl(D);
   }
 
@@ -552,11 +559,27 @@ void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
                        !Function->isDefaulted() && !Function->isDeleted() &&
                        !Function->isLateTemplateParsed();
   AddBoolean(HasBody);
-  if (HasBody) {
-    auto *Body = Function->getBody();
-    AddBoolean(Body);
-    if (Body)
-      AddStmt(Body);
+  if (!HasBody) {
+    return;
+  }
+
+  auto *Body = Function->getBody();
+  AddBoolean(Body);
+  if (Body)
+    AddStmt(Body);
+
+  // 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 : Function->decls()) {
+    if (isWhitelistedDecl(SubDecl, Function)) {
+      Decls.push_back(SubDecl);
+    }
+  }
+
+  ID.AddInteger(Decls.size());
+  for (auto SubDecl : Decls) {
+    AddSubDecl(SubDecl);
   }
 }
 
@@ -592,13 +615,24 @@ void ODRHash::AddDecl(const Decl *D) {
   assert(D && "Expecting non-null pointer.");
   D = D->getCanonicalDecl();
 
-  if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
-    AddDeclarationName(ND->getDeclName());
+  const NamedDecl *ND = dyn_cast<NamedDecl>(D);
+  AddBoolean(ND);
+  if (!ND) {
+    ID.AddInteger(D->getKind());
     return;
   }
 
-  ID.AddInteger(D->getKind());
-  // TODO: Handle non-NamedDecl here.
+  AddDeclarationName(ND->getDeclName());
+
+  const auto *Specialization =
+            dyn_cast<ClassTemplateSpecializationDecl>(D);
+  AddBoolean(Specialization);
+  if (Specialization) {
+    const TemplateArgumentList &List = Specialization->getTemplateArgs();
+    ID.AddInteger(List.size());
+    for (const TemplateArgument &TA : List.asArray())
+      AddTemplateArgument(TA);
+  }
 }
 
 namespace {
@@ -700,11 +734,67 @@ public:
     VisitArrayType(T);
   }
 
+  void VisitAttributedType(const AttributedType *T) {
+    ID.AddInteger(T->getAttrKind());
+    AddQualType(T->getModifiedType());
+    AddQualType(T->getEquivalentType());
+
+    VisitType(T);
+  }
+
+  void VisitBlockPointerType(const BlockPointerType *T) {
+    AddQualType(T->getPointeeType());
+    VisitType(T);
+  }
+
   void VisitBuiltinType(const BuiltinType *T) {
     ID.AddInteger(T->getKind());
     VisitType(T);
   }
 
+  void VisitComplexType(const ComplexType *T) {
+    AddQualType(T->getElementType());
+    VisitType(T);
+  }
+
+  void VisitDecltypeType(const DecltypeType *T) {
+    AddStmt(T->getUnderlyingExpr());
+    AddQualType(T->getUnderlyingType());
+    VisitType(T);
+  }
+
+  void VisitDependentDecltypeType(const DependentDecltypeType *T) {
+    VisitDecltypeType(T);
+  }
+
+  void VisitDeducedType(const DeducedType *T) {
+    AddQualType(T->getDeducedType());
+    VisitType(T);
+  }
+
+  void VisitAutoType(const AutoType *T) {
+    ID.AddInteger((unsigned)T->getKeyword());
+    VisitDeducedType(T);
+  }
+
+  void VisitDeducedTemplateSpecializationType(
+      const DeducedTemplateSpecializationType *T) {
+    Hash.AddTemplateName(T->getTemplateName());
+    VisitDeducedType(T);
+  }
+
+  void VisitDependentAddressSpaceType(const DependentAddressSpaceType *T) {
+    AddQualType(T->getPointeeType());
+    AddStmt(T->getAddrSpaceExpr());
+    VisitType(T);
+  }
+
+  void VisitDependentSizedExtVectorType(const DependentSizedExtVectorType *T) {
+    AddQualType(T->getElementType());
+    AddStmt(T->getSizeExpr());
+    VisitType(T);
+  }
+
   void VisitFunctionType(const FunctionType *T) {
     AddQualType(T->getReturnType());
     T->getExtInfo().Profile(ID);
@@ -726,6 +816,74 @@ public:
     VisitFunctionType(T);
   }
 
+  void VisitInjectedClassNameType(const InjectedClassNameType *T) {
+    AddDecl(T->getDecl());
+    VisitType(T);
+  }
+
+  void VisitMemberPointerType(const MemberPointerType *T) {
+    AddQualType(T->getPointeeType());
+    AddType(T->getClass());
+    VisitType(T);
+  }
+
+  void VisitObjCObjectPointerType(const ObjCObjectPointerType *T) {
+    AddQualType(T->getPointeeType());
+    VisitType(T);
+  }
+
+  void VisitObjCObjectType(const ObjCObjectType *T) {
+    AddDecl(T->getInterface());
+
+    auto TypeArgs = T->getTypeArgsAsWritten();
+    ID.AddInteger(TypeArgs.size());
+    for (auto Arg : TypeArgs) {
+      AddQualType(Arg);
+    }
+
+    auto Protocols = T->getProtocols();
+    ID.AddInteger(Protocols.size());
+    for (auto Protocol : Protocols) {
+      AddDecl(Protocol);
+    }
+
+    Hash.AddBoolean(T->isKindOfType());
+
+    VisitType(T);
+  }
+
+  void VisitObjCInterfaceType(const ObjCInterfaceType *T) {
+    // This type is handled by the parent type ObjCObjectType.
+    VisitObjCObjectType(T);
+  }
+
+  void VisitObjCTypeParamType(const ObjCTypeParamType *T) {
+    AddDecl(T->getDecl());
+    auto Protocols = T->getProtocols();
+    ID.AddInteger(Protocols.size());
+    for (auto Protocol : Protocols) {
+      AddDecl(Protocol);
+    }
+
+    VisitType(T);
+  }
+
+  void VisitPackExpansionType(const PackExpansionType *T) {
+    AddQualType(T->getPattern());
+    VisitType(T);
+  }
+
+  void VisitParenType(const ParenType *T) {
+    AddQualType(T->getInnerType());
+    VisitType(T);
+  }
+
+  void VisitPipeType(const PipeType *T) {
+    AddQualType(T->getElementType());
+    Hash.AddBoolean(T->isReadOnly());
+    VisitType(T);
+  }
+
   void VisitPointerType(const PointerType *T) {
     AddQualType(T->getPointeeType());
     VisitType(T);
@@ -744,6 +902,43 @@ public:
     VisitReferenceType(T);
   }
 
+  void
+  VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) {
+    AddType(T->getReplacedParameter());
+    Hash.AddTemplateArgument(T->getArgumentPack());
+    VisitType(T);
+  }
+
+  void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) {
+    AddType(T->getReplacedParameter());
+    AddQualType(T->getReplacementType());
+    VisitType(T);
+  }
+
+  void VisitTagType(const TagType *T) {
+    AddDecl(T->getDecl());
+    VisitType(T);
+  }
+
+  void VisitRecordType(const RecordType *T) { VisitTagType(T); }
+  void VisitEnumType(const EnumType *T) { VisitTagType(T); }
+
+  void VisitTemplateSpecializationType(const TemplateSpecializationType *T) {
+    ID.AddInteger(T->getNumArgs());
+    for (const auto &TA : T->template_arguments()) {
+      Hash.AddTemplateArgument(TA);
+    }
+    Hash.AddTemplateName(T->getTemplateName());
+    VisitType(T);
+  }
+
+  void VisitTemplateTypeParmType(const TemplateTypeParmType *T) {
+    ID.AddInteger(T->getDepth());
+    ID.AddInteger(T->getIndex());
+    Hash.AddBoolean(T->isParameterPack());
+    AddDecl(T->getDecl());
+  }
+
   void VisitTypedefType(const TypedefType *T) {
     AddDecl(T->getDecl());
     QualType UnderlyingType = T->getDecl()->getUnderlyingType();
@@ -766,13 +961,18 @@ public:
     VisitType(T);
   }
 
-  void VisitTagType(const TagType *T) {
-    AddDecl(T->getDecl());
+  void VisitTypeOfExprType(const TypeOfExprType *T) {
+    AddStmt(T->getUnderlyingExpr());
+    Hash.AddBoolean(T->isSugared());
+    if (T->isSugared())
+      AddQualType(T->desugar());
+
+    VisitType(T);
+  }
+  void VisitTypeOfType(const TypeOfType *T) {
+    AddQualType(T->getUnderlyingType());
     VisitType(T);
   }
-
-  void VisitRecordType(const RecordType *T) { VisitTagType(T); }
-  void VisitEnumType(const EnumType *T) { VisitTagType(T); }
 
   void VisitTypeWithKeyword(const TypeWithKeyword *T) {
     ID.AddInteger(T->getKeyword());
@@ -802,20 +1002,26 @@ public:
     VisitTypeWithKeyword(T);
   }
 
-  void VisitTemplateSpecializationType(const TemplateSpecializationType *T) {
-    ID.AddInteger(T->getNumArgs());
-    for (const auto &TA : T->template_arguments()) {
-      Hash.AddTemplateArgument(TA);
-    }
-    Hash.AddTemplateName(T->getTemplateName());
+  void VisitUnaryTransformType(const UnaryTransformType *T) {
+    AddQualType(T->getUnderlyingType());
+    AddQualType(T->getBaseType());
     VisitType(T);
   }
 
-  void VisitTemplateTypeParmType(const TemplateTypeParmType *T) {
-    ID.AddInteger(T->getDepth());
-    ID.AddInteger(T->getIndex());
-    Hash.AddBoolean(T->isParameterPack());
+  void VisitUnresolvedUsingType(const UnresolvedUsingType *T) {
     AddDecl(T->getDecl());
+    VisitType(T);
+  }
+
+  void VisitVectorType(const VectorType *T) {
+    AddQualType(T->getElementType());
+    ID.AddInteger(T->getNumElements());
+    ID.AddInteger(T->getVectorKind());
+    VisitType(T);
+  }
+
+  void VisitExtVectorType(const ExtVectorType * T) {
+    VisitVectorType(T);
   }
 };
 } // namespace
index 15653c4fd838c9daa76060dbb376a6603d920c2e..133ecf7cdc6831c859642ea10cef555fbfba7e52 100644 (file)
@@ -189,7 +189,7 @@ namespace {
         // store its nullness.  Add a boolean here to match.
         ID.AddBoolean(true);
       }
-      Hash.AddDeclarationName(Name);
+      Hash.AddDeclarationName(Name, TreatAsDecl);
     }
     void VisitIdentifierInfo(IdentifierInfo *II) override {
       ID.AddBoolean(II);
index 07dfa4ce2ac88843661e9699e63a34fdee57288d..512c659f841e491bcae8182c0e408ce8e17a16cc 100644 (file)
@@ -41,7 +41,7 @@
 #define ACCESS private:
 #endif
 
-// TODO: S1, S2, and S3 should generate errors.
+// TODO: S1 and S2 should generate errors.
 namespace Blocks {
 #if defined(FIRST)
 struct S1 {
@@ -77,6 +77,8 @@ struct S3 {
 };
 #else
 S3 s3;
+// expected-error@first.h:* {{'Blocks::S3::run' from module 'FirstModule' is not present in definition of 'Blocks::S3' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'run' does not match}}
 #endif
 
 #define DECLS                                       \
diff --git a/test/Modules/odr_hash-gnu.cpp b/test/Modules/odr_hash-gnu.cpp
new file mode 100644 (file)
index 0000000..584c24d
--- /dev/null
@@ -0,0 +1,130 @@
+// 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
+
+// Test that each header can compile
+// RUN: %clang_cc1 -fsyntax-only -x c++ -std=gnu++11 %t/Inputs/first.h
+// RUN: %clang_cc1 -fsyntax-only -x c++ -std=gnu++11 %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=gnu++11 -fcolor-diagnostics
+
+#if !defined(FIRST) && !defined(SECOND)
+#include "first.h"
+#include "second.h"
+#endif
+
+namespace Types {
+namespace TypeOfExpr {
+#if defined(FIRST)
+struct Invalid1 {
+  typeof(1 + 2) x;
+};
+double global;
+struct Invalid2 {
+  typeof(global) x;
+};
+struct Valid {
+  typeof(3) x;
+  typeof(x) y;
+  typeof(Valid*) self;
+};
+#elif defined(SECOND)
+struct Invalid1 {
+  typeof(3) x;
+};
+int global;
+struct Invalid2 {
+  typeof(global) x;
+};
+struct Valid {
+  typeof(3) x;
+  typeof(x) y;
+  typeof(Valid*) self;
+};
+#else
+Invalid1 i1;
+// expected-error@first.h:* {{'Types::TypeOfExpr::Invalid1' has different definitions in different modules; first difference is definition in module 'FirstModule' found field 'x' with type 'typeof (1 + 2)' (aka 'int')}}
+// expected-note@second.h:* {{but in 'SecondModule' found field 'x' with type 'typeof (3)' (aka 'int')}}
+Invalid2 i2;
+// expected-error@second.h:* {{'Types::TypeOfExpr::Invalid2::x' from module 'SecondModule' is not present in definition of 'Types::TypeOfExpr::Invalid2' in module 'FirstModule'}}
+// expected-note@first.h:* {{declaration of 'x' does not match}}
+Valid v;
+#endif
+}  // namespace TypeOfExpr
+
+namespace TypeOf {
+#if defined(FIRST)
+struct Invalid1 {
+  typeof(int) x;
+};
+struct Invalid2 {
+  typeof(int) x;
+};
+using T = int;
+struct Invalid3 {
+  typeof(T) x;
+};
+struct Valid {
+  typeof(int) x;
+  using T = typeof(double);
+  typeof(T) y;
+};
+#elif defined(SECOND)
+struct Invalid1 {
+  typeof(double) x;
+};
+using I = int;
+struct Invalid2 {
+  typeof(I) x;
+};
+using T = short;
+struct Invalid3 {
+  typeof(T) x;
+};
+struct Valid {
+  typeof(int) x;
+  using T = typeof(double);
+  typeof(T) y;
+};
+#else
+Invalid1 i1;
+// expected-error@second.h:* {{'Types::TypeOf::Invalid1::x' from module 'SecondModule' is not present in definition of 'Types::TypeOf::Invalid1' in module 'FirstModule'}}
+// expected-note@first.h:* {{declaration of 'x' does not match}}
+Invalid2 i2;
+// expected-error@first.h:* {{'Types::TypeOf::Invalid2' has different definitions in different modules; first difference is definition in module 'FirstModule' found field 'x' with type 'typeof(int)' (aka 'int')}}
+// expected-note@second.h:* {{but in 'SecondModule' found field 'x' with type 'typeof(Types::TypeOf::I)' (aka 'int')}}
+Invalid3 i3;
+// expected-error@second.h:* {{'Types::TypeOf::Invalid3::x' from module 'SecondModule' is not present in definition of 'Types::TypeOf::Invalid3' in module 'FirstModule'}}
+// expected-note@first.h:* {{declaration of 'x' does not match}}
+Valid v;
+#endif
+}  // namespace TypeOf
+}  // namespace Types
+
+// Keep macros contained to one file.
+#ifdef FIRST
+#undef FIRST
+#endif
+
+#ifdef SECOND
+#undef SECOND
+#endif
diff --git a/test/Modules/odr_hash-vector.cpp b/test/Modules/odr_hash-vector.cpp
new file mode 100644 (file)
index 0000000..ee59cb6
--- /dev/null
@@ -0,0 +1,128 @@
+// 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
+
+// Test that each header can compile
+// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++11 %t/Inputs/first.h -fzvector
+// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++11 %t/Inputs/second.h -fzvector
+
+// 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 -fzvector
+
+#if !defined(FIRST) && !defined(SECOND)
+#include "first.h"
+#include "second.h"
+#endif
+
+namespace Types {
+namespace Vector {
+#if defined(FIRST)
+struct Invalid1 {
+  __attribute((vector_size(8))) int x;
+};
+struct Invalid2 {
+  __attribute((vector_size(8))) int x;
+};
+struct Invalid3 {
+  __attribute((vector_size(16))) int x;
+};
+struct Valid {
+  __attribute((vector_size(8))) int x1;
+  __attribute((vector_size(16))) int x2;
+  __attribute((vector_size(8))) unsigned x3;
+  __attribute((vector_size(16))) long x4;
+  vector unsigned x5;
+  vector int x6;
+};
+#elif defined(SECOND)
+struct Invalid1 {
+  __attribute((vector_size(16))) int x;
+};
+struct Invalid2 {
+  __attribute((vector_size(8))) unsigned x;
+};
+struct Invalid3 {
+  vector unsigned x;
+};
+struct Valid {
+  __attribute((vector_size(8))) int x1;
+  __attribute((vector_size(16))) int x2;
+  __attribute((vector_size(8))) unsigned x3;
+  __attribute((vector_size(16))) long x4;
+  vector unsigned x5;
+  vector int x6;
+};
+#else
+Invalid1 i1;
+// expected-error@second.h:* {{'Types::Vector::Invalid1::x' from module 'SecondModule' is not present in definition of 'Types::Vector::Invalid1' in module 'FirstModule'}}
+// expected-note@first.h:* {{declaration of 'x' does not match}}
+Invalid2 i2;
+// expected-error@second.h:* {{'Types::Vector::Invalid2::x' from module 'SecondModule' is not present in definition of 'Types::Vector::Invalid2' in module 'FirstModule'}}
+// expected-note@first.h:* {{declaration of 'x' does not match}}
+Invalid3 i3;
+// expected-error@second.h:* {{'Types::Vector::Invalid3::x' from module 'SecondModule' is not present in definition of 'Types::Vector::Invalid3' in module 'FirstModule'}}
+// expected-note@first.h:* {{declaration of 'x' does not match}}
+
+Valid v;
+#endif
+}  // namespace Vector
+
+
+
+namespace ExtVector {
+}  // namespace ExtVector
+#if defined(FIRST)
+struct Invalid {
+  using f = __attribute__((ext_vector_type(4))) float;
+};
+struct Valid {
+  using f = __attribute__((ext_vector_type(8))) float;
+};
+#elif defined(SECOND)
+struct Invalid {
+  using f = __attribute__((ext_vector_type(8))) float;
+};
+struct Valid {
+  using f = __attribute__((ext_vector_type(8))) float;
+};
+#else
+Invalid i;
+// expected-error@first.h:* {{'Types::Invalid::f' from module 'FirstModule' is not present in definition of 'Types::Invalid' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'f' does not match}}
+
+Valid v;
+#endif
+
+}  // namespace Types
+
+
+// Keep macros contained to one file.
+#ifdef FIRST
+#undef FIRST
+#endif
+
+#ifdef SECOND
+#undef SECOND
+#endif
+
+#ifdef ACCESS
+#undef ACCESS
+#endif
diff --git a/test/Modules/odr_hash.cl b/test/Modules/odr_hash.cl
new file mode 100644 (file)
index 0000000..d4af1ce
--- /dev/null
@@ -0,0 +1,80 @@
+// 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
+
+// Test that each header can compile
+// RUN: %clang_cc1 -fsyntax-only -x c++ %t/Inputs/first.h -cl-std=CL2.0
+// RUN: %clang_cc1 -fsyntax-only -x c++ %t/Inputs/second.h -cl-std=CL2.0
+
+// 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 -cl-std=CL2.0
+
+#if !defined(FIRST) && !defined(SECOND)
+#include "first.h"
+#include "second.h"
+#endif
+
+
+#if defined(FIRST)
+void invalid1() {
+  typedef read_only pipe int x;
+}
+void invalid2() {
+  typedef read_only pipe int x;
+}
+void valid() {
+  typedef read_only pipe int x;
+  typedef write_only pipe int y;
+  typedef read_write pipe int z;
+}
+#elif defined(SECOND)
+void invalid1() {
+  typedef write_only pipe int x;
+}
+void invalid2() {
+  typedef read_only pipe float x;
+}
+void valid() {
+  typedef read_only pipe int x;
+  typedef write_only pipe int y;
+  typedef read_write pipe int z;
+}
+#else
+void run() {
+  invalid1();
+// expected-error@second.h:* {{'invalid1' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+  invalid2();
+// expected-error@second.h:* {{'invalid2' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+  valid();
+}
+#endif
+
+
+// Keep macros contained to one file.
+#ifdef FIRST
+#undef FIRST
+#endif
+
+#ifdef SECOND
+#undef SECOND
+#endif
index 117029405ea03fbd3845381a009cf2fb6d779269..3cfda89d16a6a2754a8bb98bebcffb9087a4e9c3 100644 (file)
@@ -25,7 +25,7 @@
 // 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++1z
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++1z -fcolor-diagnostics
 
 #if !defined(FIRST) && !defined(SECOND)
 #include "first.h"
@@ -3299,6 +3299,568 @@ Valid V;
 #endif
 }  // namespace Enums
 
+namespace Types {
+namespace Complex {
+#if defined(FIRST)
+void invalid() {
+  _Complex float x;
+}
+void valid() {
+  _Complex float x;
+}
+#elif defined(SECOND)
+void invalid() {
+  _Complex double x;
+}
+void valid() {
+  _Complex float x;
+}
+#else
+auto function1 = invalid;
+// expected-error@second.h:* {{'Types::Complex::invalid' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function2 = valid;
+#endif
+}  // namespace Complex
+
+namespace Decltype {
+#if defined(FIRST)
+void invalid1() {
+  decltype(1 + 1) x;
+}
+int global;
+void invalid2() {
+  decltype(global) x;
+}
+void valid() {
+  decltype(1.5) x;
+  decltype(x) y;
+}
+#elif defined(SECOND)
+void invalid1() {
+  decltype(2) x;
+}
+float global;
+void invalid2() {
+  decltype(global) x;
+}
+void valid() {
+  decltype(1.5) x;
+  decltype(x) y;
+}
+#else
+auto function1 = invalid1;
+// expected-error@second.h:* {{'Types::Decltype::invalid1' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function2 = invalid2;
+// expected-error@second.h:* {{'Types::Decltype::invalid2' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function3 = valid;
+#endif
+}  // namespace Decltype
+
+namespace Auto {
+#if defined(FIRST)
+void invalid1() {
+  decltype(auto) x = 1;
+}
+void invalid2() {
+  auto x = 1;
+}
+void invalid3() {
+  __auto_type x = 1;
+}
+void valid() {
+  decltype(auto) x = 1;
+  auto y = 1;
+  __auto_type z = 1;
+}
+#elif defined(SECOND)
+void invalid1() {
+  auto x = 1;
+}
+void invalid2() {
+  __auto_type x = 1;
+}
+void invalid3() {
+  decltype(auto) x = 1;
+}
+void valid() {
+  decltype(auto) x = 1;
+  auto y = 1;
+  __auto_type z = 1;
+}
+#else
+auto function1 = invalid1;
+// expected-error@second.h:* {{'Types::Auto::invalid1' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function2 = invalid3;
+// expected-error@second.h:* {{'Types::Auto::invalid2' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function3 = invalid2;
+// expected-error@second.h:* {{'Types::Auto::invalid3' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function4 = valid;
+#endif
+}  // namespace Auto
+
+namespace DeducedTemplateSpecialization {
+#if defined(FIRST)
+template<typename T> struct A {};
+A() -> A<int>;
+template<typename T> struct B {};
+B() -> B<int>;
+
+void invalid1() {
+  A a{};
+}
+void invalid2() {
+  A a{};
+}
+void valid() {
+  B b{};
+}
+#elif defined(SECOND)
+template<typename T> struct A {};
+A() -> A<float>;
+template<typename T> struct B {};
+B() -> B<int>;
+
+void invalid1() {
+  A a{};
+}
+void invalid2() {
+  B a{};
+}
+void valid() {
+  B b{};
+}
+#else
+auto function1 = invalid1;
+// expected-error@second.h:* {{'Types::DeducedTemplateSpecialization::invalid1' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function2 = invalid2;
+// expected-error@second.h:* {{'Types::DeducedTemplateSpecialization::invalid2' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function3 = valid;
+#endif
+}  // namespace DeducedTemplateSpecialization
+
+namespace DependentAddressSpace {
+#if defined(FIRST)
+template <int A1, int A2>
+void invalid1() {
+  using type = int __attribute__((address_space(A1)));
+}
+template <int A1>
+void invalid2() {
+  using type = float __attribute__((address_space(A1)));
+}
+template <int A1, int A2>
+void valid() {
+  using type1 = float __attribute__((address_space(A1)));
+  using type2 = int __attribute__((address_space(A2)));
+  using type3 = int __attribute__((address_space(A1 + A2)));
+}
+#elif defined(SECOND)
+template <int A1, int A2>
+void invalid1() {
+  using type = int __attribute__((address_space(A2)));
+}
+template <int A1>
+void invalid2() {
+  using type = int __attribute__((address_space(A1)));
+}
+template <int A1, int A2>
+void valid() {
+  using type1 = float __attribute__((address_space(A1)));
+  using type2 = int __attribute__((address_space(A2)));
+  using type3 = int __attribute__((address_space(A1 + A2)));
+}
+#else
+template <int A, int B>
+class S {
+  static auto function1 = invalid1<A, B>;
+  // expected-error@first.h:* {{'Types::DependentAddressSpace::invalid1' has different definitions in different modules; definition in module 'FirstModule' first difference is function body}}
+  // expected-note@second.h:* {{but in 'SecondModule' found a different body}}
+  static auto function2 = invalid2<B>;
+  // expected-error@first.h:* {{'Types::DependentAddressSpace::invalid2' has different definitions in different modules; definition in module 'FirstModule' first difference is function body}}
+  // expected-note@second.h:* {{but in 'SecondModule' found a different body}}
+  static auto function3 = valid<A, B>;
+};
+#endif
+}  // namespace DependentAddressSpace
+
+namespace DependentSizedExtVector {
+#if defined(FIRST)
+template<int Size>
+void invalid1() {
+  typedef int __attribute__((ext_vector_type(Size))) type;
+}
+template<int Size>
+void invalid2() {
+  typedef int __attribute__((ext_vector_type(Size + 0))) type;
+}
+template<int Size>
+void valid() {
+  typedef int __attribute__((ext_vector_type(Size))) type;
+}
+#elif defined(SECOND)
+template<int Size>
+void invalid1() {
+  typedef float __attribute__((ext_vector_type(Size))) type;
+}
+template<int Size>
+void invalid2() {
+  typedef int __attribute__((ext_vector_type(Size + 1))) type;
+}
+template<int Size>
+void valid() {
+  typedef int __attribute__((ext_vector_type(Size))) type;
+}
+#else
+template <int Num>
+class S {
+  static auto Function1 = invalid1<Num>;
+  // expected-error@first.h:* {{'Types::DependentSizedExtVector::invalid1' has different definitions in different modules; definition in module 'FirstModule' first difference is function body}}
+  // expected-note@second.h:* {{but in 'SecondModule' found a different body}}
+  static auto Function2 = invalid2<Num>;
+  // expected-error@first.h:* {{'Types::DependentSizedExtVector::invalid2' has different definitions in different modules; definition in module 'FirstModule' first difference is function body}}
+  // expected-note@second.h:* {{but in 'SecondModule' found a different body}}
+  static auto Function3 = valid<Num>;
+};
+#endif
+}  // namespace DependentSizedExtVector
+
+namespace InjectedClassName {
+#if defined(FIRST)
+struct Invalid {
+  template <int>
+  struct L2 {
+    template <int>
+    struct L3 {
+      L3 *x;
+    };
+  };
+};
+struct Valid {
+  template <int>
+  struct L2 {
+    template <int>
+    struct L3 {
+      L2 *x;
+      L3 *y;
+    };
+  };
+};
+#elif defined(SECOND)
+struct Invalid {
+  template <int>
+  struct L2 {
+    template <int>
+    struct L3 {
+      L2 *x;
+    };
+  };
+};
+struct Valid {
+  template <int>
+  struct L2 {
+    template <int>
+    struct L3 {
+      L2 *x;
+      L3 *y;
+    };
+  };
+};
+#else
+Invalid::L2<1>::L3<1> invalid;
+// expected-error@second.h:* {{'Types::InjectedClassName::Invalid::L2::L3::x' from module 'SecondModule' is not present in definition of 'L3<>' in module 'FirstModule'}}
+// expected-note@first.h:* {{declaration of 'x' does not match}}
+Valid::L2<1>::L3<1> valid;
+#endif
+}  // namespace InjectedClassName
+
+namespace MemberPointer {
+#if defined(FIRST)
+struct A {};
+struct B {};
+
+void Invalid1() {
+  int A::*x;
+};
+void Invalid2() {
+  int A::*x;
+}
+void Invalid3() {
+  int (A::*x)(int);
+}
+void Valid() {
+  int A::*x;
+  float A::*y;
+  bool B::*z;
+  void (A::*fun1)();
+  int (A::*fun2)();
+  void (B::*fun3)(int);
+  void (B::*fun4)(bool*, int);
+}
+#elif defined(SECOND)
+struct A {};
+struct B {};
+
+void Invalid1() {
+  float A::*x;
+};
+void Invalid2() {
+  int B::*x;
+}
+void Invalid3() {
+  int (A::*x)(int, int);
+}
+void Valid() {
+  int A::*x;
+  float A::*y;
+  bool B::*z;
+  void (A::*fun1)();
+  int (A::*fun2)();
+  void (B::*fun3)(int);
+  void (B::*fun4)(bool*, int);
+}
+#else
+auto function1 = Invalid1;
+// expected-error@second.h:* {{'Types::MemberPointer::Invalid1' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function2 = Invalid2;
+// expected-error@second.h:* {{'Types::MemberPointer::Invalid2' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function3 = Invalid3;
+// expected-error@second.h:* {{'Types::MemberPointer::Invalid3' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function4 = Valid;
+#endif
+
+}  // namespace MemberPointer
+
+namespace PackExpansion {
+#if defined(FIRST)
+struct Invalid {
+  template <class... A>
+  struct L2 {
+    template <class... B>
+    struct L3 {
+      void run(A...);
+      void run(B...);
+    };
+  };
+};
+struct Valid {
+  template <class... A>
+  struct L2 {
+    template <class... B>
+    struct L3 {
+      void run(A...);
+      void run(B...);
+    };
+  };
+};
+#elif defined(SECOND)
+struct Invalid {
+  template <class... A>
+  struct L2 {
+    template <class... B>
+    struct L3 {
+      void run(B...);
+      void run(A...);
+    };
+  };
+};
+struct Valid {
+  template <class... A>
+  struct L2 {
+    template <class... B>
+    struct L3 {
+      void run(A...);
+      void run(B...);
+    };
+  };
+};
+#else
+Invalid::L2<int>::L3<short, bool> invalid;
+// expected-error@first.h:* {{'Types::PackExpansion::Invalid::L2::L3' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'run' with 1st parameter of type 'A...'}}
+// expected-note@second.h:* {{but in 'SecondModule' found method 'run' with 1st parameter of type 'B...'}}
+Valid::L2<int>::L3<short, bool> valid;
+#endif
+
+}  // namespace PackExpansion
+
+namespace Paren {
+#if defined(FIRST)
+void invalid() {
+  int (*x);
+}
+void valid() {
+  int (*x);
+}
+#elif defined(SECOND)
+void invalid() {
+  float (*x);
+}
+void valid() {
+  int (*x);
+}
+#else
+auto function1 = invalid;
+// expected-error@second.h:* {{'Types::Paren::invalid' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function2 = valid;
+#endif
+}  // namespace Paren
+
+namespace SubstTemplateTypeParm {
+#if defined(FIRST)
+template <class> struct wrapper {};
+template <class, class, class> struct triple {};
+struct Valid {
+  template <class T,
+           template <class _T, class _U, class = wrapper<_T>> class A = triple>
+  struct L2 {
+    A<T, T> x;
+  };
+};
+#elif defined(SECOND)
+template <class> struct wrapper {};
+template <class, class, class> struct triple {};
+struct Valid {
+  template <class T,
+           template <class _T, class _U, class = wrapper<_T>> class A = triple>
+  struct L2 {
+    A<T, T> x;
+  };
+};
+#else
+template <class T,
+          template <class _T, class _U, class = wrapper<_T>> class A = triple>
+using V = Valid::L2<T, A>;
+#endif
+}  // namespace SubstTemplateTypeParm
+
+namespace SubstTemplateTypeParmPack {
+}  // namespace SubstTemplateTypeParmPack
+
+namespace UnaryTransform {
+#if defined(FIRST)
+enum class E1a : unsigned {};
+struct Invalid1 {
+  __underlying_type(E1a) x;
+};
+enum E2a : unsigned {};
+struct Invalid2 {
+  __underlying_type(E2a) x;
+};
+enum E3a {};
+struct Invalid3 {
+  __underlying_type(E3a) x;
+};
+enum E4a {};
+struct Invalid4 {
+  __underlying_type(E4a) x;
+};
+enum E1 {};
+struct Valid1 {
+  __underlying_type(E1) x;
+};
+enum E2 : unsigned {};
+struct Valid2 {
+  __underlying_type(E2) x;
+};
+enum class E3 {};
+struct Valid3 {
+  __underlying_type(E3) x;
+};
+#elif defined(SECOND)
+enum class E1b : signed {};
+struct Invalid1 {
+  __underlying_type(E1b) x;
+};
+enum class E2b : unsigned {};
+struct Invalid2 {
+  __underlying_type(E2b) x;
+};
+enum E3b : int {};
+struct Invalid3 {
+  __underlying_type(E3b) x;
+};
+enum E4b {};
+struct Invalid4 {
+  __underlying_type(E4b) x;
+};
+#else
+Invalid1 i1;
+// expected-error@first.h:* {{'Types::UnaryTransform::Invalid1::x' from module 'FirstModule' is not present in definition of 'Types::UnaryTransform::Invalid1' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'x' does not match}}
+Invalid2 i2;
+// expected-error@second.h:* {{'Types::UnaryTransform::Invalid2' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with type '__underlying_type(Types::UnaryTransform::E2b)' (aka 'unsigned int')}}
+// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with type '__underlying_type(Types::UnaryTransform::E2a)' (aka 'unsigned int')}}
+Invalid3 i3;
+// expected-error@first.h:* {{'Types::UnaryTransform::Invalid3::x' from module 'FirstModule' is not present in definition of 'Types::UnaryTransform::Invalid3' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'x' does not match}}
+Invalid4 i4;
+// expected-error@second.h:* {{'Types::UnaryTransform::Invalid4' has different definitions in different modules; first difference is definition in module 'SecondModule' found field 'x' with type '__underlying_type(Types::UnaryTransform::E4b)' (aka 'unsigned int')}}
+// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with type '__underlying_type(Types::UnaryTransform::E4a)' (aka 'unsigned int')}}
+Valid1 v1;
+Valid2 v2;
+Valid3 v3;
+#endif
+}  // namespace UnaryTransform
+
+namespace UnresolvedUsing {
+#if defined(FIRST)
+template <class T> struct wrapper {};
+template <class T>
+struct Invalid {
+  using typename wrapper<T>::T1;
+  using typename wrapper<T>::T2;
+  T1 x;
+};
+template <class T>
+struct Valid {
+  using typename wrapper<T>::T1;
+  using typename wrapper<T>::T2;
+  T1 x;
+  T2 y;
+};
+#elif defined(SECOND)
+template <class T> struct wrapper {};
+template <class T>
+struct Invalid {
+  using typename wrapper<T>::T1;
+  using typename wrapper<T>::T2;
+  T2 x;
+};
+template <class T>
+struct Valid {
+  using typename wrapper<T>::T1;
+  using typename wrapper<T>::T2;
+  T1 x;
+  T2 y;
+};
+#else
+template <class T> using I = Invalid<T>;
+// expected-error@first.h:* {{'Types::UnresolvedUsing::Invalid::x' from module 'FirstModule' is not present in definition of 'Invalid<T>' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'x' does not match}}
+
+template <class T> using V = Valid<T>;
+#endif
+
+}  // namespace UnresolvedUsing
+
+// Vector
+// void invalid1() {
+//  __attribute((vector_size(8))) int *x1;
+//}
+
+}  // namespace Types
+
 // Collection of interesting cases below.
 
 // Naive parsing of AST can lead to cycles in processing.  Ensure
index 724ed95dc1cf4acd8c05d011d298789f0a5de11f..f04fb7eb5e8e03bbfaa58abf0d81282663ec6494 100644 (file)
 @protocol P1
 @end
 
+@protocol P2
+@end
+
 @interface I1
 @end
 
+@interface I2 : I1
+@end
+
 @interface Interface1 <T : I1 *> {
 @public
   T<P1> x;
 }
 @end
+
+@interface Interface2 <T : I1 *>
+@end
+
+@interface Interface3 <T : I1 *>
+@end
+
 #endif
 
 #if defined(FIRST)
@@ -64,6 +77,218 @@ S s;
 // expected-note@first.h:* {{declaration of 'y' does not match}}
 #endif
 
+namespace Types {
+namespace Attributed {
+#if defined(FIRST)
+void invalid1() {
+  static double __attribute((objc_gc(strong))) *x;
+}
+void invalid2() {
+  static int __attribute((objc_gc(strong))) *x;
+}
+void valid() {
+  static int __attribute((objc_gc(strong))) *x;
+}
+#elif defined(SECOND)
+void invalid1() {
+  static int __attribute((objc_gc(strong))) *x;
+}
+void invalid2() {
+  static int __attribute((objc_gc(weak))) *x;
+}
+void valid() {
+  static int __attribute((objc_gc(strong))) *x;
+}
+#else
+auto function1 = invalid1;
+// expected-error@second.h:* {{Types::Attributed::invalid1' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function2 = invalid2;
+// expected-error@second.h:* {{'Types::Attributed::invalid2' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function3 = valid;
+#endif
+}  // namespace Attributed
+
+namespace BlockPointer {
+#if defined(FIRST)
+void invalid1() {
+  void (^x)(int);
+}
+void invalid2() {
+  void (^x)(int);
+}
+void invalid3() {
+  void (^x)(int);
+}
+void invalid4() {
+  void (^x)(int);
+}
+void valid() {
+  void (^x1)(int);
+  int (^x2)(int);
+  void (^x3)(int, int);
+  void (^x4)(short);
+}
+#elif defined(SECOND)
+void invalid1() {
+  void (^x)();
+}
+void invalid2() {
+  void (^x)(int, int);
+}
+void invalid3() {
+  int (^x)(int);
+}
+void invalid4() {
+  void (^x)(float);
+}
+void valid() {
+  void (^x1)(int);
+  int (^x2)(int);
+  void (^x3)(int, int);
+  void (^x4)(short);
+}
+#else
+auto function1 = invalid1;
+// expected-error@second.h:* {{'Types::BlockPointer::invalid1' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function2 = invalid2;
+// expected-error@second.h:* {{'Types::BlockPointer::invalid2' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function3 = invalid3;
+// expected-error@second.h:* {{'Types::BlockPointer::invalid3' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function4 = invalid4;
+// expected-error@second.h:* {{'Types::BlockPointer::invalid4' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note@first.h:* {{but in 'FirstModule' found a different body}}
+auto function5 = valid;
+#endif
+}  // namespace BlockPointer
+
+namespace ObjCObject {
+#if defined(FIRST)
+struct Invalid1 {
+  using T = Interface2<I1*>;
+};
+struct Invalid2 {
+  using T = Interface2<I1*>;
+};
+struct Invalid3 {
+  using T = Interface2<P1, P1>;
+};
+struct Invalid4 {
+  using T = Interface2<P1>;
+};
+struct Valid {
+  using T1 = Interface2<I1*>;
+  using T2 = Interface3<I1*>;
+  using T3 = Interface2<P1>;
+  using T4 = Interface3<P1, P2>;
+  using T5 = __kindof Interface2;
+};
+#elif defined(SECOND)
+struct Invalid1 {
+  using T = Interface3<I1*>;
+};
+struct Invalid2 {
+  using T = Interface2<I2*>;
+};
+struct Invalid3 {
+  using T = Interface2<P1>;
+};
+struct Invalid4 {
+  using T = Interface2<P2>;
+};
+struct Valid {
+  using T1 = Interface2<I1*>;
+  using T2 = Interface3<I1*>;
+  using T3 = Interface2<P1>;
+  using T4 = Interface3<P1, P2>;
+  using T5 = __kindof Interface2;
+};
+#else
+Invalid1 i1;
+// expected-error@first.h:* {{'Types::ObjCObject::Invalid1::T' from module 'FirstModule' is not present in definition of 'Types::ObjCObject::Invalid1' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'T' does not match}}
+Invalid2 i2;
+// expected-error@first.h:* {{'Types::ObjCObject::Invalid2::T' from module 'FirstModule' is not present in definition of 'Types::ObjCObject::Invalid2' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'T' does not match}}
+Invalid3 i3;
+// expected-error@second.h:* {{'Types::ObjCObject::Invalid3' has different definitions in different modules; first difference is definition in module 'SecondModule' found type alias 'T' with underlying type 'Interface2<P1>'}}
+// expected-note@first.h:* {{but in 'FirstModule' found type alias 'T' with different underlying type 'Interface2<P1,P1>'}}
+Invalid4 i4;
+// expected-error@first.h:* {{'Types::ObjCObject::Invalid4::T' from module 'FirstModule' is not present in definition of 'Types::ObjCObject::Invalid4' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'T' does not match}}
+Valid v;
+#endif
+}  // namespace VisitObjCObject
+}  // namespace Types
+
+#if defined(FIRST)
+@interface Interface4 <T : I1 *> {
+@public
+  T<P1> x;
+}
+@end
+@interface Interface5 <T : I1 *> {
+@public
+  T<P1> x;
+}
+@end
+@interface Interface6 <T1 : I1 *, T2 : I2 *> {
+@public
+  T1 x;
+}
+@end
+#elif defined(SECOND)
+@interface Interface4 <T : I1 *> {
+@public
+  T<P2> x;
+}
+@end
+@interface Interface5 <T : I1 *> {
+@public
+  T<P1, P2> x;
+}
+@end
+@interface Interface6 <T1 : I1 *, T2 : I2 *> {
+@public
+  T2 x;
+}
+@end
+#endif
+
+namespace Types {
+namespace ObjCTypeParam {
+#if defined(FIRST) || defined(SECOND)
+struct Invalid1 {
+  Interface4 *I;
+  decltype(I->x) x;
+};
+struct Invalid2 {
+  Interface5 *I;
+  decltype(I->x) x;
+};
+struct Invalid3 {
+  Interface6 *I;
+  decltype(I->x) x;
+};
+#else
+Invalid1 i1;
+// expected-error@first.h:* {{'Types::ObjCTypeParam::Invalid1::x' from module 'FirstModule' is not present in definition of 'Types::ObjCTypeParam::Invalid1' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'x' does not match}}
+Invalid2 i2;
+// expected-error@first.h:* {{'Types::ObjCTypeParam::Invalid2::x' from module 'FirstModule' is not present in definition of 'Types::ObjCTypeParam::Invalid2' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'x' does not match}}
+Invalid3 i3;
+// expected-error@first.h:* {{'Types::ObjCTypeParam::Invalid3::x' from module 'FirstModule' is not present in definition of 'Types::ObjCTypeParam::Invalid3' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'x' does not match}}
+#endif
+
+}  // namespace ObjCTypeParam
+}  // namespace Types
+
 // Keep macros contained to one file.
 #ifdef FIRST
 #undef FIRST