[clang] 6ba4afb - [ODRHash] Hash `ObjCInterfaceDecl` and diagnose discovered mismatches.

Volodymyr Sapsai via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 20 08:18:26 PST 2023


Author: Volodymyr Sapsai
Date: 2023-01-20T10:18:18-06:00
New Revision: 6ba4afb4d6f2f8f293ad704a37de4139c5c8c0f0

URL: https://github.com/llvm/llvm-project/commit/6ba4afb4d6f2f8f293ad704a37de4139c5c8c0f0
DIFF: https://github.com/llvm/llvm-project/commit/6ba4afb4d6f2f8f293ad704a37de4139c5c8c0f0.diff

LOG: [ODRHash] Hash `ObjCInterfaceDecl` and diagnose discovered mismatches.

When two modules contain interfaces with the same name, check the
definitions are equivalent and diagnose if they are not.

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

Added: 
    clang/test/Modules/compare-objc-interface.m

Modified: 
    clang/include/clang/AST/DeclObjC.h
    clang/include/clang/AST/ODRDiagsEmitter.h
    clang/include/clang/AST/ODRHash.h
    clang/include/clang/Basic/DiagnosticASTKinds.td
    clang/include/clang/Serialization/ASTReader.h
    clang/lib/AST/DeclObjC.cpp
    clang/lib/AST/ODRDiagsEmitter.cpp
    clang/lib/AST/ODRHash.cpp
    clang/lib/Serialization/ASTReader.cpp
    clang/lib/Serialization/ASTReaderDecl.cpp
    clang/lib/Serialization/ASTWriter.cpp
    clang/lib/Serialization/ASTWriterDecl.cpp
    clang/test/Modules/interface-diagnose-missing-import.m
    clang/test/Modules/method_pool.m

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h
index 7c72ec9507bf1..77fde99b6b60b 100644
--- a/clang/include/clang/AST/DeclObjC.h
+++ b/clang/include/clang/AST/DeclObjC.h
@@ -1146,6 +1146,7 @@ class ObjCContainerDecl : public NamedDecl, public DeclContext {
 class ObjCInterfaceDecl : public ObjCContainerDecl
                         , public Redeclarable<ObjCInterfaceDecl> {
   friend class ASTContext;
+  friend class ODRDiagsEmitter;
 
   /// TypeForDecl - This indicates the Type object that represents this
   /// TypeDecl.  It is a cache maintained by ASTContext::getObjCInterfaceType
@@ -1203,6 +1204,12 @@ class ObjCInterfaceDecl : public ObjCContainerDecl
     /// One of the \c InheritedDesignatedInitializersState enumeratos.
     mutable unsigned InheritedDesignatedInitializers : 2;
 
+    /// Tracks whether a ODR hash has been computed for this interface.
+    unsigned HasODRHash : 1;
+
+    /// A hash of parts of the class to help in ODR checking.
+    unsigned ODRHash = 0;
+
     /// The location of the last location in this declaration, before
     /// the properties/methods. For example, this will be the '>', '}', or
     /// identifier,
@@ -1211,7 +1218,7 @@ class ObjCInterfaceDecl : public ObjCContainerDecl
     DefinitionData()
         : ExternallyCompleted(false), IvarListMissingImplementation(true),
           HasDesignatedInitializers(false),
-          InheritedDesignatedInitializers(IDI_Unknown) {}
+          InheritedDesignatedInitializers(IDI_Unknown), HasODRHash(false) {}
   };
 
   /// The type parameters associated with this class, if any.
@@ -1892,10 +1899,17 @@ class ObjCInterfaceDecl : public ObjCContainerDecl
   const Type *getTypeForDecl() const { return TypeForDecl; }
   void setTypeForDecl(const Type *TD) const { TypeForDecl = TD; }
 
+  /// Get precomputed ODRHash or add a new one.
+  unsigned getODRHash();
+
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == ObjCInterface; }
 
 private:
+  /// True if a valid hash is stored in ODRHash.
+  bool hasODRHash() const;
+  void setHasODRHash(bool HasHash);
+
   const ObjCInterfaceDecl *findInterfaceWithDesignatedInitializers() const;
   bool inheritsDesignatedInitializers() const;
 };

