Skip to content

Commit 47ea602

Browse files
committed
[test] eff. prop + protocol conformance, composition & inheritance
1 parent f3be40e commit 47ea602

File tree

2 files changed

+347
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)