Skip to content

Commit f09b975

Browse files
hborladempseyatgithubshahmishal
authored
Swift 5.10 release blog post (#542)
* Add a draft of the 5.10 release blog post. * Grammar correction: opt out -> opt-out Co-authored-by: James Dempsey <[email protected]> * Improve the structure of the introduction. * Explain why unsafe opt-outs are important. * Add a link to instructions for enabling complete concurrency checking. * Improve the Next Steps section. * Separate out the content on language evolution ahead of Swift 6 from the next steps. * Provide more context about data races in the introduction. * Improve the instructions for enabling strict concurrency in SwiftPM. * Add a few more explanations to the code examples in the data race safety section. * Clarify that the Swift 6 language mode is opt-in in the intro. * Correct the name of the Xcode build setting for strict concurrency. * Editorial pass * Update the `nonisolated(unsafe)` code example to avoid a compiler bug. * Update the date for the blog post --------- Co-authored-by: James Dempsey <[email protected]> Co-authored-by: Mishal Shah <[email protected]>
1 parent 7f4ccfb commit f09b975

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
layout: post
3+
published: true
4+
date: 2024-03-05 14:00:00
5+
title: Swift 5.10 Released
6+
author: [hborla]
7+
---
8+
9+
Swift was designed to be safe by default, preventing entire categories of programming mistakes at compile time. Sources of undefined behavior in C-based languages, such as using variables before they're initialized or a use-after-free, are defined away in Swift.
10+
11+
An increasingly important source of undefined behavior is concurrent code that inadvertently accesses memory from one thread at the same time that another thread is writing to the same memory. This kind of unsafety is called a _data race_, and data races make concurrent programs exceptionally difficult to write correctly. Swift solves this problem through _data isolation_ provided by actors and tasks, which guarantees mutually exclusive access to shared mutable state. Data isolation enforcement has been under active development since 2020 when the [Swift concurrency roadmap](https://forums.swift.org/t/swift-concurrency-roadmap/41611) was posted.
12+
13+
**Swift 5.10 accomplishes full data isolation in the concurrency language model.** This important milestone has taken years of active development over many releases. The concurrency model was introduced in Swift 5.5 including `async`/`await`, actors, and structured concurrency. Swift 5.7 introduced `Sendable` as the fundamental concept for thread-safe types whose values can be shared across arbitrary concurrent contexts without introducing a risk of data races. And now, in Swift 5.10, full data isolation is enforced at compile time in all areas of the language when the complete concurrency checking option is enabled.
14+
15+
Full data isolation in Swift 5.10 sets the stage for the next major release, Swift 6. The Swift 6.0 compiler will offer a new, opt-in Swift 6 language mode that will enforce full data isolation by default, and we will embark upon the transition to eliminate data races across all software written in Swift.
16+
17+
Swift 5.10 will produce data-race warnings in some circumstances where the code could be proven safe with additional compiler analysis. A major focus of language development for the Swift 6 release is improving the usability of strict concurrency checking by mitigating false positive concurrency errors in common code patterns that are proven to be safe.
18+
19+
Read on to learn about full data isolation in Swift 5.10, new unsafe opt-outs for actor isolation checking, and the remaining concurrency evolution ahead of Swift 6.
20+
21+
## Data-race safety in Swift 5.10
22+
23+
### Full data isolation
24+
25+
Swift 5.10 rounds out the data-race safety semantics in all corners of the language, and fixes numerous bugs in `Sendable` and actor isolation checking to strengthen the guarantees of complete concurrency checking. When building code with the compiler flag `-strict-concurrency=complete`, Swift 5.10 will diagnose the potential for data races at compile time except where an explicit unsafe opt-out, such as `nonisolated(unsafe)` or `@unchecked Sendable`, is used.
26+
27+
For example, in Swift 5.9, the following code fails an isolation assertion at runtime due to a `@MainActor`-isolated initializer being evaluated outside the actor, but it was not diagnosed under `-strict-concurrency=complete`:
28+
29+
```swift
30+
@MainActor
31+
class MyModel {
32+
private init() {
33+
MainActor.assertIsolated()
34+
}
35+
36+
static let shared = MyModel()
37+
}
38+
39+
func useShared() async {
40+
let model = MyModel.shared
41+
}
42+
43+
await useShared()
44+
```
45+
46+
The above code admits data races. `MyModel.shared` is a `@MainActor`-isolated static variable, which evaluates a `@MainActor`-isolated initial value upon first access. `MyModel.shared` is accessed synchronously from a `nonisolated` context inside the `useShared()` function, so the initial value is computed off the main actor. In Swift 5.10, compiling the code with `-strict-concurrency=complete` produces a warning that the access must be done asynchronously:
47+
48+
```
49+
warning: expression is 'async' but is not marked with 'await'
50+
let model = MyModel.shared
51+
^~~~~~~~~~~~~~
52+
await
53+
```
54+
55+
The possible fixes for resolving the data race are 1) access `MyModel.shared` asynchronously using `await`, 2) make `MyModel.init` and `MyModel.shared` both `nonisolated` and move the code that requires the main actor into a separate isolated method, or 3) isolate `useShared()` to `@MainActor`.
56+
57+
You can find more details about the changes and additions to the full data isolation programming model in the [Swift 5.10 release notes](https://github.com/apple/swift/blob/release/5.10/CHANGELOG.md).
58+
59+
### Unsafe opt-outs
60+
61+
Unsafe opt-outs, such as `@unchecked Sendable` conformances, are important for communicating that code is safe from data-races when it cannot be proven automatically by the compiler. These tools are necessary in cases where synchronization is implemented in a way that the compiler cannot reason about, such as through OS-specific primitives or when working with thread-safe types implemented in C/C++/Objective-C. However, `@unchecked Sendable` conformances are difficult to use correctly, because they opt the entire type out of data-race safety checking. In many cases, only one specific property in a type needs the opt-out, while the rest of the implementation adheres to static concurrency safety.
62+
63+
Swift 5.10 introduces a new `nonisolated(unsafe)` keyword to opt out of actor isolation checking for stored properties and variables. `nonisolated(unsafe)` can be used on any form of storage, including stored properties, local variables, and global/static variables.
64+
65+
For example, global and static variables can be accessed from anywhere in your code, so they are required to either be immutable and `Sendable`, or isolated to a global actor:
66+
67+
```swift
68+
import Dispatch
69+
70+
struct MyData {
71+
static let cacheQueue = DispatchQueue(...)
72+
// All access to 'globalCache' is guarded by 'cacheQueue'
73+
static var globalCache: [MyData] = []
74+
}
75+
```
76+
77+
When building the above code with `-strict-concurrency=complete`, the compiler emits a warning:
78+
79+
```
80+
warning: static property 'globalCache' is not concurrency-safe because it is non-isolated global shared mutable state
81+
static var globalCache: [MyData] = []
82+
^
83+
note: isolate 'globalCache' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'
84+
```
85+
86+
All uses of `globalCache` are guarded by `cacheQueue.async { ... }`, so this code is free of data races in practice. In this case, `nonisolated(unsafe)` can be applied to the static variable to silence the concurrency warning:
87+
88+
```swift
89+
import Dispatch
90+
91+
struct MyData {
92+
static let cacheQueue = DispatchQueue(...)
93+
// All access to 'globalCache' is guarded by 'cacheQueue'
94+
nonisolated(unsafe) static var globalCache: [MyData] = []
95+
}
96+
```
97+
98+
`nonisolated(unsafe)` also eliminates the need for `@unchecked Sendable` wrapper types that are used only to pass specific instances of non-`Sendable` values across isolation boundaries when there is no potential for concurrent access:
99+
100+
```swift
101+
// 'MutableData' is not 'Sendable'
102+
class MutableData { ... }
103+
104+
func processData(_: MutableData) async { ... }
105+
106+
@MainActor func send() async {
107+
nonisolated(unsafe) let data = MutableData()
108+
await processData(data)
109+
}
110+
```
111+
112+
Note that without correct implementation of a synchronization mechanism to achieve data isolation, dynamic analysis from exclusivity enforcement or tools such as the Thread Sanitizer may still identify failures.
113+
114+
## Language evolution ahead of Swift 6
115+
116+
**The next release of Swift will be Swift 6.** The complete concurrency model in Swift 5.10 is overly restrictive, and several Swift Evolution proposals are in active development to improve the usability of full data isolation by removing false postive data-race errors. This work includes [lifting limitations on passing non-`Sendable` values across isolation boundaries](https://github.com/apple/swift-evolution/blob/main/proposals/0414-region-based-isolation.md) when the compiler determines there is no potential for concurrent access, [more effective `Sendable` inference for functions and key-paths](https://github.com/apple/swift-evolution/blob/main/proposals/0418-inferring-sendable-for-methods.md), and more. You can find the set of proposals that will round out Swift 6 at [Swift.org/swift-evolution](https://www.swift.org/swift-evolution/#?version=6.0).
117+
118+
## Next Steps
119+
120+
### Try out complete concurrency checking
121+
122+
You can help shape the transition to the Swift 6 language mode by [trying out complete concurrency checking](/documentation/concurrency/) in your project and providing feedback on your experience.
123+
124+
If you find any remaining compiler bugs where complete concurrency checking does not diagnose a data race at compile time, please [report an issue](https://github.com/apple/swift/issues/new/choose).
125+
126+
You can also provide feedback that helps improve the concurrency documentation, compiler error messages, and the upcoming Swift 6 migration guide. If you encounter a case where the compiler diagnoses a data-race warning that you don't understand or you're not sure how to resolve a given data-race warning, please start a [discussion thread on the Swift forums](https://forums.swift.org/tags/c/swift-users/15/concurrency) using the `concurrency` tag.
127+
128+
### Downloads
129+
130+
Official binaries for Swift 5.10 are [available for download](https://swift.org/download/) from [Swift.org](http://swift.org/) for macOS, Windows, and Linux.
131+
132+
## Swift Evolution Appendix
133+
134+
The following language proposals were accepted through the [Swift Evolution](https://github.com/apple/swift-evolution) process and [implemented in Swift 5.10](https://www.swift.org/swift-evolution/#?version=5.10):
135+
136+
* SE-0327: [On Actors and Initialization](https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md)
137+
* SE-0383: [Deprecate @UIApplicationMain and @NSApplicationMain](https://github.com/apple/swift-evolution/blob/main/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md)
138+
* SE-0404: [Allow Protocols to be Nested in Non-Generic Contexts](https://github.com/apple/swift-evolution/blob/main/proposals/0404-nested-protocols.md)
139+
* SE-0411: [Isolated default value expressions](https://github.com/apple/swift-evolution/blob/main/proposals/0411-isolated-default-values.md)
140+
* SE-0412: [Strict concurrency for global variables](https://github.com/apple/swift-evolution/blob/main/proposals/0412-strict-concurrency-for-global-variables.md)

documentation/concurrency/index.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
layout: page
3+
date: 2024-03-03 12:00:00
4+
title: Enabling Complete Concurrency Checking
5+
---
6+
7+
Data-race safety in the Swift 6 language mode is designed for incremental migration. You can address data-race safety issues in your projects module-by-module, and you can enable the compiler's actor isolation and `Sendable` checking as warnings in the Swift 5 language mode, allowing you to assess your progress toward eliminating data races before turning on the Swift 6 language mode.
8+
9+
Complete data-race safety checking can be enabled as warnings in the Swift 5 language mode using the `-strict-concurrency` compiler flag.
10+
11+
## Using the Swift compiler
12+
13+
To enable complete concurrency checking when running `swift` or `swiftc` directly at the command line, pass `-strict-concurrency=complete`:
14+
15+
```
16+
~ swift -strict-concurrency=complete main.swift
17+
```
18+
19+
## Using SwiftPM
20+
21+
### In a SwiftPM command-line invocation
22+
23+
`-strict-concurrency=complete` can be passed in a Swift package manager command-line invocation using the `-Xswiftc` flag:
24+
25+
```
26+
~ swift build -Xswiftc -strict-concurrency=complete
27+
~ swift test -Xswiftc -strict-concurrency=complete
28+
```
29+
30+
This can be useful to gauge the amount of concurrency warnings before adding the flag permanently in the package manifest as described in the following section.
31+
32+
### In a SwiftPM package manifest
33+
34+
To enable complete concurrency checking for a target in a Swift package using Swift 5.9 or Swift 5.10 tools, use [`SwiftSetting.enableExperimentalFeature`](https://developer.apple.com/documentation/packagedescription/swiftsetting/enableexperimentalfeature(_:_:)) in the Swift settings for the given target:
35+
36+
```swift
37+
.target(
38+
name: "MyTarget",
39+
swiftSettings: [
40+
.enableExperimentalFeature("StrictConcurrency")
41+
]
42+
)
43+
```
44+
45+
When using Swift 6.0 tools or later, use [`SwiftSetting.enableUpcomingFeature`](https://developer.apple.com/documentation/packagedescription/swiftsetting/enableupcomingfeature(_:_:)) in the Swift settings for the given target:
46+
47+
```swift
48+
.target(
49+
name: "MyTarget",
50+
swiftSettings: [
51+
.enableUpcomingFeature("StrictConcurrency")
52+
]
53+
)
54+
```
55+
56+
## Using Xcode
57+
58+
To enable complete concurrency checking in an Xcode project, set the "Strict Concurrency Checking" setting to "Complete" in the Xcode build settings. Alternatively, you can set `SWIFT_STRICT_CONCURRENCY` to `complete` in an xcconfig file:
59+
60+
```
61+
// In a Settings.xcconfig
62+
63+
SWIFT_STRICT_CONCURRENCY = complete;
64+
```

0 commit comments

Comments
 (0)