Condition is always false - Type check warning with Kotlin 2.0.0, not with Kotlin 1.9.24

Hello,

TL;DR: the reproducer is available here.

We have an extension function of a third-party type that is implemented as follows:

    fun Config.validateNonEmptyIfVisible(name: String) {
        this.configValues()
            .first { it.name() == name }
            .let { config ->
                if (config.visible() &&
                    (when (val value = config.value()) {
                        is Int? -> value == null
                        is Boolean? -> value == null
                        is String? -> value.isNullOrEmpty()
                        is List<*>? -> value.isEmpty()
                        else ->
                            throw IllegalArgumentException(
                                "unexpected value '$value' for configuration $name"
                            )
                    })
                ) {
                    System.err.println("Invalid value for configuration $name: Must not be blank.")
                }
            }
    }

The compiler for Kotlin 2.0.0 emits a warning for the Boolean? type check and our build is configured to fail on compilation warnings, so we currently cannot upgrade.

The same code compiles fine with Kotlin 1.9.24.

Is this is a compiler bug or is it an issue on our end?

1 Like

I think that the check for a nullable type confuses the compiler. And to be fair, it also confuses me :thinking:.

For me, either the value is null, or it is of a certain type. I do not really understand the reason for is T? (and that makes me wonder why the compiler even allow it).

Personally, I would simplify your when instruction like that:

when (val value = config.value) {
    null -> true
    is Int, is Boolean -> false
    is String -> value.isEmpty()
    is Collection<*> -> value.isEmpty()
    else -> throw IllegalArgumentException("unexpected value '$value' for configuration $name")
}
1 Like

That code confuses me, too.

If the value is null, then it’ll take the first (Int?) branch. The second branch will never see a null, so it’s equivalent to is Boolean (and so will always return false).

What do you expect the code to be doing?

(In case it’s not already clear, is checks the run-time type of value, i.e. of the object it refers to or null if it doesn’t; it ignores any static compile-time information about what it could refer to, and so it treats all nulls the same.)

The code is technically valid, even it it might not be doing exactly what you expect, so it shouldn’t generate a compilation error. But Kotlin’s quite eager to give warnings when strictly valid code is likely to be mistaken (such as when a == check can’t possibly succeed), and so a compilation warning here is arguably justified. I expect that the new Kotlin 2 compiler is a bit smarter about such cases, and so gives a warning when the older, not-quite-so-smart compiler doesn’t.

I’d do as Alexis suggests, and treat null as a separate case.

(And/or maybe reconsider whether a compilation warning should necessarily be a build failure. There are a few other rare cases where code with a warning might arguably be justified, such as checking for a null value in a variable that Kotlin thinks is non-nullable but you know has come from a Java framework and could be null in rare situations.)

2 Likes

Thanks everyone, we extract the null check in a separate clause, that indeed makes a lot more sense.

1 Like