You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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].
20
+
We propose a new, [limited](#limitations) protocol `BitwiseCopyable` that _can_ be conformed to by types that are "bitwise-copyable"[^1]--that is, that can be moved or copied with direct calls to `memcpy` and which require no special destroy operation.
21
21
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.
22
22
Alternatively, developers can use this constraint to selectively provide high-performance variations of specific operations, such as bulk copying of a container.
23
23
24
-
[^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 exported types that are trivial will not in fact be `BitwiseCopyable`.
24
+
[^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 this property. The discussion below will explain why certain generic or exported types that are bitwise-copyable will not in fact be `BitwiseCopyable`.
25
25
26
26
## Motivation
27
27
@@ -57,12 +57,15 @@ We add a new protocol `BitwiseCopyable` to the standard library:
57
57
@_markerpublicprotocolBitwiseCopyable {}
58
58
```
59
59
60
+
That a type conforms to the protocol [implies](#transient-and-permanent) that the type is bitwise-copyable; the reverse is _not_ true.
61
+
60
62
Many basic types in the standard library will conformed to this protocol.
61
63
62
64
Developer's own types may be conformed to the protocol, as well.
63
65
The compiler will check any such conformance and emit a diagnostic if the type contains elements that are not `BitwiseCopyable`.
64
66
65
-
Furthermore, when building a module, the compiler will infer conformance to `BitwiseCopyable` for any non-exported struct or enum defined within the module whose stored members are all `BitwiseCopyable`.
67
+
Furthermore, when building a module, the compiler will infer conformance to `BitwiseCopyable` for any non-exported struct or enum defined within the module whose stored members are all `BitwiseCopyable`,
68
+
except those for which conformance is explicitly [suppressed](#suppression).
66
69
67
70
Developers cannot conform types defined in other modules to the protocol.
68
71
@@ -148,7 +151,7 @@ When the module containing the type is built, if all of the type's fields are `B
148
151
149
152
For generic types, a conformance will only be inferred if its fields unconditionally conform to `BitwiseCopyable`.
150
153
In the `RegularBox` example above, a conditional conformance will not be inferred.
151
-
If this is desired, the developer can explicitly write the conditional conformance.
154
+
If such a conformance is desired, the developer must explicitly write the conditional conformance.
152
155
153
156
[^2]: This includes raw-value enums. While such enums do include a conformance to `RawRepresentable` where `RawValue` could be a non-conforming type (`String`), the instances of the enums themselves are `BitwiseCopyable`.
154
157
@@ -162,7 +165,7 @@ For an imported C struct, if all its fields are `BitwiseCopyable`, the compiler
162
165
The same is true for an imported C++ struct or class, unless the type is non-trivial[^3].
163
166
164
167
For an imported C or C++ struct, if any of its fields cannot be represented in Swift, the compiler will not generate a conformance.
165
-
This can be overridden, however, by annotating the type `__attribute__((__swift_attr__("_BitwiseCopyable")))`.
168
+
This can be overridden, however, by annotating the type `__attribute__((__swift_attr__("BitwiseCopyable")))`.
166
169
167
170
[^3]: A C++ type is considered non-trivial (for the purpose of calls, as defined by the Itanium ABI) if any of the following is non-default: its constructor; its copy-constructor; its destructor.
168
171
@@ -180,26 +183,65 @@ For example, the compiler will infer a conformance of the following struct
To suppress the inference of `BitwiseCopyable`, a conformance can explicitly be made unavailable:
194
+
To suppress the inference of `BitwiseCopyable`, `~BitwiseCopyable`can be added to the type's inheritance list.
192
195
196
+
```swift
197
+
structCoordinate4 : ~BitwiseCopyable {...}
193
198
```
194
-
@available(*, unavailable)
195
-
extension Coordinate4 : BitwiseCopyable {}
196
-
```
199
+
200
+
Suppression must be declared on the type declaration itself, not on an extension.
201
+
202
+
### Transient and permanent notions<aname="transient-and-permanent"/>
203
+
204
+
The Swift runtime already describes[^4] whether a type is bitwise-copyable.
205
+
It is surfaced, among other places, in the standard library function `_isPOD`[^5].
206
+
207
+
[^4]: The `IsNonPOD` value witness flag is set for every type that is _not_ bitwise-copyable.
208
+
209
+
[^5]: "POD" here is an acronym for "plain old data" which is yet another name for the notion of bitwise-copyable or trivial.
210
+
211
+
If a type conforms to `BitwiseCopyable`, then `_isPOD` must be true for the type.
212
+
The converse is not true, however.
213
+
214
+
As a type evolves, it may [both gain _and_ lose bitwise-copyability](#fluctuating-bitwise-copyability).
215
+
A type may only _gain_ a conformance to `BitwiseCopyable`, however;
216
+
it cannot _lose_ its conformance without breaking source and ABI.
217
+
218
+
The two notions are related, but distinct:
219
+
That a type `_isPOD` is a statement that the type is currently bitwise-copyable.
220
+
That a type conforms to `BitwiseCopyable` is a promise that the type is now and will remain bitwise-copyable as the library evolves.
221
+
In other words returning true from `_isPOD` is a transient property, and conformance to `BitwiseCopyable` is a permanent one.
222
+
223
+
For this reason, conformance to `BitwiseCopyable` is not inherent.
224
+
Its declaration on a public type provides a guarantee that the compiler cannot infer.
225
+
226
+
### Limitations of BitwiseCopyable<aname="limitations"/>
227
+
228
+
Being declared with `@_marker`, `BitwiseCopyable` is a limited protocol.
229
+
Its limited nature allows the protocol's runtime behavior to be defined later, as needed.
230
+
231
+
1.`BitwiseCopyable` cannot be extended.
232
+
This limitation is similar to that on `Sendable` and `Any`:
233
+
it prevents polluting the namespace of conforming types, especially types whose conformance is inferred.
234
+
235
+
2. Because conformance to `BitwiseCopyable` is distinct from being bitwise-copyable,
236
+
the runtime cannot use the `IsNonPOD` bit as a proxy for conformance (although actual [conformance could be ignored](#casting-by-duck-typing)).
237
+
A separate mechanism would be necessary.
238
+
Until such a mechanism is added, `is`, `as?` and usage as a generic constraint to enable conditional conformance to another protocol is not possible.
197
239
198
240
### Standard library API improvements
199
241
200
242
The standard library includes a load method on both `UnsafeRawPointer` and `UnsafeMutableRawPointer`
201
243
202
-
```
244
+
```swift
203
245
@inlinable
204
246
@_alwaysEmitIntoClient
205
247
publicfuncloadUnaligned<T>(
@@ -210,7 +252,7 @@ public func loadUnaligned<T>(
210
252
211
253
and a corresponding write method on `UnsafeMutableRawPointer`
212
254
213
-
```
255
+
```swift
214
256
@inlinable
215
257
@_alwaysEmitIntoClient
216
258
publicfuncstoreBytes<T>(
@@ -222,7 +264,7 @@ that must be called with a trivial `T`.
222
264
223
265
We propose adding overloads of these methods to constrain the value to `BitwiseCopyable`:
224
266
225
-
```
267
+
```swift
226
268
// on both UnsafeRawPointer and UnsafeMutableRawPointer
227
269
@inlinable
228
270
@_alwaysEmitIntoClient
@@ -241,6 +283,8 @@ public func storeBytes<T : BitwiseCopyable>(
241
283
242
284
This allows for optimal code generation because `memcpy` instead of value witnesses can be used.
243
285
286
+
Additionally, we propose deprecating the original, unconstrained overloads of `loadUnaligned` and `storeBytes`.
287
+
244
288
## Effect on ABI stability
245
289
246
290
The addition of the `BitwiseCopyable` constraint to either a type or a protocol in a library will not cause an ABI break for users.
@@ -249,7 +293,7 @@ The addition of the `BitwiseCopyable` constraint to either a type or a protocol
249
293
250
294
This addition of a new protocol will not impact existing source code that does not use it.
251
295
252
-
Removing the `BitwiseCopyable`marker from a type is source-breaking.
296
+
Removing the `BitwiseCopyable`conformance from a type is source-breaking.
253
297
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`.
254
298
255
299
## Effect on API resilience
@@ -278,10 +322,42 @@ extension Box : BitwiseCopyable where Value : BitwiseCopyable {}
278
322
279
323
In the future we may in some cases be able to derive it automatically.
280
324
281
-
### MemoryLayout<T>.isBitwiseCopyable
325
+
### Dynamic casting
326
+
327
+
Being a [limited](#limitations) protocol, `BitwiseCopyable` does not currently have any runtime representation.
328
+
While a type's [transient](#transient-and-permanent) bitwise-copyability has a preexisting runtime representation, that is different from the type conforming to `BitwiseCopyable`.
329
+
330
+
Being a low-level, performance-enabling feature, it is not clear that dynamic casting should be allowed at all.
331
+
If it were to be allowed at some point, a few different approaches can already be foreseen:
332
+
333
+
#### Explicitly record a type's conformance
334
+
335
+
The standard way to support dynamic casting would be to represent a type's conformance to the protocol and query the type at runtime.
336
+
337
+
This approach has the virtue that dynamic casting behaves as usual.
338
+
A type could only be cast to `BitwiseCopyable` if it actually conformed to the protocol.
339
+
For example, casting a type which suppressed a conformance to `BitwiseCopyable` would fail.
340
+
341
+
If this approach were taken, such casting could be back-deployed as far as the oldest OS in which this runtime representation was added.
342
+
Further back deployment would be possible by adding conformance records to back deployed binaries.
282
343
283
-
In certain circumstances, it would be useful to be able to dynamically determine whether a type conforms to `BitwiseCopyable`.
284
-
In order to allow that, a new field could be added to `MemoryLayout`.
344
+
#### Duck typing for BitwiseCopyable<aname="casting-by-duck-typing"/>
345
+
346
+
An alternative would be to dynamically treat any type that's bitwise-copyable as if it conformed to `BitwiseCopyable`.
347
+
348
+
This is quite different from typical Swift casting behavior.
349
+
Rather than relying on a permanent characteristic of the type, it would rely on a [transient](#transient-and-permanent) one.
350
+
This would be visible to the programmer in several ways:
351
+
- different overloads would be selected for a value of concrete type from those selected for a value dynamically cast to `BitwiseCopyable`
352
+
- dynamic casts to `BitwiseCopyable` could fail, then succeed, then fail again in successive OS versions
353
+
354
+
On the other hand, these behavioral differences may be desireable.
355
+
356
+
Considering that this approach would just ignore the existence of conformances to `BitwiseCopyable`,
357
+
it would be reasonable to ignore the existence of a suppressed conformance as well.
358
+
359
+
This approach also has the virtue of being completely back-deployable[^6].
Such a definition remains possible after this proposal.
299
375
300
-
Because `BitwiseCopyable` is a marker protocol, its ABI is rather limited.
376
+
Because `BitwiseCopyable` is annotated `@_marker`, its ABI is rather limited.
301
377
Specifically, it only affects name mangling.
302
378
If, in a subsequent proposal, the protocol were redefined as a composition, symbols into which `BitwiseCopyable` was mangled could still be mangled in the same way, ensuring ABI compatibility.
303
379
@@ -356,3 +432,53 @@ The following types in the standard library will gain the `BitwiseCopyable` cons
0 commit comments