diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -1146,6 +1146,7 @@ class ObjCInterfaceDecl : public ObjCContainerDecl , public Redeclarable { 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 @@ /// 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 @@ 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 @@ 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 --- a/clang/include/clang/AST/ODRDiagsEmitter.h +++ b/clang/include/clang/AST/ODRDiagsEmitter.h @@ -51,6 +51,13 @@ 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 @@ Friend, FunctionTemplate, ObjCMethod, + ObjCIvar, ObjCProperty, Other }; diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h --- a/clang/include/clang/AST/ODRHash.h +++ b/clang/include/clang/AST/ODRHash.h @@ -59,6 +59,10 @@ // 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 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -615,6 +615,22 @@ "%ordinal2 base class %3 with " "%select{public|protected|private|no}4 access specifier}1">; +def err_module_odr_violation_objc_interface : Error < + "%0 has different definitions in different modules; first difference 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 different definitions in different modules; first difference is " "%select{definition in module '%2'|defined here}1 found " @@ -637,12 +653,14 @@ "%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 different definitions in different modules; first difference is " @@ -937,14 +955,14 @@ "%q0 %select{with definition in module '%2'|defined here}1 has different " "definitions in different modules; first difference 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{||||different static assert|different field|different method|" "different type alias|different typedef|different data member|" "different friend declaration|different function template|different method|" - "different property|another unexpected decl}2">; + "different instance variable|different 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 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1149,6 +1149,9 @@ using DataPointers = std::pair; + using ObjCInterfaceDataPointers = + std::pair; using ObjCProtocolDataPointers = std::pair; @@ -1168,6 +1171,11 @@ llvm::SmallDenseMap, 2> PendingEnumOdrMergeFailures; + /// ObjCInterfaceDecl in which we found an ODR violation. + llvm::SmallDenseMap, 2> + PendingObjCInterfaceOdrMergeFailures; + /// ObjCProtocolDecl in which we found an ODR violation. llvm::SmallDenseMap, 2> diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -778,6 +778,33 @@ 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 --- a/clang/lib/AST/ODRDiagsEmitter.cpp +++ b/clang/lib/AST/ODRDiagsEmitter.cpp @@ -615,6 +615,8 @@ return FunctionTemplate; case Decl::ObjCMethod: return ObjCMethod; + case Decl::ObjCIvar: + return ObjCIvar; case Decl::ObjCProperty: return ObjCProperty; } @@ -675,6 +677,8 @@ if (DiffType == EndOfClass) { if (auto *Tag = dyn_cast(Container)) Loc = Tag->getBraceRange().getEnd(); + else if (auto *IF = dyn_cast(Container)) + Loc = IF->getAtEndRange().getBegin(); else Loc = Container->getEndLoc(); } else { @@ -970,6 +974,7 @@ case PrivateSpecifer: case ProtectedSpecifer: case ObjCMethod: + case ObjCIvar: case ObjCProperty: llvm_unreachable("Invalid diff type"); @@ -1604,6 +1609,7 @@ 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 @@ return false; } +bool ODRDiagsEmitter::diagnoseMismatch( + const ObjCInterfaceDecl *FirstID, const ObjCInterfaceDecl *SecondID, + const struct ObjCInterfaceDecl::DefinitionData *SecondDD) const { + // Multiple different 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()->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(FirstDecl), + cast(SecondDecl))) + return true; + break; + } + case ObjCIvar: { + if (diagnoseSubMismatchField(FirstID, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl))) + return true; + + // Check if the access match. + const ObjCIvarDecl *FirstIvar = cast(FirstDecl); + const ObjCIvarDecl *SecondIvar = cast(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(FirstDecl), + cast(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 @@ 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 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -343,6 +343,11 @@ 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 @@ case Decl::Typedef: case Decl::Var: case Decl::ObjCMethod: + case Decl::ObjCIvar: case Decl::ObjCProperty: return true; } @@ -613,6 +619,33 @@ 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 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 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6857,6 +6857,7 @@ void TypeLocReader::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { TL.setNameLoc(readSourceLocation()); + TL.setNameEndLoc(readSourceLocation()); } void TypeLocReader::VisitObjCTypeParamTypeLoc(ObjCTypeParamTypeLoc TL) { @@ -9507,6 +9508,7 @@ PendingRecordOdrMergeFailures.empty() && PendingFunctionOdrMergeFailures.empty() && PendingEnumOdrMergeFailures.empty() && + PendingObjCInterfaceOdrMergeFailures.empty() && PendingObjCProtocolOdrMergeFailures.empty()) return; @@ -9538,6 +9540,16 @@ 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 @@ if (OdrMergeFailures.empty() && RecordOdrMergeFailures.empty() && FunctionOdrMergeFailures.empty() && EnumOdrMergeFailures.empty() && + ObjCInterfaceOdrMergeFailures.empty() && ObjCProtocolOdrMergeFailures.empty()) return; @@ -9749,6 +9762,25 @@ 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 different" 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 different" diagnostic. diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1207,6 +1207,8 @@ 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::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 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -569,6 +569,7 @@ 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 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -775,6 +775,7 @@ 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 --- /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 different 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 +@interface NSObject @end +@protocol CommonProtocol @end +@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) +@class CompareForwardDeclaration1; +@interface CompareForwardDeclaration2: NSObject @end +#elif defined(SECOND) +@interface CompareForwardDeclaration1: NSObject @end +@class CompareForwardDeclaration2; +#else +CompareForwardDeclaration1 *compareForwardDeclaration1; +CompareForwardDeclaration2 *compareForwardDeclaration2; +#endif + +#if defined(FIRST) +@interface CompareMatchingSuperclass: NSObject @end + +@interface CompareSuperclassPresence1: NSObject @end +@interface CompareSuperclassPresence2 @end + +@interface CompareDifferentSuperclass: NSObject @end +#elif defined(SECOND) +@interface CompareMatchingSuperclass: NSObject @end + +@interface CompareSuperclassPresence1 @end +@interface CompareSuperclassPresence2: NSObject @end + +@interface DifferentSuperclass: NSObject @end +@interface CompareDifferentSuperclass: DifferentSuperclass @end +#else +CompareMatchingSuperclass *compareMatchingSuperclass; +CompareSuperclassPresence1 *compareSuperclassPresence1; +// expected-error@first.h:* {{'CompareSuperclassPresence1' has different definitions in different modules; first difference is definition in module 'First.Hidden' found super class with type 'NSObject'}} +// expected-note@second.h:* {{but in 'Second' found no super class}} +CompareSuperclassPresence2 *compareSuperclassPresence2; +// expected-error@first.h:* {{'CompareSuperclassPresence2' has different definitions in different modules; first difference is definition in module 'First.Hidden' found no super class}} +// expected-note@second.h:* {{but in 'Second' found super class with type 'NSObject'}} +CompareDifferentSuperclass *compareDifferentSuperclass; +// expected-error@first.h:* {{'CompareDifferentSuperclass' has different definitions in different modules; first difference is definition in module 'First.Hidden' found super class with type 'NSObject'}} +// expected-note@second.h:* {{but in 'Second' found super class with type 'DifferentSuperclass'}} +#endif + +#if defined(FIRST) +@interface CompareMatchingConformingProtocols: NSObject @end +@protocol ForwardProtocol; +@interface CompareMatchingConformingForwardProtocols: NSObject @end + +@interface CompareProtocolPresence1: NSObject @end +@interface CompareProtocolPresence2: NSObject @end + +@interface CompareDifferentProtocols: NSObject @end +@interface CompareProtocolOrder: NSObject @end +#elif defined(SECOND) +@interface CompareMatchingConformingProtocols: NSObject @end +@protocol ForwardProtocol @end +@interface CompareMatchingConformingForwardProtocols: NSObject @end + +@interface CompareProtocolPresence1: NSObject @end +@interface CompareProtocolPresence2: NSObject @end + +@interface CompareDifferentProtocols: NSObject @end +@interface CompareProtocolOrder: NSObject @end +#else +CompareMatchingConformingProtocols *compareMatchingConformingProtocols; +CompareMatchingConformingForwardProtocols *compareMatchingConformingForwardProtocols; + +CompareProtocolPresence1 *compareProtocolPresence1; +// expected-error@first.h:* {{'CompareProtocolPresence1' has different definitions in different modules; first difference is definition in module 'First.Hidden' found 1 referenced protocol}} +// expected-note@second.h:* {{but in 'Second' found 0 referenced protocols}} +CompareProtocolPresence2 *compareProtocolPresence2; +// expected-error@first.h:* {{'CompareProtocolPresence2' has different definitions in different modules; first difference is definition in module 'First.Hidden' found 0 referenced protocols}} +// expected-note@second.h:* {{but in 'Second' found 1 referenced protocol}} + +CompareDifferentProtocols *compareDifferentProtocols; +// expected-error@first.h:* {{'CompareDifferentProtocols' has different definitions in different modules; first difference is definition in module 'First.Hidden' found 1st referenced protocol with name 'CommonProtocol'}} +// expected-note@second.h:* {{but in 'Second' found 1st referenced protocol with different name 'ExtraProtocol'}} +CompareProtocolOrder *compareProtocolOrder; +// expected-error@first.h:* {{'CompareProtocolOrder' has different definitions in different modules; first difference is definition in module 'First.Hidden' found 1st referenced protocol with name 'CommonProtocol'}} +// expected-note@second.h:* {{but in 'Second' found 1st referenced protocol with different name 'ExtraProtocol'}} +#endif + +#if defined(FIRST) +@interface CompareMatchingIVars: NSObject { int ivarName; } @end + +@interface CompareIVarPresence1: NSObject @end +@interface CompareIVarPresence2: NSObject { int ivarPresence2; } @end + +@interface CompareIVarName: NSObject { int ivarName; } @end +@interface CompareIVarType: NSObject { int ivarType; } @end +@interface CompareIVarOrder: NSObject { + int ivarNameInt; + float ivarNameFloat; +} +@end + +@interface CompareIVarVisibilityExplicit: NSObject { +@public + int ivarVisibility; +} +@end +@interface CompareIVarVisibilityDefault: NSObject { + int ivarVisibilityDefault; +} +@end +#elif defined(SECOND) +@interface CompareMatchingIVars: NSObject { int ivarName; } @end + +@interface CompareIVarPresence1: NSObject { int ivarPresence1; } @end +@interface CompareIVarPresence2: NSObject @end + +@interface CompareIVarName: NSObject { int differentIvarName; } @end +@interface CompareIVarType: NSObject { float ivarType; } @end +@interface CompareIVarOrder: NSObject { + float ivarNameFloat; + int ivarNameInt; +} +@end + +@interface CompareIVarVisibilityExplicit: NSObject { +@private + int ivarVisibility; +} +@end +@interface CompareIVarVisibilityDefault: NSObject { +@public + int ivarVisibilityDefault; +} +@end +#else +CompareMatchingIVars *compareMatchingIVars; + +CompareIVarPresence1 *compareIVarPresence1; +// expected-error@second.h:* {{'CompareIVarPresence1::ivarPresence1' from module 'Second' is not present in definition of 'CompareIVarPresence1' in module 'First.Hidden'}} +// expected-note@first.h:* {{definition has no member 'ivarPresence1'}} +CompareIVarPresence2 *compareIVarPresence2; +// expected-error@first.h:* {{'CompareIVarPresence2' has different definitions in different modules; first difference is definition in module 'First.Hidden' found instance variable}} +// expected-note@second.h:* {{but in 'Second' found end of class}} + +CompareIVarName *compareIVarName; +// expected-error@second.h:* {{'CompareIVarName::differentIvarName' from module 'Second' is not present in definition of 'CompareIVarName' in module 'First.Hidden'}} +// expected-note@first.h:* {{definition has no member 'differentIvarName'}} +CompareIVarType *compareIVarType; +// expected-error@second.h:* {{'CompareIVarType::ivarType' from module 'Second' is not present in definition of 'CompareIVarType' in module 'First.Hidden'}} +// expected-note@first.h:* {{declaration of 'ivarType' does not match}} +CompareIVarOrder *compareIVarOrder; +// expected-error@first.h:* {{'CompareIVarOrder' has different definitions in different modules; first difference is definition in module 'First.Hidden' found field 'ivarNameInt'}} +// expected-note@second.h:* {{but in 'Second' found field 'ivarNameFloat'}} + +CompareIVarVisibilityExplicit *compareIVarVisibilityExplicit; +// expected-error@first.h:* {{'CompareIVarVisibilityExplicit' has different definitions in different modules; first difference is definition in module 'First.Hidden' found instance variable 'ivarVisibility' access control is @public}} +// expected-note@second.h:* {{but in 'Second' found instance variable 'ivarVisibility' access control is @private}} +CompareIVarVisibilityDefault *compareIVarVisibilityDefault; +// expected-error@first.h:* {{'CompareIVarVisibilityDefault' has different definitions in different modules; first difference is definition in module 'First.Hidden' found instance variable 'ivarVisibilityDefault' access control is @protected}} +// expected-note@second.h:* {{but in 'Second' found instance variable 'ivarVisibilityDefault' access control is @public}} +#endif + +#if defined(FIRST) +@interface CompareMatchingMethods: NSObject +- (float)matchingMethod:(int)arg; +@end + +@interface CompareMethodPresence1: NSObject +- (void)presenceMethod1; +@end +@interface CompareMethodPresence2: NSObject +@end + +@interface CompareMethodName: NSObject +- (void)methodNameA; +@end + +@interface CompareMethodArgCount: NSObject +- (void)methodArgCount:(int)arg0 :(int)arg1; +@end +@interface CompareMethodArgName: NSObject +- (void)methodArgName:(int)argNameA; +@end +@interface CompareMethodArgType: NSObject +- (void)methodArgType:(int)argType; +@end + +@interface CompareMethodReturnType: NSObject +- (int)methodReturnType; +@end + +@interface CompareMethodOrder: NSObject +- (void)methodOrderFirst; +- (void)methodOrderSecond; +@end + +@interface CompareMethodClassInstance: NSObject ++ (void)methodClassInstance; +@end +#elif defined(SECOND) +@interface CompareMatchingMethods: NSObject +- (float)matchingMethod:(int)arg; +@end + +@interface CompareMethodPresence1: NSObject +@end +@interface CompareMethodPresence2: NSObject +- (void)presenceMethod2; +@end + +@interface CompareMethodName: NSObject +- (void)methodNameB; +@end + +@interface CompareMethodArgCount: NSObject +- (void)methodArgCount:(int)arg0; +@end +@interface CompareMethodArgName: NSObject +- (void)methodArgName:(int)argNameB; +@end +@interface CompareMethodArgType: NSObject +- (void)methodArgType:(float)argType; +@end + +@interface CompareMethodReturnType: NSObject +- (float)methodReturnType; +@end + +@interface CompareMethodOrder: NSObject +- (void)methodOrderSecond; +- (void)methodOrderFirst; +@end + +@interface CompareMethodClassInstance: NSObject +- (void)methodClassInstance; +@end +#else +CompareMatchingMethods *compareMatchingMethods; +CompareMethodPresence1 *compareMethodPresence1; +// expected-error@first.h:* {{'CompareMethodPresence1' has different definitions in different modules; first difference is definition in module 'First.Hidden' found method}} +// expected-note@second.h:* {{but in 'Second' found end of class}} +CompareMethodPresence2 *compareMethodPresence2; +// expected-error@first.h:* {{'CompareMethodPresence2' has different definitions in different modules; first difference is definition in module 'First.Hidden' found end of class}} +// expected-note@second.h:* {{but in 'Second' found method}} +CompareMethodName *compareMethodName; +// expected-error@first.h:* {{'CompareMethodName' has different definitions in different modules; first difference is definition in module 'First.Hidden' found method 'methodNameA'}} +// expected-note@second.h:* {{but in 'Second' found different method 'methodNameB'}} + +CompareMethodArgCount *compareMethodArgCount; +// expected-error@first.h:* {{'CompareMethodArgCount' has different definitions in different modules; first difference is definition in module 'First.Hidden' found method 'methodArgCount::' that has 2 parameters}} +// expected-note@second.h:* {{but in 'Second' found method 'methodArgCount:' that has 1 parameter}} +CompareMethodArgName *compareMethodArgName; +// expected-error@first.h:* {{'CompareMethodArgName' has different definitions in different modules; first difference is definition in module 'First.Hidden' found method 'methodArgName:' with 1st parameter named 'argNameA'}} +// expected-note@second.h:* {{but in 'Second' found method 'methodArgName:' with 1st parameter named 'argNameB'}} +CompareMethodArgType *compareMethodArgType; +// expected-error@first.h:* {{'CompareMethodArgType' has different definitions in different modules; first difference is definition in module 'First.Hidden' found method 'methodArgType:' with 1st parameter of type 'int'}} +// expected-note@second.h:* {{but in 'Second' found method 'methodArgType:' with 1st parameter of type 'float'}} + +CompareMethodReturnType *compareMethodReturnType; +// expected-error@first.h:* {{'CompareMethodReturnType' has different definitions in different modules; first difference is definition in module 'First.Hidden' found method 'methodReturnType' with return type 'int'}} +// expected-note@second.h:* {{but in 'Second' found method 'methodReturnType' with different return type 'float'}} + +CompareMethodOrder *compareMethodOrder; +// expected-error@first.h:* {{'CompareMethodOrder' has different definitions in different modules; first difference is definition in module 'First.Hidden' found method 'methodOrderFirst'}} +// expected-note@second.h:* {{but in 'Second' found different method 'methodOrderSecond'}} +CompareMethodClassInstance *compareMethodClassInstance; +// expected-error@first.h:* {{'CompareMethodClassInstance' has different definitions in different modules; first difference is definition in module 'First.Hidden' found class method 'methodClassInstance'}} +// expected-note@second.h:* {{but in 'Second' found method 'methodClassInstance' as instance method}} +#endif + +#if defined(FIRST) +@interface CompareMatchingProperties: NSObject +@property int matchingPropName; +@end + +@interface ComparePropertyPresence1: NSObject +@property int propPresence1; +@end +@interface ComparePropertyPresence2: NSObject +@end + +@interface ComparePropertyName: NSObject +@property int propNameA; +@end + +@interface ComparePropertyType: NSObject +@property int propType; +@end + +@interface ComparePropertyOrder: NSObject +@property int propOrderX; +@property int propOrderY; +@end + +@interface CompareMatchingPropertyAttributes: NSObject +@property (nonatomic, assign) int matchingProp; +@end +@interface ComparePropertyAttributes: NSObject +@property (readonly) int propAttributes; +@end +// Edge cases. +@interface CompareFirstImplAttribute: NSObject +@property int firstImplAttribute; +@end +@interface CompareLastImplAttribute: NSObject +@property (direct) int lastImplAttribute; +@end +#elif defined(SECOND) +@interface CompareMatchingProperties: NSObject +@property int matchingPropName; +@end + +@interface ComparePropertyPresence1: NSObject +@end +@interface ComparePropertyPresence2: NSObject +@property int propPresence2; +@end + +@interface ComparePropertyName: NSObject +@property int propNameB; +@end + +@interface ComparePropertyType: NSObject +@property float propType; +@end + +@interface ComparePropertyOrder: NSObject +@property int propOrderY; +@property int propOrderX; +@end + +@interface CompareMatchingPropertyAttributes: NSObject +@property (assign, nonatomic) int matchingProp; +@end +@interface ComparePropertyAttributes: NSObject +@property (readwrite) int propAttributes; +@end +// Edge cases. +@interface CompareFirstImplAttribute: NSObject +@property (readonly) int firstImplAttribute; +@end +@interface CompareLastImplAttribute: NSObject +@property int lastImplAttribute; +@end +#else +CompareMatchingProperties *compareMatchingProperties; +ComparePropertyPresence1 *comparePropertyPresence1; +// expected-error@first.h:* {{'ComparePropertyPresence1' has different definitions in different modules; first difference is definition in module 'First.Hidden' found property}} +// expected-note@second.h:* {{but in 'Second' found end of class}} +ComparePropertyPresence2 *comparePropertyPresence2; +// expected-error@first.h:* {{'ComparePropertyPresence2' has different definitions in different modules; first difference is definition in module 'First.Hidden' found end of class}} +// expected-note@second.h:* {{but in 'Second' found property}} + +ComparePropertyName *comparePropertyName; +// expected-error@first.h:* {{'ComparePropertyName' has different definitions in different modules; first difference is definition in module 'First.Hidden' found property 'propNameA'}} +// expected-note@second.h:* {{but in 'Second' found property 'propNameB'}} +ComparePropertyType *comparePropertyType; +// expected-error@first.h:* {{'ComparePropertyType' has different definitions in different modules; first difference is definition in module 'First.Hidden' found property 'propType' with type 'int'}} +// expected-note@second.h:* {{but in 'Second' found property 'propType' with type 'float'}} +ComparePropertyOrder *comparePropertyOrder; +// expected-error@first.h:* {{'ComparePropertyOrder' has different definitions in different modules; first difference is definition in module 'First.Hidden' found property 'propOrderX'}} +// expected-note@second.h:* {{but in 'Second' found property 'propOrderY'}} + +CompareMatchingPropertyAttributes *compareMatchingPropertyAttributes; +ComparePropertyAttributes *comparePropertyAttributes; +// expected-error@first.h:* {{'ComparePropertyAttributes' has different definitions in different modules; first difference is definition in module 'First.Hidden' found property 'propAttributes' with 'readonly' attribute}} +// expected-note@second.h:* {{but in 'Second' found property 'propAttributes' with different 'readonly' attribute}} +CompareFirstImplAttribute *compareFirstImplAttribute; +// expected-error@first.h:* {{'CompareFirstImplAttribute' has different definitions in different modules; first difference is definition in module 'First.Hidden' found property 'firstImplAttribute' with default 'readonly' attribute}} +// expected-note@second.h:* {{but in 'Second' found property 'firstImplAttribute' with different 'readonly' attribute}} +CompareLastImplAttribute *compareLastImplAttribute; +// expected-error@first.h:* {{'CompareLastImplAttribute' has different definitions in different modules; first difference is definition in module 'First.Hidden' found property 'lastImplAttribute' with 'direct' attribute}} +// expected-note@second.h:* {{but in 'Second' found property 'lastImplAttribute' with different 'direct' attribute}} +#endif diff --git a/clang/test/Modules/interface-diagnose-missing-import.m b/clang/test/Modules/interface-diagnose-missing-import.m --- 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 -@interface Buggy +@interface NSObject @end +@interface Buggy : NSObject @end @import Foo.Bar; diff --git a/clang/test/Modules/method_pool.m b/clang/test/Modules/method_pool.m --- a/clang/test/Modules/method_pool.m +++ b/clang/test/Modules/method_pool.m @@ -28,6 +28,8 @@ } @import MethodPoolB; +// expected-error@MethodPoolB.h:* {{'B' has different definitions in different modules; first difference is definition in module 'MethodPoolB' found no super class}} +// expected-note@MethodPoolA.h:* {{but in 'MethodPoolA' found super class with type 'A'}} void testMethod1Again(id object) { [object method1];