This seems correct to me.
To review, an opaque type can be thought of as generics “in reverse.” It is not equivalent to an existential type, which is a kind of container that can store an instance of any conforming type. With generics, the caller gets to decide what type to use, and the callee can rely only on the generic constraints laid out beforehand. With opaque types, the callee gets to decide what type to use, and the caller can rely only on the guarantee that it conforms to the specified protocol. But underneath it all there is a type.
So, in other words, you cannot know if any two instances of type
some P are of the same type merely from the fact that they are
some P (hence why they are called opaque). One function could return an
some Hashable and another function could return a
Even if you can see with your own two eyes what the underlying types are, opaque types tell the compiler not to let you (the caller) rely on that—so that a future version of the function can be changed to return a value of a different type without affecting callers.
In the first example, the compiler knows that
bb are of the same type. This is because they both store a result of the same function; the function may change in the future, but then the underlying type of
bb would both change and still match.
In the second example, you have made the type of
aa opaque to yourself. In other words, you explicitly told the compiler not to allow you to rely on the knowledge that the type of
aa is the result of
a(). That is what the diagnostic is telling you.
In the future, Swift might add a way to say, make this variable have an opaque type that’s the same as this other variable’s, but there is no way for you to express that today.