diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 2 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 2 | ||||
-rw-r--r-- | lib/CodeGen/CGObjC.cpp | 22 | ||||
-rw-r--r-- | lib/Parse/ParseStmt.cpp | 13 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 103 | ||||
-rw-r--r-- | lib/Sema/TreeTransform.h | 15 | ||||
-rw-r--r-- | test/CodeGenObjC/arc-foreach.m | 84 | ||||
-rw-r--r-- | test/CodeGenObjCXX/arc.mm | 45 | ||||
-rw-r--r-- | test/SemaObjC/arc.m | 7 |
9 files changed, 252 insertions, 41 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7ee93568e3..25d7763be5 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2715,6 +2715,8 @@ def err_arc_may_not_respond : Error< "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">; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index aab175733d..897d41efca 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2029,6 +2029,8 @@ public: FullExprArg Third, SourceLocation RParenLoc, Stmt *Body); + ExprResult ActOnObjCForCollectionOperand(SourceLocation forLoc, + Expr *collection); StmtResult ActOnObjCForCollectionStmt(SourceLocation ForColLoc, SourceLocation LParenLoc, Stmt *First, Expr *Second, diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index c817115cbe..f81b6b75e3 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -1020,8 +1020,16 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ 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 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ DI->EmitRegionEnd(Builder); } + // Leave the cleanup we entered in ARC. + if (getLangOptions().ObjCAutoRefCount) + PopCleanupBlock(); + EmitBlock(LoopEnd.getBlock()); } @@ -1967,6 +1979,12 @@ static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, 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; diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 622c8ea905..b34e4f8277 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1405,13 +1405,22 @@ StmtResult Parser::ParseForStatement(ParsedAttributes &attrs) { // 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 @@ StmtResult Parser::ParseForStatement(ParsedAttributes &attrs) { 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, diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index c22555e4dd..0fd3f03982 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -965,6 +965,76 @@ StmtResult Sema::ActOnForEachLValueExpr(Expr *E) { 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 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, 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)); } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 2221fc0c21..373339a2cb 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1200,7 +1200,16 @@ public: 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 @@ TreeTransform<Derived>::TransformObjCForCollectionStmt( 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()); diff --git a/test/CodeGenObjC/arc-foreach.m b/test/CodeGenObjC/arc-foreach.m index f9d7782fbe..c5593ffb70 100644 --- a/test/CodeGenObjC/arc-foreach.m +++ b/test/CodeGenObjC/arc-foreach.m @@ -5,7 +5,13 @@ extern void use(id); extern void use_block(void (^)(void)); -@class NSArray; + +struct NSFastEnumerationState; +@interface NSArray +- (unsigned long) countByEnumeratingWithState: (struct NSFastEnumerationState*) state + objects: (id*) buffer + count: (unsigned long) bufferSize; +@end; void test0(NSArray *array) { // 'x' should be initialized without a retain. @@ -17,12 +23,37 @@ void test0(NSArray *array) { } // 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 @@ void test0(NSArray *array) { // 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 @@ void test1(NSArray *array) { // 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 +@interface Test2 +- (NSArray *) array; +@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]]) diff --git a/test/CodeGenObjCXX/arc.mm b/test/CodeGenObjCXX/arc.mm index 0c5466a4ce..faf1c1a556 100644 --- a/test/CodeGenObjCXX/arc.mm +++ b/test/CodeGenObjCXX/arc.mm @@ -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; +@interface NSArray +- (unsigned long) countByEnumeratingWithState: (struct NSFastEnumerationState*) state + objects: (id*) buffer + count: (unsigned long) bufferSize; +@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 @@ id test36(id z) { // CHECK: objc_autoreleaseReturnValue return z; } + +// Template instantiation side of rdar://problem/9817306 +@interface Test37 +- (NSArray *) array; +@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]]) diff --git a/test/SemaObjC/arc.m b/test/SemaObjC/arc.m index 1ee76ecdba..49dd8a6951 100644 --- a/test/SemaObjC/arc.m +++ b/test/SemaObjC/arc.m @@ -638,3 +638,10 @@ void test36(int first, ...) { id obj = __builtin_va_arg(arglist, id); __builtin_va_end(arglist); } + +@class Test37; +void test37(Test37 *c) { + for (id y in c) { // expected-error {{collection expression type 'Test37' is a forward declaration}} + (void) y; + } +} |