diff --git a/Guide.docc/LibraryEvolution.md b/Guide.docc/LibraryEvolution.md new file mode 100644 index 0000000..dc92740 --- /dev/null +++ b/Guide.docc/LibraryEvolution.md @@ -0,0 +1,211 @@ +# Library Evolution + +Annotate library APIs for concurrency while preserving source and ABI +compatibility. + +Concurrency annotations such as `@MainActor` and `@Sendable` can impact source +and ABI compatibility that library authors should be aware of when annotating +existing APIs. + +## Preconcurrency annotations + +The `@preconcurrency` attribute can be used directly on library APIs to +stage in new concurrency requirements that are checked at compile time +without breaking source or ABI compatibility for clients: + +```swift +@preconcurrency @MainActor +struct S { ... } + +@preconcurrency +public func performConcurrently( + completion: @escaping @Sendable () -> Void +) { ... } +``` + +Clients do not need to use a `@preconcurrency import` for the new errors +to be downgraded. If the clients build with minimal concurrency checking, +errors from `@preconcurrency` APIs will be suppressed. If the clients build +with complete concurrency checking or the Swift 6 language mode, the errors +will be downgraded to warnings. + +For ABI compatibility, `@preconcurrency` will mangle symbol names without any +concurrency annotations. If an API was introduced with some concurrency +annotations, and is later updated to include additional concurrency +annotations, then applying `@preconcurrency` is not sufficient for preserving +mangling. `@_silgen_name` can be used in cases where you need more precise +control over mangling concurrency annotations. + +Note that all APIs imported from C, C++, and Objective-C are automatically +considered `@preconcurrency`. Concurrency attributes can always be applied +to these APIs using `__attribute__((__swift_attr__("")))` +without breaking source or ABI compatibility. + +## Sendable + +### Conformances on concrete types + +Adding a `Sendable` conformance to a concrete type, including conditional +conformances, is typically a source compatible change in practice: + +```diff +-public struct S ++public struct S: Sendable +``` + +Like any other conformance, adding a conformance to `Sendable` can change +overload resolution if the concrete type satisfies more specialized +requirements. However, it's unlikely that an API which overloads on a +`Sendable` conformance would change type inference in a way that breaks +source compatibility or program behavior. + +Adding a `Sendable` conformance to a concrete type, and not one of its type +parameters, is always an ABI compatible change. + +### Generic requirements + +Adding a `Sendable` conformance requirement to a generic type or function is +a source incompatible change, because it places a restriction on generic +arguments passed by the client: + +```diff +-public func generic ++public func generic where T: Sendable +``` + +Apply `@preconcurrency` to the type or function declaration to downgrade +requirement failures to warnings and preserve ABI: + +```swift +@preconcurrency +public func generic where T: Sendable { ... } +``` + +### Function types + +Like generic requirements, adding `@Sendable` to a function type is a +source and ABI incompatible change: + +```diff +-public func performConcurrently(completion: @escaping () -> Void) ++public func performConcurrently(completion: @escaping @Sendable () -> Void) +``` + +Apply `@preconcurrency` to the enclosing function declaration to downgrade +requirement failures to warnings and preserve ABI: + +```swift +@preconcurrency +public func performConcurrently(completion: @escaping @Sendable () -> Void) +``` + +## Main actor annotations + +### Protocols and types + +Adding `@MainActor` annotations to protocols or type declarations is a source +and ABI incompatible change: + +```diff +-public protocol P ++@MainActor public protocol P + +-public class C ++@MainActor public class C +``` + +Adding `@MainActor` to protocols and type declarations has a wider impact than +other concurrency annotations because the `@MainActor` annotation can be +inferred throughout client code, including protocol conformances, subclasses, +and extension methods. + +Applying `@preconcurrency` to the protocol or type declaration will downgrade +actor isolation errors based on the concurrency checking level. However, +`@preconcurrency` is not sufficient for preserving ABI compatibility for +clients in cases where the `@preconcurrency @MainActor` annotation can be +inferred on other declarations in client code. For example, consider the +following API in a client library: + +```swift +extension P { + public func onChange(action: @escaping @Sendable () -> Void) +} +``` + +If `P` is retroactively annotated with `@preconcurrency @MainActor`, these +annotations will be inferred on the extension method. If an extension method is +also part of a library with ABI compatibility constraints, then +`@preconcurrency` will strip all concurrency related annotations from mangling. +This can be worked around in the client library either by applying the +appropriate isolation explicitly, such as: + +```swift +extension P { + nonisolated public func onChange(action: @escaping @Sendable () -> Void) +} +``` + +Language affordances for precise control over the ABI of a declaration are +[under development](https://forums.swift.org/t/pitch-controlling-the-abi-of-a-declaration/75123). + +### Function declarations and types + +Adding `@MainActor` to a function declaration or a function type is a +source and ABI incompatible change: + +```diff +-public func runOnMain() ++@MainActor public func runOnMain() + +-public func performConcurrently(completion: @escaping () -> Void) ++public func performConcurrently(completion: @escaping @MainActor () -> Void) +``` + +Apply `@preconcurrency` to the enclosing function declaration to downgrade +requirement failures to warnings and preserve ABI: + +```swift +@preconcurrency @MainActor +public func runOnMain() { ... } + +@preconcurrency +public func performConcurrently(completion: @escaping @MainActor () -> Void) { ... } +``` + +## sending parameters and results + +Adding `sending` to a result lifts restrictions in client code, and is +always a source and ABI compatible change: + +```diff +-public func getValue() -> NotSendable ++public func getValue() -> sending NotSendable +``` + +However, adding `sending` to a parameter is more restrictive at the caller: + +```diff +-public func takeValue(_: NotSendable) ++public func takeValue(_: sending NotSendable) +``` + +Adding `sending` to a parameter also changes name mangling, so any adoption +must preserve the mangling using `@_silgen_name`. Adopting `sending` in +parameter position must preserve the ownership convention of parameters. No +additional annotation is necessary if the parameter already has an explicit +ownership modifier. For all functions except initializers, use +`__shared sending` to preserve the ownership convention: + +```swift +public func takeValue(_: __shared sending NotSendable) +``` + +For initializers, `sending` preserves the default ownership convention, so it's not +necessary to specify an ownership modifier when adopting `sending` on initializer +parameters: + +```swift +public class C { + public init(ns: sending NotSendable) +} +``` diff --git a/Guide.docc/MigrationGuide.md b/Guide.docc/MigrationGuide.md index cfdd8e4..dbc7227 100644 --- a/Guide.docc/MigrationGuide.md +++ b/Guide.docc/MigrationGuide.md @@ -73,6 +73,7 @@ For more information, see the [contributing][] document. - - - +- ### Swift Concurrency in Depth