From ffad82b290ed4622bce0e221f8525eced89db528 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 7 Jul 2015 03:58:42 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@241548 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 5 +- include/clang/AST/Type.h | 72 ++++- include/clang/Basic/Attr.td | 5 + include/clang/Basic/DiagnosticSemaKinds.td | 6 + include/clang/Basic/TokenKinds.def | 3 + include/clang/Sema/Sema.h | 4 + lib/AST/ASTContext.cpp | 111 +++++-- lib/AST/ASTDiagnostic.cpp | 3 +- lib/AST/ASTImporter.cpp | 4 +- lib/AST/ItaniumMangle.cpp | 13 + lib/AST/Type.cpp | 171 ++++++++-- lib/AST/TypePrinter.cpp | 15 +- lib/Basic/IdentifierTable.cpp | 4 +- lib/Lex/PPMacroExpansion.cpp | 1 + lib/Parse/ParseDecl.cpp | 19 ++ lib/Parse/ParseTentative.cpp | 1 + lib/Sema/SemaExprObjC.cpp | 27 +- lib/Sema/SemaOverload.cpp | 18 +- lib/Sema/SemaType.cpp | 84 ++++- lib/Serialization/ASTReader.cpp | 3 +- lib/Serialization/ASTWriter.cpp | 1 + test/CodeGenObjCXX/mangle.mm | 15 + test/PCH/objc_kindof.m | 33 ++ test/SemaObjC/kindof.m | 304 ++++++++++++++++++ test/SemaObjC/parameterized_classes_subst.m | 14 +- .../SemaObjCXX/parameterized_classes_subst.mm | 2 +- 26 files changed, 841 insertions(+), 97 deletions(-) create mode 100644 test/PCH/objc_kindof.m create mode 100644 test/SemaObjC/kindof.m diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index e7a90be8b2..a2bd55a089 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -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 typeArgs, - ArrayRef protocols) const; + ArrayRef protocols, + bool isKindOf) const; bool ObjCObjectAdoptsQTypeProtocols(QualType QT, ObjCInterfaceDecl *Decl); /// QIdProtocolsAdoptObjCObjectProtocols - Checks that protocols in diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index c3188e4a24..18d054733d 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -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 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 + /// . + /// + /// \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 . + /// + /// 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 typeArgs, - ArrayRef protocols); + ArrayRef 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 getProtocols() const { + return ArrayRef(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 ObjCObjectType, public llvm::FoldingSetNode { ObjCObjectTypeImpl(QualType Canonical, QualType Base, ArrayRef typeArgs, - ArrayRef protocols) - : ObjCObjectType(Canonical, Base, typeArgs, protocols) {} + ArrayRef protocols, + bool isKindOf) + : ObjCObjectType(Canonical, Base, typeArgs, protocols, isKindOf) {} public: void Profile(llvm::FoldingSetNodeID &ID); static void Profile(llvm::FoldingSetNodeID &ID, QualType Base, ArrayRef typeArgs, - ArrayRef protocols); + ArrayRef 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

