Support jump out of the constructor call in suspend functions

Jump out from expression (e.g., break or continue expression in call
arguments) requires stack normalization, which inserts POP instructions.
POPping an uninitialized value is similar to ASTORE, except that it
doesn't store a value to a local variable. Such POP instructions should
be removed during postprocessing of the uninitialized stores.
This commit is contained in:
Dmitry Petrov
2017-10-03 10:22:53 +03:00
parent da6841163b
commit c3d74bdabb
6 changed files with 215 additions and 7 deletions
@@ -0,0 +1,152 @@
// WITH_RUNTIME
// WITH_COROUTINES
import helpers.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
class Controller {
suspend fun suspendHere(): String = suspendCoroutineOrReturn { x ->
x.resume("K")
COROUTINE_SUSPENDED
}
suspend fun suspendWithArgument(v: String): String = suspendCoroutineOrReturn { x ->
x.resume(v)
COROUTINE_SUSPENDED
}
suspend fun suspendWithDouble(v: Double): Double = suspendCoroutineOrReturn { x ->
x.resume(v)
COROUTINE_SUSPENDED
}
}
fun builder(c: suspend Controller.() -> Unit) {
c.startCoroutine(Controller(), EmptyContinuation)
}
class A(val first: String, val second: String) {
override fun toString() = "$first$second"
}
class B(val first: String, val second: String, val third: String) {
override fun toString() = "$first$second$third"
}
class C(val a: Long, val b: Double, val c: Int, val d: String) {
override fun toString() = "$a#$b#$c#$d"
}
val condition = true
fun box(): String {
var result = "OK"
builder {
for (count in 0..3) {
val local = A(if (count > 0) break else "O", suspendHere())
if (count > 0) {
result = "fail 1: count=$count"
return@builder
}
if (local.toString() != "OK") {
result = "fail 1: $local"
return@builder
}
}
for (count in 0..3) {
val local = B(if (count > 0) break else "#", suspendWithArgument("O"), suspendHere())
if (count > 0) {
result = "fail 2: count=$count"
return@builder
}
if (local.toString() != "#OK") {
result = "fail 2: $local"
return@builder
}
}
for (count in 0..3) {
val local = B(suspendWithArgument("#"), if (count > 0) break else "O", suspendHere())
if (count > 0) {
result = "fail 3: count=$count"
return@builder
}
if (local.toString() != "#OK") {
result = "fail 3: $local"
return@builder
}
}
for (count in 0..3) {
val local = B(
"#",
B("",
if (count > 0) break else "O",
suspendWithArgument("")
).toString(),
suspendHere()
)
if (count > 0) {
result = "fail 4: count=$count"
return@builder
}
if (local.toString() != "#OK") {
result = "fail 4: $local"
return@builder
}
}
loop@for (count in 0..3) {
val local = B(
if (!condition) "1" else suspendWithArgument("#"),
when {
count > 0 -> break@loop
condition -> suspendWithArgument("O")
else -> "2"
},
if (condition) suspendHere() else suspendWithArgument("3")
)
if (count > 0) {
result = "fail 5: count=$count"
return@builder
}
if (local.toString() != "#OK") {
result = "fail 5: $local"
return@builder
}
}
for (count in 0..3) {
val local = C(
1234567890123L,
suspendWithDouble(3.14),
42,
if (count > 0) break else suspendWithArgument("OK")
)
if (count > 0) {
result = "fail 6: count=$count"
return@builder
}
if (local.toString() != "1234567890123#3.14#42#OK") {
result = "fail 6: $local"
return@builder
}
}
}
return result
}