A (better?) example:
Over in "Inferring @Sendable for methods and key path literals", I pointed out that currently you can form a KeyPath to a @MainActor property, whilst not on the main actor. You can then use that KeyPath to write to that property from any actor, bypassing the actor isolation.
If I've understood Holly's response correctly, she is suggesting that to fix this hole,
- The key path literal for a
@MainActorproperty should not beSendable, and - It should be statically illegal to form a key path literal to a
@MainActorproperty outside of a@MainActorscope.
That seems reasonable to me, and I believe it would currently fix the problem.
If I've understood this proposal correctly, you would now be able to do something like this:
actor DoNaughtyThings<Root: AnyObject> {
var root: Root
init(root: Root) { self.root = root }
func setKeyPath<Root, T: Sendable>(_ keyPath: ReferenceWritableKeyPath<Root, T>, to value: T) {
root[keyPath: keyPath] = value
}
}
@MainActor
final class OnlyOnMain {
var s: String = "hi" {
willSet {
MainActor.preconditionIsolated() // should be fine, in an @MainActor var...
}
}
}
@MainActor
func commitCrimes() async {
let onlyOnMain = OnlyOnMain()
let naughty = DoNaughtyThings(root: onlyOnMain) // should be OK, since @MainActor types are Sendable?
let keyPath = \OnlyOnMain.s // OK, we're on the main actor. keyPath has type `ReferenceWritableKeyPath<OnlyOnMain, String>`. It does not have `& Sendable`
await naughty.setKeyPath(keyPath, to: "uhoh") // OK, transferring keyPath to the actor, but it's not used again
// crash in `preconditionIsolated`, since it's called from the `DoNaughtyThings` actor?
}