apple / apple/swift-async-algorithms
MultiProducerSingleConsumerAsyncChannel breaks library evolution (BUILD_LIBRARY_FOR_DISTRIBUTION=YES) in 1.1.x
- Dominant language
- Swift
- Stars
- 3.7k
- Forks
- 226
- Avg merge
- 10d 3h
- Merged PRs (30d)
- 1
Description
## Summary
`swift-async-algorithms` 1.1.x is incompatible with `BUILD_LIBRARY_FOR_DISTRIBUTION=YES` (Swift library evolution mode). Every version in the 1.1.x line (1.1.0 through 1.1.3) fails to compile when used as a dependency of an XCFramework built with library evolution enabled. The 1.0.x line (including 1.0.4) compiles cleanly under the same conditions.
This is a blocking issue for any team distributing a binary SDK as an XCFramework that depends on `swift-async-algorithms`.
---
## Environment
| Property | Value |
|----------|-------|
| Xcode | 26.3 |
| Swift | 6.2.4 |
| Platform | iOS |
| Minimum deployment target | iOS 16.0 |
| swift-async-algorithms | 1.1.3 (reproduced on 1.1.0, 1.1.1, 1.1.2) |
| Build flag | `BUILD_LIBRARY_FOR_DISTRIBUTION=YES` |
---
## How to Reproduce
Add `swift-async-algorithms` 1.1.x as a dependency to any Swift package and archive with library evolution enabled:
```bash
xcodebuild archive \
-workspace MySDK.xcworkspace \
-scheme MySDK \
-destination "generic/platform=iOS" \
-archivePath build/MySDK.xcarchive \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
SKIP_INSTALL=NO
```
---
## Errors
All 17 errors originate from a single file introduced in 1.1.0:
```
Sources/AsyncAlgorithms/MultiProducerSingleConsumerChannel/MultiProducerSingleConsumerAsyncChannel+Internal.swift
```
### Type 1 — `@inlinable` on a class designated initializer (1 error)
```
error: initializer for class '_Storage' is '@inlinable' and must delegate to another initializer
```
Under library evolution, a class's stored properties are accessed through resilient accessors. A designated initializer must initialize those properties directly and therefore cannot be `@inlinable`.
**Location:** `_Storage.init(backpressureStrategy:)` — line 122.
### Type 2 — `switch consume self._state` on a non-`@frozen` struct (16 errors)
```
error: 'consume' can only be used to partially consume storage
```
`_StateMachine` is a non-`@frozen` struct. Under library evolution its stored properties are accessed through resilient accessors rather than direct storage. The `consume` operator requires direct ownership of storage and cannot be applied to `self._state` through a resilient accessor.
All 16 affected locations are `@inlinable mutating func` methods on `_StateMachine`:
| Line | Method |
|------|--------|
| 552 | `setOnTerminationCallback(sourceID:callback:)` |
| 596 | `cancelOnTerminationCallback(sourceID:)` |
| 628 | `finish(sourceID:error:)` |
| 694 | `cancelSource(sourceID:)` |
| 723 | `registerConsumer(continuation:)` |
| 782 | `cancelConsumer(continuation:)` |
| 835 | `next(for:)` |
| 888 | `send(contentsOf:)` — first overload |
| 967 | `send(contentsOf:)` — second overload |
| 1057 | `send(_:)` |
| 1107 | `addSource()` |
| 1155 | `removeSource(sourceID:)` |
| 1209 | `cancelSource(sourceID:)` — second overload |
| 1288 | `nextFromConsumer()` |
| 1373 | `nextFromProducer(sourceID:)` |
| 1458 | `cancelNextFromProducer(sourceID:)` |
> **Note:** The `switch consume sendResult` patterns in `MultiProducerSingleConsumerAsyncChannel.swift` are not affected — those operate on a local variable, not a stored property, and are valid under library evolution.
---
## Question
Was the incompatibility with `BUILD_LIBRARY_FOR_DISTRIBUTION=YES` / library evolution considered when `MultiProducerSingleConsumerAsyncChannel` was introduced in 1.1.0? Is there a plan to address this in an upcoming release?
The `1.0.x` line remains the only usable version for teams building XCFrameworks today.
Contributor guide
Research direction
Reproduce the failure with the provided xcodebuild archive command and BUILD_LIBRARY_FOR_DISTRIBUTION=YES. Inspect Sources/AsyncAlgorithms/MultiProducerSingleConsumerChannel/MultiProducerSingleConsumerAsyncChannel+Internal.swift, especially _Storage.init(backpressureStrategy:) and the listed _StateMachine methods. Done means the 1.1.x dependency compiles successfully when building an iOS XCFramework with library evolution enabled.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100