How to unhide the unchecked warning when ClassTag is synthesised for a type with erased parameters?

Viewed 120

Pattern matching on type parameter T raises the unchecked warning

scala> def f[T](v: List[Any]): List[T] = v.collect { case x: T => x }
                                                             ^
       warning: abstract type pattern T is unchecked since it is eliminated by erasure
def f[T](v: List[Any]): List[T]

which one might try to resolve with ClassTag

scala> def f[T: scala.reflect.ClassTag](v: List[Any]): List[T] = v.collect { case x: T => x }
def f[T](v: List[Any])(implicit evidence$1: scala.reflect.ClassTag[T]): List[T]

which resolves the warning for simple class-based types

scala> f[String](List(42, "a"))
val res7: List[String] = List(a)

however now the unchecked warning becomes hidden even for types with parameterised types that have erased components at run-time

f[List[String]](List(List(42)))
val res9: List[List[String]] = List(List(42)) // oops!

How can we continue to raise the unchecked warning in the latter case?

1 Answers

Scala 3 replaces ClassTag with TypeTest, or its shorthand Typeable, which does warn when we attempt to check more than just the class component of a type

scala> def f[T: scala.reflect.Typeable](v: List[Any]): List[T] = v.collect { case x: T => x }
def f[T](v: List[Any])(implicit evidence$1: scala.reflect.Typeable[T]): List[T]

scala> f[String](List(42, "a"))
val res0: List[String] = List(a)

scala> f[List[String]](List(List(42)))
1 |f[List[String]](List(List(42)))
  |                               ^
  |               the type test for List[String] cannot be checked at runtime
val res1: List[List[String]] = List(List(42))

As smarter puts it

TypeTag doesn't exist anymore

ClassTag.unapply for type tests is unsound and shouldn't be used

instead we have TypeTest now: lampepfl/dotty#7555

Related