diff options
-rw-r--r-- | include/clang/Serialization/ASTBitCodes.h | 41 | ||||
-rw-r--r-- | include/clang/Serialization/ASTReader.h | 26 | ||||
-rw-r--r-- | include/clang/Serialization/ASTWriter.h | 17 | ||||
-rw-r--r-- | include/clang/Serialization/Module.h | 22 | ||||
-rw-r--r-- | lib/Serialization/ASTReader.cpp | 50 | ||||
-rw-r--r-- | lib/Serialization/ASTReaderDecl.cpp | 251 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 90 | ||||
-rw-r--r-- | lib/Serialization/ASTWriterDecl.cpp | 12 | ||||
-rw-r--r-- | lib/Serialization/Module.cpp | 1 | ||||
-rw-r--r-- | test/Modules/Inputs/category_other.h | 6 | ||||
-rw-r--r-- | test/Modules/Inputs/category_top.h | 8 | ||||
-rw-r--r-- | test/Modules/Inputs/module.map | 1 | ||||
-rw-r--r-- | test/Modules/objc-categories.m | 13 |
13 files changed, 329 insertions, 209 deletions
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index df56d57d56..c0d9edff4b 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -444,9 +444,9 @@ namespace clang { /// which stores information about #line directives. SOURCE_MANAGER_LINE_TABLE = 48, - /// \brief Record code for ObjC categories in a module that are chained to - /// an interface. - OBJC_CHAINED_CATEGORIES = 49, + /// \brief Record code for map of Objective-C class definition IDs to the + /// ObjC categories in a module that are attached to that class. + OBJC_CATEGORIES_MAP = 49, /// \brief Record code for a file sorted array of DeclIDs in a module. FILE_SORTED_DECLS = 50, @@ -462,7 +462,14 @@ namespace clang { /// /// This array can only be interpreted properly using the local /// redeclarations map. - LOCAL_REDECLARATIONS = 53 + LOCAL_REDECLARATIONS = 53, + + /// \brief Record code for the array of Objective-C categories (including + /// extensions). + /// + /// This array can only be interpreted properly using the Objective-C + /// categories map. + OBJC_CATEGORIES }; /// \brief Record types used within a source manager block. @@ -1225,6 +1232,32 @@ namespace clang { } }; + /// \brief Describes the categories of an Objective-C class. + struct ObjCCategoriesInfo { + DeclID DefinitionID; // The ID of the definition + unsigned Offset; // Offset into the array of category lists. + + friend bool operator<(const ObjCCategoriesInfo &X, + const ObjCCategoriesInfo &Y) { + return X.DefinitionID < Y.DefinitionID; + } + + friend bool operator>(const ObjCCategoriesInfo &X, + const ObjCCategoriesInfo &Y) { + return X.DefinitionID > Y.DefinitionID; + } + + friend bool operator<=(const ObjCCategoriesInfo &X, + const ObjCCategoriesInfo &Y) { + return X.DefinitionID <= Y.DefinitionID; + } + + friend bool operator>=(const ObjCCategoriesInfo &X, + const ObjCCategoriesInfo &Y) { + return X.DefinitionID >= Y.DefinitionID; + } + }; + /// @} } } // end namespace clang diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 91f872bc1b..2ebdd095a7 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -335,10 +335,6 @@ private: /// declarations that have not yet been linked to their definitions. llvm::SmallPtrSet<Decl *, 4> PendingDefinitions; - /// \brief Set of ObjC interfaces that have categories chained to them in - /// other modules. - llvm::DenseSet<serialization::GlobalDeclID> ObjCChainedCategoriesInterfaces; - /// \brief Read the records that describe the contents of declcontexts. bool ReadDeclContextStorage(ModuleFile &M, llvm::BitstreamCursor &Cursor, @@ -687,6 +683,15 @@ private: /// \brief Keeps track of the elements added to PendingDeclChains. llvm::SmallSet<serialization::DeclID, 16> PendingDeclChainsKnown; + /// \brief The set of Objective-C categories that have been deserialized + /// since the last time the declaration chains were linked. + llvm::SmallPtrSet<ObjCCategoryDecl *, 16> CategoriesDeserialized; + + /// \brief The set of Objective-C class definitions that have already been + /// loaded, for which we will need to check for categories whenever a new + /// module is loaded. + llvm::SmallVector<ObjCInterfaceDecl *, 16> ObjCClassesLoaded; + typedef llvm::DenseMap<Decl *, llvm::SmallVector<serialization::DeclID, 2> > MergedDeclsMap; @@ -715,11 +720,6 @@ private: MergedDeclsMap::iterator combineStoredMergedDecls(Decl *Canon, serialization::GlobalDeclID CanonID); - /// \brief We delay loading the chain of objc categories after recursive - /// loading of declarations is finished. - std::vector<std::pair<ObjCInterfaceDecl *, serialization::DeclID> > - PendingChainedObjCCategories; - /// \brief Ready to load the previous declaration of the given Decl. void loadAndAttachPreviousDecl(Decl *D, serialization::DeclID ID); @@ -801,8 +801,8 @@ private: unsigned &RawLocation); void loadDeclUpdateRecords(serialization::DeclID ID, Decl *D); void loadPendingDeclChain(serialization::GlobalDeclID ID); - void loadObjCChainedCategories(serialization::GlobalDeclID ID, - ObjCInterfaceDecl *D); + void loadObjCCategories(serialization::GlobalDeclID ID, ObjCInterfaceDecl *D, + unsigned PreviousGeneration = 0); RecordLocation getLocalBitOffset(uint64_t GlobalOffset); uint64_t getGlobalBitOffset(ModuleFile &M, uint32_t LocalOffset); @@ -1049,6 +1049,10 @@ public: /// \arg M. bool isDeclIDFromModule(serialization::GlobalDeclID ID, ModuleFile &M) const; + /// \brief Retrieve the module file that owns the given declaration, or NULL + /// if the declaration is not from a module file. + ModuleFile *getOwningModuleFile(Decl *D); + /// \brief Returns the source location for the decl \arg ID. SourceLocation getSourceLocationForDeclID(serialization::GlobalDeclID ID); diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 8852d5dc67..f6a2c4817a 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -284,17 +284,10 @@ private: /// \brief Decls that will be replaced in the current dependent AST file. DeclsToRewriteTy DeclsToRewrite; - struct ChainedObjCCategoriesData { - /// \brief The interface in the imported module. - const ObjCInterfaceDecl *Interface; - /// \brief The local tail category ID that got chained to the imported - /// interface. - const ObjCCategoryDecl *TailCategory; - }; - /// \brief ObjC categories that got chained to an interface imported from - /// another module. - SmallVector<ChainedObjCCategoriesData, 16> LocalChainedObjCCategories; - + /// \brief The set of Objective-C class that have categories we + /// should serialize. + llvm::SetVector<ObjCInterfaceDecl *> ObjCClassesWithCategories; + struct ReplacedDeclInfo { serialization::DeclID ID; uint64_t Offset; @@ -413,10 +406,10 @@ private: void ResolveDeclUpdatesBlocks(); void WriteDeclUpdatesBlocks(); void WriteDeclReplacementsBlock(); - void WriteChainedObjCCategories(); void WriteDeclContextVisibleUpdate(const DeclContext *DC); void WriteFPPragmaOptions(const FPOptions &Opts); void WriteOpenCLExtensions(Sema &SemaRef); + void WriteObjCCategories(); void WriteRedeclarations(); void WriteMergedDecls(); diff --git a/include/clang/Serialization/Module.h b/include/clang/Serialization/Module.h index 831a873ac1..34f208d71f 100644 --- a/include/clang/Serialization/Module.h +++ b/include/clang/Serialization/Module.h @@ -286,15 +286,6 @@ public: /// for each DeclContext. DeclContextInfosMap DeclContextInfos; - typedef llvm::DenseMap<serialization::GlobalDeclID, - std::pair<serialization::LocalDeclID, serialization::LocalDeclID> > - ChainedObjCCategoriesMap; - /// \brief ObjC categories that got chained to an interface from another - /// module. - /// Key is the ID of the interface. - /// Value is a pair of linked category DeclIDs (head category, tail category). - ChainedObjCCategoriesMap ChainedObjCCategories; - /// \brief Array of file-level DeclIDs sorted by file. const serialization::DeclID *FileSortedDecls; @@ -302,13 +293,24 @@ public: /// module file, sorted by the first declaration ID. const serialization::LocalRedeclarationsInfo *RedeclarationsMap; - /// \brief The number of redeclaration info entries in RedeclarationsInfo. + /// \brief The number of redeclaration info entries in RedeclarationsMap. unsigned LocalNumRedeclarationsInMap; /// \brief The redeclaration chains for declarations local to this /// module file. SmallVector<uint64_t, 1> RedeclarationChains; + /// \brief Array of category list location information within this + /// module file, sorted by the definition ID. + const serialization::ObjCCategoriesInfo *ObjCCategoriesMap; + + /// \brief The number of redeclaration info entries in ObjCCategoriesMap. + unsigned LocalNumObjCCategoriesInMap; + + /// \brief The Objective-C category lists for categories known to this + /// module. + SmallVector<uint64_t, 1> ObjCCategories; + // === Types === /// \brief The number of types in this AST file. diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index d336eb1547..42a848df78 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2325,20 +2325,21 @@ ASTReader::ReadASTBlock(ModuleFile &F) { break; } - case OBJC_CHAINED_CATEGORIES: { - if (Record.size() % 3 != 0) { - Error("invalid OBJC_CHAINED_CATEGORIES block in AST file"); + case OBJC_CATEGORIES_MAP: { + if (F.LocalNumObjCCategoriesInMap != 0) { + Error("duplicate OBJC_CATEGORIES_MAP record in AST file"); return Failure; } - for (unsigned I = 0, N = Record.size(); I != N; I += 3) { - serialization::GlobalDeclID GlobID = getGlobalDeclID(F, Record[I]); - F.ChainedObjCCategories[GlobID] = std::make_pair(Record[I+1], - Record[I+2]); - ObjCChainedCategoriesInterfaces.insert(GlobID); - } + + F.LocalNumObjCCategoriesInMap = Record[0]; + F.ObjCCategoriesMap = (const ObjCCategoriesInfo *)BlobStart; break; } + case OBJC_CATEGORIES: + F.ObjCCategories.swap(Record); + break; + case CXX_BASE_SPECIFIER_OFFSETS: { if (F.LocalNumCXXBaseSpecifiers != 0) { Error("duplicate CXX_BASE_SPECIFIER_OFFSETS record in AST file"); @@ -2643,7 +2644,7 @@ void ASTReader::makeModuleVisible(Module *Mod, ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName, ModuleKind Type) { // Bump the generation number. - ++CurrentGeneration; + unsigned PreviousGeneration = CurrentGeneration++; switch(ReadASTCore(FileName, Type, /*ImportedBy=*/0)) { case Failure: return Failure; @@ -2706,6 +2707,14 @@ ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName, } } + // For any Objective-C class definitions we have already loaded, make sure + // that we load any additional categories. + for (unsigned I = 0, N = ObjCClassesLoaded.size(); I != N; ++I) { + loadObjCCategories(ObjCClassesLoaded[I]->getGlobalID(), + ObjCClassesLoaded[I], + PreviousGeneration); + } + return Success; } @@ -4576,6 +4585,14 @@ bool ASTReader::isDeclIDFromModule(serialization::GlobalDeclID ID, return &M == I->second; } +ModuleFile *ASTReader::getOwningModuleFile(Decl *D) { + if (!D->isFromASTFile()) + return 0; + GlobalDeclMapType::const_iterator I = GlobalDeclMap.find(D->getGlobalID()); + assert(I != GlobalDeclMap.end() && "Corrupted global declaration map"); + return I->second; +} + SourceLocation ASTReader::getSourceLocationForDeclID(GlobalDeclID ID) { if (ID < NUM_PREDEF_DECL_IDS) return SourceLocation(); @@ -6147,10 +6164,7 @@ void ASTReader::ClearSwitchCaseIDs() { } void ASTReader::finishPendingActions() { - while (!PendingIdentifierInfos.empty() || - !PendingDeclChains.empty() || - !PendingChainedObjCCategories.empty()) { - + while (!PendingIdentifierInfos.empty() || !PendingDeclChains.empty()) { // If any identifiers with corresponding top-level declarations have // been loaded, load those declarations now. while (!PendingIdentifierInfos.empty()) { @@ -6165,14 +6179,6 @@ void ASTReader::finishPendingActions() { PendingDeclChainsKnown.erase(PendingDeclChains[I]); } PendingDeclChains.clear(); - - for (std::vector<std::pair<ObjCInterfaceDecl *, - serialization::DeclID> >::iterator - I = PendingChainedObjCCategories.begin(), - E = PendingChainedObjCCategories.end(); I != E; ++I) { - loadObjCChainedCategories(I->second, I->first); - } - PendingChainedObjCCategories.clear(); } // If we deserialized any C++ or Objective-C class definitions, any diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index a1f1933920..fbe861d6cc 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -705,14 +705,14 @@ void ASTDeclReader::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID) { ID->data().AllReferencedProtocols.set(Protocols.data(), NumProtocols, Reader.getContext()); - // Read the categories. - ID->setCategoryList(ReadDeclAs<ObjCCategoryDecl>(Record, Idx)); - // We will rebuild this list lazily. ID->setIvarList(0); // Note that we have deserialized a definition. Reader.PendingDefinitions.insert(ID); + + // Note that we've loaded this Objective-C class. + Reader.ObjCClassesLoaded.push_back(ID); } else { ID->Data = ID->getCanonicalDecl()->Data; } @@ -765,6 +765,13 @@ void ASTDeclReader::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *FD) { void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { VisitObjCContainerDecl(CD); + CD->setCategoryNameLoc(ReadSourceLocation(Record, Idx)); + + // Note that this category has been deserialized. We do this before + // deserializing the interface declaration, so that it will consider this + /// category. + Reader.CategoriesDeserialized.insert(CD); + CD->ClassInterface = ReadDeclAs<ObjCInterfaceDecl>(Record, Idx); unsigned NumProtoRefs = Record[Idx++]; SmallVector<ObjCProtocolDecl *, 16> ProtoRefs; @@ -777,9 +784,7 @@ void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { ProtoLocs.push_back(ReadSourceLocation(Record, Idx)); CD->setProtocolList(ProtoRefs.data(), NumProtoRefs, ProtoLocs.data(), Reader.getContext()); - CD->NextClassCategory = ReadDeclAs<ObjCCategoryDecl>(Record, Idx); CD->setHasSynthBitfield(Record[Idx++]); - CD->setCategoryNameLoc(ReadSourceLocation(Record, Idx)); } void ASTDeclReader::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) { @@ -2049,17 +2054,17 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { // Load any relevant update records. loadDeclUpdateRecords(ID, D); - // Load the category chain after recursive loading is finished. - if (ObjCChainedCategoriesInterfaces.count(ID)) - PendingChainedObjCCategories.push_back( - std::make_pair(cast<ObjCInterfaceDecl>(D), ID)); + // Load the categories after recursive loading is finished. + if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(D)) + if (Class->isThisDeclarationADefinition()) + loadObjCCategories(ID, Class); // If we have deserialized a declaration that has a definition the // AST consumer might need to know about, queue it. // We don't pass it to the consumer immediately because we may be in recursive // loading, and some declarations may still be initializing. if (isConsumerInterestedIn(D)) - InterestingDecls.push_back(D); + InterestingDecls.push_back(D); return D; } @@ -2174,7 +2179,7 @@ namespace { return; } - // Dig out the starting/ending declarations. + // Dig out all of the redeclarations. unsigned Offset = Result->Offset; unsigned N = M.RedeclarationChains[Offset]; M.RedeclarationChains[Offset++] = 0; // Don't try to deserialize again @@ -2233,129 +2238,145 @@ void ASTReader::loadPendingDeclChain(serialization::GlobalDeclID ID) { } namespace { + struct CompareObjCCategoriesInfo { + bool operator()(const ObjCCategoriesInfo &X, DeclID Y) { + return X.DefinitionID < Y; + } + + bool operator()(DeclID X, const ObjCCategoriesInfo &Y) { + return X < Y.DefinitionID; + } + + bool operator()(const ObjCCategoriesInfo &X, + const ObjCCategoriesInfo &Y) { + return X.DefinitionID < Y.DefinitionID; + } + bool operator()(DeclID X, DeclID Y) { + return X < Y; + } + }; + /// \brief Given an ObjC interface, goes through the modules and links to the /// interface all the categories for it. - class ObjCChainedCategoriesVisitor { + class ObjCCategoriesVisitor { ASTReader &Reader; serialization::GlobalDeclID InterfaceID; ObjCInterfaceDecl *Interface; - ObjCCategoryDecl *GlobHeadCat, *GlobTailCat; + llvm::SmallPtrSet<ObjCCategoryDecl *, 16> &Deserialized; + unsigned PreviousGeneration; + ObjCCategoryDecl *Tail; llvm::DenseMap<DeclarationName, ObjCCategoryDecl *> NameCategoryMap; - + + void add(ObjCCategoryDecl *Cat) { + // Only process each category once. + if (!Deserialized.count(Cat)) + return; + Deserialized.erase(Cat); + + // Check for duplicate categories. + if (Cat->getDeclName()) { + ObjCCategoryDecl *&Existing = NameCategoryMap[Cat->getDeclName()]; + if (Existing && + Reader.getOwningModuleFile(Existing) + != Reader.getOwningModuleFile(Cat)) { + // FIXME: We should not warn for duplicates in diamond: + // + // MT // + // / \ // + // ML MR // + // \ / // + // MB // + // + // If there are duplicates in ML/MR, there will be warning when + // creating MB *and* when importing MB. We should not warn when + // importing. + Reader.Diag(Cat->getLocation(), diag::warn_dup_category_def) + << Interface->getDeclName() << Cat->getDeclName(); + Reader.Diag(Existing->getLocation(), diag::note_previous_definition); + } else if (!Existing) { + // Record this category. + Existing = Cat; + } + } + + // Add this category to the end of the chain. + if (Tail) + ASTDeclReader::setNextObjCCategory(Tail, Cat); + else + Interface->setCategoryList(Cat); + Tail = Cat; + } + public: - ObjCChainedCategoriesVisitor(ASTReader &Reader, - serialization::GlobalDeclID InterfaceID, - ObjCInterfaceDecl *Interface) + ObjCCategoriesVisitor(ASTReader &Reader, + serialization::GlobalDeclID InterfaceID, + ObjCInterfaceDecl *Interface, + llvm::SmallPtrSet<ObjCCategoryDecl *, 16> &Deserialized, + unsigned PreviousGeneration) : Reader(Reader), InterfaceID(InterfaceID), Interface(Interface), - GlobHeadCat(0), GlobTailCat(0) { } + Deserialized(Deserialized), PreviousGeneration(PreviousGeneration), + Tail(0) + { + // Populate the name -> category map with the set of known categories. + for (ObjCCategoryDecl *Cat = Interface->getCategoryList(); Cat; + Cat = Cat->getNextClassCategory()) { + if (Cat->getDeclName()) + NameCategoryMap[Cat->getDeclName()] = Cat; + + // Keep track of the tail of the category list. + Tail = Cat; + } + } static bool visit(ModuleFile &M, void *UserData) { - return static_cast<ObjCChainedCategoriesVisitor *>(UserData)->visit(M); + return static_cast<ObjCCategoriesVisitor *>(UserData)->visit(M); } bool visit(ModuleFile &M) { - if (Reader.isDeclIDFromModule(InterfaceID, M)) - return true; // We reached the module where the interface originated - // from. Stop traversing the imported modules. - - ModuleFile::ChainedObjCCategoriesMap::iterator - I = M.ChainedObjCCategories.find(InterfaceID); - if (I == M.ChainedObjCCategories.end()) - return false; - - ObjCCategoryDecl * - HeadCat = Reader.GetLocalDeclAs<ObjCCategoryDecl>(M, I->second.first); - ObjCCategoryDecl * - TailCat = Reader.GetLocalDeclAs<ObjCCategoryDecl>(M, I->second.second); - - addCategories(HeadCat, TailCat); - return false; - } - - void addCategories(ObjCCategoryDecl *HeadCat, - ObjCCategoryDecl *TailCat = 0) { - if (!HeadCat) { - assert(!TailCat); - return; - } - - if (!TailCat) { - TailCat = HeadCat; - while (TailCat->getNextClassCategory()) - TailCat = TailCat->getNextClassCategory(); - } - - if (!GlobHeadCat) { - GlobHeadCat = HeadCat; - GlobTailCat = TailCat; - } else { - ASTDeclReader::setNextObjCCategory(GlobTailCat, HeadCat); - GlobTailCat = TailCat; - } - - llvm::DenseSet<DeclarationName> Checked; - for (ObjCCategoryDecl *Cat = HeadCat, - *CatEnd = TailCat->getNextClassCategory(); - Cat != CatEnd; Cat = Cat->getNextClassCategory()) { - if (Checked.count(Cat->getDeclName())) - continue; - Checked.insert(Cat->getDeclName()); - checkForDuplicate(Cat); - } - } - - /// \brief Warns for duplicate categories that come from different modules. - void checkForDuplicate(ObjCCategoryDecl *Cat) { - DeclarationName Name = Cat->getDeclName(); - // Find the top category with the same name. We do not want to warn for - // duplicates along the established chain because there were already - // warnings for them when the module was created. We only want to warn for - // duplicates between non-dependent modules: - // - // MT // - // / \ // - // ML MR // - // - // We want to warn for duplicates between ML and MR,not between ML and MT. - // - // FIXME: We should not warn for duplicates in diamond: - // - // MT // - // / \ // - // ML MR // - // \ / // - // MB // - // - // If there are duplicates in ML/MR, there will be warning when creating - // MB *and* when importing MB. We should not warn when importing. - for (ObjCCategoryDecl *Next = Cat->getNextClassCategory(); Next; - Next = Next->getNextClassCategory()) { - if (Next->getDeclName() == Name) - Cat = Next; - } - - ObjCCategoryDecl *&PrevCat = NameCategoryMap[Name]; - if (!PrevCat) - PrevCat = Cat; + // If we've loaded all of the category information we care about from + // this module file, we're done. + if (M.Generation <= PreviousGeneration) + return true; + + // Map global ID of the definition down to the local ID used in this + // module file. If there is no such mapping, we'll find nothing here + // (or in any module it imports). + DeclID LocalID = Reader.mapGlobalIDToModuleFileGlobalID(M, InterfaceID); + if (!LocalID) + return true; - if (PrevCat != Cat) { - Reader.Diag(Cat->getLocation(), diag::warn_dup_category_def) - << Interface->getDeclName() << Name; - Reader.Diag(PrevCat->getLocation(), diag::note_previous_definition); + // Perform a binary search to find the local redeclarations for this + // declaration (if any). + const ObjCCategoriesInfo *Result + = std::lower_bound(M.ObjCCategoriesMap, + M.ObjCCategoriesMap + M.LocalNumObjCCategoriesInMap, + LocalID, CompareObjCCategoriesInfo()); + if (Result == M.ObjCCategoriesMap + M.LocalNumObjCCategoriesInMap || + Result->DefinitionID != LocalID) { + // We didn't find anything. If the class definition is in this module + // file, then the module files it depends on cannot have any categories, + // so suppress further lookup. + return Reader.isDeclIDFromModule(InterfaceID, M); } + + // We found something. Dig out all of the categories. + unsigned Offset = Result->Offset; + unsigned N = M.ObjCCategories[Offset]; + M.ObjCCategories[Offset++] = 0; // Don't try to deserialize again + for (unsigned I = 0; I != N; ++I) + add(cast_or_null<ObjCCategoryDecl>( + Reader.GetLocalDecl(M, M.ObjCCategories[Offset++]))); + return true; } - - ObjCCategoryDecl *getHeadCategory() const { return GlobHeadCat; } }; } -void ASTReader::loadObjCChainedCategories(serialization::GlobalDeclID ID, - ObjCInterfaceDecl *D) { - ObjCChainedCategoriesVisitor Visitor(*this, ID, D); - ModuleMgr.visit(ObjCChainedCategoriesVisitor::visit, &Visitor); - // Also add the categories that the interface already links to. - Visitor.addCategories(D->getCategoryList()); - D->setCategoryList(Visitor.getHeadCategory()); +void ASTReader::loadObjCCategories(serialization::GlobalDeclID ID, + ObjCInterfaceDecl *D, + unsigned PreviousGeneration) { + ObjCCategoriesVisitor Visitor(*this, ID, D, CategoriesDeserialized, + PreviousGeneration); + ModuleMgr.visit(ObjCCategoriesVisitor::visit, &Visitor); } void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile, diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index c5f9b2dc88..838ec35173 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -798,11 +798,12 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(KNOWN_NAMESPACES); RECORD(MODULE_OFFSET_MAP); RECORD(SOURCE_MANAGER_LINE_TABLE); - RECORD(OBJC_CHAINED_CATEGORIES); + RECORD(OBJC_CATEGORIES_MAP); RECORD(FILE_SORTED_DECLS); RECORD(IMPORTED_MODULES); RECORD(MERGED_DECLARATIONS); RECORD(LOCAL_REDECLARATIONS); + RECORD(OBJC_CATEGORIES); // SourceManager Block. BLOCK(SOURCE_MANAGER_BLOCK); @@ -2975,6 +2976,57 @@ void ASTWriter::WriteRedeclarations() { Stream.EmitRecord(LOCAL_REDECLARATIONS, LocalRedeclChains); } +void ASTWriter::WriteObjCCategories() { + llvm::SmallVector<ObjCCategoriesInfo, 2> CategoriesMap; + RecordData Categories; + + for (unsigned I = 0, N = ObjCClassesWithCategories.size(); I != N; ++I) { + unsigned Size = 0; + unsigned StartIndex = Categories.size(); + + ObjCInterfaceDecl *Class = ObjCClassesWithCategories[I]; + + // Allocate space for the size. + Categories.push_back(0); + + // Add the categories. + for (ObjCCategoryDecl *Cat = Class->getCategoryList(); + Cat; Cat = Cat->getNextClassCategory(), ++Size) { + assert(getDeclID(Cat) != 0 && "Bogus category"); + AddDeclRef(Cat, Categories); + } + + // Update the size. + Categories[StartIndex] = Size; + + // Record this interface -> category map. + ObjCCategoriesInfo CatInfo = { getDeclID(Class), StartIndex }; + CategoriesMap.push_back(CatInfo); + } + + // Sort the categories map by the definition ID, since the reader will be + // performing binary searches on this information. + llvm::array_pod_sort(CategoriesMap.begin(), CategoriesMap.end()); + + // Emit the categories map. + using namespace llvm; + llvm::BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(OBJC_CATEGORIES_MAP)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // # of entries + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned AbbrevID = Stream.EmitAbbrev(Abbrev); + + RecordData Record; + Record.push_back(OBJC_CATEGORIES_MAP); + Record.push_back(CategoriesMap.size()); + Stream.EmitRecordWithBlob(AbbrevID, Record, + reinterpret_cast<char*>(CategoriesMap.data()), + CategoriesMap.size() * sizeof(ObjCCategoriesInfo)); + + // Emit the category lists. + Stream.EmitRecord(OBJC_CATEGORIES, Categories); +} + void ASTWriter::WriteMergedDecls() { if (!Chain || Chain->MergedDecls.empty()) return; @@ -3492,9 +3544,9 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, WriteDeclUpdatesBlocks(); WriteDeclReplacementsBlock(); - WriteChainedObjCCategories(); WriteMergedDecls(); WriteRedeclarations(); + WriteObjCCategories(); // Some simple statistics Record.clear(); @@ -3573,29 +3625,6 @@ void ASTWriter::WriteDeclReplacementsBlock() { Stream.EmitRecord(DECL_REPLACEMENTS, Record); } -void ASTWriter::WriteChainedObjCCategories() { - if (LocalChainedObjCCategories.empty()) - return; - - RecordData Record; - for (SmallVector<ChainedObjCCategoriesData, 16>::iterator - I = LocalChainedObjCCategories.begin(), - E = LocalChainedObjCCategories.end(); I != E; ++I) { - ChainedObjCCategoriesData &Data = *I; - if (isRewritten(Data.Interface)) - continue; - - assert(Data.Interface->getCategoryList()); - serialization::DeclID - HeadCatID = getDeclID(Data.Interface->getCategoryList()); - - Record.push_back(getDeclID(Data.Interface)); - Record.push_back(HeadCatID); - Record.push_back(getDeclID(Data.TailCategory)); - } - Stream.EmitRecord(OBJC_CHAINED_CATEGORIES, Record); -} - void ASTWriter::AddSourceLocation(SourceLocation Loc, RecordDataImpl &Record) { Record.push_back(Loc.getRawEncoding()); } @@ -4447,13 +4476,10 @@ void ASTWriter::AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, assert(!WritingAST && "Already writing the AST!"); if (!IFD->isFromASTFile()) return; // Declaration not imported from PCH. - if (CatD->getNextClassCategory() && - !CatD->getNextClassCategory()->isFromASTFile()) - return; // We already recorded that the tail of a category chain should be - // attached to an interface. - - ChainedObjCCategoriesData Data = { IFD, CatD }; - LocalChainedObjCCategories.push_back(Data); + + assert(IFD->getDefinition() && "Category on a class without a definition?"); + ObjCClassesWithCategories.insert( + const_cast<ObjCInterfaceDecl *>(IFD->getDefinition())); } diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index a6fae4f20d..404eb88f59 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -481,7 +481,14 @@ void ASTDeclWriter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { P != PEnd; ++P) Writer.AddDeclRef(*P, Record); - Writer.AddDeclRef(D->getCategoryList(), Record); + if (ObjCCategoryDecl *Cat = D->getCategoryList()) { + // Ensure that we write out the set of categories for this class. + Writer.ObjCClassesWithCategories.insert(D); + + // Make sure that the categories get serialized. + for (; Cat; Cat = Cat->getNextClassCategory()) + (void)Writer.GetDeclRef(Cat); + } } Code = serialization::DECL_OBJC_INTERFACE; @@ -533,6 +540,7 @@ void ASTDeclWriter::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D) { void ASTDeclWriter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { VisitObjCContainerDecl(D); + Writer.AddSourceLocation(D->getCategoryNameLoc(), Record); Writer.AddDeclRef(D->getClassInterface(), Record); Record.push_back(D->protocol_size()); for (ObjCCategoryDecl::protocol_iterator @@ -542,9 +550,7 @@ void ASTDeclWriter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { PL = D->protocol_loc_begin(), PLEnd = D->protocol_loc_end(); PL != PLEnd; ++PL) Writer.AddSourceLocation(*PL, Record); - Writer.AddDeclRef(D->getNextClassCategory(), Record); Record.push_back(D->hasSynthBitfield()); - Writer.AddSourceLocation(D->getCategoryNameLoc(), Record); Code = serialization::DECL_OBJC_CATEGORY; } diff --git a/lib/Serialization/Module.cpp b/lib/Serialization/Module.cpp index bed545a0b2..16b95e2a68 100644 --- a/lib/Serialization/Module.cpp +++ b/lib/Serialization/Module.cpp @@ -36,6 +36,7 @@ ModuleFile::ModuleFile(ModuleKind Kind, unsigned Generation) DeclOffsets(0), BaseDeclID(0), LocalNumCXXBaseSpecifiers(0), CXXBaseSpecifiersOffsets(0), FileSortedDecls(0), RedeclarationsMap(0), LocalNumRedeclarationsInMap(0), + ObjCCategoriesMap(0), LocalNumObjCCategoriesInMap(0), LocalNumTypes(0), TypeOffsets(0), BaseTypeIndex(0), StatCache(0) {} diff --git a/test/Modules/Inputs/category_other.h b/test/Modules/Inputs/category_other.h new file mode 100644 index 0000000000..2c3f4794c2 --- /dev/null +++ b/test/Modules/Inputs/category_other.h @@ -0,0 +1,6 @@ +@import category_top; + +@interface Foo(Other) +-(void)other; +@end + diff --git a/test/Modules/Inputs/category_top.h b/test/Modules/Inputs/category_top.h index df6a6d3368..c9558b6c29 100644 --- a/test/Modules/Inputs/category_top.h +++ b/test/Modules/Inputs/category_top.h @@ -4,3 +4,11 @@ @interface Foo(Top) -(void)top; @end + +@interface Foo(Top2) +-(void)top2; +@end + +@interface Foo(Top3) +-(void)top3; +@end diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map index e09073f818..58dafca097 100644 --- a/test/Modules/Inputs/module.map +++ b/ |