You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/ReferenceGuides/LifetimeAnnotation.md
+76-74Lines changed: 76 additions & 74 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,58 +18,73 @@ The `@lifetime` annotation is enforced both in the body of the function and at e
18
18
19
19
## Default lifetimes
20
20
21
-
The Swift 6.2 compiler provided default `@_lifetime` behavior whenever it can do so without ambiguity. Often, despite ambiguity, an "obvious" default exists, but we wanted to introduce defaults slowly after developers have enough experience to inform discussion about them. This document tracks the current state of the implementation as it progresses from the original 6.2 implementation. Corresponding tests are in `test/Sema/lifetime_depend_infer.swift`; searching for "DEFAULT:" highlights the rules defined below...
21
+
The Swift 6.2 compiler provided default `@_lifetime` behavior whenever it can do so without ambiguity. Often, despite ambiguity, an obvious default exists, but we wanted to introduce defaults slowly after developers have enough experience to inform discussion about them. This document tracks the current state of the implementation as it progresses from the original 6.2 implementation. Corresponding tests are in `test/Sema/lifetime_depend_infer.swift`; searching for "DEFAULT:" highlights the rules defined below...
22
22
23
-
### Single parameter default rule
23
+
### Same-type default lifetime
24
24
25
-
Given a function or method that returns a non-Escapable result:
25
+
Given a function declaration:
26
26
27
-
- Default to `@_lifetime(<scope> a)` for a `~Escapable` result on functions with a single parameter `a`.
27
+
`func foo(..., a: A, ...) -> R { ... }`
28
28
29
-
- Default to `@_lifetime(<scope> self)` for a `~Escapable` result on methods with no parameters.
29
+
Where `R: ~Escapable`, `A == R`, and `a` is not an `inout` parameter, default to `@_lifetime(copy a)`.
30
+
For non-mutating methods, the same rule applies to implicit `Self` parameter.
This handles the obvious cases in which both the parameter and result are `~Escapable`. For example:
37
33
38
-
[^1]: When the parameter is `BitwiseCopyable`, such as an integer or unsafe pointer, the single parameter default rule applies to function parameters but not to the implicit `self` parameter. Depending on a `BitwiseCopyable` value is a convenience for APIs that construct span-like values from an `UnsafePointer` passed as an argument. This creates a dependency on a local copy of the pointer variable with subtle semantics. User-defined `BitwiseCopyable` structs should generally avoid such subtle lifetime dependencies. If needed, the author of the data type should explicitly opt into them.
34
+
```swift
35
+
extensionSpan {
36
+
/* DEFAULT: @_lifetime(copy self) */
37
+
funcextracting(droppingLastk: Int) ->Self { ... }
38
+
}
39
+
```
39
40
40
-
[^2]: When the single parameter is also `~Escapable`, the result must depend on it, but the dependency may either be scoped (`borrow` or `&`) or it may be copied (`copy`). `copy` is the obvious choice when the parameter and result are the same type, but it is not always correct. Furthermore, a lifetime dependency can only be copied from a generic type when result as the same generic type. This case is therefore handled by same-type default lifetime (discussed below) rather than as a default `@_lifetime` rule.
41
+
#### Generic same-type default lifetime
41
42
42
-
Examples:
43
+
The same-type default lifetime rule described above is convenient for nominal types but essential for generic type parameters.
43
44
44
-
```swift
45
-
structA: Escapable {
46
-
let obj: AnyObject// ~BitwiseCopyable
47
-
}
48
-
structNE: ~Escapable {...}
45
+
Given a generic function declaration:
49
46
50
-
/* DEFAULT: @_lifetime(borrow a) */
51
-
funconeParam_NEResult(a: A) -> NE
47
+
`func foo<A, R>(..., a: A, ...) -> R { ... }`
52
48
53
-
/* DEFAULT: @_lifetime(&a) */
54
-
funconeInoutParam_NEResult(a: inout A) -> NE
49
+
The same-type default lifetime rule applies to the types in the function declaration's generic context just as it did for nominal types in the previous example. So, again, if type resolution determines `R: ~Escapable` and `A == R`, then `@_lifetime(copy a)` will be default.
55
50
56
-
extensionA /* Self: Escapable */ {
57
-
/* DEFAULT: @_lifetime(borrow self) */
58
-
funcnoParam_NEResult() -> NE
51
+
Unlike nominal types, the programmer is not allowed to explicitly declare a lifetime dependency, `@_lifetime(copy
52
+
a)`, unless the argument and result types are equivalent (`A == R`). Copying a lifetime dependency from one value to another requires that both values are non-Escapable. Generic types are conditionally non-Escapable (their lifetime dependencies are type-erased), so type equivalence is the only way to ensure that both values are non-Escapable under the same conditions.
59
53
60
-
/* DEFAULT: @_lifetime(&self) */
61
-
mutatingfuncmutating_noParam_NEResult() -> NE
54
+
Here we see how same-type lifetime requirement applies to type substitution and associated types:
55
+
56
+
```swift
57
+
protocolP {
58
+
associatedtypeT: ~Escapable
59
+
}
60
+
61
+
protocolQ {
62
+
associatedtypeU: ~Escapable
63
+
}
64
+
65
+
structS<A: P, B: Q> {
66
+
/* OK: @_lifetime(copy a) is valid and default */
67
+
funcfoo(a: A.T) -> B.U where A.T == B.U
62
68
}
63
69
```
64
70
65
-
### Implicit initializer and setter defaults
71
+
Note that lifetime dependencies are resolved at function declaration time, which determines the function's type. The generic context at the point of function invocation is not considered. For example, the following declaration of `foo` is invalid, because it's argument and results types don't match at the point of declaration, even though the argument and result do have the same type when invoked inside `bar`:
66
72
67
-
An implicit setter of a `~Escapable` stored property defaults to `@_lifetime(self: copy self, copy newValue)`. This is always correct because the setter simply assigns the stored property to the newValue. Assigning a `~Escapable` variable copies the lifetime dependency.
73
+
```swift
74
+
structS<T: ~Escapable, U: ~Escapable> {
75
+
staticfuncfoo(a: T) -> U // ERROR: missing lifetime dependency
76
+
}
68
77
69
-
Similarly, an implicit initializer of a non-Escapable struct defaults to `@_lifetime(self: copy arg)` if all of the initializer arguments are `~Escapable`. This is equivalent to assigning each `~Escapable` stored property. If, however, any initializer arguments are `Escapable`, then no default lifetime is provided unless it is the sole argument, in which case the single parameter rule applies.
78
+
/* OK: @_lifetime(copy a) is valid and default */
79
+
funcbar<T: ~Escapable>(a: T) -> T {
80
+
S<T, T>.foo(a: a) // The same-type rule is satisfied in this context, but 'foo's declaration is invalid.
81
+
}
82
+
```
70
83
71
84
### `inout` parameter default rule
72
85
86
+
The following `inout` parameter default rule directly follows from the general same-type default rule above. We descrive it as a separate rule here only because the behavior is important and would otherwise be nonobvious:
87
+
73
88
- Default to `@_lifetime(a: copy a)` for all `inout` parameters where `a` is `~Escapable`.
74
89
75
90
- Default to `@_lifetime(self: copy self)` on `mutating` methods where `self` is `~Escapable`.
The same-type default lifetime rule applies to the types in the function declaration's generic context just as it did for nominal types in the previous example. So, again, if type resolution determines `R: ~Escapable` and `A == R`, then `@_lifetime(copy a)` will be default.
155
+
[^1]: When the parameter is `BitwiseCopyable`, such as an integer or unsafe pointer, the single parameter default rule applies to function parameters but not to the implicit `self` parameter. Depending on a `BitwiseCopyable` value is a convenience for APIs that construct span-like values from an `UnsafePointer` passed as an argument. This creates a dependency on a local copy of the pointer variable with subtle semantics. User-defined `BitwiseCopyable` structs should generally avoid such subtle lifetime dependencies. If needed, the author of the data type should explicitly opt into them.
152
156
153
-
Unlike nominal types, the programmer is not allowed to explicitly declare a lifetime dependency, `@_lifetime(copy
154
-
a)`, unless the argument and result types are equivalent (`A == R`). Copying a lifetime dependency from one value to another requires that both values are non-Escapable. Generic types are conditionally non-Escapable (their lifetime dependencies are type-erased), so type equivalence is the only way to ensure that both values are non-Escapable under the same conditions.
157
+
[^2]: When the single parameter is also `~Escapable`, the result must depend on it, but the dependency may either be scoped (`borrow` or `&`) or it may be copied (`copy`). `copy` is the obvious choice when the parameter and result are the same type, but it is not always correct. Furthermore, a lifetime dependency can only be copied from a generic type when result as the same generic type. This case is therefore handled by same-type default lifetime (discussed below) rather than as a default `@_lifetime` rule.
155
158
156
-
Here we see how same-type lifetime requirement applies to type substitution and associated types:
159
+
Examples:
157
160
158
161
```swift
159
-
protocolP {
160
-
associatedtypeT: ~Escapable
162
+
structA: Escapable{
163
+
let obj: AnyObject// ~BitwiseCopyable
161
164
}
165
+
structNE: ~Escapable {...}
162
166
163
-
protocolQ {
164
-
associatedtypeU: ~Escapable
165
-
}
167
+
/* DEFAULT: @_lifetime(borrow a) */
168
+
funconeParam_NEResult(a: A) -> NE
166
169
167
-
structS<A: P, B: Q> {
168
-
/* OK: @_lifetime(copy a) is valid and default */
169
-
funcfoo(a: A.T) -> B.U where A.T == B.U
170
+
/* DEFAULT: @_lifetime(&a) */
171
+
funconeInoutParam_NEResult(a: inout A) -> NE
172
+
173
+
extensionA /* Self: Escapable */ {
174
+
/* DEFAULT: @_lifetime(borrow self) */
175
+
funcnoParam_NEResult() -> NE
176
+
177
+
/* DEFAULT: @_lifetime(&self) */
178
+
mutatingfuncmutating_noParam_NEResult() -> NE
170
179
}
171
180
```
172
181
173
-
Note that lifetime dependencies are resolved at function declaration time, which determines the function's type. The generic context at the point of function invocation is not considered. For example, the following declaration of `foo` is invalid, because it's argument and results types don't match at the point of declaraion, even though the argument and result do have the same type when invoked inside `bar`:
182
+
### Implicit initializer and setter defaults
174
183
175
-
```swift
176
-
structS<T: ~Escapable, U: ~Escapable> {
177
-
staticfuncfoo(a: T) -> U // ERROR: missing lifetime dependency
178
-
}
184
+
An implicit setter of a `~Escapable` stored property defaults to `@_lifetime(self: copy self, copy newValue)`. This is always correct because the setter simply assigns the stored property to the newValue. Assigning a `~Escapable` variable copies the lifetime dependency.
179
185
180
-
/* OK: @_lifetime(copy a) is valid and default */
181
-
funcbar<T: ~Escapable>(a: T) -> T {
182
-
S<T, T>.foo(a: a) // The same-type rule is satisfied in this context, but 'foo's declaration is invalid.
183
-
}
184
-
```
186
+
Similarly, an implicit initializer of a non-Escapable struct defaults to `@_lifetime(self: copy arg)` if all of the initializer arguments are `~Escapable`. This is equivalent to assigning each `~Escapable` stored property. If, however, any initializer arguments are `Escapable`, then no default lifetime is provided unless it is the sole argument, in which case the single parameter rule applies.
0 commit comments