Skip to content

Commit 3650b5e

Browse files
committed
extra test case targeting implicitly-async calls to actor methods
1 parent 0b140a0 commit 3650b5e

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency
2+
// REQUIRES: concurrency
3+
4+
actor class BankAccount {
5+
6+
private var curBalance : Int
7+
8+
private var accountHolder : String = "unknown"
9+
10+
// expected-note@+1 2 {{mutable state is only available within the actor instance}}
11+
var owner : String {
12+
get { accountHolder }
13+
set { accountHolder = newValue }
14+
}
15+
16+
init(initialDeposit : Int) {
17+
curBalance = initialDeposit
18+
}
19+
20+
// NOTE: this func is accessed through both async and sync calls.
21+
// expected-note@+1 {{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}}
22+
func balance() -> Int { return curBalance }
23+
24+
// expected-note@+1 {{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}}
25+
func deposit(_ amount : Int) -> Int {
26+
guard amount >= 0 else { return 0 }
27+
28+
curBalance = curBalance + amount
29+
return curBalance
30+
}
31+
32+
func canWithdraw(_ amount : Int) -> Bool {
33+
// call 'balance' from sync through self
34+
return self.balance() >= amount
35+
}
36+
37+
func testSelfBalance() async {
38+
_ = await balance() // expected-warning {{no calls to 'async' functions occur within 'await' expression}}
39+
}
40+
41+
// returns the amount actually withdrawn
42+
func withdraw(_ amount : Int) -> Int {
43+
guard canWithdraw(amount) else { return 0 }
44+
45+
curBalance = curBalance - amount
46+
return amount
47+
}
48+
49+
// returns the balance of this account following the transfer
50+
func transferAll(from : BankAccount) async -> Int {
51+
// call sync methods on another actor
52+
let amountTaken = await from.withdraw(from.balance())
53+
return deposit(amountTaken)
54+
}
55+
56+
func greaterThan(other : BankAccount) async -> Bool {
57+
return await balance() > other.balance()
58+
}
59+
60+
func testTransactions() {
61+
_ = deposit(withdraw(deposit(withdraw(balance()))))
62+
}
63+
64+
} // end actor class
65+
66+
func someAsyncFunc() async {
67+
let deposit1 = 120, deposit2 = 45
68+
let a = BankAccount(initialDeposit: 0)
69+
let b = BankAccount(initialDeposit: deposit2)
70+
71+
let _ = await a.deposit(deposit1)
72+
let afterXfer = await a.transferAll(from: b)
73+
let reportedBal = await a.balance()
74+
75+
// check on account A
76+
guard afterXfer == (deposit1 + deposit2) && afterXfer == reportedBal else {
77+
print("BUG 1!")
78+
return
79+
}
80+
81+
// check on account B
82+
guard await b.balance() == 0 else {
83+
print("BUG 2!")
84+
return
85+
}
86+
87+
_ = await a.deposit(b.withdraw(a.deposit(b.withdraw(b.balance()))))
88+
89+
print("ok!")
90+
}
91+
92+
93+
//////////////////
94+
// check for appropriate error messages
95+
//////////////////
96+
97+
extension BankAccount {
98+
func totalBalance(including other: BankAccount) async -> Int {
99+
return balance()
100+
+ other.balance() // expected-error{{call is 'async' but is not marked with 'await'}}
101+
}
102+
103+
func breakAccounts(other: BankAccount) async {
104+
_ = other.deposit( // expected-error{{call is 'async' but is not marked with 'await'}}
105+
other.withdraw( // expected-error{{call is 'async' but is not marked with 'await'}}
106+
self.deposit(
107+
other.withdraw( // expected-error{{call is 'async' but is not marked with 'await'}}
108+
other.balance())))) // expected-error{{call is 'async' but is not marked with 'await'}}
109+
}
110+
}
111+
112+
func anotherAsyncFunc() async {
113+
let a = BankAccount(initialDeposit: 34)
114+
let b = BankAccount(initialDeposit: 35)
115+
116+
_ = a.deposit(1) // expected-error{{call is 'async' but is not marked with 'await'}}
117+
_ = b.balance() // expected-error{{call is 'async' but is not marked with 'await'}}
118+
119+
_ = b.balance // expected-error {{actor-isolated instance method 'balance()' can only be referenced inside the actor}}
120+
121+
a.owner = "cat" // expected-error{{actor-isolated property 'owner' can only be referenced inside the actor}}
122+
_ = b.owner // expected-error{{actor-isolated property 'owner' can only be referenced inside the actor}}
123+
124+
}
125+
126+
// expected-note@+2 {{add 'async' to function 'regularFunc()' to make it asynchronous}}
127+
// expected-note@+1 {{add '@asyncHandler' to function 'regularFunc()' to create an implicit asynchronous context}}
128+
func regularFunc() {
129+
let a = BankAccount(initialDeposit: 34)
130+
131+
_ = a.deposit //expected-error{{actor-isolated instance method 'deposit' can only be referenced inside the actor}}
132+
133+
_ = a.deposit(1) // expected-error{{'async' in a function that does not support concurrency}}
134+
}
135+
136+
137+
actor class TestActor {}
138+
139+
@globalActor
140+
struct BananaActor {
141+
static var shared: TestActor { TestActor() }
142+
}
143+
144+
@globalActor
145+
struct OrangeActor {
146+
static var shared: TestActor { TestActor() }
147+
}
148+
149+
func blender(_ peeler : () -> Void) {
150+
peeler()
151+
}
152+
153+
@BananaActor func wisk(_ something : Any) { } // expected-note 4 {{only asynchronous methods can be used outside the actor instance}}
154+
155+
@BananaActor func peelBanana() { } // expected-note 2 {{only asynchronous methods can be used outside the actor instance}}
156+
157+
@OrangeActor func makeSmoothie() async {
158+
await wisk({})
159+
await wisk(1)
160+
await (peelBanana)()
161+
await (((((peelBanana)))))()
162+
await (((wisk)))((wisk)((wisk)(1)))
163+
164+
blender((peelBanana)) // expected-error {{global function 'peelBanana()' isolated to global actor 'BananaActor' can not be referenced from different global actor 'OrangeActor'}}
165+
await wisk(peelBanana) // expected-error {{global function 'peelBanana()' isolated to global actor 'BananaActor' can not be referenced from different global actor 'OrangeActor'}}
166+
167+
await wisk(wisk) // expected-error {{global function 'wisk' isolated to global actor 'BananaActor' can not be referenced from different global actor 'OrangeActor'}}
168+
await (((wisk)))(((wisk))) // expected-error {{global function 'wisk' isolated to global actor 'BananaActor' can not be referenced from different global actor 'OrangeActor'}}
169+
170+
// expected-warning@+2 {{no calls to 'async' functions occur within 'await' expression}}
171+
// expected-error@+1 {{global function 'wisk' isolated to global actor 'BananaActor' can not be referenced from different global actor 'OrangeActor'}}
172+
await {wisk}()(1)
173+
174+
// expected-warning@+2 {{no calls to 'async' functions occur within 'await' expression}}
175+
// expected-error@+1 {{global function 'wisk' isolated to global actor 'BananaActor' can not be referenced from different global actor 'OrangeActor'}}
176+
await (true ? wisk : {n in return})(1)
177+
}

0 commit comments

Comments
 (0)