' 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()); } diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index b14d5770d2..fb1eb58dcc 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -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]>; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 99400985d7..e606cbd258 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -978,6 +978,12 @@ def warning_multiple_selectors: Warning< "several methods with selector %0 of mismatched types are found " "for the @selector expression">, InGroup, 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">; diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index ed2aa82d17..6c0a6414f9 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -526,6 +526,9 @@ KEYWORD(__bridge_transfer , KEYARC) 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) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index edf47e233c..fc6cb7d550 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index a92f0052f0..58d703a67e 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3615,21 +3615,24 @@ QualType ASTContext::getObjCObjectType(QualType BaseType, 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 typeArgs, - ArrayRef protocols) const { + ArrayRef 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(baseType)) + if (typeArgs.empty() && protocols.empty() && !isKindOf && + isa(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(const ObjCObjectPointerType *LHSOPT, RHS->isObjCUnqualifiedIdOrClass()) return true; - if (LHS->isObjCQualifiedId() || RHS->isObjCQualifiedId()) - return ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0), - QualType(RHSOPT,0), - false); + // 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 ObjCQualifiedClassTypesAreCompatible(QualType(LHSOPT,0), - QualType(RHSOPT,0)); + 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::canAssignObjCInterfacesInBlockPointer( 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(ASTContext &Context, // Check that the given Objective-C type argument lists are equivalent. static bool sameObjCTypeArgs(const ASTContext &ctx, ArrayRef lhsArgs, - ArrayRef rhsArgs) { + ArrayRef 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::areCommonBaseCompatible( 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::areCommonBaseCompatible( // 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::areCommonBaseCompatible( 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::areCommonBaseCompatible( 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(const ObjCObjectType *LHS, // 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; } } diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index d6de454846..68a58a517d 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -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()); } } diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index f7bfcaacd7..d264e962be 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -1853,7 +1853,6 @@ QualType ASTNodeImporter::VisitObjCObjectType(const ObjCObjectType *T) { TypeArgs.push_back(ImportedTypeArg); } - SmallVector Protocols; for (auto *P : T->quals()) { ObjCProtocolDecl *Protocol @@ -1864,7 +1863,8 @@ QualType ASTNodeImporter::VisitObjCObjectType(const ObjCObjectType *T) { } return Importer.getToContext().getObjCObjectType(ToBaseType, TypeArgs, - Protocols); + Protocols, + T->isKindOfTypeAsWritten()); } QualType diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 0134c090d6..dac803e5d2 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2379,6 +2379,10 @@ void CXXNameMangler::mangleType(const ObjCInterfaceType *T) { } 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 ObjCObjectType *T) { QualOS.flush(); Out << 'U' << QualStr.size() << QualStr; } + mangleType(T->getBaseType()); + + if (T->isSpecialized()) { + // Mangle type arguments as I + E + Out << 'I'; + for (auto typeArg : T->getTypeArgs()) + mangleType(typeArg); + Out << 'E'; + } } void CXXNameMangler::mangleType(const BlockPointerType *T) { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index c3d5feb78a..73cf60d34b 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -466,15 +466,61 @@ const RecordType *Type::getAsUnionType() const { return nullptr; } +bool Type::isObjCIdOrObjectKindOfType(const ASTContext &ctx, + const ObjCObjectType *&bound) const { + bound = nullptr; + + const ObjCObjectPointerType *OPT = getAs(); + 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(); + return true; +} + +bool Type::isObjCClassOrClassKindOfType() const { + const ObjCObjectPointerType *OPT = getAs(); + 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 typeArgs, - ArrayRef protocols) + ArrayRef 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 ObjCObjectType::getTypeArgs() const { return { }; } +bool ObjCObjectType::isKindOfType() const { + if (isKindOfTypeAsWritten()) + return true; + + // Look at the base type, which might have type arguments. + if (auto objcObject = getBaseType()->getAs()) { + // Terminate when we reach an interface type. + if (isa(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()) { + 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(); +} + namespace { /// Perform a simple type transformation that does not change the @@ -888,7 +980,8 @@ QualType simpleTransform(ASTContext &ctx, QualType type, F &&f) { 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(); + + // __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(QualType objectType, return *this; } +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; + }); +} + Optional> Type::getObjCSubstitutions( const DeclContext *dc) const { // Look through method scopes. @@ -2745,7 +2875,9 @@ bool AttributedType::isCallingConv() const { 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 ASTContext &Context, const Type *T) const { void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID, QualType BaseType, ArrayRef typeArgs, - ArrayRef protocols) { + ArrayRef protocols, + bool isKindOf) { ID.AddPointer(BaseType.getAsOpaquePtr()); ID.AddInteger(typeArgs.size()); for (auto typeArg : typeArgs) @@ -2903,11 +3036,13 @@ void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID, 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 { diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index ab63babf3f..0bb50c6ba8 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -1129,6 +1129,9 @@ void TypePrinter::printAttributedBefore(const AttributedType *T, 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(const AttributedType *T, 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::printObjCInterfaceAfter(const ObjCInterfaceType *T, 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(const ObjCObjectType *T, } 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); } diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index 36fba994f1..dcb7603bf5 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/lib/Basic/IdentifierTable.cpp @@ -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(const LangOptions &LangOpts, // 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; } diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 0fb4456790..6790908fe4 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1088,6 +1088,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .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", diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 9c12abab39..bc253f7f86 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2601,6 +2601,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, /// [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(DeclSpec &DS, 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 DisambiguatingWithExpression) { 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(DeclSpec &DS, unsigned AttrReqs, 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. diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 3fdd902f07..9d2a2b931e 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -1281,6 +1281,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw__Nonnull: case tok::kw__Nullable: case tok::kw__Null_unspecified: + case tok::kw___kindof: return TPResult::True; // Borland diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 51ab68a8ab..6cd0626219 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -962,7 +962,8 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, Context.getObjCObjectType(Context.ObjCBuiltinIdTy, { }, llvm::makeArrayRef( (ObjCProtocolDecl**) PQ, - 1)); + 1), + false); QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying); } } @@ -2620,35 +2621,41 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // 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"), 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) { diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index a0fdcd78e5..31f581dc15 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2152,23 +2152,7 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType, 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(); diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 06d832c9de..768efeb64a 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -643,6 +643,9 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state, 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 &S, SourceLocation loc, QualType type, } // 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 applyObjCProtocolQualifiers( return ctx.getObjCObjectType(objT->getBaseType(), objT->getTypeArgsAsWritten(), - protocols); + protocols, + objT->isKindOfTypeAsWritten()); } if (type->isObjCObjectType()) { @@ -953,18 +957,22 @@ static QualType applyObjCProtocolQualifiers( // 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 if (type->isObjCIdType()) { - type = ctx.getObjCObjectType(ctx.ObjCBuiltinIdTy, { }, protocols); + const ObjCObjectPointerType *objPtr = type->castAs(); + type = ctx.getObjCObjectType(ctx.ObjCBuiltinIdTy, { }, protocols, + objPtr->isKindOfType()); return ctx.getObjCObjectPointerType(type); } // Class if (type->isObjCClassType()) { - type = ctx.getObjCObjectType(ctx.ObjCBuiltinClassTy, { }, protocols); + const ObjCObjectPointerType *objPtr = type->castAs(); + type = ctx.getObjCObjectType(ctx.ObjCBuiltinClassTy, { }, protocols, + objPtr->isKindOfType()); return ctx.getObjCObjectPointerType(type); } @@ -1021,7 +1029,8 @@ TypeResult Sema::actOnObjCProtocolQualifierType( 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 getAttrListKind(AttributedType::Kind kind) { 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(QualType &type, 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(); + const ObjCObjectType *objType = ptrType ? ptrType->getObjectType() + : type->getAs(); + + // 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(TypeProcessingState &state, QualType &type, 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(TypeProcessingState &state, QualType &type, } 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; diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 89464c443e..3de1159e78 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -5271,7 +5271,8 @@ QualType ASTReader::readTypeRecord(unsigned Index) { SmallVector Protos; for (unsigned I = 0; I != NumProtos; ++I) Protos.push_back(ReadDeclAs(*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: { diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index f54bc5f491..22925c4b4b 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -427,6 +427,7 @@ void ASTTypeWriter::VisitObjCObjectType(const ObjCObjectType *T) { Record.push_back(T->getNumProtocols()); for (const auto *I : T->quals()) Writer.AddDeclRef(I, Record); + Record.push_back(T->isKindOfTypeAsWritten()); Code = TYPE_OBJC_OBJECT; } diff --git a/test/CodeGenObjCXX/mangle.mm b/test/CodeGenObjCXX/mangle.mm index 5b944caf1f..bcb920ba1e 100644 --- a/test/CodeGenObjCXX/mangle.mm +++ b/test/CodeGenObjCXX/mangle.mm @@ -98,3 +98,18 @@ template<> void X::f() {} // CHECK-LABEL: define void @_ZN1XIP1AE1fEv template<> void X*>::f() {} // CHECK-LABEL: define void @_ZN1XIPU11objcproto1P1AE1fEv + +// CHECK-LABEL: define void @_Z12kindof_test2PU8__kindof5Test2 +void kindof_test2(__kindof Test2 *t2) { } + +@interface Parameterized : A +@end + +// CHECK-LABEL: define void @_Z19parameterized_test1P13ParameterizedIP1AP4TestE +void parameterized_test1(Parameterized *p) {} + +// CHECK-LABEL: define void @_Z19parameterized_test2PU8__kindof13ParameterizedIP1AP4TestE +void parameterized_test2(__kindof Parameterized *p) {} + +// CHECK-LABEL: define void @_Z19parameterized_test3P13Parameterized +void parameterized_test3(Parameterized *p) {} diff --git a/test/PCH/objc_kindof.m b/test/PCH/objc_kindof.m new file mode 100644 index 0000000000..437c417104 --- /dev/null +++ b/test/PCH/objc_kindof.m @@ -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 +@protocol NSObject +@end + +@protocol NSCopying +@end + +__attribute__((objc_root_class)) +@interface NSObject +@end + +@interface NSString : NSObject +@end + +@interface NSMutableString : NSString +@end + +@interface NSNumber : NSObject +@end + +extern __kindof NSObject *kindof_NSObject_NSCopying; + +#else +void testPrettyPrint(int *ip) { + ip = kindof_NSObject_NSCopying; // expected-warning{{from '__kindof NSObject *'}} +} + +#endif diff --git a/test/SemaObjC/kindof.m b/test/SemaObjC/kindof.m new file mode 100644 index 0000000000..7f795c5148 --- /dev/null +++ b/test/SemaObjC/kindof.m @@ -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 + +@protocol NSObject +@end + +@protocol NSCopying +- (id)copy; ++ (Class)classCopy; +@end + +@protocol NSRandomProto +- (void)randomMethod; ++ (void)randomClassMethod; +@end + +__attribute__((objc_root_class)) +@interface NSObject +- (NSObject *)retain; +@end + +@interface NSString : NSObject +- (NSString *)stringByAppendingString:(NSString *)string; ++ (instancetype)string; +@end + +@interface NSMutableString : NSString +- (void)appendString:(NSString *)string; +@end + +@interface NSNumber : NSObject +- (NSNumber *)numberByAddingNumber:(NSNumber *)number; +@end + +// --------------------------------------------------------------------------- +// Parsing and semantic analysis for __kindof +// --------------------------------------------------------------------------- + +// Test proper application of __kindof. +typedef __kindof NSObject *typedef1; +typedef NSObject __kindof *typedef2; +typedef __kindof NSObject typedef3; +typedef NSObject __kindof *typedef4; +typedef __kindof id typedef5; +typedef __kindof Class 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@-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 *kindof_NSCopying; + ip = kindof_NSCopying; // expected-warning{{from '__kindof id *'}} + + __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 kindof_id_NSCopying_obj; + id 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 kindof_id_NSCopying_obj; + id 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 *ptr_kindof_id_NSCopying_obj; + id *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 kindof_NSCopying_obj; + id NSCopying_obj; + kindof_NSString_obj = kindof_NSCopying_obj; + kindof_NSString_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id'}} + NSString_obj = kindof_NSCopying_obj; + NSString_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id'}} + kindof_NSObject_obj = kindof_NSCopying_obj; + kindof_NSObject_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id'}} + NSObject_obj = kindof_NSCopying_obj; + NSObject_obj = NSCopying_obj; // expected-warning{{from incompatible type 'id'}} +} + +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 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 kindof_NSCopying) { + [kindof_NSCopying classCopy]; // in protocol + [kindof_NSCopying string]; // in some class + [kindof_NSCopying randomClassMethod]; // in unrelated protocol +} + +// --------------------------------------------------------------------------- +// __kindof within specialized types +// --------------------------------------------------------------------------- +@interface NSArray : NSObject +@end + +void implicit_convert_array(NSArray<__kindof NSString *> *kindofStringsArray, + NSArray *stringsArray, + NSArray<__kindof NSMutableString *> + *kindofMutStringsArray, + NSArray *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 *> *kindofStringsArrayArray; + NSArray *> *stringsArrayArray; + kindofStringsArrayArray = stringsArrayArray; + stringsArrayArray = kindofStringsArrayArray; +} + +// --------------------------------------------------------------------------- +// __kindof + nullability +// --------------------------------------------------------------------------- + +void testNullability() { + // The base type being a pointer type tickles the bug. + extern __kindof id __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}} +} diff --git a/test/SemaObjC/parameterized_classes_subst.m b/test/SemaObjC/parameterized_classes_subst.m index 6804bdc795..dc243069ca 100644 --- a/test/SemaObjC/parameterized_classes_subst.m +++ b/test/SemaObjC/parameterized_classes_subst.m @@ -18,6 +18,9 @@ __attribute__((objc_root_class)) @interface NSString : NSObject @end +@interface NSMutableString : NSString +@end + @interface NSNumber : NSObject @end @@ -144,6 +147,7 @@ void test_message_send_result( NSMutableSet *mutSet, MutableSetOfArrays *mutArraySet, NSArray *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 alloc] initWithArray: stringArray]; // okay [[NSMutableArray alloc] initWithArray: stringArray]; // expected-warning{{sending 'NSArray *' to parameter of type 'NSArray *'}} + + 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'}} + + ip = [[NSViewController alloc] init].view; // expected-warning{{from '__kindof NSView *'}} } void test_property_write( diff --git a/test/SemaObjCXX/parameterized_classes_subst.mm b/test/SemaObjCXX/parameterized_classes_subst.mm index cd0096c3fe..1b4c86edb7 100644 --- a/test/SemaObjCXX/parameterized_classes_subst.mm +++ b/test/SemaObjCXX/parameterized_classes_subst.mm @@ -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'}} } void test_property_write( -- 2.40.0