Closed
Description
Compiler version
Scala compiler version 3.0.0-RC2-bin-SNAPSHOT-nonbootstrapped-git-7bef33b -- Copyright 2002-2021, LAMP/EPFL
Minimized code
@main def test: Unit = {
trait TyCon[+A]
trait S[T]
trait P[T] extends S[TyCon[T]] {
def consume(t: T): Unit
}
def patmat(s: S[TyCon[Int]]) = s match {
case p: P[t] =>
// "t is a type in method patmat which is an alias of Any"
// def whatist(x: t): Nothing = x
p.consume("Hi")
}
patmat(new P[Int] {
override def consume(t: Int): Unit = t + 1 // ClassCastException: class String cannot be cast to class Integer
})
}
Output
Click to expand
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:99)
at Test$$anon$1.consume(Test.scala:15)
at Test$.patmat$1(Test.scala:11)
at Test$.main(Test.scala:16)
at Test.main(Test.scala)
Expectation
The code should be rejected (by not concluding that t =:= Any
).
By putting some logs, we observe the following run:
PatternTypeConstrainer#constrainPatternType
is givenP[t]
andS[TyCon[Int]]
- In
S
, types appearing in variant positions are (recursively) widened, yieldingS[TyCon[?]]
.P[t]
is wrapped in a Skolem type - The algorithm then calls
isSubType
with(?1 : P[t])
andS[TyCon[?]]
- which leads to
S[TyCon[t]] <:< S[TyCon[?]]
- Since
S
is invariant, we have:TyCon[?] <:< TyCon[t]
which records thatAny <:< t
(from⊥..⊤ <:< t
)- and
TyCon[t] <:< TyCon[?]
(true; nothing inferred)
- which leads to
If I understand it correctly, it seems that, in GADT constraint inferencing mode, it is unwanted to deduce anything from ⊥..⊤ <:< x
(and from x <:< ⊥..⊤
too) as ⊥..⊤
is used as a placeholder more than anything else.