Consider a pattern match of this form: (x: X) match { case _: P => }
There are four possibilities to consider:
[P1] X will always conform to P
[P2] x will never conform to P
[P3] X <: P if some runtime test is true
[P4] X cannot be checked against P
The first two cases correspond to those when there is enough
static information to say X <: P or that (x ∈ X) ⇒ (x ∉ P).
The fourth case includes unknown abstract types or structural
refinements appearing within a pattern.
The third case is the interesting one. We designate another type, XR,
which is essentially the intersection of X and |P|, where |P| is
the erasure of P. If XR <: P, then no warning is emitted.
Examples of how this info is put to use:
sealed trait A[T] ; class B[T] extends A[T]
def f(x: B[Int]) = x match { case _: A[Int] if true => }
def g(x: A[Int]) = x match { case _: B[Int] => }
f requires no warning because X=B[Int], P=A[Int], and B[Int] <:< A[Int].
g requires no warning because X=A[Int], P=B[Int], XR=B[Int], and B[Int] <:< B[Int].
XR=B[Int] because a value of type A[Int] which is tested to be a B can
only be a B[Int], due to the definition of B (B[T] extends A[T].)
This is something like asSeenFrom, only rather than asking what a type looks
like from the point of view of one of its base classes, we ask what it looks
like from the point of view of one of its subclasses.
On pattern matcher checkability:
Consider a pattern match of this form: (x: X) match { case _: P => }
There are four possibilities to consider: [P1] X will always conform to P [P2] x will never conform to P [P3] X <: P if some runtime test is true [P4] X cannot be checked against P
The first two cases correspond to those when there is enough static information to say X <: P or that (x ∈ X) ⇒ (x ∉ P). The fourth case includes unknown abstract types or structural refinements appearing within a pattern.
The third case is the interesting one. We designate another type, XR, which is essentially the intersection of X and |P|, where |P| is the erasure of P. If XR <: P, then no warning is emitted.
Examples of how this info is put to use: sealed trait A[T] ; class B[T] extends A[T] def f(x: B[Int]) = x match { case _: A[Int] if true => } def g(x: A[Int]) = x match { case _: B[Int] => }
f
requires no warning because X=B[Int], P=A[Int], and B[Int] <:< A[Int].g
requires no warning because X=A[Int], P=B[Int], XR=B[Int], and B[Int] <:< B[Int]. XR=B[Int] because a value of type A[Int] which is tested to be a B can only be a B[Int], due to the definition of B (B[T] extends A[T].)This is something like asSeenFrom, only rather than asking what a type looks like from the point of view of one of its base classes, we ask what it looks like from the point of view of one of its subclasses.