File: transfernonsendable_isolationcrossing_partialapply.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (169 lines) | stat: -rw-r--r-- 5,357 bytes parent folder | download
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)
}