Why is the generic constraint dropped here? (constrained existential)

Consider the following snippet:

protocol P<PT> {
    associatedtype PT
}

struct AnyP<T>: P {
    typealias PT = T
}

extension P {
    func eraseToAnyP() -> AnyP<PT> {
        AnyP()
    }
}

protocol Q {
    associatedtype QT: P<Int>

    func f() -> QT
}

func g(_ q: any Q) {
    q.f().eraseToAnyP() // error
}

in Xcode 14 beta 3, the q.f().eraseToAnyP() line results in a warning/error. The warning is that the result of q.f() is erased to any P, causing the subsequent error that eraseToAnyP() cannot be called on a value of type any P. However, at first glance it seems to me like this should be supported by erasing the result of q.f() to any P<Int> rather than any P.

Is this just a vestigial result of the SE-0309 erasure that wasn't updated to take SE-0353 into account, or is there a more fundamental limitation here that's escaping me?

cc @codafi @hborla happy to file this on GitHub but wanted a gut check to make sure there wasn't something I misunderstood here

This looks like a bug to me too. SE-0353 specifies that covariant erasure can still be performed when the associated type is in invariant position and has been made concrete. I believe that covers the case you've written above. This code does work:

protocol P<PT> {
  associatedtype PT
}

extension P where PT == Int {
  func f() -> Self { self }
}

struct AnyP<T>: P {
  typealias PT = T
}

extension P {
  func eraseToAnyP() -> AnyP<PT> {
    AnyP()
  }
}

func g(p: any P<Int>) {
  p.f().eraseToAnyP() // okay
}

which is slightly different, but the result of calling f() has the same generic constraints as far as I can tell!

1 Like

Reported as #59948!

1 Like