From 809435e390e91355f64bee0142a65c4fe6e9f488 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 22 Apr 2021 09:48:54 -0700 Subject: [PATCH] [Sema] Don't set BlockDecl's DoesNotEscape bit if the parameter type of the function the block is passed to isn't a block pointer type This patch fixes a bug where a block passed to a function taking a parameter that doesn't have a block pointer type (e.g., id or reference to a block pointer) was marked as noescape. This partially fixes PR50043. rdar://77030453 Differential Revision: https://reviews.llvm.org/D101097 --- clang/lib/Sema/SemaExpr.cpp | 3 +- clang/lib/Sema/SemaExprObjC.cpp | 3 +- clang/test/SemaObjCXX/noescape.mm | 86 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 4c44295..e0f46a0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5911,7 +5911,8 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl, (!Param || !Param->hasAttr())) CFAudited = true; - if (Proto->getExtParameterInfo(i).isNoEscape()) + if (Proto->getExtParameterInfo(i).isNoEscape() && + ProtoArgType->isBlockPointerType()) if (auto *BE = dyn_cast(Arg->IgnoreParenNoopCasts(Context))) BE->getBlockDecl()->setDoesNotEscape(); diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index 71c1500..68a38ac 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -1821,7 +1821,8 @@ bool Sema::CheckMessageArgumentTypes( ParmVarDecl *param = Method->parameters()[i]; assert(argExpr && "CheckMessageArgumentTypes(): missing expression"); - if (param->hasAttr()) + if (param->hasAttr() && + param->getType()->isBlockPointerType()) if (auto *BE = dyn_cast( argExpr->IgnoreParenNoopCasts(Context))) BE->getBlockDecl()->setDoesNotEscape(); diff --git a/clang/test/SemaObjCXX/noescape.mm b/clang/test/SemaObjCXX/noescape.mm index 199d1ae..4b52164 100644 --- a/clang/test/SemaObjCXX/noescape.mm +++ b/clang/test/SemaObjCXX/noescape.mm @@ -16,6 +16,13 @@ void noescapeFunc3(__attribute__((noescape)) id); void noescapeFunc4(__attribute__((noescape)) int &); void noescapeFunc2(int *); // expected-error {{conflicting types for 'noescapeFunc2'}} +template +void noescapeFunc5(__attribute__((noescape)) T); // expected-warning {{'noescape' attribute only applies to pointer arguments}} +template +void noescapeFunc6(__attribute__((noescape)) const T &); +template +void noescapeFunc7(__attribute__((noescape)) T &&); + void invalidFunc0(int __attribute__((noescape))); // expected-warning {{'noescape' attribute only applies to pointer arguments}} void invalidFunc1(int __attribute__((noescape(0)))); // expected-error {{'noescape' attribute takes no arguments}} void invalidFunc2(int0 *__attribute__((noescape))); // expected-error {{use of undeclared identifier 'int0'; did you mean 'int'?}} @@ -38,10 +45,16 @@ struct S3 : S1 { __attribute__((objc_root_class)) @interface C0 -(void) m0:(int*)__attribute__((noescape)) p; // expected-note {{parameter of overridden method is annotated with __attribute__((noescape))}} +- (void)noescapeLValRefParam:(const BlockTy &)__attribute__((noescape))p; +- (void)noescapeRValRefParam:(BlockTy &&)__attribute__((noescape))p; @end @implementation C0 -(void) m0:(int*)__attribute__((noescape)) p {} +- (void)noescapeLValRefParam:(const BlockTy &)__attribute__((noescape))p { +} +- (void)noescapeRValRefParam:(BlockTy &&)__attribute__((noescape))p { +} @end @interface C1 : C0 @@ -131,11 +144,11 @@ __attribute__((objc_root_class)) struct S6 { S6(); - S6(const S6 &) = delete; // expected-note 3 {{'S6' has been explicitly marked deleted here}} + S6(const S6 &) = delete; // expected-note 12 {{'S6' has been explicitly marked deleted here}} int f; }; -void test1() { +void test1(C0 *c0) { id a; // __block variables that are not captured by escaping blocks don't // necessitate having accessible copy constructors. @@ -143,6 +156,17 @@ void test1() { __block S6 b1; // expected-error {{call to deleted constructor of 'S6'}} __block S6 b2; // expected-error {{call to deleted constructor of 'S6'}} __block S6 b3; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b4; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b5; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b6; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b7; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b8; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b9; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b10; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b11; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b12; + __block S6 b13; + __block S6 b14; // expected-error {{call to deleted constructor of 'S6'}} noescapeFunc0(a, ^{ (void)b0; }); escapingFunc0(^{ (void)b1; }); @@ -151,4 +175,62 @@ void test1() { } noescapeFunc0(a, ^{ escapingFunc0(^{ (void)b2; }); }); escapingFunc0(^{ noescapeFunc0(a, ^{ (void)b3; }); }); + + [c0 noescapeLValRefParam:^{ + (void)b4; + }]; + + [c0 noescapeRValRefParam:^{ + (void)b5; + }]; + + void noescape_id(__attribute__((noescape)) id); + noescape_id(^{ + (void)b6; + }); + + void noescapeLValRefParam(__attribute__((noescape)) const BlockTy &); + noescapeLValRefParam(^{ + (void)b7; + }); + + void noescapeRValRefParam(__attribute__((noescape)) BlockTy &&); + noescapeRValRefParam(^{ + (void)b8; + }); + + // FIXME: clang shouldn't reject this. + noescapeFunc5(^{ + (void)b9; + }); + + noescapeFunc6(^{ + (void)b10; + }); + + noescapeFunc7(^{ + (void)b11; + }); + + struct NoescapeCtor { + NoescapeCtor(__attribute__((noescape)) void (^)()); + }; + struct EscapeCtor { + EscapeCtor(void (^)()); + }; + + void helper1(NoescapeCtor a); + helper1(^{ + (void)b12; + }); + + void helper2(NoescapeCtor && a); + helper2(^{ + (void)b13; + }); + + void helper3(__attribute__((noescape)) EscapeCtor && a); + helper3(^{ + (void)b14; + }); } -- 2.7.4