Forbid the block and lambda copy-capture of __autoreleasing variables

in ARC, under the usual reasoning limiting the use of __autoreleasing.

llvm-svn: 153725
This commit is contained in:
John McCall 2012-03-30 05:23:48 +00:00
parent 678a53c350
commit 67cd5e094e
5 changed files with 53 additions and 1 deletions

View File

@ -1006,7 +1006,9 @@ operation has a weak-unavailable type.</p>
<h1>Storage duration of <tt>__autoreleasing</tt> objects</h1>
<p>A program is ill-formed if it declares an <tt>__autoreleasing</tt>
object of non-automatic storage duration.</p>
object of non-automatic storage duration. A program is ill-formed
if it captures an <tt>__autoreleasing</tt> object in a block or,
unless by reference, in a C++11 lambda.</p>
<div class="rationale"><p>Rationale: autorelease pools are tied to the
current thread and scope by their nature. While it is possible to

View File

@ -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<

View File

@ -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.

View File

@ -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}}
}

View File

@ -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}}
}