Skip to content

Commit caaafe5

Browse files
fix #91; retroactive conformances to Sendable (#99)
* fix #91; retroactive conformances to Sendable * Typos
1 parent d75ad5b commit caaafe5

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

Guide.docc/CommonProblems.md

+25
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,31 @@ it `Sendable` should not be your first approach.
656656
It is often easier to try other techniques first, falling back to
657657
manual synchronization only when truly necessary.
658658

659+
#### Retroactive Sendable Conformance
660+
661+
Your dependencies may also expose types that are using manual synchronization.
662+
This is usually visible only via documentation.
663+
It is possible to add an `@unchecked Sendable` conformance in this case as well.
664+
665+
```swift
666+
extension ColorComponents: @retroactive @unchecked Sendable {
667+
}
668+
```
669+
670+
Because `Sendable` is a marker protocol, a retroactive conformance
671+
does not have direct binary compatibility issues.
672+
However, it should still be used with extreme caution.
673+
Types that use manual synchronization can come with conditions or
674+
exceptions to their safety that may not completely match the semantics of
675+
`Sendable`.
676+
Further, you should be _particularly_ careful about using this technique
677+
for types that are part of your system's public API.
678+
679+
> Note: To learn more about retroactive conformances,
680+
see the associated [Swift evolution proposal][SE-0364].
681+
682+
[SE-0364]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0364-retroactive-conformance-warning.md
683+
659684
#### Sendable Reference Types
660685

661686
It is possible for reference types to be validated as `Sendable` without

Sources/Examples/Boundaries.swift

+35-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ func applyBackground(_ color: ColorComponents) {
88
}
99

1010
#if swift(<6.0)
11-
/// A non-isolated function that accepts non-`Sendable` parameters.
11+
/// A non-isolated function that accepts non-`Sendable` parameters.
1212
func updateStyle(backgroundColor: ColorComponents) async {
1313
// the `backgroundColor` parameter is being moved from the
1414
// non-isolated domain to the `MainActor` here.
@@ -19,6 +19,13 @@ func updateStyle(backgroundColor: ColorComponents) async {
1919
}
2020
#endif
2121

22+
#if swift(>=6.0)
23+
/// A non-isolated function that accepts non-`Sendable` parameters which must be safe to use at callsites.
24+
func sending_updateStyle(backgroundColor: sending ColorComponents) async {
25+
await applyBackground(backgroundColor)
26+
}
27+
#endif
28+
2229
// MARK: Latent Isolation
2330

2431
/// MainActor-isolated function that accepts non-`Sendable` parameters.
@@ -96,13 +103,35 @@ actor Style {
96103
}
97104
}
98105

106+
// MARK: Manual Synchronization
107+
108+
extension RetroactiveColorComponents: @retroactive @unchecked Sendable {
109+
}
110+
111+
/// An overload used by `retroactive_updateStyle` to match types.
112+
@MainActor
113+
func applyBackground(_ color: RetroactiveColorComponents ) {
114+
}
115+
116+
/// A non-isolated function that accepts retroactively-`Sendable` parameters.
117+
func retroactive_updateStyle(backgroundColor: RetroactiveColorComponents) async {
118+
await applyBackground(backgroundColor)
119+
}
120+
99121
func exerciseBoundaryCrossingExamples() async {
100122
print("Isolation Boundary Crossing Examples")
101123

102124
#if swift(<6.0)
103125
print(" - updateStyle(backgroundColor:) passing its argument unsafely")
104126
#endif
105127

128+
#if swift(>=6.0)
129+
print(" - using sending to allow safe usage of ColorComponents")
130+
let nonSendableComponents = ColorComponents()
131+
132+
await sending_updateStyle(backgroundColor: nonSendableComponents)
133+
#endif
134+
106135
print(" - using ColorComponents only from the main actor")
107136
let t1 = Task { @MainActor in
108137
let components = ColorComponents()
@@ -137,4 +166,9 @@ func exerciseBoundaryCrossingExamples() async {
137166
let actor = Style(background: actorComponents)
138167

139168
await actor.applyBackground()
169+
170+
print(" - using a retroactive unchecked Sendable argument")
171+
let retroactiveComponents = RetroactiveColorComponents()
172+
173+
await retroactive_updateStyle(backgroundColor: retroactiveComponents)
140174
}

Sources/Library/Library.swift

+9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ public struct ColorComponents {
2121
}
2222
}
2323

24+
/// A variant of `ColorComponents` that could be marked as Sendable
25+
public struct RetroactiveColorComponents {
26+
public let red: Float = 1.0
27+
public let green: Float = 1.0
28+
public let blue: Float = 1.0
29+
30+
public init() {}
31+
}
32+
2433
/// Explicitly-Sendable variant of `ColorComponents`.
2534
public struct SendableColorComponents : Sendable {
2635
public let red: Float = 1.0

0 commit comments

Comments
 (0)