FIR checker: report SMARTCAST_IMPOSSIBLE for local variables

This commit is contained in:
Tianyu Geng
2021-05-13 17:23:18 -07:00
committed by TeamCityServer
parent 092750e215
commit 0ecc752813
38 changed files with 792 additions and 99 deletions
@@ -10,5 +10,5 @@ fun use() {
// Write to x is AFTER
x.hashCode()
// No smart cast should be here!
foo(bar { x = null }, x<!UNSAFE_CALL!>.<!>hashCode())
foo(bar { x = null }, <!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode())
}
@@ -3,7 +3,7 @@ fun foo(arg: Int?) {
var x = arg
if (x == null) return
run {
// Not safe: x = null later in the owner
// Safe: since `run` is in-place
x.hashCode()
}
x = null
@@ -3,7 +3,7 @@ fun foo(arg: Int?) {
var x = arg
if (x == null) return
run {
// Not safe: x = null later in the owner
// Safe: since `run` is in-place
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
}
x = null
@@ -1,15 +0,0 @@
fun trans(n: Int, f: () -> Boolean) = if (f()) n else null
fun foo() {
var i: Int? = 5
if (i != null) {
class Changing {
fun bar() {
i = null
}
}
i.hashCode()
Changing().bar()
i.hashCode()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun trans(n: Int, f: () -> Boolean) = if (f()) n else null
fun foo() {
@@ -1,14 +0,0 @@
fun trans(n: Int, f: () -> Boolean) = if (f()) n else null
fun foo() {
var i: Int? = 5
if (i != null) {
fun can(): Boolean {
i = null
return true
}
i.hashCode()
trans(i, ::can)
i.hashCode()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun trans(n: Int, f: () -> Boolean) = if (f()) n else null
fun foo() {
@@ -10,6 +10,6 @@ fun foo() {
i = null
}
}.bar()
i.hashCode()
<!SMARTCAST_IMPOSSIBLE!>i<!>.hashCode()
}
}
@@ -3,8 +3,8 @@ fun foo(arg: Int?) {
var x = arg
if (x == null) return
run {
// Unsafe because of owner modification
x<!UNSAFE_CALL!>.<!>hashCode()
// Stable because `run` is in-place
x.hashCode()
x = null
}
if (x != null) x = 42
@@ -3,7 +3,7 @@ fun foo(arg: Int?) {
var x = arg
if (x == null) return
run {
// Unsafe because of owner modification
// Stable because `run` is in-place
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
x = null
}
@@ -1,6 +1,6 @@
class My {
val y: Int
val y: Int
get() {
var x: Int?
x = 3
@@ -0,0 +1,56 @@
import kotlin.contracts.*
@Suppress("EXPERIMENTAL_API_USAGE_ERROR")
inline fun atLeastOnce(block: () -> Unit) {
contract {
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
}
block()
}
@Suppress("EXPERIMENTAL_API_USAGE_ERROR")
inline fun atMostOnce(block: () -> Unit) {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
block()
}
@Suppress("EXPERIMENTAL_API_USAGE_ERROR")
inline fun exactlyOnce(block: () -> Unit) {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
}
fun test() {
var s: String? = null
s = ""
atLeastOnce {
<!SMARTCAST_IMPOSSIBLE!>s<!>.length // unstable since lambda can be called twice
s = null
var s2: String? = null
s2 = ""
s2.length // local variable declared inside lambda is stable
s2 = null
}
s = ""
exactlyOnce {
s.length // stable since lambda can be called only once
s = null
var s2: String? = null
s2 = ""
s2.length // local variable declared inside lambda is stable
s2 = null
}
s = ""
atMostOnce {
s.length // stable since lambda can be called at most once
s = null
var s2: String? = null
s2 = ""
s2.length // local variable declared inside lambda is stable
s2 = null
}
}
@@ -0,0 +1,56 @@
import kotlin.contracts.*
@Suppress("EXPERIMENTAL_API_USAGE_ERROR")
inline fun atLeastOnce(block: () -> Unit) {
contract {
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
}
block()
}
@Suppress("EXPERIMENTAL_API_USAGE_ERROR")
inline fun atMostOnce(block: () -> Unit) {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
block()
}
@Suppress("EXPERIMENTAL_API_USAGE_ERROR")
inline fun exactlyOnce(block: () -> Unit) {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
}
fun test() {
var s: String? = <!VARIABLE_WITH_REDUNDANT_INITIALIZER!>null<!>
s = ""
atLeastOnce {
<!SMARTCAST_IMPOSSIBLE!>s<!>.length // unstable since lambda can be called twice
s = null
var s2: String? = <!VARIABLE_WITH_REDUNDANT_INITIALIZER!>null<!>
s2 = ""
<!DEBUG_INFO_SMARTCAST!>s2<!>.length // local variable declared inside lambda is stable
s2 = null
}
s = ""
exactlyOnce {
<!SMARTCAST_IMPOSSIBLE!>s<!>.length // stable since lambda can be called only once
s = null
var s2: String? = <!VARIABLE_WITH_REDUNDANT_INITIALIZER!>null<!>
s2 = ""
<!SMARTCAST_IMPOSSIBLE!>s2<!>.length // local variable declared inside lambda is stable
s2 = null
}
s = ""
atMostOnce {
<!SMARTCAST_IMPOSSIBLE!>s<!>.length // stable since lambda can be called at most once
s = null
var s2: String? = <!VARIABLE_WITH_REDUNDANT_INITIALIZER!>null<!>
s2 = ""
<!SMARTCAST_IMPOSSIBLE!>s2<!>.length // local variable declared inside lambda is stable
s2 = null
}
}
@@ -0,0 +1,12 @@
package
@kotlin.Suppress(names = {"EXPERIMENTAL_API_USAGE_ERROR"}) public inline fun atLeastOnce(/*0*/ block: () -> kotlin.Unit): kotlin.Unit
CallsInPlace(block, AT_LEAST_ONCE)
@kotlin.Suppress(names = {"EXPERIMENTAL_API_USAGE_ERROR"}) public inline fun atMostOnce(/*0*/ block: () -> kotlin.Unit): kotlin.Unit
CallsInPlace(block, AT_MOST_ONCE)
@kotlin.Suppress(names = {"EXPERIMENTAL_API_USAGE_ERROR"}) public inline fun exactlyOnce(/*0*/ block: () -> kotlin.Unit): kotlin.Unit
CallsInPlace(block, EXACTLY_ONCE)
public fun test(): kotlin.Unit
@@ -0,0 +1,31 @@
import kotlin.contracts.*
@Suppress("EXPERIMENTAL_API_USAGE_ERROR")
fun foo(f1: () -> Unit, f2: () -> Unit) {
contract {
callsInPlace(f1, InvocationKind.EXACTLY_ONCE)
callsInPlace(f2, InvocationKind.EXACTLY_ONCE)
}
f2()
f1()
}
fun test() {
var s: String? = null
s = ""
foo(
{ <!SMARTCAST_IMPOSSIBLE!>s<!>.length }, // unstable since lambda evaluation order is indeterministic
{ s = null },
)
s = ""
foo(
{ s = null },
{ <!SMARTCAST_IMPOSSIBLE!>s<!>.length }, // unstable since lambda evaluation order is indeterministic
)
s = ""
foo(
{ s.length }, // stable
{ s.length }, // stable
)
s = null
}
@@ -0,0 +1,31 @@
import kotlin.contracts.*
@Suppress("EXPERIMENTAL_API_USAGE_ERROR")
fun foo(f1: () -> Unit, f2: () -> Unit) {
contract {
callsInPlace(f1, InvocationKind.EXACTLY_ONCE)
callsInPlace(f2, InvocationKind.EXACTLY_ONCE)
}
f2()
f1()
}
fun test() {
var s: String? = <!VARIABLE_WITH_REDUNDANT_INITIALIZER!>null<!>
s = ""
foo(
{ <!SMARTCAST_IMPOSSIBLE!>s<!>.length }, // unstable since lambda evaluation order is indeterministic
{ s = null },
)
s = ""
foo(
{ s = null },
{ <!SMARTCAST_IMPOSSIBLE!>s<!>.length }, // unstable since lambda evaluation order is indeterministic
)
s = ""
foo(
{ <!SMARTCAST_IMPOSSIBLE!>s<!>.length }, // stable
{ <!SMARTCAST_IMPOSSIBLE!>s<!>.length }, // stable
)
s = null
}
@@ -0,0 +1,8 @@
package
@kotlin.Suppress(names = {"EXPERIMENTAL_API_USAGE_ERROR"}) public fun foo(/*0*/ f1: () -> kotlin.Unit, /*1*/ f2: () -> kotlin.Unit): kotlin.Unit
CallsInPlace(f1, EXACTLY_ONCE)
CallsInPlace(f2, EXACTLY_ONCE)
public fun test(): kotlin.Unit
@@ -5,5 +5,5 @@ fun foo(x: Int, f: () -> Unit, y: Int) {}
fun bar() {
var x: Int?
x = 4
foo(x, { x = null; x<!UNSAFE_CALL!>.<!>hashCode() }, <!ARGUMENT_TYPE_MISMATCH!>x<!>)
foo(x, { x = null; x<!UNSAFE_CALL!>.<!>hashCode() }, <!SMARTCAST_IMPOSSIBLE!>x<!>)
}
@@ -27,10 +27,10 @@ fun baz(s: String?) {
var x = s
if (x != null) {
run {
x.hashCode()
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
}
run {
x<!UNSAFE_CALL!>.<!>hashCode()
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
x = null
}
}
@@ -40,11 +40,11 @@ fun gaz(s: String?) {
var x = s
if (x != null) {
run {
x<!UNSAFE_CALL!>.<!>hashCode()
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
x = null
}
run {
x<!UNSAFE_CALL!>.<!>hashCode()
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
}
}
}
@@ -53,7 +53,7 @@ fun gav(s: String?) {
var x = s
if (x != null) {
run {
x.hashCode()
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
}
x = null
}
@@ -1,17 +0,0 @@
public fun foo() {
var s: String? = ""
fun closure(): Int {
if (s == "") {
s = null
return -1
} else if (s == null) {
return -2
} else {
return s.length // Here smartcast is possible, at least in principle
}
}
if (s != null) {
System.out.println(closure())
System.out.println(s.length) // Here smartcast is not possible due to a closure predecessor
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
public fun foo() {
var s: String? = ""
fun closure(): Int {
@@ -14,4 +15,4 @@ public fun foo() {
System.out.println(closure())
System.out.println(<!SMARTCAST_IMPOSSIBLE!>s<!>.length) // Here smartcast is not possible due to a closure predecessor
}
}
}
@@ -1,15 +0,0 @@
// See also KT-7186
fun IntArray.forEachIndexed( op: (i: Int, value: Int) -> Unit) {
for (i in 0..this.size)
op(i, this[i])
}
fun max(a: IntArray): Int? {
var maxI: Int? = null
a.forEachIndexed { i, value ->
if (maxI == null || value >= a[maxI])
maxI = i
}
return maxI
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// See also KT-7186
fun IntArray.forEachIndexed( op: (i: Int, value: Int) -> Unit) {