r241548 - Implement the Objective-C __kindof type qualifier.

Douglas Gregor dgregor at apple.com
Mon Jul 6 20:58:43 PDT 2015


Author: dgregor
Date: Mon Jul  6 22:58:42 2015
New Revision: 241548

URL: http://llvm.org/viewvc/llvm-project?rev=241548&view=rev
Log:
Implement the Objective-C __kindof type qualifier.

The __kindof type qualifier can be applied to Objective-C object
(pointer) types to indicate id-like behavior, which includes implicit
"downcasting" of __kindof types to subclasses and id-like message-send
behavior. __kindof types provide better type bounds for substitutions
into unspecified generic types, which preserves more type information.

Added:
    cfe/trunk/test/PCH/objc_kindof.m
    cfe/trunk/test/SemaObjC/kindof.m
Modified:
    cfe/trunk/include/clang/AST/ASTContext.h
    cfe/trunk/include/clang/AST/Type.h
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Basic/TokenKinds.def
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/AST/ASTDiagnostic.cpp
    cfe/trunk/lib/AST/ASTImporter.cpp
    cfe/trunk/lib/AST/ItaniumMangle.cpp
    cfe/trunk/lib/AST/Type.cpp
    cfe/trunk/lib/AST/TypePrinter.cpp
    cfe/trunk/lib/Basic/IdentifierTable.cpp
    cfe/trunk/lib/Lex/PPMacroExpansion.cpp
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseTentative.cpp
    cfe/trunk/lib/Sema/SemaExprObjC.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaType.cpp
    cfe/trunk/lib/Serialization/ASTReader.cpp
    cfe/trunk/lib/Serialization/ASTWriter.cpp
    cfe/trunk/test/CodeGenObjCXX/mangle.mm
    cfe/trunk/test/SemaObjC/parameterized_classes_subst.m
    cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Mon Jul  6 22:58:42 2015
@@ -1195,14 +1195,15 @@ public:
   QualType getObjCInterfaceType(const ObjCInterfaceDecl *Decl,
                                 ObjCInterfaceDecl *PrevDecl = nullptr) const;
 
-  /// Legacy interface: cannot provide type arguments.
+  /// Legacy interface: cannot provide type arguments or __kindof.
   QualType getObjCObjectType(QualType Base,
                              ObjCProtocolDecl * const *Protocols,
                              unsigned NumProtocols) const;
 
   QualType getObjCObjectType(QualType Base,
                              ArrayRef<QualType> typeArgs,
-                             ArrayRef<ObjCProtocolDecl *> protocols) const;
+                             ArrayRef<ObjCProtocolDecl *> protocols,
+                             bool isKindOf) const;
   
   bool ObjCObjectAdoptsQTypeProtocols(QualType QT, ObjCInterfaceDecl *Decl);
   /// QIdProtocolsAdoptObjCObjectProtocols - Checks that protocols in

Modified: cfe/trunk/include/clang/AST/Type.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Type.h (original)
+++ cfe/trunk/include/clang/AST/Type.h Mon Jul  6 22:58:42 2015
@@ -1054,6 +1054,9 @@ public:
                                const DeclContext *dc,
                                ObjCSubstitutionContext context) const;
 
+  /// Strip Objective-C "__kindof" types from the given type.
+  QualType stripObjCKindOfType(const ASTContext &ctx) const;
+
 private:
   // These methods are implemented in a separate translation unit;
   // "static"-ize them to avoid creating temporary QualTypes in the
@@ -1353,9 +1356,12 @@ protected:
 
     /// NumProtocols - The number of protocols stored directly on this
     /// object type.
-    unsigned NumProtocols : 7;
+    unsigned NumProtocols : 6;
+
+    /// Whether this is a "kindof" type.
+    unsigned IsKindOf : 1;
   };
-  static_assert(NumTypeBits + 7 + 7 <= 32, "Does not fit in an unsigned");
+  static_assert(NumTypeBits + 7 + 6 + 1 <= 32, "Does not fit in an unsigned");
 
   class ReferenceTypeBitfields {
     friend class ReferenceType;
@@ -1649,7 +1655,27 @@ public:
   bool isObjCQualifiedClassType() const;        // Class<foo>
   bool isObjCObjectOrInterfaceType() const;
   bool isObjCIdType() const;                    // id
+
+  /// Whether the type is Objective-C 'id' or a __kindof type of an
+  /// object type, e.g., __kindof NSView * or __kindof id
+  /// <NSCopying>.
+  ///
+  /// \param bound Will be set to the bound on non-id subtype types,
+  /// which will be (possibly specialized) Objective-C class type, or
+  /// null for 'id.
+  bool isObjCIdOrObjectKindOfType(const ASTContext &ctx,
+                                  const ObjCObjectType *&bound) const;
+
   bool isObjCClassType() const;                 // Class
+
+  /// Whether the type is Objective-C 'Class' or a __kindof type of an
+  /// Class type, e.g., __kindof Class <NSCopying>.
+  ///
+  /// Unlike \c isObjCIdOrObjectKindOfType, there is no relevant bound
+  /// here because Objective-C's type system cannot express "a class
+  /// object for a subclass of NSFoo".
+  bool isObjCClassOrClassKindOfType() const;
+
   bool isBlockCompatibleObjCPointerType(ASTContext &ctx) const;
   bool isObjCSelType() const;                 // Class
   bool isObjCBuiltinType() const;               // 'id' or 'Class'
@@ -3581,6 +3607,7 @@ public:
     attr_nonnull,
     attr_nullable,
     attr_null_unspecified,
+    attr_objc_kindof,
   };
 
 private:
@@ -4514,7 +4541,8 @@ class ObjCObjectType : public Type {
 protected:
   ObjCObjectType(QualType Canonical, QualType Base,
                  ArrayRef<QualType> typeArgs,
-                 ArrayRef<ObjCProtocolDecl *> protocols);
+                 ArrayRef<ObjCProtocolDecl *> protocols,
+                 bool isKindOf);
 
   enum Nonce_ObjCInterface { Nonce_ObjCInterface };
   ObjCObjectType(enum Nonce_ObjCInterface)
@@ -4522,6 +4550,7 @@ protected:
       BaseType(QualType(this_(), 0)) {
     ObjCObjectTypeBits.NumProtocols = 0;
     ObjCObjectTypeBits.NumTypeArgs = 0;
+    ObjCObjectTypeBits.IsKindOf = 0;
   }
 
   void computeSuperClassTypeSlow() const;
@@ -4603,6 +4632,17 @@ public:
     return qual_begin()[I];
   }
 
+  /// Retrieve all of the protocol qualifiers.
+  ArrayRef<ObjCProtocolDecl *> getProtocols() const {
+    return ArrayRef<ObjCProtocolDecl *>(qual_begin(), getNumProtocols());
+  }
+
+  /// Whether this is a "__kindof" type as written.
+  bool isKindOfTypeAsWritten() const { return ObjCObjectTypeBits.IsKindOf; }
+
+  /// Whether this ia a "__kindof" type (semantically).
+  bool isKindOfType() const;
+
   /// Retrieve the type of the superclass of this object type.
   ///
   /// This operation substitutes any type arguments into the
@@ -4617,6 +4657,10 @@ public:
     return QualType(CachedSuperClassType.getPointer(), 0);
   }
 
+  /// Strip off the Objective-C "kindof" type and (with it) any
+  /// protocol qualifiers.
+  QualType stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const;
+
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
@@ -4638,15 +4682,17 @@ class ObjCObjectTypeImpl : public ObjCOb
 
   ObjCObjectTypeImpl(QualType Canonical, QualType Base,
                      ArrayRef<QualType> typeArgs,
-                     ArrayRef<ObjCProtocolDecl *> protocols)
-    : ObjCObjectType(Canonical, Base, typeArgs, protocols) {}
+                     ArrayRef<ObjCProtocolDecl *> protocols,
+                     bool isKindOf)
+    : ObjCObjectType(Canonical, Base, typeArgs, protocols, isKindOf) {}
 
 public:
   void Profile(llvm::FoldingSetNodeID &ID);
   static void Profile(llvm::FoldingSetNodeID &ID,
                       QualType Base,
                       ArrayRef<QualType> typeArgs,
-                      ArrayRef<ObjCProtocolDecl *> protocols);
+                      ArrayRef<ObjCProtocolDecl *> protocols,
+                      bool isKindOf);
 };
 
 inline QualType *ObjCObjectType::getTypeArgStorage() {
@@ -4798,6 +4844,12 @@ public:
     return getObjectType()->isObjCUnqualifiedClass();
   }
 
+  /// isObjCIdOrClassType - True if this is equivalent to the 'id' or
+  /// 'Class' type,
+  bool isObjCIdOrClassType() const {
+    return getObjectType()->isObjCUnqualifiedIdOrClass();
+  }
+
   /// isObjCQualifiedIdType - True if this is equivalent to 'id<P>' for some
   /// non-empty set of protocols.
   bool isObjCQualifiedIdType() const {
@@ -4810,6 +4862,9 @@ public:
     return getObjectType()->isObjCQualifiedClass();
   }
 
+  /// Whether this is a "__kindof" type.
+  bool isKindOfType() const { return getObjectType()->isKindOfType(); }
+
   /// Whether this type is specialized, meaning that it has type arguments.
   bool isSpecialized() const { return getObjectType()->isSpecialized(); }
 
