I'm trying to understand what can and can't be done with this feature. In Xcode 14.0 beta 4 I get this behaviour:
func specific<T : Incredible>(_ x: T) {
}
func general(_ x: any Collection<any Incredible>) {
specific(x.first!) // 1: Type 'any Incredible' cannot conform to 'Incredible'
let z = x.first!
specific(z) // 2: works
for y in x {
specific(y) // 3: Global function 'specific' requires that 'T' conform to 'Incredible'
}
}
Leading to several observations and questions:
It works fine with "some Collection...". Can this feature not be used with nested existentials? (Edit: code block 1 still doesn't work with "some Collection".)
The compiler doesn't care if I spell it "any Collection", without the second any keyword. I assume this is a limitations in the error reporting, and it's treated the same?
That line 2 works and line 1 doesn't surely must be a compiler error? They should do exactly the same thing. And note that line 2 does work, i.e. it definitely executes the code correctly.
In line 3, it can't be made to work with introduction of more constants.
Edit: In line 3, the type of y seems to be "Any".
Does anyone have any input on this? Thanks in advance.
@hborla is the authority. But I think you’re hitting a couple of known issues.
Specifically, this sounds familiar to me. It might be a known issue.
any Collection<any Incredible> is sugar for any Collection where Element == any Incredible. If we drop the any before Incredible, we get any Collection where Element == Incredible. But a bare protocol name in a same-type constraint is just the old syntax for any P, so they’re equivalent.
I do think it would be good to force the new syntax in a primary associated type constraint.
Implicit existential opening is specific to arguments, so I would not be surprised if there’s a subtle interaction with unwrapping syntax in argument position. Sounds worthy of a GitHub issue.
Thanks for the insight. Line 3 (or code block 3; the for-loop) can't be made to work with extra constant, like I mentioned, but it can using a trampoline function like this:
func specific(_ x: some Incredible) {
}
func trampoline(_ x: some Collection<any Incredible>) {
for y in x {
specific(y)
}
}
func general(_ x: any Collection<any Incredible>) {
trampoline(x)
}
While I'm at it: Is there any way to print or debug the compile-time type of an expression? If I print type(of:) I get the runtime type, but I am trying to figure out what the compiler knows the type to be.