This patch instantiates objects for forward protocols and in general handles use of 
protocols referenced in @protocol declarations.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@42191 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/AST/Decl.cpp b/AST/Decl.cpp
index 4b330ee..bde4b6b 100644
--- a/AST/Decl.cpp
+++ b/AST/Decl.cpp
@@ -29,6 +29,7 @@
 static unsigned nClassDecls = 0;
 static unsigned nMethodDecls = 0;
 static unsigned nProtocolDecls = 0;
+static unsigned nForwardProtocolDecls = 0;
 static unsigned nCategoryDecls = 0;
 static unsigned nIvarDecls = 0;
 
@@ -59,6 +60,8 @@
     return "ObjcProtoMethod";
   case ObjcProtocol:
     return "ObjcProtocol";
+  case ObjcForwardProtocol:
+    return "ObjcForwardProtocol"; 
   case Struct:
     return "Struct";
   case Union:
@@ -122,6 +125,9 @@
   fprintf(stderr, "    %d protocol decls, %d each (%d bytes)\n", 
 	  nProtocolDecls, (int)sizeof(ObjcProtocolDecl),
 	  int(nProtocolDecls*sizeof(ObjcProtocolDecl)));
+  fprintf(stderr, "    %d forward protocol decls, %d each (%d bytes)\n", 
+	  nForwardProtocolDecls, (int)sizeof(ObjcForwardProtocolDecl),
+	  int(nForwardProtocolDecls*sizeof(ObjcForwardProtocolDecl)));
   fprintf(stderr, "    %d category decls, %d each (%d bytes)\n", 
 	  nCategoryDecls, (int)sizeof(ObjcCategoryDecl),
 	  int(nCategoryDecls*sizeof(ObjcCategoryDecl)));
@@ -178,6 +184,9 @@
     case ObjcProtocol:
       nProtocolDecls++;
       break;
+    case ObjcForwardProtocol:
+      nForwardProtocolDecls++;
+      break;
     case ObjcCategory:
      nCategoryDecls++;
      break;
diff --git a/Parse/MinimalAction.cpp b/Parse/MinimalAction.cpp
index 7f1f239..c5be83b 100644
--- a/Parse/MinimalAction.cpp
+++ b/Parse/MinimalAction.cpp
@@ -109,6 +109,23 @@
   return 0;
 }
 
