I don't have the 100% complete answer, but I have a pointer that might be sufficient for you.
Scala compiler deals with GADTs (Generalized Algebraic Data Types) in a very particular way. Some cases are solved with special handling, some cases are unsolved. Dotty is trying to fill most of the holes, and it has already solved a lot of related issues, however there are still quite a few open ones as well.
Typical example of special GADT handling in Scala 2 compiler is very related to your use case. If we take a look at:
def method[A](arg: Base[A]) = {
arg match {
case Derived(_) => 42
}
}
and we explicitly declare the return type to be A
:
def method[A](arg: Base[A]): A
it will compile just fine. Your IDE might complain, but the compiler will let it through. Method says it returns an A
, but the pattern matching case evaluates into an Int
, which theoretically shouldn't compile. However, special handling of GADTs in the compiler says it's fine, because in that particular pattern matching branch A
has been "fixed" to be an Int
(because we matched on Derived
which is a Base[Int]
).
Generic type parameter for the GADT (in our case A
) has to be declared somewhere. And here's the interesting part - special compiler handling only works when it's declared as the type parameter of the enclosing method. If it's coming from a type member or a type parameter of the enclosing trait/class, it doesn't compile, as you witnessed yourself.
This is why I said it's not a 100% complete answer - I cannot point to a concrete place (such as official specification) which documents this properly. Sources on handling of GADTs in Scala come down to a couple of blogposts, which are great by the way, but if you want more than that you will have to dig into the compiler code yourself. I tried doing exactly that, and I think it comes down to this method, but if you really want to go deeper, you might want to ping someone more experienced with the Scala compiler codebase.