1
- # ` sendable ` parameter and result values
1
+ # ` sending ` parameter and result values
2
2
3
3
* Proposal: [ SE-0430] ( 0430-transferring-parameters-and-results.md )
4
4
* Authors: [ Michael Gottesman] ( https://github.com/gottesmm ) , [ Holly Borla] ( https://github.com/hborla ) , [ John McCall] ( https://github.com/rjmccall )
12
12
## Introduction
13
13
14
14
This proposal extends region isolation to enable the application of an explicit
15
- ` sendable ` annotation to function parameters and results. A function parameter
16
- or result that is annotated with ` sendable ` is required to be disconnected at
15
+ ` sending ` annotation to function parameters and results. A function parameter
16
+ or result that is annotated with ` sending ` is required to be disconnected at
17
17
the function boundary and thus possesses the capability of being safely sent
18
18
across an isolation domain or merged into an actor-isolated region in the
19
19
function's body or the function's caller respectively.
@@ -140,17 +140,17 @@ used again after the call to `resume(returning:)`.
140
140
141
141
This proposal enables explicitly specifying parameter and result values as
142
142
possessing the capability of being sent over an isolation boundary by annotating
143
- the value with a contextual ` sendable ` keyword:
143
+ the value with a contextual ` sending ` keyword:
144
144
145
145
``` swift
146
146
public struct CheckedContinuation <T , E : Error >: Sendable {
147
- public func resume (returning value : sendable T)
147
+ public func resume (returning value : sending T)
148
148
}
149
149
150
150
public func withCheckedContinuation <T >(
151
151
function : String = #function ,
152
152
_ body : (CheckedContinuation<T, Never >) -> Void
153
- ) async -> sendable T
153
+ ) async -> sending T
154
154
```
155
155
156
156
## Detailed design
@@ -166,35 +166,35 @@ the value's entire region implying that all uses of the value (and anything
166
166
non-` Sendable ` typed that can be reached from the value) must end in the source
167
167
concurrency context before any uses can begin in the destination concurrency
168
168
context. Swift achieves this property by requiring that the value is in a
169
- disconnected region and we say that such a value is a ` sendable ` value.
169
+ disconnected region and we say that such a value is a ` sending ` value.
170
170
171
171
Thus a newly-created value with no connections to existing regions is always a
172
- ` sendable ` value:
172
+ ` sending ` value:
173
173
174
174
``` swift
175
175
func f () async {
176
- // This is a `sendable ` value since we can transfer it safely...
176
+ // This is a `sending ` value since we can transfer it safely...
177
177
let ns = NonSendable ()
178
178
179
179
// ... here by calling 'sendToMain'.
180
180
await sendToMain (ns)
181
181
}
182
182
```
183
183
184
- Once defined, a ` sendable ` value can be merged into other isolation
184
+ Once defined, a ` sending ` value can be merged into other isolation
185
185
regions. Once merged, such regions, if not disconnected, will prevent the value
186
186
from being sent to another isolation domain implying that the value is no longer
187
- a ` sendable ` value:
187
+ a ` sending ` value:
188
188
189
189
``` swift
190
190
actor MyActor {
191
191
var myNS: NonSendable
192
192
193
193
func g () async {
194
- // 'ns' is initially a `sendable ` value since it is in a disconnected region...
194
+ // 'ns' is initially a `sending ` value since it is in a disconnected region...
195
195
let ns = NonSendable ()
196
196
197
- // ... but once we assign 'ns' into 'myNS', 'ns' is no longer a sendable
197
+ // ... but once we assign 'ns' into 'myNS', 'ns' is no longer a sending
198
198
// value...
199
199
myNS = ns
200
200
@@ -204,43 +204,43 @@ actor MyActor {
204
204
}
205
205
```
206
206
207
- If a ` sendable ` value's isolation region is merged into another disconnected
208
- isolation region, then the value is still considered to be ` sendable ` since two
207
+ If a ` sending ` value's isolation region is merged into another disconnected
208
+ isolation region, then the value is still considered to be ` sending ` since two
209
209
disconnected regions when merged form a new disconnected region:
210
210
211
211
``` swift
212
212
func h () async {
213
- // This is a `sendable ` value.
214
- let ns = NonSendable ()
213
+ // This is a `sending ` value.
214
+ let ns = Nonsending ()
215
215
216
- // This also a `sendable value.
216
+ // This also a `sending value.
217
217
let ns2 = NonSendable ()
218
218
219
219
// Since both ns and ns2 are disconnected, the region associated with
220
- // tuple is also disconnected and thus 't' is a `sendable ` value...
220
+ // tuple is also disconnected and thus 't' is a `sending ` value...
221
221
let t = (ns, ns2)
222
222
223
223
// ... that can be sent across a concurrency boundary safely.
224
224
await sendToMain (ns)
225
225
}
226
226
```
227
227
228
- ### Sendable Parameters and Results
228
+ ### sending Parameters and Results
229
229
230
- A ` sendable ` function parameter requires that the argument value be in a
230
+ A ` sending ` function parameter requires that the argument value be in a
231
231
disconnected region. At the point of the call, the disconnected region is no
232
232
longer in the caller's isolation domain, allowing the callee to send the
233
233
parameter value to a region that is opaque to the caller:
234
234
235
235
``` swift
236
236
@MainActor
237
- func acceptSend (_ : sendable NonSendable) {}
237
+ func acceptSend (_ : sending NonSendable) {}
238
238
239
239
func sendToMain () async {
240
240
let ns = NonSendable ()
241
241
242
242
// error: sending 'ns' may cause a race
243
- // note: 'ns' is passed as a 'sendable ' parameter to 'acceptSend'. Local uses could race with
243
+ // note: 'ns' is passed as a 'sending ' parameter to 'acceptSend'. Local uses could race with
244
244
// later uses in 'acceptSend'.
245
245
await acceptSend (ns)
246
246
@@ -253,28 +253,28 @@ What the callee does with the argument value is opaque to the caller; the callee
253
253
may send the value away, or it may merge the value to the isolation region of
254
254
one of the other parameters.
255
255
256
- A ` sendable ` result requires that the function implementation returns a value in
256
+ A ` sending ` result requires that the function implementation returns a value in
257
257
a disconnected region:
258
258
259
259
``` swift
260
260
@MainActor
261
261
struct S {
262
262
let ns: NonSendable
263
263
264
- func getNonSendableInvalid () -> sendable NonSendable {
264
+ func getNonSendableInvalid () -> sending NonSendable {
265
265
// error: sending 'self.ns' may cause a data race
266
- // note: main actor-isolated 'self.ns' is returned as a 'sendable ' result.
266
+ // note: main actor-isolated 'self.ns' is returned as a 'sending ' result.
267
267
// Caller uses could race against main actor-isolated uses.
268
268
return ns
269
269
}
270
270
271
- func getNonSendable () -> sendable NonSendable {
271
+ func getNonSendable () -> sending NonSendable {
272
272
return NonSendable () // okay
273
273
}
274
274
}
275
275
```
276
276
277
- The caller of a function returning a ` sendable ` result can assume the value is
277
+ The caller of a function returning a ` sending ` result can assume the value is
278
278
in a disconnected region, enabling non-` Sendable ` typed result values to cross
279
279
an actor isolation boundary:
280
280
@@ -290,75 +290,75 @@ nonisolated func f(s: S) async {
290
290
291
291
### Function subtyping
292
292
293
- For a given type ` T ` , ` sendable T` is a subtype of ` T ` . ` sendable ` is
293
+ For a given type ` T ` , ` sending T` is a subtype of ` T ` . ` sending ` is
294
294
contravariant in parameter position; if a function type is expecting a regular
295
- parameter of type ` T ` , it's perfectly valid to pass a ` sendable T` value
295
+ parameter of type ` T ` , it's perfectly valid to pass a ` sending T` value
296
296
that is known to be in a disconnected region. If a function is expecting a
297
- parameter of type ` sendable T` , it is not valid to pass a value that is not
297
+ parameter of type ` sending T` , it is not valid to pass a value that is not
298
298
in a disconnected region:
299
299
300
300
``` swift
301
- func sendableParameterConversions (
302
- f1 : (sendable NonSendable) -> Void ,
301
+ func sendingParameterConversions (
302
+ f1 : (sending NonSendable) -> Void ,
303
303
f2 : (NonSendable) -> Void
304
304
) {
305
- let _: (sendable NonSendable) -> Void = f1 // okay
306
- let _: (sendable NonSendable) -> Void = f2 // okay
305
+ let _: (sending NonSendable) -> Void = f1 // okay
306
+ let _: (sending NonSendable) -> Void = f2 // okay
307
307
let _: (NonSendable) -> Void = f1 // error
308
308
}
309
309
```
310
310
311
- ` sendable ` is covariant in result position. If a function returns a value
312
- of type ` sendable T` , it's valid to instead treat the result as if it were
311
+ ` sending ` is covariant in result position. If a function returns a value
312
+ of type ` sending T` , it's valid to instead treat the result as if it were
313
313
merged with the other parameters. If a function returns a regular value of type
314
314
` T ` , it is not valid to assume the value is in a disconnected region:
315
315
316
316
``` swift
317
- func sendableResultConversions (
318
- f1 : () -> sendable NonSendable,
317
+ func sendingResultConversions (
318
+ f1 : () -> sending NonSendable,
319
319
f2 : () -> NonSendable
320
320
) {
321
- let _: () -> sendable NonSendable = f1 // okay
322
- let _: () -> sendable NonSendable = f2 // error
321
+ let _: () -> sending NonSendable = f1 // okay
322
+ let _: () -> sending NonSendable = f2 // error
323
323
let _: () -> NonSendable = f1 // okay
324
324
}
325
325
```
326
326
327
327
### Protocol conformances
328
328
329
- A protocol requirement may include ` sendable ` parameter or result annotations:
329
+ A protocol requirement may include ` sending ` parameter or result annotations:
330
330
331
331
``` swift
332
332
protocol P1 {
333
- func requirement (_ : sendable NonSendable)
333
+ func requirement (_ : sending NonSendable)
334
334
}
335
335
336
336
protocol P2 {
337
- func requirement () -> sendable NonSendable
337
+ func requirement () -> sending NonSendable
338
338
}
339
339
```
340
340
341
341
Following the function subtyping rules in the previous section, a protocol
342
- requirement with a ` sendable ` parameter may be witnessed by a function with a
342
+ requirement with a ` sending ` parameter may be witnessed by a function with a
343
343
non-` Sendable ` typed parameter:
344
344
345
345
``` swift
346
346
struct X1 : P1 {
347
- func requirement (_ : sendable NonSendable) {}
347
+ func requirement (_ : sending NonSendable) {}
348
348
}
349
349
350
350
struct X2 : P1 {
351
351
func requirement (_ : NonSendable) {}
352
352
}
353
353
```
354
354
355
- A protocol requirement with a ` sendable ` result must be witnessed by a function
356
- with a ` sendable ` result, and a requirement with a plain result of type ` T ` may
357
- be witnessed by a function returning a ` sendable T` :
355
+ A protocol requirement with a ` sending ` result must be witnessed by a function
356
+ with a ` sending ` result, and a requirement with a plain result of type ` T ` may
357
+ be witnessed by a function returning a ` sending T` :
358
358
359
359
``` swift
360
360
struct Y1 : P1 {
361
- func requirement () -> sendable NonSendable {
361
+ func requirement () -> sending NonSendable {
362
362
return NonSendable ()
363
363
}
364
364
}
@@ -371,42 +371,42 @@ struct Y2: P1 {
371
371
}
372
372
```
373
373
374
- ### ` sendable inout` parameters
374
+ ### ` sending inout` parameters
375
375
376
- A ` sendable ` parameter can also be marked as ` inout ` , meaning that the argument
376
+ A ` sending ` parameter can also be marked as ` inout ` , meaning that the argument
377
377
value must be in a disconnected region when passed to the function, and the
378
378
parameter value must be in a disconnected region when the function
379
- returns. Inside the function, the ` sendable inout` parameter can be merged with
379
+ returns. Inside the function, the ` sending inout` parameter can be merged with
380
380
actor-isolated callees or further sent as long as the parameter is
381
381
re-assigned a value in a disconnected region upon function exit.
382
382
383
- ### Ownership convention for ` sendable ` parameters
383
+ ### Ownership convention for ` sending ` parameters
384
384
385
- When a call passes an argument to a ` sendable ` parameter, the caller cannot
386
- use the argument value again after the callee returns. By default ` sendable `
385
+ When a call passes an argument to a ` sending ` parameter, the caller cannot
386
+ use the argument value again after the callee returns. By default ` sending `
387
387
on a function parameter implies that the callee consumes the parameter. Like
388
- ` consuming ` parameters, a ` sendable ` parameter can be re-assigned inside
389
- the callee. Unlike ` consuming ` parameters, ` sendable ` parameters do not
388
+ ` consuming ` parameters, a ` sending ` parameter can be re-assigned inside
389
+ the callee. Unlike ` consuming ` parameters, ` sending ` parameters do not
390
390
have no-implicit-copying semantics.
391
391
392
392
To opt into no-implicit-copying semantics or to change the default ownership
393
- convention, ` sendable ` may also be paired with an explicit ` consuming ` or
393
+ convention, ` sending ` may also be paired with an explicit ` consuming ` or
394
394
` borrowing ` ownership modifier:
395
395
396
396
``` swift
397
- func sendableConsuming (_ x : consuming sendable T) { ... }
398
- func sendableBorrowing (_ x : borrowing sendable T) { ... }
397
+ func sendingConsuming (_ x : consuming sending T) { ... }
398
+ func sendingBorrowing (_ x : borrowing sending T) { ... }
399
399
```
400
400
401
401
Note that an explicit ` borrowing ` annotation always implies no-implicit-copying,
402
402
so there is no way to change the default ownership convention of a
403
- ` sendable ` parameter without also opting into no-implicit-copying semantics.
403
+ ` sending ` parameter without also opting into no-implicit-copying semantics.
404
404
405
405
### Adoption in the Concurrency library
406
406
407
407
There are several APIs in the concurrency library that send a parameter across
408
408
isolation boundaries and don't need the full guarnatees of ` Sendable ` . These
409
- APIs will instead adopt ` sendable ` parameters:
409
+ APIs will instead adopt ` sending ` parameters:
410
410
411
411
* ` CheckedContinuation.resume(returning:) `
412
412
* ` Async{Throwing}Stream.Continuation.yield(_:) `
@@ -418,11 +418,11 @@ because `UnsafeContinuation` deliberately opts out of correctness checking.
418
418
419
419
## Source compatibility
420
420
421
- In the Swift 5 language mode, ` sendable ` diagnostics are suppressed under
421
+ In the Swift 5 language mode, ` sending ` diagnostics are suppressed under
422
422
minimal concurrency checking, and diagnosed as warnings under strict concurrency
423
423
checking. The diagnostics are errors in the Swift 6 language mode, as shown in
424
424
the code examples in this proposal. This diagnostic behavior based on language
425
- mode allows ` sendable ` to be adopted in existing Concurrency APIs including
425
+ mode allows ` sending ` to be adopted in existing Concurrency APIs including
426
426
` CheckedContinuation ` .
427
427
428
428
## ABI compatibility
@@ -431,21 +431,21 @@ This proposal does not change how any existing code is compiled.
431
431
432
432
## Implications on adoption
433
433
434
- Adding ` sendable ` to a parameter is more restrictive at the caller, and
435
- more expressive in the callee. Adding ` sendable ` to a result type is more
434
+ Adding ` sending ` to a parameter is more restrictive at the caller, and
435
+ more expressive in the callee. Adding ` sending ` to a result type is more
436
436
restrictive in the callee, and more expressive in the caller.
437
437
438
- For libraries with library evolution, ` sendable ` changes name mangling, so
438
+ For libraries with library evolution, ` sending ` changes name mangling, so
439
439
any adoption must preserve the mangling using ` @_silgen_name ` . Adoping
440
- ` sendable ` must preserve the ownership convention of parameters; no
440
+ ` sending ` must preserve the ownership convention of parameters; no
441
441
additional annotation is necessary if the parameter is already (implicitly or
442
442
explicitly) ` consuming ` .
443
443
444
444
## Future directions
445
445
446
446
### ` Disconnected ` types
447
447
448
- ` sendable ` requires parameter and result values to be in a disconnected
448
+ ` sending ` requires parameter and result values to be in a disconnected
449
449
region at the function boundary, but there is no way to preserve that a value
450
450
is in a disconnected region through stored properties, collections, function
451
451
calls, etc. To preserve that a value is in a disconnected region through the
@@ -455,11 +455,11 @@ would conform to `Sendable`, constructing a `Disconnected` instance would
455
455
require the value it wraps to be in a disconnected region, and a value of type
456
456
` Disconnected ` can never be merged into another isolation region.
457
457
458
- This would enable important patterns that take a ` sendable T` parameter, store
458
+ This would enable important patterns that take a ` sending T` parameter, store
459
459
the value in a collection of ` Disconnected<T> ` , and later remove values from the
460
- collection and return them as ` sendable T` results. This would allow some
460
+ collection and return them as ` sending T` results. This would allow some
461
461
` AsyncSequence ` types to return non-` Sendable ` typed buffered elements as
462
- ` sendable ` without resorting to unsafe opt-outs in the implementation.
462
+ ` sending ` without resorting to unsafe opt-outs in the implementation.
463
463
464
464
## Alternatives considered
465
465
0 commit comments