[cfe-commits] r149112 - in /cfe/trunk: include/clang/Serialization/ASTBitCodes.h include/clang/Serialization/ASTReader.h include/clang/Serialization/ASTWriter.h include/clang/Serialization/Module.h lib/Serialization/ASTReader.cpp lib/Serialization/ASTReaderDecl.cpp lib/Serialization/ASTWriter.cpp lib/Serialization/ASTWriterDecl.cpp lib/Serialization/Module.cpp test/Modules/Inputs/category_other.h test/Modules/Inputs/category_top.h test/Modules/Inputs/module.map test/Modules/objc-categories.m

Douglas Gregor dgregor at apple.com
Thu Jan 26 17:47:08 PST 2012


Author: dgregor
Date: Thu Jan 26 19:47:08 2012
New Revision: 149112

URL: http://llvm.org/viewvc/llvm-project?rev=149112&view=rev
Log:
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.


Added:
    cfe/trunk/test/Modules/Inputs/category_other.h
Modified:
    cfe/trunk/include/clang/Serialization/ASTBitCodes.h
    cfe/trunk/include/clang/Serialization/ASTReader.h
    cfe/trunk/include/clang/Serialization/ASTWriter.h
    cfe/trunk/include/clang/Serialization/Module.h
    cfe/trunk/lib/Serialization/ASTReader.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriter.cpp
    cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
    cfe/trunk/lib/Serialization/Module.cpp
    cfe/trunk/test/Modules/Inputs/category_top.h
    cfe/trunk/test/Modules/Inputs/module.map
    cfe/trunk/test/Modules/objc-categories.m

Modified: cfe/trunk/include/clang/Serialization/ASTBitCodes.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTBitCodes.h?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/ASTBitCodes.h (original)
+++ cfe/trunk/include/clang/Serialization/ASTBitCodes.h Thu Jan 26 19:47:08 2012
@@ -444,9 +444,9 @@
       /// 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 @@
       ///
       /// 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 @@
       }
     };
 
+    /// \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

Modified: cfe/trunk/include/clang/Serialization/ASTReader.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTReader.h?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/ASTReader.h (original)
+++ cfe/trunk/include/clang/Serialization/ASTReader.h Thu Jan 26 19:47:08 2012
@@ -335,10 +335,6 @@
   /// 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 @@
   /// \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 @@
   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 @@
                                  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 @@
   /// \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);
 

Modified: cfe/trunk/include/clang/Serialization/ASTWriter.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTWriter.h?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/ASTWriter.h (original)
+++ cfe/trunk/include/clang/Serialization/ASTWriter.h Thu Jan 26 19:47:08 2012
@@ -284,17 +284,10 @@
   /// \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 @@
   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();
                         

Modified: cfe/trunk/include/clang/Serialization/Module.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/Module.h?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/Module.h (original)
+++ cfe/trunk/include/clang/Serialization/Module.h Thu Jan 26 19:47:08 2012
@@ -286,15 +286,6 @@
   /// 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 @@
   /// 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.

Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Thu Jan 26 19:47:08 2012
@@ -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

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Thu Jan 26 19:47:08 2012
@@ -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.
-
-      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;
-      }
+      // 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;
 
-      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);
+      // 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,

Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp Thu Jan 26 19:47:08 2012
@@ -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()));
 }
 
 

Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Thu Jan 26 19:47:08 2012
@@ -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;
 }
 

Modified: cfe/trunk/lib/Serialization/Module.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/Module.cpp?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/Module.cpp (original)
+++ cfe/trunk/lib/Serialization/Module.cpp Thu Jan 26 19:47:08 2012
@@ -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)
 {}
 

Added: cfe/trunk/test/Modules/Inputs/category_other.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/category_other.h?rev=149112&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/category_other.h (added)
+++ cfe/trunk/test/Modules/Inputs/category_other.h Thu Jan 26 19:47:08 2012
@@ -0,0 +1,6 @@
+ at import category_top;
+
+ at interface Foo(Other)
+-(void)other;
+ at end
+

Modified: cfe/trunk/test/Modules/Inputs/category_top.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/category_top.h?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/test/Modules/Inputs/category_top.h (original)
+++ cfe/trunk/test/Modules/Inputs/category_top.h Thu Jan 26 19:47:08 2012
@@ -4,3 +4,11 @@
 @interface Foo(Top)
 -(void)top;
 @end
+
+ at interface Foo(Top2)
+-(void)top2;
+ at end
+
+ at interface Foo(Top3)
+-(void)top3;
+ at end

Modified: cfe/trunk/test/Modules/Inputs/module.map
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/module.map?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/test/Modules/Inputs/module.map (original)
+++ cfe/trunk/test/Modules/Inputs/module.map Thu Jan 26 19:47:08 2012
@@ -33,6 +33,7 @@
   export category_left
   export category_right
 }
+module category_other { header "category_other.h" }
 module redeclarations_left { header "redeclarations_left.h" }
 module redeclarations_right { header "redeclarations_right.h" }
 module load_failure { header "load_failure.h" }

Modified: cfe/trunk/test/Modules/objc-categories.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/objc-categories.m?rev=149112&r1=149111&r2=149112&view=diff
==============================================================================
--- cfe/trunk/test/Modules/objc-categories.m (original)
+++ cfe/trunk/test/Modules/objc-categories.m Thu Jan 26 19:47:08 2012
@@ -3,11 +3,14 @@
 // RUN: %clang_cc1 -fmodules -fmodule-cache-path %t -x objective-c -fmodule-name=category_left -emit-module %S/Inputs/module.map
 // RUN: %clang_cc1 -fmodules -fmodule-cache-path %t -x objective-c -fmodule-name=category_right -emit-module %S/Inputs/module.map
 // RUN: %clang_cc1 -fmodules -fmodule-cache-path %t -x objective-c -fmodule-name=category_bottom -emit-module %S/Inputs/module.map
+// RUN: %clang_cc1 -fmodules -fmodule-cache-path %t -x objective-c -fmodule-name=category_other -emit-module %S/Inputs/module.map
 // RUN: %clang_cc1 -fmodules -fmodule-cache-path %t %s -verify
 
 @import category_bottom;
 
 
+
+
 // in category_left.h: expected-note {{previous definition}}
 
 @interface Foo(Source)
@@ -21,7 +24,17 @@
   [foo right1];
   [foo right2];
   [foo top];
+  [foo top2];
+  [foo top3];
 
   [leftFoo left];
   [leftFoo bottom];
 }
+
+// Load another module that also adds categories to Foo, verify that
+// we see those categories.
+ at import category_other;
+
+void test_other(Foo *foo) {
+  [foo other];
+}





More information about the cfe-commits mailing list