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
|
// RUN: %target-swift-frontend -emit-sil -strict-concurrency=complete -disable-availability-checking -verify %s -o /dev/null -enable-upcoming-feature GlobalActorIsolatedTypesUsability
// REQUIRES: concurrency
// REQUIRES: asserts
// This test validates how we handle partial applies that are isolated to a
// specific isolation domain (causing isolation crossings to occur).
////////////////////////
// MARK: Declarations //
////////////////////////
class NonSendableKlass {}
actor Custom {
var x = NonSendableKlass()
}
@globalActor
struct CustomActor {
static var shared: Custom {
return Custom()
}
}
func useValue<T>(_ t: T) {}
@MainActor func transferToMain<T>(_ t: T) {}
@CustomActor func transferToCustom<T>(_ t: T) {}
var boolValue: Bool { false }
/////////////////
// MARK: Tests //
/////////////////
func doSomething(_ x: NonSendableKlass, _ y: NonSendableKlass) { }
actor ProtectsNonSendable {
var ns: NonSendableKlass = .init()
nonisolated func testParameter(_ nsArg: NonSendableKlass) async {
self.assumeIsolated { isolatedSelf in
isolatedSelf.ns = nsArg // expected-warning {{sending 'nsArg' risks causing data races}}
// expected-note @-1 {{task-isolated 'nsArg' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses}}
}
}
nonisolated func testParameterOutOfLine2(_ nsArg: NonSendableKlass) async {
let closure: (isolated ProtectsNonSendable) -> () = { isolatedSelf in
isolatedSelf.ns = nsArg // expected-warning {{sending 'nsArg' risks causing data races}}
// expected-note @-1 {{task-isolated 'nsArg' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses}}
}
self.assumeIsolated(closure)
self.assumeIsolated(closure)
}
nonisolated func testParameterMergedIntoLocal(_ nsArg: NonSendableKlass) async {
let l = NonSendableKlass()
doSomething(l, nsArg)
self.assumeIsolated { isolatedSelf in
isolatedSelf.ns = l // expected-warning {{sending 'l' risks causing data races}}
// expected-note @-1 {{task-isolated 'l' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses}}
}
}
nonisolated func testLocal() async {
let l = NonSendableKlass()
// This is safe since we do not reuse l.
self.assumeIsolated { isolatedSelf in
isolatedSelf.ns = l
}
}
nonisolated func testLocal2() async {
let l = NonSendableKlass()
// This is not safe since we use l later.
self.assumeIsolated { isolatedSelf in
isolatedSelf.ns = l // expected-warning {{sending 'l' risks causing data races}}
// expected-note @-1 {{'l' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses}}
}
useValue(l) // expected-note {{access can happen concurrently}}
}
}
func normalFunc_testLocal_1() {
let x = NonSendableKlass()
let _ = { @MainActor in
print(x)
}
}
func normalFunc_testLocal_2() {
let x = NonSendableKlass()
let _ = { @MainActor in
useValue(x) // expected-warning {{sending 'x' risks causing data races}}
// expected-note @-1 {{'x' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses}}
}
useValue(x) // expected-note {{access can happen concurrently}}
}
// We error here since we are performing a double transfer.
//
// TODO: Add special transfer use so we can emit a double transfer error
// diagnostic.
func transferBeforeCaptureErrors() async {
let x = NonSendableKlass()
await transferToCustom(x) // expected-warning {{sending 'x' risks causing data races}}
// expected-note @-1 {{sending 'x' to global actor 'CustomActor'-isolated global function 'transferToCustom' risks causing data races between global actor 'CustomActor'-isolated and local nonisolated uses}}
let _ = { @MainActor in // expected-note {{access can happen concurrently}}
useValue(x)
}
}
// TODO: This should have an error. We aren't disambiguating the actors.
func testDifferentIsolationFromSameClassKindPartialApply() async {
let p1 = ProtectsNonSendable()
let p2 = ProtectsNonSendable()
let x = NonSendableKlass()
let closure: (isolated ProtectsNonSendable) -> () = { isolatedSelf in
print(x)
}
await closure(p1)
await closure(p2)
}
// TODO: This should have an error. We aren't disambiguating the actors.
func testDifferentIsolationFromSameClassKindPartialApplyFlowSensitive() async {
let p1 = ProtectsNonSendable()
let p2 = ProtectsNonSendable()
let x = NonSendableKlass()
let closure: (isolated ProtectsNonSendable) -> () = { isolatedSelf in
print(x)
}
if await boolValue {
await closure(p1)
await closure(p1)
} else {
await closure(p2)
await closure(p2)
}
}
// TODO: This should have an error. We aren't disambiguating the actors.
func testDifferentIsolationFromSameClassKindPartialApplyFlowSensitive2() async {
let p1 = ProtectsNonSendable()
let p2 = ProtectsNonSendable()
let x = NonSendableKlass()
let closure: (isolated ProtectsNonSendable) -> () = { isolatedSelf in
print(x)
}
if await boolValue {
await closure(p1)
} else {
await closure(p2)
}
await closure(p2)
}
|