Compilation error: Smart cast to '<type>' is impossible, because '<variable>' is a local variable that is captured by a changing closure

In general, when a mutable variable is captured in a lambda function closure, smart casts are not applicable to that variable, both inside the lambda and in the declaring scope after the lambda was created.

It's because the function may escape from its enclosing scope and may be executed later in a different context, possibly multiple times and possibly in parallel. As an example, consider a hypothetical function List.forEachInParallel { ... }, which executes the given lambda function for each element of the list, but in parallel.

The compiler must generate code that will remain correct even in that severe case, so it doesn't make an assumption that the value of variable remains unchanged after the null check and thus cannot smart cast it.

However, List.forEach is quite different, because it is an inline function. The body of an inline function and the bodies of its functional parameters (unless the parameter has noinline or crossinline modifiers) are inlined at the call site, so the compiler could reason about the code in a lambda passed as an argument to inline function as if it was written directly in the calling method body making the smart cast possible.

It could, but currently, it doesn't. Simply because that feature is not implemented yet. There is an open issue for it: KT-7186.


Thanks to Ilya for the detailed explanation of the problem! You can use the standard for(item in list){...} expression like this:

var max : Int? = null
val list = listOf(1, 2, 3)
for(item in list){
    if (max == null || item > max) {
        max = item
    }
}

Tags:

Casting

Kotlin