|
| 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