]> granicus.if.org Git - clang/commitdiff
[PCH] When completing an objc forward reference, do not serialize the chain of its...
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Sat, 12 Nov 2011 21:07:46 +0000 (21:07 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Sat, 12 Nov 2011 21:07:46 +0000 (21:07 +0000)
it is going to be rewritten (and the chain will be serialized again), otherwise we may form a cycle in its
categories list when deserializing.

Also introduce ASTMutationListener::CompletedObjCForwardRef to notify that a forward reference
was completed; using Decl's isChangedSinceDeserialization/setChangedSinceDeserialization
is bug inducing and kinda gross, we should phase it out.

Fixes infinite loop in rdar://10418538.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144465 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ASTMutationListener.h
include/clang/AST/DeclObjC.h
include/clang/Serialization/ASTWriter.h
lib/AST/ASTImporter.cpp
lib/AST/DeclObjC.cpp
lib/Sema/SemaDeclObjC.cpp
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTWriter.cpp
test/PCH/chain-categories2.m [new file with mode: 0644]

index 793d3ee2b1fbd5452bff7ad1522e2f2a6ba735a4..7f5cbd1b019f90dec59b26d97c8b1ededaf2e2da 100644 (file)
@@ -24,6 +24,7 @@ namespace clang {
   class FunctionTemplateDecl;
   class ObjCCategoryDecl;
   class ObjCInterfaceDecl;
+  class ObjCContainerDecl;
 
 /// \brief An abstract interface that should be implemented by listeners
 /// that want to be notified when an AST entity gets modified after its
@@ -60,6 +61,9 @@ public:
   /// \brief A new objc category class was added for an interface.
   virtual void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD,
                                             const ObjCInterfaceDecl *IFD) {}
+
+  /// \brief A objc interface or protocol forward reference was completed.
+  virtual void CompletedObjCForwardRef(const ObjCContainerDecl *D) {}
 };
 
 } // end namespace clang
index 2a7849188b42241fada17ced0d8b49406f4a0446..4e65bf049dd63b1f66c7f5a5001dc04882d70603 100644 (file)
@@ -710,7 +710,8 @@ public:
   bool isInitiallyForwardDecl() const { return InitiallyForwardDecl; }
 
   bool isForwardDecl() const { return ForwardDecl; }
-  void setForwardDecl(bool val) { ForwardDecl = val; }
+
+  void completedForwardDecl();
 
   ObjCInterfaceDecl *getSuperClass() const {
     if (ExternallyCompleted)
@@ -1000,7 +1001,8 @@ public:
   bool isInitiallyForwardDecl() const { return InitiallyForwardDecl; }
 
   bool isForwardDecl() const { return isForwardProtoDecl; }
-  void setForwardDecl(bool val) { isForwardProtoDecl = val; }
+
+  void completedForwardDecl();
 
   // Location information, modeled after the Stmt API.
   SourceLocation getLocStart() const { return getAtStartLoc(); } // '@'protocol
index f6a8812b206bdc3d54d02e0179623f76e5348fb8..bff14ec71673973f319f906c0ef363067e6d6d6c 100644 (file)
@@ -394,7 +394,6 @@ private:
   void ResolveDeclUpdatesBlocks();
   void WriteDeclUpdatesBlocks();
   void WriteDeclReplacementsBlock();
-  void ResolveChainedObjCCategories();
   void WriteChainedObjCCategories();
   void WriteDeclContextVisibleUpdate(const DeclContext *DC);
   void WriteFPPragmaOptions(const FPOptions &Opts);
@@ -592,6 +591,10 @@ public:
     const_cast<Decl *>(D)->setChangedSinceDeserialization(false);
   }
 
+  bool isRewritten(const Decl *D) const {
+    return DeclsToRewrite.count(D);
+  }
+
   /// \brief Note that the identifier II occurs at the given offset
   /// within the identifier table.
   void SetIdentifierOffset(const IdentifierInfo *II, uint32_t Offset);
@@ -665,6 +668,7 @@ public:
   virtual void StaticDataMemberInstantiated(const VarDecl *D);
   virtual void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD,
                                             const ObjCInterfaceDecl *IFD);
