Preliminary declaration visitor for estimating local variable's predictability for smart casts

Predictability estimation algorithm is completely new, but backward compatibility should present.
A large set of tests. Some updated tests.
Smart casts allowed for captured variables if they are not modified in closure #KT-9051 Fixed
Also #KT-8643 Fixed
Also #KT-7976 Fixed
Correct handling of lambda arguments in functions #KT-9143 Fixed
This commit is contained in:
Mikhail Glukhikh
2015-09-28 17:18:19 +03:00
parent de7d2978db
commit 4b35e3b135
84 changed files with 1047 additions and 43 deletions
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -NOTHING_TO_INLINE
inline fun <T> foo(t1: T, t2: T) = t1 ?: t2
inline fun <T> bar(<!UNUSED_PARAMETER!>l<!>: (T) -> Unit): T = null!!
fun use() {
var x: Int?
x = 5
// Write is AFTER
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
// x is nullable at the second argument
foo(bar { x = null }, x!!)
}
@@ -0,0 +1,5 @@
package
@kotlin.inline() public fun </*0*/ T> bar(/*0*/ l: (T) -> kotlin.Unit): T
@kotlin.inline() public fun </*0*/ T> foo(/*0*/ t1: T, /*1*/ t2: T): T
public fun use(): kotlin.Unit
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -NOTHING_TO_INLINE
// See KT-9143: smart cast on a variable nulled inside a lambda argument
inline fun <T> foo(t1: T, t2: T) = t1 ?: t2
inline fun <T> bar(<!UNUSED_PARAMETER!>l<!>: (T) -> Unit): T = null!!
fun use() {
var x: Int?
x = 5
// Write to x is AFTER
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
// No smart cast should be here!
foo(bar { x = null }, <!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode())
}
@@ -0,0 +1,5 @@
package
@kotlin.inline() public fun </*0*/ T> bar(/*0*/ l: (T) -> kotlin.Unit): T
@kotlin.inline() public fun </*0*/ T> foo(/*0*/ t1: T, /*1*/ t2: T): T
public fun use(): kotlin.Unit
@@ -0,0 +1,10 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun run(f: () -> Unit) = 0
fun foo(arg: Int?) {
run {
var x = arg
if (x == null) return@run
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
@@ -0,0 +1,12 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun run(f: () -> Unit) = 0
fun foo(arg: Int?) {
run {
var x = arg
while (x != null) {
x = <!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
if (x == 0) x = null
}
}
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
@@ -0,0 +1,12 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun run(f: () -> Unit) = 0
class My {
constructor(arg: Int?) {
run {
var x = arg
if (x == null) return@run
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
}
}
@@ -0,0 +1,10 @@
package
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
public final class My {
public constructor My(/*0*/ arg: kotlin.Int?)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,22 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun run(f: () -> Unit) = 0
fun foo(arg: Int?) = run {
var x = arg
if (x == null) return@run
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
class My {
fun foo(arg: Int?) = run {
var x = arg
if (x == null) return@run
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
fun Int?.bar() = run {
var x = this
if (x == null) return@run
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
}
@@ -0,0 +1,13 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Int
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
public final class My {
public constructor My()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun foo(/*0*/ arg: kotlin.Int?): kotlin.Int
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final fun kotlin.Int?.bar(): kotlin.Int
}
@@ -0,0 +1,12 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun run(f: () -> Unit) = 0
fun foo(arg: Int?) {
var x = arg
if (x == null) return
run {
// Not safe: x = null later in the owner
x<!UNSAFE_CALL!>.<!>hashCode()
}
x = null
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
@@ -0,0 +1,15 @@
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<!UNSAFE_CALL!>.<!>hashCode()
Changing().bar()
i<!UNSAFE_CALL!>.<!>hashCode()
}
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Unit
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int?
@@ -0,0 +1,15 @@
open class Base
class Derived: Base()
fun bar(derived: Derived) = derived
fun trans(n: Int, f: (Int) -> Boolean) = if (f(n)) n else null
fun foo() {
val base: Base = Derived()
if (base is Derived) {
fun can(n: Int) = n > 0
trans(42, ::can)
bar(<!DEBUG_INFO_SMARTCAST!>base<!>)
}
}
@@ -0,0 +1,19 @@
package
public fun bar(/*0*/ derived: Derived): Derived
public fun foo(): kotlin.Unit
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: (kotlin.Int) -> kotlin.Boolean): kotlin.Int?
public open class Base {
public constructor Base()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Derived : Base {
public constructor Derived()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,14 @@
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<!UNSAFE_CALL!>.<!>hashCode()
trans(<!TYPE_MISMATCH!>i<!>, ::can)
i<!UNSAFE_CALL!>.<!>hashCode()
}
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Unit
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int?
@@ -0,0 +1,15 @@
fun trans(n: Int, f: () -> Boolean) = if (f()) n else null
fun foo() {
var i: Int? = 5
if (i != null) {
// Write is AFTER this place
<!DEBUG_INFO_SMARTCAST!>i<!>.hashCode()
object {
fun bar() {
i = null
}
}.bar()
i<!UNSAFE_CALL!>.<!>hashCode()
}
}
@@ -0,0 +1,4 @@
package
public fun foo(): kotlin.Unit
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int?
@@ -0,0 +1,15 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun run(f: () -> Unit) = 0
fun foo(arg: Int?) {
var x = arg
if (x == null) return
run {
// Unsafe because of owner modification
x<!UNSAFE_CALL!>.<!>hashCode()
x = null
}
if (x != null) x = 42
// Unsafe because of lambda
x<!UNSAFE_CALL!>.<!>hashCode()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
@@ -0,0 +1,7 @@
class My {
init {
var y: Int?
y = 42
<!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
}
@@ -0,0 +1,8 @@
package
public final class My {
public constructor My()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,10 @@
fun foo() {
class My {
val x: Int
init {
var y: Int?
y = 42
x = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
}
}
@@ -0,0 +1,3 @@
package
public fun foo(): kotlin.Unit
@@ -0,0 +1,17 @@
var x: Int = 0
get() {
var y: Int? = null
if (y != null) {
return <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
return field
}
set(param) {
var y: Int? = null
if (y != null) {
field = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
else {
field = param
}
}
@@ -0,0 +1,3 @@
package
public var x: kotlin.Int
@@ -0,0 +1,12 @@
class My {
val x: Int
init {
var y: Int? = null
if (y != null) {
x = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
else {
x = 0
}
}
}
@@ -0,0 +1,9 @@
package
public final class My {
public constructor My()
public final val x: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,10 @@
class My(val z: Int) {
var x: Int = 0
constructor(arg: Int?): this(arg ?: 42) {
var y: Int?
y = arg
if (y != null) {
x = <!DEBUG_INFO_SMARTCAST!>y<!>
}
}
}
@@ -0,0 +1,11 @@
package
public final class My {
public constructor My(/*0*/ z: kotlin.Int)
public constructor My(/*0*/ arg: kotlin.Int?)
public final var x: kotlin.Int
public final val z: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,17 @@
// Based on KT-8643
public class MyClass
{
fun main() {
var str: String? = null
if (str != null)
callback {
// Nodoby writes str, smart cast is possible
method1(<!DEBUG_INFO_SMARTCAST!>str<!>)
}
}
inline fun callback(foo: () ->Unit) = foo()
fun method1(str: String) = str
}
@@ -0,0 +1,11 @@
package
public final class MyClass {
public constructor MyClass()
@kotlin.inline() public final fun callback(/*0*/ foo: () -> kotlin.Unit): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final fun main(): kotlin.Unit
public final fun method1(/*0*/ str: kotlin.String): kotlin.String
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,23 @@
class My {
val y: Int
get() {
var x: Int?
x = 3
return <!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
}
fun test() {
var x: Int?
x = 2
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
fun bb() {
var <!NAME_SHADOWING!>x<!>: Any?
x = 4
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
}
x = 4
// Really smart cast is possible but name shadowing by bb() prevents it
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
}
}
@@ -0,0 +1,10 @@
package
public final class My {
public constructor My()
public final val y: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final fun test(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,18 @@
// FILE: My.java
public interface My {
String foo(String arg);
}
// FILE: test.kt
class Your {
val x = My() {
arg: String? ->
var y = arg
val z: String
if (y != null) z = <!DEBUG_INFO_SMARTCAST!>y<!>
else z = "42"
z
}
}
@@ -0,0 +1,18 @@
package
public /*synthesized*/ fun My(/*0*/ function: (kotlin.String!) -> kotlin.String!): My
public interface My {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(/*0*/ arg: kotlin.String!): kotlin.String!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Your {
public constructor Your()
public final val x: My
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun foo(x: Int, f: () -> Unit, y: Int) {}
fun bar() {
var x: Int?
x = 4
foo(<!DEBUG_INFO_SMARTCAST!>x<!>, { x = null; x<!UNSAFE_CALL!>.<!>hashCode() }, <!SMARTCAST_IMPOSSIBLE!>x<!>)
}
@@ -0,0 +1,4 @@
package
public fun bar(): kotlin.Unit
public fun foo(/*0*/ x: kotlin.Int, /*1*/ f: () -> kotlin.Unit, /*2*/ y: kotlin.Int): kotlin.Unit
@@ -9,7 +9,7 @@ public fun foo() {
}
if (s != null) {
System.out.println(closure())
// Smart cast is possible but closure makes it harder to understand
System.out.println(s<!UNSAFE_CALL!>.<!>length)
// Smart cast is possible, nobody modifies s
System.out.println(<!DEBUG_INFO_SMARTCAST!>s<!>.length)
}
}