Skip to content

Commit 104a422

Browse files
authored
Update vector after review feedback (#2626)
* Update 0453-vector.md * Fix documentation of the (first:next:) initializer * Remove syntax sugar
1 parent a6a8cc3 commit 104a422

File tree

1 file changed

+94
-154
lines changed

1 file changed

+94
-154
lines changed

proposals/0453-vector.md

Lines changed: 94 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -92,44 +92,19 @@ func complexAlgorithm() -> Int {
9292
}
9393
```
9494

95-
This type also serves as a safer replacement to `withUnsafeTemporaryAllocation`
96-
when the amount of things to reserve is known statically:
97-
98-
```swift
99-
// Previously
100-
withUnsafeTemporaryAllocation(of: Int.self, capacity: 16) {
101-
$0.initialize(repeating: 0)
102-
103-
for i in $0.indices {
104-
$0[i] = compute()
105-
}
106-
}
107-
108-
// Now
109-
var ints = Vector<16, Int>(repeating: 0)
110-
111-
for i in ints.indices {
112-
ints[i] = compute()
113-
}
114-
```
115-
11695
Vectors of noncopyable values will be possible by using any of the closure based
11796
taking initializers or the literal initializer:
11897

11998
```swift
12099
// [Atomic(0), Atomic(1), Atomic(2), Atomic(3)]
121100
let incrementingAtomics = Vector<4, Atomic<Int>> { i in
122101
Atomic(i)
123-
} // [Atomic(0), Atomic(1), Atomic(2), Atomic(3)]
124-
125-
// [Atomic(337523057234), Atomic(8726493873498347), Atomic(98573472834767364),
126-
// Atomic(72394735763974)] (maybe)
127-
let randomAtomics = Vector<4, _>(expand: RNG()) { Atomic($0.next()) }
102+
}
128103

129104
// [Sprite(), Sprite(), Sprite(), Sprite()]
130-
// Where the 2nd, 3rd, and 4th elements are all copies of the initial first
131-
// value
132-
let copiedSprites = Vector<4, _>(unfold: Sprite()) { $0.copy() }
105+
// Where the 2nd, 3rd, and 4th elements are all copies of their previous
106+
// element.
107+
let copiedSprites = Vector<4, _>(first: Sprite()) { $0.copy() }
133108

134109
// Inferred to be Vector<3, Mutex<Int>>
135110
let literalMutexes: Vector = [Mutex(0), Mutex(1), Mutex(2)]
@@ -299,44 +274,25 @@ extension Vector where Element: ~Copyable {
299274
public init<E: Error>(with next: (Int) throws(E) -> Element) throws(E)
300275

301276
/// Initializes every element in this vector by running the closure with the
302-
/// passed in mutable state.
303-
///
304-
/// This will call the closure 'count' times, where 'count' is the static
305-
/// count of the vector, to initialize every element by passing the closure
306-
/// an inout reference to the passed state. The closure is allowed to throw
307-
/// an error at any point during initialization at which point the vector will
308-
/// stop initialization, deinitialize every currently initialized element, and
309-
/// throw the given error back out to the caller.
310-
///
311-
/// - Parameter state: The mutable state that can be altered during each
312-
/// iteration of the closure initializing the vector.
313-
/// - Parameter next: A closure that passes in an inout reference to the
314-
/// user given mutable state which returns an owned
315-
/// `Element` instance to insert into the vector.
316-
public init<State: ~Copyable, E: Error>(
317-
expand state: consuming State,
318-
with next: (inout State) throws(E) -> Element
319-
) throws(E)
320-
321-
/// Initializes every element in this vector by running the closure with the
322-
/// passed in first element.
277+
/// previously initialized element.
323278
///
324279
/// This will call the closure 'count' times, where 'count' is the static
325280
/// count of the vector, to initialize every element by passing the closure
326-
/// an immutable borrow reference to the first element given to the
327-
/// initializer. The closure is allowed to throw an error at any point during
328-
/// initialization at which point the vector will stop initialization,
329-
/// deinitialize every currently initialized element, and throw the given
330-
/// error back out to the caller.
281+
/// an immutable borrow reference to the previously initialized element. The
282+
/// closure is allowed to throw an error at any point during initialization at
283+
/// which point the vector will stop initialization, deinitialize every
284+
/// currently initialized element, and throw the given error back out to the
285+
/// caller.
331286
///
332287
/// - Parameter first: The first value to insert into the vector which will be
333288
/// passed to the closure as a borrow.
334289
/// - Parameter next: A closure that passes in an immutable borrow reference
335-
/// of the given first element of the vector which returns
336-
/// an owned `Element` instance to insert into the vector.
290+
/// of the previously initialized element of the vector
291+
/// which returns an owned `Element` instance to insert into
292+
/// the vector.
337293
public init<E: Error>(
338-
unfold first: consuming Element,
339-
with next: (borrowing Element) throws(E) -> Element
294+
first: consuming Element,
295+
next: (borrowing Element) throws(E) -> Element
340296
) throws(E)
341297
}
342298