+  virtual void CompletedObjCForwardRef(const ObjCContainerDecl *D);
 };
 
 /// \brief AST and semantic-analysis consumer that generates a
index f7a55a1474fe5e0e7c974c96092da3ac50c8ad79..318f6f28976032da2a2b138a1fc15b9e6597471a 100644 (file)
@@ -3111,9 +3111,10 @@ Decl *ASTNodeImporter::VisitObjCProtocolDecl(ObjCProtocolDecl *D) {
                                          Name.getAsIdentifierInfo(), Loc,
                                          Importer.Import(D->getAtStartLoc()),
                                          D->isInitiallyForwardDecl());
-      ToProto->setForwardDecl(D->isForwardDecl());
       ToProto->setLexicalDeclContext(LexicalDC);
       LexicalDC->addDeclInternal(ToProto);
+      if (D->isInitiallyForwardDecl() && !D->isForwardDecl())
+        ToProto->completedForwardDecl();
     }
     Importer.Imported(D, ToProto);
 
@@ -3172,11 +3173,12 @@ Decl *ASTNodeImporter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
       ToIface = ObjCInterfaceDecl::Create(Importer.getToContext(), DC,
                                           Importer.Import(D->getAtStartLoc()),
                                           Name.getAsIdentifierInfo(), Loc,
-                                          D->isForwardDecl(),
+                                          D->isInitiallyForwardDecl(),
                                           D->isImplicitInterfaceDecl());
-      ToIface->setForwardDecl(D->isForwardDecl());
       ToIface->setLexicalDeclContext(LexicalDC);
       LexicalDC->addDeclInternal(ToIface);
+      if (D->isInitiallyForwardDecl() && !D->isForwardDecl())
+        ToIface->completedForwardDecl();
     }
     Importer.Imported(D, ToIface);
 
index 5c4d25fd0260424c110f3473e6b7e01ade18f090..35ee7c6ccdb4f735d316d8fa7eb8b82158f624c3 100644 (file)
@@ -217,6 +217,13 @@ void ObjCInterfaceDecl::mergeClassExtensionProtocolList(
   AllReferencedProtocols.set(ProtocolRefs.data(), ProtocolRefs.size(), C);
 }
 
