File: transfernonsendable_sending_results.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 (297 lines) | stat: -rw-r--r-- 12,129 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
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
// RUN: %target-swift-frontend -emit-sil -parse-as-library -strict-concurrency=complete -disable-availability-checking -verify %s -o /dev/null -enable-upcoming-feature GlobalActorIsolatedTypesUsability

// REQUIRES: concurrency
// REQUIRES: asserts

////////////////////////
// MARK: Declarations //
////////////////////////

class NonSendableKlass {} // expected-note 2{{}}

struct NonSendableStruct {
  var first = NonSendableKlass()
  var second = NonSendableKlass()
}

func getValue<T>() -> T { fatalError() }
func getValueAsync<T>() async -> T { fatalError() }
func getValueAsyncTransferring<T>() async -> sending T { fatalError() }
@MainActor func getMainActorValueAsync<T>() async -> T { fatalError() }
@MainActor func getMainActorValueAsyncTransferring<T>() async -> sending T { fatalError() }

func useValue<T>(_ t: T) {}
func getAny() -> Any { fatalError() }

actor Custom {
}

@globalActor
struct CustomActor {
    static var shared: Custom {
        return Custom()
    }
}

@MainActor func transferToMainIndirect<T>(_ t: T) async {}
@CustomActor func transferToCustomIndirect<T>(_ t: T) async {}
@MainActor func transferToMainDirect(_ t: NonSendableKlass) async {}
@CustomActor func transferToCustomDirect(_ t: NonSendableKlass) async {}
func useValueIndirect<T>(_ t: T) {}
func useValueDirect(_ t: NonSendableKlass) {}

func transferValueDirect(_ x: sending NonSendableKlass) {}
func transferValueIndirect<T>(_ x: sending T) {}

func transferResult() -> sending NonSendableKlass { NonSendableKlass() }
func transferResultWithArg(_ x: NonSendableKlass) -> sending NonSendableKlass { NonSendableKlass() }
func transferResultWithTransferringArg(_ x: sending NonSendableKlass) -> sending NonSendableKlass { NonSendableKlass() }
func transferResultWithTransferringArg2(_ x: sending NonSendableKlass, _ y: NonSendableKlass) -> sending NonSendableKlass { NonSendableKlass() }
func transferResultWithTransferringArg2Throwing(_ x: sending NonSendableKlass, _ y: NonSendableKlass) throws -> sending NonSendableKlass { NonSendableKlass() }

func transferAsyncResult() async -> sending NonSendableKlass { NonSendableKlass() }
func transferAsyncResultWithArg(_ x: NonSendableKlass) async -> sending NonSendableKlass { NonSendableKlass() }
func transferAsyncResultWithTransferringArg(_ x: sending NonSendableKlass) async -> sending NonSendableKlass { NonSendableKlass() }
func transferAsyncResultWithTransferringArg2(_ x: sending NonSendableKlass, _ y: NonSendableKlass) async -> sending NonSendableKlass { NonSendableKlass() }
func transferAsyncResultWithTransferringArg2Throwing(_ x: sending NonSendableKlass, _ y: NonSendableKlass) async throws -> sending NonSendableKlass { NonSendableKlass() }

@MainActor func transferAsyncResultMainActor() async -> sending NonSendableKlass { NonSendableKlass() }

@MainActor var globalNonSendableKlass = NonSendableKlass()

@MainActor
struct MainActorIsolatedStruct {
  let ns = NonSendableKlass()
}

@MainActor
enum MainActorIsolatedEnum {
  case first
  case second(NonSendableKlass)
}

struct GenericNonSendableStruct<T> {
  var t: T
  var t2: T?
  var x: NonSendableKlass
}

class GenericNonSendableKlass<T> {
  var t: T
  var t2: T?
  var x: NonSendableKlass?

  init(_ inputT: T) {
    t = inputT
    t2 = nil
    x = NonSendableKlass()
  }
}

func sendParameter<T>(_ t: sending T) {}

/////////////////
// MARK: Tests //
/////////////////

func simpleTest() async {
  let x = NonSendableKlass()
  let y = transferResultWithArg(x)
  await transferToMainDirect(x)
  useValue(y)
}

