Introduce CHECK_TAIL_CALL_OPTIMIZATION directive

This directive generates TailCallOptimizationChecker in package helpers.
The check for tail call optimization is based on coroutine stack traces
bug (feature?): when tail call optimization hits, the continuation
object is not generated. Thus, there is no debug metadata for this
suspend function. Consequently, the coroutines stack trace does not
contain stack trace element for that function.
This check is performed by TailCallOptimizationChecker.

Since this is runtime check, unlike bytecode tests, it does not require
test data adjustments on each codegen or inliner change.

Since the check is based on debug metadata, which is JVM specific, there
is not support for other backends yet.
This commit is contained in:
Ilmir Usmanov
2019-04-04 20:33:13 +03:00
parent 40441a18cc
commit 00e952ab15
49 changed files with 633 additions and 1613 deletions
@@ -3,7 +3,6 @@
// COMMON_COROUTINES_TEST
// WITH_RUNTIME
// WITH_COROUTINES
// CHECK_BYTECODE_LISTING
import helpers.*
import COROUTINES_PACKAGE.*
@@ -1,13 +1,14 @@
// IGNORE_BACKEND: JVM_IR
// COMMON_COROUTINES_TEST
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
// CHECK_BYTECODE_LISTING
// CHECK_TAIL_CALL_OPTIMIZATION
import helpers.*
import COROUTINES_PACKAGE.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
suspend fun dummy(): Unit = Unit
suspend fun dummy() = TailCallOptimizationChecker.saveStackTrace()
suspend fun test(): Int = 1.also {
dummy()
}
@@ -21,5 +22,6 @@ fun box(): String {
builder {
res = test()
}
TailCallOptimizationChecker.checkStateMachineIn("test")
return if (res == 1) "OK" else "FAIL"
}
@@ -1,34 +0,0 @@
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class AlsoKt$box$1 {
synthetic final field $res: kotlin.jvm.internal.Ref$IntRef
field L$0: java.lang.Object
field label: int
inner class AlsoKt$box$1
method <init>(p0: kotlin.jvm.internal.Ref$IntRef, p1: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
final class AlsoKt$test$1 {
field I$0: int
field L$0: java.lang.Object
field label: int
synthetic field result: java.lang.Object
inner class AlsoKt$test$1
method <init>(p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class AlsoKt {
inner class AlsoKt$box$1
inner class AlsoKt$test$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method dummy(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method test(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
}
@@ -1,33 +0,0 @@
@kotlin.Metadata
final class AlsoKt$box$1 {
synthetic final field $res: kotlin.jvm.internal.Ref$IntRef
field L$0: java.lang.Object
inner class AlsoKt$box$1
method <init>(p0: kotlin.jvm.internal.Ref$IntRef, p1: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
public final method invoke(p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
final class AlsoKt$test$1 {
field I$0: int
field L$0: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
inner class AlsoKt$test$1
method <init>(p0: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
public final class AlsoKt {
inner class AlsoKt$box$1
inner class AlsoKt$test$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method dummy(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method test(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
}
@@ -1,83 +0,0 @@
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
public final class EpamKt$box$1$invokeSuspend$$inlined$zip$1 {
synthetic final field $source1: MyDeferred
synthetic final field $source2: MyDeferred
field L$0: java.lang.Object
field label: int
inner class EpamKt$box$1$invokeSuspend$$inlined$zip$1
public method <init>(p0: MyDeferred, p1: MyDeferred, p2: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class EpamKt$box$1 {
synthetic final field $first: MyDeferred
synthetic final field $result: kotlin.jvm.internal.Ref$IntRef
synthetic final field $second: MyDeferred
field L$0: java.lang.Object
field label: int
inner class EpamKt$box$1
method <init>(p0: kotlin.jvm.internal.Ref$IntRef, p1: MyDeferred, p2: MyDeferred, p3: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class EpamKt$box$first$1 {
field label: int
inner class EpamKt$box$first$1
method <init>(p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class EpamKt$box$second$1 {
field label: int
inner class EpamKt$box$second$1
method <init>(p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class EpamKt$zip$1 {
synthetic final field $source1: MyDeferred
synthetic final field $source2: MyDeferred
synthetic final field $zipper: kotlin.jvm.functions.Function2
field label: int
inner class EpamKt$zip$1
public method <init>(p0: kotlin.jvm.functions.Function2, p1: MyDeferred, p2: MyDeferred, p3: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class EpamKt {
inner class EpamKt$box$1
inner class EpamKt$box$first$1
inner class EpamKt$box$second$1
inner class EpamKt$zip$1
public final static @org.jetbrains.annotations.NotNull method async(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): MyDeferred
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.NotNull method zip(@org.jetbrains.annotations.NotNull p0: MyDeferred, @org.jetbrains.annotations.NotNull p1: MyDeferred, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function2): MyDeferred
}
@kotlin.Metadata
public final class MyDeferred {
private final @org.jetbrains.annotations.NotNull field t: kotlin.jvm.functions.Function1
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final @org.jetbrains.annotations.Nullable method await(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
public final @org.jetbrains.annotations.NotNull method getT(): kotlin.jvm.functions.Function1
}
@@ -1,74 +0,0 @@
@kotlin.Metadata
public final class EpamKt$box$1$doResume$$inlined$zip$1 {
synthetic final field $source1: MyDeferred
synthetic final field $source2: MyDeferred
field L$0: java.lang.Object
inner class EpamKt$box$1$doResume$$inlined$zip$1
public method <init>(p0: MyDeferred, p1: MyDeferred, p2: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
public final method invoke(p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
final class EpamKt$box$1 {
synthetic final field $first: MyDeferred
synthetic final field $result: kotlin.jvm.internal.Ref$IntRef
synthetic final field $second: MyDeferred
field L$0: java.lang.Object
inner class EpamKt$box$1
method <init>(p0: kotlin.jvm.internal.Ref$IntRef, p1: MyDeferred, p2: MyDeferred, p3: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
public final method invoke(p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
final class EpamKt$box$first$1 {
inner class EpamKt$box$first$1
method <init>(p0: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
public final method invoke(p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
final class EpamKt$box$second$1 {
inner class EpamKt$box$second$1
method <init>(p0: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
public final method invoke(p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class EpamKt$zip$1 {
synthetic final field $source1: MyDeferred
synthetic final field $source2: MyDeferred
synthetic final field $zipper: kotlin.jvm.functions.Function2
inner class EpamKt$zip$1
public method <init>(p0: kotlin.jvm.functions.Function2, p1: MyDeferred, p2: MyDeferred, p3: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
public final method invoke(p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class EpamKt {
inner class EpamKt$box$1
inner class EpamKt$box$first$1
inner class EpamKt$box$second$1
inner class EpamKt$zip$1
public final static @org.jetbrains.annotations.NotNull method async(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): MyDeferred
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.NotNull method zip(@org.jetbrains.annotations.NotNull p0: MyDeferred, @org.jetbrains.annotations.NotNull p1: MyDeferred, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function2): MyDeferred
}
@kotlin.Metadata
public final class MyDeferred {
private final @org.jetbrains.annotations.NotNull field t: kotlin.jvm.functions.Function1
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final @org.jetbrains.annotations.Nullable method await(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final @org.jetbrains.annotations.NotNull method getT(): kotlin.jvm.functions.Function1
}
@@ -1,15 +1,15 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
// CHECK_TAIL_CALL_OPTIMIZATION
import helpers.*
// CHECK_BYTECODE_LISTING
// CHECK_NEW_COUNT: function=suspendHere count=1
// CHECK_NEW_COUNT: function=mainSuspend count=1
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
inline suspend fun suspendThere(v: String): String = suspendCoroutineUninterceptedOrReturn { x ->
TailCallOptimizationChecker.saveStackTrace(x)
x.resume(v)
COROUTINE_SUSPENDED
}
@@ -30,5 +30,7 @@ fun box(): String {
result = mainSuspend()
}
TailCallOptimizationChecker.checkStateMachineIn("mainSuspend")
return result
}
@@ -1,50 +0,0 @@
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class InlineWithStateMachineKt$box$1 {
synthetic final field $result: kotlin.jvm.internal.Ref$ObjectRef
field L$0: java.lang.Object
field label: int
inner class InlineWithStateMachineKt$box$1
method <init>(p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
final class InlineWithStateMachineKt$mainSuspend$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field label: int
synthetic field result: java.lang.Object
inner class InlineWithStateMachineKt$mainSuspend$1
method <init>(p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
public final class InlineWithStateMachineKt$suspendHere$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field label: int
synthetic field result: java.lang.Object
inner class InlineWithStateMachineKt$suspendHere$1
public method <init>(p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class InlineWithStateMachineKt {
inner class InlineWithStateMachineKt$box$1
inner class InlineWithStateMachineKt$mainSuspend$1
inner class InlineWithStateMachineKt$suspendHere$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method mainSuspend(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
private final static @org.jetbrains.annotations.Nullable method suspendHere$$forInline(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
private final static @org.jetbrains.annotations.Nullable method suspendThere$$forInline(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@@ -1,50 +0,0 @@
@kotlin.Metadata
final class InlineWithStateMachineKt$box$1 {
synthetic final field $result: kotlin.jvm.internal.Ref$ObjectRef
field L$0: java.lang.Object
inner class InlineWithStateMachineKt$box$1
method <init>(p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
public final method invoke(p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
final class InlineWithStateMachineKt$mainSuspend$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
inner class InlineWithStateMachineKt$mainSuspend$1
method <init>(p0: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
public final class InlineWithStateMachineKt$suspendHere$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
inner class InlineWithStateMachineKt$suspendHere$1
public method <init>(p0: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
public final class InlineWithStateMachineKt {
inner class InlineWithStateMachineKt$box$1
inner class InlineWithStateMachineKt$mainSuspend$1
inner class InlineWithStateMachineKt$suspendHere$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method mainSuspend(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
private final static @org.jetbrains.annotations.Nullable method suspendHere$$forInline(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
private final static @org.jetbrains.annotations.Nullable method suspendThere$$forInline(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
}
@@ -1,14 +1,15 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
// CHECK_BYTECODE_LISTING
// CHECK_NEW_COUNT: function=suspendHere count=0
// COMMON_COROUTINES_TEST
// CHECK_TAIL_CALL_OPTIMIZATION
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
suspend fun suspendThere(v: String): String = suspendCoroutineUninterceptedOrReturn { x ->
TailCallOptimizationChecker.saveStackTrace(x)
x.resume(v)
COROUTINE_SUSPENDED
}
@@ -25,6 +26,7 @@ fun box(): String {
builder {
result = suspendHere()
}
TailCallOptimizationChecker.checkNoStateMachineIn("suspendHere")
return result
}
@@ -1,21 +0,0 @@
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class SimpleKt$box$1 {
synthetic final field $result: kotlin.jvm.internal.Ref$ObjectRef
field L$0: java.lang.Object
field label: int
inner class SimpleKt$box$1
method <init>(p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class SimpleKt {
inner class SimpleKt$box$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@@ -1,19 +0,0 @@
@kotlin.Metadata
final class SimpleKt$box$1 {
synthetic final field $result: kotlin.jvm.internal.Ref$ObjectRef
field L$0: java.lang.Object
inner class SimpleKt$box$1
method <init>(p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
public final method invoke(p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class SimpleKt {
inner class SimpleKt$box$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
}
@@ -0,0 +1,33 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
// CHECK_TAIL_CALL_OPTIMIZATION
import helpers.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
fun check() = true
suspend fun f_1(): Unit {
return f_2()
}
private inline suspend fun f_2(): Unit {
if (check()) return
return suspendCoroutineUninterceptedOrReturn {
TailCallOptimizationChecker.saveStackTrace(it)
COROUTINE_SUSPENDED
}
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
fun box(): String {
builder { f_1() }
TailCallOptimizationChecker.checkNoStateMachineIn("f_1")
return "OK"
}
@@ -0,0 +1,40 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
// CHECK_TAIL_CALL_OPTIMIZATION
import helpers.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
var p: Int = 5846814
private suspend fun withoutInline() {
val c = { c: Continuation<Unit> ->
TailCallOptimizationChecker.saveStackTrace(c)
if (p > 52158) Unit else COROUTINE_SUSPENDED
}
return suspendCoroutineUninterceptedOrReturn(c)
}
private suspend fun withInline() {
return suspendCoroutineUninterceptedOrReturn { c ->
TailCallOptimizationChecker.saveStackTrace(c)
if (p > 52158) Unit else COROUTINE_SUSPENDED
}
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
fun box(): String {
builder {
withoutInline()
TailCallOptimizationChecker.checkNoStateMachineIn("withoutInline")
withInline()
TailCallOptimizationChecker.checkNoStateMachineIn("withInline")
}
return "OK"
}
@@ -0,0 +1,129 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
// CHECK_TAIL_CALL_OPTIMIZATION
import helpers.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
suspend fun empty() = TailCallOptimizationChecker.saveStackTrace()
suspend fun withoutReturn() { empty() }
suspend fun withReturn() { return empty() }
suspend fun notTailCall() { empty(); empty() }
suspend fun lambdaAsParameter(c: suspend ()->Unit) { c() }
suspend fun lambdaAsParameterNotTailCall(c: suspend ()->Unit) { c(); c() }
suspend fun lambdaAsParameterReturn(c: suspend ()->Unit) { return c() }
suspend fun returnsInt() = 42.also { TailCallOptimizationChecker.saveStackTrace() }
// This should not be tail-call, since the caller should push Unit.INSTANCE on stack
suspend fun callsIntNotTailCall() { returnsInt() }
suspend fun multipleExitPoints(b: Boolean) { if (b) empty() else withoutReturn() }
suspend fun multipleExitPointsNotTailCall(b: Boolean) { if (b) empty() else returnsInt() }
fun ordinary() = 1
inline fun ordinaryInline() { ordinary() }
suspend fun multipleExitPointsWithOrdinaryInline(b: Boolean) { if (b) empty() else ordinaryInline() }
suspend fun multipleExitPointsWhen(i: Int) {
when(i) {
1 -> empty()
2 -> withReturn()
3 -> withoutReturn()
else -> lambdaAsParameter {}
}
}
suspend fun <T> generic() = (Unit as T).also { TailCallOptimizationChecker.saveStackTrace() }
suspend fun useGenericReturningUnit() {
generic<Unit>()
}
class Generic<T> {
suspend fun foo() = generic<T>()
}
suspend fun useGenericClass(g: Generic<Unit>) {
g.foo()
}
suspend fun <T> genericInferType(c: () -> T) = c().also { TailCallOptimizationChecker.saveStackTrace() }
suspend fun useGenericInferType() {
genericInferType {}
}
suspend fun nullableUnit(): Unit? = null.also { TailCallOptimizationChecker.saveStackTrace() }
suspend fun useNullableUnit() {
nullableUnit()
}
suspend fun useRunRunRunRunRun() {
run {
run {
run {
run {
run {
empty()
}
}
}
}
}
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
fun box(): String {
builder {
withoutReturn()
TailCallOptimizationChecker.checkNoStateMachineIn("withoutReturn")
withReturn()
TailCallOptimizationChecker.checkNoStateMachineIn("withReturn")
notTailCall()
TailCallOptimizationChecker.checkStateMachineIn("notTailCall")
lambdaAsParameter { TailCallOptimizationChecker.saveStackTrace() }
TailCallOptimizationChecker.checkNoStateMachineIn("lambdaAsParameter")
lambdaAsParameterNotTailCall { TailCallOptimizationChecker.saveStackTrace() }
TailCallOptimizationChecker.checkStateMachineIn("lambdaAsParameterNotTailCall")
lambdaAsParameterReturn { TailCallOptimizationChecker.saveStackTrace() }
TailCallOptimizationChecker.checkNoStateMachineIn("lambdaAsParameterReturn")
callsIntNotTailCall()
TailCallOptimizationChecker.checkStateMachineIn("callsIntNotTailCall")
multipleExitPoints(false)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPoints")
multipleExitPointsNotTailCall(false)
TailCallOptimizationChecker.checkStateMachineIn("multipleExitPointsNotTailCall")
multipleExitPointsWithOrdinaryInline(true)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPointsWithOrdinaryInline")
multipleExitPointsWhen(1)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPointsWhen")
useGenericReturningUnit()
TailCallOptimizationChecker.checkNoStateMachineIn("useGenericReturningUnit")
useGenericClass(Generic())
TailCallOptimizationChecker.checkNoStateMachineIn("useGenericClass")
useGenericInferType()
TailCallOptimizationChecker.checkNoStateMachineIn("useGenericInferType")
useNullableUnit()
TailCallOptimizationChecker.checkStateMachineIn("useNullableUnit")
useRunRunRunRunRun()
TailCallOptimizationChecker.checkNoStateMachineIn("useRunRunRunRunRun")
}
return "OK"
}
@@ -0,0 +1,33 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
// CHECK_TAIL_CALL_OPTIMIZATION
import helpers.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
suspend fun catchException(): String {
try {
return suspendWithException()
}
catch(e: Exception) {
return e.message!!
}
}
suspend fun suspendWithException(): String = "OK".also { TailCallOptimizationChecker.saveStackTrace() }
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
fun box(): String {
var res = "FAIL"
builder {
res = catchException()
}
TailCallOptimizationChecker.checkStateMachineIn("catchException")
return res
}
@@ -1,26 +1,187 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
// CHECK_TAIL_CALL_OPTIMIZATION
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
suspend fun twoReturns(c: suspend () -> Unit) {
return c()
throw RuntimeException("FAIL 1")
suspend fun empty() = TailCallOptimizationChecker.saveStackTrace()
suspend fun withoutReturn() {
empty()
}
suspend fun withReturn() {
return empty()
return empty()
}
suspend fun notTailCall() {
empty()
return empty()
empty()
}
suspend fun lambdaAsParameter(c: suspend () -> Unit) {
c()
}
suspend fun lambdaAsParameterNotTailCall(c: suspend () -> Unit) {
c()
return c()
c()
}
suspend fun lambdaAsParameterReturn(c: suspend () -> Unit) {
return c()
c()
}
suspend fun returnsInt() = 42.also { TailCallOptimizationChecker.saveStackTrace() }
// This should not be tail-call, since the caller should push Unit.INSTANCE on stack
suspend fun callsIntNotTailCall() {
returnsInt()
return
empty()
}
suspend fun multipleExitPoints(b: Boolean) {
if (b) empty() else withoutReturn()
return
empty()
}
suspend fun multipleExitPointsNotTailCall(b: Boolean) {
if (b) empty() else returnsInt()
return
empty()
}
fun ordinary() = 1
inline fun ordinaryInline() {
ordinary()
}
suspend fun multipleExitPointsWithOrdinaryInline(b: Boolean) {
if (b) empty() else ordinaryInline()
return
empty()
}
suspend fun multipleExitPointsWhen(i: Int) {
when (i) {
1 -> empty()
2 -> withReturn()
3 -> withoutReturn()
else -> lambdaAsParameter {}
}
return
empty()
}
suspend fun <T> generic() = (Unit as T).also { TailCallOptimizationChecker.saveStackTrace() }
suspend fun useGenericReturningUnit() {
generic<Unit>()
return
empty()
}
class Generic<T> {
suspend fun foo() = generic<T>()
}
suspend fun useGenericClass(g: Generic<Unit>) {
g.foo()
return
empty()
}
suspend fun <T> genericInferType(c: () -> T) = c().also { TailCallOptimizationChecker.saveStackTrace() }
suspend fun useGenericInferType() {
genericInferType {}
return
empty()
}
suspend fun nullableUnit(): Unit? = null.also { TailCallOptimizationChecker.saveStackTrace() }
suspend fun useNullableUnit() {
nullableUnit()
return
empty()
}
suspend fun useRunRunRunRunRun() {
run {
run {
run {
run {
run {
empty()
}
}
}
}
}
return
empty()
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
fun box(): String {
var res = "FAIL"
builder {
twoReturns {
res = "OK"
}
withoutReturn()
TailCallOptimizationChecker.checkNoStateMachineIn("withoutReturn")
withReturn()
TailCallOptimizationChecker.checkNoStateMachineIn("withReturn")
notTailCall()
TailCallOptimizationChecker.checkStateMachineIn("notTailCall")
lambdaAsParameter { TailCallOptimizationChecker.saveStackTrace() }
TailCallOptimizationChecker.checkNoStateMachineIn("lambdaAsParameter")
lambdaAsParameterNotTailCall { TailCallOptimizationChecker.saveStackTrace() }
TailCallOptimizationChecker.checkStateMachineIn("lambdaAsParameterNotTailCall")
lambdaAsParameterReturn { TailCallOptimizationChecker.saveStackTrace() }
TailCallOptimizationChecker.checkNoStateMachineIn("lambdaAsParameterReturn")
callsIntNotTailCall()
TailCallOptimizationChecker.checkStateMachineIn("callsIntNotTailCall")
multipleExitPoints(false)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPoints")
multipleExitPointsNotTailCall(false)
TailCallOptimizationChecker.checkStateMachineIn("multipleExitPointsNotTailCall")
multipleExitPointsWithOrdinaryInline(true)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPointsWithOrdinaryInline")
multipleExitPointsWhen(1)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPointsWhen")
useGenericReturningUnit()
TailCallOptimizationChecker.checkNoStateMachineIn("useGenericReturningUnit")
useGenericClass(Generic())
TailCallOptimizationChecker.checkNoStateMachineIn("useGenericClass")
useGenericInferType()
TailCallOptimizationChecker.checkNoStateMachineIn("useGenericInferType")
useNullableUnit()
TailCallOptimizationChecker.checkStateMachineIn("useNullableUnit")
useRunRunRunRunRun()
TailCallOptimizationChecker.checkNoStateMachineIn("useRunRunRunRunRun")
}
return res
return "OK"
}
@@ -1,10 +1,12 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
// CHECK_BYTECODE_LISTING
// CHECK_TAIL_CALL_OPTIMIZATION
import helpers.*
import COROUTINES_PACKAGE.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
sealed class X {
class A : X()
@@ -13,8 +15,15 @@ sealed class X {
var log = ""
suspend fun process(a: X.A) { log = "${log}from A;" }
suspend fun process(b: X.B) { log = "${log}from B;" }
suspend fun process(a: X.A) {
log = "${log}from A;"
TailCallOptimizationChecker.saveStackTrace()
}
suspend fun process(b: X.B) {
log = "${log}from B;"
TailCallOptimizationChecker.saveStackTrace()
}
suspend fun process(x: X) = when (x) {
is X.A -> process(x)
@@ -28,7 +37,9 @@ fun builder(c: suspend () -> Unit) {
fun box(): String {
builder {
process(X.A())
TailCallOptimizationChecker.checkNoStateMachineIn("process")
process(X.B())
TailCallOptimizationChecker.checkNoStateMachineIn("process")
}
if (log != "from A;from B;") return log
return "OK"
@@ -1,44 +0,0 @@
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class WhenUnitKt$box$1 {
field label: int
inner class WhenUnitKt$box$1
method <init>(p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class WhenUnitKt {
private static @org.jetbrains.annotations.NotNull field log: java.lang.String
inner class WhenUnitKt$box$1
static method <clinit>(): void
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.NotNull method getLog(): java.lang.String
public final static @org.jetbrains.annotations.Nullable method process(@org.jetbrains.annotations.NotNull p0: X$A, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method process(@org.jetbrains.annotations.NotNull p0: X$B, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method process(@org.jetbrains.annotations.NotNull p0: X, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
public final static method setLog(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
}
@kotlin.Metadata
public final class X$A {
inner class X$A
public method <init>(): void
}
@kotlin.Metadata
public final class X$B {
inner class X$B
public method <init>(): void
}
@kotlin.Metadata
public abstract class X {
inner class X$A
inner class X$B
private method <init>(): void
public synthetic method <init>(p0: kotlin.jvm.internal.DefaultConstructorMarker): void
}
@@ -1,42 +0,0 @@
@kotlin.Metadata
final class WhenUnitKt$box$1 {
inner class WhenUnitKt$box$1
method <init>(p0: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): kotlin.coroutines.experimental.Continuation
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
public final method invoke(p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class WhenUnitKt {
private static @org.jetbrains.annotations.NotNull field log: java.lang.String
inner class WhenUnitKt$box$1
static method <clinit>(): void
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.NotNull method getLog(): java.lang.String
public final static @org.jetbrains.annotations.Nullable method process(@org.jetbrains.annotations.NotNull p0: X$A, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method process(@org.jetbrains.annotations.NotNull p0: X$B, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method process(@org.jetbrains.annotations.NotNull p0: X, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static method setLog(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
}
@kotlin.Metadata
public final class X$A {
inner class X$A
public method <init>(): void
}
@kotlin.Metadata
public final class X$B {
inner class X$B
public method <init>(): void
}
@kotlin.Metadata
public abstract class X {
inner class X$A
inner class X$B
private method <init>(): void
public synthetic method <init>(p0: kotlin.jvm.internal.DefaultConstructorMarker): void
}