Skip to content

Commit

Permalink
Simplified implicit lifetime dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
atrick committed Aug 1, 2024
1 parent fa1e5d1 commit c6131aa
Showing 1 changed file with 17 additions and 26 deletions.
43 changes: 17 additions & 26 deletions proposals/NNNN-lifetime-dependency.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ This is a key requirement for the `Span` type (previously called `BufferView`) b
**Edited** (July 31, 2024)

- New alternative considered: @lifetime annotation
- Simplified implicit lifetime dependencies

#### See Also

Expand Down Expand Up @@ -256,12 +257,11 @@ init(arg: <parameter-convention> ArgType) -> dependsOn(arg) Self

### Implicit Lifetime Dependencies

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 one “obvious” source for the dependency.
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.

In particular, 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:
#### Self dependence

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:

```swift
struct NonescapableType: ~Escapable { ... }
Expand All @@ -285,25 +285,21 @@ struct NEStruct: ~Escapable {
}
```

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.
For example:
#### Same-type dependence

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

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

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

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

We expect these implicit inferences to cover most cases, with the explicit form only occasionally being necessary in practice.
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.

### Dependent parameters

Expand Down Expand Up @@ -671,16 +667,11 @@ The implications of mutation modifiers and argument type on the resulting lifeti

### Inference Rules

If there is no explicit lifetime dependency, we will automatically infer one according to the following rules:
If there is no explicit lifetime dependency on the result of a method or function, we will automatically infer one according to the following rules:

**For methods where the return value is nonescapable**, we will infer a dependency against self, depending on the mutation type of the function.
Note that this is not affected by the presence, type, or modifier of any other arguments to the method.

**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:
- is nonescapable, or
- is non-BitwiseCopyable and has an explicit `borrowing`, or `inout` convention
**For methods and functions where the return value is nonescapable**, we will infer a copied lifetime dependency on all parameters of the same type.

In this case, the compiler will infer a dependency on the unique argument identified by these conditions.
**For methods where the return value is nonescapable and self is not the same type as the result**, we will infer a dependency against self, depending on the mutation type of the function. Note that this is not affected by the presence, type, or modifier of any other arguments to the method.

**In no other case** will a function, method, or initializer implicitly gain a lifetime dependency.
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.
Expand Down

0 comments on commit c6131aa

Please sign in to comment.