Skip to content

Commit 2d0590b

Browse files
Clean up key path bitcasting. (#3411)
* Clean up key path bitcasting. * wip * wip --------- Co-authored-by: Stephen Celis <[email protected]>
1 parent 4699a1e commit 2d0590b

File tree

9 files changed

+51
-46
lines changed

9 files changed

+51
-46
lines changed
Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
#if compiler(>=6)
2-
public typealias _AnyKeyPath = any AnyKeyPath & Sendable
3-
public typealias _PartialKeyPath<Root> = any PartialKeyPath<Root> & Sendable
4-
public typealias _KeyPath<Root, Value> = any KeyPath<Root, Value> & Sendable
5-
public typealias _WritableKeyPath<Root, Value> = any WritableKeyPath<Root, Value> & Sendable
6-
public typealias _ReferenceWritableKeyPath<Root, Value> = any ReferenceWritableKeyPath<
2+
public typealias _SendableAnyKeyPath = any AnyKeyPath & Sendable
3+
public typealias _SendablePartialKeyPath<Root> = any PartialKeyPath<Root> & Sendable
4+
public typealias _SendableKeyPath<Root, Value> = any KeyPath<Root, Value> & Sendable
5+
public typealias _SendableWritableKeyPath<Root, Value> = any WritableKeyPath<Root, Value> & Sendable
6+
public typealias _SendableReferenceWritableKeyPath<Root, Value> = any ReferenceWritableKeyPath<
77
Root, Value
88
>
99
& Sendable
10-
public typealias _PartialCaseKeyPath<Root> = any PartialCaseKeyPath<Root> & Sendable
11-
public typealias _CaseKeyPath<Root, Value> = any CaseKeyPath<Root, Value> & Sendable
10+
public typealias _SendablePartialCaseKeyPath<Root> = any PartialCaseKeyPath<Root> & Sendable
11+
public typealias _SendableCaseKeyPath<Root, Value> = any CaseKeyPath<Root, Value> & Sendable
1212
#else
13-
public typealias _AnyKeyPath = AnyKeyPath
14-
public typealias _PartialKeyPath<Root> = PartialKeyPath<Root>
15-
public typealias _KeyPath<Root, Value> = KeyPath<Root, Value>
16-
public typealias _WritableKeyPath<Root, Value> = WritableKeyPath<Root, Value>
17-
public typealias _ReferenceWritableKeyPath<Root, Value> = ReferenceWritableKeyPath<Root, Value>
18-
public typealias _PartialCaseKeyPath<Root> = PartialCaseKeyPath<Root>
19-
public typealias _CaseKeyPath<Root, Value> = CaseKeyPath<Root, Value>
13+
public typealias _SendableAnyKeyPath = AnyKeyPath
14+
public typealias _SendablePartialKeyPath<Root> = PartialKeyPath<Root>
15+
public typealias _SendableKeyPath<Root, Value> = KeyPath<Root, Value>
16+
public typealias _SendableWritableKeyPath<Root, Value> = WritableKeyPath<Root, Value>
17+
public typealias _SendableReferenceWritableKeyPath<Root, Value> = ReferenceWritableKeyPath<Root, Value>
18+
public typealias _SendablePartialCaseKeyPath<Root> = PartialCaseKeyPath<Root>
19+
public typealias _SendableCaseKeyPath<Root, Value> = CaseKeyPath<Root, Value>
2020
#endif
21+
22+
@_transparent
23+
func sendableKeyPath(
24+
_ keyPath: AnyKeyPath
25+
) -> _SendableAnyKeyPath {
26+
unsafeBitCast(keyPath, to: _SendableAnyKeyPath.self)
27+
}

Sources/ComposableArchitecture/Observation/Binding+Observation.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ extension UIBindable {
5858

5959
extension BindingAction {
6060
public static func set<Value: Equatable & Sendable>(
61-
_ keyPath: _WritableKeyPath<Root, Value>,
61+
_ keyPath: _SendableWritableKeyPath<Root, Value>,
6262
_ value: Value
6363
) -> Self where Root: ObservableState {
6464
.init(
@@ -116,7 +116,7 @@ extension BindingAction {
116116

117117
extension BindableAction where State: ObservableState {
118118
fileprivate static func set<Value: Equatable & Sendable>(
119-
_ keyPath: _WritableKeyPath<State, Value>,
119+
_ keyPath: _SendableWritableKeyPath<State, Value>,
120120
_ value: Value,
121121
isInvalidated: (@MainActor @Sendable () -> Bool)?
122122
) -> Self {
@@ -150,7 +150,7 @@ extension BindableAction where State: ObservableState {
150150
}
151151

152152
public static func set<Value: Equatable & Sendable>(
153-
_ keyPath: _WritableKeyPath<State, Value>,
153+
_ keyPath: _SendableWritableKeyPath<State, Value>,
154154
_ value: Value
155155
) -> Self {
156156
self.set(keyPath, value, isInvalidated: nil)
@@ -159,7 +159,7 @@ extension BindableAction where State: ObservableState {
159159

160160
extension Store where State: ObservableState, Action: BindableAction, Action.State == State {
161161
public subscript<Value: Equatable & Sendable>(
162-
dynamicMember keyPath: _WritableKeyPath<State, Value>
162+
dynamicMember keyPath: _SendableWritableKeyPath<State, Value>
163163
) -> Value {
164164
get { self.state[keyPath: keyPath] }
165165
set {
@@ -195,7 +195,7 @@ where
195195
Action.ViewAction.State == State
196196
{
197197
public subscript<Value: Equatable & Sendable>(
198-
dynamicMember keyPath: _WritableKeyPath<State, Value>
198+
dynamicMember keyPath: _SendableWritableKeyPath<State, Value>
199199
) -> Value {
200200
get { self.state[keyPath: keyPath] }
201201
set {

Sources/ComposableArchitecture/Reducer/Reducers/PresentationReducer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ extension PresentationAction: CasePathable {
293293
}
294294

295295
public subscript<AppendedAction>(
296-
dynamicMember keyPath: _CaseKeyPath<Action, AppendedAction>
296+
dynamicMember keyPath: _SendableCaseKeyPath<Action, AppendedAction>
297297
) -> AnyCasePath<PresentationAction, AppendedAction>
298298
where Action: CasePathable {
299299
AnyCasePath<PresentationAction, AppendedAction>(
@@ -307,7 +307,7 @@ extension PresentationAction: CasePathable {
307307

308308
@_disfavoredOverload
309309
public subscript<AppendedAction>(
310-
dynamicMember keyPath: _CaseKeyPath<Action, AppendedAction>
310+
dynamicMember keyPath: _SendableCaseKeyPath<Action, AppendedAction>
311311
) -> AnyCasePath<PresentationAction, PresentationAction<AppendedAction>>
312312
where Action: CasePathable {
313313
AnyCasePath<PresentationAction, PresentationAction<AppendedAction>>(

Sources/ComposableArchitecture/SharedState/PersistenceKey/AppStorageKeyPathKey.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ extension PersistenceReaderKey {
1515
/// - Parameter key: A string key identifying a value to share in memory.
1616
/// - Returns: A persistence key.
1717
public static func appStorage<Value>(
18-
_ keyPath: _ReferenceWritableKeyPath<UserDefaults, Value>
18+
_ keyPath: _SendableReferenceWritableKeyPath<UserDefaults, Value>
1919
) -> Self where Self == AppStorageKeyPathKey<Value> {
2020
AppStorageKeyPathKey(keyPath)
2121
}
@@ -25,10 +25,10 @@ extension PersistenceReaderKey {
2525
///
2626
/// See ``PersistenceReaderKey/appStorage(_:)-5jsie`` to create values of this type.
2727
public struct AppStorageKeyPathKey<Value: Sendable>: Sendable {
28-
private let keyPath: _ReferenceWritableKeyPath<UserDefaults, Value>
28+
private let keyPath: _SendableReferenceWritableKeyPath<UserDefaults, Value>
2929
private let store: UncheckedSendable<UserDefaults>
3030

31-
public init(_ keyPath: _ReferenceWritableKeyPath<UserDefaults, Value>) {
31+
public init(_ keyPath: _SendableReferenceWritableKeyPath<UserDefaults, Value>) {
3232
@Dependency(\.defaultAppStorage) var store
3333
self.keyPath = keyPath
3434
self.store = UncheckedSendable(store)

Sources/ComposableArchitecture/SharedState/Shared.swift

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import IssueReporting
1414
@propertyWrapper
1515
public struct Shared<Value: Sendable>: Sendable {
1616
private let reference: any Reference
17-
private let keyPath: _AnyKeyPath
17+
private let keyPath: _SendableAnyKeyPath
1818

19-
init(reference: any Reference, keyPath: _AnyKeyPath) {
19+
init(reference: any Reference, keyPath: _SendableAnyKeyPath) {
2020
self.reference = reference
2121
self.keyPath = keyPath
2222
}
@@ -64,10 +64,9 @@ public struct Shared<Value: Sendable>: Sendable {
6464
reference: base.reference,
6565
// NB: Can get rid of bitcast when this is fixed:
6666
// https://github.com/swiftlang/swift/issues/75531
67-
keyPath: unsafeBitCast(
67+
keyPath: sendableKeyPath(
6868
(base.keyPath as AnyKeyPath)
69-
.appending(path: \Value?.[default: DefaultSubscript(initialValue)])!,
70-
to: _AnyKeyPath.self
69+
.appending(path: \Value?.[default: DefaultSubscript(initialValue)])!
7170
)
7271
)
7372
}
@@ -180,9 +179,8 @@ public struct Shared<Value: Sendable>: Sendable {
180179
reference: self.reference,
181180
// NB: Can get rid of bitcast when this is fixed:
182181
// https://github.com/swiftlang/swift/issues/75531
183-
keyPath: unsafeBitCast(
184-
(self.keyPath as AnyKeyPath).appending(path: keyPath)!,
185-
to: _AnyKeyPath.self
182+
keyPath: sendableKeyPath(
183+
(self.keyPath as AnyKeyPath).appending(path: keyPath)!
186184
)
187185
)
188186
}
@@ -463,9 +461,8 @@ extension Shared {
463461
reference: self.reference,
464462
// NB: Can get rid of bitcast when this is fixed:
465463
// https://github.com/swiftlang/swift/issues/75531
466-
keyPath: unsafeBitCast(
467-
(self.keyPath as AnyKeyPath).appending(path: keyPath)!,
468-
to: _AnyKeyPath.self
464+
keyPath: sendableKeyPath(
465+
(self.keyPath as AnyKeyPath).appending(path: keyPath)!
469466
)
470467
)
471468
}

Sources/ComposableArchitecture/SwiftUI/Binding.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,15 @@ extension BindingState: Sendable where Value: Sendable {}
144144
///
145145
/// Read <doc:Bindings> for more information.
146146
public struct BindingAction<Root>: CasePathable, Equatable, Sendable {
147-
public let keyPath: _PartialKeyPath<Root>
147+
public let keyPath: _SendablePartialKeyPath<Root>
148148

149149
@usableFromInline
150150
let set: @Sendable (inout Root) -> Void
151151
let value: any Sendable
152152
let valueIsEqualTo: @Sendable (Any) -> Bool
153153

154154
init(
155-
keyPath: _PartialKeyPath<Root>,
155+
keyPath: _SendablePartialKeyPath<Root>,
156156
set: @escaping @Sendable (inout Root) -> Void,
157157
value: any Sendable,
158158
valueIsEqualTo: @escaping @Sendable (Any) -> Bool
@@ -174,7 +174,7 @@ public struct BindingAction<Root>: CasePathable, Equatable, Sendable {
174174
@dynamicMemberLookup
175175
public struct AllCasePaths {
176176
public subscript<Value: Equatable & Sendable>(
177-
dynamicMember keyPath: _WritableKeyPath<Root, Value>
177+
dynamicMember keyPath: _SendableWritableKeyPath<Root, Value>
178178
) -> AnyCasePath<BindingAction, Value> where Root: ObservableState {
179179
AnyCasePath(
180180
embed: { .set(keyPath, $0) },
@@ -183,7 +183,7 @@ public struct BindingAction<Root>: CasePathable, Equatable, Sendable {
183183
}
184184

185185
public subscript<Value: Equatable & Sendable>(
186-
dynamicMember keyPath: _WritableKeyPath<Root, BindingState<Value>>
186+
dynamicMember keyPath: _SendableWritableKeyPath<Root, BindingState<Value>>
187187
) -> AnyCasePath<BindingAction, Value> {
188188
AnyCasePath(
189189
embed: { .set(keyPath, $0) },
@@ -204,7 +204,7 @@ extension BindingAction {
204204
/// - Returns: An action that describes simple mutations to some root state at a writable key
205205
/// path.
206206
public static func set<Value: Equatable & Sendable>(
207-
_ keyPath: _WritableKeyPath<Root, BindingState<Value>>,
207+
_ keyPath: _SendableWritableKeyPath<Root, BindingState<Value>>,
208208
_ value: Value
209209
) -> Self {
210210
return .init(
@@ -234,7 +234,7 @@ extension BindingAction {
234234
}
235235

236236
init<Value: Equatable & Sendable>(
237-
keyPath: _WritableKeyPath<Root, BindingState<Value>>,
237+
keyPath: _SendableWritableKeyPath<Root, BindingState<Value>>,
238238
set: @escaping @Sendable (_ state: inout Root) -> Void,
239239
value: Value
240240
) {
@@ -290,7 +290,7 @@ extension BindableAction {
290290
///
291291
/// - Returns: A binding action.
292292
public static func set<Value: Equatable & Sendable>(
293-
_ keyPath: _WritableKeyPath<State, BindingState<Value>>,
293+
_ keyPath: _SendableWritableKeyPath<State, BindingState<Value>>,
294294
_ value: Value
295295
) -> Self {
296296
self.binding(.set(keyPath, value))
@@ -299,7 +299,7 @@ extension BindableAction {
299299

300300
extension ViewStore where ViewAction: BindableAction, ViewAction.State == ViewState {
301301
public subscript<Value: Equatable & Sendable>(
302-
dynamicMember keyPath: _WritableKeyPath<ViewState, BindingState<Value>>
302+
dynamicMember keyPath: _SendableWritableKeyPath<ViewState, BindingState<Value>>
303303
) -> Binding<Value> {
304304
self.binding(
305305
get: { $0[keyPath: keyPath].wrappedValue },
@@ -454,7 +454,7 @@ public struct BindingViewStore<State> {
454454
}
455455

456456
public subscript<Value: Equatable & Sendable>(
457-
dynamicMember keyPath: _WritableKeyPath<State, BindingState<Value>>
457+
dynamicMember keyPath: _SendableWritableKeyPath<State, BindingState<Value>>
458458
) -> BindingViewState<Value> {
459459
BindingViewState(
460460
binding: ViewStore(self.store, observe: { $0[keyPath: keyPath].wrappedValue })

Sources/ComposableArchitecture/TestStore.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1918,7 +1918,7 @@ extension TestStore where State: Equatable {
19181918
@_disfavoredOverload
19191919
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
19201920
public func receive<Value: Equatable & Sendable>(
1921-
_ actionCase: _CaseKeyPath<Action, Value>,
1921+
_ actionCase: _SendableCaseKeyPath<Action, Value>,
19221922
_ value: Value,
19231923
timeout duration: Duration,
19241924
assert updateStateToExpectedResult: ((_ state: inout State) throws -> Void)? = nil,

Tests/ComposableArchitectureTests/AppStorageTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ final class AppStorageTests: XCTestCase {
285285
await fulfillment(of: [perceptionExpectation, publisherExpectation], timeout: 1)
286286
}
287287

288-
func testUpdateStoreFromBackgroundThread_KeyPath() async throws {
288+
func testUpdateStoreFromBackgroundThread_SendableKeyPath() async throws {
289289
@Dependency(\.defaultAppStorage) var store
290290
@Shared(.appStorage(\.count)) var count = 0
291291

Tests/ComposableArchitectureTests/EffectTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ final class EffectTests: BaseTCATestCase {
5050
await withMainSerialExecutor {
5151
let values = LockIsolated<[Int]>([])
5252

53+
5354
let effect = Effect<Int>.concatenate(
5455
.publisher { Just(1).delay(for: 1, scheduler: self.mainQueue) }
5556
)

0 commit comments

Comments
 (0)