Currently SE-0143 is a little unclear on whether a conditional conformance implies conformances to any inherited protocols automatically. I'm proposing we clarify this as a "no".
With conditional conformances, the constraints for the conformance to the inherited protocol may not be clear, so the conformance to the inherited protocol will need to be stated explicitly. For example:
protocol P { } protocol Q : P { } protocol R : P { } struct X<T> { } extension X: Q where T: Q { } extension X: R where T: R { }
Note that both of the constrained extensions could imply the conformance to
P
. However, because the two extensions have disjoint sets of constraints (one requiresT: Q
, the otherT: R
), it becomes unclear which constraints should apply to the conformance toP
which sounds reasonable, but then it goes on to say:
In cases where the different sets of constraints used to describe the implied inherited conformances can be ordered, the least-specialized (i.e., most general) constraints will be used for the implied inherited conformance. For example:
protocol R: P { } protocol S: R { } struct Y<T> { } extension Y: R where T: R { } extension Y: S where T: S { }
The conformances of
Y: R
andY: S
both imply the conformanceY: P
. [...] Therefore,Y
will conform toP
whenT: R
,
I think that the unclear logic from the previous situation still applies: it's likely that Y: P
when T: P
, not when T: R
. Pretty much all Collection
wrappers are of this form:
struct MyWrapper<Wrapped: Sequence> {}
extension MyWrapper: Sequence {} // Wrapped: Sequence
extension MyWrapper: Collection where Wrapped: Collection {} // *
extension MyWrapper: BidirectionalCollection where Wrapped: BidirectionalCollection {}
If the *
line was omitted, there would be an implied conformance of the form:
extension MyWrapper: Collection where Wrapped: BidirectionalCollection {}
which is unnecessarily strong. And, noticing this later and changing it to Wrapped: Collection
is actually a breaking change.
Thus, I think we should disallow the automatic inference and require an explicit conformance to be stated, which comes in two relatively simple forms:
- adding it to one of the conditional extensions (easiest, and is the explicit form of the implied conformance),
extension Y: R, P where T: R {}
in the second example, or - creating a new extension (to allow having different bounds),
extension Y: P where ... {}
in the second.
Notes:
-
we should be able to reverse this decision (i.e. allow conditional conformances to imply others) backwards compatibly in future, if we decide it would be useful
-
that this doesn't affect inference from non-conditional conformances, even if there are other conditional conformances that theoretically could be a source to infer, e.g. this will still infer
P
fromQ
:protocol P { } protocol Q : P { } protocol R : P { } struct X<T> { } extension X: Q { } extension X: R where T: R { }
-
I don't think this will break much code now, because this implication rarely actually worked due to some bugs. I have an implementation at https://github.com/apple/swift/pull/15268, and it required no changes to the stdlib (source compatibility testing currently running).
-
The implementation currently includes a fixit for matching the implied behaviour (i.e. adding
, P
to the existing extension), but not creating a whole new extension.
Thoughts?