Hello! I came across an interesting case lately where actor isolation is not respected or warned when conforming to a delegate from an extension on an @MainActor class. For example, the below code compiles without warnings:
protocol MyDelegate {
func doThing()
}
@MainActor
class MyClass {
var myCounter = 0
}
extension MyClass: MyDelegate {
func doThing() {
myCounter += 1
print("Count incremented to \(myCounter)")
}
}
var v: MyDelegate?
Task { @MainActor in
v = MyClass()
}
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 1) {
v?.doThing()
}
This seems problematic as I am calling doThing() in a nonisolated context and mutating myCounter outside of the MainActor. I would expect to get a warning here telling me to mark doThing() as nonisolated or an error around my nonisolated call to doThing(). If I annotate the extension or the protocol with @MainActor I do get warnings and errors, but with the above code I don't.
Can someone help me understand what's going on here? Is this a bug, or am I just missing something? Thanks in advance!
That's correct. If I move the conformance up within the type definition scope, I get a "Main actor-isolated instance method 'doThing()' cannot be used to satisfy nonisolated protocol requirement" warning.
Explicitly marking the extension as @MainActor also causes the same warning to surface, but it's not clear to me why that isn't implicitly happening.
I filed a bug recently that seems to be similar. You can compile some unsafe code with no warnings, and Xcode will even trigger a data race warning when you run it.
I only came across this while messing with static properties on protocols, but your example shows it's a bigger problem.