diff  --git a/clang/include/clang/AST/ODRDiagsEmitter.h b/clang/include/clang/AST/ODRDiagsEmitter.h
index 00a681b78a5eb..fdbd85cb10e5b 100644
--- a/clang/include/clang/AST/ODRDiagsEmitter.h
+++ b/clang/include/clang/AST/ODRDiagsEmitter.h
@@ -51,6 +51,13 @@ class ODRDiagsEmitter {
   bool diagnoseMismatch(const RecordDecl *FirstRecord,
                         const RecordDecl *SecondRecord) const;
 
+  /// Diagnose ODR mismatch between 2 ObjCInterfaceDecl.
+  ///
+  /// Returns true if found a mismatch and diagnosed it.
+  bool diagnoseMismatch(
+      const ObjCInterfaceDecl *FirstID, const ObjCInterfaceDecl *SecondID,
+      const struct ObjCInterfaceDecl::DefinitionData *SecondDD) const;
+
   /// Diagnose ODR mismatch between 2 ObjCProtocolDecl.
   ///
   /// Returns true if found a mismatch and diagnosed it.
@@ -97,6 +104,7 @@ class ODRDiagsEmitter {
     Friend,
     FunctionTemplate,
     ObjCMethod,
+    ObjCIvar,
     ObjCProperty,
     Other
   };

diff  --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h
index a489bb73deb67..cedf644520fc3 100644
--- a/clang/include/clang/AST/ODRHash.h
+++ b/clang/include/clang/AST/ODRHash.h
@@ -59,6 +59,10 @@ class ODRHash {
   // method compares more information than the AddDecl class.
   void AddRecordDecl(const RecordDecl *Record);
 
+  // Use this for ODR checking ObjC interfaces. This
+  // method compares more information than the AddDecl class.
+  void AddObjCInterfaceDecl(const ObjCInterfaceDecl *Record);
+
   // Use this for ODR checking functions between modules.  This method compares
   // more information than the AddDecl class.  SkipBody will process the
   // hash as if the function has no body.

diff  --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index d4303ef834c17..715249b9d6f5e 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -615,6 +615,22 @@ def note_module_odr_violation_definition_data : Note <
   "%ordinal2 base class %3 with "
   "%select{public|protected|private|no}4 access specifier}1">;
 
+def err_module_odr_violation_objc_interface : Error <
+  "%0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{"
+  "%select{no super class|super class with type %5}4|"
+  "instance variable '%4' access control is "
+    "%select{|@private|@protected|@public|@package}5"
+  "}3">;
+def note_module_odr_violation_objc_interface : Note <
+  "but in '%0' found "
+  "%select{"
+  "%select{no super class|super class with type %3}2|"
+  "instance variable '%2' access control is "
+    "%select{|@private|@protected|@public|@package}3"
+  "}1">;
+
 def err_module_odr_violation_template_parameter : Error <
   "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
   "%select{definition in module '%2'|defined here}1 found "
@@ -637,12 +653,14 @@ def err_module_odr_violation_mismatch_decl : Error<
   "%select{definition in module '%2'|defined here}1 found "
   "%select{end of class|public access specifier|private access specifier|"
   "protected access specifier|static assert|field|method|type alias|typedef|"
-  "data member|friend declaration|function template|method|property}3">;
+  "data member|friend declaration|function template|method|instance variable|"
+  "property}3">;
 def note_module_odr_violation_mismatch_decl : Note<
   "but in %select{'%1'|definition here}0 found "
   "%select{end of class|public access specifier|private access specifier|"
   "protected access specifier|static assert|field|method|type alias|typedef|"
-  "data member|friend declaration|function template|method|property}2">;
+  "data member|friend declaration|function template|method|instance variable|"
+  "property}2">;
 
 def err_module_odr_violation_record : Error<
   "%q0 has 
diff erent definitions in 
diff erent modules; first 
diff erence is "
@@ -937,14 +955,14 @@ def err_module_odr_violation_mismatch_decl_unknown : Error<
   "%q0 %select{with definition in module '%2'|defined here}1 has 
diff erent "
   "definitions in 
diff erent modules; first 
diff erence is this "
   "%select{||||static assert|field|method|type alias|typedef|data member|"
-  "friend declaration|function template|method|"
+  "friend declaration|function template|method|instance variable|"
   "property|unexpected decl}3">;
 def note_module_odr_violation_mismatch_decl_unknown : Note<
   "but in %select{'%1'|definition here}0 found "
   "%select{||||
diff erent static assert|
diff erent field|
diff erent method|"
   "
diff erent type alias|
diff erent typedef|
diff erent data member|"
   "
diff erent friend declaration|
diff erent function template|
diff erent method|"
-  "
diff erent property|another unexpected decl}2">;
+  "
diff erent instance variable|
diff erent property|another unexpected decl}2">;
 
 
 def remark_sanitize_address_insert_extra_padding_accepted : Remark<

diff  --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 5eb4834444ab6..5cdbdfe4e38d4 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -1149,6 +1149,9 @@ class ASTReader
 
   using DataPointers =
       std::pair<CXXRecordDecl *, struct CXXRecordDecl::DefinitionData *>;
+  using ObjCInterfaceDataPointers =
+      std::pair<ObjCInterfaceDecl *,
+                struct ObjCInterfaceDecl::DefinitionData *>;
   using ObjCProtocolDataPointers =
       std::pair<ObjCProtocolDecl *, struct ObjCProtocolDecl::DefinitionData *>;
 
@@ -1168,6 +1171,11 @@ class ASTReader
   llvm::SmallDenseMap<EnumDecl *, llvm::SmallVector<EnumDecl *, 2>, 2>
       PendingEnumOdrMergeFailures;
 
+  /// ObjCInterfaceDecl in which we found an ODR violation.
+  llvm::SmallDenseMap<ObjCInterfaceDecl *,
+                      llvm::SmallVector<ObjCInterfaceDataPointers, 2>, 2>
+      PendingObjCInterfaceOdrMergeFailures;
+
   /// ObjCProtocolDecl in which we found an ODR violation.
   llvm::SmallDenseMap<ObjCProtocolDecl *,
                       llvm::SmallVector<ObjCProtocolDataPointers, 2>, 2>

diff  --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp
index 6b21f7890b782..33b9d33075a2a 100644
--- a/clang/lib/AST/DeclObjC.cpp
+++ b/clang/lib/AST/DeclObjC.cpp
@@ -778,6 +778,33 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod(
   return Method;
 }
 
+unsigned ObjCInterfaceDecl::getODRHash() {
+  assert(hasDefinition() && "ODRHash only for records with definitions");
+
+  // Previously calculated hash is stored in DefinitionData.
+  if (hasODRHash())
+    return data().ODRHash;
+
+  // Only calculate hash on first call of getODRHash per record.
+  ODRHash Hasher;
+  Hasher.AddObjCInterfaceDecl(getDefinition());
+  data().ODRHash = Hasher.CalculateHash();
+  setHasODRHash(true);
+
+  return data().ODRHash;
+}
+
+bool ObjCInterfaceDecl::hasODRHash() const {
+  if (!hasDefinition())
+    return false;
+  return data().HasODRHash;
+}
+
+void ObjCInterfaceDecl::setHasODRHash(bool HasHash) {
+  assert(hasDefinition() && "Cannot set ODRHash without definition");
+  data().HasODRHash = HasHash;
+}
+
 //===----------------------------------------------------------------------===//
 // ObjCMethodDecl
 //===----------------------------------------------------------------------===//

diff  --git a/clang/lib/AST/ODRDiagsEmitter.cpp b/clang/lib/AST/ODRDiagsEmitter.cpp
index b5888fc47a270..bd23fda6ac921 100644
--- a/clang/lib/AST/ODRDiagsEmitter.cpp
+++ b/clang/lib/AST/ODRDiagsEmitter.cpp
@@ -615,6 +615,8 @@ ODRDiagsEmitter::FindTypeDiffs(DeclHashes &FirstHashes,
       return FunctionTemplate;
     case Decl::ObjCMethod:
       return ObjCMethod;
+    case Decl::ObjCIvar:
+      return ObjCIvar;
     case Decl::ObjCProperty:
       return ObjCProperty;
     }
