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:
@@ -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
|
||||
Reference in New Issue
Block a user