-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy patheffectful_properties.swift
346 lines (273 loc) · 13.8 KB
/
effectful_properties.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
// RUN: %target-typecheck-verify-swift -target %target-swift-5.1-abi-triple
/////
// This test is focused on checking protocol conformance and constraint checking
protocol None {
associatedtype V
// expected-note@+2 {{protocol requires property 'someProp' with type 'CA_N.V' (aka 'Int')}}
// expected-note@+1 2 {{protocol requires property 'someProp' with type 'Self.V'}}
var someProp : V { get }
}
protocol T {
associatedtype V
// expected-note@+2 {{protocol requires property 'someProp' with type 'CAT_T.V' (aka 'Int')}}
// expected-note@+1 {{protocol requires property 'someProp' with type 'Self.V'}}
var someProp : V { get throws }
}
protocol A {
associatedtype V
// expected-note@+1 2 {{protocol requires property 'someProp' with type 'Self.V'}}
var someProp : V { get async }
}
protocol AT {
associatedtype V
var someProp : V { get async throws }
}
/////
// exercise the space of conformances to a property with effects
// The naming scheme here is:
// CN_K
// where
// C = "conformer"
// N = candidate witness's effect abbreviation
// K = protocol requirement's effect abbreviation
// "effect abbreviation" = [
// N -> <none>, T -> throws, A -> async, AT -> async throws
// ]
// First group here also demonstrates that stored or mutable properties can
// witness a protocol requirement that allows for effects.
class CN_N : None { typealias V = Int; var someProp : Int { get {0} } }
class CN_T : T { typealias V = Int; var someProp : Int = 0 }
class CN_T_v2 : T { typealias V = Int; var someProp : Int { get {0} } }
class CN_A : A { typealias V = Int; var someProp : Int { get {0} set {} } }
class CN_AT : AT { typealias V = Int; var someProp : Int { _read {yield 0} } }
// ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
// Remaining conformances test the limit check for kinds of effects allowed
// expected-note@+3 {{add stubs for conformance}}
// expected-note@+2 3 {{candidate throws, but protocol does not allow it}}
// expected-error@+1 {{type 'CT_N' does not conform to protocol 'None'}}
class CT_N : None { typealias V = Int; var someProp : Int { get throws {0} } }
class CT_T : T { typealias V = Int; var someProp : Int { get throws {0} } }
// expected-note@+3 {{add stubs for conformance}}
// expected-note@+2 {{candidate throws, but protocol does not allow it}}
// expected-error@+1{{type 'CT_A' does not conform to protocol 'A'}}
class CT_A : A { typealias V = Int; var someProp : Int { get throws {0} } }
class CT_AT : AT { typealias V = Int; var someProp : Int { get throws {0} } }
// ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
// expected-note@+3 {{add stubs for conformance}}
// expected-note@+2 3 {{candidate is 'async', but protocol requirement is not}}
// expected-error@+1 {{type 'CA_N' does not conform to protocol 'None'}}
struct CA_N : None { typealias V = Int; var someProp : Int { get async {0} } }
// expected-note@+3 {{add stubs for conformance}}
// expected-note@+2 {{candidate is 'async', but protocol requirement is not}}
// expected-error@+1 {{type 'CA_T' does not conform to protocol 'T'}}
class CA_T : T { typealias V = Int; var someProp : Int { get async {0} } }
struct CA_A : A { typealias V = Int; var someProp : Int { get async {0} } }
enum CA_AT : AT { typealias V = Int; var someProp : Int { get async {0} } }
// ------ ------ ------ ------ ------ ------ ------ ------ ------ ------
// I put these on separate lines to ensure diagnostics point to the right thing.
// expected-note@+2 {{add stubs for conformance}}
// expected-error@+1 {{type 'CAT_N' does not conform to protocol 'None'}}
class CAT_N : None { typealias V = Int;
// expected-note@+2 {{candidate throws, but protocol does not allow it}}
// expected-note@+1 2 {{candidate is 'async', but protocol requirement is not}}
var someProp : Int { get async throws {0} }
}
// expected-note@+2 {{add stubs for conformance}}
// expected-error@+1 {{type 'CAT_T' does not conform to protocol 'T'}}
enum CAT_T : T { typealias V = Int;
// expected-note@+2 {{candidate throws, but protocol does not allow it}}
// expected-note@+1 2 {{candidate is 'async', but protocol requirement is not}}
var someProp : Int { get async throws {0} }
}
// expected-note@+2 {{add stubs for conformance}}
// expected-error@+1 {{type 'CAT_A' does not conform to protocol 'A'}}
class CAT_A : A { typealias V = Int;
// expected-note@+1 {{candidate throws, but protocol does not allow it}}
var someProp : Int { get async throws {0} }
}
class CAT_AT : AT { typealias V = Int;
var someProp : Int { get async throws {0} }
}
// because the protocols above are generic over a type (i.e.,
// have an associatedtype), we can't express the constraints
// below with just 'as'.
func asNone<U : None>(u : U) async throws {
_ = u.someProp
}
func asAsync<U : A>(u : U) async {
// expected-error@+1 {{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
_ = u.someProp // expected-note@:7{{property access is 'async'}}
_ = await u.someProp
}
func asThrows<U : T>(u : U) throws {
// expected-note@+3 {{did you mean to handle error as optional value?}}
// expected-note@+2 {{did you mean to disable error propagation?}}
// expected-note@+1 {{did you mean to use 'try'?}}
_ = u.someProp // expected-error {{property access can throw but is not marked with 'try'}}
_ = try u.someProp
}
func asAsyncThrows<U : AT>(u : U) async throws {
// expected-note@+6 {{did you mean to handle error as optional value?}}
// expected-note@+5 {{did you mean to disable error propagation?}}
// expected-note@+4 {{did you mean to use 'try'?}}
// expected-error@+3 {{expression is 'async' but is not marked with 'await'}}{{9-9=await }}
// expected-note@+2 {{property access is 'async'}}
// expected-error@+1 {{property access can throw but is not marked with 'try'}}
_ = u.someProp
_ = try await u.someProp
}
////////
// specific conformance coverage for subscripts
protocol NoneSub {
// expected-note@+1 3 {{protocol requires subscript with type '(Int) -> Bool'}}
subscript(_ i : Int) -> Bool { get }
}
protocol AsyncSub {
// expected-note@+1 2 {{protocol requires subscript with type '(Int) -> Bool'}}
subscript(_ i : Int) -> Bool { get async }
}
protocol ThrowsSub {
// expected-note@+1 2 {{protocol requires subscript with type '(Int) -> Bool'}}
subscript(_ i : Int) -> Bool { get throws }
}
protocol AsyncThrowsSub {
subscript(_ i : Int) -> Bool { get async throws }
}
// "S" stands for "subscript", but otherwise the convention above applies:
// Sx_y ==> witness x trying to conform to y. A = async, T = throws, AT = async throws
// I peppered some enums and structs in there for flavor.
struct SN_N : NoneSub
{ subscript(_ i : Int) -> Bool { get { true } }}
class SA_N : NoneSub // expected-error{{type 'SA_N' does not conform to protocol 'NoneSub'}} expected-note {{add stubs for conformance}}
{ subscript(_ i : Int) -> Bool { get async { true } }} // expected-note{{candidate is 'async', but protocol requirement is not}}
class ST_N : NoneSub // expected-error{{type 'ST_N' does not conform to protocol 'NoneSub'}} expected-note {{add stubs for conformance}}
{ subscript(_ i : Int) -> Bool { get throws { true } }} // expected-note{{candidate throws, but protocol does not allow it}}
class SAT_N : NoneSub // expected-error{{type 'SAT_N' does not conform to protocol 'NoneSub'}} expected-note {{add stubs for conformance}}
{ subscript(_ i : Int) -> Bool { get async throws { true } }} // expected-note{{candidate is 'async', but protocol requirement is not}}
class SN_A : AsyncSub
{ subscript(_ i : Int) -> Bool { get { true } }}
struct SA_A : AsyncSub
{ subscript(_ i : Int) -> Bool { get async { true } }}
enum ST_A : AsyncSub // expected-error{{type 'ST_A' does not conform to protocol 'AsyncSub'}} expected-note {{add stubs for conformance}}
{ subscript(_ i : Int) -> Bool { get throws { true } }} // expected-note{{candidate throws, but protocol does not allow it}}
class SAT_A : AsyncSub // expected-error{{type 'SAT_A' does not conform to protocol 'AsyncSub'}} expected-note {{add stubs for conformance}}
{ subscript(_ i : Int) -> Bool { get async throws { true } }} // expected-note{{candidate throws, but protocol does not allow it}}
class SN_T : ThrowsSub
{ subscript(_ i : Int) -> Bool { get { true } }}
class SA_T : ThrowsSub // expected-error{{type 'SA_T' does not conform to protocol 'ThrowsSub'}} expected-note {{add stubs for conformance}}
{ subscript(_ i : Int) -> Bool { get async { true } }} // expected-note{{candidate is 'async', but protocol requirement is not}}
struct ST_T : ThrowsSub
{ subscript(_ i : Int) -> Bool { get throws { true } }}
struct SAT_T : ThrowsSub // expected-error{{type 'SAT_T' does not conform to protocol 'ThrowsSub'}} expected-note {{add stubs for conformance}}
{ subscript(_ i : Int) -> Bool { get async throws { true } }} // expected-note{{candidate is 'async', but protocol requirement is not}}
class SN_AT : AsyncThrowsSub
{ subscript(_ i : Int) -> Bool { get { true } }}
enum SA_AT : AsyncThrowsSub
{ subscript(_ i : Int) -> Bool { get async { true } }}
class ST_AT : AsyncThrowsSub
{ subscript(_ i : Int) -> Bool { get throws { true } }}
struct SAT_AT : AsyncThrowsSub
{ subscript(_ i : Int) -> Bool { get async throws { true } }}
////////
// protocol composition & inheritance
func composed1<U : A & T >(u : U) async throws {
// expected-note@+4 {{did you mean to handle error as optional value?}}
// expected-note@+3 {{did you mean to disable error propagation?}}
// expected-note@+2 {{did you mean to use 'try'?}}
// expected-error@+1 {{property access can throw but is not marked with 'try'}}
_ = u.someProp
// FIXME: this ^ should raise property access is 'async' but is not marked with 'await'
_ = try u.someProp
}
func composed2<U : None & A >(u : U) async {
_ = u.someProp
// FIXME: this ^ should raise "property access is 'async' but is not marked with 'await'""
_ = await u.someProp // expected-warning {{no 'async' operations occur within 'await' expression}}
}
func composed3<U : T & None >(u : U) throws {
// expected-note@+3 {{did you mean to handle error as optional value?}}
// expected-note@+2 {{did you mean to disable error propagation?}}
// expected-note@+1 {{did you mean to use 'try'?}}
_ = u.someProp // expected-error {{property access can throw but is not marked with 'try'}}
_ = try u.someProp
}
func composed4<U : T & None >(u : U) {
_ = u.someProp // expected-error {{property access can throw, but it is not marked with 'try' and the error is not handled}}
_ = try! u.someProp
}
/////////////////
// redefining the protocols to make sure the fix-its are matched
protocol NoEffects {
// expected-note@+1 2 {{protocol requires property 'someProp' with type 'Int'}}
var someProp : Int { get }
}
protocol Throws {
// expected-note@+1 2 {{protocol requires property 'someProp' with type 'Int'}}
var someProp : Int { get throws }
}
protocol Async {
// expected-note@+1 3 {{protocol requires property 'someProp' with type 'Int'}}
var someProp : Int { get async }
}
protocol AsyncThrowsByInheritance : Async, Throws {}
protocol AsyncByInheritance : Async, NoEffects {}
protocol ThrowsByInheritance : Throws, NoEffects {}
extension CN_N : AsyncByInheritance, ThrowsByInheritance, AsyncThrowsByInheritance {}
extension CN_T : AsyncByInheritance, ThrowsByInheritance, AsyncThrowsByInheritance {}
extension CN_A : AsyncByInheritance, ThrowsByInheritance, AsyncThrowsByInheritance {}
extension CN_AT : AsyncByInheritance, ThrowsByInheritance, AsyncThrowsByInheritance {}
// ----- -----
extension CT_N : Throws {}
// expected-note@+2 {{add stubs for conformance}}
// expected-error@+1 {{type 'CT_N' does not conform to protocol 'NoEffects'}}
extension CT_N : ThrowsByInheritance {}
// expected-note@+2 {{add stubs for conformance}}
// expected-error@+1 {{type 'CT_N' does not conform to protocol 'Async'}}
extension CT_N : AsyncByInheritance {}
// ----- -----
extension CA_N : Async {}
// expected-note@+2 {{add stubs for conformance}}
// expected-error@+1 {{type 'CA_N' does not conform to protocol 'NoEffects'}}
extension CA_N : AsyncByInheritance {}
// expected-note@+2 {{add stubs for conformance}}
// expected-error@+1 {{type 'CA_N' does not conform to protocol 'Throws'}}
extension CA_N : ThrowsByInheritance {}
// ----- -----
// expected-note@+2 {{add stubs for conformance}}
// expected-error@+1 {{type 'CAT_N' does not conform to protocol 'Async'}}
extension CAT_N : Async {}
// expected-note@+2 {{add stubs for conformance}}
// expected-error@+1 {{type 'CAT_N' does not conform to protocol 'Throws'}}
extension CAT_N : Throws {}
// expected-note@+3 {{add stubs for conformance}}
// expected-error@+2 {{type 'CAT_T' does not conform to protocol 'Async'}}
// expected-error@+1 {{type 'CAT_T' does not conform to protocol 'Throws'}}
extension CAT_T : AsyncThrowsByInheritance {}
struct S : Async, Throws {
var someProp : Int { 3 }
}
func play(s : S) async throws {
_ = s.someProp
_ = await (s as Async).someProp
_ = try (s as Throws).someProp
}
//////////
/// Check protocol overrides. Cannot override with more effects.
protocol HammeredDulcimer {
subscript(_ note : Int) -> Int { get }
var bridges : Int { get async throws }
}
protocol Santur : HammeredDulcimer {
override subscript(_ note : Int) -> Int { get throws } // expected-error{{cannot override non-throwing subscript with throwing subscript}}
override var bridges : Int { get throws }
}
protocol Santoor : Santur {
override var bridges : Int { get async throws } // expected-error{{cannot override non-async property with async property}}
}
protocol Yangqin : HammeredDulcimer {
override var bridges : Int { get async throws } // same effects are OK
}
protocol Hackbrett : HammeredDulcimer {
override var bridges : Int { get } // no effects are OK
override subscript(_ note : Int) -> Int { get async throws } // expected-error {{cannot override non-async subscript with async subscript}}
}