Skip to content

Commit 11d801d

Browse files
committed
[Concurrency] Add @asyncHandler attribute.
An asynchronous handler behaves externally like a synchronous function, but internally it is handled like an asynchronous function, meaning that it can execute operations with suspension points such as calling other async functions. The body of the function is executed just as if it and its caller were async functions, except that the caller is resumed when the callee reaches its first suspension point rather than only when the callee exits.
1 parent 9331cfa commit 11d801d

File tree

6 files changed

+115
-0
lines changed

6 files changed

+115
-0
lines changed

include/swift/AST/Attr.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,11 @@ SIMPLE_DECL_ATTR(noDerivative, NoDerivative,
560560
ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove,
561561
100)
562562

563+
SIMPLE_DECL_ATTR(asyncHandler, AsyncHandler,
564+
OnFunc | UserInaccessible |
565+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove |
566+
NotSerialized, 101)
567+
563568
#undef TYPE_ATTR
564569
#undef DECL_ATTR_ALIAS
565570
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsSema.def

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4074,6 +4074,28 @@ NOTE(protocol_witness_async_conflict,none,
40744074
ERROR(async_autoclosure_nonasync_function,none,
40754075
"'async' autoclosure parameter in a non-'async' function", ())
40764076

4077+
ERROR(asynchandler_attr_requires_concurrency,none,
4078+
"'@asyncHandler' is only valid when experimental concurrency is enabled",
4079+
())
4080+
ERROR(asynchandler_non_func,none,
4081+
"'@asyncHandler' can only be applied to functions",
4082+
())
4083+
ERROR(asynchandler_returns_value,none,
4084+
"'@asyncHandler' function can only return 'Void'",
4085+
())
4086+
ERROR(asynchandler_throws,none,
4087+
"'@asyncHandler' function cannot throw",
4088+
())
4089+
ERROR(asynchandler_async,none,
4090+
"'@asyncHandler' function cannot be 'async' itself",
4091+
())
4092+
ERROR(asynchandler_inout_parameter,none,
4093+
"'inout' parameter is not allowed in '@asyncHandler' function",
4094+
())
4095+
ERROR(asynchandler_mutating,none,
4096+
"'@asyncHandler' function cannot be 'mutating'",
4097+
())
4098+
40774099
//------------------------------------------------------------------------------
40784100
// MARK: Type Check Types
40794101
//------------------------------------------------------------------------------

lib/Sema/TypeCheckAttr.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,59 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
257257
void visitDifferentiableAttr(DifferentiableAttr *attr);
258258
void visitDerivativeAttr(DerivativeAttr *attr);
259259
void visitTransposeAttr(TransposeAttr *attr);
260+
261+
void visitAsyncHandlerAttr(AsyncHandlerAttr *attr) {
262+
if (!Ctx.LangOpts.EnableExperimentalConcurrency) {
263+
diagnoseAndRemoveAttr(attr, diag::asynchandler_attr_requires_concurrency);
264+
return;
265+
}
266+
267+
auto func = dyn_cast<FuncDecl>(D);
268+
if (!func) {
269+
diagnoseAndRemoveAttr(attr, diag::asynchandler_non_func);
270+
return;
271+
}
272+
273+
if (!func->getResultInterfaceType()->isVoid()) {
274+
func->diagnose(diag::asynchandler_returns_value)
275+
.highlight(func->getBodyResultTypeLoc().getSourceRange());
276+
attr->setInvalid();
277+
return;
278+
}
279+
280+
if (func->hasThrows()) {
281+
func->diagnose(diag::asynchandler_throws)
282+
.fixItRemove(func->getThrowsLoc());
283+
attr->setInvalid();
284+
return;
285+
}
286+
287+
if (func->hasAsync()) {
288+
func->diagnose(diag::asynchandler_async)
289+
.fixItRemove(func->getAsyncLoc());
290+
attr->setInvalid();
291+
return;
292+
}
293+
294+
for (auto param : *func->getParameters()) {
295+
if (param->isInOut()) {
296+
param->diagnose(diag::asynchandler_inout_parameter)
297+
.fixItRemove(param->getSpecifierLoc());
298+
attr->setInvalid();
299+
return;
300+
}
301+
}
302+
303+
if (func->isMutating()) {
304+
auto diag = Ctx.Diags.diagnose(func, diag::asynchandler_mutating);
305+
diag.highlight(attr->getRangeWithAt());
306+
if (auto mutatingAttr = func->getAttrs().getAttribute<MutatingAttr>()) {
307+
diag.fixItRemove(mutatingAttr->getRange());
308+
}
309+
attr->setInvalid();
310+
return;
311+
}
312+
}
260313
};
261314
} // end anonymous namespace
262315

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,7 @@ namespace {
14161416
UNINTERESTING_ATTR(AccessControl)
14171417
UNINTERESTING_ATTR(Alignment)
14181418
UNINTERESTING_ATTR(AlwaysEmitIntoClient)
1419+
UNINTERESTING_ATTR(AsyncHandler)
14191420
UNINTERESTING_ATTR(Borrowed)
14201421
UNINTERESTING_ATTR(CDecl)
14211422
UNINTERESTING_ATTR(Consuming)

test/attr/asynchandler.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %target-swift-frontend -typecheck -verify %s -enable-experimental-concurrency
2+
3+
@asyncHandler func asyncHandler1() { }
4+
5+
@asyncHandler
6+
func asyncHandlerBad1() -> Int { 0 }
7+
// expected-error@-1{{'@asyncHandler' function can only return 'Void'}}
8+
9+
@asyncHandler
10+
func asyncHandlerBad2() async { }
11+
// expected-error@-1{{'@asyncHandler' function cannot be 'async' itself}}{{25-31=}}
12+
13+
@asyncHandler
14+
func asyncHandlerBad3() throws { }
15+
// expected-error@-1{{'@asyncHandler' function cannot throw}}{{25-32=}}
16+
17+
@asyncHandler
18+
func asyncHandlerBad4(result: inout Int) { }
19+
// expected-error@-1{{'inout' parameter is not allowed in '@asyncHandler' function}}
20+
21+
struct X {
22+
@asyncHandler func asyncHandlerMethod() { }
23+
24+
@asyncHandler
25+
mutating func asyncHandlerMethodBad1() { }
26+
// expected-error@-1{{'@asyncHandler' function cannot be 'mutating'}}{{3-12=}}
27+
28+
@asyncHandler init() { }
29+
// expected-error@-1{{@asyncHandler may only be used on 'func' declarations}}
30+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// RUN: %target-swift-frontend -typecheck -verify %s
2+
3+
@asyncHandler func asyncHandler1() { }
4+
// expected-error@-1{{'@asyncHandler' is only valid when experimental concurrency is enabled}}

0 commit comments

Comments
 (0)