@@ -675,6 +677,8 @@ void ODRDiagsEmitter::diagnoseSubMismatchDifferentDeclKinds(
     if (DiffType == EndOfClass) {
       if (auto *Tag = dyn_cast<TagDecl>(Container))
         Loc = Tag->getBraceRange().getEnd();
+      else if (auto *IF = dyn_cast<ObjCInterfaceDecl>(Container))
+        Loc = IF->getAtEndRange().getBegin();
       else
         Loc = Container->getEndLoc();
     } else {
@@ -970,6 +974,7 @@ bool ODRDiagsEmitter::diagnoseMismatch(
   case PrivateSpecifer:
   case ProtectedSpecifer:
   case ObjCMethod:
+  case ObjCIvar:
   case ObjCProperty:
     llvm_unreachable("Invalid 
diff  type");
 
@@ -1604,6 +1609,7 @@ bool ODRDiagsEmitter::diagnoseMismatch(const RecordDecl *FirstRecord,
   case FunctionTemplate:
   // Cannot be contained by RecordDecl, invalid in this context.
   case ObjCMethod:
+  case ObjCIvar:
   case ObjCProperty:
     llvm_unreachable("Invalid 
diff  type");
 
@@ -1908,6 +1914,189 @@ bool ODRDiagsEmitter::diagnoseMismatch(const EnumDecl *FirstEnum,
   return false;
 }
 
+bool ODRDiagsEmitter::diagnoseMismatch(
+    const ObjCInterfaceDecl *FirstID, const ObjCInterfaceDecl *SecondID,
+    const struct ObjCInterfaceDecl::DefinitionData *SecondDD) const {
+  // Multiple 
diff erent declarations got merged together; tell the user
+  // where they came from.
+  if (FirstID == SecondID)
+    return false;
+
+  std::string FirstModule = getOwningModuleNameForDiagnostic(FirstID);
+  std::string SecondModule = getOwningModuleNameForDiagnostic(SecondID);
+
+  // Keep in sync with err_module_odr_violation_objc_interface.
+  enum ODRInterfaceDifference {
+    SuperClassType,
+    IVarAccess,
+  };
+
+  auto DiagError = [FirstID, &FirstModule,
+                    this](SourceLocation Loc, SourceRange Range,
+                          ODRInterfaceDifference DiffType) {
+    return Diag(Loc, diag::err_module_odr_violation_objc_interface)
+           << FirstID << FirstModule.empty() << FirstModule << Range
+           << DiffType;
+  };
+  auto DiagNote = [&SecondModule, this](SourceLocation Loc, SourceRange Range,
+                                        ODRInterfaceDifference DiffType) {
+    return Diag(Loc, diag::note_module_odr_violation_objc_interface)
+           << SecondModule << Range << DiffType;
+  };
+
+  const struct ObjCInterfaceDecl::DefinitionData *FirstDD = &FirstID->data();
+  assert(FirstDD && SecondDD && "Definitions without DefinitionData");
+  if (FirstDD != SecondDD) {
+    // Check for matching super class.
+    auto GetSuperClassSourceRange = [](const TypeSourceInfo *SuperInfo,
+                                       const ObjCInterfaceDecl *ID) {
+      if (!SuperInfo)
+        return ID->getSourceRange();
+      TypeLoc Loc = SuperInfo->getTypeLoc();
+      return SourceRange(Loc.getBeginLoc(), Loc.getEndLoc());
+    };
+
+    ObjCInterfaceDecl *FirstSuperClass = FirstID->getSuperClass();
+    ObjCInterfaceDecl *SecondSuperClass = nullptr;
+    const TypeSourceInfo *FirstSuperInfo = FirstID->getSuperClassTInfo();
+    const TypeSourceInfo *SecondSuperInfo = SecondDD->SuperClassTInfo;
+    if (SecondSuperInfo)
+      SecondSuperClass =
+          SecondSuperInfo->getType()->castAs<ObjCObjectType>()->getInterface();
+
+    if ((FirstSuperClass && SecondSuperClass &&
+         FirstSuperClass->getODRHash() != SecondSuperClass->getODRHash()) ||
+        (FirstSuperClass && !SecondSuperClass) ||
+        (!FirstSuperClass && SecondSuperClass)) {
+      QualType FirstType;
+      if (FirstSuperInfo)
+        FirstType = FirstSuperInfo->getType();
+
+      DiagError(FirstID->getLocation(),
+                GetSuperClassSourceRange(FirstSuperInfo, FirstID),
+                SuperClassType)
+          << (bool)FirstSuperInfo << FirstType;
+
+      QualType SecondType;
+      if (SecondSuperInfo)
+        SecondType = SecondSuperInfo->getType();
+
+      DiagNote(SecondID->getLocation(),
+               GetSuperClassSourceRange(SecondSuperInfo, SecondID),
+               SuperClassType)
+          << (bool)SecondSuperInfo << SecondType;
+      return true;
+    }
+
+    // Check both interfaces reference the same protocols.
+    auto &FirstProtos = FirstID->getReferencedProtocols();
+    auto &SecondProtos = SecondDD->ReferencedProtocols;
+    if (diagnoseSubMismatchProtocols(FirstProtos, FirstID, FirstModule,
+                                     SecondProtos, SecondID, SecondModule))
+      return true;
+  }
+
+  auto PopulateHashes = [](DeclHashes &Hashes, const ObjCInterfaceDecl *ID,
+                           const DeclContext *DC) {
+    for (auto *D : ID->decls()) {
+      if (!ODRHash::isSubDeclToBeProcessed(D, DC))
+        continue;
+      Hashes.emplace_back(D, computeODRHash(D));
+    }
+  };
+
+  DeclHashes FirstHashes;
+  DeclHashes SecondHashes;
+  PopulateHashes(FirstHashes, FirstID, FirstID);
+  PopulateHashes(SecondHashes, SecondID, FirstID);
+
+  DiffResult DR = FindTypeDiffs(FirstHashes, SecondHashes);
+  ODRMismatchDecl FirstDiffType = DR.FirstDiffType;
+  ODRMismatchDecl SecondDiffType = DR.SecondDiffType;
+  const Decl *FirstDecl = DR.FirstDecl;
+  const Decl *SecondDecl = DR.SecondDecl;
+
+  if (FirstDiffType == Other || SecondDiffType == Other) {
+    diagnoseSubMismatchUnexpected(DR, FirstID, FirstModule, SecondID,
+                                  SecondModule);
+    return true;
+  }
+
+  if (FirstDiffType != SecondDiffType) {
+    diagnoseSubMismatchDifferentDeclKinds(DR, FirstID, FirstModule, SecondID,
+                                          SecondModule);
+    return true;
+  }
+
+  assert(FirstDiffType == SecondDiffType);
+  switch (FirstDiffType) {
+  // Already handled.
+  case EndOfClass:
+  case Other:
+  // Cannot be contained by ObjCInterfaceDecl, invalid in this context.
+  case Field:
+  case TypeDef:
+  case Var:
+  // C++ only, invalid in this context.
+  case PublicSpecifer:
+  case PrivateSpecifer:
+  case ProtectedSpecifer:
+  case StaticAssert:
+  case CXXMethod:
+  case TypeAlias:
+  case Friend:
+  case FunctionTemplate:
+    llvm_unreachable("Invalid 
diff  type");
+
+  case ObjCMethod: {
+    if (diagnoseSubMismatchObjCMethod(FirstID, FirstModule, SecondModule,
+                                      cast<ObjCMethodDecl>(FirstDecl),
+                                      cast<ObjCMethodDecl>(SecondDecl)))
+      return true;
+    break;
+  }
+  case ObjCIvar: {
+    if (diagnoseSubMismatchField(FirstID, FirstModule, SecondModule,
+                                 cast<FieldDecl>(FirstDecl),
+                                 cast<FieldDecl>(SecondDecl)))
+      return true;
+
+    // Check if the access match.
+    const ObjCIvarDecl *FirstIvar = cast<ObjCIvarDecl>(FirstDecl);
+    const ObjCIvarDecl *SecondIvar = cast<ObjCIvarDecl>(SecondDecl);
+    if (FirstIvar->getCanonicalAccessControl() !=
+        SecondIvar->getCanonicalAccessControl()) {
+      DiagError(FirstIvar->getLocation(), FirstIvar->getSourceRange(),
+                IVarAccess)
+          << FirstIvar->getName()
+          << (int)FirstIvar->getCanonicalAccessControl();
+      DiagNote(SecondIvar->getLocation(), SecondIvar->getSourceRange(),
+               IVarAccess)
+          << SecondIvar->getName()
+          << (int)SecondIvar->getCanonicalAccessControl();
+      return true;
+    }
+    break;
+  }
+  case ObjCProperty: {
+    if (diagnoseSubMismatchObjCProperty(FirstID, FirstModule, SecondModule,
+                                        cast<ObjCPropertyDecl>(FirstDecl),
+                                        cast<ObjCPropertyDecl>(SecondDecl)))
+      return true;
+    break;
+  }
+  }
+
+  Diag(FirstDecl->getLocation(),
+       diag::err_module_odr_violation_mismatch_decl_unknown)
+      << FirstID << FirstModule.empty() << FirstModule << FirstDiffType
+      << FirstDecl->getSourceRange();
+  Diag(SecondDecl->getLocation(),
+       diag::note_module_odr_violation_mismatch_decl_unknown)
+      << SecondModule << FirstDiffType << SecondDecl->getSourceRange();
+  return true;
+}
+
 bool ODRDiagsEmitter::diagnoseMismatch(
     const ObjCProtocolDecl *FirstProtocol,
     const ObjCProtocolDecl *SecondProtocol,
@@ -1975,6 +2164,7 @@ bool ODRDiagsEmitter::diagnoseMismatch(
   case Field:
   case TypeDef:
   case Var:
+  case ObjCIvar:
   // C++ only, invalid in this context.
   case PublicSpecifer:
   case PrivateSpecifer:

diff  --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 6912f67daa8f8..3374b49f5d8e2 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -343,6 +343,11 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
     Inherited::VisitFieldDecl(D);
   }
 
+  void VisitObjCIvarDecl(const ObjCIvarDecl *D) {
+    ID.AddInteger(D->getCanonicalAccessControl());
+    Inherited::VisitObjCIvarDecl(D);
+  }
+
   void VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
     ID.AddInteger(D->getPropertyAttributes());
     ID.AddInteger(D->getPropertyImplementation());
@@ -537,6 +542,7 @@ bool ODRHash::isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent) {
     case Decl::Typedef:
     case Decl::Var:
     case Decl::ObjCMethod:
+    case Decl::ObjCIvar:
     case Decl::ObjCProperty:
       return true;
   }
@@ -613,6 +619,33 @@ void ODRHash::AddRecordDecl(const RecordDecl *Record) {
     AddSubDecl(SubDecl);
 }
 
+void ODRHash::AddObjCInterfaceDecl(const ObjCInterfaceDecl *IF) {
+  AddDecl(IF);
+
+  auto *SuperClass = IF->getSuperClass();
+  AddBoolean(SuperClass);
+  if (SuperClass)
+    ID.AddInteger(SuperClass->getODRHash());
+
+  // Hash referenced protocols.
+  ID.AddInteger(IF->getReferencedProtocols().size());
+  for (const ObjCProtocolDecl *RefP : IF->protocols()) {
+    // Hash the name only as a referenced protocol can be a forward declaration.
+    AddDeclarationName(RefP->getDeclName());
+  }
+
+  // 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 : IF->decls())
+    if (isSubDeclToBeProcessed(SubDecl, IF))
+      Decls.push_back(SubDecl);
+
+  ID.AddInteger(Decls.size());
+  for (auto *SubDecl : Decls)
+    AddSubDecl(SubDecl);
+}
+
 void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
                               bool SkipBody) {
   assert(Function && "Expecting non-null pointer.");

diff  --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 753d4166f5306..72a4be8ab6af3 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -6857,6 +6857,7 @@ void TypeLocReader::VisitPackExpansionTypeLoc(PackExpansionTypeLoc TL) {
 
 void TypeLocReader::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) {
   TL.setNameLoc(readSourceLocation());
+  TL.setNameEndLoc(readSourceLocation());
 }
 
 void TypeLocReader::VisitObjCTypeParamTypeLoc(ObjCTypeParamTypeLoc TL) {
@@ -9507,6 +9508,7 @@ void ASTReader::diagnoseOdrViolations() {
       PendingRecordOdrMergeFailures.empty() &&
       PendingFunctionOdrMergeFailures.empty() &&
       PendingEnumOdrMergeFailures.empty() &&
+      PendingObjCInterfaceOdrMergeFailures.empty() &&
       PendingObjCProtocolOdrMergeFailures.empty())
     return;
 
@@ -9538,6 +9540,16 @@ void ASTReader::diagnoseOdrViolations() {
       D->decls_begin();
   }
 
+  // Trigger the import of the full interface definition.
+  auto ObjCInterfaceOdrMergeFailures =
+      std::move(PendingObjCInterfaceOdrMergeFailures);
+  PendingObjCInterfaceOdrMergeFailures.clear();
+  for (auto &Merge : ObjCInterfaceOdrMergeFailures) {
+    Merge.first->decls_begin();
+    for (auto &InterfacePair : Merge.second)
+      InterfacePair.first->decls_begin();
+  }
+
   // Trigger the import of functions.
   auto FunctionOdrMergeFailures = std::move(PendingFunctionOdrMergeFailures);
   PendingFunctionOdrMergeFailures.clear();
@@ -9657,6 +9669,7 @@ void ASTReader::diagnoseOdrViolations() {
 
   if (OdrMergeFailures.empty() && RecordOdrMergeFailures.empty() &&
       FunctionOdrMergeFailures.empty() && EnumOdrMergeFailures.empty() &&
+      ObjCInterfaceOdrMergeFailures.empty() &&
       ObjCProtocolOdrMergeFailures.empty())
     return;
 
@@ -9749,6 +9762,25 @@ void ASTReader::diagnoseOdrViolations() {
     assert(Diagnosed && "Unable to emit ODR diagnostic.");
   }
 
+  for (auto &Merge : ObjCInterfaceOdrMergeFailures) {
+    // If we've already pointed out a specific problem with this interface,
+    // don't bother issuing a general "something's 
diff erent" diagnostic.
+    if (!DiagnosedOdrMergeFailures.insert(Merge.first).second)
+      continue;
+
+    bool Diagnosed = false;
+    ObjCInterfaceDecl *FirstID = Merge.first;
+    for (auto &InterfacePair : Merge.second) {
+      if (DiagsEmitter.diagnoseMismatch(FirstID, InterfacePair.first,
+                                        InterfacePair.second)) {
+        Diagnosed = true;
+        break;
+      }
+    }
+    (void)Diagnosed;
+    assert(Diagnosed && "Unable to emit ODR diagnostic.");
+  }
+
   for (auto &Merge : ObjCProtocolOdrMergeFailures) {
     // If we've already pointed out a specific problem with this protocol,
     // don't bother issuing a general "something's 
diff erent" diagnostic.

diff  --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 6c7198d7935a1..8cb513eff13e0 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1207,6 +1207,8 @@ void ASTDeclReader::ReadObjCDefinitionData(
 
   Data.EndLoc = readSourceLocation();
   Data.HasDesignatedInitializers = Record.readInt();
+  Data.ODRHash = Record.readInt();
+  Data.HasODRHash = true;
 
   // Read the directly referenced protocols and their SourceLocations.
   unsigned NumProtocols = Record.readInt();
@@ -1234,13 +1236,16 @@ void ASTDeclReader::ReadObjCDefinitionData(
 void ASTDeclReader::MergeDefinitionData(ObjCInterfaceDecl *D,
          struct ObjCInterfaceDecl::DefinitionData &&NewDD) {
   struct ObjCInterfaceDecl::DefinitionData &DD = D->data();
-  if (DD.Definition != NewDD.Definition) {
-    Reader.MergedDeclContexts.insert(
-        std::make_pair(NewDD.Definition, DD.Definition));
-    Reader.mergeDefinitionVisibility(DD.Definition, NewDD.Definition);
-  }
+  if (DD.Definition == NewDD.Definition)
+    return;
 
-  // FIXME: odr checking?
+  Reader.MergedDeclContexts.insert(
+      std::make_pair(NewDD.Definition, DD.Definition));
+  Reader.mergeDefinitionVisibility(DD.Definition, NewDD.Definition);
+
+  if (D->getODRHash() != NewDD.ODRHash)
+    Reader.PendingObjCInterfaceOdrMergeFailures[DD.Definition].push_back(
+        {NewDD.Definition, &NewDD});
 }
 
 void ASTDeclReader::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID) {

diff  --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 43a74cdb5a5b7..7fd52c267592f 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -569,6 +569,7 @@ void TypeLocWriter::VisitPackExpansionTypeLoc(PackExpansionTypeLoc TL) {
 
 void TypeLocWriter::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) {
   addSourceLocation(TL.getNameLoc());
+  addSourceLocation(TL.getNameEndLoc());
 }
 
 void TypeLocWriter::VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) {

diff  --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index f7daa5076d2bb..a58e0d796b31e 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -775,6 +775,7 @@ void ASTDeclWriter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
     Record.AddTypeSourceInfo(D->getSuperClassTInfo());
     Record.AddSourceLocation(D->getEndOfDefinitionLoc());
     Record.push_back(Data.HasDesignatedInitializers);
+    Record.push_back(D->getODRHash());
 
     // Write out the protocols that are directly referenced by the @interface.
     Record.push_back(Data.ReferencedProtocols.size());

diff  --git a/clang/test/Modules/compare-objc-interface.m b/clang/test/Modules/compare-objc-interface.m
new file mode 100644
index 0000000000000..71f1cc853a7a3
--- /dev/null
+++ b/clang/test/Modules/compare-objc-interface.m
@@ -0,0 +1,424 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// Build first header file
+// RUN: echo "#define FIRST" >> %t/include/first.h
+// RUN: cat %t/test.m        >> %t/include/first.h
+// RUN: echo "#undef FIRST"  >> %t/include/first.h
+
+// Build second header file
+// RUN: echo "#define SECOND" >> %t/include/second.h
+// RUN: cat %t/test.m         >> %t/include/second.h
+// RUN: echo "#undef SECOND"  >> %t/include/second.h
+
+// Test that each header can compile
+// RUN: %clang_cc1 -fsyntax-only -x objective-c %t/include/first.h -fblocks -fobjc-arc
+// RUN: %clang_cc1 -fsyntax-only -x objective-c %t/include/second.h -fblocks -fobjc-arc
+
+// Run test
+// RUN: %clang_cc1 -I%t/include -verify %t/test.m -fblocks -fobjc-arc \
+// RUN:            -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache
+
+// Test that we don't accept 
diff erent class definitions with the same name
+// from multiple modules but detect mismatches and provide actionable
+// diagnostic.
+
+//--- include/common.h
+#ifndef COMMON_H
+#define COMMON_H
+ at interface NSObject @end
+ at protocol CommonProtocol @end
+ at protocol ExtraProtocol @end
+#endif
+
+//--- include/first-empty.h
+//--- include/module.modulemap
+module First {
+  module Empty {
+    header "first-empty.h"
+  }
+  module Hidden {
+    header "first.h"
+    export *
+  }
+}
+module Second {
+  header "second.h"
+  export *
+}
+
+//--- test.m
+#if defined(FIRST) || defined(SECOND)
+# include "common.h"
+#endif
+
+#if !defined(FIRST) && !defined(SECOND)
+# include "first-empty.h"
+# include "second.h"
+#endif
+
+#if defined(FIRST)
+ at class CompareForwardDeclaration1;
+ at interface CompareForwardDeclaration2: NSObject @end
+#elif defined(SECOND)
+ at interface CompareForwardDeclaration1: NSObject @end
+ at class CompareForwardDeclaration2;
+#else
+CompareForwardDeclaration1 *compareForwardDeclaration1;
+CompareForwardDeclaration2 *compareForwardDeclaration2;
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingSuperclass: NSObject @end
+
+ at interface CompareSuperclassPresence1: NSObject @end
+ at interface CompareSuperclassPresence2 @end
+
+ at interface CompareDifferentSuperclass: NSObject @end
+#elif defined(SECOND)
+ at interface CompareMatchingSuperclass: NSObject @end
+
+ at interface CompareSuperclassPresence1 @end
+ at interface CompareSuperclassPresence2: NSObject @end
+
+ at interface DifferentSuperclass: NSObject @end
+ at interface CompareDifferentSuperclass: DifferentSuperclass @end
+#else
+CompareMatchingSuperclass *compareMatchingSuperclass;
+CompareSuperclassPresence1 *compareSuperclassPresence1;
+// expected-error at first.h:* {{'CompareSuperclassPresence1' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found super class with type 'NSObject'}}
+// expected-note at second.h:* {{but in 'Second' found no super class}}
+CompareSuperclassPresence2 *compareSuperclassPresence2;
+// expected-error at first.h:* {{'CompareSuperclassPresence2' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found no super class}}
+// expected-note at second.h:* {{but in 'Second' found super class with type 'NSObject'}}
+CompareDifferentSuperclass *compareDifferentSuperclass;
+// expected-error at first.h:* {{'CompareDifferentSuperclass' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found super class with type 'NSObject'}}
+// expected-note at second.h:* {{but in 'Second' found super class with type 'DifferentSuperclass'}}
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingConformingProtocols: NSObject<CommonProtocol> @end
+ at protocol ForwardProtocol;
+ at interface CompareMatchingConformingForwardProtocols: NSObject<ForwardProtocol> @end
+
+ at interface CompareProtocolPresence1: NSObject<CommonProtocol> @end
+ at interface CompareProtocolPresence2: NSObject @end
+
+ at interface CompareDifferentProtocols: NSObject<CommonProtocol> @end
+ at interface CompareProtocolOrder: NSObject<CommonProtocol, ExtraProtocol> @end
+#elif defined(SECOND)
+ at interface CompareMatchingConformingProtocols: NSObject<CommonProtocol> @end
+ at protocol ForwardProtocol @end
+ at interface CompareMatchingConformingForwardProtocols: NSObject<ForwardProtocol> @end
+
+ at interface CompareProtocolPresence1: NSObject @end
+ at interface CompareProtocolPresence2: NSObject<CommonProtocol> @end
+
+ at interface CompareDifferentProtocols: NSObject<ExtraProtocol> @end
+ at interface CompareProtocolOrder: NSObject<ExtraProtocol, CommonProtocol> @end
+#else
+CompareMatchingConformingProtocols *compareMatchingConformingProtocols;
+CompareMatchingConformingForwardProtocols *compareMatchingConformingForwardProtocols;
+
+CompareProtocolPresence1 *compareProtocolPresence1;
+// expected-error at first.h:* {{'CompareProtocolPresence1' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found 1 referenced protocol}}
+// expected-note at second.h:* {{but in 'Second' found 0 referenced protocols}}
+CompareProtocolPresence2 *compareProtocolPresence2;
+// expected-error at first.h:* {{'CompareProtocolPresence2' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found 0 referenced protocols}}
+// expected-note at second.h:* {{but in 'Second' found 1 referenced protocol}}
+
+CompareDifferentProtocols *compareDifferentProtocols;
+// expected-error at first.h:* {{'CompareDifferentProtocols' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found 1st referenced protocol with name 'CommonProtocol'}}
+// expected-note at second.h:* {{but in 'Second' found 1st referenced protocol with 
diff erent name 'ExtraProtocol'}}
+CompareProtocolOrder *compareProtocolOrder;
+// expected-error at first.h:* {{'CompareProtocolOrder' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found 1st referenced protocol with name 'CommonProtocol'}}
+// expected-note at second.h:* {{but in 'Second' found 1st referenced protocol with 
diff erent name 'ExtraProtocol'}}
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingIVars: NSObject { int ivarName; } @end
+
+ at interface CompareIVarPresence1: NSObject @end
+ at interface CompareIVarPresence2: NSObject { int ivarPresence2; } @end
+
+ at interface CompareIVarName: NSObject { int ivarName; } @end
+ at interface CompareIVarType: NSObject { int ivarType; } @end
+ at interface CompareIVarOrder: NSObject {
+  int ivarNameInt;
+  float ivarNameFloat;
+}
+ at end
+
+ at interface CompareIVarVisibilityExplicit: NSObject {
+ at public
+  int ivarVisibility;
+}
+ at end
+ at interface CompareIVarVisibilityDefault: NSObject {
+  int ivarVisibilityDefault;
+}
+ at end
+#elif defined(SECOND)
+ at interface CompareMatchingIVars: NSObject { int ivarName; } @end
+
+ at interface CompareIVarPresence1: NSObject { int ivarPresence1; } @end
+ at interface CompareIVarPresence2: NSObject @end
+
+ at interface CompareIVarName: NSObject { int 
diff erentIvarName; } @end
+ at interface CompareIVarType: NSObject { float ivarType; } @end
+ at interface CompareIVarOrder: NSObject {
+  float ivarNameFloat;
+  int ivarNameInt;
+}
+ at end
+
+ at interface CompareIVarVisibilityExplicit: NSObject {
+ at private
+  int ivarVisibility;
+}
+ at end
+ at interface CompareIVarVisibilityDefault: NSObject {
+ at public
+  int ivarVisibilityDefault;
+}
+ at end
+#else
+CompareMatchingIVars *compareMatchingIVars;
+
+CompareIVarPresence1 *compareIVarPresence1;
+// expected-error at second.h:* {{'CompareIVarPresence1::ivarPresence1' from module 'Second' is not present in definition of 'CompareIVarPresence1' in module 'First.Hidden'}}
+// expected-note at first.h:* {{definition has no member 'ivarPresence1'}}
+CompareIVarPresence2 *compareIVarPresence2;
+// expected-error at first.h:* {{'CompareIVarPresence2' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found instance variable}}
+// expected-note at second.h:* {{but in 'Second' found end of class}}
+
+CompareIVarName *compareIVarName;
+// expected-error at second.h:* {{'CompareIVarName::
diff erentIvarName' from module 'Second' is not present in definition of 'CompareIVarName' in module 'First.Hidden'}}
+// expected-note at first.h:* {{definition has no member '
diff erentIvarName'}}
+CompareIVarType *compareIVarType;
+// expected-error at second.h:* {{'CompareIVarType::ivarType' from module 'Second' is not present in definition of 'CompareIVarType' in module 'First.Hidden'}}
+// expected-note at first.h:* {{declaration of 'ivarType' does not match}}
+CompareIVarOrder *compareIVarOrder;
+// expected-error at first.h:* {{'CompareIVarOrder' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found field 'ivarNameInt'}}
+// expected-note at second.h:* {{but in 'Second' found field 'ivarNameFloat'}}
+
+CompareIVarVisibilityExplicit *compareIVarVisibilityExplicit;
+// expected-error at first.h:* {{'CompareIVarVisibilityExplicit' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found instance variable 'ivarVisibility' access control is @public}}
+// expected-note at second.h:* {{but in 'Second' found instance variable 'ivarVisibility' access control is @private}}
+CompareIVarVisibilityDefault *compareIVarVisibilityDefault;
+// expected-error at first.h:* {{'CompareIVarVisibilityDefault' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found instance variable 'ivarVisibilityDefault' access control is @protected}}
+// expected-note at second.h:* {{but in 'Second' found instance variable 'ivarVisibilityDefault' access control is @public}}
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingMethods: NSObject
+- (float)matchingMethod:(int)arg;
+ at end
+
+ at interface CompareMethodPresence1: NSObject
+- (void)presenceMethod1;
+ at end
+ at interface CompareMethodPresence2: NSObject
+ at end
+
+ at interface CompareMethodName: NSObject
+- (void)methodNameA;
+ at end
+
+ at interface CompareMethodArgCount: NSObject
+- (void)methodArgCount:(int)arg0 :(int)arg1;
+ at end
+ at interface CompareMethodArgName: NSObject
+- (void)methodArgName:(int)argNameA;
+ at end
+ at interface CompareMethodArgType: NSObject
+- (void)methodArgType:(int)argType;
+ at end
+
+ at interface CompareMethodReturnType: NSObject
+- (int)methodReturnType;
+ at end
+
+ at interface CompareMethodOrder: NSObject
+- (void)methodOrderFirst;
+- (void)methodOrderSecond;
+ at end
+
+ at interface CompareMethodClassInstance: NSObject
++ (void)methodClassInstance;
+ at end
+#elif defined(SECOND)
+ at interface CompareMatchingMethods: NSObject
+- (float)matchingMethod:(int)arg;
+ at end
+
+ at interface CompareMethodPresence1: NSObject
+ at end
+ at interface CompareMethodPresence2: NSObject
+- (void)presenceMethod2;
+ at end
+
+ at interface CompareMethodName: NSObject
+- (void)methodNameB;
+ at end
+
+ at interface CompareMethodArgCount: NSObject
+- (void)methodArgCount:(int)arg0;
+ at end
+ at interface CompareMethodArgName: NSObject
+- (void)methodArgName:(int)argNameB;
+ at end
+ at interface CompareMethodArgType: NSObject
+- (void)methodArgType:(float)argType;
+ at end
+
+ at interface CompareMethodReturnType: NSObject
+- (float)methodReturnType;
+ at end
+
+ at interface CompareMethodOrder: NSObject
+- (void)methodOrderSecond;
+- (void)methodOrderFirst;
+ at end
+
+ at interface CompareMethodClassInstance: NSObject
+- (void)methodClassInstance;
+ at end
+#else
+CompareMatchingMethods *compareMatchingMethods;
+CompareMethodPresence1 *compareMethodPresence1;
+// expected-error at first.h:* {{'CompareMethodPresence1' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found method}}
+// expected-note at second.h:* {{but in 'Second' found end of class}}
+CompareMethodPresence2 *compareMethodPresence2;
+// expected-error at first.h:* {{'CompareMethodPresence2' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found end of class}}
+// expected-note at second.h:* {{but in 'Second' found method}}
+CompareMethodName *compareMethodName;
+// expected-error at first.h:* {{'CompareMethodName' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found method 'methodNameA'}}
+// expected-note at second.h:* {{but in 'Second' found 
diff erent method 'methodNameB'}}
+
+CompareMethodArgCount *compareMethodArgCount;
+// expected-error at first.h:* {{'CompareMethodArgCount' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found method 'methodArgCount::' that has 2 parameters}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodArgCount:' that has 1 parameter}}
+CompareMethodArgName *compareMethodArgName;
+// expected-error at first.h:* {{'CompareMethodArgName' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found method 'methodArgName:' with 1st parameter named 'argNameA'}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodArgName:' with 1st parameter named 'argNameB'}}
+CompareMethodArgType *compareMethodArgType;
+// expected-error at first.h:* {{'CompareMethodArgType' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found method 'methodArgType:' with 1st parameter of type 'int'}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodArgType:' with 1st parameter of type 'float'}}
+
+CompareMethodReturnType *compareMethodReturnType;
+// expected-error at first.h:* {{'CompareMethodReturnType' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found method 'methodReturnType' with return type 'int'}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodReturnType' with 
diff erent return type 'float'}}
+
+CompareMethodOrder *compareMethodOrder;
+// expected-error at first.h:* {{'CompareMethodOrder' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found method 'methodOrderFirst'}}
+// expected-note at second.h:* {{but in 'Second' found 
diff erent method 'methodOrderSecond'}}
+CompareMethodClassInstance *compareMethodClassInstance;
+// expected-error at first.h:* {{'CompareMethodClassInstance' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found class method 'methodClassInstance'}}
+// expected-note at second.h:* {{but in 'Second' found method 'methodClassInstance' as instance method}}
+#endif
+
+#if defined(FIRST)
+ at interface CompareMatchingProperties: NSObject
+ at property int matchingPropName;
+ at end
+
+ at interface ComparePropertyPresence1: NSObject
+ at property int propPresence1;
+ at end
+ at interface ComparePropertyPresence2: NSObject
+ at end
+
+ at interface ComparePropertyName: NSObject
+ at property int propNameA;
+ at end
+
+ at interface ComparePropertyType: NSObject
+ at property int propType;
+ at end
+
+ at interface ComparePropertyOrder: NSObject
+ at property int propOrderX;
+ at property int propOrderY;
+ at end
+
+ at interface CompareMatchingPropertyAttributes: NSObject
+ at property (nonatomic, assign) int matchingProp;
+ at end
+ at interface ComparePropertyAttributes: NSObject
+ at property (readonly) int propAttributes;
+ at end
+// Edge cases.
+ at interface CompareFirstImplAttribute: NSObject
+ at property int firstImplAttribute;
+ at end
+ at interface CompareLastImplAttribute: NSObject
+ at property (direct) int lastImplAttribute;
+ at end
+#elif defined(SECOND)
+ at interface CompareMatchingProperties: NSObject
+ at property int matchingPropName;
+ at end
+
+ at interface ComparePropertyPresence1: NSObject
+ at end
+ at interface ComparePropertyPresence2: NSObject
+ at property int propPresence2;
+ at end
+
+ at interface ComparePropertyName: NSObject
+ at property int propNameB;
+ at end
+
+ at interface ComparePropertyType: NSObject
+ at property float propType;
+ at end
+
+ at interface ComparePropertyOrder: NSObject
+ at property int propOrderY;
+ at property int propOrderX;
+ at end
+
+ at interface CompareMatchingPropertyAttributes: NSObject
+ at property (assign, nonatomic) int matchingProp;
+ at end
+ at interface ComparePropertyAttributes: NSObject
+ at property (readwrite) int propAttributes;
+ at end
+// Edge cases.
+ at interface CompareFirstImplAttribute: NSObject
+ at property (readonly) int firstImplAttribute;
+ at end
+ at interface CompareLastImplAttribute: NSObject
+ at property int lastImplAttribute;
+ at end
+#else
+CompareMatchingProperties *compareMatchingProperties;
+ComparePropertyPresence1 *comparePropertyPresence1;
+// expected-error at first.h:* {{'ComparePropertyPresence1' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found property}}
+// expected-note at second.h:* {{but in 'Second' found end of class}}
+ComparePropertyPresence2 *comparePropertyPresence2;
+// expected-error at first.h:* {{'ComparePropertyPresence2' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found end of class}}
+// expected-note at second.h:* {{but in 'Second' found property}}
+
+ComparePropertyName *comparePropertyName;
+// expected-error at first.h:* {{'ComparePropertyName' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found property 'propNameA'}}
+// expected-note at second.h:* {{but in 'Second' found property 'propNameB'}}
+ComparePropertyType *comparePropertyType;
+// expected-error at first.h:* {{'ComparePropertyType' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found property 'propType' with type 'int'}}
+// expected-note at second.h:* {{but in 'Second' found property 'propType' with type 'float'}}
+ComparePropertyOrder *comparePropertyOrder;
+// expected-error at first.h:* {{'ComparePropertyOrder' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found property 'propOrderX'}}
+// expected-note at second.h:* {{but in 'Second' found property 'propOrderY'}}
+
+CompareMatchingPropertyAttributes *compareMatchingPropertyAttributes;
+ComparePropertyAttributes *comparePropertyAttributes;
+// expected-error at first.h:* {{'ComparePropertyAttributes' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found property 'propAttributes' with 'readonly' attribute}}
+// expected-note at second.h:* {{but in 'Second' found property 'propAttributes' with 
diff erent 'readonly' attribute}}
+CompareFirstImplAttribute *compareFirstImplAttribute;
+// expected-error at first.h:* {{'CompareFirstImplAttribute' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found property 'firstImplAttribute' with default 'readonly' attribute}}
+// expected-note at second.h:* {{but in 'Second' found property 'firstImplAttribute' with 
diff erent 'readonly' attribute}}
+CompareLastImplAttribute *compareLastImplAttribute;
+// expected-error at first.h:* {{'CompareLastImplAttribute' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'First.Hidden' found property 'lastImplAttribute' with 'direct' attribute}}
+// expected-note at second.h:* {{but in 'Second' found property 'lastImplAttribute' with 
diff erent 'direct' attribute}}
+#endif

