Null check in do while loop

fun main() {
    var count = 2
    do {
        val abc = if (count == 0) {null} else {Test(true)}
        if (abc == null) {
            break
        }
        count--
    } while(abc.isOk && count >=0)
}

data class Test(var isOk: Boolean)

When compile above code,
I got the following error
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Test?
in
} while(abc.isOk && count >=0)

But there is already a null check break inside the loop, why cannot infer it inside while condition?

This has been discussed a number of times in very long and somewhat heated threads (Smart casts and nullability in single-threaded contexts - #11 by mslenc).

Fundamentally the compiler can’t be sure that abc won’t be null when you read it. (you can add an instruction in between changing abc or there might another thread changing it).

All given variables are local and not captured. This is not about multithreading.

It’s a long standing issue: https://youtrack.jetbrains.com/issue/KT-7508

1 Like

This does not apply in this case. Here you have a local variable instead of a member variable, so another thread can definitely not change abc.

Theoretically the compiler can infer that abc is not null after the if statement, but it could be that it would make compilation so slow that it is not worth implementing.

Thank you for your reference! I understand it now.

Btw, compare KT-7508 which is var
I am using val here. Will it be easier for this case? (I am not very familiar the underlying optimization though…)

ah, the point is about analyze the body for loop… so maybe the same issue.
Anyway, thanks for point that reference

Updates:
Interesting that, I found the following inferring work. (because abc is a non null type now)

The following code is working well

fun main() {
    var count = 2
    do {
        val abc = if (count == 0) {null} else {Test(true)} ?: break
        count--
    } while(abc.isOk && count >=0)
}

data class Test(var isOk: Boolean)

It can be better (and more terse):

val abc = if (count == 0) break else Test(true)