FIR DFA: fix logic clear aliasing

The logic should clear back aliases as well. To ensure all back aliases
don't lose any information, statements on the original variable are
copied over to its aliases.
This commit is contained in:
Tianyu Geng
2021-07-26 21:44:46 -07:00
committed by teamcityserver
parent e495c722c7
commit 7e2f15f532
14 changed files with 233 additions and 9 deletions
@@ -0,0 +1,65 @@
fun test() {
var a: Any? = null
var b = a
var c = b
// Now both `b` and `c` are aliases of `a`.
if (a is String) {
b.length // OK
c.length // OK
}
if (b is String) {
a.length // OK
c.length // OK
}
if (c is String) {
a.length // OK
b.length // OK
}
b = 3 // break `b` -> `a`
if (a is String) {
b.<!UNRESOLVED_REFERENCE!>length<!> // error
c.length // OK, since `c` is aliased to `a`
}
if (b is String) {
a.<!UNRESOLVED_REFERENCE!>length<!> // error
c.<!UNRESOLVED_REFERENCE!>length<!> // error
}
if (c is String) {
a.length // OK, since `c` is alised to `a`
b.<!UNRESOLVED_REFERENCE!>length<!> // error
}
a = 2 // break `c` -> `a`
if (a is String) {
b.<!UNRESOLVED_REFERENCE!>length<!> // error
c.<!UNRESOLVED_REFERENCE!>length<!> // error
}
if (b is String) {
a.<!UNRESOLVED_REFERENCE!>length<!> // error
c.<!UNRESOLVED_REFERENCE!>length<!> // error
}
if (c is String) {
a.<!UNRESOLVED_REFERENCE!>length<!> // error
b.<!UNRESOLVED_REFERENCE!>length<!> // error
}
c = b // create aliasing `c` -> `b`
c.unaryPlus() // OK due to aliasing
b = ""
c.unaryPlus() // OK since `c` should carry all typing information that was on `b` before `b = ""`.
c = ""
c.length // OK
c.<!UNRESOLVED_REFERENCE!>unaryPlus<!>() // error
}
fun test2() {
var a: Any? = null
a = ""
var b = a
a = 3
b.length // OK
b.<!UNRESOLVED_REFERENCE!>unaryPlus<!>() // error
}
@@ -0,0 +1,65 @@
fun test() {
var a: Any? = null
var b = a
var c = b
// Now both `b` and `c` are aliases of `a`.
if (a is String) {
b.<!UNRESOLVED_REFERENCE!>length<!> // OK
c.<!UNRESOLVED_REFERENCE!>length<!> // OK
}
if (b is String) {
a.<!UNRESOLVED_REFERENCE!>length<!> // OK
c.<!UNRESOLVED_REFERENCE!>length<!> // OK
}
if (c is String) {
a.<!UNRESOLVED_REFERENCE!>length<!> // OK
b.<!UNRESOLVED_REFERENCE!>length<!> // OK
}
b = 3 // break `b` -> `a`
if (a is String) {
b.<!UNRESOLVED_REFERENCE!>length<!> // error
c.<!UNRESOLVED_REFERENCE!>length<!> // OK, since `c` is aliased to `a`
}
if (<!USELESS_IS_CHECK!>b is String<!>) {
a.<!UNRESOLVED_REFERENCE!>length<!> // error
c.<!UNRESOLVED_REFERENCE!>length<!> // error
}
if (c is String) {
a.<!UNRESOLVED_REFERENCE!>length<!> // OK, since `c` is alised to `a`
b.<!UNRESOLVED_REFERENCE!>length<!> // error
}
a = 2 // break `c` -> `a`
if (<!USELESS_IS_CHECK!>a is String<!>) {
b.<!UNRESOLVED_REFERENCE!>length<!> // error
c.<!UNRESOLVED_REFERENCE!>length<!> // error
}
if (<!USELESS_IS_CHECK!>b is String<!>) {
a.<!UNRESOLVED_REFERENCE!>length<!> // error
c.<!UNRESOLVED_REFERENCE!>length<!> // error
}
if (c is String) {
a.<!UNRESOLVED_REFERENCE!>length<!> // error
b.<!UNRESOLVED_REFERENCE!>length<!> // error
}
c = b // create aliasing `c` -> `b`
<!DEBUG_INFO_SMARTCAST!>c<!>.unaryPlus() // OK due to aliasing
b = ""
<!DEBUG_INFO_SMARTCAST!>c<!>.unaryPlus() // OK since `c` should carry all typing information that was on `b` before `b = ""`.
c = ""
<!DEBUG_INFO_SMARTCAST!>c<!>.length // OK
c.<!UNRESOLVED_REFERENCE!>unaryPlus<!>() // error
}
fun test2() {
var a: Any? = <!VARIABLE_WITH_REDUNDANT_INITIALIZER!>null<!>
a = ""
var b = a
a = 3
<!DEBUG_INFO_SMARTCAST!>b<!>.length // OK
b.<!UNRESOLVED_REFERENCE!>unaryPlus<!>() // error
}
@@ -0,0 +1,4 @@
package
public fun test(): kotlin.Unit
public fun test2(): kotlin.Unit
@@ -5,7 +5,7 @@ fun foo() {
val y = x
x = null
if (y != null) {
x.hashCode()
x<!UNSAFE_CALL!>.<!>hashCode()
}
}
@@ -11,7 +11,7 @@ operator fun Long?.inc() = this?.let { it + 1 }
fun bar(arg: Long?): Long {
var i = arg
if (i++ == 5L) {
return i<!UNSAFE_CALL!>--<!> + i
return i<!UNSAFE_CALL!>--<!> <!UNSAFE_OPERATOR_CALL!>+<!> i
}
if (i++ == 7L) {
return i++ <!UNSAFE_OPERATOR_CALL!>+<!> i