diff  --git a/clang/test/Modules/interface-diagnose-missing-import.m b/clang/test/Modules/interface-diagnose-missing-import.m
index a6b8c1b03a9dd..2b81983691f05 100644
--- a/clang/test/Modules/interface-diagnose-missing-import.m
+++ b/clang/test/Modules/interface-diagnose-missing-import.m
@@ -1,7 +1,8 @@
 // RUN: rm -rf %t
 // RUN: %clang_cc1 %s -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -F%S/Inputs/interface-diagnose-missing-import -verify
 // expected-no-diagnostics
- at interface Buggy
+ at interface NSObject @end
+ at interface Buggy : NSObject
 @end
 
 @import Foo.Bar;

diff  --git a/clang/test/Modules/method_pool.m b/clang/test/Modules/method_pool.m
index 1d76a8404c6b1..7410e25975a0d 100644
--- a/clang/test/Modules/method_pool.m
+++ b/clang/test/Modules/method_pool.m
@@ -28,6 +28,8 @@ void testMethod5(id object, D* d) {
 }
 
 @import MethodPoolB;
+// expected-error at MethodPoolB.h:* {{'B' has 
diff erent definitions in 
diff erent modules; first 
diff erence is definition in module 'MethodPoolB' found no super class}}
+// expected-note at MethodPoolA.h:* {{but in 'MethodPoolA' found super class with type 'A'}}
 
 void testMethod1Again(id object) {
   [object method1];


        


More information about the cfe-commits mailing list