[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