+void ObjCInterfaceDecl::completedForwardDecl() {
+  assert(isForwardDecl() && "Only valid to call for forward refs");
+  ForwardDecl = false;
+  if (ASTMutationListener *L = getASTContext().getASTMutationListener())
+    L->CompletedObjCForwardRef(this);
+}
+
 /// getFirstClassExtension - Find first class extension of the given class.
 ObjCCategoryDecl* ObjCInterfaceDecl::getFirstClassExtension() const {
   for (ObjCCategoryDecl *CDecl = getCategoryList(); CDecl;
@@ -913,6 +920,13 @@ ObjCMethodDecl *ObjCProtocolDecl::lookupMethod(Selector Sel,
   return NULL;
 }
 
+void ObjCProtocolDecl::completedForwardDecl() {
+  assert(isForwardDecl() && "Only valid to call for forward refs");
+  isForwardProtoDecl = false;
+  if (ASTMutationListener *L = getASTContext().getASTMutationListener())
+    L->CompletedObjCForwardRef(this);
+}
+
 //===----------------------------------------------------------------------===//
 // ObjCClassDecl
 //===----------------------------------------------------------------------===//
index 1632e092e3b23f4a4fa918b9f5cb5af4d1a8b28a..3d1f5bd0640c5eeb975ce1a53ec2a0c15c0d52ee 100644 (file)
@@ -377,18 +377,16 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc,
       return ActOnObjCContainerStartDefinition(IDecl);
     } else {
       IDecl->setLocation(ClassLoc);
-      IDecl->setForwardDecl(false);
       IDecl->setAtStartLoc(AtInterfaceLoc);
-      // If the forward decl was in a PCH, we need to write it again in a
-      // dependent AST file.
-      IDecl->setChangedSinceDeserialization(true);
       
       // Since this ObjCInterfaceDecl was created by a forward declaration,
       // we now add it to the DeclContext since it wasn't added before
       // (see ActOnForwardClassDeclaration).
       IDecl->setLexicalDeclContext(CurContext);
       CurContext->addDecl(IDecl);
-      
+
+      IDecl->completedForwardDecl();
+
       if (AttrList)
         ProcessDeclAttributeList(TUScope, IDecl, AttrList);
     }
@@ -588,19 +586,16 @@ Sema::ActOnStartProtocolInterface(SourceLocation AtProtoInterfaceLoc,
     // Make sure the cached decl gets a valid start location.
     PDecl->setAtStartLoc(AtProtoInterfaceLoc);
     PDecl->setLocation(ProtocolLoc);
-    PDecl->setForwardDecl(false);
     // Since this ObjCProtocolDecl was created by a forward declaration,
     // we now add it to the DeclContext since it wasn't added before
     PDecl->setLexicalDeclContext(CurContext);
     CurContext->addDecl(PDecl);
-    // Repeat in dependent AST files.
-    PDecl->setChangedSinceDeserialization(true);
+    PDecl->completedForwardDecl();
   } else {
     PDecl = ObjCProtocolDecl::Create(Context, CurContext, ProtocolName,
                                      ProtocolLoc, AtProtoInterfaceLoc,
                                      /*isForwardDecl=*/false);
     PushOnScopeChains(PDecl, TUScope);
-    PDecl->setForwardDecl(false);
   }
   if (AttrList)
     ProcessDeclAttributeList(TUScope, PDecl, AttrList);
@@ -925,7 +920,8 @@ Decl *Sema::ActOnStartClassImplementation(
     // Mark the interface as being completed, even if it was just as
     //   @class ....;
     // declaration; the user cannot reopen it.
-    IDecl->setForwardDecl(false);
+    if (IDecl->isForwardDecl())
+      IDecl->completedForwardDecl();
   }
 
   ObjCImplementationDecl* IMPDecl =
index d414c07290799445ad842dce0ec7c888ed5604bb..729cde0abfba8d613f84df2f076526b60093622a 100644 (file)
@@ -558,7 +558,7 @@ void ASTDeclReader::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID) {
   // We will rebuild this list lazily.
   ID->setIvarList(0);
   ID->InitiallyForwardDecl = Record[Idx++];
-  ID->setForwardDecl(Record[Idx++]);
+  ID->ForwardDecl = Record[Idx++];
   ID->setImplicitInterfaceDecl(Record[Idx++]);
   ID->setSuperClassLoc(ReadSourceLocation(Record, Idx));
   ID->setLocEnd(ReadSourceLocation(Record, Idx));
@@ -576,7 +576,7 @@ void ASTDeclReader::VisitObjCIvarDecl(ObjCIvarDecl *IVD) {
 void ASTDeclReader::VisitObjCProtocolDecl(ObjCProtocolDecl *PD) {
   VisitObjCContainerDecl(PD);
   PD->InitiallyForwardDecl = Record[Idx++];
-  PD->setForwardDecl(Record[Idx++]);
+  PD->isForwardProtoDecl = Record[Idx++];
   PD->setLocEnd(ReadSourceLocation(Record, Idx));
   unsigned NumProtoRefs = Record[Idx++];
   SmallVector<ObjCProtocolDecl *, 16> ProtoRefs;
index deaec0227113a0dd4be666b3936b8a3bf3f3bcca..a98c4cc82bb61c731c3f957893cafb67444fd493 100644 (file)
@@ -2995,7 +2995,6 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
   // Resolve any declaration pointers within the declaration updates block and
   // chained Objective-C categories block to declaration IDs.
   ResolveDeclUpdatesBlocks();
-  ResolveChainedObjCCategories();
   
   // Form the record of special types.
   RecordData SpecialTypes;
@@ -3183,7 +3182,7 @@ void ASTWriter::ResolveDeclUpdatesBlocks() {
     const Decl *D = I->first;
     UpdateRecord &URec = I->second;
     
-    if (DeclsToRewrite.count(D))
+    if (isRewritten(D))
       continue; // The decl will be written completely
 
     unsigned Idx = 0, N = URec.size();
@@ -3216,7 +3215,7 @@ void ASTWriter::WriteDeclUpdatesBlocks() {
     const Decl *D = I->first;
     UpdateRecord &URec = I->second;
 
-    if (DeclsToRewrite.count(D))
+    if (isRewritten(D))
       continue; // The decl will be written completely,no need to store updates.
 
     uint64_t Offset = Stream.GetCurrentBitNo();
@@ -3243,17 +3242,6 @@ void ASTWriter::WriteDeclReplacementsBlock() {
   Stream.EmitRecord(DECL_REPLACEMENTS, Record);
 }
 
-void ASTWriter::ResolveChainedObjCCategories() {
-  for (SmallVector<ChainedObjCCategoriesData, 16>::iterator
-       I = LocalChainedObjCCategories.begin(),
-       E = LocalChainedObjCCategories.end(); I != E; ++I) {
-    ChainedObjCCategoriesData &Data = *I;
-    Data.InterfaceID = GetDeclRef(Data.Interface);
-    Data.TailCategoryID = GetDeclRef(Data.TailCategory);
-  }
-
-}
-
 void ASTWriter::WriteChainedObjCCategories() {
   if (LocalChainedObjCCategories.empty())
     return;
@@ -3263,13 +3251,16 @@ void ASTWriter::WriteChainedObjCCategories() {
          I = LocalChainedObjCCategories.begin(),
          E = LocalChainedObjCCategories.end(); I != E; ++I) {
     ChainedObjCCategoriesData &Data = *I;
+    if (isRewritten(Data.Interface))
+      continue;
+
     serialization::DeclID
         HeadCatID = getDeclID(Data.Interface->getCategoryList());
     assert(HeadCatID != 0 && "Category not written ?");
 
-    Record.push_back(Data.InterfaceID);
+    Record.push_back(GetDeclRef(Data.Interface));
     Record.push_back(HeadCatID);
-    Record.push_back(Data.TailCategoryID);
+    Record.push_back(GetDeclRef(Data.TailCategory));
   }
   Stream.EmitRecord(OBJC_CHAINED_CATEGORIES, Record);
 }
@@ -4136,3 +4127,11 @@ void ASTWriter::AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD,
   ChainedObjCCategoriesData Data =  { IFD, CatD, 0, 0 };
   LocalChainedObjCCategories.push_back(Data);
 }
+
+void ASTWriter::CompletedObjCForwardRef(const ObjCContainerDecl *D) {
+  assert(!WritingAST && "Already writing the AST!");
+  if (!D->isFromASTFile())
+    return; // Declaration not imported from PCH.
+
+  RewriteDecl(D);
+}
diff --git a/test/PCH/chain-categories2.m b/test/PCH/chain-categories2.m
new file mode 100644 (file)
index 0000000..dcff3d4
--- /dev/null
@@ -0,0 +1,44 @@
+// Test that infinite loop in rdar://10418538 was fixed.
+
+// Without PCH
+// RUN: %clang_cc1 -fsyntax-only -verify -include %s -include %s %s
+
+// With PCH
+// RUN: %clang_cc1 -fsyntax-only -verify %s -chain-include %s -chain-include %s
+
+#ifndef HEADER1
+#define HEADER1
+//===----------------------------------------------------------------------===//
+// Primary header
+
+@class I;
+
+//===----------------------------------------------------------------------===//
+#elif !defined(HEADER2)
+#define HEADER2
+#if !defined(HEADER1)
+#error Header inclusion order messed up
+#endif
+
+//===----------------------------------------------------------------------===//
+// Dependent header
+
+@interface I
+@end
+
+@interface I(Cat1)
+@end
+
+@interface I(Cat2)
+@end
+
+//===----------------------------------------------------------------------===//
+#else
+//===----------------------------------------------------------------------===//
+
+void f(I* i) {
+  [i meth]; // expected-warning {{not found}}
+}
+
+//===----------------------------------------------------------------------===//
+#endif