[cfe-commits] r136204 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/Sema.h lib/CodeGen/CGObjC.cpp lib/Parse/ParseStmt.cpp lib/Sema/SemaStmt.cpp lib/Sema/TreeTransform.h test/CodeGenObjC/arc-foreach.m test/CodeGenObjCXX/arc.mm test/SemaObjC/arc.m

John McCall rjmccall at apple.com
Tue Jul 26 18:07:16 PDT 2011


Author: rjmccall
Date: Tue Jul 26 20:07:15 2011
New Revision: 136204

URL: http://llvm.org/viewvc/llvm-project?rev=136204&view=rev
Log:
Clean up the analysis of the collection operand to ObjC
for-in statements;  specifically, make sure to close over any
temporaries or cleanups it might require.  In ARC, this has
implications for the lifetime of the collection, so emit it
with a retain and release it upon exit from the loop.

rdar://problem/9817306


Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/CodeGen/CGObjC.cpp
    cfe/trunk/lib/Parse/ParseStmt.cpp
    cfe/trunk/lib/Sema/SemaStmt.cpp
    cfe/trunk/lib/Sema/TreeTransform.h
    cfe/trunk/test/CodeGenObjC/arc-foreach.m
    cfe/trunk/test/CodeGenObjCXX/arc.mm
    cfe/trunk/test/SemaObjC/arc.m

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=136204&r1=136203&r2=136204&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Jul 26 20:07:15 2011
@@ -2715,6 +2715,8 @@
   "selector %1">;
 def err_arc_receiver_forward_instance : Error<
   "receiver type %0 for instance message is a forward declaration">;
+def err_arc_collection_forward : Error<
+  "collection expression type %0 is a forward declaration">;
 def err_arc_multiple_method_decl : Error< 
   "multiple methods named %0 found with mismatched result, "
   "parameter type or attributes">;

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=136204&r1=136203&r2=136204&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Tue Jul 26 20:07:15 2011
@@ -2029,6 +2029,8 @@
                           FullExprArg Third,
                           SourceLocation RParenLoc,
                           Stmt *Body);
+  ExprResult ActOnObjCForCollectionOperand(SourceLocation forLoc,
+                                           Expr *collection);
   StmtResult ActOnObjCForCollectionStmt(SourceLocation ForColLoc,
                                         SourceLocation LParenLoc,
                                         Stmt *First, Expr *Second,

Modified: cfe/trunk/lib/CodeGen/CGObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGObjC.cpp?rev=136204&r1=136203&r2=136204&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGObjC.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGObjC.cpp Tue Jul 26 20:07:15 2011
@@ -1020,8 +1020,16 @@
                                       ArrayType::Normal, 0);
   llvm::Value *ItemsPtr = CreateMemTemp(ItemsTy, "items.ptr");
 
-  // Emit the collection pointer.
-  llvm::Value *Collection = EmitScalarExpr(S.getCollection());
+  // Emit the collection pointer.  In ARC, we do a retain.
+  llvm::Value *Collection;
+  if (getLangOptions().ObjCAutoRefCount) {
+    Collection = EmitARCRetainScalarExpr(S.getCollection());
+
+    // Enter a cleanup to do the release.
+    EmitObjCConsumeObject(S.getCollection()->getType(), Collection);
+  } else {
+    Collection = EmitScalarExpr(S.getCollection());
+  }
 
   // Send it our message:
   CallArgList Args;
@@ -1236,6 +1244,10 @@
     DI->EmitRegionEnd(Builder);
   }
 
+  // Leave the cleanup we entered in ARC.
+  if (getLangOptions().ObjCAutoRefCount)
+    PopCleanupBlock();
+
   EmitBlock(LoopEnd.getBlock());
 }
 
@@ -1967,6 +1979,12 @@
 
 static TryEmitResult
 tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) {
+  // Look through cleanups.
+  if (const ExprWithCleanups *cleanups = dyn_cast<ExprWithCleanups>(e)) {
+    CodeGenFunction::RunCleanupsScope scope(CGF);
+    return tryEmitARCRetainScalarExpr(CGF, cleanups->getSubExpr());
+  }
+
   // The desired result type, if it differs from the type of the
   // ultimate opaque expression.
   llvm::Type *resultType = 0;

Modified: cfe/trunk/lib/Parse/ParseStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseStmt.cpp?rev=136204&r1=136203&r2=136204&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseStmt.cpp (original)
+++ cfe/trunk/lib/Parse/ParseStmt.cpp Tue Jul 26 20:07:15 2011
@@ -1405,13 +1405,22 @@
   // statememt before parsing the body, in order to be able to deduce the type
   // of an auto-typed loop variable.
   StmtResult ForRangeStmt;
-  if (ForRange)
+  if (ForRange) {
     ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, LParenLoc,
                                                 FirstPart.take(),
                                                 ForRangeInit.ColonLoc,
                                                 ForRangeInit.RangeExpr.get(),
                                                 RParenLoc);
 
+
+  // Similarly, we need to do the semantic analysis for a for-range
+  // statement immediately in order to close over temporaries correctly.
+  } else if (ForEach) {
+    if (!Collection.isInvalid())
+      Collection =
+        Actions.ActOnObjCForCollectionOperand(ForLoc, Collection.take());
+  }
+
   // C99 6.8.5p5 - In C99, the body of the if statement is a scope, even if
   // there is no compound stmt.  C90 does not have this clause.  We only do this
   // if the body isn't a compound statement to avoid push/pop in common cases.
