Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lifetime Dependency Annotations for Non-escapable Types #2305

Open
wants to merge 62 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
7e767dc
First full draft.
tbkka Jan 25, 2024
243e33b
Whitespace cleanup
tbkka Jan 25, 2024
b30308d
Fix formatting
tbkka Jan 25, 2024
ccc54d9
Future direction: Lifetime dependencies between arguments
tbkka Jan 26, 2024
2992389
Link to pitch thread
tbkka Feb 6, 2024
ba3946c
New examples for Future Directions
tbkka Feb 7, 2024
0aab19d
Match the implementation: Use local parameter names for argument ref…
tbkka Feb 7, 2024
f9b06b7
Clarify some Alternatives and Future Directions
tbkka Feb 8, 2024
84521ca
methods are `mutating`, not `inout`
tbkka Feb 12, 2024
3055bec
Expand alternative syntaxes
tbkka Feb 22, 2024
78d07f2
Overhaul to adopt the new `@dependsOn()` syntax
tbkka Mar 21, 2024
6ebcdb4
Corrections from meg-gupta
tbkka Mar 21, 2024
eed1b05
Overhaul semantics discussion
tbkka Mar 22, 2024
2e82c6b
Use "nonescapable" for the concept, rather than `~Escapable`
tbkka Mar 26, 2024
f648b02
Use "copied lifetime dependency" more consistently
tbkka Mar 26, 2024
a502076
s/@dependsOn/dependsOn/ to match implementation
tbkka Apr 12, 2024
40a78d9
Changed `StorageView` and `BufferReference` to `Span` to match the si…
atrick May 3, 2024
bf846dd
Merge pull request #1 from atrick/rename-span
tbkka May 7, 2024
25e4355
Add a link to the forum discussion.
atrick May 4, 2024
af162ef
Additions to Proposed solutions
atrick May 28, 2024
adfe8a0
Dependency semantics by example
atrick May 28, 2024
3b6f859
Cleanup Future directions
atrick May 28, 2024
2fd52e1
Additions to Future directions
atrick May 28, 2024
1c5a02e
Grammar updates
atrick May 28, 2024
c6ede5d
Merge pull request #2 from atrick/update
tbkka May 28, 2024
def2c82
Update and cleanup the grammar.
atrick May 29, 2024
d9aa90b
Merge pull request #3 from atrick/lifetime-dependency
tbkka May 31, 2024
992b2fe
Add a change log entry.
atrick Jun 5, 2024
aed8750
Syntax update: dependsOn(self).
atrick Jun 7, 2024
1859b98
Remove Future Direction: only limited to results
atrick Jun 7, 2024
6c80aca
Fix OwnedSpan ~Escapable typo.
atrick Jun 7, 2024
43dbc15
Tweak wording in the `ref1.drop` example.
atrick Jun 7, 2024
d8f4cb3
New section: Immortal requirements.
atrick Jun 9, 2024
5555ee6
New alternatives considered: Initializer syntax.
atrick Jun 9, 2024
eb1d88e
Update Future component syntax.
atrick Jun 8, 2024
a9ca58d
New alternative considered: dependsOn(unchecked)
atrick Jun 10, 2024
181050a
Remove redundant future direction: containers and their elements
atrick Jun 11, 2024
d7c1ac7
Move "Parameter index" section to Alternatives considered.
atrick Jun 11, 2024
b01cd1e
Typo in example from "Depending on an escapable"
atrick Jun 11, 2024
02fc95a
Rename Array to ContiguousArray.
atrick Jun 12, 2024
de45422
New example: Escapable properties in a nonescapable type
atrick Jun 12, 2024
5b0fd84
Changelog entries
atrick Jun 9, 2024
226d635
Merge pull request #4 from atrick/lifetime-dependency
tbkka Jun 12, 2024
9acaba8
Fix a grammar typo.
atrick Jun 14, 2024
51832e8
Fix a typo in the dependent parameters example.
atrick Jun 14, 2024
0ed7906
Typo: fix two more cases of Array -> ContiguousArray renaming.
atrick Jun 14, 2024
b6c51d8
Merge pull request #5 from atrick/lifetime-dependency
tbkka Jun 17, 2024
a969447
Update the free-standing function inference rules.
atrick Jun 20, 2024
25f30b3
Merge pull request #6 from atrick/lifetime-dependency
tbkka Jun 21, 2024
e1d364e
Added future directions: Function type syntax and closures
atrick Jun 21, 2024
e814e62
Merge pull request #7 from atrick/lifetime-dependency
tbkka Jun 25, 2024
9583bd7
Add an alternative spelling: `lifetime` vs. `dependsOn`.
atrick Jul 10, 2024
699d547
Merge pull request #8 from atrick/lifetime-dependency
tbkka Jul 11, 2024
f337c2f
Remove the Alternative: Initializer syntax: result vs. inout syntax
atrick Jul 31, 2024
3b6439c
Add Alternative: @lifetime annotation and where clause.
atrick Jul 31, 2024
ac43daa
Simplified implicit lifetime dependencies
atrick Aug 1, 2024
4152b2e
Merge pull request #9 from atrick/lifetime-dependency
tbkka Aug 13, 2024
5cd2fd3
Fix inference rules to be sequenced.
atrick Aug 13, 2024
0a4392f
In the lifetime annotation example, add an argument label.
atrick Aug 15, 2024
f16293c
Merge pull request #10 from atrick/lifetime-dependency
tbkka Aug 16, 2024
b841b47
Update Future Direction: Lifetime dependence for closures
atrick Aug 19, 2024
9ba7a57
Merge pull request #11 from atrick/lifetime-dependency
tbkka Aug 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Whitespace cleanup
  • Loading branch information
