[cfe-commits] r140912 - in /cfe/trunk: include/clang/Sema/Sema.h lib/Sema/SemaExprObjC.cpp test/CodeGenObjC/arc-unbridged-cast.m test/SemaObjC/arc-cf.m test/SemaObjC/arc-unbridged-cast.m
John McCall
rjmccall at apple.com
Fri Sep 30 18:01:09 PDT 2011
Author: rjmccall
Date: Fri Sep 30 20:01:08 2011
New Revision: 140912
URL: http://llvm.org/viewvc/llvm-project?rev=140912&view=rev
Log:
Allow the results of cf_returns_not_retained function
calls, or calls to audited functions without an explicit
return attribute, to be casted without a bridge cast.
Tie this mechanism in with the existing exceptions to
the cast restrictions. State those restrictions more
correctly and generalize.
Added:
cfe/trunk/test/SemaObjC/arc-cf.m
Modified:
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/lib/Sema/SemaExprObjC.cpp
cfe/trunk/test/CodeGenObjC/arc-unbridged-cast.m
cfe/trunk/test/SemaObjC/arc-unbridged-cast.m
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=140912&r1=140911&r2=140912&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Fri Sep 30 20:01:08 2011
@@ -5762,10 +5762,6 @@
Expr *CastExpr, CastKind &Kind,
CXXCastPath &BasePath, bool FunctionalStyle);
- /// \brief Checks for valid expressions which can be cast to an ObjC
- /// pointer without needing a bridge cast.
- bool ValidObjCARCNoBridgeCastExpr(Expr *&Exp, QualType castType);
-
/// \brief Checks for invalid conversions and casts between
/// retainable pointers and other pointer kinds.
void CheckObjCARCConversion(SourceRange castRange, QualType castType,
Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=140912&r1=140911&r2=140912&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Fri Sep 30 20:01:08 2011
@@ -16,6 +16,7 @@
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Initialization.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ExprObjC.h"
@@ -1546,203 +1547,356 @@
}
enum ARCConversionTypeClass {
+ /// int, void, struct A
ACTC_none,
+
+ /// id, void (^)()
ACTC_retainable,
- ACTC_indirectRetainable
+
+ /// id*, id***, void (^*)(),
+ ACTC_indirectRetainable,
+
+ /// void* might be a normal C type, or it might a CF type.
+ ACTC_voidPtr,
+
+ /// struct A*
+ ACTC_coreFoundation
};
+static bool isAnyRetainable(ARCConversionTypeClass ACTC) {
+ return (ACTC == ACTC_retainable ||
+ ACTC == ACTC_coreFoundation ||
+ ACTC == ACTC_voidPtr);
+}
+static bool isAnyCLike(ARCConversionTypeClass ACTC) {
+ return ACTC == ACTC_none ||
+ ACTC == ACTC_voidPtr ||
+ ACTC == ACTC_coreFoundation;
+}
+
static ARCConversionTypeClass classifyTypeForARCConversion(QualType type) {
- ARCConversionTypeClass ACTC = ACTC_retainable;
+ bool isIndirect = false;
// Ignore an outermost reference type.
- if (const ReferenceType *ref = type->getAs<ReferenceType>())
+ if (const ReferenceType *ref = type->getAs<ReferenceType>()) {
type = ref->getPointeeType();
+ isIndirect = true;
+ }
// Drill through pointers and arrays recursively.
while (true) {
if (const PointerType *ptr = type->getAs<PointerType>()) {
type = ptr->getPointeeType();
+
+ // The first level of pointer may be the innermost pointer on a CF type.
+ if (!isIndirect) {
+ if (type->isVoidType()) return ACTC_voidPtr;
+ if (type->isRecordType()) return ACTC_coreFoundation;
+ }
} else if (const ArrayType *array = type->getAsArrayTypeUnsafe()) {
type = QualType(array->getElementType()->getBaseElementTypeUnsafe(), 0);
} else {
break;
}
- ACTC = ACTC_indirectRetainable;
+ isIndirect = true;
}
- if (!type->isObjCRetainableType()) return ACTC_none;
- return ACTC;
+ if (isIndirect) {
+ if (type->isObjCARCBridgableType())
+ return ACTC_indirectRetainable;
+ return ACTC_none;
+ }
+
+ if (type->isObjCARCBridgableType())
+ return ACTC_retainable;
+
+ return ACTC_none;
}
namespace {
- /// Return true if the given expression can be reasonably converted
- /// between a retainable pointer type and a C pointer type.
- struct ARCCastChecker : StmtVisitor<ARCCastChecker, bool> {
+ /// A result from the cast checker.
+ enum ACCResult {
+ /// Cannot be casted.
+ ACC_invalid,
+
+ /// Can be safely retained or not retained.
+ ACC_bottom,
+
+ /// Can be casted at +0.
+ ACC_plusZero,
+
+ /// Can be casted at +1.
+ ACC_plusOne
+ };
+ ACCResult merge(ACCResult left, ACCResult right) {
+ if (left == right) return left;
+ if (left == ACC_bottom) return right;
+ if (right == ACC_bottom) return left;
+ return ACC_invalid;
+ }
+
+ /// A checker which white-lists certain expressions whose conversion
+ /// to or from retainable type would otherwise be forbidden in ARC.
+ class ARCCastChecker : public StmtVisitor<ARCCastChecker, ACCResult> {
+ typedef StmtVisitor<ARCCastChecker, ACCResult> super;
+
ASTContext &Context;
- ARCCastChecker(ASTContext &Context) : Context(Context) {}
- bool VisitStmt(Stmt *s) {
- return false;
+ ARCConversionTypeClass SourceClass;
+ ARCConversionTypeClass TargetClass;
+
+ static bool isCFType(QualType type) {
+ // Someday this can use ns_bridged. For now, it has to do this.
+ return type->isCARCBridgableType();
}
- bool VisitExpr(Expr *e) {
- return e->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull);
+
+ public:
+ ARCCastChecker(ASTContext &Context, ARCConversionTypeClass source,
+ ARCConversionTypeClass target)
+ : Context(Context), SourceClass(source), TargetClass(target) {}
+
+ using super::Visit;
+ ACCResult Visit(Expr *e) {
+ return super::Visit(e->IgnoreParens());
}
-
- bool VisitParenExpr(ParenExpr *e) {
- return Visit(e->getSubExpr());
+
+ ACCResult VisitStmt(Stmt *s) {
+ return ACC_invalid;
}
- bool VisitCastExpr(CastExpr *e) {
+
+ /// Null pointer constants can be casted however you please.
+ ACCResult VisitExpr(Expr *e) {
+ if (e->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull))
+ return ACC_bottom;
+ return ACC_invalid;
+ }
+
+ /// Objective-C string literals can be safely casted.
+ ACCResult VisitObjCStringLiteral(ObjCStringLiteral *e) {
+ // If we're casting to any retainable type, go ahead. Global
+ // strings are immune to retains, so this is bottom.
+ if (isAnyRetainable(TargetClass)) return ACC_bottom;
+
+ return ACC_invalid;
+ }
+
+ /// Look through certain implicit and explicit casts.
+ ACCResult VisitCastExpr(CastExpr *e) {
switch (e->getCastKind()) {
case CK_NullToPointer:
- return true;
+ return ACC_bottom;
+
case CK_NoOp:
case CK_LValueToRValue:
case CK_BitCast:
+ case CK_GetObjCProperty:
case CK_CPointerToObjCPointerCast:
case CK_BlockPointerToObjCPointerCast:
case CK_AnyPointerToBlockPointerCast:
return Visit(e->getSubExpr());
+
default:
- return false;
+ return ACC_invalid;
}
}
- bool VisitUnaryExtension(UnaryOperator *e) {
+
+ /// Look through unary extension.
+ ACCResult VisitUnaryExtension(UnaryOperator *e) {
return Visit(e->getSubExpr());
}
- bool VisitBinComma(BinaryOperator *e) {
+
+ /// Ignore the LHS of a comma operator.
+ ACCResult VisitBinComma(BinaryOperator *e) {
return Visit(e->getRHS());
}
- bool VisitConditionalOperator(ConditionalOperator *e) {
- // Conditional operators are okay if both sides are okay.
- return Visit(e->getTrueExpr()) && Visit(e->getFalseExpr());
- }
- bool VisitObjCStringLiteral(ObjCStringLiteral *e) {
- // Always white-list Objective-C string literals.
- return true;
+
+ /// Conditional operators are okay if both sides are okay.
+ ACCResult VisitConditionalOperator(ConditionalOperator *e) {
+ ACCResult left = Visit(e->getTrueExpr());
+ if (left == ACC_invalid) return ACC_invalid;
+ return merge(left, Visit(e->getFalseExpr()));
}
- bool VisitStmtExpr(StmtExpr *e) {
+
+ /// Statement expressions are okay if their result expression is okay.
+ ACCResult VisitStmtExpr(StmtExpr *e) {
return Visit(e->getSubStmt()->body_back());
}
- bool VisitDeclRefExpr(DeclRefExpr *e) {
- // White-list references to global extern strings from system
- // headers.
- if (VarDecl *var = dyn_cast<VarDecl>(e->getDecl()))
- if (var->getStorageClass() == SC_Extern &&
- var->getType().isConstQualified() &&
- Context.getSourceManager().isInSystemHeader(var->getLocation()))
- return true;
- return false;
+
+ /// Some declaration references are okay.
+ ACCResult VisitDeclRefExpr(DeclRefExpr *e) {
+ // References to global constants from system headers are okay.
+ // These are things like 'kCFStringTransformToLatin'. They are
+ // can also be assumed to be immune to retains.
+ VarDecl *var = dyn_cast<VarDecl>(e->getDecl());
+ if (isAnyRetainable(TargetClass) &&
+ isAnyRetainable(SourceClass) &&
+ var &&
+ var->getStorageClass() == SC_Extern &&
+ var->getType().isConstQualified() &&
+ Context.getSourceManager().isInSystemHeader(var->getLocation())) {
+ return ACC_bottom;
+ }
+
+ // Nothing else.
+ return ACC_invalid;
}
- };
-}
-bool
-Sema::ValidObjCARCNoBridgeCastExpr(Expr *&Exp, QualType castType) {
- Expr *NewExp = Exp->IgnoreParenCasts();
-
- if (!isa<ObjCMessageExpr>(NewExp) && !isa<ObjCPropertyRefExpr>(NewExp)
- && !isa<CallExpr>(NewExp))
- return false;
- ObjCMethodDecl *method = 0;
- bool MethodReturnsPlusOne = false;
-
- if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(NewExp)) {
- method = PRE->getExplicitProperty()->getGetterMethodDecl();
- }
- else if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(NewExp))
- method = ME->getMethodDecl();
- else {
- CallExpr *CE = cast<CallExpr>(NewExp);
- Decl *CallDecl = CE->getCalleeDecl();
- if (!CallDecl)
- return false;
- if (CallDecl->hasAttr<CFReturnsNotRetainedAttr>())
- return true;
- MethodReturnsPlusOne = CallDecl->hasAttr<CFReturnsRetainedAttr>();
- if (!MethodReturnsPlusOne) {
- if (NamedDecl *ND = dyn_cast<NamedDecl>(CallDecl))
- if (const IdentifierInfo *Id = ND->getIdentifier())
- if (Id->isStr("__builtin___CFStringMakeConstantString"))
- return true;
+ /// Some calls are okay.
+ ACCResult VisitCallExpr(CallExpr *e) {
+ if (FunctionDecl *fn = e->getDirectCallee())
+ if (ACCResult result = checkCallToFunction(fn))
+ return result;
+
+ return super::VisitCallExpr(e);
}
- }
-
- if (!MethodReturnsPlusOne) {
- if (!method)
- return false;
- if (method->hasAttr<CFReturnsNotRetainedAttr>())
- return true;
- MethodReturnsPlusOne = method->hasAttr<CFReturnsRetainedAttr>();
- if (!MethodReturnsPlusOne) {
- ObjCMethodFamily family = method->getSelector().getMethodFamily();
- switch (family) {
- case OMF_alloc:
- case OMF_copy:
- case OMF_mutableCopy:
- case OMF_new:
- MethodReturnsPlusOne = true;
- break;
- default:
- break;
+
+ ACCResult checkCallToFunction(FunctionDecl *fn) {
+ // Require a CF*Ref return type.
+ if (!isCFType(fn->getResultType()))
+ return ACC_invalid;
+
+ if (!isAnyRetainable(TargetClass))
+ return ACC_invalid;
+
+ // Honor an explicit 'not retained' attribute.
+ if (fn->hasAttr<CFReturnsNotRetainedAttr>())
+ return ACC_plusZero;
+
+ // Honor an explicit 'retained' attribute, except that for
+ // now we're not going to permit implicit handling of +1 results,
+ // because it's a bit frightening.
+ if (fn->hasAttr<CFReturnsRetainedAttr>())
+ return ACC_invalid; // ACC_plusOne if we start accepting this
+
+ // Recognize this specific builtin function, which is used by CFSTR.
+ unsigned builtinID = fn->getBuiltinID();
+ if (builtinID == Builtin::BI__builtin___CFStringMakeConstantString)
+ return ACC_bottom;
+
+ // Otherwise, don't do anything implicit with an unaudited function.
+ if (!fn->hasAttr<CFAuditedTransferAttr>())
+ return ACC_invalid;
+
+ // Otherwise, it's +0 unless it follows the create convention.
+ if (ento::coreFoundation::followsCreateRule(fn))
+ return ACC_invalid; // ACC_plusOne if we start accepting this
+
+ return ACC_plusZero;
+ }
+
+ ACCResult VisitObjCMessageExpr(ObjCMessageExpr *e) {
+ return checkCallToMethod(e->getMethodDecl());
+ }
+
+ ACCResult VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *e) {
+ ObjCMethodDecl *method;
+ if (e->isExplicitProperty())
+ method = e->getExplicitProperty()->getGetterMethodDecl();
+ else
+ method = e->getImplicitPropertyGetter();
+ return checkCallToMethod(method);
+ }
+
+ ACCResult checkCallToMethod(ObjCMethodDecl *method) {
+ if (!method) return ACC_invalid;
+
+ // Check for message sends to functions returning CF types. We
+ // just obey the Cocoa conventions with these, even though the
+ // return type is CF.
+ if (!isAnyRetainable(TargetClass) || !isCFType(method->getResultType()))
+ return ACC_invalid;
+
+ // If the method is explicitly marked not-retained, it's +0.
+ if (method->hasAttr<CFReturnsNotRetainedAttr>())
+ return ACC_plusZero;
+
+ // If the method is explicitly marked as returning retained, or its
+ // selector follows a +1 Cocoa convention, treat it as +1.
+ if (method->hasAttr<CFReturnsRetainedAttr>())
+ return ACC_plusOne;
+
+ switch (method->getSelector().getMethodFamily()) {
+ case OMF_alloc:
+ case OMF_copy:
+ case OMF_mutableCopy:
+ case OMF_new:
+ return ACC_plusOne;
+
+ default:
+ // Otherwise, treat it as +0.
+ return ACC_plusZero;
}
}
- }
-
- if (MethodReturnsPlusOne) {
- TypeSourceInfo *TSInfo =
- Context.getTrivialTypeSourceInfo(castType, SourceLocation());
- ExprResult ExpRes = BuildObjCBridgedCast(SourceLocation(), OBC_BridgeTransfer,
- SourceLocation(), TSInfo, Exp);
- Exp = ExpRes.take();
- }
- return true;
+ };
}
void
Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType,
Expr *&castExpr, CheckedConversionKind CCK) {
QualType castExprType = castExpr->getType();
+
+ // For the purposes of the classification, we assume reference types
+ // will bind to temporaries.
+ QualType effCastType = castType;
+ if (const ReferenceType *ref = castType->getAs<ReferenceType>())
+ effCastType = ref->getPointeeType();
ARCConversionTypeClass exprACTC = classifyTypeForARCConversion(castExprType);
- ARCConversionTypeClass castACTC = classifyTypeForARCConversion(castType);
+ ARCConversionTypeClass castACTC = classifyTypeForARCConversion(effCastType);
if (exprACTC == castACTC) return;
- if (exprACTC && castType->isIntegralType(Context)) return;
+ if (isAnyCLike(exprACTC) && isAnyCLike(castACTC)) return;
+
+ // Allow all of these types to be cast to integer types (but not
+ // vice-versa).
+ if (castACTC == ACTC_none && castType->isIntegralType(Context))
+ return;
// Allow casts between pointers to lifetime types (e.g., __strong id*)
// and pointers to void (e.g., cv void *). Casting from void* to lifetime*
// must be explicit.
- if (const PointerType *CastPtr = castType->getAs<PointerType>()) {
- if (const PointerType *CastExprPtr = castExprType->getAs<PointerType>()) {
- QualType CastPointee = CastPtr->getPointeeType();
- QualType CastExprPointee = CastExprPtr->getPointeeType();
- if ((CCK != CCK_ImplicitConversion &&
- CastPointee->isObjCIndirectLifetimeType() &&
- CastExprPointee->isVoidType()) ||
- (CastPointee->isVoidType() &&
- CastExprPointee->isObjCIndirectLifetimeType()))
- return;
- }
- }
-
- if (ARCCastChecker(Context).Visit(castExpr))
+ if (exprACTC == ACTC_indirectRetainable && castACTC == ACTC_voidPtr)
+ return;
+ if (castACTC == ACTC_indirectRetainable && exprACTC == ACTC_voidPtr &&
+ CCK != CCK_ImplicitConversion)
+ return;
+
+ switch (ARCCastChecker(Context, exprACTC, castACTC).Visit(castExpr)) {
+ // For invalid casts, fall through.
+ case ACC_invalid:
+ break;
+
+ // Do nothing for both bottom and +0.
+ case ACC_bottom:
+ case ACC_plusZero:
+ return;
+
+ // If the result is +1, consume it here.
+ case ACC_plusOne:
+ castExpr = ImplicitCastExpr::Create(Context, castExpr->getType(),
+ CK_ARCConsumeObject, castExpr,
+ 0, VK_RValue);
+ ExprNeedsCleanups = true;
return;
+ }
SourceLocation loc =
- (castRange.isValid() ? castRange.getBegin() : castExpr->getExprLoc());
+ (castRange.isValid() ? castRange.getBegin() : castExpr->getExprLoc());
if (makeUnavailableInSystemHeader(loc,
- "converts between Objective-C and C pointers in -fobjc-arc"))
+ "converts between Objective-C and C pointers in -fobjc-arc"))
return;
unsigned srcKind = 0;
switch (exprACTC) {
- case ACTC_none:
- srcKind = (castExprType->isPointerType() ? 1 : 0);
- break;
- case ACTC_retainable:
- srcKind = (castExprType->isBlockPointerType() ? 2 : 3);
- break;
- case ACTC_indirectRetainable:
- srcKind = 4;
- break;
+ case ACTC_none:
+ case ACTC_coreFoundation:
+ case ACTC_voidPtr:
+ srcKind = (castExprType->isPointerType() ? 1 : 0);
+ break;
+ case ACTC_retainable:
+ srcKind = (castExprType->isBlockPointerType() ? 2 : 3);
+ break;
+ case ACTC_indirectRetainable:
+ srcKind = 4;
+ break;
}
if (CCK == CCK_CStyleCast) {
@@ -1750,12 +1904,7 @@
SourceLocation AfterLParen = PP.getLocForEndOfToken(castRange.getBegin());
SourceLocation NoteLoc = AfterLParen.isValid()? AfterLParen : loc;
- if (castType->isObjCARCBridgableType() &&
- castExprType->isCARCBridgableType()) {
- // explicit unbridged casts are allowed if the source of the cast is a
- // message sent to an objc method (or property access)
- if (ValidObjCARCNoBridgeCastExpr(castExpr, castType))
- return;
+ if (castACTC == ACTC_retainable && isAnyRetainable(exprACTC)) {
Diag(loc, diag::err_arc_cast_requires_bridge)
<< 2
<< castExprType
@@ -1772,8 +1921,7 @@
return;
}
- if (castType->isCARCBridgableType() &&
- castExprType->isObjCARCBridgableType()){
+ if (exprACTC == ACTC_retainable && isAnyRetainable(castACTC)) {
Diag(loc, diag::err_arc_cast_requires_bridge)
<< (castExprType->isBlockPointerType()? 1 : 0)
<< castExprType
Modified: cfe/trunk/test/CodeGenObjC/arc-unbridged-cast.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/arc-unbridged-cast.m?rev=140912&r1=140911&r2=140912&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/arc-unbridged-cast.m (original)
+++ cfe/trunk/test/CodeGenObjC/arc-unbridged-cast.m Fri Sep 30 20:01:08 2011
@@ -29,8 +29,6 @@
id MMM()
{
id obj = (id)((CFStringRef) __builtin___CFStringMakeConstantString ("" "Some CF String" ""));
- if (obj)
- return (id) SomeOtherFunc();
return 0;
}
Added: cfe/trunk/test/SemaObjC/arc-cf.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/arc-cf.m?rev=140912&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjC/arc-cf.m (added)
+++ cfe/trunk/test/SemaObjC/arc-cf.m Fri Sep 30 20:01:08 2011
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify %s
+
+typedef const void *CFTypeRef;
+typedef const struct __CFString *CFStringRef;
+
+extern CFStringRef CFMakeString0(void);
+extern CFStringRef CFCreateString0(void);
+void test0() {
+ id x;
+ x = (id) CFMakeString0(); // expected-error {{requires a bridged cast}} expected-note {{__bridge to convert directly}} expected-note {{__bridge_transfer to transfer}}
+ x = (id) CFCreateString0(); // expected-error {{requires a bridged cast}} expected-note {{__bridge to convert directly}} expected-note {{__bridge_transfer to transfer}}
+}
+
+extern CFStringRef CFMakeString1(void) __attribute__((cf_returns_not_retained));
+extern CFStringRef CFCreateString1(void) __attribute__((cf_returns_retained));
+void test1() {
+ id x;
+ x = (id) CFMakeString1();
+ x = (id) CFCreateString1(); // expected-error {{requires a bridged cast}} expected-note {{__bridge to convert directly}} expected-note {{__bridge_transfer to transfer}}
+}
Modified: cfe/trunk/test/SemaObjC/arc-unbridged-cast.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/arc-unbridged-cast.m?rev=140912&r1=140911&r2=140912&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjC/arc-unbridged-cast.m (original)
+++ cfe/trunk/test/SemaObjC/arc-unbridged-cast.m Fri Sep 30 20:01:08 2011
@@ -1,18 +1,72 @@
// // RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify %s
-// rdar://9744349
typedef const struct __CFString * CFStringRef;
- at interface I
- at property CFStringRef P;
+ at interface Object
+ at property CFStringRef property;
+- (CFStringRef) implicitProperty;
+- (CFStringRef) newString;
+- (CFStringRef) makeString;
@end
- at implementation I
- at synthesize P;
-- (id) Meth {
- I* p1 = (id)[p1 P];
- id p2 = (__bridge_transfer id)[p1 P];
- id p3 = (__bridge I*)[p1 P];
- return (id) p1.P;
+extern Object *object;
+
+// rdar://9744349
+id test0(void) {
+ id p1 = (id)[object property];
+ id p2 = (__bridge_transfer id)[object property];
+ id p3 = (__bridge id)[object property];
+ return (id) object.property;
+}
+
+// rdar://10140692
+CFStringRef unauditedString(void);
+CFStringRef plusOneString(void) __attribute__((cf_returns_retained));
+
+#pragma clang arc_cf_code_audited begin
+CFStringRef auditedString(void);
+CFStringRef auditedCreateString(void);
+#pragma clang arc_cf_code_audited end
+
+void test1(int cond) {
+ id x;
+ x = (id) auditedString();
+ x = (id) (cond ? auditedString() : (void*) 0);
+ x = (id) (cond ? (void*) 0 : auditedString());
+ x = (id) (cond ? (CFStringRef) @"help" : auditedString());
+
+ x = (id) unauditedString(); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use __bridge_transfer to}}
+ x = (id) (cond ? unauditedString() : (void*) 0); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use __bridge_transfer to}}
+ x = (id) (cond ? (void*) 0 : unauditedString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use __bridge_transfer to}}
+ x = (id) (cond ? (CFStringRef) @"help" : unauditedString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use __bridge_transfer to}}
+
+ x = (id) auditedCreateString(); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use __bridge_transfer to}}
+ x = (id) (cond ? auditedCreateString() : (void*) 0); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use __bridge_transfer to}}
+ x = (id) (cond ? (void*) 0 : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use __bridge_transfer to}}
+ x = (id) (cond ? (CFStringRef) @"help" : auditedCreateString()); // expected-error {{requires a bridged cast}} expected-note {{use __bridge to}} expected-note {{use __bridge_transfer to}}
+
+ x = (id) [object property];
+ x = (id) (cond ? [object property] : (void*) 0);
+ x = (id) (cond ? (void*) 0 : [object property]);
+ x = (id) (cond ? (CFStringRef) @"help" : [object property]);
+
+ x = (id) object.property;
+ x = (id) (cond ? object.property : (void*) 0);
+ x = (id) (cond ? (void*) 0 : object.property);
+ x = (id) (cond ? (CFStringRef) @"help" : object.property);
+
+ x = (id) object.implicitProperty;
+ x = (id) (cond ? object.implicitProperty : (void*) 0);
+ x = (id) (cond ? (void*) 0 : object.implicitProperty);
+ x = (id) (cond ? (CFStringRef) @"help" : object.implicitProperty);
+
+ x = (id) [object makeString];
+ x = (id) (cond ? [object makeString] : (void*) 0);
+ x = (id) (cond ? (void*) 0 : [object makeString]);
+ x = (id) (cond ? (CFStringRef) @"help" : [object makeString]);
+
+ x = (id) [object newString];
+ x = (id) (cond ? [object newString] : (void*) 0);
+ x = (id) (cond ? (void*) 0 : [object newString]);
+ x = (id) (cond ? (CFStringRef) @"help" : [object newString]); // a bit questionable
}
- at end
More information about the cfe-commits
mailing list