@@ -14,6 +14,8 @@ To reduce the burden of manually adding such annotations, we also propose inferr
14
14
15
15
This is a key requirement for the ` StorageView ` type (previously called ` BufferView ` ) being discussed elsewhere, and is closely related to the proposal for ` ~Escapable ` types.
16
16
17
+ ** Edited** (Apr 12, 2024): Changed ` @dependsOn ` to ` dependsOn ` to match the current implementation.
18
+
17
19
#### See Also
18
20
19
21
* ** TODO** : *** * Forum thread discussing this proposal
@@ -97,16 +99,16 @@ Our proposal would allow you to declare an `array.bufferReference()` method as f
97
99
98
100
``` swift
99
101
extension Array {
100
- borrowing func bufferReference () -> @ dependsOn (self ) BufferReference<Element > {
102
+ borrowing func bufferReference () -> dependsOn(self) BufferReference<Element > {
101
103
... construct a BufferReference ...
102
104
}
103
105
}
104
106
```
105
107
106
- The annotation ` @ dependsOn(self)` here indicates that the returned value must not outlive the array that produced it.
108
+ The annotation ` dependsOn(self) ` here indicates that the returned value must not outlive the array that produced it.
107
109
Conceptually, it is a continuation of the function's borrowing access:
108
110
the array is being borrowed by the function while the function executes and then continues to be borrowed by the ` BufferReference ` for as long as the return value exists.
109
- Specifically, the ` @ dependsOn(self)` annotation in this example informs the compiler that:
111
+ Specifically, the ` dependsOn(self) ` annotation in this example informs the compiler that:
110
112
111
113
* The array must not be destroyed until after the ` BufferReference<Element> ` is destroyed.
112
114
This ensures that use-after-free cannot occur.
@@ -119,13 +121,13 @@ Let’s consider another hypothetical type: a `MutatingBufferReference<T>` type
119
121
Here's one way such a value might be produced:
120
122
121
123
``` swift
122
- func mutatingBufferReference (to : inout Array , count : Int ) -> @ dependsOn (to) MutatingBufferReference<Element > {
124
+ func mutatingBufferReference (to : inout Array , count : Int ) -> dependsOn(to) MutatingBufferReference<Element > {
123
125
... construct a MutatingBufferReference ...
124
126
}
125
127
```
126
128
127
129
We’ve written this example as a free function rather than as a method to show how this annotation syntax can be used to express constraints that apply to a particular argument.
128
- The ` @ dependsOn(to)` annotation indicates that the returned value depends on the argument named ` to ` .
130
+ The ` dependsOn(to) ` annotation indicates that the returned value depends on the argument named ` to ` .
129
131
Because ` count ` is not mentioned in the lifetime dependency, that argument does not participate.
130
132
Similar to the previous example:
131
133
@@ -143,7 +145,7 @@ Here's a typical example that constructs a new `BufferReference` from an existin
143
145
``` swift
144
146
struct BufferReference <T >: ~ Escapable {
145
147
...
146
- consuming func drop (_ : Int ) -> @ dependsOn (self ) BufferReference<T> { ... }
148
+ consuming func drop (_ : Int ) -> dependsOn(self) BufferReference<T> { ... }
147
149
...
148
150
}
149
151
```
@@ -176,7 +178,7 @@ The syntax is somewhat different for functions and methods, though the basic rul
176
178
** Functions:** A simple function with an explicit lifetime dependency annotation generally takes this form:
177
179
178
180
``` swift
179
- func f (arg : <parameter-convention> ArgType) -> @ dependsOn (arg) ResultType
181
+ func f (arg : <parameter-convention> ArgType) -> dependsOn(arg) ResultType
180
182
```
181
183
182
184
Where
@@ -195,21 +197,21 @@ Also, access to the argument will be restricted for the lifetime of the result f
195
197
* A ` consuming ` parameter-convention is illegal, since that ends the lifetime of the argument immediately.
196
198
197
199
If the ` ArgType ` is nonescapable, then it can have a pre-existing lifetime dependency.
198
- In this case, the semantics of ` @ dependsOn()` are slightly different:
200
+ In this case, the semantics of ` dependsOn() ` are slightly different:
199
201
* A ` consuming ` parameter-convention will copy the lifetime dependency from the argument to the result
200
202
* A ` borrowing ` or ` inout ` parameter-convention can either copy the lifetime dependency or create a new scoped lifetime dependency.
201
203
In this case, for reasons explained earlier, we default to copying the lifetime dependency.
202
204
If a scoped lifetime dependency is needed, it can be explicitly requested by adding the ` scoped ` keyword:
203
205
204
206
``` swift
205
- func f (arg : borrowing ArgType) -> @ dependsOn (scoped arg) ResultType
207
+ func f (arg : borrowing ArgType) -> dependsOn(scoped arg) ResultType
206
208
```
207
209
208
210
** Methods:** Similar rules apply to ` self ` lifetime dependencies on methods.
209
211
Given a method of this form:
210
212
211
213
``` swift
212
- < mutation- modifier> func method (... args ...) -> @ dependsOn (self ) ResultType
214
+ < mutation- modifier> func method (... args ...) -> dependsOn(self) ResultType
213
215
```
214
216
215
217
The behavior depends as above on the mutation-modifier and whether the defining type is escapable or nonescapable.
@@ -219,7 +221,7 @@ In this case, we use the same rules as for “Functions” above
219
221
by using the convention that initializers can be viewed as functions that return ` Self ` :
220
222
221
223
``` swift
222
- init (arg : <parameter-convention> ArgType) -> @ dependsOn(arg) Self
224
+ init (arg : <parameter-convention> ArgType) -> dependsOn(arg) Self
223
225
```
224
226
225
227
### Implicit Lifetime Dependencies
@@ -234,22 +236,22 @@ As above, the details vary depending on whether `self` is escapable or nonescapa
234
236
``` swift
235
237
struct NonescapableType : ~ Escapable { ... }
236
238
struct EscStruct {
237
- func f1 (...) -> /* @ dependsOn(self) */ NonescapableType
238
- borrowing func f2 (...) -> /* @ dependsOn(self) */ NonescapableType
239
- mutating func f3 (...) -> /* @ dependsOn(self) */ NonescapableType
239
+ func f1 (...) -> /* dependsOn(self) */ NonescapableType
240
+ borrowing func f2 (...) -> /* dependsOn(self) */ NonescapableType
241
+ mutating func f3 (...) -> /* dependsOn(self) */ NonescapableType
240
242
241
243
// 🛑 Error: there is no valid lifetime dependency for
242
244
// a consuming method on an `Escapable` type
243
245
consuming func f4 (...) -> NonescapableType
244
246
}
245
247
246
248
struct NEStruct : ~ Escapable {
247
- func f1 (...) -> /* @ dependsOn(self) */ NonescapableType
248
- borrowing func f2 (...) -> /* @ dependsOn(self) */ NonescapableType
249
- mutating func f3 (...) -> /* @ dependsOn(self) */ NonescapableType
249
+ func f1 (...) -> /* dependsOn(self) */ NonescapableType
250
+ borrowing func f2 (...) -> /* dependsOn(self) */ NonescapableType
251
+ mutating func f3 (...) -> /* dependsOn(self) */ NonescapableType
250
252
251
253
// Note: A copied lifetime dependency is legal here
252
- consuming func f4 (...) -> /* @ dependsOn(self) */ NonescapableType
254
+ consuming func f4 (...) -> /* dependsOn(self) */ NonescapableType
253
255
}
254
256
```
255
257
@@ -260,10 +262,10 @@ For example:
260
262
struct NEType : ~ Escapable { ... }
261
263
262
264
// If there is only one argument with an explicit parameter convention:
263
- func f (..., arg1 : borrowing Type1, ...) -> /* @ dependsOn(arg1) */ NEType
265
+ func f (..., arg1 : borrowing Type1, ...) -> /* dependsOn(arg1) */ NEType
264
266
265
267
// Or there is only one argument that is `~Escapable`:
266
- func g (..., arg2 : NEType, ...) -> /* @ dependsOn(arg2) */ NEType
268
+ func g (..., arg2 : NEType, ...) -> /* dependsOn(arg2) */ NEType
267
269
268
270
// If there are multiple possible arguments that we might depend
269
271
// on, we require an explicit dependency:
@@ -312,7 +314,7 @@ This modifies *function-result* in the Swift grammar as follows:
312
314
> * function-signature* → * parameter-clause* ** ` async ` *** ?* ** ` rethrows ` ** * function-result** ?* \
313
315
> * function-result* → ** ` -> ` ** * attributes?* * lifetime-modifiers?* * type* \
314
316
> * lifetime-modifiers* → ** ` -> ` ** * lifetime-modifier* * lifetime-modifiers?* \
315
- > * lifetime-modifier* → ** ` -> ` ** ** ` @ dependsOn` ** ** ` ( ` ** * lifetime-dependent* ** ` ) ` ** \
317
+ > * lifetime-modifier* → ** ` -> ` ** ** ` dependsOn ` ** ** ` ( ` ** * lifetime-dependent* ** ` ) ` ** \
316
318
> * lifetime-dependent* → ** ` -> ` ** ** ` self ` ** | * local-parameter-name* | ** ` scoped self ` ** | ** ` scoped ` ** * local-parameter-name*
317
319
>
318
320
@@ -356,8 +358,8 @@ The return type must be exactly the token `Self` or the token sequence `Self?` i
356
358
357
359
``` swift
358
360
struct S {
359
- init (arg1 : Type1) -> @ dependsOn(arg1) Self
360
- init? (arg2 : Type2) -> @ dependsOn(arg2) Self?
361
+ init (arg1 : Type1) -> dependsOn(arg1) Self
362
+ init? (arg2 : Type2) -> dependsOn(arg2) Self?
361
363
}
362
364
```
363
365
@@ -440,11 +442,11 @@ The notation for an explicit lifetime dependency on a property might look like t
440
442
441
443
``` swift
442
444
struct Container {
443
- var view: @ dependsOn (self ) ReturnType { get }
445
+ var view: dependsOn(self) ReturnType { get }
444
446
}
445
447
446
448
struct Type1 : ~ Escapable {
447
- var childView: @ dependsOn (self ) Type1 { get }
449
+ var childView: dependsOn(self) Type1 { get }
448
450
}
449
451
```
450
452
@@ -464,7 +466,7 @@ A caller may need assurance that a callee will honor a lifetime dependency betwe
464
466
For example, if a function is going to destroy a container and a reference to that container in the process of computing some result,
465
467
it needs to guarantee that the reference is destroyed before the container:
466
468
``` swift
467
- func f (container : consuming ContainerType, ref : @ dependsOn (container) consuming RefType) -> ResultType
469
+ func f (container : consuming ContainerType, ref : dependsOn(container) consuming RefType) -> ResultType
468
470
```
469
471
470
472
#### Lifetime Dependencies for Tuples
@@ -481,7 +483,7 @@ We expect to address this in the near future in a separate proposal.
481
483
It should be possible to return containers with collections of lifetime-constrained elements.
482
484
For example, a container may want to return a partition of its contents:
483
485
``` swift
484
- borrowing func chunks (n : Int ) -> @ dependsOn (self ) SomeList<@ dependsOn (self ) StorageView<UInt8 >>
486
+ borrowing func chunks (n : Int ) -> dependsOn(self) SomeList<dependsOn(self) StorageView<UInt8 >>
485
487
```
486
488
We're actively looking into ways to support these more involved cases and expect to address this in a future proposal.
487
489
@@ -491,7 +493,7 @@ Internally, the implementation records dependencies based on the parameter index
491
493
This could be exposed as an alternate spelling if there were sufficient demand.
492
494
493
495
``` swift
494
- func f (arg1 : Type1, arg2 : Type2, arg3 : Type3) -> @ dependsOn (0 ) ReturnType
496
+ func f (arg1 : Type1, arg2 : Type2, arg3 : Type3) -> dependsOn(0) ReturnType
495
497
```
496
498
497
499
## Acknowledgements
0 commit comments