Skip to content

Commit f7cc996

Browse files
committed
burp
1 parent 78c8b36 commit f7cc996

File tree

1 file changed

+287
-0
lines changed

1 file changed

+287
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
# Migration tooling for Swift upcoming features
2+
3+
* Proposal: [SE-NNNN](NNNN-filename.md)
4+
* Authors: [Anthony Latsis](https://github.com/AnthonyLatsis)
5+
* Review Manager: TBD
6+
* Status: **Awaiting implementation**
7+
* Implementation: TBD
8+
* Review: TBD
9+
10+
## Introduction
11+
12+
As Swift evolves, source-breaking changes to the language are staged behind new
13+
language modes and, more recently, [upcoming features][SE-0362] that may be
14+
enabled piecemeal without adopting an entire new language mode.
15+
16+
This proposal centers on the experience of adopting individual features.
17+
The premise is that Swift needs a built-in mechanism for supporting quality
18+
assistance with code migration and adoption.
19+
And that, in principle, comprehensive, code-aware assistance can be delivered
20+
without breaking source and followed incrementally.
21+
22+
[SE-0362]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md
23+
24+
## Motivation
25+
26+
For many projects, source-breaking language features are expected to generate
27+
hundreds of targeted errors.
28+
It is also likely for developers to encounter knock-on collateral errors that
29+
are not indicative of the exact cause or source of the problem.
30+
We should strive for a better migration experience than requiring programmers
31+
to slog through such a volume of errors and blocking development on atomic
32+
adoption.
33+
34+
### Automation
35+
36+
Many existing and prospective upcoming features provide reliable solutions for
37+
preserving behavior:
38+
39+
* [SE-0192]`NonfrozenEnumExhaustivity`: Restore exhaustivity with
40+
`@unknown default:`.
41+
* [SE-0274]`ConciseMagicFile`: `#file``#filePath`.
42+
* [SE-0286]`ForwardTrailingClosures`: Disambiguate argument matching by
43+
de-trailing closures or/and inlining default arguments.
44+
* [SE-0335]`ExistentialAny`: `P``any P`.
45+
* [SE-0352]`ImplicitOpenExistentials`: Suppress opening with `as any P` coercions.
46+
* [SE-0354]`BareSlashRegexLiterals`: `foo(/a, b/)``foo((/a), b/)`
47+
(disambiguate using parentheses).
48+
* [SE-0383]`DeprecateApplicationMain`: (`@UIApplicationMain`|`@NSApplicationMain`) → `@main`.
49+
* [SE-0401]`DisableOutwardActorInference`: Specify global actor isolation explicitly.
50+
* [SE-0409]`InternalImportsByDefault`: `import X``public import X`.
51+
* [SE-0412]`GlobalConcurrency`: Mark nonisolated global state with
52+
`nonisolated(unsafe)`.
53+
* [SE-0444]`MemberImportVisibility`: Add explicit imports appropriately.
54+
* [SE-0418]`InferSendableFromCaptures`: Suppress inference with coercions
55+
and type annotations.
56+
* [Inherit isolation by default for async functions][async-inherit-isolation-pitch]
57+
: Mark nonisolated functions with the proposed attribute.
58+
59+
UPCOMING_FEATURE(RegionBasedIsolation, 414, 6)
60+
61+
[SE-0192]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0192-non-exhaustive-enums.md
62+
[SE-0274]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0274-magic-file.md
63+
[SE-0286]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0286-forward-scan-trailing-closures.md
64+
[SE-0335]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0335-existential-any.md
65+
[SE-0352]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md
66+
[SE-0354]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0354-regex-literals.md
67+
[SE-0383]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md
68+
[SE-0401]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0401-remove-property-wrapper-isolation.md
69+
[SE-0409]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md
70+
[SE-0411]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0411-isolated-default-values.md
71+
[SE-0412]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0412-strict-concurrency-for-global-variables.md
72+
[SE-0418]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0418-inferring-sendable-for-methods.md
73+
[SE-0444]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md
74+
[async-inherit-isolation-pitch]: https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862
75+
76+
### Flexibility
77+
78+
Currently best solution is to warn under the feature + error in language mode
79+
where feature is enabled. This approach cannot be applied to all features (why?)
80+
81+
UPCOMING_FEATURE(DynamicActorIsolation, 423, 6)
82+
UPCOMING_FEATURE(GlobalActorIsolatedTypesUsability, 0434, 6)
83+
UPCOMING_FEATURE(ImportObjcForwardDeclarations, 384, 6)
84+
UPCOMING_FEATURE(StrictConcurrency, 0337, 6)
85+
UPCOMING_FEATURE(IsolatedDefaultValues, 411, 6)
86+
87+
Adjusting to new behaviors or language requirements can demand research,
88+
careful consideration, coordinated efforts, and manual code refactorings,
89+
sometimes on a case-by-case basis.
90+
91+
Something about the following being good illustrations of the problems
92+
described above:
93+
94+
* [SE-0409]: Introduces the concept of access levels on import declarations to
95+
give library developers control over which module dependencies are exposed to
96+
clients and an upcoming feature for defaulting to internal imports to bias
97+
toward hiding dependencies rather than exposing them. This feature can break
98+
source across modules and may require a coordinated adoption effort in large
99+
projects.
100+
101+
* [SE-0444] Fixing member import visibility: The goal of this proposal is to reduce the likelihood of ambiguities arising from the use of extension members. Swift’s existing rules about when imports are required when referencing external declarations in a source file are inconsistent and they lead to extension members being visible even when the module they are declared in has not been imported. This proposal amends the rules for member lookup to make them consistent with top-level name lookup, giving developers more direct control over which extension members are visible.
102+
103+
* [SE-0335] introduced syntax for existential or boxed types to visually
104+
distinguish them from conformance constraints and deliver contrast between
105+
dynamic existential types and fixed `some` types. The associated
106+
`ExistentialAny` feature is a vivid example where automatic migration is
107+
possible, but not desirable. The niche that exitential types occupy combined
108+
with their lighweight syntax make for an enticing and viable, yet often
109+
misused, abstraction construct. In places where type dynamism may be
110+
superfluous, we want to guide developers toward an informed choice of
111+
abstraction.
112+
113+
[SE-0335]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0335-existential-any.md
114+
[SE-0337]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md
115+
[SE-0409]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md
116+
[SE-0444]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md
117+
118+
### A standard for staging upcoming features
119+
120+
121+
122+
## Proposed Solution
123+
124+
Introduce the notion of a "preview" mode for features.
125+
Preview mode can be used to provide adoption tips and specific source changes
126+
that can be applied to preserve or enhance the behavior of existing code when
127+
the feature is effectively enabled.
128+
129+
## Detailed Design
130+
131+
### Behavior
132+
133+
Enabling an additive feature or previously disabled source-breaking feature in
134+
preview mode is necessarily a source-compatible action and must never result in
135+
behavioral changes or new errors.
136+
137+
The effect of preview mode on the state of a feature will depend on whether it
138+
can break source:
139+
* Additive features will be enabled in preview mode.
140+
* Source-breaking features will be disabled in preview mode.
141+
If a source-breaking feature does not implement preview mode, a corresponding
142+
warning will also be emitted to avoid the false impression that the impacted
143+
code is source-compatible with the feature.
144+
145+
Experimental features can be both additive and source-breaking. Upcoming
146+
features are necessarily source-breaking. Baseline features, such as
147+
`async`/`await`, are necessarily additive and cannot be disabled. They are
148+
currently defined for the sole purpose of supporting feature availability
149+
checks in conditional compilation blocks: `#if hasFeature`.
150+
151+
Preview mode will deliver guidance in the shape of warnings, notes, remarks,
152+
and fix-its, as and when appropriate.
153+
154+
When implemented, preview mode for upcoming features is expected to anticipate
155+
and call out any hard and soft source breaks — compiler errors and silent
156+
behavioral changes — that would result from actually enabling the feature,
157+
coupling diagnostic messages with counteracting source-compatible changes
158+
whenever possible.
159+
However, as previously highlighted with `ExistentialAny`, preview mode cannot
160+
promise to offer exclusively source-compatible changes because the source
161+
impact of a change is generally nondeterministic.
162+
Neither can it promise to always offer source changes in the first place for the
163+
same reason in regards to user intention.
164+
Although upcoming features should strive to facilitate automatic code
165+
migration, every source-breaking feature poses a unique trade-off between
166+
estimated impact and code migration solutions.
167+
168+
### Interface
169+
170+
#### Compiler
171+
172+
The `-enable-*-feature` frontend and driver command line options will start
173+
supporting an optional mode specifier with `preview` as the only valid mode:
174+
175+
```
176+
-enable-upcoming-feature <feature>[:<mode>]
177+
-enable-experimental-feature <feature>[:<mode>]
178+
179+
<mode> := preview
180+
```
181+
182+
For example:
183+
184+
```
185+
-enable-upcoming-feature InternalImportsByDefault:preview
186+
```
187+
188+
In a series of either of these options applied to a given feature, only the
189+
last option will be honored.
190+
If a feature is both implied by the effective language mode and enabled in
191+
preview mode using either of the aforementioned options, the latter will be
192+
disregarded, analogous to how the language mode overrides
193+
`-disable-upcoming-feature` today.
194+
195+
#### Swift Package Manager
196+
197+
The [`SwiftSetting.enableUpcomingFeature`] and
198+
[`SwiftSetting.enableExperimentalFeature`] methods from the
199+
[`PackageDescription`](https://developer.apple.com/documentation/packagedescription)
200+
library will be augmented with a `mode` parameter defaulted to match the
201+
current behavior:
202+
203+
```swift
204+
extension SwiftSetting {
205+
@available(_PackageDescription, introduced: 6.2)
206+
public enum SwiftFeatureMode {
207+
case preview
208+
case on
209+
}
210+
}
211+
```
212+
```diff
213+
public static func enableUpcomingFeature(
214+
_ name: String,
215+
+ mode: SwiftFeatureMode = .on,
216+
_ condition: BuildSettingCondition? = nil
217+
) -> SwiftSetting {
218+
+ let argument = switch mode {
219+
+ case .preview: "\(name):preview"
220+
+ case .mode: name
221+
+ }
222+
+
223+
return SwiftSetting(
224+
- name: "enableUpcomingFeature", value: [name], condition: condition)
225+
+ name: "enableUpcomingFeature", value: [argument], condition: condition)
226+
}
227+
```
228+
```diff
229+
public static func enableExperimentalFeature(
230+
_ name: String,
231+
+ mode: SwiftFeatureMode = .on,
232+
_ condition: BuildSettingCondition? = nil
233+
) -> SwiftSetting {
234+
+ let argument = switch mode {
235+
+ case .preview: "\(name):preview"
236+
+ case .mode: name
237+
+ }
238+
+
239+
return SwiftSetting(
240+
- name: "enableExperimentalFeature", value: [name], condition: condition)
241+
+ name: "enableExperimentalFeature", value: [argument], condition: condition)
242+
}
243+
```
244+
245+
For example:
246+
247+
```
248+
SwiftSetting.enableUpcomingFeature("InternalImportsByDefault", mode: .preview)
249+
```
250+
251+
### Diagnostics
252+
253+
* Diagnostics emitted by preview mode must be grouped
254+
* It will generally be hard for developers to work out diagnostic affiliation,
255+
especially when multiple features are enabled in preview mode. We may want to
256+
vend the diagnostic group (named after the feature) to IDEs and annotate
257+
diagnostics accordingly (`-print-diagnostics-groups`) in our formatters.
258+
* Establish conventions around severity.
259+
260+
## Source compatibility
261+
262+
This proposal has no impact on existing code.
263+
264+
## ABI compatibility
265+
266+
This proposal has no impact on existing code.
267+
268+
## Implications on adoption
269+
270+
TBD
271+
272+
## Future directions
273+
274+
TBD. Talk about `swift migrate`.
275+
276+
## Alternatives considered
277+
278+
?
279+
280+
## Acknowledgements
281+
282+
This proposal is inspired by documents devised by [Allan Shortlidge][Allan]
283+
and [Holly Borla][Holly] (@bhorla).
284+
285+
[Holly]: https://github.com/hborla
286+
[Allan]: https://github.com/tshortli
287+

0 commit comments

Comments
 (0)