This patch computes composite type of two objective-c expressions
used in a conditional expression by finding the most-derived common
super class of the two and qualifies the resulting type by the
intersection of the protocl qualifier list of the two objective-c
pointer types. ( this is continuation of radar 7334235).



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85554 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index c70b364..dca55b6 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -837,6 +837,8 @@
                                llvm::SmallVectorImpl<ObjCIvarDecl*> &Ivars);
   unsigned CountSynthesizedIvars(const ObjCInterfaceDecl *OI);
   unsigned CountProtocolSynthesizedIvars(const ObjCProtocolDecl *PD);
+  void CollectInheritedProtocols(const Decl *CDecl,
+                          llvm::SmallVectorImpl<ObjCProtocolDecl*> &Protocols);
 
   //===--------------------------------------------------------------------===//
   //                            Type Operators
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index d60a820..f29f56e 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -873,6 +873,55 @@
   }
 }
 
+/// CollectInheritedProtocols - Collect all protocols in current class and
+/// those inherited by it.
+void ASTContext::CollectInheritedProtocols(const Decl *CDecl,
+                          llvm::SmallVectorImpl<ObjCProtocolDecl*> &Protocols) {
+  if (const ObjCInterfaceDecl *OI = dyn_cast<ObjCInterfaceDecl>(CDecl)) {
+    for (ObjCInterfaceDecl::protocol_iterator P = OI->protocol_begin(),
+         PE = OI->protocol_end(); P != PE; ++P) {
+      ObjCProtocolDecl *Proto = (*P);
+      Protocols.push_back(Proto);
+      for (ObjCProtocolDecl::protocol_iterator P = Proto->protocol_begin(),
+           PE = Proto->protocol_end(); P != PE; ++P)
+        CollectInheritedProtocols(*P, Protocols);
+      }
+    
+    // Categories of this Interface.
+    for (const ObjCCategoryDecl *CDeclChain = OI->getCategoryList(); 
+         CDeclChain; CDeclChain = CDeclChain->getNextClassCategory())
+      CollectInheritedProtocols(CDeclChain, Protocols);
+    if (ObjCInterfaceDecl *SD = OI->getSuperClass())
+      while (SD) {
+        CollectInheritedProtocols(SD, Protocols);
+        SD = SD->getSuperClass();
+      }
+    return;
+  }
+  if (const ObjCCategoryDecl *OC = dyn_cast<ObjCCategoryDecl>(CDecl)) {
+    for (ObjCInterfaceDecl::protocol_iterator P = OC->protocol_begin(),
+         PE = OC->protocol_end(); P != PE; ++P) {
+      ObjCProtocolDecl *Proto = (*P);
+      Protocols.push_back(Proto);
+      for (ObjCProtocolDecl::protocol_iterator P = Proto->protocol_begin(),
+           PE = Proto->protocol_end(); P != PE; ++P)
+        CollectInheritedProtocols(*P, Protocols);
+    }
+    return;
+  }
+  if (const ObjCProtocolDecl *OP = dyn_cast<ObjCProtocolDecl>(CDecl)) {
+    for (ObjCProtocolDecl::protocol_iterator P = OP->protocol_begin(),
+         PE = OP->protocol_end(); P != PE; ++P) {
+      ObjCProtocolDecl *Proto = (*P);
+      Protocols.push_back(Proto);
+      for (ObjCProtocolDecl::protocol_iterator P = Proto->protocol_begin(),
+           PE = Proto->protocol_end(); P != PE; ++P)
+        CollectInheritedProtocols(*P, Protocols);
+    }
+    return;
+  }
+}
+
 unsigned ASTContext::CountProtocolSynthesizedIvars(const ObjCProtocolDecl *PD) {
   unsigned count = 0;
   for (ObjCContainerDecl::prop_iterator I = PD->prop_begin(),
@@ -3859,6 +3908,48 @@
   return false;
 }
 
+/// getIntersectionOfProtocols - This routine finds the intersection of set
+/// of protocols inherited from two distinct objective-c pointer objects.
+/// It is used to build composite qualifier list of the composite type of
+/// the conditional expression involving two objective-c pointer objects.
+static 
+void getIntersectionOfProtocols(ASTContext &Context,
+                                const ObjCObjectPointerType *LHSOPT,
+                                const ObjCObjectPointerType *RHSOPT,
+      llvm::SmallVectorImpl<ObjCProtocolDecl *> &IntersectionOfProtocols) {
+  
+  const ObjCInterfaceType* LHS = LHSOPT->getInterfaceType();
+  const ObjCInterfaceType* RHS = RHSOPT->getInterfaceType();
+  
+  llvm::SmallPtrSet<ObjCProtocolDecl *, 8> InheritedProtocolSet;
+  unsigned LHSNumProtocols = LHS->getNumProtocols();
+  if (LHSNumProtocols > 0)
+    InheritedProtocolSet.insert(LHS->qual_begin(), LHS->qual_end());
+  else {
+    llvm::SmallVector<ObjCProtocolDecl *, 8> LHSInheritedProtocols;
+     Context.CollectInheritedProtocols(LHS->getDecl(), LHSInheritedProtocols);
+    InheritedProtocolSet.insert(LHSInheritedProtocols.begin(), 
+                                LHSInheritedProtocols.end());
+  }
+  
+  unsigned RHSNumProtocols = RHS->getNumProtocols();
+  if (RHSNumProtocols > 0) {
+    ObjCProtocolDecl **RHSProtocols = (ObjCProtocolDecl **)RHS->qual_begin();
+    for (unsigned i = 0; i < RHSNumProtocols; ++i)
+      if (InheritedProtocolSet.count(RHSProtocols[i]))
+        IntersectionOfProtocols.push_back(RHSProtocols[i]);
+  }
+  else {
+    llvm::SmallVector<ObjCProtocolDecl *, 8> RHSInheritedProtocols;
+    Context.CollectInheritedProtocols(RHS->getDecl(), RHSInheritedProtocols);
+    // FIXME. This may cause duplication of protocols in the list, but should
+    // be harmless.
+    for (unsigned i = 0, len = RHSInheritedProtocols.size(); i < len; ++i)
+      if (InheritedProtocolSet.count(RHSInheritedProtocols[i]))
+        IntersectionOfProtocols.push_back(RHSInheritedProtocols[i]);
+  }
+}
+
 /// areCommonBaseCompatible - Returns common base class of the two classes if
 /// one found. Note that this is O'2 algorithm. But it will be called as the
 /// last type comparison in a ?-exp of ObjC pointer types before a 
@@ -3874,8 +3965,17 @@
   while (const ObjCInterfaceDecl *LHSIDecl = LHS->getDecl()->getSuperClass()) {
     QualType LHSTy = getObjCInterfaceType(LHSIDecl);
     LHS = LHSTy->getAs<ObjCInterfaceType>();
-    if (canAssignObjCInterfaces(LHS, RHS))
-      return getObjCObjectPointerType(LHSTy);
+    if (canAssignObjCInterfaces(LHS, RHS)) {
+      llvm::SmallVector<ObjCProtocolDecl *, 8> IntersectionOfProtocols;
+      getIntersectionOfProtocols(*this, 
+                                 LHSOPT, RHSOPT, IntersectionOfProtocols);
+      if (IntersectionOfProtocols.empty())
+        LHSTy = getObjCObjectPointerType(LHSTy);
+      else
+        LHSTy = getObjCObjectPointerType(LHSTy, &IntersectionOfProtocols[0],
+                                                IntersectionOfProtocols.size());
+      return LHSTy;
+    }
   }
     
   return QualType();
diff --git a/test/SemaObjC/conditional-expr-6.m b/test/SemaObjC/conditional-expr-6.m
index 88e943f..bba51bb 100644
--- a/test/SemaObjC/conditional-expr-6.m
+++ b/test/SemaObjC/conditional-expr-6.m
@@ -1,17 +1,25 @@
 // RUN: clang-cc -fsyntax-only -verify %s
 
+@protocol MyProtocol @end
+
 @interface NSObject @end
 
-@interface NSInterm : NSObject
+@interface NSInterm : NSObject <MyProtocol>
 @end
 
 @interface NSArray : NSInterm 
 @end
 
-@interface NSSet : NSObject
+@interface NSSet : NSObject <MyProtocol>
 @end
 
 
+@interface N1 : NSObject
+@end
+
+@interface N1() <MyProtocol>
+@end
+
 NSObject* test (int argc) {
     NSArray *array = ((void*)0);
     NSSet *set = ((void*)0);
@@ -22,5 +30,22 @@
 NSObject* test1 (int argc) {
     NSArray *array = ((void*)0);
     NSSet *set = ((void*)0);
+    id <MyProtocol> instance = (argc) ? array : set;
+    id <MyProtocol> instance1 = (argc) ? set : array;
+
+    N1 *n1 = ((void*)0);
+    id <MyProtocol> instance2 = (argc) ? set : n1;
+    id <MyProtocol> instance3 = (argc) ? n1 : array;
+
+    NSArray<MyProtocol> *qual_array = ((void*)0);
+    id <MyProtocol> instance4 = (argc) ? array : qual_array;
+    id <MyProtocol> instance5 = (argc) ? qual_array : array;
+    NSSet<MyProtocol> *qual_set = ((void*)0);
+    id <MyProtocol> instance6 = (argc) ? qual_set : qual_array;
+    id <MyProtocol> instance7 = (argc) ? qual_set : array;
+    id <MyProtocol> instance8 = (argc) ? qual_array : set;
+    id <MyProtocol> instance9 = (argc) ? qual_array : qual_set;
+
+
     return (argc) ? array : set;
 }