From 100c6491c99e92f6b6e9551d0b0cfe3d65eb306b Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 30 Mar 2012 05:23:48 +0000 Subject: [PATCH] Forbid the block and lambda copy-capture of __autoreleasing variables in ARC, under the usual reasoning limiting the use of __autoreleasing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153725 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/AutomaticReferenceCounting.html | 4 +++- include/clang/Basic/DiagnosticSemaKinds.td | 3 +++ lib/Sema/SemaExpr.cpp | 21 +++++++++++++++++++++ test/SemaObjC/arc-invalid.m | 5 +++++ test/SemaObjCXX/arc-0x.mm | 21 +++++++++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/AutomaticReferenceCounting.html b/docs/AutomaticReferenceCounting.html index ff65f3be48..1416df58e4 100644 --- a/docs/AutomaticReferenceCounting.html +++ b/docs/AutomaticReferenceCounting.html @@ -1006,7 +1006,9 @@ operation has a weak-unavailable type.

Storage duration of __autoreleasing objects

A program is ill-formed if it declares an __autoreleasing -object of non-automatic storage duration.

+object of non-automatic storage duration. A program is ill-formed +if it captures an __autoreleasing object in a block or, +unless by reference, in a C++11 lambda.

Rationale: autorelease pools are tied to the current thread and scope by their nature. While it is possible to diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9dd04b99d5..30780104d6 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3270,6 +3270,9 @@ def warn_err_new_delete_object_array : Warning< def err_arc_autoreleasing_var : Error< "%select{__block variables|global variables|fields|ivars}0 cannot have " "__autoreleasing ownership">; +def err_arc_autoreleasing_capture : Error< + "cannot capture __autoreleasing variable in a " + "%select{block|lambda by copy}0">; def err_arc_thread_ownership : Error< "thread-local variable has non-trivial ownership: type is %0">; def err_arc_indirect_no_ownership : Error< diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 6b30643db9..88ef8cd5d8 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -10087,6 +10087,17 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, return true; } + // Forbid the block-capture of autoreleasing variables. + if (CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { + if (BuildAndDiagnose) { + Diag(Loc, diag::err_arc_autoreleasing_capture) + << /*block*/ 0; + Diag(Var->getLocation(), diag::note_previous_decl) + << Var->getDeclName(); + } + return true; + } + if (HasBlocksAttr || CaptureType->isReferenceType()) { // Block capture by reference does not change the capture or // declaration reference types. @@ -10179,6 +10190,16 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, if (!RefType->getPointeeType()->isFunctionType()) CaptureType = RefType->getPointeeType(); } + + // Forbid the lambda copy-capture of autoreleasing variables. + if (CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { + if (BuildAndDiagnose) { + Diag(Loc, diag::err_arc_autoreleasing_capture) << /*lambda*/ 1; + Diag(Var->getLocation(), diag::note_previous_decl) + << Var->getDeclName(); + } + return true; + } } // Capture this variable in the lambda. diff --git a/test/SemaObjC/arc-invalid.m b/test/SemaObjC/arc-invalid.m index b07460dbec..c736ed4a16 100644 --- a/test/SemaObjC/arc-invalid.m +++ b/test/SemaObjC/arc-invalid.m @@ -11,3 +11,8 @@ void takeBlock(void (^)(void)); void test0(id p) { takeBlock(^{ [p foo] + p; }); // expected-error {{invalid operands to binary expression}} } + +void test1(void) { + __autoreleasing id p; // expected-note {{'p' declared here}} + takeBlock(^{ (void) p; }); // expected-error {{cannot capture __autoreleasing variable in a block}} +} diff --git a/test/SemaObjCXX/arc-0x.mm b/test/SemaObjCXX/arc-0x.mm index be9f1dc2e2..28eec51775 100644 --- a/test/SemaObjCXX/arc-0x.mm +++ b/test/SemaObjCXX/arc-0x.mm @@ -30,3 +30,24 @@ void deduction(id obj) { } @catch (auto e) { // expected-error {{'auto' not allowed in exception declaration}} } } + +// rdar://problem/11068137 +void test1a() { + __autoreleasing id p; // expected-note 2 {{'p' declared here}} + (void) [&p] {}; + (void) [p] {}; // expected-error {{cannot capture __autoreleasing variable in a lambda by copy}} + (void) [=] { (void) p; }; // expected-error {{cannot capture __autoreleasing variable in a lambda by copy}} +} +void test1b() { + __autoreleasing id v; + __autoreleasing id &p = v; // expected-note 2 {{'p' declared here}} + (void) [&p] {}; + (void) [p] {}; // expected-error {{cannot capture __autoreleasing variable in a lambda by copy}} + (void) [=] { (void) p; }; // expected-error {{cannot capture __autoreleasing variable in a lambda by copy}} +} +void test1c() { + __autoreleasing id v; // expected-note {{'v' declared here}} + __autoreleasing id &p = v; + (void) ^{ (void) p; }; + (void) ^{ (void) v; }; // expected-error {{cannot capture __autoreleasing variable in a block}} +} -- 2.40.0