@@ -4873,6 +4928,11 @@ public:
   /// null type if there is no superclass.
   QualType getSuperClassType() const;
 
+  /// Strip off the Objective-C "kindof" type and (with it) any
+  /// protocol qualifiers.
+  const ObjCObjectPointerType *stripObjCKindOfTypeAndQuals(
+                                 const ASTContext &ctx) const;
+
   void Profile(llvm::FoldingSetNodeID &ID) {
     Profile(ID, getPointeeType());
   }

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Mon Jul  6 22:58:42 2015
@@ -976,6 +976,11 @@ def TypeNullUnspecified : TypeAttr {
   let Documentation = [TypeNullUnspecifiedDocs];
 }
 
+def ObjCKindOf : TypeAttr {
+  let Spellings = [Keyword<"__kindof">];
+  let Documentation = [Undocumented];
+}
+
 def AssumeAligned : InheritableAttr {
   let Spellings = [GCC<"assume_aligned">];
   let Subjects = SubjectList<[ObjCMethod, Function]>;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Jul  6 22:58:42 2015
@@ -978,6 +978,12 @@ def warning_multiple_selectors: Warning<
   "several methods with selector %0 of mismatched types are found "
   "for the @selector expression">,
   InGroup<SelectorTypeMismatch>, DefaultIgnore;
+
+def err_objc_kindof_nonobject : Error<
+  "'__kindof' specifier cannot be applied to non-object type %0">;
+def err_objc_kindof_wrong_position : Error<
+  "'__kindof' type specifier must precede the declarator">;
+
 // C++ declarations
 def err_static_assert_expression_is_not_constant : Error<
   "static_assert expression is not an integral constant expression">;

Modified: cfe/trunk/include/clang/Basic/TokenKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/TokenKinds.def?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/TokenKinds.def (original)
+++ cfe/trunk/include/clang/Basic/TokenKinds.def Mon Jul  6 22:58:42 2015
@@ -526,6 +526,9 @@ KEYWORD(__bridge_transfer            , K
 KEYWORD(__bridge_retained            , KEYARC)
 KEYWORD(__bridge_retain              , KEYARC)
 
+// Objective-C keywords.
+KEYWORD(__kindof                 , KEYOBJC2)
+
 // Alternate spelling for various tokens.  There are GCC extensions in all
 // languages, but should not be disabled in strict conformance mode.
 ALIAS("__alignof__"  , __alignof  , KEYALL)

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Jul  6 22:58:42 2015
@@ -7240,6 +7240,10 @@ public:
                                SourceLocation ProtocolRAngleLoc,
                                bool FailOnError = false);
 
+  /// Check the application of the Objective-C '__kindof' qualifier to
+  /// the given type.
+  bool checkObjCKindOfType(QualType &type, SourceLocation loc);
+
   /// Ensure attributes are consistent with type.
   /// \param [in, out] Attributes The attributes to check; they will
   /// be modified to be consistent with \p PropertyTy.

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Mon Jul  6 22:58:42 2015
@@ -3615,21 +3615,24 @@ QualType ASTContext::getObjCObjectType(Q
                                        ObjCProtocolDecl * const *Protocols,
                                        unsigned NumProtocols) const {
   return getObjCObjectType(BaseType, { },
-                           llvm::makeArrayRef(Protocols, NumProtocols));
+                           llvm::makeArrayRef(Protocols, NumProtocols),
+                           /*isKindOf=*/false);
 }
 
 QualType ASTContext::getObjCObjectType(
            QualType baseType,
            ArrayRef<QualType> typeArgs,
-           ArrayRef<ObjCProtocolDecl *> protocols) const {
+           ArrayRef<ObjCProtocolDecl *> protocols,
+           bool isKindOf) const {
   // If the base type is an interface and there aren't any protocols or
   // type arguments to add, then the interface type will do just fine.
-  if (typeArgs.empty() && protocols.empty() && isa<ObjCInterfaceType>(baseType))
+  if (typeArgs.empty() && protocols.empty() && !isKindOf &&
+      isa<ObjCInterfaceType>(baseType))
     return baseType;
 
   // Look in the folding set for an existing type.
   llvm::FoldingSetNodeID ID;
-  ObjCObjectTypeImpl::Profile(ID, baseType, typeArgs, protocols);
+  ObjCObjectTypeImpl::Profile(ID, baseType, typeArgs, protocols, isKindOf);
   void *InsertPos = nullptr;
   if (ObjCObjectType *QT = ObjCObjectTypes.FindNodeOrInsertPos(ID, InsertPos))
     return QualType(QT, 0);
@@ -3681,7 +3684,7 @@ QualType ASTContext::getObjCObjectType(
     }
 
     canonical = getObjCObjectType(getCanonicalType(baseType), canonTypeArgs,
-                                  canonProtocols);
+                                  canonProtocols, isKindOf);
 
     // Regenerate InsertPos.
     ObjCObjectTypes.FindNodeOrInsertPos(ID, InsertPos);
@@ -3692,7 +3695,8 @@ QualType ASTContext::getObjCObjectType(
   size += protocols.size() * sizeof(ObjCProtocolDecl *);
   void *mem = Allocate(size, TypeAlignment);
   ObjCObjectTypeImpl *T =
-    new (mem) ObjCObjectTypeImpl(canonical, baseType, typeArgs, protocols);
+    new (mem) ObjCObjectTypeImpl(canonical, baseType, typeArgs, protocols,
+                                 isKindOf);
 
   Types.push_back(T);
   ObjCObjectTypes.InsertNode(T, InsertPos);
@@ -6775,18 +6779,36 @@ bool ASTContext::canAssignObjCInterfaces
       RHS->isObjCUnqualifiedIdOrClass())
     return true;
 
-  if (LHS->isObjCQualifiedId() || RHS->isObjCQualifiedId())
-    return ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
-                                             QualType(RHSOPT,0),
-                                             false);
-  
-  if (LHS->isObjCQualifiedClass() && RHS->isObjCQualifiedClass())
-    return ObjCQualifiedClassTypesAreCompatible(QualType(LHSOPT,0),
-                                                QualType(RHSOPT,0));
+  // Function object that propagates a successful result or handles
+  // __kindof types.
+  auto finish = [&](bool succeeded) -> bool {
+    if (succeeded)
+      return true;
+
+    if (!RHS->isKindOfType())
+      return false;
+
+    // Strip off __kindof and protocol qualifiers, then check whether
+    // we can assign the other way.
+    return canAssignObjCInterfaces(RHSOPT->stripObjCKindOfTypeAndQuals(*this),
+                                   LHSOPT->stripObjCKindOfTypeAndQuals(*this));
+  };
+
+  if (LHS->isObjCQualifiedId() || RHS->isObjCQualifiedId()) {
+    return finish(ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
+                                                    QualType(RHSOPT,0),
+                                                    false));
+  }
+  
+  if (LHS->isObjCQualifiedClass() && RHS->isObjCQualifiedClass()) {
+    return finish(ObjCQualifiedClassTypesAreCompatible(QualType(LHSOPT,0),
+                                                       QualType(RHSOPT,0)));
+  }
   
   // If we have 2 user-defined types, fall into that path.
-  if (LHS->getInterface() && RHS->getInterface())
-    return canAssignObjCInterfaces(LHS, RHS);
+  if (LHS->getInterface() && RHS->getInterface()) {
+    return finish(canAssignObjCInterfaces(LHS, RHS));
+  }
 
   return false;
 }
