Skip to content

Commit 723d785

Browse files
committed
[Concurrency] Ban non-escaping closures in @asyncHandler
An asynchronous handler appears to be a synchronous function, but actually runs its body in a detached task. That means that any non-escaping closure parameters to the asynchronous handler will effectively escape. Fixes rdar://70820569.
1 parent 0861f7e commit 723d785

File tree

5 files changed

+35
-14
lines changed

5 files changed

+35
-14
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4135,6 +4135,9 @@ ERROR(asynchandler_async,none,
41354135
ERROR(asynchandler_inout_parameter,none,
41364136
"'inout' parameter is not allowed in '@asyncHandler' function",
41374137
())
4138+
ERROR(asynchandler_noescape_closure_parameter,none,
4139+
"non-escaping closure parameter is not allowed in '@asyncHandler' function",
4140+
())
41384141
ERROR(asynchandler_mutating,none,
41394142
"'@asyncHandler' function cannot be 'mutating'",
41404143
())

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ static bool checkAsyncHandler(FuncDecl *func, bool diagnose) {
8787

8888
return true;
8989
}
90+
91+
if (auto fnType = param->getInterfaceType()->getAs<FunctionType>()) {
92+
if (fnType->isNoEscape()) {
93+
if (diagnose) {
94+
param->diagnose(diag::asynchandler_noescape_closure_parameter);
95+
}
96+
97+
return true;
98+
}
99+
}
90100
}
91101

92102
if (func->isMutating()) {

lib/Sema/TypeCheckDecl.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,19 +2217,6 @@ static Type validateParameterType(ParamDecl *decl) {
22172217
}
22182218
}
22192219

2220-
// async autoclosures can only occur as parameters to async functions.
2221-
if (decl->isAutoClosure()) {
2222-
if (auto fnType = Ty->getAs<FunctionType>()) {
2223-
if (fnType->isAsync() &&
2224-
!(isa<AbstractFunctionDecl>(dc) &&
2225-
cast<AbstractFunctionDecl>(dc)->isAsyncContext())) {
2226-
decl->diagnose(diag::async_autoclosure_nonasync_function);
2227-
if (auto func = dyn_cast<FuncDecl>(dc))
2228-
addAsyncNotes(func);
2229-
}
2230-
}
2231-
}
2232-
22332220
return Ty;
22342221
}
22352222

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,19 @@ void TypeChecker::checkParameterList(ParameterList *params,
13221322
DeclContext *owner) {
13231323
for (auto param: *params) {
13241324
checkDeclAttributes(param);
1325+
1326+
// async autoclosures can only occur as parameters to async functions.
1327+
if (param->isAutoClosure()) {
1328+
if (auto fnType = param->getInterfaceType()->getAs<FunctionType>()) {
1329+
if (fnType->isAsync() &&
1330+
!(isa<AbstractFunctionDecl>(owner) &&
1331+
cast<AbstractFunctionDecl>(owner)->isAsyncContext())) {
1332+
param->diagnose(diag::async_autoclosure_nonasync_function);
1333+
if (auto func = dyn_cast<FuncDecl>(owner))
1334+
addAsyncNotes(func);
1335+
}
1336+
}
1337+
}
13251338
}
13261339

13271340
// For source compatibilty, allow duplicate internal parameter names

test/attr/asynchandler.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ func globalAsyncFunction() async -> Int { 0 }
99
let _ = await globalAsyncFunction()
1010
}
1111

12-
@asyncHandler func asyncHandler2(fn: @autoclosure () async -> Int ) {
12+
@asyncHandler func asyncHandler2(fn: @autoclosure @escaping () async -> Int ) {
13+
// okay, it's an async context
14+
}
15+
16+
@asyncHandler func asyncHandler3(fn: @escaping () -> Int) {
1317
// okay, it's an async context
1418
}
1519

@@ -29,6 +33,10 @@ func asyncHandlerBad3() throws { }
2933
func asyncHandlerBad4(result: inout Int) { }
3034
// expected-error@-1{{'inout' parameter is not allowed in '@asyncHandler' function}}
3135

36+
@asyncHandler
37+
func asyncHandlerBad5(result: () -> Int) { }
38+
// expected-error@-1{{non-escaping closure parameter is not allowed in '@asyncHandler' function}}
39+
3240
actor class X {
3341
@asyncHandler func asyncHandlerMethod() { }
3442

0 commit comments

Comments
 (0)