I think that is different although it doesn't work for the same reason. I don't think SE-0434 and SE-0461 work correctly together, or at least 461 is underspecified in the context of 434? Sema doesn't have enough information to decide if this cast is safe (disconnected is not a Sema concept), and I'm not clear if 461 is taking into account that 434 means actor isolated can imply Sendable, I think 434 gives the better rule. Maybe 461 was hoping Sema would defer this to RBI? If you wrote this, it would be legal:
let disconnectedClosure = { @MainActor in
ns.value += 1
}
because ns would be sent into disconnectedClosure, which RBI will ensure is ok.
You could also treat a function as sending when it is disconnected, that is fine, RBI will ensure it is ok. It's a similar idea as capturing it, except capturing it when it's already in an isolation can go further, and call it repeatedly.
class NotSendable {
var value = 0
}
nonisolated(nonsending)
func convert(closure: () -> Void) async {
let ns = NotSendable()
let disconnectedClosure = {
ns.value += 1
}
await take(disconnectedClosure)
}
@MainActor
func take(_ f: sending () -> ()) {
f()
}
But Sema doesn't know which functions are sending, and can't reason about the cast shown in 461. For Sema to allow the cast in 461, Sema / RBI also needs to prevent use of the function after casting!
Capturing functions vs casting functions is asking about implicit to explicit isolation cast, which I think is "more" ok, since it is casting a @MainActor isolated (due to being a param of a @MainActor func which Sema knows) and doesn't need to prevent use of the original value. 461 describes a cast which is informed by RBI, and needs to prevent use of the original value.
The same pattern which works in the original question works for the 461 example:
class NotSendable {
var value = 0
}
nonisolated(nonsending)
func convert(closure: () -> Void) async {
let ns = NotSendable()
let disconnectedClosure = {
ns.value += 1
}
let valid: @MainActor () -> Void = { disconnectedClosure() } // okay
await valid()
}
but the only safe way to call disconnectedClosure after capturing it is if it was @MainActor.