@@ -1439,8 +1448,6 @@
     return StmtError();
 
   if (ForEach)
-    // FIXME: It isn't clear how to communicate the late destruction of 
-    // C++ temporaries used to create the collection.
     return Actions.ActOnObjCForCollectionStmt(ForLoc, LParenLoc,
                                               FirstPart.take(),
                                               Collection.take(), RParenLoc, 

Modified: cfe/trunk/lib/Sema/SemaStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=136204&r1=136203&r2=136204&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaStmt.cpp (original)
+++ cfe/trunk/lib/Sema/SemaStmt.cpp Tue Jul 26 20:07:15 2011
@@ -965,6 +965,76 @@
   return Owned(static_cast<Stmt*>(Result.get()));
 }
 
+ExprResult
+Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) {
+  assert(collection);
+
+  // Bail out early if we've got a type-dependent expression.
+  if (collection->isTypeDependent()) return Owned(collection);
+
+  // Perform normal l-value conversion.
+  ExprResult result = DefaultFunctionArrayLvalueConversion(collection);
+  if (result.isInvalid())
+    return ExprError();
+  collection = result.take();
+
+  // The operand needs to have object-pointer type.
+  // TODO: should we do a contextual conversion?
+  const ObjCObjectPointerType *pointerType =
+    collection->getType()->getAs<ObjCObjectPointerType>();
+  if (!pointerType)
+    return Diag(forLoc, diag::err_collection_expr_type)
+             << collection->getType() << collection->getSourceRange();
+
+  // Check that the operand provides
+  //   - countByEnumeratingWithState:objects:count:
+  const ObjCObjectType *objectType = pointerType->getObjectType();
+  ObjCInterfaceDecl *iface = objectType->getInterface();
+
+  // If we have a forward-declared type, we can't do this check.
+  if (iface && iface->isForwardDecl()) {
+    // This is ill-formed under ARC.
+    if (getLangOptions().ObjCAutoRefCount) {
+      Diag(forLoc, diag::err_arc_collection_forward)
+        << pointerType->getPointeeType() << collection->getSourceRange();
+    }
+
+    // Otherwise, if we have any useful type information, check that
+    // the type declares the appropriate method.
+  } else if (iface || !objectType->qual_empty()) {
+    IdentifierInfo *selectorIdents[] = {
+      &Context.Idents.get("countByEnumeratingWithState"),
+      &Context.Idents.get("objects"),
+      &Context.Idents.get("count")
+    };
+    Selector selector = Context.Selectors.getSelector(3, &selectorIdents[0]);
+
+    ObjCMethodDecl *method = 0;
+
+    // If there's an interface, look in both the public and private APIs.
+    if (iface) {
+      method = iface->lookupInstanceMethod(selector);
+      if (!method) method = LookupPrivateInstanceMethod(selector, iface);
+    }
+
+    // Also check protocol qualifiers.
+    if (!method)
+      method = LookupMethodInQualifiedType(selector, pointerType,
+                                           /*instance*/ true);
+
+    // If we didn't find it anywhere, give up.
+    if (!method) {
+      Diag(forLoc, diag::warn_collection_expr_type)
+        << collection->getType() << selector << collection->getSourceRange();
+    }
+
+    // TODO: check for an incompatible signature?
+  }
+
+  // Wrap up any cleanups in the expression.
+  return Owned(MaybeCreateExprWithCleanups(collection));
+}
+
 StmtResult
 Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc,
                                  SourceLocation LParenLoc,
@@ -1000,38 +1070,7 @@
         Diag(ForLoc, diag::err_selector_element_type)
           << FirstType << First->getSourceRange();
   }
