Grammar of a closure expression
closure-expression β { attributes? closure-signature? statements? }
closure-signature β capture-list? closure-parameter-clause async? throws-clause? function-result? in
closure-signature β capture-list in
closure-parameter-clause β ( ) | ( closure-parameter-list ,? ) | identifier-list
closure-parameter-list β closure-parameter | closure-parameter , closure-parameter-list
closure-parameter β closure-parameter-name type-annotation?
closure-parameter β closure-parameter-name type-annotation ...
closure-parameter-name β identifier
capture-list β [ capture-list-items ,? ]
capture-list-items β capture-list-item | capture-list-item , capture-list-items
capture-list-item β capture-specifier? identifier
capture-list-item β capture-specifier? identifier = expression
capture-list-item β capture-specifier? self-expression
capture-specifier β weak | unowned | unowned(safe) | unowned(unsafe)
ββ docs.swift.org | Expressions | Closure Expression | Capture Lists
This is not a supported configuration at the moment. Take this error as guidance for reading the chart
await withTaskCancellationHandler { @nonisolated(nonsending) in
// `~~~~~~~~~~~~~~~~~~~~~~~
// error: 'nonisolated' is a declaration modifier, not an attribute
β¦
} onCancel: {
β¦
}
Relevant Grammar:
closure-expression β { attributes? closure-signature? statements? }
closure-signature β capture-list? closure-parameter-clause async? throws-clause? function-result? in
closure-signature β capture-list in
Declaration modifiers are not accounted in these grammar specs and as such are conflated by the compiler as variables and other valid resources. If you really donβt want a extra line, you can hint the compiler with as nonisolated(nonsending) () -> Void
You could also make a wrapper function that hints the compiler that the closure should be nonisolated(nonsending) by elaborating the type or hint the closures as being nonisolated(nonsending) with as on the closure as a parameter.
Potential Wrapper Implementation
This should be a good wrapper function to do hinting with minimal excess besides what is necessary for your use.
// This is `@inline(always)` as it is a pass through function
/// `nonisolatedNonsending:` must be passed `()` to hint
/// that this is the right function.
/// A information free type is used as this is a compiler hint
/// and as the values passed in are not accessible to the function.
/// Simply add `(nonisolatedNonsending: ())` into your call site
/// to hint for this overload.
@inline(always)
func withTaskCancellationHandler<T>(
nonisolatedNonsending _: Void,
operation: nonisolated(nonsending) () async throws -> T,
onCancel handler: @Sendable () -> Void,
isolation: isolated (any Actor)?
) async rethrows -> T {
return try await Swift.withTaskCancellationHandler(
operation: operation,
onCancel : handler,
isolation: isolation
)
}
You could potentially use an attached macro but that would only be worth it if you have many places where you want to have nonisolated or multiple declaration modifiers that are commonly used in shorthanded closures