+/// ObjcForwardProtocolDeclaration - 
+/// Scope will always be top level file scope. 
+Action::DeclTy *
+MinimalAction::ObjcForwardProtocolDeclaration(Scope *S, SourceLocation AtClassLoc,
+                 IdentifierInfo **IdentList, unsigned NumElts) {
+  for (unsigned i = 0; i != NumElts; ++i) {
+    TypeNameInfo *TI =
+    new TypeNameInfo(1, IdentList[i]->getFETokenInfo<TypeNameInfo>());
+    
+    IdentList[i]->setFETokenInfo(TI);
+    
+    // Remember that this needs to be removed when the scope is popped.
+    S->AddDecl(IdentList[i]);
+  }
+  return 0;
+}
+
 /// PopScope - When a scope is popped, if any typedefs are now out-of-scope,
 /// they are removed from the IdentifierInfo::FETokenInfo field.
 void MinimalAction::PopScope(SourceLocation Loc, Scope *S) {
diff --git a/Parse/ParseObjc.cpp b/Parse/ParseObjc.cpp
index 0533660..116c2d8 100644
--- a/Parse/ParseObjc.cpp
+++ b/Parse/ParseObjc.cpp
@@ -713,13 +713,13 @@
   IdentifierInfo *protocolName = Tok.getIdentifierInfo();
   SourceLocation nameLoc = ConsumeToken();
   
-  if (Tok.getKind() == tok::semi) { // forward declaration.
+  llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs;
+  if (Tok.getKind() == tok::semi) { // forward declaration of one protocol.
     ConsumeToken();
-    return 0; // FIXME: add protocolName
+    ProtocolRefs.push_back(protocolName);
   }
   if (Tok.getKind() == tok::comma) { // list of forward declarations.
     // Parse the list of forward declarations.
-    llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs;
     ProtocolRefs.push_back(protocolName);
     
     while (1) {
@@ -738,10 +738,12 @@
     // Consume the ';'.
     if (ExpectAndConsume(tok::semi, diag::err_expected_semi_after, "@protocol"))
       return 0;
-    return 0; // FIXME
   }
+  if (ProtocolRefs.size() > 0)
+    return Actions.ObjcForwardProtocolDeclaration(CurScope, AtLoc,
+                                                  &ProtocolRefs[0], 
+                                                  ProtocolRefs.size());
   // Last, and definitely not least, parse a protocol declaration.
-  llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs;
   if (Tok.getKind() == tok::less) {
     if (ParseObjCProtocolReferences(ProtocolRefs))
       return 0;
diff --git a/Sema/Sema.h b/Sema/Sema.h
index 29a03d1..d99b1fc 100644
--- a/Sema/Sema.h
+++ b/Sema/Sema.h
@@ -370,6 +370,11 @@
   virtual DeclTy *ObjcClassDeclaration(Scope *S, SourceLocation AtClassLoc,
                                        IdentifierInfo **IdentList,
                                        unsigned NumElts);
+  
+  virtual DeclTy *ObjcForwardProtocolDeclaration(Scope *S, 
+                                                 SourceLocation AtProtocolLoc,
+                                                 IdentifierInfo **IdentList,
+                                                 unsigned NumElts);
 
   virtual void ObjcAddMethodsToClass(DeclTy *ClassDecl, 
 				     DeclTy **allMethods, unsigned allNum);
diff --git a/Sema/SemaDecl.cpp b/Sema/SemaDecl.cpp
index f46ec42..a82aea9 100644
--- a/Sema/SemaDecl.cpp
+++ b/Sema/SemaDecl.cpp
@@ -905,16 +905,66 @@
                 IdentifierInfo *ProtocolName, SourceLocation ProtocolLoc,
                 IdentifierInfo **ProtoRefNames, unsigned NumProtoRefs) {
   assert(ProtocolName && "Missing protocol identifier");
-  ObjcProtocolDecl *PDecl;
+  ObjcProtocolDecl *PDecl = Context.getObjCProtocolDecl(ProtocolName);
+  if (PDecl) {
+    // Protocol already seen. Better be a forward protocol declaration
+    if (!PDecl->getIsForwardProtoDecl())
+      Diag(ProtocolLoc, diag::err_duplicate_protocol_def, 
+           ProtocolName->getName());
+    else {
+      PDecl->setIsForwardProtoDecl(false);
+      PDecl->AllocReferencedProtocols(NumProtoRefs);
+    }
+  }
+  else {
+    PDecl = new ObjcProtocolDecl(AtProtoInterfaceLoc, NumProtoRefs, 
+                                 ProtocolName);
+    PDecl->setIsForwardProtoDecl(false);
+    // Chain & install the protocol decl into the identifier.
+    PDecl->setNext(ProtocolName->getFETokenInfo<ScopedDecl>());
+    ProtocolName->setFETokenInfo(PDecl);
+    Context.setObjCProtocolDecl(ProtocolName, PDecl);
+  }    
+  
+  /// Check then save referenced protocols
+  for (unsigned int i = 0; i != NumProtoRefs; i++) {
+    ObjcProtocolDecl* RefPDecl = Context.getObjCProtocolDecl(ProtoRefNames[i]);
+    if (!RefPDecl || RefPDecl->getIsForwardProtoDecl())
+      Diag(ProtocolLoc, diag::err_undef_protocolref,
+	   ProtoRefNames[i]->getName(),
+           ProtocolName->getName());
+    PDecl->setReferencedProtocols((int)i, RefPDecl);
+  }
 
-  PDecl = new ObjcProtocolDecl(AtProtoInterfaceLoc, ProtocolName);
-
-  // Chain & install the protocol decl into the identifier.
-  PDecl->setNext(ProtocolName->getFETokenInfo<ScopedDecl>());
-  ProtocolName->setFETokenInfo(PDecl);
   return PDecl;
 }
 
+/// ObjcForwardProtocolDeclaration - 
+/// Scope will always be top level file scope. 
+Action::DeclTy *
+Sema::ObjcForwardProtocolDeclaration(Scope *S, SourceLocation AtProtocolLoc,
+        IdentifierInfo **IdentList, unsigned NumElts) {
+  ObjcForwardProtocolDecl *FDecl = new ObjcForwardProtocolDecl(AtProtocolLoc, 
+                                                               NumElts);
+  
+  for (unsigned i = 0; i != NumElts; ++i) {
+    ObjcProtocolDecl *PDecl;
+    PDecl = Context.getObjCProtocolDecl(IdentList[i]);
+    if (!PDecl)  {// Already seen?
+      PDecl = new ObjcProtocolDecl(SourceLocation(), 0, IdentList[i], true);
+      // Chain & install the protocol decl into the identifier.
+      PDecl->setNext(IdentList[i]->getFETokenInfo<ScopedDecl>());
+      IdentList[i]->setFETokenInfo(PDecl);
+      Context.setObjCProtocolDecl(IdentList[i], PDecl);
+    }
+    // Remember that this needs to be removed when the scope is popped.
+    S->AddDecl(IdentList[i]);
+    
+    FDecl->setForwardProtocolDecl((int)i, PDecl);
+  }
+  return FDecl;
+}
+
 Sema::DeclTy *Sema::ObjcStartCatInterface(SourceLocation AtInterfaceLoc,
                       IdentifierInfo *ClassName, SourceLocation ClassLoc,
                       IdentifierInfo *CategoryName, SourceLocation CategoryLoc,
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 0e4bab1..fe7cc3c 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -38,6 +38,7 @@
   llvm::FoldingSet<FunctionTypeProto> FunctionTypeProtos;
   llvm::DenseMap<const RecordDecl*, const RecordLayout*> RecordLayoutInfo;
   llvm::DenseMap<const IdentifierInfo*, ObjcInterfaceDecl*> ClassNameInfo;
+  llvm::DenseMap<const IdentifierInfo*, ObjcProtocolDecl*> ProtocolNameInfo;
   RecordDecl *CFConstantStringTypeDecl;
 public:
   
@@ -165,6 +166,12 @@
                             ObjcInterfaceDecl* InterfaceDecl)
   { ClassNameInfo[ClassName] = InterfaceDecl; }
   
+  ObjcProtocolDecl* getObjCProtocolDecl(const IdentifierInfo* ProtocolName) 
+  { return ProtocolNameInfo[ProtocolName]; }
+  void setObjCProtocolDecl(const IdentifierInfo* ProtocolName,
+                            ObjcProtocolDecl* ProtocolDecl)
+  { ProtocolNameInfo[ProtocolName] = ProtocolDecl; }
+  
   //===--------------------------------------------------------------------===//
   //                            Type Operators
   //===--------------------------------------------------------------------===//
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 40607cf..0ba4581 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -38,7 +38,7 @@
     Function, BlockVariable, FileVariable, ParmVariable, EnumConstant,
     // Concrete sub-classes of TypeDecl
     Typedef, Struct, Union, Class, Enum, ObjcInterface, ObjcClass, ObjcMethod,
-    ObjcProtoMethod, ObjcProtocol, ObjcCategory,
+    ObjcProtoMethod, ObjcProtocol, ObjcForwardProtocol, ObjcCategory,
     // Concrete sub-class of Decl
     Field, ObjcIvar
   };
@@ -680,6 +680,10 @@
 };
 
 class ObjcProtocolDecl : public TypeDecl {
+  /// referenced protocols
+  ObjcProtocolDecl **ReferencedProtocols;  // Null if none
+  int NumReferencedProtocols;  // -1 if none
+  
   /// protocol instance methods
   ObjcMethodDecl **ProtoInsMethods;  // Null if not defined
   int NumProtoInsMethods;  // -1 if not defined
@@ -690,20 +694,62 @@
 
   bool isForwardProtoDecl; // declared with @protocol.
 public:
-  ObjcProtocolDecl(SourceLocation L, IdentifierInfo *Id, bool FD = false)
+  ObjcProtocolDecl(SourceLocation L, unsigned numRefProtos,
+                   IdentifierInfo *Id, bool FD = false)
     : TypeDecl(ObjcProtocol, L, Id, 0), 
+      ReferencedProtocols(0), NumReferencedProtocols(-1),
       ProtoInsMethods(0), NumProtoInsMethods(-1), 
       ProtoClsMethods(0), NumProtoClsMethods(-1),
-      isForwardProtoDecl(FD) { }
-
+      isForwardProtoDecl(FD) {
+        AllocReferencedProtocols(numRefProtos);
+      }
+  void AllocReferencedProtocols(unsigned numRefProtos) {
+    if (numRefProtos) {
+      ReferencedProtocols = new ObjcProtocolDecl*[numRefProtos];
+      memset(ReferencedProtocols, '\0', 
+             numRefProtos*sizeof(ObjcProtocolDecl*));
+      NumReferencedProtocols = numRefProtos;
+    }    
+  }
   void ObjcAddProtoMethods(ObjcMethodDecl **insMethods, unsigned numInsMembers,
                            ObjcMethodDecl **clsMethods, unsigned numClsMembers);
+  
+  void setReferencedProtocols(int idx, ObjcProtocolDecl *OID) {
+    assert((idx < NumReferencedProtocols) && "index out of range");
+    ReferencedProtocols[idx] = OID;
+  }
+  
+  
+  bool getIsForwardProtoDecl() const { return isForwardProtoDecl; }
+  void setIsForwardProtoDecl(bool val) { isForwardProtoDecl = val; }
 
   static bool classof(const Decl *D) {
     return D->getKind() == ObjcProtocol;
   }
   static bool classof(const ObjcProtocolDecl *D) { return true; }
 };
+  
+class ObjcForwardProtocolDecl : public TypeDecl {
+    ObjcProtocolDecl **ForwardProtocolDecls;   // Null if not defined.
+    int NumForwardProtocolDecls;               // -1 if not defined.
+  public:
+    ObjcForwardProtocolDecl(SourceLocation L, unsigned nElts)
+    : TypeDecl(ObjcForwardProtocol, L, 0, 0) { 
+      if (nElts) {
+        ForwardProtocolDecls = new ObjcProtocolDecl*[nElts];
+        memset(ForwardProtocolDecls, '\0', nElts*sizeof(ObjcProtocolDecl*));
+        NumForwardProtocolDecls = nElts;
+      }
+    }
+    void setForwardProtocolDecl(int idx, ObjcProtocolDecl *OID) {
+      assert((idx < NumForwardProtocolDecls) && "index out of range");
+      ForwardProtocolDecls[idx] = OID;
+    }
+    static bool classof(const Decl *D) {
+      return D->getKind() == ObjcForwardProtocol;
+    }
+    static bool classof(const ObjcForwardProtocolDecl *D) { return true; }
+};
 
 class ObjcCategoryDecl : public ScopedDecl {
   /// category instance methods
diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def
index 445f3bf..2556b23 100644
--- a/include/clang/Basic/DiagnosticKinds.def
+++ b/include/clang/Basic/DiagnosticKinds.def
@@ -412,7 +412,10 @@
      "cannot find interface declaration for '%0', superclass of '%1'")
 DIAG(err_duplicate_class_def, ERROR,
      "duplicate interface declaration for class '%0'")
-
+DIAG(err_undef_protocolref, ERROR,
+     "cannot find protocol definition for '%0', referenced by '%1'")
+DIAG(err_duplicate_protocol_def, ERROR,
+     "duplicate protocol declaration of '%0'")
 
 //===----------------------------------------------------------------------===//
 // Semantic Analysis
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h
index fab3bfe..dca0100 100644
--- a/include/clang/Parse/Action.h
+++ b/include/clang/Parse/Action.h
@@ -502,6 +502,14 @@
                                        unsigned NumElts) {
     return 0;
   }
