Unexpected Warning: Capture of non-sendable type

When building a package with Xcode 26 beta I get now a couple of concurrency related warnings where the Swift 6.1 compiler was happy with.

I was able to fix some of those by conforming a protocol to Sendable. However, I'm actually not totally convinced that this should be required, but as it didn't hurt in my use case, this is probably for another topic.

Here, I will show the issue where I suspect that the warning is not justified, and where I don't have a solution:

protocol P: Sendable {
    associatedtype State: P2
}

protocol P2 {}

@MainActor
final class Foo<T: P> {
    
    private(set) var state: T.State
    
    init(initialState: T.State) {
        self.state = initialState
        Task {
            try await self.run(keyPath: \.state) 
                           ^ Warning: Capture of non-sendable type 'T.State.Type' in an isolated closure
        }
    }

    private func run(
        keyPath: ReferenceWritableKeyPath<Foo, T.State>
    ) async throws {
    }
}

Note: the Swift 6.2 compiler does want protocol P to conform to Sendable, otherwise I do get more warnings (these are the cases, which I can easily fix in my use case)

Important:
If associatedtype State in protocol P will not be constrained to protocol P2, i.e.:

protocol P: Sendable {
    associatedtype State
}

then, the compiler does not emit the warning in this line:

try await self.run(keyPath: \.state)

So, what would be the correct way to resolve the warning?

Is the warning correct/reasonable, and if yes, how would one benefit from it?

(If it should turn out in cases where the code above is used, that there is actually a concurrency issue, the compiler should be able to emit an error anyway, IMHO)

A more reduced example:

Summary
protocol P2 {}

@MainActor
final class Foo<State: P2> {
    
    private(set) var state: State
    
    init(initialState: State) {
        self.state = initialState
        Task {
            try await self.run(keyPath: \.state)
        }
    }

    private func run(
        keyPath: ReferenceWritableKeyPath<Foo, State>
    ) async throws {
    }
}
1 Like

There is an article about this on swift.org: Sendable Metatypes.

You need to add a SendableMetatype constraint. You can add it for all P like this:

protocol P: Sendable {
    associatedtype State: P2 & SendableMetatype
}

Or you can add it for all Foo like this:

final class Foo<T: P> where T.State: SendableMetatype {

Or you can add it for just that init like this:

    init(initialState: T.State) where T.State: SendableMetatype {

I think the use of SendableMetatype should back-deploy (it is a marker protocol with no constraints and the documentation says it is available since iOS 8, macOS 10.10, etc.). However, Xcodes older than 26 beta 1 don't know it. So you may also want to add this definition:

#if compiler(<6.2)
public typealias SendableMetatype = Any
#endif

You can find more discussion here: Issues with SE-0470: Global-actor isolated conformances

1 Like

Thank you very much for the insights and the links. I wasn't aware of SE-0470, until now.

There's also another thread on swiftlang here: Swift 6.2: undesired "Capture of non-sendable type 'T.Type'" warnings · Issue #82116 · swiftlang/swift · GitHub