Unify covariant return type behaviour


(Howard Lovatt) #1

Currently Swift is inconsistent on how it handles covariant return types, consider the code below:

class Top {}
class Bottom: Top {}

protocol P {
    func mT() -> Top
    typealias A: Top
    func mA() -> A
}

struct S: P {
    func mT() -> Top { // Must be `Top` otherwise *type's declaration* gets error "does not conform to protocol P"
        return Bottom()
    }
    typealias A = Top
    func mA() -> Top { // Must be `Top` otherwise *type's declaration* gets error "does not conform to protocol P"
        return Bottom()
    }
}

class BC<G: Top>: P {
    func mG() -> G {
        fatalError("Needs to be overridden")
    }
    func mT() -> Top { // Must be `Top` otherwise *type's declaration* gets error "does not conform to protocol P"
        fatalError("Needs to be overridden")
    }
    typealias A = Top
    func mA() -> Top { // Must be `Top` otherwise *type's declaration* gets error "does not conform to protocol P"
        fatalError("Needs to be overridden")
    }
}

class DC: BC<Top> {
    override func mG() -> Bottom { // Can be Bottom!
        return Bottom()
    }
    override func mT() -> Bottom { // Can be ottom!
        return Bottom()
    }
    override func mA() -> Bottom { // Can be Bottom!
        return Bottom()
    }
}

The behaviour is odd in that if a class overrides a method from another class then covariant return types are allowed. However if a class or struct implements a method from a protocol then it can’t implement with a covariant return type.

When you implement a method with a protocol extension you get similar behaviour in that a class that overrides the already implemented behaviour can now have a covariant return type:

protocol PE {
    func mT() -> Top
}

extension PE {
    func mT() -> Top { // Must be `Top` otherwise **compiler crashes** (seg. fault: 11)
        fatalError("Needs to be overridden")
    }
}

class CE: PE {
    func mT() -> Bottom { // Can be Bottom!
        return Bottom()
    }
}

The suggestion is that like overrides, implementations of protocol methods can have covariant return types.

Thanks in advance for any comments,

— Howard.

PS another thing, though not part of the proposal, is that not requiring `override` when implementing a protocol method:

1. Is causing vague error messages and a compiler crash that do not finger the source of the problem.
2. Looks odd when some ‘inherited’ functions have override and other don’t,
3. Is inconsistent with other languages, i.e. Java (Java changed to @Override on interface methods - originally only on class methods - i.e. precedence for change), Scala, etc..
4. Is particularly jarring when an extension implements the method but then the overriding method cannot be marked with `override`.

PPS This proposal was originally part of “Protocols on Steroids” which was a collection of proposals that worked well together but comments indicated it would be easier to discuss as seperate proposals.