From 38d0c47ac00718bc55655b6e9cbc9029c0b458b3 Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Mon, 9 Jun 2014 21:42:01 +0000 Subject: [PATCH] Objective-C. Consider block pointer as NSObject as well as conforming to 'NSCopying' protocol when diagnosing block to ObjC pointer conversion. // rdar://16739120 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@210491 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaExpr.cpp | 35 +++++++++++++++++++++++++++++-- test/SemaObjC/block-type-safety.m | 21 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d767b3c8e7..1e56410bd4 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -5478,6 +5478,36 @@ static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS, return ResultTy; } +/// \brief Returns true if QT is quelified-id and implements 'NSObject' and/or +/// 'NSCopying' protocols (and nothing else); or QT is an NSObject and optionally +/// implements 'NSObject' and/or NSCopying' protocols (and nothing else). +static bool isObjCPtrBlockCompatible(Sema &S, ASTContext &C, QualType QT) { + if (QT->isObjCIdType()) + return true; + + const ObjCObjectPointerType *OPT = QT->getAs(); + if (!OPT) + return false; + + if (ObjCInterfaceDecl *ID = OPT->getInterfaceDecl()) + if (ID->getIdentifier() != &C.Idents.get("NSObject")) + return false; + + ObjCProtocolDecl* PNSCopying = + S.LookupProtocol(&C.Idents.get("NSCopying"), SourceLocation()); + ObjCProtocolDecl* PNSObject = + S.LookupProtocol(&C.Idents.get("NSObject"), SourceLocation()); + + for (auto *Proto : OPT->quals()) { + if ((PNSCopying && declaresSameEntity(Proto, PNSCopying)) || + (PNSObject && declaresSameEntity(Proto, PNSObject))) + ; + else + return false; + } + return true; +} + /// \brief Return the resulting type when the operands are both block pointers. static QualType checkConditionalBlockPointerCompatibility(Sema &S, ExprResult &LHS, @@ -6435,8 +6465,9 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, return IncompatiblePointer; } - // T^ -> id; not T^ ->A* and not T^ -> id

- if (RHSType->isBlockPointerType() && LHSType->isObjCIdType()) { + // Only under strict condition T^ is compatible with an Objective-C pointer. + if (RHSType->isBlockPointerType() && + isObjCPtrBlockCompatible(*this, Context, LHSType)) { maybeExtendBlockObject(*this, RHS); Kind = CK_BlockPointerToObjCPointerCast; return Compatible; diff --git a/test/SemaObjC/block-type-safety.m b/test/SemaObjC/block-type-safety.m index 45866704b5..b2c4398dc3 100644 --- a/test/SemaObjC/block-type-safety.m +++ b/test/SemaObjC/block-type-safety.m @@ -23,6 +23,7 @@ void r1(Sub* (^f)()) { // expected-note{{passing argument to parameter 'f' here} } @protocol NSObject; +@class NSObject; void r2 (id (^f) (void)) { id o = f(); @@ -177,3 +178,23 @@ NSArray* anArray1; aBlock = anArray1; // expected-error {{assigning to 'void (^)()' from incompatible type 'NSArray *'}} } +void Test2() { + void (^aBlock)(); + id anQualId1 = aBlock; // Ok + id anQualId2 = aBlock; // Ok + id anQualId3 = aBlock; // Ok + id anQualId4 = aBlock; // expected-error {{initializing 'id' with an expression of incompatible type 'void (^)()'}} + id anQualId5 = aBlock; // expected-error {{initializing 'id' with an expression of incompatible type 'void (^)()'}} + id anQualId6 = aBlock; // Ok +} + +void Test3() { + void (^aBlock)(); + NSObject *NSO = aBlock; // Ok + NSObject *NSO1 = aBlock; // Ok + NSObject *NSO2 = aBlock; // Ok + NSObject *NSO3 = aBlock; // Ok + NSObject *NSO4 = aBlock; // expected-error {{initializing 'NSObject *' with an expression of incompatible type 'void (^)()'}} + NSObject *NSO5 = aBlock; // expected-error {{initializing 'NSObject *' with an expression of incompatible type 'void (^)()'}} + NSObject *NSO6 = aBlock; // Ok +} -- 2.40.0