Skip to content

Commit 95aee2e

Browse files
committed
Add migration paths
1 parent 8268dd9 commit 95aee2e

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

proposals/NNNN-extensible-enums.md

+116
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ With the following proposed solution we want to achieve the following goals:
168168
language mode so they can start declaring **new** extensible enumerations
169169
3. Provide a migration path to the new behavior without forcing new SemVer
170170
majors
171+
4. Provide tools for developers to treat dependencies as source stable
171172

172173
We propose to introduce a new language feature `ExtensibleEnums` that aligns the
173174
behavior of enumerations in both language dialects. This will make **public**
@@ -255,6 +256,113 @@ The behavior of `swift package diagnose-api-breaking-changes` is also updated
255256
to understand if the language feature is enabled and only diagnose new enum
256257
cases as a breaking change in non-frozen enumerations.
257258

259+
### Migration paths
260+
261+
The following section is outlining the migration paths and tools we provide for
262+
different kinds of projects to adopt the proposed feature. The goal is to reduce
263+
churn across the ecosystem while still allowing us to align the default behavior
264+
of enums. There are many scenarios why these migration paths must exist such as:
265+
266+
- Projects split up into multiple packages
267+
- Projects build with other tools than Swift PM
268+
- Projects explicitly vendoring packages without wanting to modify the original
269+
source
270+
- Projects that prefer to deal with source breaks as they come up rather than
271+
writing source-stable code
272+
273+
#### Semantically versioned packages
274+
275+
Semantically versioned packages are the primary reason for this proposal. The
276+
expected migration path for packages when adopting the proposed feature is one
277+
of the two:
278+
279+
- API stable adoption by turning on the feature and marking all existing public
280+
enums with `@frozen`
281+
- API breaking adoption by turning on the feature and tagging a new major if the
282+
public API contains enums
283+
284+
### Projects with multiple non-semantically versioned packages
285+
286+
A common project setup is splitting the code base into multiple packages that
287+
are not semantically versioned. This can either be done by using local packages
288+
or by using _revision locked_ dependencies. The packages in such a setup are
289+
often considered part of the same logical collection of code and would like to
290+
follow the same source stability rules as same module or same package code. We
291+
propose to extend then package manifest to allow overriding the package name
292+
used by a target.
293+
294+
```swift
295+
extension SwiftSetting {
296+
/// Defines the package name used by the target.
297+
///
298+
/// This setting is passed as the `-package-name` flag
299+
/// to the compiler. It allows overriding the package name on a
300+
/// per target basis. The default package name is the package identity.
301+
///
302+
/// - Important: Package names should only be aligned across co-developed and
303+
/// co-released packages.
304+
///
305+
/// - Parameters:
306+
/// - name: The package name to use.
307+
/// - condition: A condition that restricts the application of the build
308+
/// setting.
309+
public static func packageName(_ name: String, _ condition: PackageDescription.BuildSettingCondition? = nil) -> PackageDescription.SwiftSetting
310+
}
311+
```
312+
313+
This allows to construct arbitrary package _domains_ across multiple targets
314+
inside a single package or across multiple packages. When adopting the
315+
`ExtensibleEnums` feature across multiple packages the new Swift setting can be
316+
used to continue allowing exhaustive matching.
317+
318+
While this setting allows treating multiple targets as part of the same package.
319+
This setting should only be used across packages when the packages are
320+
both co-developed and co-released.
321+
322+
### Other build systems
323+
324+
Swift PM isn't the only system used to create and build Swift projects. Build
325+
systems and IDEs such as Bazel or Xcode offer support for Swift projects as
326+
well. When using such tools it is common to split a project into multiple
327+
targets/modules. Since those targets/modules are by default not considered to be
328+
part of the package, when adopting the `ExtensibleEnums` feature it would
329+
require to either add an `@unknown default` when switching over enums defined in
330+
other targets/modules or marking all public enums as `@frozen`. Similarly, to
331+
the above to avoid this churn we recommend specifying the `-package-name` flag
332+
to the compiler for all targets/modules that should be considered as part of the
333+
same unit.
334+
335+
### Escape hatch
336+
337+
There might still be cases where developers need to consume a module that is
338+
outside of their control which adopts the `ExtensibleEnums` feature. For such
339+
cases we propose to introduce a flag `--assume-source-stable-package` that
340+
allows assuming modules of a package as source stable. When checking if a switch
341+
needs to be exhaustive we will check if the code is either in the same module,
342+
the same package, or if the defining package is assumed to be source stable.
343+
This flag can be passed multiple times to define a set of assumed-source-stable
344+
packages.
345+
346+
```swift
347+
// a.swift inside Package A
348+
public enum MyEnum {
349+
case foo
350+
case bar
351+
}
352+
353+
// b.swift inside Package B compiled with `--assume-source-stable-package A`
354+
355+
switch myEnum { // No @unknown default case needed
356+
case .foo:
357+
print("foo")
358+
case .bar:
359+
print("bar")
360+
}
361+
```
362+
363+
In general, we recommend to avoid using this flag but it provides an important
364+
escape hatch to the ecosystem.
365+
258366
## Source compatibility
259367

260368
- Enabling the language feature `ExtensibleEnums` in a module compiled without
@@ -304,6 +412,14 @@ dependency graph. This would allow a package to adopt the new language feature,
304412
break their existing, and release a new major while having minimal impact on
305413
the larger ecosystem.
306414

415+
### Using `--assume-source-stable-packages` for other diagnostics
416+
417+
During the pitch it was brought up that there are more potential future
418+
use-cases for assuming modules of another package as source stable such as
419+
borrowing from a declaration which distinguishes between a stored property and
420+
one written with a `get`. Such features would also benefit from the
421+
`--assume-source-stable-packages` flag.
422+
307423
## Alternatives considered
308424

309425
### Provide an `@extensible` annotation

0 commit comments

Comments
 (0)