@@ -364,7 +320,6 @@ collection type.
364320
extension Vector where Element: ~Copyable {
365321
public typealias Element = Element
366322
public typealias Index = Int
367-
public typealias Indices = Range<Int>
368323

369324
/// Provides the count of the collection statically without an instance.
370325
public static var count: Int { count }
@@ -378,11 +333,6 @@ extension Vector where Element: ~Copyable {
378333
public borrowing func index(after i: Int) -> Int
379334
public borrowing func index(before i: Int) -> Int
380335

381-
public borrowing func reduce<Result: ~Copyable, E: Error>(
382-
into initialResult: consuming Result,
383-
_ updateAccumulatingResult: (inout Result, borrowing Element) throws(E) -> ()
384-
) throws(E) -> Result
385-
386336
public mutating func swapAt(
387337
_ i: Int,
388338
_ j: Int
@@ -392,95 +342,6 @@ extension Vector where Element: ~Copyable {
392342
}
393343
```
394344

395-
### C Interop changes
396-
397-
With the introduction of `Vector`, we have a unique opportunity to fix another
398-
pain point within the language with regards to C interop. Currently, the Swift
399-
compiler imports a C array of type `T[24]` as a tuple of `T` with 24 elements.
400-
Previously, this was really the only representation that the compiler could pick
401-
to allow interfacing with C arrays. It was a real challenge working with these
402-
fields from C in Swift. Consider the following C struct:
403-
404-
```c
405-
struct section_64 {
406-
char sectname[16];
407-
char segname[16];
408-
uint64_t addr;
409-
uint64_t size;
410-
uint32_t offset;
411-
uint32_t align;
412-
...
413-
};
414-
```
415-
416-
Today, this gets imported as the following Swift struct:
417-
418-
```swift
419-
struct section_64 {
420-
let sectname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times)
421-
let segname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times)
422-
let addr: UInt64
423-
let size: UInt64
424-
let offset: UInt32
425-
let align: UInt32
426-
...
427-
}
428-
```
429-
430-
Using an instance of `section_64` in Swift for the most part is really easy.
431-
Accessing things like `addr` or `size` are simple and easy to use, but using the
432-
`sectname` property introduces a level of complexity that isn't so fun to use.
433-
434-
```swift
435-
func getSectionName(_ section: section_64) -> String {
436-
withUnsafePointer(to: section.sectname) {
437-
// This is unsafe! 'sectname' isn't guaranteed to have a null byte
438-
// indicating the end of the C string!
439-
String(cString: $0)
440-
}
441-
}
442-
443-
func iterateSectionNameBytes(_ section: section_64) {
444-
withUnsafeBytes(to: section.sectname) {
445-
for byte in $0 {
446-
...
447-
}
448-
}
449-
}
450-
```
451-
452-
Having to resort to using very unsafe API to do anything useful with imported C
453-
arrays is not something a memory safe language like Swift should be in the
454-
business of. `Vector` allows us to clean up this sore spot in a very elegant and
455-
much more importantly, safe way.
456-
457-
We plan to introduce a new _upcoming_ feature that folks can enable whenever
458-
they'd like to via `-enable-upcoming-feature ImportCArraysAsVectors`. This will
459-
change the current behavior of importing C arrays as tuples, to instead import
460-
them as `Vector`. For the previous example, we'd instead generate the following:
461-
462-
```swift
463-
struct section_64 {
464-
let sectname: Vector<16, CChar>
465-
let segname: Vector<16, CChar>
466-
let addr: UInt64
467-
let size: UInt64
468-
let offset: UInt32
469-
let align: UInt32
470-
...
471-
}
472-
```
473-
474-
By introducing an upcoming feature, it lets folks who are ready to migrate their
475-
codebase using a C interface to use `Vector` entirely, while still allowing
476-
dependencies or dependents to use the old behavior with the same C interface.
477-
478-
In a future Swift language mode, we plan to make this behavior the default and
479-
drop importing C array fields as tuples and only as `Vector`. This approach
480-
allows developers to get the new behavior early if they'd like to use it, which
481-
will make the language mode transition much smoother because there would be no
482-
source break with regards to this feature.
483-
484345
## Source compatibility
485346

486347
`Vector` is a brand new type in the standard library, so source should still be
@@ -758,6 +619,85 @@ Some syntax suggestions:
758619
Note that it may make more sense to have the length appear before the type. I
759620
discuss this more in depth in [Reorder the generic arguments](#reorder-the-generic-arguments-vectort-n-instead-of-vectorn-t).
760621

622+
### C Interop changes
623+
624+
With the introduction of `Vector`, we have a unique opportunity to fix another
625+
pain point within the language with regards to C interop. Currently, the Swift
626+
compiler imports a C array of type `T[24]` as a tuple of `T` with 24 elements.
627+
Previously, this was really the only representation that the compiler could pick
628+
to allow interfacing with C arrays. It was a real challenge working with these
629+
fields from C in Swift. Consider the following C struct:
630+
631+
```c
632+
struct section_64 {
633+
char sectname[16];
634+
char segname[16];
635+
uint64_t addr;
636+
uint64_t size;
637+
uint32_t offset;
638+
uint32_t align;
639+
...
640+
};
641+
```
642+
643+
Today, this gets imported as the following Swift struct:
644+
645+
```swift
646+
struct section_64 {
647+
let sectname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times)
648+
let segname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times)
649+
let addr: UInt64
650+
let size: UInt64
651+
let offset: UInt32
652+
let align: UInt32
653+
...
654+
}
655+
```
656+
657+
Using an instance of `section_64` in Swift for the most part is really easy.
658+
Accessing things like `addr` or `size` are simple and easy to use, but using the
659+
`sectname` property introduces a level of complexity that isn't so fun to use.
660+
661+
```swift
662+
func getSectionName(_ section: section_64) -> String {
663+
withUnsafePointer(to: section.sectname) {
664+
// This is unsafe! 'sectname' isn't guaranteed to have a null byte
665+
// indicating the end of the C string!
666+
String(cString: $0)
667+
}
668+
}
669+
670+
func iterateSectionNameBytes(_ section: section_64) {
671+
withUnsafeBytes(to: section.sectname) {
672+
for byte in $0 {
673+
...
674+
}
675+
}
676+
}
677+
```
678+
679+
Having to resort to using very unsafe API to do anything useful with imported C
680+
arrays is not something a memory safe language like Swift should be in the
681+
business of.
682+
683+
Ideally we could migrate the importer from using tuples to this new `Vector`
684+
type, however that would be massively source breaking. A previous revision of
685+
this proposal proposed an _upcoming_ feature flag that modules can opt into,
686+
but this poses issues with the current importer implementation with regards to
687+
inlinable code.
688+
689+
Another idea was to import struct fields with C array types twice, one with the
690+
existing name with a tuple type (as to not break source) and another with some
691+
`Vector` suffix in the name with the `Vector` type. This works pretty well for
692+
struct fields and globals, but it leaves fields and functions who have pointers
693+
to C arrays in question as well (spelt `char (*x)[4]`). Do we import such
694+
functions twice using a similar method of giving it a different name? Such a
695+
solution would also incur a longer deprecation period to eventually having just
696+
`Vector` be imported and no more tuples.
697+
698+
We're holding off on any C interop changes here as there are still lots of open
699+
questions as to what the best path forward is.
700+
761701
## Alternatives considered
762702

763703
### A name other than `Vector`

0 commit comments

Comments
 (0)