Fix infinite loop in suspend function in case of return from finally [#KT-21961]

* add test
This commit is contained in:
Roman Artemev
2018-07-18 18:30:46 +03:00
committed by Roman Artemev
parent a81c06049a
commit dec307799a
8 changed files with 141 additions and 39 deletions
@@ -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"
}
@@ -6170,6 +6170,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt", "kotlin.coroutines");
}
@TestMetadata("returnWithFinally.kt")
public void testReturnWithFinally_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnWithFinally.kt", "kotlin.coroutines.experimental");
}
@TestMetadata("returnWithFinally.kt")
public void testReturnWithFinally_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnWithFinally.kt", "kotlin.coroutines");
}
@TestMetadata("switchLikeWhen.kt")
public void testSwitchLikeWhen_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt", "kotlin.coroutines.experimental");
@@ -6170,6 +6170,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt", "kotlin.coroutines");
}
@TestMetadata("returnWithFinally.kt")
public void testReturnWithFinally_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnWithFinally.kt", "kotlin.coroutines.experimental");
}
@TestMetadata("returnWithFinally.kt")
public void testReturnWithFinally_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnWithFinally.kt", "kotlin.coroutines");
}
@TestMetadata("switchLikeWhen.kt")
public void testSwitchLikeWhen_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt", "kotlin.coroutines.experimental");
@@ -6170,6 +6170,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt", "kotlin.coroutines");
}
@TestMetadata("returnWithFinally.kt")
public void testReturnWithFinally_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnWithFinally.kt", "kotlin.coroutines.experimental");
}
@TestMetadata("returnWithFinally.kt")
public void testReturnWithFinally_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnWithFinally.kt", "kotlin.coroutines");
}
@TestMetadata("switchLikeWhen.kt")
public void testSwitchLikeWhen_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt", "kotlin.coroutines.experimental");
@@ -290,6 +290,8 @@ class CoroutineBodyTransformer(private val context: CoroutineTransformationConte
}
}
tryStack.removeAt(tryStack.lastIndex)
// Handle finally node
if (finallyNode != null) {
currentBlock = finallyBlock
@@ -298,8 +300,6 @@ class CoroutineBodyTransformer(private val context: CoroutineTransformationConte
hasFinallyBlocks = true
}
tryStack.removeAt(tryStack.lastIndex)
currentBlock = successor
}
@@ -5430,6 +5430,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt", "kotlin.coroutines.experimental");
}
@TestMetadata("returnWithFinally.kt")
public void testReturnWithFinally_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnWithFinally.kt", "kotlin.coroutines.experimental");
}
@TestMetadata("switchLikeWhen.kt")
public void testSwitchLikeWhen_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt", "kotlin.coroutines.experimental");
@@ -5895,6 +5895,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt", "kotlin.coroutines");
}
@TestMetadata("returnWithFinally.kt")
public void testReturnWithFinally_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnWithFinally.kt", "kotlin.coroutines.experimental");
}
@TestMetadata("returnWithFinally.kt")
public void testReturnWithFinally_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/returnWithFinally.kt", "kotlin.coroutines");
}
@TestMetadata("switchLikeWhen.kt")
public void testSwitchLikeWhen_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt", "kotlin.coroutines.experimental");