From b6de3c2fcc92ee0647251ae9ffea32df5ed51cc7 Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Wed, 4 Dec 2019 16:51:59 +0300 Subject: [PATCH] Disable tail-call optimization for suspend functions with Unit return type if it overrides functions with another return type. Otherwise, we cannot determine on call site that the function returns Unit and cannot { POP, PUSH Unit } in order to avoid the situation when callee's continuation resumes with non-unit result. The observed behavior is that suspend function, which should return Unit, suddenly returns other value. #KT-35262: Fixed --- .../codegen/coroutines/CoroutineCodegen.kt | 3 +- .../CoroutineTransformerMethodVisitor.kt | 48 +++++++----- .../SuspendFunctionGenerationStrategy.kt | 20 ++++- .../inline/coroutines/CoroutineTransformer.kt | 21 +++++- .../backend/jvm/codegen/CoroutineCodegen.kt | 15 +++- .../tailCallOptimizations/unit/override.kt | 42 +++++++++++ .../tailCallOptimizations/unit/override2.kt | 44 +++++++++++ .../tailCallOptimizations/unit/override3.kt | 46 ++++++++++++ .../tailCallOptimizations/unit/override4.kt | 40 ++++++++++ .../tailCallOptimizations/unit/override5.kt | 35 +++++++++ .../tailCallOptimizations/unit/override5.txt | 70 ++++++++++++++++++ .../tailCallOptimizations/unit/override6.kt | 38 ++++++++++ .../unit/overrideCrossinline.kt | 53 ++++++++++++++ .../unit/overrideOverriden.kt | 45 ++++++++++++ .../reflection.kt} | 0 .../simple.kt} | 0 .../codegen/BlackBoxCodegenTestGenerated.java | 73 ++++++++++++++++--- .../LightAnalysisModeTestGenerated.java | 73 ++++++++++++++++--- .../ir/FirBlackBoxCodegenTestGenerated.java | 73 ++++++++++++++++--- .../ir/IrBlackBoxCodegenTestGenerated.java | 73 ++++++++++++++++--- .../IrJsCodegenBoxTestGenerated.java | 13 ++++ .../semantics/JsCodegenBoxTestGenerated.java | 13 ++++ 22 files changed, 768 insertions(+), 70 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override.kt create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override2.kt create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override3.kt create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override4.kt create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.kt create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.txt create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override6.kt create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideCrossinline.kt create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideOverriden.kt rename compiler/testData/codegen/box/coroutines/tailCallOptimizations/{unitFunReturnsNonUnitCallSuspend.kt => unit/reflection.kt} (100%) rename compiler/testData/codegen/box/coroutines/tailCallOptimizations/{unitFunReturnsNonUnit.kt => unit/simple.kt} (100%) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt index f6aff56026e..fcfe41aa36d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -475,7 +475,8 @@ class CoroutineCodegenForLambda private constructor( shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization, containingClassInternalName = v.thisName, isForNamedFunction = false, - languageVersionSettings = languageVersionSettings + languageVersionSettings = languageVersionSettings, + disableTailCallOptimizationForFunctionReturningUnit = false ) return if (forInline) AddEndLabelMethodVisitor( MethodNodeCopyingMethodVisitor( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index 5f739b05b87..8d3e993dfe6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -63,6 +63,10 @@ class CoroutineTransformerMethodVisitor( // These two are needed to report diagnostics about suspension points inside critical section private val element: KtElement, private val diagnostics: DiagnosticSink, + // Since tail-call optimization of functions with Unit return type relies on ability of call-site to recognize them, + // in order to ignore return value and push Unit, when we cannot ensure this ability, for example, when the function overrides function, + // returning Any, we need to disable tail-call optimization for these functions. + private val disableTailCallOptimizationForFunctionReturningUnit: Boolean, // It's only matters for named functions, may differ from '!isStatic(access)' in case of DefaultImpls private val needDispatchReceiver: Boolean = false, // May differ from containingClassInternalName in case of DefaultImpls @@ -111,7 +115,8 @@ class CoroutineTransformerMethodVisitor( val examiner = MethodNodeExaminer( languageVersionSettings, containingClassInternalName, - methodNode + methodNode, + disableTailCallOptimizationForFunctionReturningUnit ) if (examiner.allSuspensionPointsAreTailCalls(suspensionPoints)) { examiner.replacePopsBeforeSafeUnitInstancesWithCoroutineSuspendedChecks() @@ -899,7 +904,8 @@ class CoroutineTransformerMethodVisitor( private class MethodNodeExaminer( val languageVersionSettings: LanguageVersionSettings, val containingClassInternalName: String, - val methodNode: MethodNode + val methodNode: MethodNode, + disableTailCallOptimizationForFunctionReturningUnit: Boolean ) { private val sourceFrames: Array?> = MethodTransformer.analyze(containingClassInternalName, methodNode, IgnoringCopyOperationSourceInterpreter()) @@ -912,25 +918,27 @@ private class MethodNodeExaminer( private val meaningfulPredecessorsCache = hashMapOf>() init { - // retrieve all POP insns - val pops = methodNode.instructions.asSequence().filter { it.opcode == Opcodes.POP } - // for each of them check that all successors are PUSH Unit - val popsBeforeUnitInstances = pops.map { it to it.meaningfulSuccessors() } - .filter { (_, succs) -> succs.all { it.isUnitInstance() } } - .map { it.first }.toList() - for (pop in popsBeforeUnitInstances) { - val units = pop.meaningfulSuccessors() - val allUnitsAreSafe = units.all { unit -> - // check no other predecessor exists - unit.meaningfulPredecessors().all { it in popsBeforeUnitInstances } && - // check they have only returns among successors - unit.meaningfulSuccessors().all { it.opcode == Opcodes.ARETURN } + if (!disableTailCallOptimizationForFunctionReturningUnit) { + // retrieve all POP insns + val pops = methodNode.instructions.asSequence().filter { it.opcode == Opcodes.POP } + // for each of them check that all successors are PUSH Unit + val popsBeforeUnitInstances = pops.map { it to it.meaningfulSuccessors() } + .filter { (_, succs) -> succs.all { it.isUnitInstance() } } + .map { it.first }.toList() + for (pop in popsBeforeUnitInstances) { + val units = pop.meaningfulSuccessors() + val allUnitsAreSafe = units.all { unit -> + // check no other predecessor exists + unit.meaningfulPredecessors().all { it in popsBeforeUnitInstances } && + // check they have only returns among successors + unit.meaningfulSuccessors().all { it.opcode == Opcodes.ARETURN } + } + if (!allUnitsAreSafe) continue + // save them all to the properties + popsBeforeSafeUnitInstances += pop + safeUnitInstances += units + units.flatMapTo(areturnsAfterSafeUnitInstances) { it.meaningfulSuccessors() } } - if (!allUnitsAreSafe) continue - // save them all to the properties - popsBeforeSafeUnitInstances += pop - safeUnitInstances += units - units.flatMapTo(areturnsAfterSafeUnitInstances) { it.meaningfulSuccessors() } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt index e557003142f..e8ff4783de7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.sure import org.jetbrains.org.objectweb.asm.MethodVisitor @@ -100,10 +101,27 @@ open class SuspendFunctionGenerationStrategy( shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization, needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null, internalNameForDispatchReceiver = containingClassInternalNameOrNull(), - languageVersionSettings = languageVersionSettings + languageVersionSettings = languageVersionSettings, + disableTailCallOptimizationForFunctionReturningUnit = originalSuspendDescriptor.returnType?.isUnit() == true && + originalSuspendDescriptor.overriddenDescriptors.isNotEmpty() && + !originalSuspendDescriptor.allOverriddenFunctionsReturnUnit() ) } + private fun FunctionDescriptor.allOverriddenFunctionsReturnUnit(): Boolean { + val visited = mutableSetOf() + + fun bfs(descriptor: FunctionDescriptor): Boolean { + if (!visited.add(descriptor)) return true + if (descriptor.original.returnType?.isUnit() != true) return false + for (parent in descriptor.overriddenDescriptors) { + if (!bfs(parent)) return false + } + return true + } + return bfs(this) + } + private fun containingClassInternalNameOrNull() = originalSuspendDescriptor.containingDeclaration.safeAs()?.let(state.typeMapper::mapClass)?.internalName diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt index bceae4fe982..103f4aa0a8c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt @@ -17,11 +17,20 @@ import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer import org.jetbrains.kotlin.config.isReleaseCoroutines import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass +import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader +import org.jetbrains.kotlin.load.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.* +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin +import org.jetbrains.kotlin.serialization.deserialization.getClassId +import org.jetbrains.kotlin.serialization.deserialization.getName import org.jetbrains.kotlin.utils.addToStdlib.cast -import org.jetbrains.org.objectweb.asm.MethodVisitor -import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.analysis.Frame import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter @@ -108,7 +117,8 @@ class CoroutineTransformer( languageVersionSettings = state.languageVersionSettings, shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, containingClassInternalName = classBuilder.thisName, - isForNamedFunction = false + isForNamedFunction = false, + disableTailCallOptimizationForFunctionReturningUnit = false ) ) @@ -137,6 +147,8 @@ class CoroutineTransformer( ArrayUtil.toStringArray(node.exceptions) ) ) { + // If the node already has state-machine, it is safer to generate state-machine. + val disableTailCallOptimization = methods.find { it.name == name && it.desc == node.desc }?.let { isStateMachine(it) } ?: false val stateMachineBuilder = surroundNoinlineCallsWithMarkers( node, CoroutineTransformerMethodVisitor( @@ -149,7 +161,8 @@ class CoroutineTransformer( containingClassInternalName = classBuilder.thisName, isForNamedFunction = true, needDispatchReceiver = true, - internalNameForDispatchReceiver = classBuilder.thisName + internalNameForDispatchReceiver = classBuilder.thisName, + disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization ) ) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt index 3881a944104..a17c1f04954 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.types.createType import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection +import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -40,6 +41,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource import org.jetbrains.kotlin.types.Variance import org.jetbrains.org.objectweb.asm.MethodVisitor +import org.jetbrains.org.objectweb.asm.Opcodes internal fun generateStateMachineForNamedFunction( irFunction: IrFunction, @@ -58,7 +60,7 @@ internal fun generateStateMachineForNamedFunction( val languageVersionSettings = state.languageVersionSettings assert(languageVersionSettings.isReleaseCoroutines()) { "Experimental coroutines are unsupported in JVM_IR backend" } return CoroutineTransformerMethodVisitor( - methodVisitor, access, signature.asmMethod.name, signature.asmMethod.descriptor, null, null, + methodVisitor, access, signature.asmMethod.name, signature.asmMethod.descriptor, signature.genericsSignature, null, obtainClassBuilderForCoroutineState = { continuationClassBuilder!! }, element = element, diagnostics = state.diagnostics, @@ -69,7 +71,11 @@ internal fun generateStateMachineForNamedFunction( needDispatchReceiver = irFunction.dispatchReceiverParameter != null || irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_IMPL_STATIC_FUNCTION, internalNameForDispatchReceiver = classCodegen.visitor.thisName, - putContinuationParameterToLvt = false + putContinuationParameterToLvt = false, + disableTailCallOptimizationForFunctionReturningUnit = irFunction.returnType.isUnit() && + (irFunction as? IrSimpleFunction)?.overriddenSymbols?.let { symbols -> + symbols.isNotEmpty() && symbols.any { !it.owner.returnType.isUnit() } + } == true ) } @@ -84,14 +90,15 @@ internal fun generateStateMachineForLambda( val languageVersionSettings = state.languageVersionSettings assert(languageVersionSettings.isReleaseCoroutines()) { "Experimental coroutines are unsupported in JVM_IR backend" } return CoroutineTransformerMethodVisitor( - methodVisitor, access, signature.asmMethod.name, signature.asmMethod.descriptor, null, null, + methodVisitor, access, signature.asmMethod.name, signature.asmMethod.descriptor, signature.genericsSignature, null, obtainClassBuilderForCoroutineState = { classCodegen.visitor }, element = element, diagnostics = state.diagnostics, languageVersionSettings = languageVersionSettings, shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, containingClassInternalName = classCodegen.visitor.thisName, - isForNamedFunction = false + isForNamedFunction = false, + disableTailCallOptimizationForFunctionReturningUnit = false ) } diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override.kt new file mode 100644 index 00000000000..e70a8df6413 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override.kt @@ -0,0 +1,42 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES + +import helpers.* +import kotlin.coroutines.* + +var c: Continuation<*>? = null + +suspend fun tx(lambda: () -> T): T = suspendCoroutine { c = it; lambda() } + +object Dummy + +interface Base { + suspend fun generic(): T +} + +class Derived: Base { + override suspend fun generic(): Unit { + tx { Dummy } + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var res: Any? = null + + builder { + val base: Base<*> = Derived() + res = base.generic() + } + + (c as? Continuation)?.resume(Dummy) + + return if (res != Unit) "$res" else "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override2.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override2.kt new file mode 100644 index 00000000000..4a18e1c9b4d --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override2.kt @@ -0,0 +1,44 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES + +import helpers.* +import kotlin.coroutines.* + +var c: Continuation<*>? = null + +suspend fun tx(lambda: () -> T): T = suspendCoroutine { c = it; lambda() } + +object Dummy + +interface Base { + suspend fun generic(): T +} + +abstract class Derived1: Base { +} + +class Derived2: Derived1() { + override suspend fun generic(): Unit { + tx { Dummy } + } +} +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var res: Any? = null + + builder { + val base: Base<*> = Derived2() + res = base.generic() + } + + (c as? Continuation)?.resume(Dummy) + + return if (res != Unit) "$res" else "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override3.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override3.kt new file mode 100644 index 00000000000..948463c0100 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override3.kt @@ -0,0 +1,46 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES + +import helpers.* +import kotlin.coroutines.* + +var c: Continuation<*>? = null + +suspend fun tx(lambda: () -> T): T = suspendCoroutine { c = it; lambda() } + +object Dummy + +interface Foo { + suspend fun generic(): Any +} + +interface Base { + suspend fun generic(): Unit +} + +class Derived: Base, Foo { + override suspend fun generic(): Unit { + tx { Dummy } + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var res: Any? = null + + builder { + val foo: Foo = Derived() + res = foo.generic() + } + + (c as? Continuation)?.resume(Dummy) + + return if (res != Unit) "$res" else "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override4.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override4.kt new file mode 100644 index 00000000000..7a53634ef7d --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override4.kt @@ -0,0 +1,40 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION + +import helpers.* +import kotlin.coroutines.* + +var c: Continuation<*>? = null + +suspend fun suspendHere() = TailCallOptimizationChecker.saveStackTrace() + +interface Base { + suspend fun generic(): T +} + +inline fun inlineMe(crossinline c: suspend () -> Unit) = object : Base { + override suspend fun generic(): Unit { + c() + suspendHere() + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + builder { + inlineMe { }.generic() + } + + // TODO: There should be no state-machine. Should fix in IR_BE + TailCallOptimizationChecker.checkStateMachineIn("generic") + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.kt new file mode 100644 index 00000000000..c9bd9c70e1f --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.kt @@ -0,0 +1,35 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES +// CHECK_BYTECODE_LISTING + +import helpers.* +import kotlin.coroutines.* + +var c: Continuation<*>? = null + +interface Base { + suspend fun generic(): T +} + +inline fun inlineMe(crossinline c: suspend () -> Unit) = object : Base { + override suspend fun generic(): Unit { + c(); + {}() + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + builder { + inlineMe { }.generic() + } + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.txt new file mode 100644 index 00000000000..f72b5f9bcf7 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.txt @@ -0,0 +1,70 @@ +@kotlin.Metadata +public interface Base { + public abstract @org.jetbrains.annotations.Nullable method generic(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class Override5Kt$box$1$invokeSuspend$$inlined$inlineMe$1 { + inner class Override5Kt$box$1$invokeSuspend$$inlined$inlineMe$1 + inner class Override5Kt$inlineMe$1$generic$2 + public method (): void + public @org.jetbrains.annotations.Nullable method generic(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.coroutines.jvm.internal.DebugMetadata +@kotlin.Metadata +final class Override5Kt$box$1 { + field label: int + inner class Override5Kt$box$1 + method (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 +@kotlin.coroutines.jvm.internal.DebugMetadata +public final class Override5Kt$inlineMe$1$generic$1 { + field L$0: java.lang.Object + field label: int + synthetic field result: java.lang.Object + synthetic final field this$0: Override5Kt$inlineMe$1 + inner class Override5Kt$inlineMe$1 + inner class Override5Kt$inlineMe$1$generic$1 + public method (p0: Override5Kt$inlineMe$1, p1: 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 Override5Kt$inlineMe$1$generic$2 { + public final static field INSTANCE: Override5Kt$inlineMe$1$generic$2 + inner class Override5Kt$inlineMe$1 + inner class Override5Kt$inlineMe$1$generic$2 + static method (): void + public method (): void + public synthetic method invoke(): java.lang.Object + public final method invoke(): void +} + +@kotlin.Metadata +public final class Override5Kt$inlineMe$1 { + synthetic final field $c: kotlin.jvm.functions.Function1 + inner class Override5Kt$inlineMe$1 + inner class Override5Kt$inlineMe$1$generic$1 + inner class Override5Kt$inlineMe$1$generic$2 + public method (p0: kotlin.jvm.functions.Function1): void + public @org.jetbrains.annotations.Nullable method generic$$forInline(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public @org.jetbrains.annotations.Nullable method generic(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class Override5Kt { + private static @org.jetbrains.annotations.Nullable field c: kotlin.coroutines.Continuation + inner class Override5Kt$box$1 + inner class Override5Kt$inlineMe$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 getC(): kotlin.coroutines.Continuation + public final static @org.jetbrains.annotations.NotNull method inlineMe(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): Base + public final static method setC(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override6.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override6.kt new file mode 100644 index 00000000000..ecae95eb9a6 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override6.kt @@ -0,0 +1,38 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION + +import helpers.* +import kotlin.coroutines.* + +var c: Continuation<*>? = null + +suspend fun suspendHere() = TailCallOptimizationChecker.saveStackTrace() + +interface Base { + suspend fun generic(): T +} + +inline fun inlineMe(crossinline c: suspend () -> Unit) = object : Base { + override suspend fun generic(): Unit { + c() + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + builder { + inlineMe { suspendHere() }.generic() + } + + TailCallOptimizationChecker.checkStateMachineIn("generic") + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideCrossinline.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideCrossinline.kt new file mode 100644 index 00000000000..469541625b6 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideCrossinline.kt @@ -0,0 +1,53 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES + +import helpers.* +import kotlin.coroutines.* + +var c: Continuation<*>? = null + +suspend fun tx(lambda: () -> T): T = suspendCoroutine { c = it; lambda() } + +object Dummy + +interface Base { + suspend fun callMe(a: IntArray): Unit + suspend fun callMe(s: String): Unit + suspend fun callMe(a: Array): Unit + suspend fun callMe(a: Int): T +} + +inline fun inlineMe(crossinline c: suspend () -> Unit): Base<*> { + return object: Base { + override suspend fun callMe(a: IntArray) {} + + override suspend fun callMe(a: Int): Unit { + c() + } + + override suspend fun callMe(s: String) {} + override suspend fun callMe(a: Array) {} + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var res: Any? = null + + builder { + res = inlineMe { + tx { Dummy } + }.callMe(1) + } + + (c as? Continuation)?.resume(Dummy) + + return if (res != Unit) "$res" else "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideOverriden.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideOverriden.kt new file mode 100644 index 00000000000..5bf9d302cdc --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideOverriden.kt @@ -0,0 +1,45 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES + +import helpers.* +import kotlin.coroutines.* + +var c: Continuation<*>? = null + +suspend fun tx(lambda: () -> T): T = suspendCoroutine { c = it; lambda() } + +object Dummy + +interface Base { + suspend fun generic(): T +} + +open class Derived1: Base { + override suspend fun generic() {} +} + +class Derived2: Derived1() { + override suspend fun generic(): Unit { + tx { Dummy } + } +} +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var res: Any? = null + + builder { + val base: Base<*> = Derived2() + res = base.generic() + } + + (c as? Continuation)?.resume(Dummy) + + return if (res != Unit) "$res" else "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnitCallSuspend.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/reflection.kt similarity index 100% rename from compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnitCallSuspend.kt rename to compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/reflection.kt diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnit.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/simple.kt similarity index 100% rename from compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnit.kt rename to compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/simple.kt diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 3bdfa9f0713..1eb5937c45b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -8549,16 +8549,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatch.kt", "kotlin.coroutines"); } - @TestMetadata("unitFunReturnsNonUnit.kt") - public void testUnitFunReturnsNonUnit() throws Exception { - runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnit.kt"); - } - - @TestMetadata("unitFunReturnsNonUnitCallSuspend.kt") - public void testUnitFunReturnsNonUnitCallSuspend() throws Exception { - runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnitCallSuspend.kt"); - } - @TestMetadata("unreachable.kt") public void testUnreachable() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unreachable.kt"); @@ -8568,6 +8558,69 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testWhenUnit() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/whenUnit.kt"); } + + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Unit extends AbstractBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInUnit() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("override.kt") + public void testOverride() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override.kt"); + } + + @TestMetadata("override2.kt") + public void testOverride2() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override2.kt"); + } + + @TestMetadata("override3.kt") + public void testOverride3() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override3.kt"); + } + + @TestMetadata("override4.kt") + public void testOverride4() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override4.kt"); + } + + @TestMetadata("override5.kt") + public void testOverride5() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.kt"); + } + + @TestMetadata("override6.kt") + public void testOverride6() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override6.kt"); + } + + @TestMetadata("overrideCrossinline.kt") + public void testOverrideCrossinline() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideCrossinline.kt"); + } + + @TestMetadata("overrideOverriden.kt") + public void testOverrideOverriden() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideOverriden.kt"); + } + + @TestMetadata("reflection.kt") + public void testReflection() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/reflection.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/simple.kt"); + } + } } @TestMetadata("compiler/testData/codegen/box/coroutines/tailOperations") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 1acd241c1a3..8e30108a1ca 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -8549,16 +8549,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatch.kt", "kotlin.coroutines"); } - @TestMetadata("unitFunReturnsNonUnit.kt") - public void testUnitFunReturnsNonUnit() throws Exception { - runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnit.kt"); - } - - @TestMetadata("unitFunReturnsNonUnitCallSuspend.kt") - public void testUnitFunReturnsNonUnitCallSuspend() throws Exception { - runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnitCallSuspend.kt"); - } - @TestMetadata("unreachable.kt") public void testUnreachable() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unreachable.kt"); @@ -8568,6 +8558,69 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes public void testWhenUnit() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/whenUnit.kt"); } + + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Unit extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInUnit() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("override.kt") + public void testOverride() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override.kt"); + } + + @TestMetadata("override2.kt") + public void testOverride2() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override2.kt"); + } + + @TestMetadata("override3.kt") + public void testOverride3() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override3.kt"); + } + + @TestMetadata("override4.kt") + public void testOverride4() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override4.kt"); + } + + @TestMetadata("override5.kt") + public void testOverride5() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.kt"); + } + + @TestMetadata("override6.kt") + public void testOverride6() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override6.kt"); + } + + @TestMetadata("overrideCrossinline.kt") + public void testOverrideCrossinline() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideCrossinline.kt"); + } + + @TestMetadata("overrideOverriden.kt") + public void testOverrideOverriden() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideOverriden.kt"); + } + + @TestMetadata("reflection.kt") + public void testReflection() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/reflection.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/simple.kt"); + } + } } @TestMetadata("compiler/testData/codegen/box/coroutines/tailOperations") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index e56e863a624..b7b622cbf9c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -7454,16 +7454,6 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatch.kt", "kotlin.coroutines"); } - @TestMetadata("unitFunReturnsNonUnit.kt") - public void testUnitFunReturnsNonUnit() throws Exception { - runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnit.kt"); - } - - @TestMetadata("unitFunReturnsNonUnitCallSuspend.kt") - public void testUnitFunReturnsNonUnitCallSuspend() throws Exception { - runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnitCallSuspend.kt"); - } - @TestMetadata("unreachable.kt") public void testUnreachable() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unreachable.kt"); @@ -7473,6 +7463,69 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT public void testWhenUnit() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/whenUnit.kt"); } + + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Unit extends AbstractFirBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM_IR, testDataFilePath, "// IGNORE_BACKEND_FIR: "); + } + + public void testAllFilesPresentInUnit() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("override.kt") + public void testOverride() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override.kt"); + } + + @TestMetadata("override2.kt") + public void testOverride2() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override2.kt"); + } + + @TestMetadata("override3.kt") + public void testOverride3() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override3.kt"); + } + + @TestMetadata("override4.kt") + public void testOverride4() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override4.kt"); + } + + @TestMetadata("override5.kt") + public void testOverride5() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.kt"); + } + + @TestMetadata("override6.kt") + public void testOverride6() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override6.kt"); + } + + @TestMetadata("overrideCrossinline.kt") + public void testOverrideCrossinline() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideCrossinline.kt"); + } + + @TestMetadata("overrideOverriden.kt") + public void testOverrideOverriden() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideOverriden.kt"); + } + + @TestMetadata("reflection.kt") + public void testReflection() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/reflection.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/simple.kt"); + } + } } @TestMetadata("compiler/testData/codegen/box/coroutines/tailOperations") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index a57844230f7..01ccccacc85 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -7454,16 +7454,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatch.kt", "kotlin.coroutines"); } - @TestMetadata("unitFunReturnsNonUnit.kt") - public void testUnitFunReturnsNonUnit() throws Exception { - runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnit.kt"); - } - - @TestMetadata("unitFunReturnsNonUnitCallSuspend.kt") - public void testUnitFunReturnsNonUnitCallSuspend() throws Exception { - runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unitFunReturnsNonUnitCallSuspend.kt"); - } - @TestMetadata("unreachable.kt") public void testUnreachable() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unreachable.kt"); @@ -7473,6 +7463,69 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes public void testWhenUnit() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/whenUnit.kt"); } + + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Unit extends AbstractIrBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInUnit() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("override.kt") + public void testOverride() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override.kt"); + } + + @TestMetadata("override2.kt") + public void testOverride2() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override2.kt"); + } + + @TestMetadata("override3.kt") + public void testOverride3() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override3.kt"); + } + + @TestMetadata("override4.kt") + public void testOverride4() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override4.kt"); + } + + @TestMetadata("override5.kt") + public void testOverride5() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override5.kt"); + } + + @TestMetadata("override6.kt") + public void testOverride6() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/override6.kt"); + } + + @TestMetadata("overrideCrossinline.kt") + public void testOverrideCrossinline() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideCrossinline.kt"); + } + + @TestMetadata("overrideOverriden.kt") + public void testOverrideOverriden() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/overrideOverriden.kt"); + } + + @TestMetadata("reflection.kt") + public void testReflection() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/reflection.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit/simple.kt"); + } + } } @TestMetadata("compiler/testData/codegen/box/coroutines/tailOperations") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index ffb5b980992..d2cbabfbf1b 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -6383,6 +6383,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { public void testTryCatch_1_3() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatch.kt", "kotlin.coroutines"); } + + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Unit extends AbstractIrJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInUnit() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + } } @TestMetadata("compiler/testData/codegen/box/coroutines/tailOperations") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 1b619afbd01..66575d9f10a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -7403,6 +7403,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { public void testTryCatch_1_3() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatch.kt", "kotlin.coroutines"); } + + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Unit extends AbstractJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); + } + + public void testAllFilesPresentInUnit() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); + } + } } @TestMetadata("compiler/testData/codegen/box/coroutines/tailOperations")