Can we upcast a PAT?

I'm assuming there is no support in the language for what I want at the moment but maybe you know something I might be missing.

There is a module with these definitions:

public protocol P {
    associatedtype T // P is PAT
}

public protocol SomeDelegate {
    func foo<T>(p: T) where T: P
}

On my side I'm defining my own protocol that conforms to P and want to use its requirements:

protocol P1: P {
    var bar: Baz { get }
}

class SomeDelegateAdopter: SomeDelegate {
    func foo<T>(p: T) where T: P {
        // How can I access `bar` if `p` is `P1`?
    }
}

At least something like this naive method would be nice since instance of PAT cannot be returned as an existential:

func p1<T, U>(from value: T) -> U? where T: P, U: P1, T == U {
    value as? U
}

I would very like to avoid using a "shadow protocol" for purpose of the upcasting.

Would function overloading be okay as a solution?

class SomeDelegateAdopter: SomeDelegate {
  func foo<T: P>(p: T) {
    print("P")
  }

  func foo<T: P1>(p: T) {
    print("P1")
  }
}

If P1 inherits P, then Swift will call the second overload if you pass an instance conforming to P1.

That's what would be ok for me but unfortunately I don't think function overloading works like this when protocol requirement is involved. I gave it a try but func foo<T: P1>(p: T) is never called.

There is, technically, a way to do this, but it’s kind of a doozy.

The second half of my post here has some links explaining how. The simplest approach is the one by Dave Abrahams.

The basic idea is to make a marker protocol with requirements that mirror the PAT, except instead of using associated types the requirements are generic. Then you make a placeholder generic type that conditionally conforms to the marker protocol when its generic argument conforms to the real protocol.

Notably, Dave’s approach uses a single generic type, with a helper method to do the necessary casting in one place. Then it can conditionally conform to as many marker protocols as you want.

Thanks @Nevin! It is cumbersome but indeed best what I've seen. I'm again amazed what mind-blowing workaround needs be done in order to get essential semantics from these protocols. For anyone interested in the implementation I reduced Dave's approach to this:

struct PDispatch<PAdopter> {
    func toAdopter<T>(_ x: T) -> PAdopter where T: P {
        x as! PAdopter
    }
}

protocol P1Dispatch {
    func bar<T>(_: T) -> Baz where T: P
}

extension PDispatch: P1Dispatch where PAdopter: P1 {
    func bar<T>(_ x: T) -> Baz where T: P {
        toAdopter(x).bar
    }
}

extension P {
    var p1Dispatch: P1Dispatch? {
        PDispatch<Self>() as? P1Dispatch
    }

    var p1Bar: Baz? {
        p1Dispatch?.bar(self)
    }
}

Now it can be used like this:

class SomeDelegateAdopter: SomeDelegate {
    func foo<T>(p: T) where T: P {
        if let bar = p.p1Bar {
            // :-)
        }
    }
}
1 Like