-
Notifications
You must be signed in to change notification settings - Fork 175
Description
Related
Relates to #203 — same file, different error site. Together these represent all Swift 6.3 build failures in NetworkTransport.swift.
Description
NetworkTransport.swift fails to build on Swift 6.3 development snapshots due to nonisolated(unsafe) being applied to stored properties of type CheckedContinuation, which is not Sendable.
Swift 6.3 tightens enforcement: nonisolated(unsafe) now requires the annotated type to conform to Sendable. CheckedContinuation<T, E> is intentionally non-Sendable (SE-0302) to prevent double-resume at the type level.
To Reproduce
# Using any Swift 6.3 development snapshot
TOOLCHAINS=org.swift.dev.snapshot.2026-03-16-a swift buildCompiler Errors
NetworkTransport.swift:56:30: error: 'nonisolated(unsafe)' global or static variables must be of type 'Sendable'
nonisolated(unsafe) var transportContinuation: CheckedContinuation<NWConnection, any Error>?
^
NetworkTransport.swift:57:30: error: 'nonisolated(unsafe)' global or static variables must be of type 'Sendable'
nonisolated(unsafe) var stoppedContinuation: CheckedContinuation<Void, any Error>?
^
Note: The diagnostic says "global or static variables" but these are actor instance properties — this appears to be a misleading compiler diagnostic.
Environment
- Builds successfully on: Swift 6.0.3 release
- Fails on: Swift 6.3-dev (snapshot 2026-03-16-a)
- Package swift-tools-version: 6.0
Root Cause
Lines 56-57 of NetworkTransport.swift:
nonisolated(unsafe) var transportContinuation: CheckedContinuation<NWConnection, any Error>?
nonisolated(unsafe) var stoppedContinuation: CheckedContinuation<Void, any Error>?These properties are accessed from NWConnection's stateUpdateHandler callback, which runs off-actor (on the connection's dispatch queue). The nonisolated(unsafe) annotation was used to suppress actor isolation errors, but Swift 6.3 now requires the type itself to be Sendable.
Suggested Fix
Use a lock-protected wrapper that is Sendable and works on all currently supported platforms (macOS 13+/iOS 16+), without requiring import Synchronization (which would bump minimums to macOS 15+/iOS 18+):
/// Thread-safe wrapper for cross-isolation access. Sendable because
/// all access is serialized through NSLock.
private final class LockedValue<Value>: @unchecked Sendable {
private var _value: Value
private let lock = NSLock()
init(_ value: Value) { self._value = value }
func withLock<T>(_ body: (inout Value) -> T) -> T {
lock.lock()
defer { lock.unlock() }
return body(&_value)
}
}Then replace:
nonisolated(unsafe) var transportContinuation: CheckedContinuation<NWConnection, any Error>?
nonisolated(unsafe) var stoppedContinuation: CheckedContinuation<Void, any Error>?With:
private let transportContinuation = LockedValue<CheckedContinuation<NWConnection, any Error>?>(nil)
private let stoppedContinuation = LockedValue<CheckedContinuation<Void, any Error>?>(nil)This preserves the existing platform minimums, compiles on both Swift 6.0.3 and 6.3-dev, and has the same API shape as Mutex.withLock for future migration when platform minimums are bumped.
Also worth noting (relates to #203)
The @MainActor usage in sendData() and receiveData() (lines 531, 762) is a separate but related issue. @MainActor is being used purely as a serialization mechanism for continuation guard booleans — but this is an MCP protocol library with no UI. The @MainActor hop adds unnecessary main-thread contention and is semantically incorrect for a headless protocol SDK. The sendHeartbeat() method in the same file (line ~460) does NOT use this guard pattern, resuming the continuation directly — suggesting the guard was added reactively rather than by design.