Support recursive local suspend functions

Previously they were trying to call constructor (incorrectly) for
recursive calls. This is redundant, since this.invoke creates one
automatically.

In addition, support callable references to recursive local suspend
functions.

 #KT-24780 Fixed
This commit is contained in:
Ilmir Usmanov
2018-11-27 17:36:44 +03:00
parent 8359887696
commit 4a828f839f
7 changed files with 163 additions and 7 deletions
@@ -0,0 +1,90 @@
// LANGUAGE_VERSION: 1.3
// WITH_RUNTIME
// WITH_COROUTINES
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
import helpers.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(handleResultContinuation {
proceed = null
})
}
suspend fun foo(until: Int): String {
val o = "O"
val k = "K"
val dot = "."
suspend fun bar(x: Int): String =
if (x == until) dot else if (x < until) o + bar(x * 2) else k + bar(x - 1)
return bar(1)
}
var proceed: (() -> Unit)? = null
suspend fun suspendHere(value: String): String = suspendCoroutineUninterceptedOrReturn { x ->
proceed = {
x.resume(value)
}
COROUTINE_SUSPENDED
}
suspend fun foo2(until: Int): String {
val o = "O"
val k = "K"
val dot = "."
suspend fun bar(x: Int): String =
if (x == until) dot else if (x < until) suspendHere(o + bar(x * 2)) else suspendHere(k + bar(x - 1))
return bar(1)
}
suspend fun fooCallableReference(until: Int): String {
val o = "O"
val k = "K"
val dot = "."
suspend fun bar(x: Int): String =
if (x == until) dot else if (x < until) o + (::bar)(x * 2) else k + (::bar)(x - 1)
return bar(1)
}
suspend fun fooCallableReferenceIndirectRecursion(until: Int): String {
val o = "O"
val k = "K"
val dot = "."
suspend fun bar(x: Int): String {
suspend fun innerO() = o + (::bar)(x * 2)
suspend fun innerK() = k + (::bar)(x - 1)
return if (x == until) dot else if (x < until) innerO() else innerK()
}
return bar(1)
}
fun box(): String {
var res = "FAIL 1"
builder {
res = foo(10)
}
if (res != "OOOOKKKKKK.") return "FAIL 1: $res"
res = "FAIL 2"
builder {
res = foo2(10)
}
while (proceed != null) {
proceed!!()
}
if (res != "OOOOKKKKKK.") return "FAIL 2: $res"
res = "FAIL 3"
builder {
res = fooCallableReference(10)
}
if (res != "OOOOKKKKKK.") return "FAIL 3: $res"
res = "FAIL 4"
builder {
res = fooCallableReferenceIndirectRecursion(10)
}
if (res != "OOOOKKKKKK.") return "FAIL 4: $res"
return "OK"
}