tbkka committed Jan 25, 2024
commit 243e33b516c993bdbb7adaef51c940278c329f4b
35 changes: 17 additions & 18 deletions proposals/NNNN-lifetime-dependency.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ They cannot guarantee that `buff` will outlive the `array`, which means there is
Library authors trying to support this kind of code pattern today have a few options, but none are entirely satisfactory:

* The client developer can manually insert `withExtendedLifetime` and similar annotations to control the lifetime of specific objects.
This is awkward and error-prone.
We would prefer a mechanism where the library author can declare the necessary semantics and have the compiler automatically enforce them.
This is awkward and error-prone.
We would prefer a mechanism where the library author can declare the necessary semantics and have the compiler automatically enforce them.
* The library author can store a back-reference to the container as part of their "pointer" or "slice" object.
However, this incurs reference counting overhead which sacrifices some of the performance gains that pointer-based designs are generally intended to provide.
In addition, this approach is not possible in environments that lack support for dynamic allocation.
However, this incurs reference counting overhead which sacrifices some of the performance gains that pointer-based designs are generally intended to provide.
In addition, this approach is not possible in environments that lack support for dynamic allocation.
* The library author can make the pointer information available only within a scoped function, but this is also unsafe, as demonstrated by well-meaning developers who extract the pointer out of such functions using code like that below.
Even when used correctly, scoped functions can lead to a pyramid of deeply-indented code blocks.
Even when used correctly, scoped functions can lead to a pyramid of deeply-indented code blocks.

```
// 🛑 The following line of code is dangerous! DO NOT DO THIS!
Expand Down Expand Up @@ -90,7 +90,7 @@ Our proposal would allow you to declare an `array.bufferReference()` method as f
```
extension Array {
borrowing func bufferReference() -> borrow(self) BufferReference<Element> {
... construct a BufferReference ...
... construct a BufferReference ...
}
}
```
Expand All @@ -102,20 +102,20 @@ In essence, the `borrow(self)` annotation extends that borrowed access beyond th
Specifically, the `borrow(self)` annotation informs the compiler that:

* The array must not be destroyed until after the `BufferReference<Element>` is destroyed.
This ensures that use-after-free cannot occur.
This ensures that use-after-free cannot occur.
* The array must not be mutated while the `BufferReference<Element>` value exists.
This enforces the usual Swift exclusivity rules.
This enforces the usual Swift exclusivity rules.

In addition to `borrow(self)`, we also propose supporting three other lifetime dependency annotations:

#### `mutate(self)` or `mutate(arg)`

Let’s consider another hypothetical type: a `MutatingBufferReference<T>` type that could provide indirect mutating access to a block of memory.
This would need to be exposed slightly differently, since it provides *write* access:
This would need to be exposed slightly differently, since it provides *write* access:

```
func mutatingBufferReference(to: inout Array) -> mutate(to) MutatingBufferReference<Element> {
... construct a MutatingBufferReference ...
... construct a MutatingBufferReference ...
}
```

Expand Down Expand Up @@ -170,7 +170,7 @@ func complexView(data: Array<Item>, statistics: Array<Statistics>, other: Int)
#### Allowed Lifetime Dependencies

Only certain types of lifetime dependencies make sense, depending on the type of argument.
The syntax is somewhat different for functions and methods, though the basic rules are essentially the same.
The syntax is somewhat different for functions and methods, though the basic rules are essentially the same.

**Functions:** A function with a lifetime dependency annotation generally takes this form:

Expand All @@ -192,10 +192,10 @@ Further:
* `copy` lifetime-type is only permitted with `borrowing` or `inout` parameter-convention

**Methods:** Similar rules apply to `self` lifetime dependencies on methods.
Given a method of this form:
Given a method of this form:

```
<mutation-modifier> func method(... args ...) -> <lifetime-type>(self) ResultType
<mutation-modifier> func method(... args ...) -> <lifetime-type>(self) ResultType
```

We only permit
Expand All @@ -207,9 +207,8 @@ We only permit

The rules above apply regardless of whether the parameter-convention or mutation-modifier is explicitly written or is implicit.
tbkka marked this conversation as resolved.
Show resolved Hide resolved

**Initializers:** An initializer dependency annotation takes one of two forms.
An initializer can define a lifetime dependency on one of its arguments.
In this case, the rules are the same as for “Functions” above:
**Initializers:** An initializer can define a lifetime dependency on one of its arguments.
tbkka marked this conversation as resolved.
Show resolved Hide resolved
In this case, the rules are the same as for “Functions” above:

```
init(arg: <parameter-convention> ArgType) -> <lifetime-type>(arg) Self
Expand Down Expand Up @@ -265,7 +264,7 @@ This modifies *function-result* in the Swift grammar as follows:

Here, the argument to the lifetime modifier must be one of the following:

* *external-parameter-name:* the external name of one of the function parameters,
* *external-parameter-name:* the external name of one of the function parameters,
* *parameter-index:* a numeric index of one of the parameters in the *parameter-clause* (the first parameter is number zero), or
* the token **`self`**.

Expand Down Expand Up @@ -413,7 +412,7 @@ For example:
func f(arg1: Type1) -> borrow(arg1) NonEscapableType // 🛑 `arg1` must be marked `borrowing`

... Type ... {
func f() -> borrow(self) Self // 🛑 method must be marked `borrowing`
func f() -> borrow(self) Self // 🛑 method must be marked `borrowing`
}
```

Expand Down