apple / apple/swift-async-algorithms

MultiProducerSingleConsumerAsyncChannel breaks library evolution (BUILD_LIBRARY_FOR_DISTRIBUTION=YES) in 1.1.x

Open
#405 0 comments 3 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.