diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index bcca1d25ef78..416da743623d 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -551,6 +551,12 @@ def ObjCBridge : InheritableAttr { let Args = [IdentifierArgument<"BridgedType", 1>]; } +def ObjCBridgeMutable : InheritableAttr { + let Spellings = [GNU<"objc_bridge_mutable">]; + let Subjects = [Record]; + let Args = [IdentifierArgument<"BridgedType", 1>]; +} + def NSReturnsRetained : InheritableAttr { let Spellings = [GNU<"ns_returns_retained">]; let Subjects = [ObjCMethod, Function]; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 38bffa2f99e4..8b3d738c60b9 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4362,6 +4362,30 @@ static void handleObjCBridgeAttr(Sema &S, Scope *Sc, Decl *D, Attr.getAttributeSpellingListIndex())); } +static void handleObjCBridgeMutableAttr(Sema &S, Scope *Sc, Decl *D, + const AttributeList &Attr) { + if (!isa(D)) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) + << Attr.getName() + << (S.getLangOpts().CPlusPlus ? ExpectedStructOrUnionOrClass + : ExpectedStructOrUnion); + return; + } + + IdentifierLoc *Parm = 0; + if (Attr.getNumArgs() == 1) + Parm = Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : 0; + + if (!Parm) { + S.Diag(D->getLocStart(), diag::err_objc_attr_not_id) << Attr.getName() << 0; + return; + } + + D->addAttr(::new (S.Context) + ObjCBridgeMutableAttr(Attr.getRange(), S.Context, Parm->Ident, + Attr.getAttributeSpellingListIndex())); +} + static void handleObjCOwnershipAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (hasDeclarator(D)) return; @@ -4651,6 +4675,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCBridge: handleObjCBridgeAttr(S, scope, D, Attr); break; + + case AttributeList::AT_ObjCBridgeMutable: + handleObjCBridgeMutableAttr(S, scope, D, Attr); break; case AttributeList::AT_CFAuditedTransfer: case AttributeList::AT_CFUnknownTransfer: diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index a906e2e9f920..a60749b0224f 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -3165,24 +3165,26 @@ diagnoseObjCARCConversion(Sema &S, SourceRange castRange, << castRange << castExpr->getSourceRange(); } -static inline ObjCBridgeAttr *getObjCBridgeAttr(const TypedefType *TD) { +template +static inline T *getObjCBridgeAttr(const TypedefType *TD) { TypedefNameDecl *TDNDecl = TD->getDecl(); QualType QT = TDNDecl->getUnderlyingType(); if (QT->isPointerType()) { QT = QT->getPointeeType(); if (const RecordType *RT = QT->getAs()) if (RecordDecl *RD = RT->getDecl()) - if (RD->hasAttr()) - return RD->getAttr(); + if (RD->hasAttr()) + return RD->getAttr(); } return 0; } +template static bool CheckObjCBridgeNSCast(Sema &S, QualType castType, Expr *castExpr) { QualType T = castExpr->getType(); while (const TypedefType *TD = dyn_cast(T.getTypePtr())) { TypedefNameDecl *TDNDecl = TD->getDecl(); - if (ObjCBridgeAttr *ObjCBAttr = getObjCBridgeAttr(TD)) { + if (TB *ObjCBAttr = getObjCBridgeAttr(TD)) { if (IdentifierInfo *Parm = ObjCBAttr->getBridgedType()) { NamedDecl *Target = 0; // Check for an existing type with this name. @@ -3231,11 +3233,12 @@ static bool CheckObjCBridgeNSCast(Sema &S, QualType castType, Expr *castExpr) { return false; } +template static bool CheckObjCBridgeCFCast(Sema &S, QualType castType, Expr *castExpr) { QualType T = castType; while (const TypedefType *TD = dyn_cast(T.getTypePtr())) { TypedefNameDecl *TDNDecl = TD->getDecl(); - if (ObjCBridgeAttr *ObjCBAttr = getObjCBridgeAttr(TD)) { + if (TB *ObjCBAttr = getObjCBridgeAttr(TD)) { if (IdentifierInfo *Parm = ObjCBAttr->getBridgedType()) { NamedDecl *Target = 0; // Check for an existing type with this name. @@ -3289,10 +3292,14 @@ void Sema::CheckTollFreeBridgeCast(QualType castType, Expr *castExpr) { // warn in presense of __bridge casting to or from a toll free bridge cast. ARCConversionTypeClass exprACTC = classifyTypeForARCConversion(castExpr->getType()); ARCConversionTypeClass castACTC = classifyTypeForARCConversion(castType); - if (castACTC == ACTC_retainable && exprACTC == ACTC_coreFoundation) - (void)CheckObjCBridgeNSCast(*this, castType, castExpr); - else if (castACTC == ACTC_coreFoundation && exprACTC == ACTC_retainable) - (void)CheckObjCBridgeCFCast(*this, castType, castExpr); + if (castACTC == ACTC_retainable && exprACTC == ACTC_coreFoundation) { + (void)CheckObjCBridgeNSCast(*this, castType, castExpr); + (void)CheckObjCBridgeNSCast(*this, castType, castExpr); + } + else if (castACTC == ACTC_coreFoundation && exprACTC == ACTC_retainable) { + (void)CheckObjCBridgeCFCast(*this, castType, castExpr); + (void)CheckObjCBridgeCFCast(*this, castType, castExpr); + } } Sema::ARCConversionResult @@ -3355,12 +3362,14 @@ Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType, if (castACTC == ACTC_retainable && exprACTC == ACTC_coreFoundation && (CCK == CCK_CStyleCast || CCK == CCK_FunctionalCast)) - if (CheckObjCBridgeNSCast(*this, castType, castExpr)) + if (CheckObjCBridgeNSCast(*this, castType, castExpr) || + CheckObjCBridgeNSCast(*this, castType, castExpr)) return ACR_okay; if (castACTC == ACTC_coreFoundation && exprACTC == ACTC_retainable && (CCK == CCK_CStyleCast || CCK == CCK_FunctionalCast)) - if (CheckObjCBridgeCFCast(*this, castType, castExpr)) + if (CheckObjCBridgeCFCast(*this, castType, castExpr) || + CheckObjCBridgeCFCast(*this, castType, castExpr)) return ACR_okay; diff --git a/clang/test/SemaObjC/objcbridgemutable-attribute.m b/clang/test/SemaObjC/objcbridgemutable-attribute.m new file mode 100644 index 000000000000..52989f960caa --- /dev/null +++ b/clang/test/SemaObjC/objcbridgemutable-attribute.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s +// rdar://15498044 + +typedef struct __attribute__((objc_bridge_mutable(NSMutableDictionary))) __CFDictionary * CFMutableDictionaryRef; // expected-note {{declared here}} + +@interface NSDictionary +@end + +@interface NSMutableDictionary : NSDictionary +@end + +void Test(NSMutableDictionary *md, NSDictionary *nd, CFMutableDictionaryRef mcf) { + + (void) (CFMutableDictionaryRef)md; + (void) (CFMutableDictionaryRef)nd; // expected-warning {{'NSDictionary' cannot bridge to 'CFMutableDictionaryRef' (aka 'struct __CFDictionary *')}} + (void) (NSDictionary *)mcf; // expected-warning {{'CFMutableDictionaryRef' (aka 'struct __CFDictionary *') bridges to NSMutableDictionary, not 'NSDictionary'}} + (void) (NSMutableDictionary *)mcf; // ok; +} +