blob: f15e374cedafe5baf77631b35fb59271862e9dd8 [file]
// TARGET_BACKEND: WASM
// ENABLE_TAIL_CALLS
// IGNORE_KLIB_RUNTIME_ERRORS_WITH_CUSTOM_SECOND_STAGE: Wasm-JS:2.4
// ^ `-Xwasm-enable-tail-calls` was introduced in 2.5
// Direct static tail call
// WASM_CHECK_INSTRUCTION_IN_FUNCTION: instruction=return_call inFunction=staticTailCaller
// Tail call inside an IrWhen branch
// WASM_CHECK_INSTRUCTION_IN_FUNCTION: instruction=return_call inFunction=whenTailCaller
// Tail call to a Unit-returning function
// Ignored because of KT-88927
//// WASM_CHECK_INSTRUCTION_IN_FUNCTION: instruction=return_call inFunction=unitTailCaller
// Calls inside try-catch must NOT be emitted as tail calls
// WASM_CHECK_INSTRUCTION_NOT_IN_FUNCTION: instruction=return_call inFunction=tryCatchCaller
// `tailrec` is still lowered to a loop, so neither return_call nor a recursive call appears
// WASM_CHECK_INSTRUCTION_NOT_IN_FUNCTION: instruction=return_call inFunction=tailrecCaller
// WASM_CHECK_NOT_CALLED_IN_FUNCTION: shouldNotBeCalled=tailrecCaller inFunction=tailrecCaller
// Virtual dispatch tail call should produce return_call_ref
// WASM_CHECK_INSTRUCTION_IN_FUNCTION: instruction=return_call_ref inFunction=virtualTailCaller
// Interface dispatch tail call should produce return_call_ref
// WASM_CHECK_INSTRUCTION_IN_FUNCTION: instruction=return_call_ref inFunction=interfaceTailCaller
fun staticCallee(x: Int): Int = x + 1
fun staticTailCaller(x: Int): Int = staticCallee(x)
fun whenTailCaller(x: Int): Int = when {
x < 0 -> staticCallee(0)
x > 100 -> staticCallee(100)
else -> staticCallee(x)
}
fun unitCallee() {}
fun unitTailCaller() {
return unitCallee()
}
fun tryCatchCaller(x: Int): Int {
try {
return staticCallee(x)
} catch (e: Throwable) {
return -1
}
}
tailrec fun tailrecCaller(n: Int, acc: Int): Int =
if (n == 0) acc else tailrecCaller(n - 1, acc + 1)
abstract class VirtualBase {
abstract fun action(x: Int): Int
}
class VirtualImpl : VirtualBase() {
override fun action(x: Int): Int = x * 2
}
fun virtualTailCaller(b: VirtualBase, x: Int): Int = b.action(x)
interface InterfaceBase {
fun ping(x: Int): Int
}
class InterfaceImpl : InterfaceBase {
override fun ping(x: Int): Int = x + 7
}
fun interfaceTailCaller(b: InterfaceBase, x: Int): Int = b.ping(x)
// Mutual recursion deep enough to overflow the JS host stack without tail-call lowering.
fun even(n: Int): Boolean = if (n == 0) true else odd(n - 1)
fun odd(n: Int): Boolean = if (n == 0) false else even(n - 1)
fun box(): String {
if (staticTailCaller(41) != 42) return "fail static"
if (whenTailCaller(50) != 51) return "fail when"
unitTailCaller()
if (tryCatchCaller(10) != 11) return "fail try-catch"
if (tailrecCaller(50, 0) != 50) return "fail tailrec"
if (virtualTailCaller(VirtualImpl(), 21) != 42) return "fail virtual"
if (interfaceTailCaller(InterfaceImpl(), 35) != 42) return "fail interface"
if (!even(100_000)) return "fail mutual recursion (even)"
if (odd(100_000)) return "fail mutual recursion (odd)"
return "OK"
}