FIR CFG: when unifying flows, group statements by assignment
Consider a function `run2` that has 2 lambda arguments called in place.
We don't know the order in which they're called, so here:
var x: Any? = something
run2(
{ x = null },
{ x as String },
)
// <--
it's not correct to simply `&&` the statements together, as that would
produce `x is Nothing? && x is String && x is Any?`. Instead, statements
should be grouped by assignment first, and different groups are `||`-ed.
This means in the above example we now get `x is Nothing? || (x is Any?
&& x is String)` == `x is String?`.
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
||||
// !DUMP_CFG
|
||||
import kotlin.contracts.*
|
||||
|
||||
fun <T> n(): T? = null
|
||||
|
||||
fun <T> run2(x: () -> T, y: () -> T) {
|
||||
contract {
|
||||
callsInPlace(x, InvocationKind.EXACTLY_ONCE)
|
||||
callsInPlace(y, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
x()
|
||||
y()
|
||||
}
|
||||
|
||||
fun test1(x: String?) {
|
||||
var p = x
|
||||
if (p != null) {
|
||||
run2(
|
||||
{ p = null; n() },
|
||||
{ <!SMARTCAST_IMPOSSIBLE!>p<!>.length; 123 } // Bad: may or may not not be called first
|
||||
)
|
||||
p<!UNSAFE_CALL!>.<!>length // Bad: p = null
|
||||
}
|
||||
}
|
||||
|
||||
fun test2(x: Any?) {
|
||||
var p: Any? = x
|
||||
p.<!UNRESOLVED_REFERENCE!>length<!> // Bad
|
||||
run2({ p = null; n() }, { p as String; 123 })
|
||||
p<!UNSAFE_CALL!>.<!>length // Bad: p can be null
|
||||
p?.length // OK: p is either null or String
|
||||
}
|
||||
|
||||
fun test3(x: Any?) {
|
||||
var p: Any? = x
|
||||
p.<!UNRESOLVED_REFERENCE!>length<!> // Bad
|
||||
run2({ p = null; n() }, { p = ""; 123 })
|
||||
p<!UNSAFE_CALL!>.<!>length // Bad: p can be null
|
||||
p?.length // OK: p is either null or String
|
||||
}
|
||||
Reference in New Issue
Block a user