[FIR] Local variable assignment must be propagated before loops

When performing lookahead for local variable assignments, make sure
assignments taking place within loops are being propagated before loops.
This makes sure smartcasts within non-inline declarations before the
loop are disallowed.

^KT-63867 Fixed
This commit is contained in:
Brian Norman
2024-01-02 15:06:17 -06:00
committed by Space Team
parent 10d6d95ee8
commit aae8cd2a7c
3 changed files with 72 additions and 3 deletions
@@ -333,6 +333,9 @@ internal class FirLocalVariableAssignmentAnalyzer {
override fun visitLoop(loop: FirLoop, data: MiniCfgData) {
val entry = data.flow
val assignedInside = visitElementWithLexicalScope(loop, data)
// Now that the inner variables have been discarded, the rest can be propagated to prevent smartcasts
// in declarations that came before this loop.
entry.recordAssignments(assignedInside)
// All forks in the loop should have the same set of variables assigned later, equal to the set
// at the start of the loop.
data.flow.recordAssignments(assignedInside)
@@ -468,7 +468,7 @@ fun test48() {
var x: Any = materialize()
runWithoutContract {
while (x is String) {
x.length
<!SMARTCAST_IMPOSSIBLE!>x<!>.length
x = 10
}
}
@@ -536,7 +536,7 @@ fun test55() {
runWithoutContract {
for (i in 1..3) {
require(x is String)
x.length
<!SMARTCAST_IMPOSSIBLE!>x<!>.length
x = 10
}
}
@@ -619,4 +619,37 @@ fun test63() {
x?.length ?: -1
x = null
}
}
}
fun test64() {
var x: Any = materialize()
require(x is String)
runWithoutContract {
<!SMARTCAST_IMPOSSIBLE!>x<!>.length
}
for (i in 1..3) {
x = 10
}
}
fun test65() {
var x: Any = materialize()
require(x is String)
atLeastOnce {
x.length
}
for (i in 1..3) {
x = 10
}
}
fun test66() {
var x: Any = materialize()
require(x is String)
exactlyOnce {
x.length
}
for (i in 1..3) {
x = 10
}
}
@@ -620,3 +620,36 @@ fun test63() {
x = null
}
}
fun test64() {
var x: Any = materialize()
require(x is String)
runWithoutContract {
<!SMARTCAST_IMPOSSIBLE!>x<!>.length
}
for (i in 1..3) {
x = 10
}
}
fun test65() {
var x: Any = materialize()
require(x is String)
atLeastOnce {
<!SMARTCAST_IMPOSSIBLE!>x<!>.length
}
for (i in 1..3) {
x = 10
}
}
fun test66() {
var x: Any = materialize()
require(x is String)
exactlyOnce {
<!SMARTCAST_IMPOSSIBLE!>x<!>.length
}
for (i in 1..3) {
x = 10
}
}