Is/as operators are unsafe for reified types

I just noticed something in Kotlin design that concerns me. is and as operators are unsafe to use with reified types, if the reified type is parameterized. Let’s see this example:

fun Any?.fixedCast(): List<String>? {
    return this as? List<String> // unchecked cast warning
    return if (this is List<String>) this else null // compile error
}

inline fun <reified T> Any?.reifiedCast(): T? {
    return this as? T // no warnings/errors
    return if (this is T) this else null // no warnings/errors
}

val list = listOf(1, 2, 3)
    .reifiedCast<List<String>>() // no warnings/errors

val item = list?.first() // ClassCastException

This case is even mentioned in the docs here: Type checks and casts | Kotlin

I know about the type erasure and I understand what is the problem here. I’m concerned because we don’t even get any warning here. And because of this the problem may very easily propagate to other functions, making them unsafe as well. filterIsInstance() is one example. This function is unsafe to use with parameterized types, even despite the fact that it does not suppress any unchecked casts:

val list = mapOf("foo" to "bar", "baz" to 5).entries
    .filterIsInstance<Map.Entry<String, String>>()

val item = list[1].value // ClassCastException

I also understand that showing a warning here isn’t that easy, because there are two conditions for this problem to occur. One of them happens on the def-site of the inline function and another on its use-site. Maybe we could fix this problem by flagging reified types as unsafe to use with parameterized types - either explicitly by the developer or implicitly by detecting is/as. Then on use-site compiler would check whether we passed parameterized type as reified type and show a warning or compile error?

Does it make any sense? Are there any further problems or side-effects I don’t see? Should I report this on YouTrack?

In JVM it’s not possible to check cast with generic types, as they are “erased” in run time. Kotlin can’t do anything about it.

1 Like

Yes, I know this and I even mentioned this in my post. This is why both Java and Kotlin show warnings if we perform casts that can’t be checked at runtime. Normally, we can’t write unsafe code without suppressing some warnings. list as List<String> shows a warning, list is List<String> does not even compile. But with reified types we can easily write unsafe code that does not show any warnings at all. This is the problem here.

1 Like