diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 1595a0ef61f0..f808b962e113 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -677,9 +677,9 @@ private: /// \brief Reverse mapping from declarations to their global declaration IDs. /// - /// FIXME: This data structure is currently only used for ObjCInterfaceDecls, - /// support declaration merging. If we must have this for other declarations, - /// allocate it along with the Decl itself. + /// FIXME: This data structure is currently only used for ObjCInterfaceDecls + /// and ObjCProtocolDecls to support declaration merging. If we must have + /// this for other declarations, allocate it along with the Decl itself. llvm::DenseMap DeclToID; typedef llvm::DenseMap > diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 1df67f22ab53..12436c5c4fe7 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -759,12 +759,54 @@ void ASTDeclReader::VisitObjCIvarDecl(ObjCIvarDecl *IVD) { } void ASTDeclReader::VisitObjCProtocolDecl(ObjCProtocolDecl *PD) { - VisitRedeclarable(PD); + // Record the declaration -> global ID mapping. + Reader.DeclToID[PD] = ThisDeclID; + + RedeclarableResult Redecl = VisitRedeclarable(PD); VisitObjCContainerDecl(PD); PD->InitiallyForwardDecl = Record[Idx++]; PD->isForwardProtoDecl = Record[Idx++]; PD->setLocEnd(ReadSourceLocation(Record, Idx)); + // Determine whether we need to merge this declaration with another @protocol + // with the same name. + // FIXME: Not needed unless the module file graph is a DAG. + if (FindExistingResult ExistingRes = findExisting(PD)) { + if (ObjCProtocolDecl *Existing = ExistingRes) { + ObjCProtocolDecl *ExistingCanon = Existing->getCanonicalDecl(); + ObjCProtocolDecl *PDCanon = PD->getCanonicalDecl(); + if (ExistingCanon != PDCanon) { + // Have our redeclaration link point back at the canonical declaration + // of the existing declaration, so that this declaration has the + // appropriate canonical declaration. + PD->RedeclLink = ObjCProtocolDecl::PreviousDeclLink(ExistingCanon); + + // Don't introduce IDCanon into the set of pending declaration chains. + Redecl.suppress(); + + // Introduce ExistingCanon into the set of pending declaration chains, + // if in fact it came from a module file. + if (ExistingCanon->isFromASTFile()) { + GlobalDeclID ExistingCanonID = Reader.DeclToID[ExistingCanon]; + assert(ExistingCanonID && "Unrecorded canonical declaration ID?"); + if (Reader.PendingDeclChainsKnown.insert(ExistingCanonID)) + Reader.PendingDeclChains.push_back(ExistingCanonID); + } + + // If this declaration was the canonical declaration, make a note of + // that. We accept the linear algorithm here because the number of + // unique canonical declarations of an entity should always be tiny. + if (PDCanon == PD) { + SmallVectorImpl &Merged = Reader.MergedDecls[ExistingCanon]; + if (std::find(Merged.begin(), Merged.end(), Redecl.getFirstID()) + == Merged.end()) + Merged.push_back(Redecl.getFirstID()); + } + } + } + } + + ObjCProtocolDecl *Def = ReadDeclAs(Record, Idx); if (PD == Def) { // Read the definition. @@ -1645,8 +1687,8 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { Y->getDeclContext()->getRedeclContext())) return false; - // Objective-C classes with the same name always match. - if (isa(X)) + // Objective-C classes and protocols with the same name always match. + if (isa(X) || isa(X)) return true; // FIXME: Many other cases to implement. diff --git a/clang/test/Modules/Inputs/redecl-merge-bottom.h b/clang/test/Modules/Inputs/redecl-merge-bottom.h index b3cc74b655ce..26a1ba5ab176 100644 --- a/clang/test/Modules/Inputs/redecl-merge-bottom.h +++ b/clang/test/Modules/Inputs/redecl-merge-bottom.h @@ -2,6 +2,9 @@ __import_module__ redecl_merge_left; @class C4; @class C4; +@protocol P4; +@protocol P4; +@protocol P4; __import_module__ redecl_merge_right; @class B; diff --git a/clang/test/Modules/Inputs/redecl-merge-left.h b/clang/test/Modules/Inputs/redecl-merge-left.h index 2d77badeea72..4ea1e70e8e64 100644 --- a/clang/test/Modules/Inputs/redecl-merge-left.h +++ b/clang/test/Modules/Inputs/redecl-merge-left.h @@ -35,6 +35,10 @@ int *explicit_func(void); struct explicit_struct; +@protocol P3, P4; + +@protocol P3; + #ifdef __cplusplus template class Vector; diff --git a/clang/test/Modules/Inputs/redecl-merge-right.h b/clang/test/Modules/Inputs/redecl-merge-right.h index c5e35b1d63b6..82051d46f935 100644 --- a/clang/test/Modules/Inputs/redecl-merge-right.h +++ b/clang/test/Modules/Inputs/redecl-merge-right.h @@ -42,6 +42,11 @@ int *explicit_func(void); struct explicit_struct; +@protocol P4, P3; +@protocol P3; +@protocol P3; +@protocol P3; + #ifdef __cplusplus template class Vector { public: diff --git a/clang/test/Modules/redecl-merge.m b/clang/test/Modules/redecl-merge.m index d7927396cab9..4ea982b50fee 100644 --- a/clang/test/Modules/redecl-merge.m +++ b/clang/test/Modules/redecl-merge.m @@ -6,6 +6,7 @@ @class C3; __import_module__ redecl_merge_left; +@protocol P4; @class C3; @class C3; __import_module__ redecl_merge_right; @@ -83,6 +84,13 @@ void g(A *a) { [a init]; } +@protocol P3 +- (void)p3_method; +@end + +id p4; +id p3; + #ifdef __cplusplus void testVector() { Vector vec_int;