I ran into an interesting problem today. This code requires that MyProtocol refine SendableMetatype.
@MainActor
protocol MyProtocol: AnyObject, SendableMetatype {
}
public struct TypeContainer: Sendable {
let protocolType: MyProtocol.Type
}
This makes sense, because of the MyProtocol.Type reference within a Sendable type. But, it left me thinking. I kind of expected a global actor isolated protocol to refine, almost by definition, SendableMetatype. I spent some type trying to cook up a scenario where this would not be the case. But this stuff is complex.
I don't think this is a particularly big deal either way. But, if anyone has any ideas, I'd love to better understand this.
A common point of confusion that I see is people not understanding why protocols with a global actor attribute don't implicitly refine Sendable. The reason why protocols with global actor attributes don't automatically refine Sendable is because the global actor isolation is not a requirement of the conforming type. The global actor attribute on a protocol has two effects:
It applies isolation to all protocol requirements unless otherwise annotated
It's a default inference source for conforming types, but conforming types can opt out of this isolation, and the isolation inference does not apply if the conformance is written in an extension (possibly outside the module)
There's no requirement that the conforming type actually be global actor isolated, so it's not the case that all conforming types are Sendable by definition. A protocol requiring Sendable should be done carefully -- only if the generic code that uses the protocol as a conformance constraint actually needs it -- because a Sendable requirement forces conformances to implement a thread-safe type, which might be difficult if the type is not global actor isolated.
Similar reasoning applies to SendableMetatype, which is restrictive because it prevents things like isolated conformances, and should be required only when the generic code needs to use the metatype concurrently. It is possible to have an isolated conformance to a protocol that has a global actor attribute if one of the protocol requirements is marked nonisolated:
You could have a concrete type that conforms to MyProtocol where the implementation of b is main actor isolated. MyProtocol implicitly refining SendableMetatype would prevent this. Obviously this is super contrived, but I've seen use cases where a protocol wants conformances to be able to be main actor isolated by default, but also wants to support concurrently invoking protocol requirements only if the conforming type opted into it by implementing the requirements as nonisolated methods.