Implicitly Opened Existentials on Initialization

When you invoke an initializer, it's acting like a generic function call of type <T:P> (s: T) -> S<T>. If we open p, that calls the initializer by binding T to the dynamic type inside the value of p, so the call becomes (s: {dynamic type of p}) -> S<{dynamic type of p}>. SE-0352 backed off from providing a way to refer to {dynamic type of p} after p is opened, and instead chose to leave cases like this where the dynamic type shows up as part the return type unsupported. The diagnostic should be improved to make this more obvious.

If it makes sense to make the generic S type conform to a protocol, then instead of calling the S initializer directly, you could use a static factory method that returns the S as a protocol existential or opaque return type, which you can then use with an opened existential:

protocol Q {}
struct S<T: P>: Q {
  var s: T

  static func make(s: T) -> some Q { return S(s: s) }
}

func testOpenSimple(p: any P) {
  let q = S.make(s: p) // q has type `any Q`
}
3 Likes