Skip to content

Commit b3c70ef

Browse files
committed
[TypeChecker] NFC: Add test-cases verify that sendability of key path exprs is inferred correctly
1 parent cfa9905 commit b3c70ef

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature InferSendableFromCaptures -strict-concurrency=complete
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: asserts
5+
6+
class NonSendable : Hashable {
7+
var data: Int
8+
9+
init(data: Int = 42) {
10+
self.data = data
11+
}
12+
13+
init(data: [Int]) {
14+
self.data = data.first!
15+
}
16+
17+
static func == (x: NonSendable, y: NonSendable) -> Bool { false }
18+
func hash(into hasher: inout Hasher) {}
19+
}
20+
21+
final class CondSendable<T> : Hashable {
22+
init(_: T) {}
23+
init(_: Int) {}
24+
init(_: T, other: T = 42) {}
25+
init<Q>(_: [Q] = []) {}
26+
27+
static func == (x: CondSendable, y: CondSendable) -> Bool { false }
28+
func hash(into hasher: inout Hasher) {}
29+
}
30+
31+
extension CondSendable : Sendable where T: Sendable {
32+
}
33+
34+
// Test forming sendable key paths without context
35+
do {
36+
class K {
37+
var data: String = ""
38+
39+
subscript<T>(_: T) -> Bool {
40+
get { false }
41+
}
42+
43+
subscript<Q>(_: Int, _: Q) -> Int {
44+
get { 42 }
45+
set {}
46+
}
47+
}
48+
49+
let kp = \K.data // Marked as `& Sendable`
50+
51+
let _: KeyPath<K, String> = kp // Ok
52+
let _: KeyPath<K, String> & Sendable = kp // Ok
53+
54+
func test<V>(_: KeyPath<K, V> & Sendable) {
55+
}
56+
57+
test(kp) // Ok
58+
59+
let nonSendableKP = \K.[NonSendable()]
60+
61+
let _: KeyPath<K, Bool> = \.[NonSendable()] // ok
62+
let _: KeyPath<K, Bool> & Sendable = \.[NonSendable()] // expected-warning {{type 'KeyPath<K, Bool>' does not conform to the 'Sendable' protocol}}
63+
let _: KeyPath<K, Int> & Sendable = \.[42, NonSendable(data: [-1, 0, 1])] // expected-warning {{type 'ReferenceWritableKeyPath<K, Int>' does not conform to the 'Sendable' protocol}}
64+
let _: KeyPath<K, Int> & Sendable = \.[42, -1] // Ok
65+
66+
test(nonSendableKP) // expected-warning {{type 'KeyPath<K, Bool>' does not conform to the 'Sendable' protocol}}
67+
}
68+
69+
// Test using sendable and non-sendable key paths.
70+
do {
71+
class V {
72+
var i: Int = 0
73+
74+
subscript<T>(_: T) -> Int {
75+
get { 42 }
76+
}
77+
78+
subscript<Q>(_: Int, _: Q) -> Int {
79+
get { 42 }
80+
set {}
81+
}
82+
}
83+
84+
func testSendableKP<T, U>(v: T, _ kp: any KeyPath<T, U> & Sendable) {}
85+
func testSendableFn<T, U>(v: T, _: @Sendable (T) -> U) {}
86+
87+
func testNonSendableKP<T, U>(v: T, _ kp: KeyPath<T, U>) {}
88+
func testNonSendableFn<T, U>(v: T, _ kp: (T) -> U) {}
89+
90+
let v = V()
91+
92+
testSendableKP(v: v, \.i) // Ok
93+
testSendableFn(v: v, \.i) // Ok
94+
95+
testSendableKP(v: v, \.[42]) // Ok
96+
testSendableFn(v: v, \.[42]) // Ok
97+
98+
testSendableKP(v: v, \.[NonSendable()]) // expected-warning {{type 'KeyPath<V, Int>' does not conform to the 'Sendable' protocol}}
99+
// Note that there is no warning here because the key path is wrapped in a closure and not captured by it: `{ $0[keyPath: \.[NonSendable()]] }`
100+
testSendableFn(v: v, \.[NonSendable()]) // Ok
101+
102+
testNonSendableKP(v: v, \.[NonSendable()]) // Ok
103+
testNonSendableFn(v: v, \.[NonSendable()]) // Ok
104+
105+
let _: @Sendable (V) -> Int = \.[NonSendable()] // Ok
106+
107+
let _: KeyPath<V, Int> & Sendable = \.[42, CondSendable(NonSendable(data: [1, 2, 3]))]
108+
// expected-warning@-1 {{type 'ReferenceWritableKeyPath<V, Int>' does not conform to the 'Sendable' protocol}}
109+
let _: KeyPath<V, Int> & Sendable = \.[42, CondSendable(42)] // Ok
110+
111+
struct Root {
112+
let v: V
113+
}
114+
115+
testSendableKP(v: v, \.[42, CondSendable(NonSendable(data: [1, 2, 3]))])
116+
// expected-warning@-1 {{type 'ReferenceWritableKeyPath<V, Int>' does not conform to the 'Sendable' protocol}}
117+
testSendableFn(v: v, \.[42, CondSendable(NonSendable(data: [1, 2, 3]))]) // Ok
118+
testSendableKP(v: v, \.[42, CondSendable(42)]) // Ok
119+
120+
let nonSendable = NonSendable()
121+
testSendableKP(v: v, \.[42, CondSendable(nonSendable)])
122+
// expected-warning@-1 {{type 'ReferenceWritableKeyPath<V, Int>' does not conform to the 'Sendable' protocol}}
123+
124+
// TODO: This should be diagnosed by the isolation checker because implicitly synthesized closures captures a non-Sendable value.
125+
testSendableFn(v: v, \.[42, CondSendable(nonSendable)])
126+
}

0 commit comments

Comments
 (0)