Extended loop data flow analysis was implemented. #KT-6283 Fixed. #KT-6284 Fixed.

A local descendant of JetTypeInfo added to save separately current data flow info and jump point data flow info together with jump opportunity.
Now data flow analysis know about loop bodies that must be executed at least once (do...while, while(true) until the first break/continue).
A set of tests for smart casts in and after loops.
Existing DoWhile and WhileTrue resolve tests corrected in accordance.
This commit is contained in:
Mikhail Glukhikh
2015-04-06 17:52:37 +03:00
parent bb808b5620
commit 8184bccda1
73 changed files with 850 additions and 24 deletions
@@ -0,0 +1,11 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
// See KT-6283
do {
p!!.length()
} while (!x())
// Do-while loop is executed at least once, so
// p should be not null here
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,11 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
// See KT-6283
do {
p!!.length()
if (p == "abc") break
} while (!x())
// p should be smart casted despite of break
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,11 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
// See KT-6283
do {
p!!.length()
if (p == "abc") continue
} while (!x())
// p should be smart casted despite of continue
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,13 @@
fun x(): Boolean { return true }
fun y(): Boolean { return false }
public fun foo(p: String?): Int {
do {
if (y()) break
// We do not always reach this statement
p!!.length()
} while (!x())
// Here we have do while loop but p is still nullable due to break before
return p<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,5 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
internal fun y(): kotlin.Boolean
@@ -0,0 +1,13 @@
fun x(): Boolean { return true }
fun y(): Boolean { return false }
public fun foo(p: String?): Int {
do {
if (y()) continue
// We do not always reach this statement
p!!.length()
} while (!x())
// Here we have do while loop but p is still nullable due to continue before
return p<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,5 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
internal fun y(): kotlin.Boolean
@@ -0,0 +1,5 @@
fun foo(s: String?): Int {
do {
} while (s!!.length() > 0)
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
}
@@ -0,0 +1,3 @@
package
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
@@ -0,0 +1,9 @@
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
do {
if (bar()) break
} while (s!!.length() > 0)
// This call is unsafe due to break
return s<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
internal fun bar(): kotlin.Boolean
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
@@ -0,0 +1,10 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
// Exotic variant with unused literal
do <!UNUSED_FUNCTION_LITERAL!>{ ->
p!!.length()
}<!> while (!x())
// Literal is not called so p.length() is unsafe
return p<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,10 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
// See KT-6283
do {
if (p != null) break
} while (!x())
// p can be null despite of the break
return p<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,5 @@
fun foo(s: String?): Int {
do {
} while (s==null)
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
}
@@ -0,0 +1,3 @@
package
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
@@ -0,0 +1,9 @@
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
do {
if (bar()) break
} while (s==null)
// This call is unsafe due to break
return s<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
internal fun bar(): kotlin.Boolean
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
@@ -0,0 +1,12 @@
public fun foo(p: String?, y: String?): Int {
do {
// After the check, smart cast should work
if (y == null) {
"null".toString()
break
}
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
p!!.length()
} while (true)
return y?.length() ?: -1
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
@@ -0,0 +1,15 @@
public fun foo(x: String?, y: String?): Int {
do {
// After the check, smart cast should work
if (x != null) {
if (x == "abc") break
y!!.length()
} else {
y!!.length()
}
// y!! in both branches
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
} while (true)
// break is possible before so !! is necessary
return y!!.length()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
@@ -0,0 +1,9 @@
public fun foo(p: String?, y: String?): Int {
do {
// After the check, smart cast should work
if (y == null) break
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
p!!.length()
} while (true)
return y?.length() ?: -1
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
@@ -0,0 +1,13 @@
fun x(): Boolean { return true }
public fun foo(p: String?, r: String?): Int {
do {
do {
p!!.length()
} while (r == null)
} while (!x())
// Auto cast possible
<!DEBUG_INFO_SMARTCAST!>r<!>.length()
// Auto cast possible
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,14 @@
fun x(): Boolean { return true }
public fun foo(p: String?, r: String?): Int {
@outer do {
do {
p!!.length()
if (!x()) continue@outer
} while (r == null)
} while (!x())
// Auto cast NOT possible due to long continue
r<!UNSAFE_CALL!>.<!>length()
// Auto cast possible
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,17 @@
fun x(): Boolean { return true }
public fun foo(p: String?, r: String?, q: String?): Int {
while(true) {
q!!.length()
do {
do {
p!!.length()
} while (!x())
} while (r == null)
if (!x()) break
}
// Smart cast is possible everywhere
<!DEBUG_INFO_SMARTCAST!>r<!>.length()
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,15 @@
fun x(): Boolean { return true }
public fun foo(p: String?, r: String?, q: String?): Int {
while(true) {
q!!.length()
do {
p!!.length()
} while (r == null)
if (!x()) break
}
// Smart cast is possible everywhere
<!DEBUG_INFO_SMARTCAST!>r<!>.length()
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,18 @@
fun x(): Boolean { return true }
public fun foo(p: String?, r: String?, q: String?): Int {
while(true) {
q!!.length()
do {
while(true) {
p!!.length()
if (x()) break
}
} while (r == null)
if (!x()) break
}
// Smart cast is possible everywhere
<!DEBUG_INFO_SMARTCAST!>r<!>.length()
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,20 @@
fun x(p: String): Boolean { return p == "abc" }
public fun foo(p: String?, r: String?, q: String?): Int {
while(true) {
q!!.length()
@loop do {
while(true) {
p!!.length()
if (x(<!DEBUG_INFO_SMARTCAST!>p<!>)) break@loop
if (x(<!DEBUG_INFO_SMARTCAST!>q<!>)) break
}
} while (r == null)
if (!x(<!DEBUG_INFO_SMARTCAST!>p<!>)) break
}
// Long break allows r == null
r<!UNSAFE_CALL!>.<!>length()
// Smart cast is possible
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int
internal fun x(/*0*/ p: kotlin.String): kotlin.Boolean
@@ -0,0 +1,19 @@
fun x(): Boolean { return true }
public fun foo(p: String?, r: String?, q: String?): Int {
@outer while(true) {
q!!.length()
do {
if (x()) continue@outer
do {
p!!.length()
} while (!x())
} while (r == null)
if (!x()) break
}
// Smart cast is possible only for q
<!DEBUG_INFO_SMARTCAST!>q<!>.length()
// But not possible for the others
r<!UNSAFE_CALL!>.<!>length()
return p<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ r: kotlin.String?, /*2*/ q: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,9 @@
public fun foo(p: String?, y: String?): Int {
do {
// After this !!, y. should be smartcasted in loop as well as outside
y!!.length()
if (p == null) break
<!DEBUG_INFO_SMARTCAST!>y<!>.length()
} while (true)
return <!DEBUG_INFO_SMARTCAST!>y<!>.length()
}
@@ -0,0 +1,3 @@
package
public fun foo(/*0*/ p: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Int
@@ -0,0 +1,6 @@
fun foo(s: String?): Int {
while (s!!.length() > 0) {
<!DEBUG_INFO_SMARTCAST!>s<!>.length()
}
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
}
@@ -0,0 +1,3 @@
package
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
@@ -0,0 +1,9 @@
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
while (s!!.length() > 0) {
<!DEBUG_INFO_SMARTCAST!>s<!>.length()
if (bar()) break
}
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
}
@@ -0,0 +1,4 @@
package
internal fun bar(): kotlin.Boolean
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
@@ -0,0 +1,5 @@
fun foo(s: String?): Int {
while (s==null) {
}
return <!DEBUG_INFO_SMARTCAST!>s<!>.length()
}
@@ -0,0 +1,3 @@
package
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
@@ -0,0 +1,9 @@
fun bar(): Boolean { return true }
fun foo(s: String?): Int {
while (s==null) {
if (bar()) break
}
// Call is unsafe due to break
return s<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
internal fun bar(): kotlin.Boolean
internal fun foo(/*0*/ s: kotlin.String?): kotlin.Int
@@ -0,0 +1,10 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
while(x()) {
p!!.length()
if (x()) break
}
// p is nullable because it's possible loop body is not executed at all
return p<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,11 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
// Like whileTrue but 2 == 2 is in use
while(2 == 2) {
p!!.length()
if (x()) break
}
// Smart cast should not work in this case, see KT-6284
return p<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,12 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
// KT-6284
while(true) {
p!!.length()
if (x()) break
}
// while (true) loop body is executed at least once
// so p is not null here
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,12 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
while(true) {
if (x()) break
if (p==null) return -1
// p is not null
<!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
// p can be null because break is earlier than return
return p<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,11 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
while(true) {
if (x()) break
// We do not always reach this statement
p!!.length()
}
// Here we have while (true) loop but p is nullable due to break before
return p<!UNSAFE_CALL!>.<!>length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean
@@ -0,0 +1,13 @@
fun x(): Boolean { return true }
public fun foo(p: String?): Int {
while(true) {
if (p==null) return -1
if (x()) break
// p is not null
<!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
// while (true) loop body with return is executed at least once
// so p is not null here
return <!DEBUG_INFO_SMARTCAST!>p<!>.length()
}
@@ -0,0 +1,4 @@
package
public fun foo(/*0*/ p: kotlin.String?): kotlin.Int
internal fun x(): kotlin.Boolean