-  if (Second && !Second->isTypeDependent()) {
-    ExprResult Result = DefaultFunctionArrayLvalueConversion(Second);
-    if (Result.isInvalid())
-      return StmtError();
-    Second = Result.take();
-    QualType SecondType = Second->getType();
-    if (!SecondType->isObjCObjectPointerType())
-      Diag(ForLoc, diag::err_collection_expr_type)
-        << SecondType << Second->getSourceRange();
-    else if (const ObjCObjectPointerType *OPT =
-             SecondType->getAsObjCInterfacePointerType()) {
-      SmallVector<IdentifierInfo *, 4> KeyIdents;
-      IdentifierInfo* selIdent =
-        &Context.Idents.get("countByEnumeratingWithState");
-      KeyIdents.push_back(selIdent);
-      selIdent = &Context.Idents.get("objects");
-      KeyIdents.push_back(selIdent);
-      selIdent = &Context.Idents.get("count");
-      KeyIdents.push_back(selIdent);
-      Selector CSelector = Context.Selectors.getSelector(3, &KeyIdents[0]);
-      if (ObjCInterfaceDecl *IDecl = OPT->getInterfaceDecl()) {
-        if (!IDecl->isForwardDecl() &&
-            !IDecl->lookupInstanceMethod(CSelector) &&
-            !LookupMethodInQualifiedType(CSelector, OPT, true)) {
-          // Must further look into private implementation methods.
-          if (!LookupPrivateInstanceMethod(CSelector, IDecl))
-            Diag(ForLoc, diag::warn_collection_expr_type)
-              << SecondType << CSelector << Second->getSourceRange();
-        }
-      }
-    }
-  }
+
   return Owned(new (Context) ObjCForCollectionStmt(First, Second, Body,
                                                    ForLoc, RParenLoc));
 }

Modified: cfe/trunk/lib/Sema/TreeTransform.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=136204&r1=136203&r2=136204&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/TreeTransform.h (original)
+++ cfe/trunk/lib/Sema/TreeTransform.h Tue Jul 26 20:07:15 2011
@@ -1200,7 +1200,16 @@
                                             Stmt *Body) {
     return getSema().ActOnObjCAutoreleasePoolStmt(AtLoc, Body);
   }
-  
+
+  /// \brief Build the collection operand to a new Objective-C fast
+  /// enumeration statement.
+  ///
+  /// By default, performs semantic analysis to build the new statement.
+  /// Subclasses may override this routine to provide different behavior.
+  ExprResult RebuildObjCForCollectionOperand(SourceLocation forLoc,
+                                             Expr *collection) {
+    return getSema().ActOnObjCForCollectionOperand(forLoc, collection);
+  }
 
   /// \brief Build a new Objective-C fast enumeration statement.
   ///
@@ -5519,6 +5528,10 @@
   ExprResult Collection = getDerived().TransformExpr(S->getCollection());
   if (Collection.isInvalid())
     return StmtError();
+  Collection = getDerived().RebuildObjCForCollectionOperand(S->getForLoc(),
+                                                            Collection.take());
+  if (Collection.isInvalid())
+    return StmtError();
   
   // Transform the body.
   StmtResult Body = getDerived().TransformStmt(S->getBody());

Modified: cfe/trunk/test/CodeGenObjC/arc-foreach.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/arc-foreach.m?rev=136204&r1=136203&r2=136204&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/arc-foreach.m (original)
+++ cfe/trunk/test/CodeGenObjC/arc-foreach.m Tue Jul 26 20:07:15 2011
@@ -5,7 +5,13 @@
 
 extern void use(id);
 extern void use_block(void (^)(void));
- at class NSArray;
+
+struct NSFastEnumerationState;
+ at interface NSArray
+- (unsigned long) countByEnumeratingWithState: (struct NSFastEnumerationState*) state
+                  objects: (id*) buffer
+                  count: (unsigned long) bufferSize;
+ at end;
 
 void test0(NSArray *array) {
   // 'x' should be initialized without a retain.
@@ -17,12 +23,37 @@
 }
 
 // CHECK-LP64:    define void @test0(
-// CHECK-LP64:      alloca [[ARRAY_T:%.*]]*,
+// CHECK-LP64:      [[ARRAY:%.*]] = alloca [[ARRAY_T:%.*]]*,
 // CHECK-LP64-NEXT: [[X:%.*]] = alloca i8*,
 // CHECK-LP64-NEXT: [[STATE:%.*]] = alloca [[STATE_T:%.*]],
-// CHECK-LP64-NEXT: alloca [16 x i8*], align 8
+// CHECK-LP64-NEXT: [[BUFFER:%.*]] = alloca [16 x i8*], align 8
 // CHECK-LP64-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
 
