Why don't check(), require() and assert() result in a smart cast?

Viewed 675

Consider the following code fragment:

object C {
    @JvmStatic
    fun main(vararg args: String) {
        val s: String? = null
        check(s != null) {
            "The string is null"
        }
        require(s != null) {
            "The string is null"
        }
        assert(s != null) {
            "The string is null"
        }
        s.length
    }
}

While both check() and require() have

contract {
    returns() implies value
}

in their body, the above code still doesn't compile, forcing me to use either ?.:

s?.length

or !!:

s!!.length

Why no smart cast is performed in the above code?

1 Answers

I answered a related question a while ago on the difference between assert and require. TL;DR: assertisn't guaranteed to throw an exception, but require is. In 1.3, it also uses contracts, which means if the method returns, the compiler knows the statement is correct, and it can apply smart cast if applicable.

This explains why assert doesn't do it; returning from an assertion does not mean the statement is true. With the code you have, it would not throw an exception unless ea is true. Assert won't trigger smart cast, even with 1.3.

check and require both trigger smart cast on 1.3 and up (because of contracts), assert doesn't (no contracts and no guarantee it actually throws an exception if the condition fails).

I found this post on the Kotlin forums, asking exactly what you are. The contacts were present for quite a long time, but this post also backs up my initial assumptions: While the contacts are present, they weren't ready. Effectively disabled, as the second post mentions. Which is why smart cast doesn't work.

However, in Kotlin 1.3, the contracts were released. If you upgrade, you'll see that it does work (at least it does for me).


Full contract support was added in 1.3-M2, which is a pre-release for 1.3.

Related