[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