manmanren created this revision.
manmanren added a reviewer: doug.gregor.
manmanren added a subscriber: cfe-commits.


Depends on https://reviews.llvm.org/D23078 and https://reviews.llvm.org/D23079

We say ObjCTypeParamType is ObjCObjectPointerType, but we assert
fail when trying to call Type::getPointeeType.

We compare the types between declaration and definition for ObjCMethods that
use the type parameter, by using simpleTransform that transforms the
ObjCTypeParamType to the underlying type of the ObjCTypeParamDecl. This matches
our previous behavior.

rdar://24619481
rdar://25060179


https://reviews.llvm.org/D23080

Files:
  include/clang/AST/Type.h
  lib/AST/DeclObjC.cpp
  lib/AST/Type.cpp
  lib/Sema/SemaDeclObjC.cpp
  lib/Sema/SemaExprObjC.cpp
  lib/Sema/SemaType.cpp
  lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
  lib/StaticAnalyzer/Core/CallEvent.cpp
  test/SemaObjC/kindof.m
  test/SemaObjC/parameterized_classes.m
  test/SemaObjC/parameterized_classes_subst.m

Index: test/SemaObjC/parameterized_classes_subst.m
===================================================================
--- test/SemaObjC/parameterized_classes_subst.m
+++ test/SemaObjC/parameterized_classes_subst.m
@@ -426,3 +426,36 @@
 // warning about likely protocol/class name typos.
 // --------------------------------------------------------------------------
 typedef NSArray<NSObject> ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}}
+
+// rdar://25060179
+@interface MyMutableDictionary<KeyType, ObjectType> : NSObject
+- (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key; // expected-note{{passing argument to parameter 'obj' here}} \
+    // expected-note{{passing argument to parameter 'key' here}}
+@end
+
+void bar(MyMutableDictionary<NSString *, NSString *> *stringsByString,
+                             NSNumber *n1, NSNumber *n2) {
+  // We warn here when the key types do not match.
+  stringsByString[n1] = n2; // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} \
+    // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString<NSCopying> *'}}
+}
+
+@interface MyTest<K, V> : NSObject <NSCopying>
+- (V)test:(K)key;
+- (V)test2:(K)key; // expected-note{{previous definition is here}}
+- (void)mapUsingBlock:(id (^)(V))block;
+- (void)mapUsingBlock2:(id (^)(V))block; // expected-note{{previous definition is here}}
+@end
+
+@implementation MyTest
+- (id)test:(id)key {
+  return key;
+}
+- (int)test2:(id)key{ // expected-warning{{conflicting return type in implementation}}
+  return 0;
+}
+- (void)mapUsingBlock:(id (^)(id))block {
+}
+- (void)mapUsingBlock2:(id)block { // expected-warning{{conflicting parameter types in implementation}}
+}
+@end
Index: test/SemaObjC/parameterized_classes.m
===================================================================
--- test/SemaObjC/parameterized_classes.m
+++ test/SemaObjC/parameterized_classes.m
@@ -320,7 +320,7 @@
 @interface PC23<T : NSObject *> : PC1<T, U> // expected-error{{unknown type name 'U'}}
 @end
 
-@interface PC24<T> : PC1<T, T> // expected-error{{type argument 'T' (aka 'id') does not satisfy the bound ('NSObject *') of type parameter 'U'}}
+@interface PC24<T> : PC1<T, T> // expected-error{{type argument 'T' does not satisfy the bound ('NSObject *') of type parameter 'U'}}
 @end
 
 @interface NSFoo : PC1<NSObject *, NSObject *> // okay
Index: test/SemaObjC/kindof.m
===================================================================
--- test/SemaObjC/kindof.m
+++ test/SemaObjC/kindof.m
@@ -385,7 +385,7 @@
 @end
 
 @interface NSGeneric<ObjectType> : NSObject
-- (void)test:(__kindof ObjectType)T;
+- (void)test:(__kindof ObjectType)T; // expected-note{{passing argument to parameter 'T' here}}
 - (void)mapUsingBlock:(id (^)(__kindof ObjectType))block;
 @end
 @implementation NSGeneric
@@ -395,6 +395,14 @@
 }
 @end
 
