Skip to content

Commit 2b98f79

Browse files
committed
SE-0427: A few minor clarifications
- Motivate conditional conformance restrictions better - Mention that you don't have to write ~Copyable if you're declaring a conditional conformance - Better explain what's coming in the ABI section
1 parent 7202187 commit 2b98f79

File tree

1 file changed

+91
-45
lines changed

1 file changed

+91
-45
lines changed

proposals/0427-noncopyable-generics.md

Lines changed: 91 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,10 @@ protocol Derived: Base {
384384
}
385385
```
386386

387-
### Conformance to `Copyable`
387+
### Conditional conformance to `Copyable`
388388

389-
Structs and enums conform to `Copyable` unconditionally by default, but a
390-
conditional conformance can also be defined. For example, take this
391-
noncopyable generic type:
389+
Generic structs and enums can conditionally conform to `Copyable`.
390+
For example, take this unconditionally noncopyable generic type:
392391
```swift
393392
enum List<T: ~Copyable>: ~Copyable {
394393
case empty
@@ -405,35 +404,93 @@ Note that no `where` clause needs to be written, because by the rules above,
405404
the default conformances here will already range over all generic parameters
406405
of the type.
407406

408-
A conditional `Copyable` conformance is not permitted if the
409-
struct or enum declares a `deinit`. Deterministic destruction requires the
407+
A conditional `Copyable` conformance cannot be declared if the
408+
type has a `deinit` member. Deterministic destruction requires the
410409
type to be unconditionally noncopyable.
411410

412-
A conformance to `Copyable` is checked by verifying that every stored property
413-
(of a struct) or associated value (or an enum) itself conforms to `Copyable`.
414-
For a conditional `Copyable` conformance, the conditional requirements must be
415-
sufficient to ensure this is the case. For example, the following is rejected,
416-
because the struct cannot unconditionally conform to `Copyable`, having a
417-
stored property of the noncopyable type `T`:
411+
Copyability is an inherent property of the type, unlike a general protocol
412+
conformance, which is a logically distinct entity from the conforming type itself.
413+
This imposes some restrictions on the form that a conditional `Copyable`
414+
conformance can take:
415+
1. Conditional `Copyable` conformance must be declared in the same source
416+
file as the conforming struct or enum declaration.
417+
2. The conditional requirements must be of the form `T: Copyable` where `T`
418+
is a generic parameter of the conforming type. Other conditional
419+
requirements are not permitted:
420+
```swift
421+
extension Pair: Copyable where T: Equatable {} // error
422+
```
423+
In particular, general conformance requirements can be satisfied by
424+
retroactive conformances, and it would break the semantic model if the
425+
addition of a retroactive conformance to some other protocol could
426+
influence the copyability of a type.
427+
3. It is also not allowed for `Copyable` be conditional on the copyability of
428+
an associated type:
429+
```swift
430+
protocol Manager { associatedtype Resource: ~Copyable }
431+
struct ManagerManager<T: Manager>: ~Copyable {}
432+
extension ManagerManager: Copyable where T.Resource: Copyable {} // error
433+
```
434+
Even though the right-hand side of `T.Resource: Copyable` is `Copyable`,
435+
the substituted associated type depends on the conformance `T: Manager`, which
436+
again could be retroactive.
437+
438+
As a consequence of existing language rules around synthesized conformances
439+
together the first restriction above, the default conformance on the type
440+
declaration does not need to be suppressed. The below is an equivalent
441+
declaration of the earlier `List` example, and the decision to write `~Copyable`
442+
in the inheritance clause is a matter of style:
443+
444+
```swift
445+
enum List<T: ~Copyable> /* : ~Copyable */ {
446+
case empty
447+
indirect case element(T, List<T>)
448+
}
449+
450+
extension List: Copyable /* where T: Copyable */ {}
451+
```
452+
453+
It is an error to declare an unconditional `Copyable` conformance with an extension.
454+
This is always equivalent to _not_ suppressing the default conformance,
455+
and perhaps indicates a missing `~Copyable` on the generic parameter `T`:
456+
457+
```swift
458+
struct CopyableBox<T>: ~Copyable {
459+
let contents: T
460+
}
461+
462+
extension CopyableBox: Copyable {} // pointless
463+
```
464+
### Checking the `Copyable` conformance
465+
466+
When a struct or enum conforms to `Copyable`, conditionally or unconditionally, we
467+
check that every stored property (of a struct) or associated value (or an enum) is itself
468+
`Copyable`, under the same assumptions as the type itself.
469+
470+
For example, the following is rejected; while the struct claims to unconditionally conform
471+
to `Copyable`, it has a stored property of the noncopyable type `T`:
418472
```swift
419473
struct Holder<T: ~Copyable> /* : Copyable */ {
420474
var value: T // error
421475
}
422476
```
423477

424-
There are two situations when it is permissible for a copyable type to
425-
have a noncopyable generic parameter. The first is when the generic parameter
426-
is not stored inside the type itself:
478+
On the other hand, a struct or enum with a noncopyable generic parameter may still conform
479+
to `Copyable` unconditionally if its storage layout does not depend on this generic
480+
parameter. For example:
427481
```swift
428482
struct Factory<T: ~Copyable> /* : Copyable */ {
429483
let fn: () -> T // ok
430484
}
431485
```
432-
The above is permitted, because a _function_ of type `() -> T` is still copyable,
433-
even if a _value_ of type `T` is not copyable.
486+
The above is permitted, because a _function_ of type `() -> T` type is still
487+
copyable, even if its result type `T` is noncopyable.
488+
489+
### Classes
434490

435-
The second case is when the type is a class. The contents of a class is never
436-
copied, so noncopyable types can appear in the stored properties of a class:
491+
Class references are always `Copyable`, but the contents of a class instance
492+
are never implicitly copied, so a class can have noncopyable stored properties
493+
without restriction:
437494
```swift
438495
class Box<T: ~Copyable> {
439496
let value: T // ok
@@ -442,31 +499,14 @@ class Box<T: ~Copyable> {
442499
}
443500
```
444501

445-
For a conditional `Copyable` conformance, the conditional requirements must be
446-
of the form `T: Copyable` where `T` is a generic parameter of the type. It is
447-
not permitted to make `Copyable` conditional on any other kind of requirement:
448-
```swift
449-
extension Pair: Copyable where T == Array<Int> {} // error
450-
```
451-
Nor can `Copyable` be conditional on the copyability of an associated type:
452-
```swift
453-
struct ManagerManager<T: Manager>: ~Copyable {}
454-
extension ManagerManager: Copyable where T.Resource: Copyable {} // error
455-
```
456-
457-
Conditional `Copyable` conformance must be declared in the same source
458-
file as the struct or enum itself. Unlike conformance to other protocols,
459-
copyability is a deep, inherent property of the type itself.
460-
461-
### Classes
462-
463-
This proposal supports classes with noncopyable generic parameters,
502+
This proposal allows generic classes to declare noncopyable generic parameters,
464503
but it does not permit classes to themselves be `~Copyable`.
465504
Similarly, an `AnyObject` or superclass requirement cannot be combined with
466505
`~Copyable`:
467506
```swift
468507
func f<T>(_ t: T) where T: AnyObject, T: ~Copyable { ... } // error
469508
```
509+
This is left as a future direction.
470510

471511
### Existential types
472512

@@ -512,14 +552,20 @@ noncopyable structs and enums.
512552

513553
This proposal does not change the ABI of existing code.
514554

515-
Adding `~Copyable` to
516-
an existing generic parameter is generally an ABI-breaking change, even when
517-
source-compatible.
555+
A key goal is that ABI-stable frameworks should be able to adopt `~Copyable` in public APIs where it makes sense.
556+
There are two scenarios to consider:
557+
558+
1. An older client, built before noncopyable types, is running with a newer version of the framework,
559+
whose existing types have been generalized to allow for noncopyability.
560+
2. A newer client, built against a newer framework **and** making use of noncopyable types, is running
561+
with an older version of the framework that predates noncopyable types.
562+
563+
A follow-up proposal for adoption of `~Copyable` in the standard library will address both
564+
scenarios:
518565

519-
Targeted mechanisms are being developed to preserve ABI compatibility when
520-
adopting `~Copyable` on previously-shipped generic code. This will enable adoption
521-
of this feature by standard library types such as `Optional`. Such mechanisms will
522-
require extreme care to use correctly.
566+
1. The first will be handled by describing the overall rules under which `~Copyable` can retrofitted safely,
567+
together with a new attribute that will handle certain differences in mangling.
568+
2. The second scenario is inherently more complex but it is rare except for the standard library itself.
523569

524570
## Alternatives Considered
525571

0 commit comments

Comments
 (0)