+  
+  virtual DeclTy *ObjcForwardProtocolDeclaration(Scope *S, 
+                                                 SourceLocation AtProtocolLoc,
+                                                 IdentifierInfo **IdentList,
+                                                 unsigned NumElts) {
+    return 0;
+  }
+  
   virtual void ObjCStartCategoryInterface() { // FIXME
     return;
   }
@@ -548,6 +556,11 @@
                                        IdentifierInfo **IdentList,
                                        unsigned NumElts);
   
+  virtual DeclTy *ObjcForwardProtocolDeclaration(Scope *S, 
+                                                 SourceLocation AtProtocolLoc,
+                                                 IdentifierInfo **IdentList,
+                                                 unsigned NumElts);
+   
   virtual DeclTy *ObjcStartClassInterface(SourceLocation AtInterafceLoc,
                     IdentifierInfo *ClassName, SourceLocation ClassLoc,
                     IdentifierInfo *SuperName, SourceLocation SuperLoc,
diff --git a/test/Sema/protocol-test-2.m b/test/Sema/protocol-test-2.m
new file mode 100644
index 0000000..04ad3c6
--- /dev/null
+++ b/test/Sema/protocol-test-2.m
@@ -0,0 +1,29 @@
+@interface INTF1 @end
+
+@protocol p1,p2,p3;
+
+@protocol p1;
+
+@protocol PROTO1
+- (INTF1<p1>*) meth;
+@end
+
+@protocol PROTO2<p1> // expected-error {{cannot find protocol definition for 'p1', referenced by 'PROTO2'}}
+@end
+
+@protocol p1 @end
+
+@protocol PROTO<p1>
+@end
+
+@protocol PROTO<p1>	// expected-error {{duplicate protocol declaration of 'PROTO'}}
+@end
+
+@protocol PROTO3<p1, p1>
+@end
+
+@protocol p2 <p1>
+@end
+
+@protocol PROTO4 <p1, p2, PROTO, PROTO3, p3> // expected-error {{cannot find protocol definition for 'p3', referenced by 'PROTO4'}}
+@end