+void testGeneric(NSGeneric<NSString*> *generic) {
+  NSObject *NSObject_obj;
+  // Assign from NSObject_obj to __kindof NSString*.
+  [generic test:NSObject_obj]; // expected-warning{{incompatible pointer types sending 'NSObject *' to parameter of type '__kindof NSString *'}}
+  NSString *NSString_str;
+  [generic test:NSString_str];
+}
+
 // Check that clang doesn't crash when a type parameter is illegal.
 @interface Array1<T> : NSObject
 @end
Index: lib/StaticAnalyzer/Core/CallEvent.cpp
===================================================================
--- lib/StaticAnalyzer/Core/CallEvent.cpp
+++ lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -60,7 +60,8 @@
   // Check if a callback is passed inside a struct (for both, struct passed by
   // reference and by value). Dig just one level into the struct for now.
 
-  if (T->isAnyPointerType() || T->isReferenceType())
+  if ((T->isAnyPointerType() && !isa<ObjCTypeParamType>(T)) ||
+      T->isReferenceType())
     T = T->getPointeeType();
 
   if (const RecordType *RT = T->getAsStructureType()) {
@@ -129,6 +130,8 @@
 /// \brief Returns true if a type is a pointer-to-const or reference-to-const
 /// with no further indirection.
 static bool isPointerToConst(QualType Ty) {
+  if (isa<ObjCTypeParamType>(Ty))
+    return false;
   QualType PointeeTy = Ty->getPointeeType();
   if (PointeeTy == QualType())
     return false;
Index: lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -626,7 +626,7 @@
       : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> {
   public:
     IsObjCTypeParamDependentTypeVisitor() : Result(false) {}
-    bool VisitTypedefType(const TypedefType *Type) {
+    bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) {
       if (isa<ObjCTypeParamDecl>(Type->getDecl())) {
         Result = true;
         return false;
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -898,7 +898,8 @@
     }
 
     // Objective-C object pointer types must be substitutable for the bounds.
-    if (const auto *typeArgObjC = typeArg->getAs<ObjCObjectPointerType>()) {
+    if (typeArg->getAs<ObjCObjectPointerType>() ||
+        isa<ObjCTypeParamType>(typeArg)) {
       // If we don't have a type parameter to match against, assume
       // everything is fine. There was a prior pack expansion that
       // means we won't be able to match anything.
@@ -911,6 +912,14 @@
       QualType bound = typeParam->getUnderlyingType();
       const auto *boundObjC = bound->getAs<ObjCObjectPointerType>();
 
+      // For ObjCTypeParamType, we use the Decl's underlying type.
+      const ObjCObjectPointerType *typeArgObjC = nullptr;
+      if (isa<ObjCTypeParamType>(typeArg)) {
+        typeArgObjC = typeArg->getAs<ObjCTypeParamType>()->getDecl()->
+                      getUnderlyingType()->getAs<ObjCObjectPointerType>(); 
+      } else {
+        typeArgObjC = typeArg->getAs<ObjCObjectPointerType>();
+      }
       // Determine whether the type argument is substitutable for the bound.
       if (typeArgObjC->isObjCIdType()) {
         // When the type argument is 'id', the only acceptable type
@@ -1007,6 +1016,11 @@
                   const SourceLocation *protocolLocs,
                   bool failOnError = false) {
   ASTContext &ctx = S.Context;
+  if (const ObjCTypeParamType *objT =
+      dyn_cast<ObjCTypeParamType>(type.getTypePtr())) {
+    return ctx.getObjCTypeParamType(objT->getDecl(), protocols);
+  }
+
   if (const ObjCObjectType *objT = dyn_cast<ObjCObjectType>(type.getTypePtr())){
     // FIXME: Check for protocols to which the class type is already
     // known to conform.
@@ -1204,6 +1218,23 @@
     ResultTL = ObjCObjectPointerTL.getPointeeLoc();
   }
 
+  if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) {
+    // Protocol qualifier information.
+    if (OTPTL.getNumProtocols() > 0) {
+      assert(OTPTL.getNumProtocols() == Protocols.size());
+      OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc);
+      OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc);
+      for (unsigned i = 0, n = Protocols.size(); i != n; ++i)
+        OTPTL.setProtocolLoc(i, ProtocolLocs[i]);
+    } else {
+      OTPTL.setProtocolLAngleLoc(SourceLocation());
+      OTPTL.setProtocolRAngleLoc(SourceLocation());
+    }
+
+    // We're done. Return the completed type to the parser.
+    return CreateParsedType(Result, ResultTInfo);
+  }
+
   auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>();
 
   // Type argument information.
@@ -3363,7 +3394,14 @@
     }
 
     // Look at Objective-C object pointers.
-    if (auto objcObjectPtr = type->getAs<ObjCObjectPointerType>()) {
+    if (type->getAs<ObjCObjectPointerType>() ||
+        isa<ObjCTypeParamType>(type)) {
+      const ObjCObjectPointerType *objcObjectPtr = nullptr;
+      if (isa<ObjCTypeParamType>(type))
+        objcObjectPtr = type->getAs<ObjCTypeParamType>()->getDecl()->
+                        getUnderlyingType()->getAs<ObjCObjectPointerType>();
+      else
+        objcObjectPtr = type->getAs<ObjCObjectPointerType>();
       ++numNormalPointers;
       ++numTypeSpecifierPointers;
 
@@ -5911,20 +5949,22 @@
   // For the context-sensitive keywords/Objective-C property
   // attributes, require that the type be a single-level pointer.
   if (isContextSensitive) {
-    // Make sure that the pointee isn't itself a pointer type.
-    QualType pointeeType = desugared->getPointeeType();
-    if (pointeeType->isAnyPointerType() ||
-        pointeeType->isObjCObjectPointerType() ||
-        pointeeType->isMemberPointerType()) {
-      Diag(nullabilityLoc, diag::err_nullability_cs_multilevel)
-        << DiagNullabilityKind(nullability, true)
-        << type;
-      Diag(nullabilityLoc, diag::note_nullability_type_specifier)
-        << DiagNullabilityKind(nullability, false)
-        << type
-        << FixItHint::CreateReplacement(nullabilityLoc,
-                                        getNullabilitySpelling(nullability));
-      return true;
+    if (!isa<ObjCTypeParamType>(desugared)) {
+      // Make sure that the pointee isn't itself a pointer type.
+      QualType pointeeType = desugared->getPointeeType();
+      if (pointeeType->isAnyPointerType() ||
+          pointeeType->isObjCObjectPointerType() ||
+          pointeeType->isMemberPointerType()) {
+        Diag(nullabilityLoc, diag::err_nullability_cs_multilevel)
+          << DiagNullabilityKind(nullability, true)
+          << type;
+        Diag(nullabilityLoc, diag::note_nullability_type_specifier)
+          << DiagNullabilityKind(nullability, false)
+          << type
+          << FixItHint::CreateReplacement(nullabilityLoc,
+                                          getNullabilitySpelling(nullability));
+        return true;
+      }
     }
   }
 
@@ -5935,6 +5975,13 @@
 }
 
 bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) {
+  if (isa<ObjCTypeParamType>(type)) {
+    // Build the attributed type to record where __kindof occurred.
+    type = Context.getAttributedType(AttributedType::attr_objc_kindof,
+                                     type, type);
+    return false;
+  }
+
   // 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() 
Index: lib/Sema/SemaExprObjC.cpp
===================================================================
--- lib/Sema/SemaExprObjC.cpp
+++ lib/Sema/SemaExprObjC.cpp
@@ -384,6 +384,9 @@
 static ExprResult CheckObjCCollectionLiteralElement(Sema &S, Expr *Element, 
                                                     QualType T,
                                                     bool ArrayLiteral = false) {
+  if (auto *TPT = T->getAs<ObjCTypeParamType>())
+    T = TPT->getDecl()->getUnderlyingType();
+
   // If the expression is type-dependent, there's nothing for us to do.
   if (Element->isTypeDependent())
     return Element;
@@ -815,8 +818,10 @@
     // Dig out the type that all elements should be converted to.
     QualType T = Method->parameters()[0]->getType();
     const PointerType *PtrT = T->getAs<PointerType>();
+    // It is okay for the pointee type to be ObjCTypeParamType.
     if (!PtrT || 
-        !Context.hasSameUnqualifiedType(PtrT->getPointeeType(), IdT)) {
+        (!isa<ObjCTypeParamType>(PtrT->getPointeeType()) &&
+         !Context.hasSameUnqualifiedType(PtrT->getPointeeType(), IdT))) {
       Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
         << Sel;
       Diag(Method->parameters()[0]->getLocation(),
@@ -933,7 +938,8 @@
     QualType ValueT = Method->parameters()[0]->getType();
     const PointerType *PtrValue = ValueT->getAs<PointerType>();
     if (!PtrValue || 
-        !Context.hasSameUnqualifiedType(PtrValue->getPointeeType(), IdT)) {
+        (!isa<ObjCTypeParamType>(PtrValue->getPointeeType()) &&
+         !Context.hasSameUnqualifiedType(PtrValue->getPointeeType(), IdT))) {
       Diag(SR.getBegin(), diag::err_objc_literal_method_sig)
         << Sel;
       Diag(Method->parameters()[0]->getLocation(),
@@ -947,8 +953,9 @@
     QualType KeyT = Method->parameters()[1]->getType();
     const PointerType *PtrKey = KeyT->getAs<PointerType>();
     if (!PtrKey || 
-        !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(),
-                                        IdT)) {
+        (!isa<ObjCTypeParamType>(PtrKey->getPointeeType()) &&
+         !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(),
+                                        IdT))) {
       bool err = true;
       if (PtrKey) {
         if (QIDNSCopying.isNull()) {
Index: lib/Sema/SemaDeclObjC.cpp
===================================================================
--- lib/Sema/SemaDeclObjC.cpp
+++ lib/Sema/SemaDeclObjC.cpp
@@ -2269,6 +2269,15 @@
   if (S.Context.hasSameUnqualifiedType(MethodImpl->getReturnType(),
                                        MethodDecl->getReturnType()))
     return true;
+
+  // We need to transfer the types if they contain ObjCTypeParamType.
+  QualType ModifiedImplTy =
+    MethodImpl->getReturnType().handleObjCTypeParamType(S.Context);
+  QualType ModifiedIfaceTy =
+    MethodDecl->getReturnType().handleObjCTypeParamType(S.Context);
+  if (S.Context.hasSameUnqualifiedType(ModifiedImplTy, ModifiedIfaceTy))
+    return true;
+
   if (!Warn)
     return false;
 
@@ -2353,7 +2362,13 @@
   }
   if (S.Context.hasSameUnqualifiedType(ImplTy, IfaceTy))
     return true;
-  
+
+  // We need to transfer the types if they contain ObjCTypeParamType.
+  QualType ModifiedImplTy = ImplTy.handleObjCTypeParamType(S.Context);
+  QualType ModifiedIfaceTy = IfaceTy.handleObjCTypeParamType(S.Context);
+  if (S.Context.hasSameUnqualifiedType(ModifiedImplTy, ModifiedIfaceTy))
+    return true;
+
   if (!Warn)
     return false;
   unsigned DiagID = 
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -424,6 +424,8 @@
     return MPT->getPointeeType();
   if (const DecayedType *DT = getAs<DecayedType>())
     return DT->getPointeeType();
+  assert(!isa<ObjCTypeParamType>(this) &&
+         "do not call getPointeeType on ObjCTypeParamType");
   return QualType();
 }
 
@@ -1082,13 +1084,35 @@
 
     // Replace an Objective-C type parameter reference with the corresponding
     // type argument.
-    if (const auto *typedefTy = dyn_cast<TypedefType>(splitType.Ty)) {
-      if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(typedefTy->getDecl())) {
+    if (const auto *OTPTy = dyn_cast<ObjCTypeParamType>(splitType.Ty)) {
+      if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(OTPTy->getDecl())) {
         // If we have type arguments, use them.
         if (!typeArgs.empty()) {
           // FIXME: Introduce SubstObjCTypeParamType ?
           QualType argType = typeArgs[typeParam->getIndex()];
-          return ctx.getQualifiedType(argType, splitType.Quals);
+          if (OTPTy->qual_empty())
+            return ctx.getQualifiedType(argType, splitType.Quals);
+          // Apply protocol lists if exists. Should we combine protocol list?
+          if (const auto *objcPtr = dyn_cast<ObjCObjectPointerType>(argType)) {
+            if (const auto *objcObjectType = objcPtr->getObjectType()) {
+              SmallVector<ObjCProtocolDecl*, 8> protocolsVec;
+              protocolsVec.append(objcObjectType->qual_begin(),
+                                  objcObjectType->qual_end());
+              protocolsVec.append(OTPTy->qual_begin(),
+                                  OTPTy->qual_end());
+              ArrayRef<ObjCProtocolDecl *> protocols = protocolsVec;
+              // We should refactor applyObjCProtocolQualifiers.
+              QualType resultTy = ctx.getObjCObjectType(
+                       objcObjectType->getBaseType(),
+                       objcObjectType->getTypeArgsAsWritten(),
+                       protocols,
+                       objcObjectType->isKindOfTypeAsWritten());
+              // Rebuild object pointer type.
+              resultTy = ctx.getObjCObjectPointerType(resultTy);
+              return ctx.getQualifiedType(resultTy, splitType.Quals);
+            }
+          }
+          llvm_unreachable("substitute ObjC type param T<protocol> with a non ObjC object type");
         }
 
         switch (context) {
@@ -1260,6 +1284,22 @@
   return *this;
 }
 
+/// This function replaces ObjCTypeParamType with its underlying type so we
+/// can check if a ObjCMethod declaration in @interface matches the definition
+/// in @implementation. @implementation does not take type parameters.
+QualType QualType::handleObjCTypeParamType(ASTContext &ctx) const {
+  return simpleTransform(ctx, *this,
+           [&](QualType type) -> QualType {
+             SplitQualType splitType = type.split();
+             if (auto *TPT = splitType.Ty->getAs<ObjCTypeParamType>()) {
+               QualType result = TPT->getDecl()->getUnderlyingType();
+               return ctx.getQualifiedType(
+                        result, splitType.Quals);
+             }
+             return type;
+           });
+}
+
 QualType QualType::stripObjCKindOfType(const ASTContext &constCtx) const {
   // FIXME: Because ASTContext::getAttributedType() is non-const.
   auto &ctx = const_cast<ASTContext &>(constCtx);
Index: lib/AST/DeclObjC.cpp
===================================================================
--- lib/AST/DeclObjC.cpp
+++ lib/AST/DeclObjC.cpp
@@ -1320,8 +1320,12 @@
                                              IdentifierInfo *name,
                                              SourceLocation colonLoc,
                                              TypeSourceInfo *boundInfo) {
-  return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index,
-                                         nameLoc, name, colonLoc, boundInfo);
+  auto *TPDecl =
+    new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index,
+                                    nameLoc, name, colonLoc, boundInfo);
+  QualType TPType = ctx.getObjCTypeParamType(TPDecl, {});
+  TPDecl->setTypeForDecl(TPType.getTypePtr());
+  return TPDecl;
 }
 
 ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx,
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -1091,6 +1091,8 @@
   /// Strip Objective-C "__kindof" types from the given type.
   QualType stripObjCKindOfType(const ASTContext &ctx) const;
 
+  QualType handleObjCTypeParamType(ASTContext &ctx) const;
+
   /// Remove all qualifiers including _Atomic.
   QualType getAtomicUnqualifiedType() const;
 
@@ -5640,7 +5642,8 @@
   return isa<ExtVectorType>(CanonicalType);
 }
 inline bool Type::isObjCObjectPointerType() const {
-  return isa<ObjCObjectPointerType>(CanonicalType);
+  return isa<ObjCObjectPointerType>(CanonicalType) ||
+         isa<ObjCTypeParamType>(CanonicalType);
 }
 inline bool Type::isObjCObjectType() const {
   return isa<ObjCObjectType>(CanonicalType);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to