Issue
To simplify my real use case, let's suppose that I want to find the maximum number in a list:
var max : Int? = null
listOf(1, 2, 3).forEach {
if (max == null || it > max) {
max = it
}
}
However, compilation fails with the following error:
Smart cast to 'Int' is impossible, because 'max' is a local variable that is captured by a changing closure
Why does a changing closure prevent smart cast from working in this example?
Solution
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.
Answered By - Ilya
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.