Fix infinite loop in suspend function in case of return from finally [#KT-21961]
* add test
This commit is contained in:
committed by
Roman Artemev
parent
a81c06049a
commit
dec307799a
+25
-37
@@ -7,6 +7,8 @@ import helpers.*
|
||||
import COROUTINES_PACKAGE.*
|
||||
import COROUTINES_PACKAGE.intrinsics.*
|
||||
|
||||
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
@@ -15,9 +17,18 @@ class Controller {
|
||||
c.resume(value)
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
var count = 0
|
||||
fun expect(i: Int) {
|
||||
if (++count != i) throw Exception("EXPECTED $i")
|
||||
}
|
||||
|
||||
fun <T> log(value: T) {
|
||||
result += "log($value);"
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(c: suspend Controller.() -> String): String {
|
||||
fun builder(c: suspend Controller.() -> Int): String {
|
||||
val controller = Controller()
|
||||
c.startCoroutine(controller, handleResultContinuation {
|
||||
controller.result += "return($it);"
|
||||
@@ -25,45 +36,22 @@ fun builder(c: suspend Controller.() -> String): String {
|
||||
return controller.result
|
||||
}
|
||||
|
||||
fun builderUnit(c: suspend Controller.() -> Unit): String {
|
||||
val controller = Controller()
|
||||
c.startCoroutine(controller, handleResultContinuation {
|
||||
controller.result += "return;"
|
||||
})
|
||||
return controller.result
|
||||
}
|
||||
|
||||
fun <T> id(value: T) = value
|
||||
|
||||
fun box(): String {
|
||||
var value = builder {
|
||||
val res = builder {
|
||||
expect(1)
|
||||
log(1)
|
||||
try {
|
||||
if (id(23) == 23) {
|
||||
return@builder suspendAndLog("OK")
|
||||
}
|
||||
expect(2)
|
||||
suspendAndLog(2)
|
||||
} finally {
|
||||
expect(3)
|
||||
log(3)
|
||||
return@builder 4
|
||||
}
|
||||
finally {
|
||||
result += "finally;"
|
||||
}
|
||||
result += "afterFinally;"
|
||||
"shouldNotReach"
|
||||
log("FAIL")
|
||||
-1
|
||||
}
|
||||
if (value != "suspend(OK);finally;return(OK);") return "fail1: $value"
|
||||
|
||||
value = builderUnit {
|
||||
try {
|
||||
if (id(23) == 23) {
|
||||
suspendAndLog("OK")
|
||||
return@builderUnit
|
||||
}
|
||||
}
|
||||
finally {
|
||||
result += "finally;"
|
||||
}
|
||||
result += "afterFinally;"
|
||||
"shouldNotReach"
|
||||
}
|
||||
if (value != "suspend(OK);finally;return;") return "fail2: $value"
|
||||
|
||||
if (res != "log(1);suspend(2);log(3);return(4);") return "FAIL: $res"
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
import helpers.*
|
||||
import COROUTINES_PACKAGE.*
|
||||
import COROUTINES_PACKAGE.intrinsics.*
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
suspend fun <T> suspendAndLog(value: T): T = suspendCoroutineUninterceptedOrReturn { c ->
|
||||
result += "suspend($value);"
|
||||
c.resume(value)
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(c: suspend Controller.() -> String): String {
|
||||
val controller = Controller()
|
||||
c.startCoroutine(controller, handleResultContinuation {
|
||||
controller.result += "return($it);"
|
||||
})
|
||||
return controller.result
|
||||
}
|
||||
|
||||
fun builderUnit(c: suspend Controller.() -> Unit): String {
|
||||
val controller = Controller()
|
||||
c.startCoroutine(controller, handleResultContinuation {
|
||||
controller.result += "return;"
|
||||
})
|
||||
return controller.result
|
||||
}
|
||||
|
||||
fun <T> id(value: T) = value
|
||||
|
||||
fun box(): String {
|
||||
var value = builder {
|
||||
try {
|
||||
if (id(23) == 23) {
|
||||
return@builder suspendAndLog("OK")
|
||||
}
|
||||
}
|
||||
finally {
|
||||
result += "finally;"
|
||||
}
|
||||
result += "afterFinally;"
|
||||
"shouldNotReach"
|
||||
}
|
||||
if (value != "suspend(OK);finally;return(OK);") return "fail1: $value"
|
||||
|
||||
value = builderUnit {
|
||||
try {
|
||||
if (id(23) == 23) {
|
||||
suspendAndLog("OK")
|
||||
return@builderUnit
|
||||
}
|
||||
}
|
||||
finally {
|
||||
result += "finally;"
|
||||
}
|
||||
result += "afterFinally;"
|
||||
"shouldNotReach"
|
||||
}
|
||||
if (value != "suspend(OK);finally;return;") return "fail2: $value"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user