Skip to content

Commit ac43daa

Browse files
committed
Simplified implicit lifetime dependencies
Added the same-type inference rule.
1 parent 3b6439c commit ac43daa

File tree

1 file changed

+36
-25
lines changed

1 file changed

+36
-25
lines changed

proposals/NNNN-lifetime-dependency.md

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ This is a key requirement for the `Span` type (previously called `BufferView`) b
4646

4747
- New alternative considered: @lifetime annotation
4848
- New alternative considered: where clause
49+
- Simplified implicit lifetime dependencies and added same-type rule
4950

5051
#### See Also
5152

@@ -257,12 +258,11 @@ init(arg: <parameter-convention> ArgType) -> dependsOn(arg) Self
257258

258259
### Implicit Lifetime Dependencies
259260

260-
The syntax above allows developers to explicitly annotate lifetime dependencies in their code.
261-
But because the possibilities are limited, we can usually allow the compiler to infer a suitable dependency.
262-
The detailed rules are below, but generally we require that the return type be nonescapable and that there be one “obvious” source for the dependency.
261+
The syntax above allows developers to explicitly annotate lifetime dependencies in their code. But because the possibilities are limited, we can usually allow the compiler to infer a suitable dependency. The detailed rules are below, but generally we require that the return type be nonescapable and that there be an “obvious” source for the dependency.
263262

264-
In particular, we can infer a lifetime dependency on `self` for any method that returns a nonescapable value.
265-
As above, the details vary depending on whether `self` is escapable or nonescapable:
263+
#### Self dependence
264+
265+
We can infer a lifetime dependency on `self` for any method that returns a nonescapable value. As above, the details vary depending on whether `self` is escapable or nonescapable:
266266

267267
```swift
268268
struct NonescapableType: ~Escapable { ... }
@@ -286,25 +286,21 @@ struct NEStruct: ~Escapable {
286286
}
287287
```
288288

289-
For free or static functions or initializers, we can infer a lifetime dependency when the return value is nonescapable and there is only one obvious argument that can serve as the source of the dependency.
290-
For example:
289+
#### Same-type dependence
291290

292-
```swift
293-
struct NEType: ~Escapable { ... }
291+
For any function or method that returns a nonescapable type, we infer a copied lifetime dependency on all parameters of the same type.
294292

295-
// If there is only one argument with an explicit parameter convention:
296-
func f(..., arg1: borrowing Type1, ...) -> /* dependsOn(arg1) */ NEType
293+
`func foo<T: ~Escapable, U: ~Escapable, R: ~Escapable>(x: T, y: U) -> R { ... }`
297294

298-
// Or there is only one argument that is `~Escapable`:
299-
func g(..., arg2: NEType, ...) -> /* dependsOn(arg2) */ NEType
295+
implies:
300296

301-
// If there are multiple possible arguments that we might depend
302-
// on, we require an explicit dependency:
303-
// 🛑 Cannot infer lifetime dependency since `arg1` and `arg2` are both candidates
304-
func g(... arg1: borrowing Type1, arg2: NEType, ...) -> NEType
297+
```
298+
-> dependsOn(x) where R == T
299+
-> dependsOn(y) where R == U
300+
-> dependsOn(x, y) where R == T == U
305301
```
306302

307-
We expect these implicit inferences to cover most cases, with the explicit form only occasionally being necessary in practice.
303+
This is particularly helpful for Generic APIs. With this rule, indicating that a generic parameter is `~Escapable` should usually be sufficient to infer the correct lifetime dependence.
308304

309305
### Dependent parameters
310306

@@ -672,20 +668,35 @@ The implications of mutation modifiers and argument type on the resulting lifeti
672668

673669
### Inference Rules
674670

675-
If there is no explicit lifetime dependency, we will automatically infer one according to the following rules:
671+
If there is no explicit lifetime dependency on the nonescapable result of a method or function, we will attempt to infer dependencies automatically according the following rules:
676672

677-
**For methods where the return value is nonescapable**, we will infer a dependency against self, depending on the mutation type of the function.
678-
Note that this is not affected by the presence, type, or modifier of any other arguments to the method.
673+
1. For methods where the return value is nonescapable, we will infer a dependency against `self`. If `self` is nonescapable, then we infer a copying dependency. If `self` is escapable, and the method is `borrowing` or `mutating`, then we infer a scoped dependency.
679674

680-
**For a free or static functions or initializers with at least one argument,** we will infer a lifetime dependency when the return value is nonescapable and exactly one argument that satisfies any of the following:
681-
- is nonescapable, or
682-
- is non-BitwiseCopyable and has an explicit `borrowing`, or `inout` convention
675+
2. For methods, functions, and initializers where the return value is nonescapable, we infer a copied lifetime dependency on all parameters of the same (nonescapable) type, including the implicit `self` parameter.
683676

684-
In this case, the compiler will infer a dependency on the unique argument identified by these conditions.
677+
3. For functions and initializers that have a nonescapable return value and a single parameter, we infer dependence on that parameter. If the parameter is nonescapable, then we infer a copying dependency; otherwise, we infer a scoped dependency.
678+
679+
For all inference rules, the type of dependence is the same as an explicit `dependsOn(argument)` on the same argument without any `scoped` qualifier based on the argument's type.
685680

686681
**In no other case** will a function, method, or initializer implicitly gain a lifetime dependency.
687682
If a function, method, or initializer has a nonescapable return value, does not have an explicit lifetime dependency annotation, and does not fall into one of the cases above, then that will be a compile-time error.
688683

684+
We infer dependencies according to all applicable rules. Here, both rule #1 and #2 apply:
685+
686+
```
687+
struct NE: ~Escapable { ... }
688+
struct E {
689+
func foo(ne: NE) -> /* dependsOn(self, ne) */ NE
690+
}
691+
```
692+
693+
Here, both rule #2 and #3 apply:
694+
695+
```
696+
struct NE {
697+
init(ne: NE) -> /* dependsOn(ne) */ Self
698+
}
699+
```
689700

690701
### Dependency semantics by example
691702

0 commit comments

Comments
 (0)