Confusing limitations on covariant overriding

(Dimitri Racordon) #1

Hello fellow Swift enthusiasts,

I stumbled upon an interesting question on StackOverflow that asks why Swift doesn't complain when overriding a computed property typed as an optional with a property typed non-optional. More precisely, here's the (working) code that is proposed:

class A {
  var x: Int? { return nil }
class B: A {
  override var x: Int { return 0 }

While playing around to give a convincing explanation, I noticed this works as well:

class A {
  var x: A { return A() }
class B: A {
  override var x: B { return B() }

To me, this would suggest that covariant types are allowed to override a read-only property, which makes sense I guess, as one can't set the property x on an instance of B through a variable of type A.

However, after further investigations, I failed to understand the conditions for such overrides to be accepted or rejected. Indeed, declaring x as an optional of A still works, but declaring x as an array of A in the base class doesn't seem to be overridable with an array of B (while my understanding was that Swift arrays are covariant). Furthermore, declaring x with type Any doesn't seem to be overridable with any other type in the derived class.

class A {  var x: A? { return A() } }
class B: A { override var x: B { return B() } } // OK

class A {  var x: [A] { return [A()] } }
class B: A { override var x: [B] { return [B()] } } // Fails

class A {  var x: Any { return A() } }
class B: A { override var x: B { return B() } } // Fails

This makes me question if this behavior was intended at all, or if it's just a side effect of allowing supporting covariant method overrides, whose implementation is mentioned in the changelog for Swift 4.0.

Could anyone shed some light on this?

(Slava Pestov) #2

You’ll find similar limitations on covariant method overrides as well. Only subclassing and optional injection is supported, not any other subtype relationships in the type system. This part of the language has not received much attention unfortunately. Swift 4 had a new implementation of method overrides that’s fixed various compiler crashes and miscompiles, but it did not change the underlying language rules. They’re still implemented in a one-off manner that only applies to method overrides.

Protocol conformance with protocol composition property