Subclass always picks its superclass's associatedtype

I recently run into a problem. To simplify it, this is how it looks like:

// Class A is a class from an external framework 
// and its implementation cannot be changed
class A { }

protocol HanderProtocol {
    static var shouldHandle: Bool { get }
}

class Hander1: HanderProtocol {
    static let shouldHandle = true
}

class Hander2: HanderProtocol {
    static let shouldHandle = false
}

protocol Handleable {
    associatedtype Handler: HanderProtocol
}

extension Handleable {
    var shouldHandle: Bool {
        Self.Handler.shouldHandle
    }
}

extension A: Handleable {
    typealias Handler = Hander1
}

class B: A {
    typealias Handler = Hander2
}

print(A().shouldHandle) // prints 'true'
print(B().shouldHandle) // prints 'true' as well

class B's associatedtype Handler is class Hander2, which always return false.

This comes to me as a surprise. Is this expected? From my perspective, this is not, but if it is expected, shouldn't we forbid overriding the associatedtype?

Minified example:

protocol P { associatedtype A }

class C: P { typealias A = Int }

class D: C { typealias A = String }

extension P {
  static func printA() { print(A.self) }
}

D.printA()      // Int
print(D.A.self) // String

This behavior occurs because D’s conformance to P is inherited from C.

The only way in which D can customize the conformance, is by overriding methods from C that satisfy requirements of P. Since a typealias cannot be overridden, it follows that D cannot provide a different one for its conformance to P.

Note that the typealias in D does work when using D directly. It is just not the one used for purposes of conformance to P, since that conformance comes from C.

• • •

There are a number of other complications surrounding class and subclass conformances to protocols, especially when generics and/or existentials are involved:

SR–103 describes the inability of a subclass to override a protocol requirement for which the superclass uses a default implementation from the protocol.

The thread Need help understanding protocols and generics covers some strange edge-cases. This post by @xwu lists just about every possible combination you can imagine, and explains the behavior in all but the last one, which was discovered by @Lantua a few posts earlier.

3 Likes