Unexpectedly not conforming to protocol

import Foundation

protocol Node {
    var parent: Node? { get }
}

protocol Child {
    var parent: Child? { get }
}

class Person: Node, Child {
    var parent: (Child & Node)?
}

I get the following compiler errors:

Type 'Person' does not conform to protocol 'Child'. Do you want to add protocol stubs?
Type 'Person' does not conform to protocol 'Node'. Do you want to add protocol stubs?

Semantics of the example etc aside, is there something wrong in my expecting this not to be an error?

You expect Node & Child to be a subtype of Node and a subtype of Child, but also (Node & Child)? to be a subtype of both Node? and Child?. This is actually true since standard Swift optionals support covariance, but you won't be able to conform to a protocol with only a subtype in a required property. Your protocol Node requires any conforming type to accept any value of an existential type Node from property parent. In your class Person you're satisfying this requirement only partially: assigning any value of type Node to parent of class Person won't work as it has no guarantee of also being a subtype of Child.

I hope I can put it more concisely: you have a requirement in both of your protocols, but your class adds its own requirement, which is more narrow and can't satisfy both protocols at the same time.

What could help is associated type Self in your protocols, although this will require making your class final:

protocol Node {
  var parent: Self? { get }
}

protocol Child {
  var parent: Self? { get }
}

final class Person: Node, Child {
  var parent: Person?
}
1 Like

Thinking about it more, if you were to change Node and Child protocols to use associated type Self, they both become equivalent and you could remove one of them and replace its uses with another without any change in the behaviour of your code. That's because you wouldn't be able to use those PATs in places where it matters, only in protocol inheritance/conformance declarations and generic constraints. That is the case as long as Self requirements on protocols prevent those from being used as existentials or until opaque types are introduced. As soon as that restriction is lifted you'd be able to use Node and Child as existentials (or opaque types) again for concrete types, type casts and type comparisons, where different names do matter even though the protocols are structurally equivalent.

Since the protocol requirements are "just" getters, the covariance could be allowed. But it'd be a behavioral change to do so. The bug that tracks this is SR-522 (since a get-only property is like a function return).

2 Likes
Terms of Service

Privacy Policy

Cookie Policy