(Not a full answer, but a few notes and links; Maybe it can serve as a starting point for someone else)
In 2.12.13, the compiler seems to be able to prove that F[_ <: X] and F[X] are the same type if X occurs in covariant position:
println(implicitly[Option[_ <: ChildA] =:= Option[ChildA]])
This compiles (with warnings, but it compiles):
trait Parent
class ChildA extends Parent
class ChildB extends Parent
def whatIsInside(opt: Option[_ <: Parent]): String = {
opt match {
case _: Option[ChildA] => "ChildA"
case _: Option[ChildB] => "ChildB"
case None => "None"
case _ => throw new Error("meh")
}
}
This does not compile:
trait Parent
class ChildA extends Parent
class ChildB extends Parent
def whatIsInside(opt: Option[_ <: Parent]): String = {
opt match {
case _: Option[_ <: ChildA] => "ChildA"
case _: Option[_ <: ChildB] => "ChildB"
case None => "None"
case _ => throw new Error("meh")
}
}
So, it seems that it must have something to do with the bounds inference for the synthetic _$2 type-variable.
Also, this compiles:
def testConforms[A >: Nothing <: ChildA](ca: Option[A]): Option[_ <: Parent] = ca
so, unless I'm misinterpreting the spec:
If there exists a substitution σ over the type variables a_i,…,a_n such that σT conforms to pt, one determines the weakest subtype constraints C1 over the type variables a1,…,an such that C0 ∧ C1 implies that T conforms to [the expected type] pt.
, the pt would be Option[_ <: Parent], then a1,...,an would be the single synthetic type _$2, and the constraints _$2 >: Nothing <: ChildA should make the type Option[_$2] conform to Option[_ <: Parent]. So, it seems that it should work, but doesn't.
Bonus
If you just wanted to make it work, then just skip all those wildcards, they aren't needed:
trait Parent
class ChildA extends Parent
class ChildB extends Parent
def whatIsInside(opt: Option[Parent]): String = {
opt match {
case Some(_: ChildA) => "ChildA"
case Some(_: ChildB) => "ChildB"
case None => "None"
case _ => throw new Error("meh")
}
}
whatIsInside(Some(new ChildA))
whatIsInside(Some(new ChildB))
whatIsInside(None)