-
Notifications
You must be signed in to change notification settings - Fork 481
Description
As a consequence of SE-0412: Strict concurrency for global variables, we're getting new concurrency warnings when building swift-protobuf. They can be narrowed down to two places:
Default instances of storage classes:
swift-protobuf/Sources/SwiftProtobuf/AnyMessageStorage.swift:144:14: error: static property 'defaultInstance'
is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor; this
is an error in Swift 6
static let defaultInstance = AnyMessageStorage()
^
swift-protobuf/Sources/SwiftProtobuf/descriptor.pb.swift:3111:16: error: static property 'defaultInstance' is
not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor; this is
an error in Swift 6
static let defaultInstance = _StorageClass()
^
The _NameMap
of each generated message:
swift-protobuf/Sources/SwiftProtobuf/any.pb.swift:195:21: error: static property '_protobuf_nameMap' is not
concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor; this is an
error in Swift 6
public static let _protobuf_nameMap: _NameMap = [
^
SE-0412 added the requirements that:
Under strict concurrency checking, require every global variable to either be isolated to a global actor or be both:
- immutable
- of
Sendable
type
Neither the storage classes nor _NameMap
are Sendable
. I need some more eyes who are better versed with Swift concurrency than I am 🙂
Storage classes
For the storage classes, we could just drop the defaultInstance
property entirely and just allocate a new empty instance. However, it is a nice optimization to defer allocation of storage until the message is actually mutated. It's not super important if you're creating a new message and then immediately parsing or mutating it, but anywhere that we return an empty message as a default value of an unset message-typed field, it's very nice to not have to allocate storage if the user is only going to read from it.
Can we just mark the storage classes as Sendable
on @unchecked Sendable
since the messages themselves already conform to that, and the storage types can't be accessed from outside the message?
_NameMap
_NameMap
looked more challenging because of the string interning logic and storage of Unsafe*Pointer
s, but upon further reflection, a _NameMap
can never be mutated after its been initialized, so I think we could just declare _NameMap
to be @unchecked Sendable
and call it a day, right?
As a follow-up, now that Swift String
s are UTF-8 backed, we could probably revisit the UTF-8 buffer interning approach and just store String
s directly. The UTF-8 buffer interning dates back to 2017, so it was when Swift native strings weren't guaranteed to be UTF-8 backed, which I believe was the original motivation for the buffer approach.