Skip to content

Commit 3b6439c

Browse files
committed
Add Alternative: @Lifetime annotation and where clause.
1 parent f337c2f commit 3b6439c

File tree

1 file changed

+67
-1
lines changed

1 file changed

+67
-1
lines changed

proposals/NNNN-lifetime-dependency.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ This is a key requirement for the `Span` type (previously called `BufferView`) b
4242
- Updated future direction: component lifetime syntax
4343
- New example: Escapable properties in a nonescapable type
4444

45+
**Edited** (July 31, 2024)
46+
47+
- New alternative considered: @lifetime annotation
48+
- New alternative considered: where clause
49+
4550
#### See Also
4651

4752
* [Forum discussion of Non-Escapable Types and Lifetime Dependency](https://forums.swift.org/t/pitch-non-escapable-types-and-lifetime-dependency)
@@ -878,14 +883,75 @@ The currently proposed `dependsOn` spelling was chosen to convey the direction o
878883

879884
func foo(a: A, b: B) -> dependsOn(a) R
880885

881-
This does, however, introduce compound keyword. Alternatively, we could use a simpler `lifetime` keyword, which better matches the feature description. The general syntax would then be:
886+
This does, however, introduce a keyword with a compound name. Alternatively, we could use a simpler `lifetime` keyword, which better matches the feature description. The general syntax would then be:
882887

883888
> **lifetime**(*target*: [scoped] *source*)
884889
885890
APIs with ambiguous depenencies would then typically be spelled:
886891

887892
func foo(a: A, b: B) -> lifetime(a) R
888893

894+
### @lifetime annotation
895+
896+
Instead of committing to a final, lightweight syntax, we can start with a single `@lifetime` annotation. It would take this form:
897+
898+
```
899+
@lifetime(target1.component: [copy|mutate|borrow] source1.component)
900+
@lifetime(target2.component: [copy|mutate|borrow] source2.component)
901+
func foo(...)
902+
```
903+
904+
`target` can be `self`, any parameter name, or, most commonly an empty string which implies the function result. `source` can be `self` or any parameter name. The most common usage would be:
905+
906+
```
907+
@lifetime(copy arg)
908+
func foo(arg: Arg1) -> R {}
909+
```
910+
911+
The `.component` qualifier is only relevant once we have component lifetimes. See the "Component lifetime" section below.
912+
913+
An annotation has some advantages over a lighter-weight type modifier sytax:
914+
915+
The `@` sigil is helpful to distinguish lifetime dependence information from regular function syntax.
916+
917+
A position-independent annotation has an advantage that the fully expressive syntax is more self-evident. This makes it easier to educate reviewers about what is possible with the syntax.
918+
919+
The type modifier can occur in any type position within a function signature, in including before the `func` keyword for the 'self' type. This has potential readability problems when it comes to more complicated cases. Nested parentheses (`dependsOn(...)`) that can occur anywhere in the signature are visually confusing.
920+
921+
In the future, the single `@lifetime` annotation could be a useful modifier for other kinds declarations such as types and properties:
922+
923+
```
924+
// Allow two components to have distinct lifetimes...
925+
struct Pair<T: ~Escapable> {
926+
@lifetime
927+
var x: T
928+
929+
@lifetime
930+
var y: T
931+
}
932+
933+
// Allow two components to have dependent lifetimes...
934+
struct Node: ~Escapable {
935+
@lifetime
936+
var parent: Node
937+
938+
@lifetime(parent)
939+
var child: Node
940+
}
941+
942+
// Declare an abstract lifetime and alias it with another lifetime.
943+
@lifetime(elements: storage.elements)
944+
struct Container {
945+
var storage: Storage
946+
}
947+
```
948+
949+
### `where` clause
950+
951+
Some have advocated for a `where` clause on the function declaration. The function name could stand-in for its result, and directionality could be indicated with a comparison operator:
952+
953+
`func foo(arg: Arg) -> R where lifetime(foo) < lifetime([copy|borrow|mutate] arg)`
954+
889955
### dependsOn(unchecked) to disable lifetime dependence checking
890956

891957
A `dependsOn(unchecked)` annotation could allow programmers to disable lifetime dependence checking for a function result or argument. For example, the programmer may want to compose a nonescapable result from an immortal value that isn't visible to the compiler:

0 commit comments

Comments
 (0)