+// Initialize 'array'.
+// CHECK-LP64-NEXT: [[T0:%.*]] = bitcast [[ARRAY_T:%.*]]* {{%.*}} to i8*
+// CHECK-LP64-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]])
+// CHECK-LP64-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[ARRAY_T]]*
+// CHECK-LP64-NEXT: store [[ARRAY_T]]* [[T2]], [[ARRAY_T]]** [[ARRAY]], align 8
+
+// Initialize the fast enumaration state.
+// CHECK-LP64-NEXT: [[T0:%.*]] = bitcast [[STATE_T]]* [[STATE]] to i8*
+// CHECK-LP64-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T0]], i8 0, i64 64, i32 8, i1 false)
+
+// Evaluate the collection expression and retain.
+// CHECK-LP64-NEXT: [[T0:%.*]] = load [[ARRAY_T]]** [[ARRAY]], align 8
+// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[ARRAY_T]]* [[T0]] to i8*
+// CHECK-LP64-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]])
+// CHECK-LP64-NEXT: [[SAVED_ARRAY:%.*]] = bitcast i8* [[T2]] to [[ARRAY_T]]*
+
+// Call the enumeration method.
+// CHECK-LP64-NEXT: [[T0:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
+// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[ARRAY_T]]* [[SAVED_ARRAY]] to i8*
+// CHECK-LP64-NEXT: [[SIZE:%.*]] = call i64 bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i64 (i8*, i8*, [[STATE_T]]*, [16 x i8*]*, i64)*)(i8* [[T1]], i8* [[T0]], [[STATE_T]]* [[STATE]], [16 x i8*]* [[BUFFER]], i64 16)
+
+// Check for a nonzero result.
+// CHECK-LP64-NEXT: [[T0:%.*]] = icmp eq i64 [[SIZE]], 0
+// CHECK-LP64-NEXT: br i1 [[T0]]
+
 // CHECK-LP64:      [[T0:%.*]] = getelementptr inbounds [[STATE_T]]* [[STATE]], i32 0, i32 1
 // CHECK-LP64-NEXT: [[T1:%.*]] = load i8*** [[T0]]
 // CHECK-LP64-NEXT: [[T2:%.*]] = getelementptr i8** [[T1]], i64
@@ -38,6 +69,20 @@
 // CHECK-LP64-NEXT: [[T1:%.*]] = load i8** [[T0]]
 // CHECK-LP64-NEXT: call void @objc_release(i8* [[T1]])
 
+// CHECK-LP64:      [[T0:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
+// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[ARRAY_T]]* [[SAVED_ARRAY]] to i8*
+// CHECK-LP64-NEXT: [[SIZE:%.*]] = call i64 bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i64 (i8*, i8*, [[STATE_T]]*, [16 x i8*]*, i64)*)(i8* [[T1]], i8* [[T0]], [[STATE_T]]* [[STATE]], [16 x i8*]* [[BUFFER]], i64 16)
+
+// Release the array.
+// CHECK-LP64:      [[T0:%.*]] = bitcast [[ARRAY_T]]* [[SAVED_ARRAY]] to i8*
+// CHECK-LP64-NEXT: call void @objc_release(i8* [[T0]])
+
+// Destroy 'array'.
+// CHECK-LP64:      [[T0:%.*]] = load [[ARRAY_T]]** [[ARRAY]]
+// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[ARRAY_T]]* [[T0]] to i8*
+// CHECK-LP64-NEXT: call void @objc_release(i8* [[T1]])
+// CHECK-LP64-NEXT: ret void
+
 // CHECK-LP64:    define internal void @__test0_block_invoke
 // CHECK-LP64:      [[BLOCK:%.*]] = bitcast i8* {{%.*}} to [[BLOCK_T]]*
 // CHECK-LP64-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
@@ -70,3 +115,36 @@
 // CHECK-LP64: call void @use_block
 // CHECK-LP64-NEXT: call void @objc_destroyWeak(i8** [[T0]])
 // CHECK-LP64-NEXT: call void @objc_destroyWeak(i8** [[X]])
