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(CanonicalType); } inline bool Type::isObjCObjectPointerType() const { - return isa(CanonicalType); + return isa(CanonicalType) || + isa(CanonicalType); } inline bool Type::isObjCObjectType() const { return isa(CanonicalType); 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: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -424,6 +424,8 @@ return MPT->getPointeeType(); if (const DecayedType *DT = getAs()) return DT->getPointeeType(); + assert(!isa(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(splitType.Ty)) { - if (auto *typeParam = dyn_cast(typedefTy->getDecl())) { + if (const auto *OTPTy = dyn_cast(splitType.Ty)) { + if (auto *typeParam = dyn_cast(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(argType)) { + if (const auto *objcObjectType = objcPtr->getObjectType()) { + SmallVector protocolsVec; + protocolsVec.append(objcObjectType->qual_begin(), + objcObjectType->qual_end()); + protocolsVec.append(OTPTy->qual_begin(), + OTPTy->qual_end()); + ArrayRef 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 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()) { + 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(constCtx); 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/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()) + 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(); + // It is okay for the pointee type to be ObjCTypeParamType. if (!PtrT || - !Context.hasSameUnqualifiedType(PtrT->getPointeeType(), IdT)) { + (!isa(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(); if (!PtrValue || - !Context.hasSameUnqualifiedType(PtrValue->getPointeeType(), IdT)) { + (!isa(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(); if (!PtrKey || - !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), - IdT)) { + (!isa(PtrKey->getPointeeType()) && + !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), + IdT))) { bool err = true; if (PtrKey) { if (QIDNSCopying.isNull()) { 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()) { + if (typeArg->getAs() || + isa(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(); + // For ObjCTypeParamType, we use the Decl's underlying type. + const ObjCObjectPointerType *typeArgObjC = nullptr; + if (isa(typeArg)) { + typeArgObjC = typeArg->getAs()->getDecl()-> + getUnderlyingType()->getAs(); + } else { + typeArgObjC = typeArg->getAs(); + } // 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(type.getTypePtr())) { + return ctx.getObjCTypeParamType(objT->getDecl(), protocols); + } + if (const ObjCObjectType *objT = dyn_cast(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()) { + // 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(); // Type argument information. @@ -3363,7 +3394,14 @@ } // Look at Objective-C object pointers. - if (auto objcObjectPtr = type->getAs()) { + if (type->getAs() || + isa(type)) { + const ObjCObjectPointerType *objcObjectPtr = nullptr; + if (isa(type)) + objcObjectPtr = type->getAs()->getDecl()-> + getUnderlyingType()->getAs(); + else + objcObjectPtr = type->getAs(); ++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(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(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(); const ObjCObjectType *objType = ptrType ? ptrType->getObjectType() Index: lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -626,7 +626,7 @@ : public RecursiveASTVisitor { public: IsObjCTypeParamDependentTypeVisitor() : Result(false) {} - bool VisitTypedefType(const TypedefType *Type) { + bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { if (isa(Type->getDecl())) { Result = true; return false; 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(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(Ty)) + return false; QualType PointeeTy = Ty->getPointeeType(); if (PointeeTy == QualType()) return false; Index: test/SemaObjC/kindof.m =================================================================== --- test/SemaObjC/kindof.m +++ test/SemaObjC/kindof.m @@ -385,7 +385,7 @@ @end @interface NSGeneric : 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 *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 : NSObject @end Index: test/SemaObjC/parameterized_classes.m =================================================================== --- test/SemaObjC/parameterized_classes.m +++ test/SemaObjC/parameterized_classes.m @@ -320,7 +320,7 @@ @interface PC23 : PC1 // expected-error{{unknown type name 'U'}} @end -@interface PC24 : PC1 // expected-error{{type argument 'T' (aka 'id') does not satisfy the bound ('NSObject *') of type parameter 'U'}} +@interface PC24 : PC1 // expected-error{{type argument 'T' does not satisfy the bound ('NSObject *') of type parameter 'U'}} @end @interface NSFoo : PC1 // okay 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 ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}} + +// rdar://25060179 +@interface MyMutableDictionary : NSObject +- (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType )key; // expected-note{{passing argument to parameter 'obj' here}} \ + // expected-note{{passing argument to parameter 'key' here}} +@end + +void bar(MyMutableDictionary *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 *'}} +} + +@interface MyTest : NSObject +- (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