diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index f420e85ee14694d82f277c06cb75dc8f8b6bc9ba..09fb9185095ed15a3b5a3aee23c47922479ce819 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1126,6 +1126,13 @@ public: QualType getObjCObjectType(QualType Base, ObjCProtocolDecl * const *Protocols, unsigned NumProtocols) const; + + bool ObjCObjectAdoptsQTypeProtocols(QualType QT, ObjCInterfaceDecl *Decl); + /// QIdProtocolsAdoptObjCObjectProtocols - Checks that protocols in + /// QT's qualified-id protocol list adopt all protocols in IDecl's list + /// of protocols. + bool QIdProtocolsAdoptObjCObjectProtocols(QualType QT, + ObjCInterfaceDecl *IDecl); /// \brief Return a ObjCObjectPointerType type for the given ObjCObjectType. QualType getObjCObjectPointerType(QualType OIT) const; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index a03cf9e7d47bfce7413c52ca437b55da26a656aa..0d1dc4e4d176317e638bfcb29f11a87e712778e8 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3507,6 +3507,64 @@ QualType ASTContext::getObjCObjectType(QualType BaseType, return QualType(T, 0); } +/// ObjCObjectAdoptsQTypeProtocols - Checks that protocols in IC's +/// protocol list adopt all protocols in QT's qualified-id protocol +/// list. +bool ASTContext::ObjCObjectAdoptsQTypeProtocols(QualType QT, + ObjCInterfaceDecl *IC) { + if (!QT->isObjCQualifiedIdType()) + return false; + + if (const ObjCObjectPointerType *OPT = QT->getAs<ObjCObjectPointerType>()) { + // If both the right and left sides have qualifiers. + for (ObjCObjectPointerType::qual_iterator I = OPT->qual_begin(), + E = OPT->qual_end(); I != E; ++I) { + ObjCProtocolDecl *Proto = *I; + if (!IC->ClassImplementsProtocol(Proto, false)) + return false; + } + return true; + } + return false; +} + +/// QIdProtocolsAdoptObjCObjectProtocols - Checks that protocols in +/// QT's qualified-id protocol list adopt all protocols in IDecl's list +/// of protocols. +bool ASTContext::QIdProtocolsAdoptObjCObjectProtocols(QualType QT, + ObjCInterfaceDecl *IDecl) { + if (!QT->isObjCQualifiedIdType()) + return false; + const ObjCObjectPointerType *OPT = QT->getAs<ObjCObjectPointerType>(); + if (!OPT) + return false; + if (!IDecl->hasDefinition()) + return false; + ObjCInterfaceDecl::protocol_iterator PI = IDecl->protocol_begin(), + E = IDecl->protocol_end(); + if (PI == E) + return (IDecl->getSuperClass() + ? QIdProtocolsAdoptObjCObjectProtocols(QT, IDecl->getSuperClass()) + : false); + + for (; PI != E; ++PI) { + // If both the right and left sides have qualifiers. + bool Adopts = false; + for (ObjCObjectPointerType::qual_iterator I = OPT->qual_begin(), + E = OPT->qual_end(); I != E; ++I) { + ObjCProtocolDecl *Proto = *I; + // return 'true' if '*PI' is in the inheritance hierarchy of Proto + if ((Adopts = ProtocolCompatibleWithProtocol(*PI, Proto))) + break; + } + if (!Adopts) + return (IDecl->getSuperClass() + ? QIdProtocolsAdoptObjCObjectProtocols(QT, IDecl->getSuperClass()) + : false); + } + return true; +} + /// getObjCObjectPointerType - Return a ObjCObjectPointerType type for /// the given object type. QualType ASTContext::getObjCObjectPointerType(QualType ObjectT) const { diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index cc8eacee61038d0f88bdb18ccd2c0b337c1cecce..6a0c09a4db200527a5a06bb03239060d5da7045b 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -3196,14 +3196,24 @@ static bool CheckObjCBridgeNSCast(Sema &S, QualType castType, Expr *castExpr) { castType->getAsObjCInterfacePointerType()) { ObjCInterfaceDecl *CastClass = InterfacePointerType->getObjectType()->getInterface(); - if ((CastClass == ExprClass) || (CastClass && ExprClass->isSuperClassOf(CastClass))) + if ((CastClass == ExprClass) || + (CastClass && ExprClass->isSuperClassOf(CastClass))) return true; S.Diag(castExpr->getLocStart(), diag::warn_objc_invalid_bridge) << T << Target->getName() << castType->getPointeeType(); return true; - } else { + } else if (castType->isObjCIdType() || + (S.Context.ObjCObjectAdoptsQTypeProtocols( + castType, ExprClass))) + // ok to cast to 'id'. + // casting to id<p-list> is ok if bridge type adopts all of + // p-list protocols. + return true; + else { S.Diag(castExpr->getLocStart(), diag::warn_objc_invalid_bridge) << T << Target->getName() << castType; + S.Diag(TDNDecl->getLocStart(), diag::note_declared_at); + S.Diag(Target->getLocStart(), diag::note_declared_at); return true; } } @@ -3221,7 +3231,6 @@ static bool CheckObjCBridgeNSCast(Sema &S, QualType castType, Expr *castExpr) { return false; } -// (CFErrorRef)ns static bool CheckObjCBridgeCFCast(Sema &S, QualType castType, Expr *castExpr) { QualType T = castType; while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr())) { @@ -3240,16 +3249,25 @@ static bool CheckObjCBridgeCFCast(Sema &S, QualType castType, Expr *castExpr) { castExpr->getType()->getAsObjCInterfacePointerType()) { ObjCInterfaceDecl *ExprClass = InterfacePointerType->getObjectType()->getInterface(); - if ((CastClass == ExprClass) || (ExprClass && CastClass->isSuperClassOf(ExprClass))) + if ((CastClass == ExprClass) || + (ExprClass && CastClass->isSuperClassOf(ExprClass))) return true; S.Diag(castExpr->getLocStart(), diag::warn_objc_invalid_bridge_to_cf) << castExpr->getType()->getPointeeType() << T; S.Diag(TDNDecl->getLocStart(), diag::note_declared_at); return true; - } else { + } else if (castExpr->getType()->isObjCIdType() || + (S.Context.QIdProtocolsAdoptObjCObjectProtocols( + castExpr->getType(), CastClass))) + // ok to cast an 'id' expression to a CFtype. + // ok to cast an 'id<plist>' expression to CFtype provided plist + // adopts all of CFtype's ObjetiveC's class plist. + return true; + else { S.Diag(castExpr->getLocStart(), diag::warn_objc_invalid_bridge_to_cf) << castExpr->getType() << castType; S.Diag(TDNDecl->getLocStart(), diag::note_declared_at); + S.Diag(Target->getLocStart(), diag::note_declared_at); return true; } } diff --git a/test/SemaObjC/objcbridge-attribute.m b/test/SemaObjC/objcbridge-attribute.m index 2db2ff4929e8df0e78e533519234a3abf017f0a0..c5bf9f843d5170572f73bd98acd8227cd3a62808 100644 --- a/test/SemaObjC/objcbridge-attribute.m +++ b/test/SemaObjC/objcbridge-attribute.m @@ -1,7 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -fobjc-arc -verify -Wno-objc-root-class %s // rdar://15454846 -typedef struct __attribute__ ((objc_bridge(NSError))) __CFErrorRef * CFErrorRef; // expected-note 2 {{declared here}} +typedef struct __attribute__ ((objc_bridge(NSError))) __CFErrorRef * CFErrorRef; // expected-note 5 {{declared here}} + +typedef struct __attribute__ ((objc_bridge(MyError))) __CFMyErrorRef * CFMyErrorRef; // expected-note 3 {{declared here}} typedef struct __attribute__((objc_bridge(12))) __CFMyColor *CFMyColorRef; // expected-error {{parameter of 'objc_bridge' attribute must be a single name of an Objective-C class}} @@ -43,11 +45,16 @@ id Test1(CFTestingRef cf) { typedef CFErrorRef CFErrorRef1; -typedef CFErrorRef1 CFErrorRef2; +typedef CFErrorRef1 CFErrorRef2; // expected-note {{declared here}} + +@protocol P1 @end +@protocol P2 @end +@protocol P3 @end +@protocol P4 @end -@interface NSError @end +@interface NSError<P1, P2, P3> @end // expected-note 5 {{declared here}} -@interface MyError : NSError +@interface MyError : NSError // expected-note 3 {{declared here}} @end @interface NSUColor @end @@ -64,3 +71,35 @@ void Test2(CFErrorRef2 cf, NSError *ns, NSString *str, Class c, CFUColor2Ref cf2 (void)(Class)cf; // expected-warning {{'CFErrorRef2' (aka 'struct __CFErrorRef *') bridges to NSError, not 'Class'}} (void)(CFErrorRef)c; // expected-warning {{'Class' cannot bridge to 'CFErrorRef'}} } + + +void Test3(CFErrorRef cf, NSError *ns) { + (void)(id)cf; // okay + (void)(id<P1, P2>)cf; // okay + (void)(id<P1, P2, P4>)cf; // expected-warning {{'CFErrorRef' (aka 'struct __CFErrorRef *') bridges to NSError, not 'id<P1,P2,P4>'}} +} + +void Test4(CFMyErrorRef cf) { + (void)(id)cf; // okay + (void)(id<P1, P2>)cf; // ok + (void)(id<P1, P2, P3>)cf; // ok + (void)(id<P2, P3>)cf; // ok + (void)(id<P1, P2, P4>)cf; // expected-warning {{'CFMyErrorRef' (aka 'struct __CFMyErrorRef *') bridges to MyError, not 'id<P1,P2,P4>'}} +} + +void Test5(id<P1, P2, P3> P123, id ID, id<P1, P2, P3, P4> P1234, id<P1, P2> P12, id<P2, P3> P23) { + (void)(CFErrorRef)ID; // ok + (void)(CFErrorRef)P123; // ok + (void)(CFErrorRef)P1234; // ok + (void)(CFErrorRef)P12; // expected-warning {{'id<P1,P2>' cannot bridge to 'CFErrorRef' (aka 'struct __CFErrorRef *')}} + (void)(CFErrorRef)P23; // expected-warning {{'id<P2,P3>' cannot bridge to 'CFErrorRef' (aka 'struct __CFErrorRef *')}} +} + +void Test6(id<P1, P2, P3> P123, id ID, id<P1, P2, P3, P4> P1234, id<P1, P2> P12, id<P2, P3> P23) { + + (void)(CFMyErrorRef)ID; // ok + (void)(CFMyErrorRef)P123; // ok + (void)(CFMyErrorRef)P1234; // ok + (void)(CFMyErrorRef)P12; // expected-warning {{'id<P1,P2>' cannot bridge to 'CFMyErrorRef' (aka 'struct __CFMyErrorRef *')}} + (void)(CFMyErrorRef)P23; // expected-warning {{'id<P2,P3>' cannot bridge to 'CFMyErrorRef' (aka 'struct __CFMyErrorRef *')}} +}