@@ -6800,26 +6822,46 @@ bool ASTContext::canAssignObjCInterfaces
                                          const ObjCObjectPointerType *LHSOPT,
                                          const ObjCObjectPointerType *RHSOPT,
                                          bool BlockReturnType) {
+
+  // Function object that propagates a successful result or handles
+  // __kindof types.
+  auto finish = [&](bool succeeded) -> bool {
+    if (succeeded)
+      return true;
+
+    const ObjCObjectPointerType *Expected = BlockReturnType ? RHSOPT : LHSOPT;
+    if (!Expected->isKindOfType())
+      return false;
+
+    // Strip off __kindof and protocol qualifiers, then check whether
+    // we can assign the other way.
+    return canAssignObjCInterfacesInBlockPointer(
+             RHSOPT->stripObjCKindOfTypeAndQuals(*this),
+             LHSOPT->stripObjCKindOfTypeAndQuals(*this),
+             BlockReturnType);
+  };
+
   if (RHSOPT->isObjCBuiltinType() || LHSOPT->isObjCIdType())
     return true;
   
   if (LHSOPT->isObjCBuiltinType()) {
-    return RHSOPT->isObjCBuiltinType() || RHSOPT->isObjCQualifiedIdType();
+    return finish(RHSOPT->isObjCBuiltinType() ||
+                  RHSOPT->isObjCQualifiedIdType());
   }
   
   if (LHSOPT->isObjCQualifiedIdType() || RHSOPT->isObjCQualifiedIdType())
-    return ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
-                                             QualType(RHSOPT,0),
-                                             false);
+    return finish(ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
+                                                    QualType(RHSOPT,0),
+                                                    false));
   
   const ObjCInterfaceType* LHS = LHSOPT->getInterfaceType();
   const ObjCInterfaceType* RHS = RHSOPT->getInterfaceType();
   if (LHS && RHS)  { // We have 2 user-defined types.
     if (LHS != RHS) {
       if (LHS->getDecl()->isSuperClassOf(RHS->getDecl()))
-        return BlockReturnType;
+        return finish(BlockReturnType);
       if (RHS->getDecl()->isSuperClassOf(LHS->getDecl()))
-        return !BlockReturnType;
+        return finish(!BlockReturnType);
     }
     else
       return true;
@@ -6903,13 +6945,19 @@ void getIntersectionOfProtocols(ASTConte
 
 // Check that the given Objective-C type argument lists are equivalent.
 static bool sameObjCTypeArgs(const ASTContext &ctx, ArrayRef<QualType> lhsArgs,
-                             ArrayRef<QualType> rhsArgs) {
+                             ArrayRef<QualType> rhsArgs,
+                             bool stripKindOf) {
   if (lhsArgs.size() != rhsArgs.size())
     return false;
 
   for (unsigned i = 0, n = lhsArgs.size(); i != n; ++i) {
-    if (!ctx.hasSameType(lhsArgs[i], rhsArgs[i]))
-      return false;
+    if (!ctx.hasSameType(lhsArgs[i], rhsArgs[i])) {
+      if (!stripKindOf ||
+          !ctx.hasSameType(lhsArgs[i].stripObjCKindOfType(ctx),
+                           rhsArgs[i].stripObjCKindOfType(ctx))) {
+        return false;
+      }
+    }
   }
 
   return true;
@@ -6941,7 +6989,8 @@ QualType ASTContext::areCommonBaseCompat
       bool anyChanges = false;
       if (LHS->isSpecialized() && RHS->isSpecialized()) {
         // Both have type arguments, compare them.
-        if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs()))
+        if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs(),
+                              /*stripKindOf=*/true))
           return QualType();
       } else if (LHS->isSpecialized() != RHS->isSpecialized()) {
         // If only one has type arguments, the result will not have type
@@ -6960,7 +7009,8 @@ QualType ASTContext::areCommonBaseCompat
       // If anything in the LHS will have changed, build a new result type.
       if (anyChanges) {
         QualType Result = getObjCInterfaceType(LHS->getInterface());
-        Result = getObjCObjectType(Result, LHSTypeArgs, Protocols);
+        Result = getObjCObjectType(Result, LHSTypeArgs, Protocols,
+                                   LHS->isKindOfType());
         return getObjCObjectPointerType(Result);
       }
 
@@ -6987,7 +7037,8 @@ QualType ASTContext::areCommonBaseCompat
       bool anyChanges = false;
       if (LHS->isSpecialized() && RHS->isSpecialized()) {
         // Both have type arguments, compare them.
-        if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs()))
+        if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs(),
+                              /*stripKindOf=*/true))
           return QualType();
       } else if (LHS->isSpecialized() != RHS->isSpecialized()) {
         // If only one has type arguments, the result will not have type
@@ -7005,7 +7056,8 @@ QualType ASTContext::areCommonBaseCompat
 
       if (anyChanges) {
         QualType Result = getObjCInterfaceType(RHS->getInterface());
-        Result = getObjCObjectType(Result, RHSTypeArgs, Protocols);
+        Result = getObjCObjectType(Result, RHSTypeArgs, Protocols,
+                                   RHS->isKindOfType());
         return getObjCObjectPointerType(Result);
       }
 
@@ -7075,7 +7127,8 @@ bool ASTContext::canAssignObjCInterfaces
 
     // If the RHS is specializd, compare type arguments.
     if (RHSSuper->isSpecialized() &&
-        !sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHSSuper->getTypeArgs())) {
+        !sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHSSuper->getTypeArgs(),
+                          /*stripKindOf=*/true)) {
       return false;
     }
   }

Modified: cfe/trunk/lib/AST/ASTDiagnostic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTDiagnostic.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTDiagnostic.cpp (original)
+++ cfe/trunk/lib/AST/ASTDiagnostic.cpp Mon Jul  6 22:58:42 2015
@@ -139,7 +139,8 @@ break; \
       QualType BaseType = Desugar(Context, Ty->getBaseType(), ShouldAKA);
       QT = Context.getObjCObjectType(BaseType, Ty->getTypeArgsAsWritten(),
                                      llvm::makeArrayRef(Ty->qual_begin(),
-                                                        Ty->getNumProtocols()));
+                                                        Ty->getNumProtocols()),
+                                     Ty->isKindOfTypeAsWritten());
     }
   }
 

Modified: cfe/trunk/lib/AST/ASTImporter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTImporter.cpp (original)
+++ cfe/trunk/lib/AST/ASTImporter.cpp Mon Jul  6 22:58:42 2015
@@ -1853,7 +1853,6 @@ QualType ASTNodeImporter::VisitObjCObjec
     TypeArgs.push_back(ImportedTypeArg);
   }
 
-
   SmallVector<ObjCProtocolDecl *, 4> Protocols;
   for (auto *P : T->quals()) {
     ObjCProtocolDecl *Protocol
@@ -1864,7 +1863,8 @@ QualType ASTNodeImporter::VisitObjCObjec
   }
 
   return Importer.getToContext().getObjCObjectType(ToBaseType, TypeArgs,
-                                                   Protocols);
+                                                   Protocols,
+                                                   T->isKindOfTypeAsWritten());
 }
 
 QualType

Modified: cfe/trunk/lib/AST/ItaniumMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ItaniumMangle.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ItaniumMangle.cpp (original)
+++ cfe/trunk/lib/AST/ItaniumMangle.cpp Mon Jul  6 22:58:42 2015
@@ -2379,6 +2379,10 @@ void CXXNameMangler::mangleType(const Ob
 }
 
 void CXXNameMangler::mangleType(const ObjCObjectType *T) {
+  // Treat __kindof as a vendor extended type qualifier.
+  if (T->isKindOfType())
+    Out << "U8__kindof";
+
   if (!T->qual_empty()) {
     // Mangle protocol qualifiers.
     SmallString<64> QualStr;
@@ -2391,7 +2395,16 @@ void CXXNameMangler::mangleType(const Ob
     QualOS.flush();
     Out << 'U' << QualStr.size() << QualStr;
   }
+
   mangleType(T->getBaseType());
+
+  if (T->isSpecialized()) {
+    // Mangle type arguments as I <type>+ E
+    Out << 'I';
+    for (auto typeArg : T->getTypeArgs())
+      mangleType(typeArg);
+    Out << 'E';
+  }
 }
 
 void CXXNameMangler::mangleType(const BlockPointerType *T) {

Modified: cfe/trunk/lib/AST/Type.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Type.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Type.cpp (original)
+++ cfe/trunk/lib/AST/Type.cpp Mon Jul  6 22:58:42 2015
@@ -466,15 +466,61 @@ const RecordType *Type::getAsUnionType()
   return nullptr;
 }
 
+bool Type::isObjCIdOrObjectKindOfType(const ASTContext &ctx,
+                                      const ObjCObjectType *&bound) const {
+  bound = nullptr;
+
+  const ObjCObjectPointerType *OPT = getAs<ObjCObjectPointerType>();
+  if (!OPT)
+    return false;
+
+  // Easy case: id.
+  if (OPT->isObjCIdType())
+    return true;
+
+  // If it's not a __kindof type, reject it now.
+  if (!OPT->isKindOfType())
+    return false;
+
+  // If it's Class or qualified Class, it's not an object type.
+  if (OPT->isObjCClassType() || OPT->isObjCQualifiedClassType())
+    return false;
+
+  // Figure out the type bound for the __kindof type.
+  bound = OPT->getObjectType()->stripObjCKindOfTypeAndQuals(ctx)
+            ->getAs<ObjCObjectType>();
+  return true;
+}
+
+bool Type::isObjCClassOrClassKindOfType() const {
+  const ObjCObjectPointerType *OPT = getAs<ObjCObjectPointerType>();
+  if (!OPT)
+    return false;
+
+  // Easy case: Class.
+  if (OPT->isObjCClassType())
+    return true;
+
+  // If it's not a __kindof type, reject it now.
+  if (!OPT->isKindOfType())
+    return false;
+
+  // If it's Class or qualified Class, it's a class __kindof type.
+  return OPT->isObjCClassType() || OPT->isObjCQualifiedClassType();
+}
+
 ObjCObjectType::ObjCObjectType(QualType Canonical, QualType Base,
                                ArrayRef<QualType> typeArgs,
-                               ArrayRef<ObjCProtocolDecl *> protocols)
+                               ArrayRef<ObjCProtocolDecl *> protocols,
+                               bool isKindOf)
   : Type(ObjCObject, Canonical, Base->isDependentType(), 
          Base->isInstantiationDependentType(), 
          Base->isVariablyModifiedType(), 
          Base->containsUnexpandedParameterPack()),
     BaseType(Base) 
 {
+  ObjCObjectTypeBits.IsKindOf = isKindOf;
+
   ObjCObjectTypeBits.NumTypeArgs = typeArgs.size();
   assert(getTypeArgsAsWritten().size() == typeArgs.size() &&
          "bitfield overflow in type argument count");
@@ -535,6 +581,52 @@ ArrayRef<QualType> ObjCObjectType::getTy
   return { };
 }
 
