- Proposal: SE-0331
- Authors: Andrew Trick
- Review Manager: Doug Gregor
- Status: Implemented (Swift 5.6)
- Decision Notes: Rationale
- Implementation: apple/swift#39218
SE-0302 introduced the Sendable
protocol, including Sendable
requirements for various language constructs, conformances of various standard library types to Sendable
, and inference rules for non-public types to implicitly conform to Sendable
. SE-0302 states that the unsafe pointer types conform to Sendable
:
Unsafe(Mutable)(Buffer)Pointer
: these generic types unconditionally conform to theSendable
protocol. This means that an unsafe pointer to a non-Sendable value can potentially be used to share such values between concurrency domains. Unsafe pointer types provide fundamentally unsafe access to memory, and the programmer must be trusted to use them correctly; enforcing a strict safety rule for one narrow dimension of their otherwise completely unsafe use seems inconsistent with that design.
Experience with Sendable
shows that this formulation is unnecessarily dangerous and has unexpected negative consequences for implicit conformance.
Swift-evolution thread: Discussion thread
The role of Sendable
is to prevent sharing reference-semantic types across actor or task boundaries. Unsafe pointers have reference semantics, and therefore should not be be Sendable
.
Unsafe pointers are unsafe in one primary way: it is the developer's responsibility to guarantee the lifetime of the memory referenced by the unsafe pointer. This is an intentional and explicit hole in Swift's memory safety story that has been around since the beginning. That well-understood form of unsafety should not be implicitly extended to allow pointers to be unsafely used in concurrent code. The concurrency diagnostics that prevent race conditions in other types with reference semantics should provide the same protection for pointers.
Another problem with making the unsafe pointers Sendable
is the second-order effect it has on value types that store unsafe pointers. Consider a wrapper struct around a resource:
struct FileHandle { // implicitly Sendable
var stored: UnsafeMutablePointer<File>
}
The FileHandle
type will be inferred to be Sendable
because all of its instance storage is Sendable
. Even if we accept that an UnsafeMutablePointer
by itself can be Sendable
because the "unsafe" can now apply to concurrency safety as well (as was argued in SE-0302), that same argument does not hold for the FileHandle
type. Removing the conformance of the unsafe pointer types to Sendable
eliminates the potential for it to propagate out to otherwise-safe wrappers.
Remove the Sendable
conformance introduced by SE-0302 for the following types:
AutoreleasingUnsafeMutablePointer
OpaquePointer
CVaListPointer
Unsafe(Mutable)?(Raw)?(Buffer)?Pointer
Unsafe(Mutable)?(Raw)?BufferPointer.Iterator
The removal of Sendable
conformances from unsafe pointer types can break code that depends on those conformances. There are two mitigating factors here that make us feel comfortable doing so at this time. The first mitigating factor is that Sendable
is only very recently introduced in Swift 5.5, and Sendable
conformances aren't enforced in the Swift Concurrency model just yet. The second is that the staging in of Sendable
checking in the compiler implies that missing Sendable
conformances are treated as warnings, not errors, so there is a smooth transition path for any code that depended on this now-removed conformances.
Sendable
is a marker protocol, which was designed to have zero ABI impact. Therefore, this proposal itself should have no ABI impact, because adding and removing Sendable
conformances is invisible to the ABI.
This proposal does not affect API resilience.
Keep SE-0302 behavior. This would require explicit "non-Sendable" annotation of many important aggregate types that embed unsafe pointers.