[FIR] Implement new bound smartcast algorithm

#KT-36055 Fixed
This commit is contained in:
Dmitriy Novozhilov
2020-02-10 17:14:58 +03:00
parent 40f907ec2f
commit 6735cc8937
103 changed files with 2197 additions and 309 deletions
@@ -2,8 +2,8 @@
fun foo(): String {
var s: String?
s = null
s?.<!UNRESOLVED_REFERENCE!>length<!>
s.<!UNRESOLVED_REFERENCE!>length<!>
s?.length
s.<!INAPPLICABLE_CANDIDATE!>length<!>
if (s == null) return s!!
var t: String? = "y"
if (t == null) t = "x"
@@ -41,7 +41,7 @@ fun f(a: SomeClass?) {
if (aa as? SomeSubClass != null) {
aa = null
// 'aa' cannot be cast to SomeSubClass
aa.<!UNRESOLVED_REFERENCE!>hashCode<!>()
aa.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
aa.<!UNRESOLVED_REFERENCE!>foo<!>
(aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!>
(aa as SomeSubClass).foo
@@ -50,7 +50,7 @@ fun f(a: SomeClass?) {
aa = null
if (b != null) {
// 'aa' cannot be cast to SomeSubClass
aa.<!UNRESOLVED_REFERENCE!>hashCode<!>()
aa.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
aa.<!UNRESOLVED_REFERENCE!>foo<!>
(aa as? SomeSubClass).<!INAPPLICABLE_CANDIDATE!>foo<!>
(aa as SomeSubClass).foo
@@ -28,11 +28,11 @@ fun test() {
x.foo().checkType { _<CharSequence?>() }
if (x is B && x is C) {
x.foo().checkType { <!INAPPLICABLE_CANDIDATE!>_<!><CharSequence?>() }
x.foo().checkType { _<CharSequence?>() }
x.baz("")
x.baz(1).checkType { <!INAPPLICABLE_CANDIDATE!>_<!><Unit>() }
x.baz(1, 2)
x.baz(1).checkType { _<Unit>() }
x.<!INAPPLICABLE_CANDIDATE!>baz<!>(1, 2)
x.foobar().checkType { _<String>() }
x.<!UNRESOLVED_REFERENCE!>foobar<!>().<!INAPPLICABLE_CANDIDATE!>checkType<!> { <!UNRESOLVED_REFERENCE!>_<!><String>() }
}
}
@@ -6,5 +6,5 @@ public fun foo(p: String?): Int {
if (x()) break
}
// p is nullable because it's possible loop body is not executed at all
return p.length
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
}
@@ -7,5 +7,5 @@ public fun foo(p: String?): Int {
if (x()) break
}
// Smart cast should not work in this case, see KT-6284
return p.length
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
}
@@ -8,7 +8,7 @@ fun foo() {
}
// TODO: this testdata fixates undesired behavior (it should be an unsafe call)
x.<!UNRESOLVED_REFERENCE!>length<!> // 'x' is unsoundly smartcasted here
x.length // 'x' is unsoundly smartcasted here
}
fun bar() {
@@ -19,5 +19,5 @@ fun bar() {
}
// TODO: this testdata fixates undesired behavior (it should be an unsafe call)
x.<!UNRESOLVED_REFERENCE!>size<!> // 'x' is unsoundly smartcasted here
x.size // 'x' is unsoundly smartcasted here
}
@@ -7,7 +7,7 @@ fun foo() {
break
}
x.<!UNRESOLVED_REFERENCE!>length<!> // 'x' is unsoundly smartcasted here
x.length // 'x' is unsoundly smartcasted here
}
fun bar() {
@@ -17,5 +17,5 @@ fun bar() {
break
}
x.<!UNRESOLVED_REFERENCE!>size<!> // 'x' is unsoundly smartcasted here
x.size // 'x' is unsoundly smartcasted here
}
@@ -9,5 +9,5 @@ fun foo(arg: Int?) {
}
if (x != null) x = 42
// Unsafe because of lambda
x.<!UNRESOLVED_REFERENCE!>hashCode<!>()
x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
@@ -5,7 +5,7 @@ public class X {
public fun fn(): Int {
if (y != null)
// With non-default getter smartcast is not possible
return y.length
return y.<!INAPPLICABLE_CANDIDATE!>length<!>
else
return 0
}
@@ -7,5 +7,5 @@ infix fun Int.bar(i: Int) = i
fun test() {
val p = A()
// For open value properties, smart casts should not work
if (p.foo is Int) p.foo bar 11
if (p.foo is Int) p.foo <!INAPPLICABLE_CANDIDATE!>bar<!> 11
}
@@ -4,10 +4,10 @@ public class X {
public fun fn(): Int {
if (x != null)
// Smartcast is not possible for variable properties
return x.length
return x.<!INAPPLICABLE_CANDIDATE!>length<!>
else if (y != null)
// Even if they are private
return y.length
return y.<!INAPPLICABLE_CANDIDATE!>length<!>
else
return 0
}
@@ -15,6 +15,6 @@ fun bar(): Int {
return when(ss) {
"abc" -> ss
else -> "xyz"
}.length
}.<!INAPPLICABLE_CANDIDATE!>length<!>
}
@@ -8,7 +8,7 @@ fun test1() {
if (newC != null) {
c = newC
}
<!INAPPLICABLE_CANDIDATE!>foo<!>(c)
foo(c)
}
fun test2() {
@@ -19,7 +19,7 @@ fun test2() {
if (newC is String) {
c = newC
}
<!INAPPLICABLE_CANDIDATE!>foo<!>(c)
foo(c)
}
fun test3() {
@@ -30,6 +30,6 @@ fun test3() {
if (newC == null) return
c = newC
<!INAPPLICABLE_CANDIDATE!>foo<!>(c)
foo(c)
}
@@ -6,5 +6,5 @@ fun foo(x: Int, f: () -> Unit, y: Int) {}
fun bar() {
var x: Int?
x = 4
foo(x, { x = null; x.<!UNRESOLVED_REFERENCE!>hashCode<!>() }, x)
foo(x, { x = null; x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() }, x)
}
@@ -13,5 +13,5 @@ fun list(start: String) {
e = e.next()
}
// e can never be null but we do not know it
e.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
e.hashCode()
}
@@ -5,7 +5,7 @@ fun foo() {
v = "abc"
v.length
v = null
v.<!UNRESOLVED_REFERENCE!>length<!>
v.<!INAPPLICABLE_CANDIDATE!>length<!>
v = "abc"
v.length
}
@@ -6,7 +6,7 @@ fun foo() {
val y = x
x = null
if (y != null) {
x.<!UNRESOLVED_REFERENCE!>hashCode<!>()
x.hashCode()
}
}
@@ -23,7 +23,7 @@ fun bar(s: String?) {
val hashCode = ss?.hashCode()
ss = null
if (hashCode != null) {
ss.<!UNRESOLVED_REFERENCE!>hashCode<!>()
ss.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
}
@@ -44,7 +44,7 @@ fun gaz(s: String?) {
x = null
}
run {
x.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
x.hashCode()
}
}
}
@@ -11,11 +11,11 @@ fun list(start: SomeObject) {
// In theory smart cast is possible here
// But in practice we have a loop with changing e
// ?: should we "or" entrance type info with condition type info?
if (<!UNRESOLVED_REFERENCE!>!<!>e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>())
if (!e.doSomething())
break
// Smart cast here is still not possible
e = e.<!INAPPLICABLE_CANDIDATE!>next<!>()
e = e.next()
} while (e != null)
// e can be null because of next()
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
e.doSomething()
}
@@ -8,5 +8,5 @@ public fun foo(pp: String?): Int {
p = null
} while (!x())
// Smart cast is NOT possible here
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
return p.length
}
@@ -9,8 +9,8 @@ fun list(start: SomeObject): SomeObject {
var e: SomeObject? = start
for (i in 0..42) {
// Unsafe calls because of nullable e at the beginning
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
e = e.<!INAPPLICABLE_CANDIDATE!>next<!>()
e.doSomething()
e = e.next()
}
// Smart cast is not possible here due to next()
return e
@@ -16,5 +16,5 @@ fun list(start: SomeObject) {
e = e.next()
}
// e can be null because of next()
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
e.doSomething()
}
@@ -76,5 +76,5 @@ fun test6() {
finally {
a = null
}
a.<!UNRESOLVED_REFERENCE!>hashCode<!>() // a is null here
a.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() // a is null here
}
@@ -4,5 +4,5 @@ fun foo() {
// It is possible in principle to provide smart cast here
v.<!INAPPLICABLE_CANDIDATE!>length<!>
v = null
v.<!UNRESOLVED_REFERENCE!>length<!>
v.<!INAPPLICABLE_CANDIDATE!>length<!>
}
@@ -6,5 +6,5 @@ fun foo() {
try {
s = null
} catch (ex: Exception) {}
s.<!UNRESOLVED_REFERENCE!>hashCode<!>()
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
@@ -12,5 +12,5 @@ fun foo() {
finally {
bar()
}
s.<!UNRESOLVED_REFERENCE!>hashCode<!>()
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
@@ -6,5 +6,5 @@ fun foo() {
try {
s = null
} catch (ex: Exception) {}
s.<!UNRESOLVED_REFERENCE!>hashCode<!>()
s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
}
@@ -2,5 +2,5 @@
fun foo(): Int {
var i: Int? = 42
i = null
return i + 1
return i <!INAPPLICABLE_CANDIDATE!>+<!> 1
}
@@ -2,5 +2,5 @@
fun foo(): Int {
var s: String? = "abc"
s = null
return s.<!UNRESOLVED_REFERENCE!>length<!>
return s.<!INAPPLICABLE_CANDIDATE!>length<!>
}
@@ -9,5 +9,5 @@ public fun foo(pp: String?): Int {
}
// Smart cast is NOT possible here
// (we could provide it but p = null makes it much harder)
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
return p.length
}
@@ -9,5 +9,5 @@ public fun foo(pp: String?): Int {
}
// Smart cast is NOT possible here
// (we could provide it but p = null makes it much harder)
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
return p.length
}
@@ -9,5 +9,5 @@ public fun foo(pp: String?): Int {
}
// Smart cast is NOT possible here
// (we could provide it but p = null makes it much harder)
return p.<!INAPPLICABLE_CANDIDATE!>length<!>
return p.length
}
@@ -13,5 +13,5 @@ fun list(start: SomeObject) {
e = e.next()
}
// e can be null because of next()
e.<!INAPPLICABLE_CANDIDATE!>doSomething<!>()
e.doSomething()
}