Redundant conformance in class hierarchies

Hello fellow Swift enthusiasts,

I was playing with the type system when I stumbled upon this situation:

protocol P {}
protocol Q {}
protocol R: P, Q {}

class A: P {}
class B: A, P, Q, R {}

This code won't compile, complaining at B's declaration about a redundant conformance to the protocol P. I figured out the reason is that A already conforms to P, but nonetheless this left me with two questions:

  1. Why is such redundancy a full-fledged error, rather than a mere warning?
  2. If there is a legitimate reason for the error, then why does the compiler not complain about redundancy if I remove P from the last line? It is my understanding that Q is redundant, since R already refines Q, and that P is still redundant with respect to A's inheritance.

Could anyone tell me what I might be missing?

2 Likes
  1. Why is such redundancy a full-fledged error, rather than a mere warning?

I'm guessing this is probably there for historical reasons. The corresponding error was written in 2015 and there are other related warnings which were downgraded from errors to warnings in 2017/2018.

If there is a legitimate reason for the error, then why does the compiler not complain about redundancy if I remove P from the last line? It is my understanding that Q is redundant, since R already refines Q , and that P is still redundant with respect to A 's inheritance

This seems to be related to the fact that R is a protocol whereas A is a class. I don't know why that has an effect though.

1 Like

It is a full-fledged error because a class cannot conform to a protocol in two different ways. It is not just redundant in that you are restating something you don't have to state again. Suppose P has a requirement for a method func f() -> Self. Then class B: A, P /* ... */ would simultaneously require the return type to be of type A and to be of type B. This is not possible.

Again, the error isn't restating the same conformance twice; it is attempting to conform to the same protocol in two potentially different ways. It is perfectly fine--and in some coding styles in fact encouraged--and in the case of conditional conformances not just encouraged as a matter of style but fully required by the compiler--to state all increasingly refined protocols.

This makes sense. Nonetheless ...

This is not entirely correct. If you were to add such a requirement in P, you could not conform to it with a method func f() -> A in the class A unless it is final, precisely because it would not satisfy the requirement on derived classes. This makes perfect sense, as A is P and B is A should imply B is P. The compiler will suggest you implement the function using Self as the return type.

protocol P { func foo() -> Self }
// ...
class A: P { func foo() -> Self { self } }

As a consequence, I would argue that this does not compel the class B to conform to P in two ways. In fact, I think the compiler could virtually drop the conformance declaration. Is there any other counter-example?

Whoops, a think-o on my part!

Swift deliberately prevents multiple conformances from being surfaced. I don’t think I can come up with any situations in which you could pull this off simply by relaxing the redundant conformance error (and certainly not any situations which wouldn’t reflect compiler bugs).

However, if I had to surmise a reason for making this an error, it would be to serve as another layer of the deliberate design goal of avoiding inadvertently exposing the ability to conform to the same protocol in more than one way.