+bool ObjCObjectType::isKindOfType() const {
+  if (isKindOfTypeAsWritten())
+    return true;
+
+  // Look at the base type, which might have type arguments.
+  if (auto objcObject = getBaseType()->getAs<ObjCObjectType>()) {
+    // Terminate when we reach an interface type.
+    if (isa<ObjCInterfaceType>(objcObject))
+      return false;
+
+    return objcObject->isKindOfType();
+  }
+
+  // Not a "__kindof" type.
+  return false;
+}
+
+QualType ObjCObjectType::stripObjCKindOfTypeAndQuals(
+           const ASTContext &ctx) const {
+  if (!isKindOfType() && qual_empty())
+    return QualType(this, 0);
+
+  // Recursively strip __kindof.
+  SplitQualType splitBaseType = getBaseType().split();
+  QualType baseType(splitBaseType.Ty, 0);
+  if (const ObjCObjectType *baseObj
+        = splitBaseType.Ty->getAs<ObjCObjectType>()) {
+    baseType = baseObj->stripObjCKindOfTypeAndQuals(ctx);
+  }
+
+  return ctx.getObjCObjectType(ctx.getQualifiedType(baseType,
+                                                    splitBaseType.Quals),
+                               getTypeArgsAsWritten(),
+                               /*protocols=*/{ },
+                               /*isKindOf=*/false);
+}
+
+const ObjCObjectPointerType *ObjCObjectPointerType::stripObjCKindOfTypeAndQuals(
+                               const ASTContext &ctx) const {
+  if (!isKindOfType() && qual_empty())
+    return this;
+
+  QualType obj = getObjectType()->stripObjCKindOfTypeAndQuals(ctx);
+  return ctx.getObjCObjectPointerType(obj)->castAs<ObjCObjectPointerType>();
+}
+
 namespace {
 
 /// Perform a simple type transformation that does not change the
@@ -888,7 +980,8 @@ QualType simpleTransform(ASTContext &ctx
 
       return Ctx.getObjCObjectType(baseType, typeArgs, 
                                    llvm::makeArrayRef(T->qual_begin(),
-                                                      T->getNumProtocols()));
+                                                      T->getNumProtocols()),
+                                   T->isKindOfTypeAsWritten());
     }
 
     TRIVIAL_TYPE_CLASS(ObjCInterface)
@@ -971,18 +1064,28 @@ QualType QualType::substObjCTypeArgs(
                                       splitType.Quals);
 
         case ObjCSubstitutionContext::Result:
-        case ObjCSubstitutionContext::Property:
-          // Substitute 'id' or 'Class', as appropriate.
-
-          // If the underlying type is based on 'Class', substitute 'Class'.
-          if (typeParam->getUnderlyingType()->isObjCClassType() ||
-              typeParam->getUnderlyingType()->isObjCQualifiedClassType()) {
-            return ctx.getQualifiedType(ctx.getObjCClassType(),
+        case ObjCSubstitutionContext::Property: {
+          // Substitute the __kindof form of the underlying type.
+          const auto *objPtr = typeParam->getUnderlyingType()
+            ->castAs<ObjCObjectPointerType>();
+
+          // __kindof types, id, and Class don't need an additional
+          // __kindof.
+          if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType())
+            return ctx.getQualifiedType(typeParam->getUnderlyingType(),
                                         splitType.Quals);
-          }
 
-          // Otherwise, substitute 'id'.
-          return ctx.getQualifiedType(ctx.getObjCIdType(), splitType.Quals);
+          // Add __kindof.
+          const auto *obj = objPtr->getObjectType();
+          QualType resultTy = ctx.getObjCObjectType(obj->getBaseType(),
+                                                    obj->getTypeArgsAsWritten(),
+                                                    obj->getProtocols(),
+                                                    /*isKindOf=*/true);
+
+          // Rebuild object pointer type.
+          resultTy = ctx.getObjCObjectPointerType(resultTy);
+          return ctx.getQualifiedType(resultTy, splitType.Quals);
+        }
         }
       }
     }
@@ -1086,8 +1189,10 @@ QualType QualType::substObjCTypeArgs(
                                            objcObjectType->getNumProtocols());
             if (typeArgs.empty() &&
                 context != ObjCSubstitutionContext::Superclass) {
-              return ctx.getObjCObjectType(objcObjectType->getBaseType(), { },
-                                           protocols);
+              return ctx.getObjCObjectType(
+                       objcObjectType->getBaseType(), { },
+                       protocols,
+                       objcObjectType->isKindOfTypeAsWritten());
             }
 
             anyChanged = true;
@@ -1101,7 +1206,8 @@ QualType QualType::substObjCTypeArgs(
                                          objcObjectType->qual_begin(),
                                          objcObjectType->getNumProtocols());
           return ctx.getObjCObjectType(objcObjectType->getBaseType(),
-                                       newTypeArgs, protocols);
+                                       newTypeArgs, protocols,
+                                       objcObjectType->isKindOfTypeAsWritten());
         }
       }
 
@@ -1121,6 +1227,30 @@ QualType QualType::substObjCMemberType(Q
   return *this;
 }
 
