-
Notifications
You must be signed in to change notification settings - Fork 10.7k
Description
Description
According to SE-0461 a @concurrent function shouldn't be converted to nonisolated(nonsending) function. However, I find compiler fails to prevent it in some scenarios. In the code below, both test1 and test2 should fail to compile, but test2 compiles.
@concurrent
func fn(_ ns: NS) async {}
func foo(_ fn: nonisolated(nonsending) (NS) async -> Void) {}
func test1() {
foo(fn) // error: cannot convert '@Sendable (NS) async -> ()' to 'nonisolated(nonsending) (NS) async -> Void' because crossing of an isolation boundary requires parameter and result types to conform to 'Sendable' protocol
}
func test2() {
let fn: @concurrent (NS) async -> Void = { _ in }
foo(fn) // OK
}
Below is a more complete test program to verify that fn runs on global executor indeed. It also demonstrates how that could lead to a data race.
class NS {
var value = 0
}
func currentIsolation(isolation actor: (any Actor)? = #isolation) -> String {
return "\(actor, default: "non-isolated")"
}
func logStart(_ fnName: String, _ isolation: String) {
print("BEGIN \(fnName) [\(isolation)]")
}
func logEnd(_ fnName: String) {
print("END \(fnName)")
}
actor A {
var ns = NS()
func foo(_ fn: nonisolated(nonsending) (NS) async -> Void) async {
logStart("foo", currentIsolation())
await fn(self.ns)
logEnd("foo")
}
func test() {
let fn: @concurrent (NS) async -> Void = { ns in
logStart("fn", currentIsolation())
_ = ns
logEnd("fn")
}
Task {
await foo(fn)
}
// Task {
// await foo(fn)
// }
}
}
let a = A()
await a.test()
try? await Task.sleep(for: .seconds(1))
// test output:
// BEGIN foo [output.A]
// BEGIN fn [non-isolated]
// END fn
// END foo
Note the output. It shows that fn actually runs on global executor. If we starts multiple tasks (see the code commented out), there are multiple instances of fn running on global executor and mutating A.ns simultaneously.
Reproduction
See above
Expected behavior
test2 in example code should fail to compile.
Environment
nightly build. The issue can be reproduced in dev snapshot and Swift 6.2 too.
Additional information
No response