Swift Digest
SE-0331 | Swift Evolution

Remove Sendable conformance from unsafe pointer types

Proposal
SE-0331
Authors
Andrew Trick
Review Manager
Doug Gregor
Status
Implemented (Swift 5.6)

01 何が問題だったのか

SE-0302Sendable プロトコルが導入された際に、UnsafePointer / UnsafeMutablePointer / UnsafeBufferPointer などの unsafe pointer 系の型は、無条件に Sendable に適合するものとされていました。unsafe pointer はもともと「開発者が責任を持って正しく使う」ことが前提の仕組みなので、concurrency に関する安全性も同じ枠組みで扱えばよい、という考え方でした。

しかし、実際に Sendable を運用してみると、この扱いは不必要に危険で、意図しない副作用を生むことが分かってきました。

Sendable の本来の役割は、参照セマンティクスを持つ型をアクターやタスクの境界を越えて共有させないことで、データ競合を防ぐことにあります。unsafe pointer は参照セマンティクスを持つため、本来 Sendable であるべきではありません。unsafe pointer が持つ「メモリの寿命管理は開発者の責任」という既知の unsafety は、「concurrent なコードでの利用も無検査で許す」という別種の unsafety に暗黙に拡張されるべきではなく、他の参照型と同様に concurrency に関する診断で守られるべきです。

さらに深刻なのは、unsafe pointer を stored property として持つ値型への波及です。たとえば次のようなラッパーを考えます。

struct FileHandle { // implicitly Sendable
  var stored: UnsafeMutablePointer<File>
}

FileHandle のすべての stored property が Sendable であれば、FileHandle 自身も暗黙に Sendable と推論されます。unsafe pointer 自体を Sendable とみなすかどうかはともかく、それを包んだ FileHandle のような型まで自動的に Sendable になってしまうと、ユーザーは気づかないうちに危険な型を境界越しに共有できる状態を手に入れてしまいます。

02 どのように解決されるのか

SE-0302 で付与された unsafe pointer 系の型の Sendable 適合を取り除きます。対象となるのは次の型です。

  • AutoreleasingUnsafeMutablePointer
  • OpaquePointer
  • CVaListPointer
  • UnsafePointer / UnsafeMutablePointer / UnsafeRawPointer / UnsafeMutableRawPointer
  • UnsafeBufferPointer / UnsafeMutableBufferPointer / UnsafeRawBufferPointer / UnsafeMutableRawBufferPointer
  • UnsafeBufferPointer.Iterator / UnsafeMutableBufferPointer.Iterator / UnsafeRawBufferPointer.Iterator / UnsafeMutableRawBufferPointer.Iterator

これにより、unsafe pointer をそのままアクターやタスクの境界を越えて渡そうとすると、他の非 Sendable 型と同様に concurrency の診断が働くようになります。また、UnsafeMutablePointer を stored property として持つ型(前述の FileHandle のようなラッパー)が暗黙に Sendable と推論されてしまう挙動もなくなり、必要なら明示的に Sendable 適合を書く設計に戻ります。

どうしても unsafe pointer を境界越しに渡したい場合は、開発者が責任を持って @unchecked Sendable なラッパー型を用意するなど、明示的に「安全性は自分で保証している」と表明するかたちを取ります。

既存コードへの影響

Sendable が導入されて間もないこと、そして Sendable 違反はまだエラーではなく警告として段階的に導入されている段階であることから、この変更による破壊的な影響は限定的です。これまで unsafe pointer の Sendable 適合に暗黙に依存していたコードは、警告として検出されるため、必要に応じて @unchecked Sendable などで意図を明示しながら段階的に移行できます。