+
+// rdar://problem/9817306
+ at interface Test2
+- (NSArray *) array;
+ at end
+void test2(Test2 *a) {
+  for (id x in a.array) {
+    use(x);
+  }
+}
+
+// CHECK-LP64:    define void @test2(
+// CHECK-LP64:      [[T0:%.*]] = call [[ARRAY_T]]* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to [[ARRAY_T]]* (i8*, i8*)*)(
+// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[ARRAY_T]]* [[T0]] to i8*
+// CHECK-LP64-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]])
+// CHECK-LP64-NEXT: [[COLL:%.*]] = bitcast i8* [[T2]] to [[ARRAY_T]]*
+
+// Make sure it's not immediately released before starting the iteration.
+// CHECK-LP64-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
+// CHECK-LP64-NEXT: [[T0:%.*]] = bitcast [[ARRAY_T]]* [[COLL]] to i8*
+// CHECK-LP64-NEXT: @objc_msgSend
+
+// This bitcast is for the mutation check.
+// CHECK-LP64:      [[T0:%.*]] = bitcast [[ARRAY_T]]* [[COLL]] to i8*
+// CHECK-LP64-NEXT: @objc_enumerationMutation
+
+// This bitcast is for the 'next' message send.
+// CHECK-LP64:      [[T0:%.*]] = bitcast [[ARRAY_T]]* [[COLL]] to i8*
+// CHECK-LP64-NEXT: @objc_msgSend
+
+// This bitcast is for the final release.
+// CHECK-LP64:      [[T0:%.*]] = bitcast [[ARRAY_T]]* [[COLL]] to i8*
+// CHECK-LP64-NEXT: call void @objc_release(i8* [[T0]])

Modified: cfe/trunk/test/CodeGenObjCXX/arc.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjCXX/arc.mm?rev=136204&r1=136203&r2=136204&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjCXX/arc.mm (original)
+++ cfe/trunk/test/CodeGenObjCXX/arc.mm Tue Jul 26 20:07:15 2011
@@ -1,5 +1,16 @@
 // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s
 
+struct NSFastEnumerationState;
+ at interface NSArray
+- (unsigned long) countByEnumeratingWithState: (struct NSFastEnumerationState*) state
+                  objects: (id*) buffer
+                  count: (unsigned long) bufferSize;
+ at end;
+NSArray *nsarray() { return 0; }
+// CHECK: define [[NSARRAY:%.*]]* @_Z7nsarrayv()
+
+void use(id);
+
 // rdar://problem/9315552
 // The analogous ObjC testcase test46 in arr.m.
 void test0(__weak id *wp, __weak volatile id *wvp) {
@@ -164,3 +175,37 @@
   // CHECK: objc_autoreleaseReturnValue
   return z;
 }
+
+// Template instantiation side of rdar://problem/9817306
+ at interface Test37
+- (NSArray *) array;
+ at end
+template <class T> void test37(T *a) {
+  for (id x in a.array) {
+    use(x);
+  }
+}
+extern template void test37<Test37>(Test37 *a);
+template void test37<Test37>(Test37 *a);
+// CHECK: define weak_odr void @_Z6test37I6Test37EvPT_(
+// CHECK-LP64:      [[T0:%.*]] = call [[NSARRAY]]* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to [[NSARRAY]]* (i8*, i8*)*)(
+// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[NSARRAY]]* [[T0]] to i8*
+// CHECK-LP64-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]])
+// CHECK-LP64-NEXT: [[COLL:%.*]] = bitcast i8* [[T2]] to [[NSARRAY]]*
+
+// Make sure it's not immediately released before starting the iteration.
+// CHECK-LP64-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
+// CHECK-LP64-NEXT: [[T0:%.*]] = bitcast [[NSARRAY]]* [[COLL]] to i8*
+// CHECK-LP64-NEXT: @objc_msgSend
+
+// This bitcast is for the mutation check.
+// CHECK-LP64:      [[T0:%.*]] = bitcast [[NSARRAY]]* [[COLL]] to i8*
+// CHECK-LP64-NEXT: @objc_enumerationMutation
+
+// This bitcast is for the 'next' message send.
+// CHECK-LP64:      [[T0:%.*]] = bitcast [[NSARRAY]]* [[COLL]] to i8*
+// CHECK-LP64-NEXT: @objc_msgSend
+
+// This bitcast is for the final release.
+// CHECK-LP64:      [[T0:%.*]] = bitcast [[NSARRAY]]* [[COLL]] to i8*
+// CHECK-LP64-NEXT: call void @objc_release(i8* [[T0]])

Modified: cfe/trunk/test/SemaObjC/arc.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/arc.m?rev=136204&r1=136203&r2=136204&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjC/arc.m (original)
+++ cfe/trunk/test/SemaObjC/arc.m Tue Jul 26 20:07:15 2011
@@ -638,3 +638,10 @@
   id obj = __builtin_va_arg(arglist, id);
   __builtin_va_end(arglist);
 }
+
+ at class Test37;
+void test37(Test37 *c) {
+  for (id y in c) { // expected-error {{collection expression type 'Test37' is a forward declaration}}
+    (void) y;
+  }
+}





More information about the cfe-commits mailing list