Smart casts for local variables not captured in a closure and not changed in a loop, see #KT-3175.

isLocalVariable added. Assignment / initialization analysis.
Control whether a variable is changed in a loop at the beginning and at the end of the loop.
Control whether a variable is captured in a closure. #KT-3175  Fixed. #KT-2266  Fixed.
Tests for variable null safety and for variables is/as operations.
Loop / closure / variable property tests are included.
Old tests changed in accordance with KT-3175. In particular, all three of testSmartcastImpossible were fixed.
This commit is contained in:
Mikhail Glukhikh
2015-03-24 17:03:01 +03:00
parent e4b1046a54
commit d5aed62410
126 changed files with 1655 additions and 71 deletions
@@ -0,0 +1,10 @@
fun foo() {
var v: Any = 42
v.<!UNRESOLVED_REFERENCE!>length<!>()
v = "abc"
<!DEBUG_INFO_SMARTCAST!>v<!>.length()
v = 42
v.<!UNRESOLVED_REFERENCE!>length<!>()
v = "abc"
<!DEBUG_INFO_SMARTCAST!>v<!>.length()
}
@@ -0,0 +1,3 @@
package
internal fun foo(): kotlin.Unit
@@ -0,0 +1,12 @@
fun x(): Boolean { return true }
public fun foo(pp: Any): Int {
var p = pp
do {
(p as String).length()
if (p == "abc") break
p = 42
} while (!x())
// Smart cast is NOT possible here
return p.<!UNRESOLVED_REFERENCE!>length<!>()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ pp: kotlin.Any): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,15 @@
public fun foo(xx: Any): Int {
var x = xx
do {
var y: Any
// After the check, smart cast should work
if (x is String) {
y = "xyz"
} else {
y = "abc"
}
// y!! in both branches
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
} while (!(x is String))
return <!DEBUG_INFO_SMARTCAST!>x<!>.length()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ xx: kotlin.Any): kotlin.Int
@@ -0,0 +1,16 @@
public fun foo(xx: Any): Int {
var x = xx
do {
var y: Any
// After the check, smart cast should work
if (x is String) {
break
} else {
y = "abc"
}
// y!! in both branches
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
} while (true)
// We could have smart cast here but with break it's hard to detect
return x.<!UNRESOLVED_REFERENCE!>length<!>()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ xx: kotlin.Any): kotlin.Int
@@ -0,0 +1,9 @@
public fun bar(s: String) {
System.out.println("Length of $s is ${s.length()}")
}
public fun foo() {
var s: Any = "not null"
if (s is String)
bar(<!DEBUG_INFO_SMARTCAST!>s<!>)
}
@@ -0,0 +1,4 @@
package
public fun bar(/*0*/ s: kotlin.String): kotlin.Unit
public fun foo(): kotlin.Unit
@@ -0,0 +1,13 @@
// See KT-5737
fun get(): Any {
return "abc"
}
fun foo(): Int {
var ss: Any = get()
return if (ss is String && <!DEBUG_INFO_SMARTCAST!>ss<!>.length() > 0)
1
else
0
}
@@ -0,0 +1,4 @@
package
internal fun foo(): kotlin.Int
internal fun get(): kotlin.Any
@@ -0,0 +1,11 @@
public fun bar(s: String) {
System.out.println("Length of $s is ${s.length()}")
}
public fun foo() {
var s: Any = "not null"
if (s is String) {
s = 42
bar(<!TYPE_MISMATCH!>s<!>)
}
}
@@ -0,0 +1,4 @@
package
public fun bar(/*0*/ s: kotlin.String): kotlin.Unit
public fun foo(): kotlin.Unit
@@ -0,0 +1,11 @@
// See KT-774
fun box() : Int {
var a : Any = 1
var d = 1
if (a is Int) {
return <!DEBUG_INFO_SMARTCAST!>a<!> + d
} else {
return 2
}
}
@@ -0,0 +1,3 @@
package
internal fun box(): kotlin.Int
@@ -0,0 +1,8 @@
fun foo() {
var v: Any = "xyz"
// It is possible in principle to provide smart cast here
// but now we decide that v is Any
v.<!UNRESOLVED_REFERENCE!>length<!>()
v = 42
v.<!UNRESOLVED_REFERENCE!>length<!>()
}
@@ -0,0 +1,3 @@
package
internal fun foo(): kotlin.Unit
@@ -0,0 +1,10 @@
class MyClass(var p: String?)
fun bar(s: String): Int {
return s.length()
}
fun foo(m: MyClass): Int {
m.p = "xyz"
return bar(<!SMARTCAST_IMPOSSIBLE!>m.p<!>)
}
@@ -0,0 +1,12 @@
package
internal fun bar(/*0*/ s: kotlin.String): kotlin.Int
internal fun foo(/*0*/ m: MyClass): kotlin.Int
internal final class MyClass {
public constructor MyClass(/*0*/ p: kotlin.String?)
internal final var p: kotlin.String?
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 MyClass(var p: String?)
fun bar(s: String?): Int {
return s?.length() ?: -1
}
fun foo(m: MyClass): Int {
m.p = "xyz"
return bar(m.p)
}
@@ -0,0 +1,12 @@
package
internal fun bar(/*0*/ s: kotlin.String?): kotlin.Int
internal fun foo(/*0*/ m: MyClass): kotlin.Int
internal final class MyClass {
public constructor MyClass(/*0*/ p: kotlin.String?)
internal final var p: kotlin.String?
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 MyClass(var p: Any)
fun bar(s: Any): Int {
return s.hashCode()
}
fun foo(m: MyClass): Int {
m.p = "xyz"
return bar(m.p)
}
@@ -0,0 +1,12 @@
package
internal fun bar(/*0*/ s: kotlin.Any): kotlin.Int
internal fun foo(/*0*/ m: MyClass): kotlin.Int
internal final class MyClass {
public constructor MyClass(/*0*/ p: kotlin.Any)
internal final var p: kotlin.Any
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 bar(s: Any): Int {
return s.hashCode()
}
class MyClass(var p: Any) {
fun foo(): Int {
p = "xyz"
return bar(p)
}
}
@@ -0,0 +1,12 @@
package
internal fun bar(/*0*/ s: kotlin.Any): kotlin.Int
internal final class MyClass {
public constructor MyClass(/*0*/ p: kotlin.Any)
internal final var p: kotlin.Any
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
internal final fun foo(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,13 @@
fun bar(s: Any): Int {
return s.hashCode()
}
class MyClass(var p: Any) {
fun foo(): Int {
p = "xyz"
if (p is String) {
return bar(p)
}
return -1
}
}
@@ -0,0 +1,12 @@
package
internal fun bar(/*0*/ s: kotlin.Any): kotlin.Int
internal final class MyClass {
public constructor MyClass(/*0*/ p: kotlin.Any)
internal final var p: kotlin.Any
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
internal final fun foo(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,9 @@
fun get(): Any {
return ""
}
fun foo(): Int {
var c: Any = get()
(c as String).length()
return <!DEBUG_INFO_SMARTCAST!>c<!>.length() // Previous line should make as unnecessary here.
}
@@ -0,0 +1,4 @@
package
internal fun foo(): kotlin.Int
internal fun get(): kotlin.Any
@@ -0,0 +1,9 @@
public fun foo() {
var i: Any = 1
if (i is Int) {
while (i != 10) {
<!UNUSED_CHANGED_VALUE!>i<!UNRESOLVED_REFERENCE!>++<!><!> // Here smart cast should not be performed due to a successor
i = ""
}
}
}
@@ -0,0 +1,3 @@
package
public fun foo(): kotlin.Unit
@@ -0,0 +1,8 @@
public fun foo() {
var i: Any = 1
if (i is Int) {
while (i != 10) {
<!DEBUG_INFO_SMARTCAST!>i<!>++
}
}
}
@@ -0,0 +1,3 @@
package
public fun foo(): kotlin.Unit
@@ -0,0 +1,13 @@
fun x(): Boolean { return true }
public fun foo(pp: Any): Int {
var p = pp
while(true) {
(p as String).length()
if (x()) break
p = 42
}
// Smart cast is NOT possible here
// (we could provide it but p = 42 makes it difficult to understand)
return p.<!UNRESOLVED_REFERENCE!>length<!>()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ pp: kotlin.Any): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,17 @@
fun String.next(): String {
return "abc"
}
fun list(start: String) {
var e: Any? = start
if (e==null) return
while (e is String) {
// Smart cast due to the loop condition
if (<!DEBUG_INFO_SMARTCAST!>e<!>.length() == 0)
break
// We still have smart cast here despite of a break
e = <!DEBUG_INFO_SMARTCAST!>e<!>.next()
}
// e can never be null but we do not know it
e<!UNSAFE_CALL!>.<!>hashCode()
}
@@ -0,0 +1,4 @@
package
internal fun list(/*0*/ start: kotlin.String): kotlin.Unit
internal fun kotlin.String.next(): kotlin.String