From e100097e424fb1ead2957f7c083bafb960043888 Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Wed, 20 Nov 2013 00:32:12 +0000 Subject: [PATCH] ObjectiveC ARC. validate toll free bridge casting to or from 'id' and qualified-id types. // rdar://15454846 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@195178 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 7 ++++ lib/AST/ASTContext.cpp | 58 ++++++++++++++++++++++++++++ lib/Sema/SemaExprObjC.cpp | 28 +++++++++++--- test/SemaObjC/objcbridge-attribute.m | 47 ++++++++++++++++++++-- 4 files changed, 131 insertions(+), 9 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index f420e85ee1..09fb918509 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 a03cf9e7d4..0d1dc4e4d1 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()) { + // 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(); + 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 cc8eacee61..6a0c09a4db 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 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(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' 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 2db2ff4929..c5bf9f843d 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 @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)cf; // okay + (void)(id)cf; // expected-warning {{'CFErrorRef' (aka 'struct __CFErrorRef *') bridges to NSError, not 'id'}} +} + +void Test4(CFMyErrorRef cf) { + (void)(id)cf; // okay + (void)(id)cf; // ok + (void)(id)cf; // ok + (void)(id)cf; // ok + (void)(id)cf; // expected-warning {{'CFMyErrorRef' (aka 'struct __CFMyErrorRef *') bridges to MyError, not 'id'}} +} + +void Test5(id P123, id ID, id P1234, id P12, id P23) { + (void)(CFErrorRef)ID; // ok + (void)(CFErrorRef)P123; // ok + (void)(CFErrorRef)P1234; // ok + (void)(CFErrorRef)P12; // expected-warning {{'id' cannot bridge to 'CFErrorRef' (aka 'struct __CFErrorRef *')}} + (void)(CFErrorRef)P23; // expected-warning {{'id' cannot bridge to 'CFErrorRef' (aka 'struct __CFErrorRef *')}} +} + +void Test6(id P123, id ID, id P1234, id P12, id P23) { + + (void)(CFMyErrorRef)ID; // ok + (void)(CFMyErrorRef)P123; // ok + (void)(CFMyErrorRef)P1234; // ok + (void)(CFMyErrorRef)P12; // expected-warning {{'id' cannot bridge to 'CFMyErrorRef' (aka 'struct __CFMyErrorRef *')}} + (void)(CFMyErrorRef)P23; // expected-warning {{'id' cannot bridge to 'CFMyErrorRef' (aka 'struct __CFMyErrorRef *')}} +} -- 2.40.0