// Since y is transfered, we should emit the error on useValue(x). We generally
// emit the first seen error on a path, so if we were to emit an error on
// useValue(y), we would have emitted that error.
func simpleTest2() async {
  let x = NonSendableKlass()
  let y = transferResultWithArg(x)
  await transferToMainDirect(x) // expected-warning {{sending 'x' risks causing data races}}
  // expected-note @-1 {{sending 'x' to main actor-isolated global function 'transferToMainDirect' risks causing data races between main actor-isolated and local nonisolated uses}}
  useValue(y)
  useValue(x) // expected-note {{access can happen concurrently}}
}

// Make sure that later errors with y can happen.
func simpleTest3() async {
  let x = NonSendableKlass()
  let y = transferResultWithArg(x)
  await transferToMainDirect(x)
  await transferToMainDirect(y) // expected-warning {{sending 'y' risks causing data races}}
  // expected-note @-1 {{sending 'y' to main actor-isolated global function 'transferToMainDirect' risks causing data races between main actor-isolated and local nonisolated uses}}
  useValue(y) // expected-note {{access can happen concurrently}}
}

func transferResult() async -> sending NonSendableKlass {
  let x = NonSendableKlass()
  await transferToMainDirect(x) // expected-warning {{sending 'x' risks causing data races}}
  // expected-note @-1 {{sending 'x' to main actor-isolated global function 'transferToMainDirect' risks causing data races between main actor-isolated and local nonisolated uses}}
  return x // expected-note {{access can happen concurrently}}
}

func transferInAndOut(_ x: sending NonSendableKlass) -> sending NonSendableKlass {
  x
}


func transferReturnArg(_ x: NonSendableKlass) -> sending NonSendableKlass {
  return x // expected-warning {{sending 'x' risks causing data races}}
  // expected-note @-1 {{task-isolated 'x' cannot be a 'sending' result. task-isolated uses may race with caller uses}}
}

// TODO: This will be fixed once I represent @MainActor on func types.
@MainActor func transferReturnArgMainActor(_ x: NonSendableKlass) -> sending NonSendableKlass {
  return x // expected-warning {{sending 'x' risks causing data races}}
  // expected-note @-1 {{main actor-isolated 'x' cannot be a 'sending' result. main actor-isolated uses may race with caller uses}}
}

// This is safe since we are returning the whole tuple fresh. In contrast,
// (transferring NonSendableKlass, sending NonSendableKlass) would not be
// safe if we ever support that.
func transferReturnArgTuple(_ x: sending NonSendableKlass) -> sending (NonSendableKlass, NonSendableKlass) {
  return (x, x)
}

func useTransferredResultMainActor() async {
  let _ = await transferAsyncResultMainActor()
}

func useTransferredResult() async {
  let _ = await transferAsyncResult()
}

extension MainActorIsolatedStruct {
  func testNonSendableErrorReturnWithTransfer() -> sending NonSendableKlass {
    return ns // expected-warning {{sending 'self.ns' risks causing data races}}
    // expected-note @-1 {{main actor-isolated 'self.ns' cannot be a 'sending' result. main actor-isolated uses may race with caller uses}}
  }
  func testNonSendableErrorReturnNoTransfer() -> NonSendableKlass {
    return ns
  }
}

extension MainActorIsolatedEnum {
  func testSwitchReturn() -> sending NonSendableKlass? {
    switch self {
    case .first:
      return nil
    case .second(let ns):
      return ns
    }
  } // expected-warning {{sending 'ns.some' risks causing data races}}
  // expected-note @-1 {{main actor-isolated 'ns.some' cannot be a 'sending' result. main actor-isolated uses may race with caller uses}}

  func testSwitchReturnNoTransfer() -> NonSendableKlass? {
    switch self {
    case .first:
      return nil
    case .second(let ns):
      return ns
    }
  }

  func testIfLetReturn() -> sending NonSendableKlass? {
    if case .second(let ns) = self {
      return ns // TODO: The error below should be here.
    }
    return nil
  } // expected-warning {{sending 'ns.some' risks causing data races}}
  // expected-note @-1 {{main actor-isolated 'ns.some' cannot be a 'sending' result. main actor-isolated uses may race with caller uses}}

