Existential parameter type cannot be implemented with opaque type

I'm wondering if the following code should actually compile:

protocol P {}
protocol R {
  func f(p: any P)
}
struct S: R {
  func f(p: some P) {}
}

In other words, can a (some P) -> () method fulfill the protocol requirement (any P) -> ()?

A recent Swift 6.0.2 compiler doesn't accept it. What are cases where any P and some P are different as method parameters? I feel they should be equivalent. Aren't they in certain circumstances?

3 Likes

f here is a generic function:

struct S: R {
  // func f(p: some P) {} :=
  func f <T:P> (p: T) {}
}

Therefore, I venture to say that S does not conform to R. :slight_smile:

1 Like

Even the generic version could be accepted as a conforming implementation of the method. T: P being equivalent to some P should be the same as any P in this case.

2 Likes

Theoretically, the code could be allowed. To create S's protocol witness for R.f(p:), the compiler would just need to generate the code to unwrap the existential and then call S.f(p:) on it.

However, this would cause problems in today's Swift, since unfortunately the overload resolution system prefers existential parameters over generic ones. This means that R's f(p:) would always get picked over S's, even if you're directly calling f(p:) on an instance of S.

I'd like to see this aspect of overload resolution be fixed someday, but it probably won't be soon since it would be a breaking change.

3 Likes