Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -722,25 +722,30 @@ return ctx.getObjCObjectPointerType(obj)->castAs(); } -template -static QualType simpleTransform(ASTContext &ctx, QualType type, F &&f); - namespace { -/// Visitor used by simpleTransform() to perform the transformation. -template -struct SimpleTransformVisitor - : public TypeVisitor, QualType> { +/// Visitor used to perform a simple type transformation that does not change +/// the semantics of the type. +template +struct SimpleTransformVisitor : public TypeVisitor { ASTContext &Ctx; - F &&TheFunc; QualType recurse(QualType type) { - return simpleTransform(Ctx, type, std::move(TheFunc)); + // Split out the qualifiers from the type. + SplitQualType splitType = type.split(); + + // Visit the type itself. + QualType result = static_cast(this)->Visit(splitType.Ty); + if (result.isNull()) + return result; + + // Reconstruct the transformed type by applying the local qualifiers + // from the split type. + return Ctx.getQualifiedType(result, splitType.Quals); } public: - SimpleTransformVisitor(ASTContext &ctx, F &&f) - : Ctx(ctx), TheFunc(std::move(f)) {} + explicit SimpleTransformVisitor(ASTContext &ctx) : Ctx(ctx) {} // None of the clients of this transformation can occur where // there are dependent types, so skip dependent types. @@ -1108,220 +1113,241 @@ #undef TRIVIAL_TYPE_CLASS }; -} // namespace +struct SubstObjCTypeArgsVisitor + : public SimpleTransformVisitor { + using BaseType = SimpleTransformVisitor; -/// Perform a simple type transformation that does not change the -/// semantics of the type. -template -static QualType simpleTransform(ASTContext &ctx, QualType type, F &&f) { - // Transform the type. If it changed, return the transformed result. - QualType transformed = f(type); - if (transformed.getAsOpaquePtr() != type.getAsOpaquePtr()) - return transformed; - - // Split out the qualifiers from the type. - SplitQualType splitType = type.split(); - - // Visit the type itself. - SimpleTransformVisitor visitor(ctx, std::forward(f)); - QualType result = visitor.Visit(splitType.Ty); - if (result.isNull()) - return result; + ArrayRef TypeArgs; + ObjCSubstitutionContext SubstContext; - // Reconstruct the transformed type by applying the local qualifiers - // from the split type. - return ctx.getQualifiedType(result, splitType.Quals); -} - -/// Substitute the given type arguments for Objective-C type -/// parameters within the given type, recursively. -QualType QualType::substObjCTypeArgs( - ASTContext &ctx, - ArrayRef typeArgs, - ObjCSubstitutionContext context) const { - return simpleTransform(ctx, *this, - [&](QualType type) -> QualType { - SplitQualType splitType = type.split(); + SubstObjCTypeArgsVisitor(ASTContext &ctx, ArrayRef typeArgs, + ObjCSubstitutionContext context) + : BaseType(ctx), TypeArgs(typeArgs), SubstContext(context) {} + QualType VisitObjCTypeParamType(const ObjCTypeParamType *OTPTy) { // Replace an Objective-C type parameter reference with the corresponding // type argument. - if (const auto *OTPTy = dyn_cast(splitType.Ty)) { - ObjCTypeParamDecl *typeParam = OTPTy->getDecl(); - // If we have type arguments, use them. - if (!typeArgs.empty()) { - QualType argType = typeArgs[typeParam->getIndex()]; - if (OTPTy->qual_empty()) - return ctx.getQualifiedType(argType, splitType.Quals); - - // Apply protocol lists if exists. - bool hasError; - SmallVector protocolsVec; - protocolsVec.append(OTPTy->qual_begin(), - OTPTy->qual_end()); - ArrayRef protocolsToApply = protocolsVec; - QualType resultTy = ctx.applyObjCProtocolQualifiers(argType, - protocolsToApply, hasError, true/*allowOnPointerType*/); - - return ctx.getQualifiedType(resultTy, splitType.Quals); - } + ObjCTypeParamDecl *typeParam = OTPTy->getDecl(); + // If we have type arguments, use them. + if (!TypeArgs.empty()) { + QualType argType = TypeArgs[typeParam->getIndex()]; + if (OTPTy->qual_empty()) + return argType; + + // Apply protocol lists if exists. + bool hasError; + SmallVector protocolsVec; + protocolsVec.append(OTPTy->qual_begin(), OTPTy->qual_end()); + ArrayRef protocolsToApply = protocolsVec; + return Ctx.applyObjCProtocolQualifiers( + argType, protocolsToApply, hasError, true/*allowOnPointerType*/); + } - switch (context) { - case ObjCSubstitutionContext::Ordinary: - case ObjCSubstitutionContext::Parameter: - case ObjCSubstitutionContext::Superclass: - // Substitute the bound. - return ctx.getQualifiedType(typeParam->getUnderlyingType(), - splitType.Quals); - - case ObjCSubstitutionContext::Result: - case ObjCSubstitutionContext::Property: { - // Substitute the __kindof form of the underlying type. - const auto *objPtr = typeParam->getUnderlyingType() - ->castAs(); - - // __kindof types, id, and Class don't need an additional - // __kindof. - if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) - return ctx.getQualifiedType(typeParam->getUnderlyingType(), - 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); - } + switch (SubstContext) { + case ObjCSubstitutionContext::Ordinary: + case ObjCSubstitutionContext::Parameter: + case ObjCSubstitutionContext::Superclass: + // Substitute the bound. + return typeParam->getUnderlyingType(); + + case ObjCSubstitutionContext::Result: + case ObjCSubstitutionContext::Property: { + // Substitute the __kindof form of the underlying type. + const auto *objPtr = + typeParam->getUnderlyingType()->castAs(); + + // __kindof types, id, and Class don't need an additional + // __kindof. + if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) + return typeParam->getUnderlyingType(); + + // Add __kindof. + const auto *obj = objPtr->getObjectType(); + QualType resultTy = Ctx.getObjCObjectType( + obj->getBaseType(), obj->getTypeArgsAsWritten(), obj->getProtocols(), + /*isKindOf=*/true); + + // Rebuild object pointer type. + return Ctx.getObjCObjectPointerType(resultTy); } } + } + + QualType VisitFunctionType(const FunctionType *funcType) { + // If we have a function type, update the substitution context + // appropriately. + + //Substitute result type. + QualType returnType = funcType->getReturnType().substObjCTypeArgs( + Ctx, TypeArgs, ObjCSubstitutionContext::Result); + if (returnType.isNull()) + return {}; + + // Handle non-prototyped functions, which only substitute into the result + // type. + if (isa(funcType)) { + // If the return type was unchanged, do nothing. + if (returnType.getAsOpaquePtr() == + funcType->getReturnType().getAsOpaquePtr()) + return BaseType::VisitFunctionType(funcType); + + // Otherwise, build a new type. + return Ctx.getFunctionNoProtoType(returnType, funcType->getExtInfo()); + } - // If we have a function type, update the context appropriately. - if (const auto *funcType = dyn_cast(splitType.Ty)) { - // Substitute result type. - QualType returnType = funcType->getReturnType().substObjCTypeArgs( - ctx, - typeArgs, - ObjCSubstitutionContext::Result); - if (returnType.isNull()) + const auto *funcProtoType = cast(funcType); + + // Transform parameter types. + SmallVector paramTypes; + bool paramChanged = false; + for (auto paramType : funcProtoType->getParamTypes()) { + QualType newParamType = paramType.substObjCTypeArgs( + Ctx, TypeArgs, ObjCSubstitutionContext::Parameter); + if (newParamType.isNull()) return {}; - // Handle non-prototyped functions, which only substitute into the result - // type. - if (isa(funcType)) { - // If the return type was unchanged, do nothing. - if (returnType.getAsOpaquePtr() - == funcType->getReturnType().getAsOpaquePtr()) - return type; + if (newParamType.getAsOpaquePtr() != paramType.getAsOpaquePtr()) + paramChanged = true; - // Otherwise, build a new type. - return ctx.getFunctionNoProtoType(returnType, funcType->getExtInfo()); - } + paramTypes.push_back(newParamType); + } - const auto *funcProtoType = cast(funcType); - - // Transform parameter types. - SmallVector paramTypes; - bool paramChanged = false; - for (auto paramType : funcProtoType->getParamTypes()) { - QualType newParamType = paramType.substObjCTypeArgs( - ctx, - typeArgs, - ObjCSubstitutionContext::Parameter); - if (newParamType.isNull()) + // Transform extended info. + FunctionProtoType::ExtProtoInfo info = funcProtoType->getExtProtoInfo(); + bool exceptionChanged = false; + if (info.ExceptionSpec.Type == EST_Dynamic) { + SmallVector exceptionTypes; + for (auto exceptionType : info.ExceptionSpec.Exceptions) { + QualType newExceptionType = exceptionType.substObjCTypeArgs( + Ctx, TypeArgs, ObjCSubstitutionContext::Ordinary); + if (newExceptionType.isNull()) return {}; - if (newParamType.getAsOpaquePtr() != paramType.getAsOpaquePtr()) - paramChanged = true; + if (newExceptionType.getAsOpaquePtr() != exceptionType.getAsOpaquePtr()) + exceptionChanged = true; - paramTypes.push_back(newParamType); + exceptionTypes.push_back(newExceptionType); } - // Transform extended info. - FunctionProtoType::ExtProtoInfo info = funcProtoType->getExtProtoInfo(); - bool exceptionChanged = false; - if (info.ExceptionSpec.Type == EST_Dynamic) { - SmallVector exceptionTypes; - for (auto exceptionType : info.ExceptionSpec.Exceptions) { - QualType newExceptionType = exceptionType.substObjCTypeArgs( - ctx, - typeArgs, - ObjCSubstitutionContext::Ordinary); - if (newExceptionType.isNull()) - return {}; - - if (newExceptionType.getAsOpaquePtr() - != exceptionType.getAsOpaquePtr()) - exceptionChanged = true; - - exceptionTypes.push_back(newExceptionType); - } - - if (exceptionChanged) { - info.ExceptionSpec.Exceptions = - llvm::makeArrayRef(exceptionTypes).copy(ctx); - } + if (exceptionChanged) { + info.ExceptionSpec.Exceptions = + llvm::makeArrayRef(exceptionTypes).copy(Ctx); } + } - if (returnType.getAsOpaquePtr() - == funcProtoType->getReturnType().getAsOpaquePtr() && - !paramChanged && !exceptionChanged) - return type; + if (returnType.getAsOpaquePtr() == + funcProtoType->getReturnType().getAsOpaquePtr() && + !paramChanged && !exceptionChanged) + return BaseType::VisitFunctionType(funcType); - return ctx.getFunctionType(returnType, paramTypes, info); - } + return Ctx.getFunctionType(returnType, paramTypes, info); + } + QualType VisitObjCObjectType(const ObjCObjectType *objcObjectType) { // Substitute into the type arguments of a specialized Objective-C object // type. - if (const auto *objcObjectType = dyn_cast(splitType.Ty)) { - if (objcObjectType->isSpecializedAsWritten()) { - SmallVector newTypeArgs; - bool anyChanged = false; - for (auto typeArg : objcObjectType->getTypeArgsAsWritten()) { - QualType newTypeArg = typeArg.substObjCTypeArgs( - ctx, typeArgs, - ObjCSubstitutionContext::Ordinary); - if (newTypeArg.isNull()) - return {}; - - if (newTypeArg.getAsOpaquePtr() != typeArg.getAsOpaquePtr()) { - // If we're substituting based on an unspecialized context type, - // produce an unspecialized type. - ArrayRef protocols( - objcObjectType->qual_begin(), - objcObjectType->getNumProtocols()); - if (typeArgs.empty() && - context != ObjCSubstitutionContext::Superclass) { - return ctx.getObjCObjectType( - objcObjectType->getBaseType(), {}, - protocols, - objcObjectType->isKindOfTypeAsWritten()); - } - - anyChanged = true; + if (objcObjectType->isSpecializedAsWritten()) { + SmallVector newTypeArgs; + bool anyChanged = false; + for (auto typeArg : objcObjectType->getTypeArgsAsWritten()) { + QualType newTypeArg = typeArg.substObjCTypeArgs( + Ctx, TypeArgs, ObjCSubstitutionContext::Ordinary); + if (newTypeArg.isNull()) + return {}; + + if (newTypeArg.getAsOpaquePtr() != typeArg.getAsOpaquePtr()) { + // If we're substituting based on an unspecialized context type, + // produce an unspecialized type. + ArrayRef protocols( + objcObjectType->qual_begin(), objcObjectType->getNumProtocols()); + if (TypeArgs.empty() && + SubstContext != ObjCSubstitutionContext::Superclass) { + return Ctx.getObjCObjectType( + objcObjectType->getBaseType(), {}, protocols, + objcObjectType->isKindOfTypeAsWritten()); } - newTypeArgs.push_back(newTypeArg); + anyChanged = true; } - if (anyChanged) { - ArrayRef protocols( - objcObjectType->qual_begin(), - objcObjectType->getNumProtocols()); - return ctx.getObjCObjectType(objcObjectType->getBaseType(), - newTypeArgs, protocols, - objcObjectType->isKindOfTypeAsWritten()); - } + newTypeArgs.push_back(newTypeArg); } - return type; + if (anyChanged) { + ArrayRef protocols( + objcObjectType->qual_begin(), objcObjectType->getNumProtocols()); + return Ctx.getObjCObjectType(objcObjectType->getBaseType(), newTypeArgs, + protocols, + objcObjectType->isKindOfTypeAsWritten()); + } } - return type; - }); + return BaseType::VisitObjCObjectType(objcObjectType); + } + + QualType VisitAttributedType(const AttributedType *attrType) { + QualType newType = BaseType::VisitAttributedType(attrType); + if (newType.isNull()) + return {}; + + const auto *newAttrType = dyn_cast(newType.getTypePtr()); + if (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(); + const ObjCObjectType *objType = ptrType + ? ptrType->getObjectType() + : newEquivType->getAs(); + 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 + : public SimpleTransformVisitor { + using BaseType = SimpleTransformVisitor; + + explicit StripObjCKindOfTypeVisitor(ASTContext &ctx) : BaseType(ctx) {} + + QualType VisitObjCObjectType(const ObjCObjectType *objType) { + if (!objType->isKindOfType()) + return BaseType::VisitObjCObjectType(objType); + + QualType baseType = objType->getBaseType().stripObjCKindOfType(Ctx); + return Ctx.getObjCObjectType(baseType, objType->getTypeArgsAsWritten(), + objType->getProtocols(), + /*isKindOf=*/false); + } +}; + +} // namespace + +/// Substitute the given type arguments for Objective-C type +/// parameters within the given type, recursively. +QualType QualType::substObjCTypeArgs(ASTContext &ctx, + ArrayRef typeArgs, + ObjCSubstitutionContext context) const { + SubstObjCTypeArgsVisitor visitor(ctx, typeArgs, context); + return visitor.recurse(*this); } QualType QualType::substObjCMemberType(QualType objectType, @@ -1336,25 +1362,8 @@ QualType QualType::stripObjCKindOfType(const ASTContext &constCtx) const { // FIXME: Because ASTContext::getAttributedType() is non-const. auto &ctx = const_cast(constCtx); - return simpleTransform(ctx, *this, - [&](QualType type) -> QualType { - SplitQualType splitType = type.split(); - if (auto *objType = splitType.Ty->getAs()) { - 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; - }); + StripObjCKindOfTypeVisitor visitor(ctx); + return visitor.recurse(*this); } QualType QualType::getAtomicUnqualifiedType() const { Index: clang/test/SemaObjC/kindof.m =================================================================== --- clang/test/SemaObjC/kindof.m +++ clang/test/SemaObjC/kindof.m @@ -384,9 +384,17 @@ } @end +// --------------------------------------------------------------------------- +// __kindof on type parameters +// --------------------------------------------------------------------------- + @interface NSGeneric : 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 @@ } @end +@interface NSDefaultGeneric : NSObject +@property (copy) ObjectType object; +@property (copy) __kindof ObjectType kindof_object; +@end + void testGeneric(NSGeneric *generic) { NSObject *NSObject_obj; // Assign from NSObject_obj to __kindof NSString*. @@ -403,6 +416,45 @@ [generic test:NSString_str]; } +void testGenericAssignment() { + NSMutableString *NSMutableString_str; + NSNumber *NSNumber_obj; + + NSGeneric *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_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 *generic; +} + +void testKindofNullability(NSGeneric *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 : NSObject @end