Skip to content

Commit bf846dd

Browse files
authored
Merge pull request #1 from atrick/rename-span
Changed `StorageView` and `BufferReference` to `Span` to match the si…
2 parents a502076 + 40a78d9 commit bf846dd

File tree

1 file changed

+27
-27
lines changed

1 file changed

+27
-27
lines changed

proposals/NNNN-lifetime-dependency.md

+27-27
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ We would like to propose extensions to Swift's function-declaration syntax that
1212
These would also be useable with methods that wish to declare a dependency on `self`.
1313
To reduce the burden of manually adding such annotations, we also propose inferring lifetime dependencies in certain common cases without requiring any additional annotations.
1414

15-
This is a key requirement for the `StorageView` type (previously called `BufferView`) being discussed elsewhere, and is closely related to the proposal for `~Escapable` types.
15+
This is a key requirement for the `Span` type (previously called `BufferView`) being discussed elsewhere, and is closely related to the proposal for `~Escapable` types.
1616

1717
**Edited** (Apr 12, 2024): Changed `@dependsOn` to `dependsOn` to match the current implementation.
1818

19+
**Edited** (May 2, 2024): Changed `StorageView` and `BufferReference` to `Span` to match the sibling proposal.
20+
1921
#### See Also
2022

2123
* **TODO**: **** Forum thread discussing this proposal
22-
* [Pitch Thread for StorageView](https://forums.swift.org/t/pitch-safe-access-to-contiguous-storage/69888)
24+
* [Pitch Thread for Span](https://forums.swift.org/t/pitch-safe-access-to-contiguous-storage/69888)
2325
* [Forum discussion of BufferView language requirements](https://forums.swift.org/t/roadmap-language-support-for-bufferview)
2426
* [Proposed Vision document for BufferView language requirements (includes description of ~Escapable)](https://github.com/atrick/swift-evolution/blob/fd63292839808423a5062499f588f557000c5d15/visions/language-support-for-BufferView.md#non-escaping-bufferview)
2527

@@ -75,12 +77,10 @@ These types are not allowed to escape the local context except in very specific
7577
A separate proposal explains the general syntax and semantics of `Escapable` and `~Escapable`.
7678

7779
By themselves, nonescapable types have severe constraints on usage.
78-
For example, consider a hypothetical `BufferReference` type that is similar to the standard library `UnsafeBufferPointer` or the `StorageView` type that is being proposed for inclusion in the standard library.
79-
It simply holds a pointer and size and can be used to access data stored in a contiguous block of memory.
80-
(We are not proposing this type; it is shown here merely for illustrative purposes.)
80+
For example, consider a hypothetical `Span` type that is similar type that is being proposed for inclusion in the standard library. It simply holds a pointer and size and can be used to access data stored in a contiguous block of memory. (We are not proposing this type; it is shown here merely for illustrative purposes.)
8181

8282
```swift
83-
struct BufferReference<T>: ~Escapable {
83+
struct Span<T>: ~Escapable {
8484
private var base: UnsafePointer<T>
8585
private var count: Int
8686
}
@@ -94,35 +94,35 @@ In the most common cases, these constraints can be inferred automatically.
9494

9595
To make the semantics clearer, we’ll begin by describing how one can explicitly specify a lifetime constraint in cases where the default inference rules do not apply.
9696

97-
Let’s consider adding support for our hypothetical `BufferReference` type to `Array`.
98-
Our proposal would allow you to declare an `array.bufferReference()` method as follows:
97+
Let’s consider adding support for our hypothetical `Span` type to `Array`.
98+
Our proposal would allow you to declare an `array.span()` method as follows:
9999

100100
```swift
101101
extension Array {
102-
borrowing func bufferReference() -> dependsOn(self) BufferReference<Element> {
103-
... construct a BufferReference ...
102+
borrowing func span() -> dependsOn(self) Span<Element> {
103+
... construct a Span ...
104104
}
105105
}
106106
```
107107

108108
The annotation `dependsOn(self)` here indicates that the returned value must not outlive the array that produced it.
109109
Conceptually, it is a continuation of the function's borrowing access:
110-
the array is being borrowed by the function while the function executes and then continues to be borrowed by the `BufferReference` for as long as the return value exists.
110+
the array is being borrowed by the function while the function executes and then continues to be borrowed by the `Span` for as long as the return value exists.
111111
Specifically, the `dependsOn(self)` annotation in this example informs the compiler that:
112112

113-
* The array must not be destroyed until after the `BufferReference<Element>` is destroyed.
113+
* The array must not be destroyed until after the `Span<Element>` is destroyed.
114114
This ensures that use-after-free cannot occur.
115-
* The array must not be mutated while the `BufferReference<Element>` value exists.
115+
* The array must not be mutated while the `Span<Element>` value exists.
116116
This follows the usual Swift exclusivity rules for a borrowing access.
117117

118118
#### Scoped Lifetime Dependency
119119

120-
Let’s consider another hypothetical type: a `MutatingBufferReference<T>` type that could provide indirect mutating access to a block of memory.
120+
Let’s consider another hypothetical type: a `MutatingSpan<T>` type that could provide indirect mutating access to a block of memory.
121121
Here's one way such a value might be produced:
122122

123123
```swift
124-
func mutatingBufferReference(to: inout Array, count: Int) -> dependsOn(to) MutatingBufferReference<Element> {
125-
... construct a MutatingBufferReference ...
124+
func mutatingSpan(to: inout Array, count: Int) -> dependsOn(to) MutatingSpan<Element> {
125+
... construct a MutatingSpan ...
126126
}
127127
```
128128

@@ -131,21 +131,21 @@ The `dependsOn(to)` annotation indicates that the returned value depends on the
131131
Because `count` is not mentioned in the lifetime dependency, that argument does not participate.
132132
Similar to the previous example:
133133

134-
* The array will not be destroyed until after the `MutatingBufferReference<Element>` is destroyed.
134+
* The array will not be destroyed until after the `MutatingSpan<Element>` is destroyed.
135135
* No other read or write access to the array will be allowed for as long as the returned value exists.
136136

137137
In both this and the previous case, the lifetime of the return value is "scoped" to the lifetime of the original value.
138138
Because lifetime dependencies can only be attached to nonescapable values, types that contain pointers will generally need to be nonescapable in order to provide safe semantics.
139-
As a result, **scoped lifetime dependencies** are the only possibility whenever an `Escapable` value (such as an Array or similar container) is providing a nonescapable value (such as the `BufferReference` or `MutatingBufferReference` in these examples).
139+
As a result, **scoped lifetime dependencies** are the only possibility whenever an `Escapable` value (such as an Array or similar container) is providing a nonescapable value (such as the `Span` or `MutatingSpan` in these examples).
140140

141141
#### Copied Lifetime Dependency
142142

143143
The case where a nonescapable value is used to produce another nonescapable value is somewhat different.
144-
Here's a typical example that constructs a new `BufferReference` from an existing one:
144+
Here's a typical example that constructs a new `Span` from an existing one:
145145
```swift
146-
struct BufferReference<T>: ~Escapable {
146+
struct Span<T>: ~Escapable {
147147
...
148-
consuming func drop(_: Int) -> dependsOn(self) BufferReference<T> { ... }
148+
consuming func drop(_: Int) -> dependsOn(self) Span<T> { ... }
149149
...
150150
}
151151
```
@@ -155,12 +155,12 @@ Recall that nonescapable values such as these represent values that are already
155155

156156
For a `consuming` method, the return value cannot have a scoped lifetime dependency on the original value, since the original value no longer exists when the method returns.
157157
Instead, the return value must "copy" the lifetime dependency from the original:
158-
If the original `BufferReference` was borrowing some array, the new `BufferReference` will continue to borrow the same array.
158+
If the original `Span` was borrowing some array, the new `Span` will continue to borrow the same array.
159159

160160
This supports coding patterns such as this:
161161
```swift
162162
let a: Array<Int>
163-
let ref1 = a.bufferReference() // ref1 cannot outlive a
163+
let ref1 = a.span() // ref1 cannot outlive a
164164
let ref2 = ref1.drop(4) // ref2 also cannot outlive a
165165
```
166166

@@ -413,21 +413,21 @@ We propose above putting the annotation on the return value, which we believe ma
413413
It would also be possible to put an annotation on the parameters instead:
414414

415415
```swift
416-
func f(@resultDependsOn arg1: Array<Int>) -> BufferReference<Int>
416+
func f(@resultDependsOn arg1: Array<Int>) -> Span<Int>
417417
```
418418

419419
Depending on the exact language in use, it could also be more natural to put the annotation after the return value.
420420
However, we worry that this hides this critical information in cases where the return type is longer or more complex.
421421

422422
```swift
423-
func f(arg1: Array<Int>) -> BufferReference<Int> dependsOn(arg1)
423+
func f(arg1: Array<Int>) -> Span<Int> dependsOn(arg1)
424424
```
425425

426426
### Different spellings
427427

428428
An earlier version of this proposal advocated using the existing `borrow`/`mutate`/`consume`/`copy` keywords to specify a particular lifetime dependency semantic:
429429
```swift
430-
func f(arg1: borrow Array<Int>) -> borrow(arg1) BufferReference<Int>
430+
func f(arg1: borrow Array<Int>) -> borrow(arg1) Span<Int>
431431
```
432432
This was changed after we realized that there was in practice almost always a single viable semantic for any given situation, so the additional refinement seemed unnecessary.
433433

@@ -483,7 +483,7 @@ We expect to address this in the near future in a separate proposal.
483483
It should be possible to return containers with collections of lifetime-constrained elements.
484484
For example, a container may want to return a partition of its contents:
485485
```swift
486-
borrowing func chunks(n: Int) -> dependsOn(self) SomeList<dependsOn(self) StorageView<UInt8>>
486+
borrowing func chunks(n: Int) -> dependsOn(self) SomeList<dependsOn(self) Span<UInt8>>
487487
```
488488
We're actively looking into ways to support these more involved cases and expect to address this in a future proposal.
489489

0 commit comments

Comments
 (0)