I think you're right that as? is missing the check required by the SE, but I also think there's another few holes in the SE:
Protocols that extend Sendable shouldn't be eligible for isolated conformances, else:
import Dispatch
protocol P: Sendable {
func f()
}
@MainActor
struct S: @MainActor P {
func f() {
MainActor.preconditionIsolated()
}
}
@MainActor
func example(_ s: any Sendable) {
let p = s as! any P // on the main actor, so the cast is allowed dynamically
DispatchQueue.global().async {
p.f() // oops, sent it anyway
}
}
example(S())
dispatchMain()
But even without Sendable, you can still get into trouble:
import Dispatch
protocol P {
func f()
}
@MainActor
struct S: @MainActor P {
func f() {
MainActor.preconditionIsolated()
}
}
@MainActor
func example(_ s: sending Any) {
let p = s as! any P // cast is dynamically OK
Task.detached {
p.f() // oops, sending
}
}
example(S())
dispatchMain()
For the second example, I don't see a solution except to have casts join the result to the region of the casting code?