Skip to content

Commit 2db89d8

Browse files
committed
[BitwiseCopyable] Pitch draft.
1 parent 97197b9 commit 2db89d8

File tree

1 file changed

+270
-0
lines changed

1 file changed

+270
-0
lines changed

proposals/nnnn-bitwise-copyable.md

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
# BitwiseCopyable
2+
3+
* Proposal: [SE-NNNN](NNNN-filename.md)
4+
* Authors: [Kavon Farvardin](https://github.com/kavon), [Guillaume Lessard](https://github.com/glessard), [Nate Chandler](https://github.com/nate-chandler), [Tim Kientzle](https://github.com/tbkka)
5+
* Review Manager: TBD
6+
* Implementation: On `main` gated behind `-enable-experimental-feature BitwiseCopyable`
7+
8+
<!-- *During the review process, add the following fields as needed:*
9+
10+
* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) or [apple/swift-evolution-staging#NNNNN](https://github.com/apple/swift-evolution-staging/pull/NNNNN)
11+
* Decision Notes: [Rationale](https://forums.swift.org/), [Additional Commentary](https://forums.swift.org/)
12+
* Bugs: [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN), [SR-MMMM](https://bugs.swift.org/browse/SR-MMMM)
13+
* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md)
14+
* Previous Proposal: [SE-XXXX](XXXX-filename.md) -->
15+
16+
## Introduction
17+
18+
We propose a new marker protocol `BitwiseCopyable` that can be conformed to by types that can be moved or copied with direct calls to `memcpy` and which require no special destroy operation[^1].
19+
When compiling generic code with such constraints, the compiler can emit these efficient operations directly, only requiring minimal overhead to look up the size of the value at runtime.
20+
Alternatively, developers can use this constraint to selectively provide high-performance variations of specific operations, such as bulk copying of a container.
21+
22+
[^1]: The term "trivial" is used in [SE-138](0138-unsaferawbufferpointer.md) and [SE-0370](0370-pointer-family-initialization-improvements.md) to refer to types with the property above. The discussion below will explain why certain generic or resilient types that are trivial will not in fact be `BitwiseCopyable`.
23+
24+
## Motivation
25+
26+
Swift can compile generic code into an unspecialized form in which the compiled function receives a value and type information about that value.
27+
Basic operations are implemented by the compiler as calls to a table of "value witness functions."
28+
29+
This approach is flexible, but can represent significant overhead.
30+
For example, using this approach to copy a buffer with a large number of `Int` values requires a function call for each value.
31+
32+
Constraining the types in generic functions to `BitwiseCopyable` allows the compiler (and in some cases, the developer) to instead use highly efficient direct memory operations in such cases.
33+
34+
The standard library already contains many examples of functions that could benefit from such a concept, and more are being proposed:
35+
36+
The `UnsafeMutablePointer.initialize(to:count:)` function introduced in [SE-0370](0370-pointer-family-initialization-improvements.md) could use a bulk memory copy whenever it statically knew that its argument was `BitwiseCopyable`.
37+
38+
The proposal for [`StorageView`](nnnn-safe-shared-contiguous-storage.md) includes the ability to copy items to or from potentially-unaligned storage, which requires that it be safe to use bulk memory operations:
39+
```swift
40+
public func loadUnaligned<T: BitwiseCopyable>(
41+
fromByteOffset: Int = 0, as: T.Type
42+
) -> T
43+
44+
public func loadUnaligned<T: BitwiseCopyable>(
45+
from index: Index, as: T.Type
46+
) -> T
47+
```
48+
49+
## Proposed solution
50+
51+
We add a new protocol `BitwiseCopyable` to the standard library:
52+
```swift
53+
@_marker public protocol BitwiseCopyable {}
54+
```
55+
56+
Many basic types in the standard library will conformed to this protocol.
57+
58+
Developer's own types may be conformed to the protocol, as well.
59+
The compiler will check any such conformance and emit a diagnostic if the type contains elements that are not `BitwiseCopyable`.
60+
61+
Furthermore, when building a module, the compiler will infer conformance to `BitwiseCopyable` for any non-resilient struct or enum defined within the module whose stored members are all `BitwiseCopyable`.
62+
63+
Developers cannot conform types defined in other modules to the protocol.
64+
65+
## Detailed design
66+
67+
Our design first conforms a number of core types to `BitwiseCopyable`, and then extends that to aggregate types.
68+
69+
### Standard library changes
70+
71+
Many types and a few key protocols are constrained to `BitwiseCopyable`.
72+
A few highlights:
73+
74+
* Integer types
75+
* Floating point types
76+
* SIMD types
77+
* Pointer types
78+
* `Unmanaged`
79+
* `Optional`
80+
81+
For an exhaustive list, see the [appendix](#all-stdlib-conformers).
82+
83+
### Additional BitwiseCopyable types
84+
85+
In addition to the standard library types marked above, the compiler will recognize several other types as `BitwiseCopyable`:
86+
87+
* Tuples of `BitwiseCopyable` elements.
88+
89+
* `unowned(unsafe)` references.
90+
Such references can be copied without reference counting operations.
91+
92+
* `@convention(c)` and `@convention(thin)` function types do not carry a reference-counted capture context, unlike other Swift function types, and are therefore `BitwiseCopyable`.
93+
94+
### Explicit conformance to `BitwiseCopyable`
95+
96+
Enum and struct types can be explicitly declared to conform to `BitwiseCopyable`.
97+
When a type is declared to conform, the compiler will check that its elements are all `BitwiseCopyable` and emit an error otherwise.
98+
99+
For example, the following struct can conform to `BitwiseCopayble`
100+
```swift
101+
public struct Coordinate : BitwiseCopyable {
102+
var x: Int
103+
var y: Int
104+
}
105+
```
106+
because `Int` is `BitwiseCopyable`.
107+
108+
Similarly, the following enum can conform to `BitwiseCopyable`
109+
```swift
110+
public enum PositionUpdate : BitwiseCopyable {
111+
case begin(Coordinate)
112+
case move(x_change: Int, y_change: Int)
113+
case end
114+
}
115+
```
116+
because both `Coordinate` and `(x_change: Int, y_change: Int)` are `BitwiseCopyable`.
117+
118+
The same applies to generic types. For example, the following struct can conform to `BitwiseCopyable`
119+
```swift
120+
struct BittyBox<Value : BitwiseCopyable> : BitwiseCopyable {
121+
var first: Value
122+
}
123+
```
124+
because its field `first` is a of type `Value` which is `BitwiseCopyable`.
125+
126+
Generic types may be `BitwiseCopyable` only some of the time.
127+
For example,
128+
```swift
129+
struct RegularBox<Value> {
130+
var first: Value
131+
}
132+
```
133+
cannot conform unconditionally because `Value` needn't conform to `BitwiseCopyable`.
134+
In this case, a conditional conformance may be written:
135+
136+
```swift
137+
extension Box : BitwiseCopyable where Value : BitwiseCopyable {}
138+
```
139+
140+
### Automatic inference for aggregates
141+
142+
As a convenience, unconditional conformances will be inferred for structs and enums much of the time.
143+
When the module containing the type is built, if all of the type's fields are `BitwiseCopyable`, the compiler will generate a conformance for it to `BitwiseCopyable`.
144+
145+
For generic types, a conformance will only be inferred if its fields unconditionally conform to `BitwiseCopyable`.
146+
In the `RegularBox` example above, a conditional conformance will not be inferred.
147+
If this is desired, the developer can explicitly write the conditional conformance.
148+
149+
### Inference for types in evolving libraries
150+
151+
This does not apply to public (or `@usableFromInline`) types defined within a module built with library evolution.
152+
While all the type's fields may be `BitwiseCopyable` at the moment, the compiler can't predict that they will always be.
153+
If this is the developer's intent, they can explicitly conform the type.
154+
155+
For `@frozen` types, however, `BitwiseCopyable` conformance will be inferred even when in a module built with library evolution.
156+
That's because the compiler can see that the type's fields are all `BitwiseCopyable` and knows that they will remain that way.
157+
For example, the compiler will infer a conformance of the following struct
158+
```swift
159+
@frozen
160+
public struct Coordinate3 {
161+
var x: Int
162+
var y: Int
163+
}
164+
```
165+
to `BitwiseCopyable`.
166+
167+
## Effect on ABI stability
168+
169+
The addition of the `BitwiseCopyable` constraint to either a type or a protocol in a library will not cause an ABI break for users.
170+
171+
## Source compatibility
172+
173+
This addition of a new protocol will not impact existing source code that does not use it.
174+
175+
Removing the `BitwiseCopyable` marker from a type is source-breaking.
176+
As a result, future versions of Swift may conform additional existing types to `BitwiseCopyable`, but will not remove it from any type already conforming to `BitwiseCopyable`.
177+
178+
## Effect on API resilience
179+
180+
Adding a `BitwiseCopyable` constraint on a generic type will not cause an ABI break.
181+
As with any protocol, the additional constraint can cause a source break for users.
182+
183+
## Future Directions
184+
185+
### Automatic derivation of conditional conformances
186+
187+
The wrapper type mentioned above
188+
```swift
189+
struct RegularBox<Value> {
190+
var first: Value
191+
}
192+
```
193+
cannot conform to `BitwiseCopyable` unconditionally.
194+
It can, however, so long as `Value` is `BitwiseCopyable`.
195+
196+
With this proposal, such a conditional conformance can be added manually:
197+
198+
```swift
199+
extension Box : BitwiseCopyable where Value : BitwiseCopyable {}
200+
```
201+
202+
In the future we may in some cases be able to derive it automatically.
203+
204+
### MemoryLayout<T>.isBitwiseCopyable
205+
206+
In certain circumstances, it would be useful to be able to dynamically determine whether a type conforms to `BitwiseCopyable`.
207+
In order to allow that, a new field could be added to `MemoryLayout`.
208+
209+
### BitwiseMovable
210+
211+
Most Swift types have the property that their representation can be relocated in memory with direct memory operations.
212+
This could be represented with a `BitwiseMovable` protocol that would be handled similarly to `BitwiseCopyable`.
213+
214+
## Alternatives considered
215+
216+
### Alternate Spellings
217+
218+
**Trivial** is widely used within the compiler and Swift evolution discussions to refer to the property of bitwise copyability. `BitwiseCopyable`, on the other hand, is more self-documenting.
219+
220+
## Acknowledgments
221+
222+
This proposal has benefitted from discussions with John McCall, Joe Groff, Andrew Trick, Michael Gottesman, and Arnold Schwaigofer.
223+
224+
## Appendix: Standard library conformers<a name="all-stdlib-conformers"/>
225+
226+
The following protocols in the standard library will gain the `BitwiseCopyable` constraint:
227+
228+
- `FixedWidthInteger`
229+
- `_Pointer`
230+
- `SIMDStorage`, `SIMDScalar`, `SIMD`
231+
232+
233+
The following types in the standard library will gain the `BitwiseCopyable` constraint:
234+
235+
- `Optional<T>` when `T` is `BitwiseCopyable`
236+
- The fixed-precision integer types:
237+
- `Bool`
238+
- `Int8`, `Int16`, `Int32`, `Int64`, `Int`
239+
- `UInt8`, `UInt16`, `UInt32`, `UInt64`, `UInt`
240+
- `StaticBigInt`
241+
- `UInt8.Words`, `UInt16.Words`, `UInt32.Words`, `UInt64.Words`, `UInt.Words`
242+
- `Int8.Words`, `Int16.Words`, `Int32.Words`, `Int64.Words`, `Int.Words`
243+
- The fixed-precision floating-point types:
244+
- `Float`, `Double`, `Float80`
245+
- `FloatingPointSign`, `FloatingPointClassification`
246+
- The family of `SIMDx<Scalar>` types
247+
- The family of unmanaged pointer types:
248+
- `OpaquePointer`
249+
- `UnsafeRawPointer`, `UnsafeMutableRawPointer`
250+
- `UnsafePointer`, `UnsafeMutablePointer`, `AutoreleasingUnsafeMutablePointer`
251+
- `UnsafeBufferPointer`, `UnsafeMutableBufferPointer`
252+
- `UnsafeRawBufferPointer`, `UnsafeMutableRawBufferPointer`
253+
- `Unmanaged`
254+
- `CVaListPointer`
255+
- Some types related to collections
256+
- `EmptyCollection`
257+
- `UnsafeBufferPointer.Iterator`, `UnsafeRawBufferPointer.Iterator`, `EmptyCollection.Iterator`
258+
- `String.Index`, `CollectionDifference.Index`
259+
- Some types related to unicode
260+
- `Unicode`
261+
- `Unicode.ASCII`, `Unicode.UTF8`, `Unicode.UTF16`, `Unicode.UTF32`, `Unicode.Scalar`
262+
- `Unicode.ASCII.Parser`, `Unicode.UTF8.ForwardParser`, `Unicode.UTF8.ReverseParser`, `Unicode.UTF16.ForwardParser`, `Unicode.UTF16.ReverseParser`, `Unicode.UTF32.Parser`
263+
- `Unicode.Scalar.UTF8View`, `Unicode.Scalar.UTF16View`
264+
- `UnicodeDecodingResult`
265+
- Some fieldless types
266+
- `Never`, `MemoryLayout`, `CommandLine`, `SystemRandomNumberGenerator`
267+
- `StaticString`
268+
- `Hasher`
269+
- `ObjectIdentifier`
270+
- `Duration`

0 commit comments

Comments
 (0)