+QualType QualType::stripObjCKindOfType(const ASTContext &constCtx) const {
+  // FIXME: Because ASTContext::getAttributedType() is non-const.
+  auto &ctx = const_cast<ASTContext &>(constCtx);
+  return simpleTransform(ctx, *this,
+           [&](QualType type) -> QualType {
+             SplitQualType splitType = type.split();
+             if (auto *objType = splitType.Ty->getAs<ObjCObjectType>()) {
+               if (!objType->isKindOfType())
+                 return type;
+
+               QualType baseType
+                 = objType->getBaseType().stripObjCKindOfType(ctx);
+               return ctx.getQualifiedType(
+                        ctx.getObjCObjectType(baseType,
+                                              objType->getTypeArgsAsWritten(),
+                                              objType->getProtocols(),
+                                              /*isKindOf=*/false),
+                        splitType.Quals);
+             }
+
+             return type;
+           });
+}
+
 Optional<ArrayRef<QualType>> Type::getObjCSubstitutions(
                                const DeclContext *dc) const {
   // Look through method scopes.
@@ -2745,7 +2875,9 @@ bool AttributedType::isCallingConv() con
   case attr_nonnull:
   case attr_nullable:
   case attr_null_unspecified:
+  case attr_objc_kindof:
     return false;
+
   case attr_pcs:
   case attr_pcs_vfp:
   case attr_cdecl:
@@ -2895,7 +3027,8 @@ QualifierCollector::apply(const ASTConte
 void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID,
                                  QualType BaseType,
                                  ArrayRef<QualType> typeArgs,
-                                 ArrayRef<ObjCProtocolDecl *> protocols) {
+                                 ArrayRef<ObjCProtocolDecl *> protocols,
+                                 bool isKindOf) {
   ID.AddPointer(BaseType.getAsOpaquePtr());
   ID.AddInteger(typeArgs.size());
   for (auto typeArg : typeArgs)
@@ -2903,11 +3036,13 @@ void ObjCObjectTypeImpl::Profile(llvm::F
   ID.AddInteger(protocols.size());
   for (auto proto : protocols)
     ID.AddPointer(proto);
+  ID.AddBoolean(isKindOf);
 }
 
 void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID) {
-  Profile(ID, getBaseType(), getTypeArgs(), 
-          llvm::makeArrayRef(qual_begin(), getNumProtocols()));
+  Profile(ID, getBaseType(), getTypeArgsAsWritten(),
+          llvm::makeArrayRef(qual_begin(), getNumProtocols()),
+          isKindOfTypeAsWritten());
 }
 
 namespace {

Modified: cfe/trunk/lib/AST/TypePrinter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TypePrinter.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/AST/TypePrinter.cpp (original)
+++ cfe/trunk/lib/AST/TypePrinter.cpp Mon Jul  6 22:58:42 2015
@@ -1129,6 +1129,9 @@ void TypePrinter::printAttributedBefore(
       T->getAttrKind() == AttributedType::attr_objc_ownership)
     return printBefore(T->getEquivalentType(), OS);
 
+  if (T->getAttrKind() == AttributedType::attr_objc_kindof)
+    OS << "__kindof ";
+
   printBefore(T->getModifiedType(), OS);
 
   if (T->isMSTypeSpec()) {
@@ -1165,6 +1168,9 @@ void TypePrinter::printAttributedAfter(c
       T->getAttrKind() == AttributedType::attr_objc_ownership)
     return printAfter(T->getEquivalentType(), OS);
 
+  if (T->getAttrKind() == AttributedType::attr_objc_kindof)
+    return;
+
   // TODO: not all attributes are GCC-style attributes.
   if (T->isMSTypeSpec())
     return;
@@ -1310,9 +1316,13 @@ void TypePrinter::printObjCInterfaceAfte
 
 void TypePrinter::printObjCObjectBefore(const ObjCObjectType *T,
                                         raw_ostream &OS) {
-  if (T->qual_empty() && T->isUnspecializedAsWritten())
+  if (T->qual_empty() && T->isUnspecializedAsWritten() &&
+      !T->isKindOfTypeAsWritten())
     return printBefore(T->getBaseType(), OS);
 
+  if (T->isKindOfTypeAsWritten())
+    OS << "__kindof ";
+
   print(T->getBaseType(), OS, StringRef());
 
   if (T->isSpecializedAsWritten()) {
@@ -1346,7 +1356,8 @@ void TypePrinter::printObjCObjectBefore(
 }
 void TypePrinter::printObjCObjectAfter(const ObjCObjectType *T,
                                         raw_ostream &OS) {
-  if (T->qual_empty() && T->isUnspecializedAsWritten())
+  if (T->qual_empty() && T->isUnspecializedAsWritten() &&
+      !T->isKindOfTypeAsWritten())
     return printAfter(T->getBaseType(), OS);
 }
 

Modified: cfe/trunk/lib/Basic/IdentifierTable.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/IdentifierTable.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/IdentifierTable.cpp (original)
+++ cfe/trunk/lib/Basic/IdentifierTable.cpp Mon Jul  6 22:58:42 2015
@@ -109,7 +109,8 @@ namespace {
     WCHARSUPPORT = 0x04000,
     HALFSUPPORT = 0x08000,
     KEYCONCEPTS = 0x10000,
-    KEYALL = (0x1ffff & ~KEYNOMS18 &
+    KEYOBJC2    = 0x20000,
+    KEYALL = (0x3ffff & ~KEYNOMS18 &
               ~KEYNOOPENCL) // KEYNOMS18 and KEYNOOPENCL are used to exclude.
   };
 
@@ -144,6 +145,7 @@ static KeywordStatus getKeywordStatus(co
   // in non-arc mode.
   if (LangOpts.ObjC2 && (Flags & KEYARC)) return KS_Enabled;
   if (LangOpts.ConceptsTS && (Flags & KEYCONCEPTS)) return KS_Enabled;
+  if (LangOpts.ObjC2 && (Flags & KEYOBJC2)) return KS_Enabled;
   if (LangOpts.CPlusPlus && (Flags & KEYCXX11)) return KS_Future;
   return KS_Disabled;
 }

Modified: cfe/trunk/lib/Lex/PPMacroExpansion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPMacroExpansion.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/PPMacroExpansion.cpp (original)
+++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp Mon Jul  6 22:58:42 2015
@@ -1088,6 +1088,7 @@ static bool HasFeature(const Preprocesso
       .Case("objc_default_synthesize_properties", LangOpts.ObjC2)
       .Case("objc_fixed_enum", LangOpts.ObjC2)
       .Case("objc_instancetype", LangOpts.ObjC2)
+      .Case("objc_kindof", LangOpts.ObjC2)
       .Case("objc_modules", LangOpts.ObjC2 && LangOpts.Modules)
       .Case("objc_nonfragile_abi", LangOpts.ObjCRuntime.isNonFragile())
       .Case("objc_property_explicit_atomic",

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Mon Jul  6 22:58:42 2015
@@ -2601,6 +2601,7 @@ Parser::DiagnoseMissingSemiAfterTagDefin
 /// [C11]   alignment-specifier declaration-specifiers[opt]
 /// [GNU]   attributes declaration-specifiers[opt]
 /// [Clang] '__module_private__' declaration-specifiers[opt]
+/// [ObjC1] '__kindof' declaration-specifiers[opt]
 ///
 ///       storage-class-specifier: [C99 6.7.1]
 ///         'typedef'
@@ -3083,6 +3084,13 @@ void Parser::ParseDeclarationSpecifiers(
       ParseNullabilityTypeSpecifiers(DS.getAttributes());
       continue;
 
+    // Objective-C 'kindof' types.
+    case tok::kw___kindof:
+      DS.getAttributes().addNew(Tok.getIdentifierInfo(), Loc, nullptr, Loc,
+                                nullptr, 0, AttributeList::AS_Keyword);
+      (void)ConsumeToken();
+      continue;
+
     // storage-class-specifier
     case tok::kw_typedef:
       isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_typedef, Loc,
@@ -4345,6 +4353,8 @@ bool Parser::isTypeSpecifierQualifier()
   case tok::kw__Nullable:
   case tok::kw__Null_unspecified:
 
+  case tok::kw___kindof:
+
   case tok::kw___private:
   case tok::kw___local:
   case tok::kw___global:
@@ -4525,6 +4535,8 @@ bool Parser::isDeclarationSpecifier(bool
   case tok::kw__Nullable:
   case tok::kw__Null_unspecified:
 
+  case tok::kw___kindof:
+
   case tok::kw___private:
   case tok::kw___local:
   case tok::kw___global:
@@ -4762,6 +4774,13 @@ void Parser::ParseTypeQualifierListOpt(D
       ParseNullabilityTypeSpecifiers(DS.getAttributes());
       continue;
 
+    // Objective-C 'kindof' types.
+    case tok::kw___kindof:
+      DS.getAttributes().addNew(Tok.getIdentifierInfo(), Loc, nullptr, Loc,
+                                nullptr, 0, AttributeList::AS_Keyword);
+      (void)ConsumeToken();
+      continue;
+
     case tok::kw___attribute:
       if (AttrReqs & AR_GNUAttributesParsedAndRejected)
         // When GNU attributes are expressly forbidden, diagnose their usage.

Modified: cfe/trunk/lib/Parse/ParseTentative.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTentative.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseTentative.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTentative.cpp Mon Jul  6 22:58:42 2015
@@ -1281,6 +1281,7 @@ Parser::isCXXDeclarationSpecifier(Parser
   case tok::kw__Nonnull:
   case tok::kw__Nullable:
   case tok::kw__Null_unspecified:
+  case tok::kw___kindof:
     return TPResult::True;
 
     // Borland

Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Mon Jul  6 22:58:42 2015
@@ -962,7 +962,8 @@ ExprResult Sema::BuildObjCDictionaryLite
               Context.getObjCObjectType(Context.ObjCBuiltinIdTy, { },
                                         llvm::makeArrayRef(
                                           (ObjCProtocolDecl**) PQ,
-                                          1));
+                                          1),
+                                        false);
             QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying);
           }
         }
@@ -2620,35 +2621,41 @@ ExprResult Sema::BuildInstanceMessage(Ex
   // of the more detailed type-checking on the receiver.
 
   if (!Method) {
-    // Handle messages to id.
-    bool receiverIsId = ReceiverType->isObjCIdType();
-    if (receiverIsId || ReceiverType->isBlockPointerType() ||
+    // Handle messages to id and __kindof types (where we use the
+    // global method pool).
+    // FIXME: The type bound is currently ignored by lookup in the
+    // global pool.
+    const ObjCObjectType *typeBound = nullptr;
+    bool receiverIsIdLike = ReceiverType->isObjCIdOrObjectKindOfType(Context,
+                                                                     typeBound);
+    if (receiverIsIdLike || ReceiverType->isBlockPointerType() ||
         (Receiver && Context.isObjCNSObjectType(Receiver->getType()))) {
       Method = LookupInstanceMethodInGlobalPool(Sel, 
                                                 SourceRange(LBracLoc, RBracLoc),
-                                                receiverIsId);
+                                                receiverIsIdLike);
       if (!Method)
         Method = LookupFactoryMethodInGlobalPool(Sel, 
                                                  SourceRange(LBracLoc,RBracLoc),
-                                                 receiverIsId);
+                                                 receiverIsIdLike);
       if (Method) {
         if (ObjCMethodDecl *BestMethod =
               SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod()))
           Method = BestMethod;
         if (!AreMultipleMethodsInGlobalPool(Sel, Method,
                                             SourceRange(LBracLoc, RBracLoc),
-                                            receiverIsId)) {
+                                            receiverIsIdLike)) {
           DiagnoseUseOfDecl(Method, SelLoc);
         }
       }
-    } else if (ReceiverType->isObjCClassType() ||
+    } else if (ReceiverType->isObjCClassOrClassKindOfType() ||
                ReceiverType->isObjCQualifiedClassType()) {
       // Handle messages to Class.
       // We allow sending a message to a qualified Class ("Class<foo>"), which
       // is ok as long as one of the protocols implements the selector (if not,
       // warn).
-      if (const ObjCObjectPointerType *QClassTy 
-            = ReceiverType->getAsObjCQualifiedClassType()) {
+      if (!ReceiverType->isObjCClassOrClassKindOfType()) {
+        const ObjCObjectPointerType *QClassTy
+          = ReceiverType->getAsObjCQualifiedClassType();
         // Search protocols for class methods.
         Method = LookupMethodInQualifiedType(Sel, QClassTy, false);
         if (!Method) {

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Mon Jul  6 22:58:42 2015
@@ -2152,23 +2152,7 @@ bool Sema::isObjCPointerConversion(QualT
                                        FromObjCPtr->getPointeeType()))
       return false;
 
-    // Check for compatible 
-    // Objective C++: We're able to convert between "id" or "Class" and a
-    // pointer to any interface (in both directions).
-    if (ToObjCPtr->isObjCBuiltinType() && FromObjCPtr->isObjCBuiltinType()) {
-      ConvertedType = AdoptQualifiers(Context, ToType, FromQualifiers);
-      return true;
-    }
-    // Conversions with Objective-C's id<...>.
-    if ((FromObjCPtr->isObjCQualifiedIdType() ||
-         ToObjCPtr->isObjCQualifiedIdType()) &&
-        Context.ObjCQualifiedIdTypesAreCompatible(ToType, FromType,
-                                                  /*compare=*/false)) {
-      ConvertedType = AdoptQualifiers(Context, ToType, FromQualifiers);
-      return true;
-    }
-    // Objective C++: We're able to convert from a pointer to an
-    // interface to a pointer to a different interface.
+    // Conversion between Objective-C pointers.
     if (Context.canAssignObjCInterfaces(ToObjCPtr, FromObjCPtr)) {
       const ObjCInterfaceType* LHS = ToObjCPtr->getInterfaceType();
       const ObjCInterfaceType* RHS = FromObjCPtr->getInterfaceType();

Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Mon Jul  6 22:58:42 2015
@@ -643,6 +643,9 @@ static void distributeTypeAttrsFromDecla
 
     NULLABILITY_TYPE_ATTRS_CASELIST:
       // Nullability specifiers cannot go after the declarator-id.
+
+    // Objective-C __kindof does not get distributed.
+    case AttributeList::AT_ObjCKindOf:
       continue;
 
     default:
@@ -928,7 +931,7 @@ static QualType applyObjCTypeArgs(Sema &
   }
 
   // Success. Form the specialized type.
-  return S.Context.getObjCObjectType(type, finalTypeArgs, { });
+  return S.Context.getObjCObjectType(type, finalTypeArgs, { }, false);
 }
 
 /// Apply Objective-C protocol qualifiers to the given type.
@@ -944,7 +947,8 @@ static QualType applyObjCProtocolQualifi
 
     return ctx.getObjCObjectType(objT->getBaseType(),
                                  objT->getTypeArgsAsWritten(),
-                                 protocols);
+                                 protocols,
+                                 objT->isKindOfTypeAsWritten());
   }
 
   if (type->isObjCObjectType()) {
@@ -953,18 +957,22 @@ static QualType applyObjCProtocolQualifi
 
     // FIXME: Check for protocols to which the class type is already
     // known to conform.
-    return ctx.getObjCObjectType(type, { }, protocols);
+    return ctx.getObjCObjectType(type, { }, protocols, false);
   }
 
   // id<protocol-list>
   if (type->isObjCIdType()) {
-    type = ctx.getObjCObjectType(ctx.ObjCBuiltinIdTy, { }, protocols);
+    const ObjCObjectPointerType *objPtr = type->castAs<ObjCObjectPointerType>();
+    type = ctx.getObjCObjectType(ctx.ObjCBuiltinIdTy, { }, protocols,
+                                 objPtr->isKindOfType());
     return ctx.getObjCObjectPointerType(type);
   }
 
   // Class<protocol-list>
   if (type->isObjCClassType()) {
-    type = ctx.getObjCObjectType(ctx.ObjCBuiltinClassTy, { }, protocols);
+    const ObjCObjectPointerType *objPtr = type->castAs<ObjCObjectPointerType>();
+    type = ctx.getObjCObjectType(ctx.ObjCBuiltinClassTy, { }, protocols,
+                                 objPtr->isKindOfType());
     return ctx.getObjCObjectPointerType(type);
   }
 
@@ -1021,7 +1029,8 @@ TypeResult Sema::actOnObjCProtocolQualif
                       Context.ObjCBuiltinIdTy, { },
                       llvm::makeArrayRef(
                         (ObjCProtocolDecl * const *)protocols.data(),
-                        protocols.size()));
+                        protocols.size()),
+                      false);
   Result = Context.getObjCObjectPointerType(Result);
 
   TypeSourceInfo *ResultTInfo = Context.CreateTypeSourceInfo(Result);
@@ -4430,6 +4439,8 @@ static AttributeList::Kind getAttrListKi
     return AttributeList::AT_TypeNullable;
   case AttributedType::attr_null_unspecified:
     return AttributeList::AT_TypeNullUnspecified;
+  case AttributedType::attr_objc_kindof:
+    return AttributeList::AT_ObjCKindOf;
   }
   llvm_unreachable("unexpected attribute kind!");
 }
@@ -5514,6 +5525,44 @@ bool Sema::checkNullabilityTypeSpecifier
   return false;
 }
 
+bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) {
+  // Find out if it's an Objective-C object or object pointer type;
+  const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>();
+  const ObjCObjectType *objType = ptrType ? ptrType->getObjectType() 
+                                          : type->getAs<ObjCObjectType>();
+
+  // If not, we can't apply __kindof.
+  if (!objType) {
+    // FIXME: Handle dependent types that aren't yet object types.
+    Diag(loc, diag::err_objc_kindof_nonobject)
+      << type;
+    return true;
+  }
+
+  // Rebuild the "equivalent" type, which pushes __kindof down into
+  // the object type.
+  QualType equivType = Context.getObjCObjectType(objType->getBaseType(),
+                                                 objType->getTypeArgsAsWritten(),
+                                                 objType->getProtocols(),
+                                                 /*isKindOf=*/true);
+
+  // If we started with an object pointer type, rebuild it.
+  if (ptrType) {
+    equivType = Context.getObjCObjectPointerType(equivType);
+    if (auto nullability = type->getNullability(Context)) {
+      auto attrKind = AttributedType::getNullabilityAttrKind(*nullability);
+      equivType = Context.getAttributedType(attrKind, equivType, equivType);
+    }
+  }
+
+  // Build the attributed type to record where __kindof occurred.
+  type = Context.getAttributedType(AttributedType::attr_objc_kindof, 
+                                   type,
+                                   equivType);
+
+  return false;
+}
+
 /// Map a nullability attribute kind to a nullability kind.
 static NullabilityKind mapNullabilityAttrKind(AttributeList::Kind kind) {
   switch (kind) {
@@ -6124,6 +6173,7 @@ static void processTypeAttrs(TypeProcess
         attr.setUsedAsTypeAttr();
       break;
 
+
     NULLABILITY_TYPE_ATTRS_CASELIST:
       // Either add nullability here or try to distribute it.  We
       // don't want to distribute the nullability specifier past any
@@ -6142,6 +6192,28 @@ static void processTypeAttrs(TypeProcess
       }
       break;
 
+    case AttributeList::AT_ObjCKindOf:
+      // '__kindof' must be part of the decl-specifiers.
+      switch (TAL) {
+      case TAL_DeclSpec:
+        break;
+
+      case TAL_DeclChunk:
+      case TAL_DeclName:
+        state.getSema().Diag(attr.getLoc(),
+                             diag::err_objc_kindof_wrong_position)
+          << FixItHint::CreateRemoval(attr.getLoc())
+          << FixItHint::CreateInsertion(
+               state.getDeclarator().getDeclSpec().getLocStart(), "__kindof ");
+        break;
+      }
+
+      // Apply it regardless.
+      if (state.getSema().checkObjCKindOfType(type, attr.getLoc()))
+        attr.setInvalid();
+      attr.setUsedAsTypeAttr();
+      break;
+
     case AttributeList::AT_NSReturnsRetained:
       if (!state.getSema().getLangOpts().ObjCAutoRefCount)
         break;

Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Mon Jul  6 22:58:42 2015
@@ -5271,7 +5271,8 @@ QualType ASTReader::readTypeRecord(unsig
     SmallVector<ObjCProtocolDecl*, 4> Protos;
     for (unsigned I = 0; I != NumProtos; ++I)
       Protos.push_back(ReadDeclAs<ObjCProtocolDecl>(*Loc.F, Record, Idx));
-    return Context.getObjCObjectType(Base, TypeArgs, Protos);
+    bool IsKindOf = Record[Idx++];
+    return Context.getObjCObjectType(Base, TypeArgs, Protos, IsKindOf);
   }
 
   case TYPE_OBJC_OBJECT_POINTER: {

Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp Mon Jul  6 22:58:42 2015
@@ -427,6 +427,7 @@ void ASTTypeWriter::VisitObjCObjectType(
   Record.push_back(T->getNumProtocols());
   for (const auto *I : T->quals())
     Writer.AddDeclRef(I, Record);
+  Record.push_back(T->isKindOfTypeAsWritten());
   Code = TYPE_OBJC_OBJECT;
 }
 

Modified: cfe/trunk/test/CodeGenObjCXX/mangle.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjCXX/mangle.mm?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjCXX/mangle.mm (original)
+++ cfe/trunk/test/CodeGenObjCXX/mangle.mm Mon Jul  6 22:58:42 2015
@@ -98,3 +98,18 @@ template<> void X<A*>::f() {}
 // CHECK-LABEL: define void @_ZN1XIP1AE1fEv
 template<> void X<A<P>*>::f() {}
 // CHECK-LABEL: define void @_ZN1XIPU11objcproto1P1AE1fEv
+
+// CHECK-LABEL: define void @_Z12kindof_test2PU8__kindof5Test2
+void kindof_test2(__kindof Test2 *t2) { }
+
+ at interface Parameterized<T, U> : A
+ at end
+
+// CHECK-LABEL: define void @_Z19parameterized_test1P13ParameterizedIP1AP4TestE
+void parameterized_test1(Parameterized<A *, Test *> *p) {}
+
+// CHECK-LABEL: define void @_Z19parameterized_test2PU8__kindof13ParameterizedIP1AP4TestE
+void parameterized_test2(__kindof Parameterized<A *, Test *> *p) {}
+
+// CHECK-LABEL: define void @_Z19parameterized_test3P13Parameterized
+void parameterized_test3(Parameterized *p) {}

Added: cfe/trunk/test/PCH/objc_kindof.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/objc_kindof.m?rev=241548&view=auto
==============================================================================
--- cfe/trunk/test/PCH/objc_kindof.m (added)
+++ cfe/trunk/test/PCH/objc_kindof.m Mon Jul  6 22:58:42 2015
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -emit-pch %s -o %t
+// RUN: %clang_cc1 -include-pch %t -verify %s
+
+#ifndef HEADER_INCLUDED
+
+#define HEADER_INCLUDED
+ at protocol NSObject
+ at end
+
+ at protocol NSCopying
+ at end
+
+__attribute__((objc_root_class))
+ at interface NSObject <NSObject>
+ at end
+
+ at interface NSString : NSObject <NSCopying>
+ at end
+
+ at interface NSMutableString : NSString
+ at end
+
+ at interface NSNumber : NSObject <NSCopying>
+ at end
+
+extern __kindof NSObject <NSCopying> *kindof_NSObject_NSCopying;
+
+#else
+void testPrettyPrint(int *ip) {
+  ip = kindof_NSObject_NSCopying; // expected-warning{{from '__kindof NSObject<NSCopying> *'}}
+}
+
+#endif

Added: cfe/trunk/test/SemaObjC/kindof.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/kindof.m?rev=241548&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjC/kindof.m (added)
+++ cfe/trunk/test/SemaObjC/kindof.m Mon Jul  6 22:58:42 2015
@@ -0,0 +1,304 @@
+// RUN: %clang_cc1 -fblocks -fsyntax-only %s -verify
+
+// Tests Objective-C 'kindof' types.
+
+#if !__has_feature(objc_kindof)
+#error does not support __kindof
+#endif
+
+ at protocol NSObject
+ at end
+
+ at protocol NSCopying
+- (id)copy;
++ (Class)classCopy;
+ at end
+
+ at protocol NSRandomProto
+- (void)randomMethod;
++ (void)randomClassMethod;
+ at end
+
+__attribute__((objc_root_class))
+ at interface NSObject <NSObject>
+- (NSObject *)retain;
+ at end
+
+ at interface NSString : NSObject <NSCopying>
+- (NSString *)stringByAppendingString:(NSString *)string;
++ (instancetype)string;
+ at end
+
+ at interface NSMutableString : NSString
+- (void)appendString:(NSString *)string;
+ at end
+
+ at interface NSNumber : NSObject <NSCopying>
+- (NSNumber *)numberByAddingNumber:(NSNumber *)number;
+ at end
+
+// ---------------------------------------------------------------------------
+// Parsing and semantic analysis for __kindof
+// ---------------------------------------------------------------------------
+
+// Test proper application of __kindof.
+typedef __kindof NSObject *typedef1;
+typedef NSObject __kindof *typedef2;
+typedef __kindof NSObject<NSCopying> typedef3;
+typedef NSObject<NSCopying> __kindof *typedef4;
+typedef __kindof id<NSCopying> typedef5;
+typedef __kindof Class<NSCopying> typedef6;
+
+// Test redundancy of __kindof.
+typedef __kindof id __kindof redundant_typedef1;
+typedef __kindof NSObject __kindof *redundant_typedef2;
+
+// Test application of __kindof to typedefs.
+typedef NSObject *NSObject_ptr_typedef;
+typedef NSObject NSObject_typedef;
+typedef __kindof NSObject_ptr_typedef typedef_typedef1;
+typedef __kindof NSObject_typedef typedef_typedef2;
+
+// Test application of __kindof to non-object types.
+typedef __kindof int nonobject_typedef1; // expected-error{{'__kindof' specifier cannot be applied to non-object type 'int'}}
+typedef NSObject **NSObject_ptr_ptr;
+typedef __kindof NSObject_ptr_ptr nonobject_typedef2; // expected-error{{'__kindof' specifier cannot be applied to non-object type 'NSObject_ptr_ptr' (aka 'NSObject **')}}
+
+// Test application of __kindof outside of the decl-specifiers.
+typedef NSObject * __kindof bad_specifier_location1; // expected-error{{'__kindof' type specifier must precede the declarator}}
+typedef NSObject bad_specifier_location2 __kindof; // expected-error{{expected ';' after top level declarator}}
+// expected-warning at -1{{declaration does not declare anything}}
+
+// ---------------------------------------------------------------------------
+// Pretty printing of __kindof
+// ---------------------------------------------------------------------------
+void test_pretty_print(int *ip) {
+  __kindof NSObject *kindof_NSObject;
+  ip = kindof_NSObject; // expected-warning{{from '__kindof NSObject *'}}
+ 
+  __kindof NSObject_ptr_typedef kindof_NSObject_ptr;
+  ip = kindof_NSObject_ptr; // expected-warning{{from '__kindof NSObject_ptr_typedef'}}
+
+  __kindof id <NSCopying> *kindof_NSCopying;
+  ip = kindof_NSCopying; // expected-warning{{from '__kindof id<NSCopying> *'}}
+
+  __kindof NSObject_ptr_typedef *kindof_NSObject_ptr_typedef;
+  ip = kindof_NSObject_ptr_typedef; // expected-warning{{from '__kindof NSObject_ptr_typedef *'}}
+}
+
+// ---------------------------------------------------------------------------
+// Basic implicit conversions (dropping __kindof, upcasts, etc.)
+// ---------------------------------------------------------------------------
+void test_add_remove_kindof_conversions(void) {
+  __kindof NSObject *kindof_NSObject_obj;
+  NSObject *NSObject_obj;
+
+  // Conversion back and forth
+  kindof_NSObject_obj = NSObject_obj;
+  NSObject_obj = kindof_NSObject_obj;
+
+  // Qualified-id conversion back and forth.
+  __kindof id <NSCopying> kindof_id_NSCopying_obj;
+  id <NSCopying> id_NSCopying_obj;
+  kindof_id_NSCopying_obj = id_NSCopying_obj;
+  id_NSCopying_obj = kindof_id_NSCopying_obj;
+}
+
+void test_upcast_conversions(void) {
+  __kindof NSObject *kindof_NSObject_obj;
+  NSObject *NSObject_obj;
+
+  // Upcasts
+  __kindof NSString *kindof_NSString_obj;
+  NSString *NSString_obj;
+  kindof_NSObject_obj = kindof_NSString_obj;
+  kindof_NSObject_obj = NSString_obj;
+  NSObject_obj = kindof_NSString_obj;
+  NSObject_obj = NSString_obj;
+
+  // "Upcasts" with qualified-id.
+  __kindof id <NSCopying> kindof_id_NSCopying_obj;
+  id <NSCopying> id_NSCopying_obj;
+  kindof_id_NSCopying_obj = kindof_NSString_obj;
+  kindof_id_NSCopying_obj = NSString_obj;
+  id_NSCopying_obj = kindof_NSString_obj;
+  id_NSCopying_obj = NSString_obj;
+}
+
+
+void test_ptr_object_conversions(void) {
+  __kindof NSObject **ptr_kindof_NSObject_obj;
+  NSObject **ptr_NSObject_obj;
+
+  // Conversions back and forth.
+  ptr_kindof_NSObject_obj = ptr_NSObject_obj;
+  ptr_NSObject_obj = ptr_kindof_NSObject_obj;
+
+  // Conversions back and forth with qualified-id.
+  __kindof id <NSCopying> *ptr_kindof_id_NSCopying_obj;
+  id <NSCopying> *ptr_id_NSCopying_obj;
+  ptr_kindof_id_NSCopying_obj = ptr_id_NSCopying_obj;
+  ptr_id_NSCopying_obj = ptr_kindof_id_NSCopying_obj;
+
+  // Upcasts.
+  __kindof NSString **ptr_kindof_NSString_obj;
+  NSString **ptr_NSString_obj;
+  ptr_kindof_NSObject_obj = ptr_kindof_NSString_obj;
+  ptr_kindof_NSObject_obj = ptr_NSString_obj;
+  ptr_NSObject_obj = ptr_kindof_NSString_obj;
+  ptr_NSObject_obj = ptr_NSString_obj;
+}
+
+// ---------------------------------------------------------------------------
+// Implicit downcasting
+// ---------------------------------------------------------------------------
+void test_downcast_conversions(void) {
+  __kindof NSObject *kindof_NSObject_obj;
+  NSObject *NSObject_obj;
+  __kindof NSString *kindof_NSString_obj;
+  NSString *NSString_obj;
+
+  // Implicit downcasting.
+  kindof_NSString_obj = kindof_NSObject_obj;
+  kindof_NSString_obj = NSObject_obj; // expected-warning{{assigning to '__kindof NSString *' from 'NSObject *'}}
+  NSString_obj = kindof_NSObject_obj;
+  NSString_obj = NSObject_obj; // expected-warning{{assigning to 'NSString *' from 'NSObject *'}}
+
+  // Implicit downcasting with qualified id.
+  __kindof id <NSCopying> kindof_NSCopying_obj;
+  id <NSCopying> NSCopying_obj;
+  kindof_NSString_obj = kindof_NSCopying_obj;
+  kindof_NSString_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id<NSCopying>'}}
+  NSString_obj = kindof_NSCopying_obj;
+  NSString_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id<NSCopying>'}}
+  kindof_NSObject_obj = kindof_NSCopying_obj;
+  kindof_NSObject_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id<NSCopying>'}}
+  NSObject_obj = kindof_NSCopying_obj;
+  NSObject_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id<NSCopying>'}}
+}
+
+void test_crosscast_conversions(void) {
+  __kindof NSString *kindof_NSString_obj;
+  NSString *NSString_obj;
+  __kindof NSNumber *kindof_NSNumber_obj;
+  NSNumber *NSNumber_obj;
+
+  NSString_obj = kindof_NSNumber_obj; // expected-warning{{from '__kindof NSNumber *'}}
+}
+
+// ---------------------------------------------------------------------------
+// Blocks
+// ---------------------------------------------------------------------------
+void test_block_conversions(void) {
+  // Adding/removing __kindof from return type.
+  __kindof NSString *(^kindof_NSString_void_block)(void);
+  NSString *(^NSString_void_block)(void);
+  kindof_NSString_void_block = NSString_void_block;
+  NSString_void_block = kindof_NSString_void_block;
+
+  // Covariant return type.
+  __kindof NSMutableString *(^kindof_NSMutableString_void_block)(void);
+  NSMutableString *(^NSMutableString_void_block)(void);
+  kindof_NSString_void_block = NSMutableString_void_block;
+  NSString_void_block = kindof_NSMutableString_void_block;
+  kindof_NSString_void_block = NSMutableString_void_block;
+  NSString_void_block = kindof_NSMutableString_void_block;
+
+  // "Covariant" return type via downcasting rule.
+  kindof_NSMutableString_void_block = NSString_void_block; // expected-error{{from 'NSString *(^)(void)'}}
+  NSMutableString_void_block = kindof_NSString_void_block;
+  kindof_NSMutableString_void_block = NSString_void_block; // expected-error{{from 'NSString *(^)(void)'}}
+  NSMutableString_void_block = kindof_NSString_void_block;
+
+  // Cross-casted return type.
+  __kindof NSNumber *(^kindof_NSNumber_void_block)(void);
+  NSNumber *(^NSNumber_void_block)(void);
+  kindof_NSString_void_block = NSNumber_void_block; // expected-error{{from 'NSNumber *(^)(void)'}}
+  NSString_void_block = kindof_NSNumber_void_block; // expected-error{{'__kindof NSNumber *(^)(void)'}}
+  kindof_NSString_void_block = NSNumber_void_block; // expected-error{{from 'NSNumber *(^)(void)'}}
+  NSString_void_block = kindof_NSNumber_void_block; // expected-error{{'__kindof NSNumber *(^)(void)'}}
+
+  // Adding/removing __kindof from argument type.
+  void (^void_kindof_NSString_block)(__kindof NSString *);
+  void (^void_NSString_block)(NSString *);
+  void_kindof_NSString_block = void_NSString_block;
+  void_NSString_block = void_kindof_NSString_block;
+
+  // Contravariant argument type.
+  void (^void_kindof_NSMutableString_block)(__kindof NSMutableString *);
+  void (^void_NSMutableString_block)(NSMutableString *);
+  void_kindof_NSMutableString_block = void_kindof_NSString_block;
+  void_kindof_NSMutableString_block = void_NSString_block;
+  void_NSMutableString_block = void_kindof_NSString_block;
+  void_NSMutableString_block = void_NSString_block;
+
+  // "Contravariant" argument type via downcasting rule.
+  void_kindof_NSString_block = void_kindof_NSMutableString_block;
+  void_kindof_NSString_block = void_NSMutableString_block;
+  void_NSString_block = void_kindof_NSMutableString_block; // expected-error{{from 'void (^)(__kindof NSMutableString *)'}}
+  void_NSString_block = void_NSMutableString_block; // expected-error{{from 'void (^)(NSMutableString *)'}}
+}
+
+// ---------------------------------------------------------------------------
+// Messaging __kindof types.
+// ---------------------------------------------------------------------------
+void message_kindof_object(__kindof NSString *kindof_NSString) {
+  [kindof_NSString retain]; // in superclass
+  [kindof_NSString stringByAppendingString:0]; // in class
+  [kindof_NSString appendString:0]; // in subclass
+  [kindof_NSString numberByAddingNumber: 0]; // FIXME: in unrelated class
+  [kindof_NSString randomMethod]; // in protocol
+}
+
+void message_kindof_qualified_id(__kindof id <NSCopying> kindof_NSCopying) {
+  [kindof_NSCopying copy]; // in protocol
+  [kindof_NSCopying stringByAppendingString:0]; // in some class
+  [kindof_NSCopying randomMethod]; // in unrelated protocol
+}
+
+void message_kindof_qualified_class(
+       __kindof Class <NSCopying> kindof_NSCopying) {
+  [kindof_NSCopying classCopy]; // in protocol
+  [kindof_NSCopying string]; // in some class
+  [kindof_NSCopying randomClassMethod]; // in unrelated protocol
+}
+
+// ---------------------------------------------------------------------------
+// __kindof within specialized types
+// ---------------------------------------------------------------------------
+ at interface NSArray<T> : NSObject
+ at end
+
+void implicit_convert_array(NSArray<__kindof NSString *> *kindofStringsArray,
+                            NSArray<NSString *> *stringsArray,
+                            NSArray<__kindof NSMutableString *>
+                              *kindofMutStringsArray,
+                            NSArray<NSMutableString *> *mutStringsArray) {
+  // Adding/removing __kindof is okay.
+  kindofStringsArray = stringsArray;
+  stringsArray = kindofStringsArray;
+
+  // Other covariant and contravariant conversions still not permitted.
+  kindofStringsArray = mutStringsArray; // expected-warning{{incompatible pointer types}}
+  stringsArray = kindofMutStringsArray; // expected-warning{{incompatible pointer types}}
+  mutStringsArray = kindofStringsArray; // expected-warning{{incompatible pointer types}}
+
+  // Adding/removing nested __kindof is okay.
+  NSArray<NSArray<__kindof NSString *> *> *kindofStringsArrayArray;
+  NSArray<NSArray<NSString *> *> *stringsArrayArray;
+  kindofStringsArrayArray = stringsArrayArray;
+  stringsArrayArray = kindofStringsArrayArray;
+}
+
+// ---------------------------------------------------------------------------
+// __kindof + nullability
+// ---------------------------------------------------------------------------
+
+void testNullability() {
+  // The base type being a pointer type tickles the bug.
+  extern __kindof id <NSCopying> __nonnull getSomeCopyable();
+  NSString *string = getSomeCopyable(); // no-warning
+
+  void processCopyable(__typeof(getSomeCopyable()) string);
+  processCopyable(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+}

Modified: cfe/trunk/test/SemaObjC/parameterized_classes_subst.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/parameterized_classes_subst.m?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjC/parameterized_classes_subst.m (original)
+++ cfe/trunk/test/SemaObjC/parameterized_classes_subst.m Mon Jul  6 22:58:42 2015
@@ -18,6 +18,9 @@ __attribute__((objc_root_class))
 @interface NSString : NSObject <NSCopying>
 @end
 
+ at interface NSMutableString : NSString
+ at end
+
 @interface NSNumber : NSObject <NSCopying>
 @end
 
@@ -144,6 +147,7 @@ void test_message_send_result(
        NSMutableSet *mutSet,
        MutableSetOfArrays *mutArraySet,
        NSArray<NSString *> *stringArray,
+       NSArray<__kindof NSString *> *kindofStringArray,
        void (^block)(void)) {
   int *ip;
   ip = [stringSet firstObject]; // expected-warning{{from 'NSString *'}}
@@ -171,6 +175,12 @@ void test_message_send_result(
   [[NSMutableArray alloc] initWithArray: stringArray]; // okay
   [[NSMutableArray<NSString *> alloc] initWithArray: stringArray]; // okay
   [[NSMutableArray<NSNumber *> alloc] initWithArray: stringArray]; // expected-warning{{sending 'NSArray<NSString *> *' to parameter of type 'NSArray<NSNumber *> *'}}
+
+  ip = [[[NSViewController alloc] init] view]; // expected-warning{{from '__kindof NSView *'}}
+  [[[[NSViewController alloc] init] view] toggle];
+
+  NSMutableString *mutStr = kindofStringArray[0];
+  NSNumber *number = kindofStringArray[0]; // expected-warning{{of type '__kindof NSString *'}}
 }
 
 void test_message_send_param(
@@ -215,7 +225,9 @@ void test_property_read(
   ip = mutSet.allObjects; // expected-warning{{from 'NSArray *'}}
   ip = mutArraySet.allObjects; // expected-warning{{from 'NSArray *'}}
 
-  ip = mutDict.someRandomKey; // expected-warning{{from 'id'}}
+  ip = mutDict.someRandomKey; // expected-warning{{from '__kindof id<NSCopying>'}}
+
+  ip = [[NSViewController alloc] init].view; // expected-warning{{from '__kindof NSView *'}}
 }
 
 void test_property_write(

Modified: cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm?rev=241548&r1=241547&r2=241548&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm (original)
+++ cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm Mon Jul  6 22:58:42 2015
@@ -172,7 +172,7 @@ void test_property_read(
   ip = mutSet.allObjects; // expected-error{{from incompatible type 'NSArray *'}}
   ip = mutArraySet.allObjects; // expected-error{{from incompatible type 'NSArray *'}}
 
-  ip = mutDict.someRandomKey; // expected-error{{from incompatible type 'id'}}
+  ip = mutDict.someRandomKey; // expected-error{{from incompatible type '__kindof id<NSCopying>'}}
 }
 
 void test_property_write(





More information about the cfe-commits mailing list