]> granicus.if.org Git - clang/commitdiff
[ObjC generics] Fix applying `__kindof` to the type parameter.
authorVolodymyr Sapsai <vsapsai@apple.com>
Sat, 16 Feb 2019 01:01:08 +0000 (01:01 +0000)
committerVolodymyr Sapsai <vsapsai@apple.com>
Sat, 16 Feb 2019 01:01:08 +0000 (01:01 +0000)
Fixes the warning about incompatible pointer types on assigning to a
subclass of type argument an expression of type `__kindof TypeParam`.

We already have a mechanism in `ASTContext::canAssignObjCInterfaces`
that handles `ObjCObjectType` with `__kindof`. But it wasn't triggered
because during type substitution `__kindof TypeParam` was represented as
`AttributedType` with attribute `ObjCKindOf` and equivalent type
`TypeArg`. For assignment type checking we use canonical types so
attributed type was desugared and the attribute was ignored.

The fix is in checking transformed `AttributedType` and pushing
`__kindof` down into `ObjCObjectType` when necessary.

rdar://problem/38514910

Reviewers: ahatanak, erik.pilkington, doug.gregor

Reviewed By: doug.gregor

Subscribers: jkorous, dexonsmith, manmanren, jordan_rose, doug.gregor, cfe-commits

Differential Revision: https://reviews.llvm.org/D57076

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

lib/AST/Type.cpp
test/SemaObjC/kindof.m

index 5edb68d69929b4b4830f5d8d59b9c401efae04d0..aca82e2e63e9863fceccc30ab73b3bda157ba9d5 100644 (file)
@@ -1297,6 +1297,42 @@ struct SubstObjCTypeArgsVisitor
 
     return BaseType::VisitObjCObjectType(objcObjectType);
   }
+
+  QualType VisitAttributedType(const AttributedType *attrType) {
+    QualType newType = BaseType::VisitAttributedType(attrType);
+    if (newType.isNull())
+      return {};
+
+    const auto *newAttrType = dyn_cast<AttributedType>(newType.getTypePtr());
+    if (!newAttrType || newAttrType->getAttrKind() != attr::ObjCKindOf)
+      return newType;
+
+    // Find out if it's an Objective-C object or object pointer type;
+    QualType newEquivType = newAttrType->getEquivalentType();
+    const ObjCObjectPointerType *ptrType =
+        newEquivType->getAs<ObjCObjectPointerType>();
+    const ObjCObjectType *objType = ptrType
+                                        ? ptrType->getObjectType()
+                                        : newEquivType->getAs<ObjCObjectType>();
+    if (!objType)
+      return newType;
+
+    // Rebuild the "equivalent" type, which pushes __kindof down into
+    // the object type.
+    newEquivType = Ctx.getObjCObjectType(
+        objType->getBaseType(), objType->getTypeArgsAsWritten(),
+        objType->getProtocols(),
+        // There is no need to apply kindof on an unqualified id type.
+        /*isKindOf=*/objType->isObjCUnqualifiedId() ? false : true);
+
+    // If we started with an object pointer type, rebuild it.
+    if (ptrType)
+      newEquivType = Ctx.getObjCObjectPointerType(newEquivType);
+
+    // Rebuild the attributed type.
+    return Ctx.getAttributedType(newAttrType->getAttrKind(),
+                                 newAttrType->getModifiedType(), newEquivType);
+  }
 };
 
 struct StripObjCKindOfTypeVisitor
index 9d758d3cfb2a6bfc9290d82628a1879b65474ac7..b2c32051820704fbe398bcf9a8b17426740d6e0c 100644 (file)
@@ -384,9 +384,17 @@ void testNullability() {
 }
 @end
 
+// ---------------------------------------------------------------------------
+// __kindof on type parameters
+// ---------------------------------------------------------------------------
+
 @interface NSGeneric<ObjectType> : NSObject
 - (void)test:(__kindof ObjectType)T; // expected-note{{passing argument to parameter 'T' here}}
 - (void)mapUsingBlock:(id (^)(__kindof ObjectType))block;
+@property (copy) ObjectType object;
+@property (copy) __kindof ObjectType kindof_object;
+
+@property (copy) __kindof ObjectType _Nonnull nonnull_kindof_object;
 @end
 @implementation NSGeneric
 - (void)test:(id)T {
@@ -395,6 +403,11 @@ void testNullability() {
 }
 @end
 
+@interface NSDefaultGeneric<ObjectType : NSString *> : NSObject
+@property (copy) ObjectType object;
+@property (copy) __kindof ObjectType kindof_object;
+@end
+
 void testGeneric(NSGeneric<NSString*> *generic) {
   NSObject *NSObject_obj;
   // Assign from NSObject_obj to __kindof NSString*.
@@ -403,6 +416,45 @@ void testGeneric(NSGeneric<NSString*> *generic) {
   [generic test:NSString_str];
 }
 
+void testGenericAssignment() {
+  NSMutableString *NSMutableString_str;
+  NSNumber *NSNumber_obj;
+
+  NSGeneric<NSString*> *generic;
+  NSMutableString_str = generic.object; // expected-warning{{incompatible pointer types}}
+  NSNumber_obj = generic.object; // expected-warning{{incompatible pointer types}}
+  NSMutableString_str = generic.kindof_object;
+  NSNumber_obj = generic.kindof_object; // expected-warning{{incompatible pointer types assigning to 'NSNumber *' from '__kindof NSString *'}}
+
+  NSGeneric<__kindof NSString*> *kindof_generic;
+  NSMutableString_str = kindof_generic.object;
+  NSNumber_obj = kindof_generic.object; // expected-warning{{incompatible pointer types assigning to 'NSNumber *' from '__kindof NSString *'}}
+  NSMutableString_str = kindof_generic.kindof_object;
+  NSNumber_obj = kindof_generic.kindof_object; // expected-warning{{incompatible pointer types assigning to 'NSNumber *' from '__kindof __kindof NSString *'}}
+
+  NSDefaultGeneric *default_generic;
+  NSMutableString_str = default_generic.object;
+  NSNumber_obj = default_generic.object; // expected-warning{{incompatible pointer types}}
+  NSMutableString_str = default_generic.kindof_object;
+  NSNumber_obj = default_generic.kindof_object; // expected-warning{{incompatible pointer types assigning to 'NSNumber *' from '__kindof __kindof NSString *'}}
+
+  typedef NSString *Typedef_NSString;
+  NSGeneric<Typedef_NSString> *typedef_generic;
+  NSMutableString_str = typedef_generic.object; // expected-warning{{incompatible pointer types}}
+  NSNumber_obj = typedef_generic.object; // expected-warning{{incompatible pointer types}}
+  NSMutableString_str = typedef_generic.kindof_object;
+  NSNumber_obj = typedef_generic.kindof_object; // expected-warning{{incompatible pointer types assigning to 'NSNumber *' from '__kindof Typedef_NSString'}}
+}
+
+void testKindofNonObjectType() {
+  typedef void (^BlockType)(int);
+  NSGeneric<BlockType> *generic;
+}
+
+void testKindofNullability(NSGeneric<NSString*> *generic) {
+  generic.nonnull_kindof_object = 0; // expected-warning{{null passed to a callee that requires a non-null argument}}
+}
+
 // Check that clang doesn't crash when a type parameter is illegal.
 @interface Array1<T> : NSObject
 @end