Reimplement (de-)serialization of Objective-C categories to eliminate
the direct serialization of the linked-list structure. Instead, use a
scheme similar to how we handle redeclarations, with redeclaration
lists on the side. This addresses several issues:
- In cases involving mixing and matching of many categories across
many modules, the linked-list structure would not be consistent
across different modules, and categories would get lost.
- If a module is loaded after the class definition and its other
categories have already been loaded, we wouldn't see any categories
in the newly-loaded module.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149112 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index d336eb1..42a848d 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -2325,20 +2325,21 @@
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 @@
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 @@
}
}
+ // 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 @@
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::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 @@
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 a1f1933..fbe861d 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -705,14 +705,14 @@
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::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 @@
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 @@
// 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 @@
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 @@
}
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.
+ // 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;
- 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;
+ // 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;
}
-
- 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 (PrevCat != Cat) {
- Reader.Diag(Cat->getLocation(), diag::warn_dup_category_def)
- << Interface->getDeclName() << Name;
- Reader.Diag(PrevCat->getLocation(), diag::note_previous_definition);
- }
- }
-
- 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 c5f9b2d..838ec35 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -798,11 +798,12 @@
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 @@
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 @@
WriteDeclUpdatesBlocks();
WriteDeclReplacementsBlock();
- WriteChainedObjCCategories();
WriteMergedDecls();
WriteRedeclarations();
+ WriteObjCCategories();
// Some simple statistics
Record.clear();
@@ -3573,29 +3625,6 @@
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 @@
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 a6fae4f..404eb88 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -481,7 +481,14 @@
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::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 @@
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 bed545a..16b95e2 100644
--- a/lib/Serialization/Module.cpp
+++ b/lib/Serialization/Module.cpp
@@ -36,6 +36,7 @@
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)
{}