  func testIfLetReturnNoTransfer() -> NonSendableKlass? {
    if case .second(let ns) = self {
      return ns
    }
    return nil
  }

}

///////////////////////////
// MARK: Async Let Tests //
///////////////////////////
//
// Move these tests to async let once strict-concurrency=complete requires
// transfer non sendable.

// Make sure that we can properly construct a reabstraction thunk since
// constructNonSendableKlassAsync doesn't return the value sending but
// async let wants it to be transferring.
//
// Importantly, we should only emit the sema error here saying that one cannot
// return a non-Sendable value here.
func asyncLetReabstractionThunkTest() async {
  // With thunk.
  async let newValue: NonSendableKlass = await getValueAsync()
  let _ = await newValue

  // Without thunk.
  async let newValue2: NonSendableKlass = await getValueAsyncTransferring()
  let _ = await newValue2
}

func asyncLetReabstractionThunkTest2() async {
  // We emit the error here since we are returning a main actor isolated value.
  async let newValue: NonSendableKlass = await getMainActorValueAsync()
  // expected-warning @-1 {{non-sendable type 'NonSendableKlass' returned by implicitly asynchronous call to main actor-isolated function cannot cross actor boundary}}

  let _ = await newValue

  // Without thunk.
  async let newValue2: NonSendableKlass = await getMainActorValueAsyncTransferring()
  let _ = await newValue2
}

@MainActor func asyncLetReabstractionThunkTestGlobalActor() async {
  // With thunk we do not emit an error since our async let is not main actor
  // isolated despite being in an @MainActor function.
  async let newValue: NonSendableKlass = await getValueAsync()
  let _ = await newValue

  // Without thunk.
  async let newValue2: NonSendableKlass = await getValueAsyncTransferring()
  let _ = await newValue2
}

@MainActor func asyncLetReabstractionThunkTestGlobalActor2() async {
  // We emit the error here since we are returning a main actor isolated value.
  async let newValue: NonSendableKlass = await getMainActorValueAsync()
  // expected-warning @-1 {{non-sendable type 'NonSendableKlass' returned by implicitly asynchronous call to main actor-isolated function cannot cross actor boundary}}

  let _ = await newValue

  // Without thunk.
  async let newValue2: NonSendableKlass = await getMainActorValueAsyncTransferring()
  let _ = await newValue2
}

///////////////////////////////////
// MARK: Indirect Sending Result //
///////////////////////////////////

func indirectSending<T>(_ t: T) -> sending T {
  return t // expected-warning {{returning task-isolated 't' as a 'sending' result risks causing data races}}
  // expected-note @-1 {{returning task-isolated 't' risks causing data races since the caller assumes that 't' can be safely sent to other isolation domains}}
}

func indirectSendingStructField<T>(_ t: GenericNonSendableStruct<T>) -> sending T {
  return t.t // expected-warning {{returning task-isolated 't.t' as a 'sending' result risks causing data races}}
  // expected-note @-1 {{returning task-isolated 't.t' risks causing data races since the caller assumes that 't.t' can be safely sent to other isolation domains}}
}

func indirectSendingStructOptionalField<T>(_ t: GenericNonSendableStruct<T>) -> sending T {
  return t.t2! // expected-warning {{returning task-isolated 't.t2.some' as a 'sending' result risks causing data races}}
  // expected-note @-1 {{returning task-isolated 't.t2.some' risks causing data races since the caller assumes that 't.t2.some' can be safely sent to other isolation domains}}
}

func indirectSendingClassField<T>(_ t: GenericNonSendableKlass<T>) -> sending T {
  return t.t // expected-warning {{returning task-isolated 't.t' as a 'sending' result risks causing data races}}
  // expected-note @-1 {{returning task-isolated 't.t' risks causing data races since the caller assumes that 't.t' can be safely sent to other isolation domains}}
}

func indirectSendingOptionalClassField<T>(_ t: GenericNonSendableKlass<T>) -> sending T {
  return t.t2! // expected-warning {{returning a task-isolated 'Optional<T>' value as a 'sending' result risks causing data races}}
  // expected-note @-1 {{returning a task-isolated 'Optional<T>' value risks causing races since the caller assumes the value can be safely sent to other isolation domains}}
  // expected-note @-2 {{'Optional<T>' is a non-Sendable type}}
}