T
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index b7c117d..8122ace 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml
@@ -8,4 +8,7 @@ <component name="KotlinCompilerSettings"> <option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check" /> </component> -</project> + <component name="KotlinJpsPluginSettings"> + <option name="version" value="1.8.255-SNAPSHOT" /> + </component> +</project> \ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts index ac96d4e..1b23a64 100644 --- a/build.gradle.kts +++ b/build.gradle.kts
@@ -4,7 +4,7 @@ buildscript { val cacheRedirectorEnabled = findProperty("cacheRedirectorEnabled")?.toString()?.toBoolean() == true - kotlinBootstrapFrom(BootstrapOption.SpaceBootstrap(kotlinBuildProperties.kotlinBootstrapVersion!!, cacheRedirectorEnabled)) + kotlinBootstrapFrom(BootstrapOption.Local(localPath = "C:\\Users\\ilmirus\\work\\kotlin2\\build\\repo")) repositories { bootstrapKotlinRepo?.let(::maven)
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 2938194..5b76bd9 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts
@@ -7,7 +7,7 @@ val cacheRedirectorEnabled = findProperty("cacheRedirectorEnabled")?.toString()?.toBoolean() == true extra["defaultSnapshotVersion"] = kotlinBuildProperties.defaultSnapshotVersion - kotlinBootstrapFrom(BootstrapOption.SpaceBootstrap(kotlinBuildProperties.kotlinBootstrapVersion!!, cacheRedirectorEnabled)) + kotlinBootstrapFrom(BootstrapOption.Local(localPath = "C:\\Users\\ilmirus\\work\\kotlin2\\build\\repo")) repositories { if (cacheRedirectorEnabled) {
diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 59d8097..b0fea29 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java
@@ -12957,6 +12957,64 @@ } @Nested + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda") + @TestDataPath("$PROJECT_ROOT") + public class Lambda { + @Test + public void testAllFilesPresentInLambda() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("class.kt") + public void testClass() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/class.kt"); + } + + @Test + @TestMetadata("function.kt") + public void testFunction() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/function.kt"); + } + + @Test + @TestMetadata("indy.kt") + public void testIndy() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/indy.kt"); + } + + @Test + @TestMetadata("inlineLambda.kt") + public void testInlineLambda() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/inlineLambda.kt"); + } + + @Test + @TestMetadata("lambda.kt") + public void testLambda() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/lambda.kt"); + } + + @Test + @TestMetadata("object.kt") + public void testObject() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/object.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/simple.kt"); + } + + @Test + @TestMetadata("startCoroutine.kt") + public void testStartCoroutine() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/startCoroutine.kt"); + } + } + + @Nested @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") @TestDataPath("$PROJECT_ROOT") public class Unit {
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargBridgeLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargBridgeLowering.kt index feb3a61..3afc7d9 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargBridgeLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargBridgeLowering.kt
@@ -73,7 +73,8 @@ override fun visitClassNew(declaration: IrClass): IrStatement { val bigArityFunctionSuperTypes = declaration.superTypes.filterIsInstance<IrSimpleType>().filter { - it.isFunctionType && it.arguments.size > BuiltInFunctionArity.BIG_ARITY + it.isFunctionType && it.arguments.size > BuiltInFunctionArity.BIG_ARITY || + it.isSuspendFunction() && it.arguments.size >= BuiltInFunctionArity.BIG_ARITY } if (bigArityFunctionSuperTypes.isEmpty())
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index 5129333..b9af03a 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt
@@ -38,7 +38,6 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME -import org.jetbrains.kotlin.util.OperatorNameConventions internal val functionReferencePhase = makeIrFilePhase( ::FunctionReferenceLowering, @@ -50,14 +49,16 @@ private val crossinlineLambdas = HashSet<IrSimpleFunction>() private val IrFunctionReference.isIgnored: Boolean - get() = (!type.isFunctionOrKFunction() && !isSuspendFunctionReference()) || origin == JvmLoweredStatementOrigin.INLINE_LAMBDA + get() = (!type.isFunctionOrKFunction() && !isTailCallSuspendFunctionReference()) || + origin == JvmLoweredStatementOrigin.INLINE_LAMBDA // `suspend` function references are the same as non-`suspend` ones, just with an extra continuation parameter; // however, suspending lambdas require different generation implemented in SuspendLambdaLowering // because they are also their own continuation classes. // TODO: Currently, origin of callable references explicitly written in source code is null. Do we need to create one? - private fun IrFunctionReference.isSuspendFunctionReference(): Boolean = isSuspend && - (origin == null || origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE || origin == IrStatementOrigin.SUSPEND_CONVERSION) + private fun IrFunctionReference.isTailCallSuspendFunctionReference(): Boolean = isSuspend && + (origin == null || origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE || origin == IrStatementOrigin.SUSPEND_CONVERSION + || ((symbol.owner as? IrFunction)?.isTailCallSuspendLambda() == true && origin == IrStatementOrigin.LAMBDA)) override fun lower(irFile: IrFile) { irFile.findInlineLambdas(context) { argument, _, parameter, _ ->
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt index 7ffbbb2..b0b4012 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt
@@ -28,13 +28,12 @@ import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrBlock -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrFunctionReference -import org.jetbrains.kotlin.ir.expressions.IrGetValue +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.overrides.buildFakeOverrideMember import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol +import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -52,7 +51,9 @@ internal val suspendLambdaPhase = makeIrFilePhase( ::SuspendLambdaLowering, "SuspendLambda", - "Transform suspend lambdas into continuation classes" + "Transform suspend lambdas into continuation classes", + // Function reference phase generates indy-metafactory calls to replace. + prerequisite = setOf(functionReferencePhase) ) private fun IrFunction.capturesCrossinline(): Boolean { @@ -112,11 +113,65 @@ if (reference.isSuspend && reference.origin.isLambda) { assert(expression.statements.size == 2 && expression.statements[0] is IrFunction) expression.transformChildrenVoid(this) + val isTailCall = (expression.statements[0] as IrFunction).isTailCallSuspendLambda() + if (isTailCall) return super.visitBlock(expression) val parent = currentDeclarationParent ?: error("No current declaration parent at ${reference.dump()}") return generateAnonymousObjectForLambda(reference, parent) } return super.visitBlock(expression) } + + // FunctionReferencePhase generates raw function references to SuspendFunctionN's suspend invoke functions, + // replace them with function references with Function{N+1}'s invoke functions with continuation parameter. + override fun visitRawFunctionReference(expression: IrRawFunctionReference): IrExpression { + if (expression.symbol.owner.isSuspend) { + val function = expression.symbol.owner + val parentClass = function.parentAsClass + if (parentClass.parent == context.ir.symbols.kotlinJvmInternalInvokeDynamicPackage) { + // RAW_FUNCTION_REFERENCE 'public abstract fun invoke (p1: kotlin.String): kotlin.String [suspend,fake_override,operator] + // declared in kotlin.jvm.internal.invokeDynamic.<fake>' type=kotlin.Any + // -> + // RAW_FUNCTION_REFERENCE 'public abstract fun invoke (p1: kotlin.String, p2: kotlin.coroutines.Continuation<String>): kotlin.Any? [fake_override,operator] + // declared in kotlin.jvm.internal.invokeDynamic.<fake>' type=kotlin.Any + val fakeClass = context.irFactory.buildClass { name = Name.special("<fake>") } + fakeClass.parent = context.ir.symbols.kotlinJvmInternalInvokeDynamicPackage + + val superType = buildOrdinaryFunctionTypeFromSuspendInvokeTypes( + function.valueParameters.map { it.type }, function.returnType + ) + + val invokeMethod = superType.classOrNull!!.functions.single { it.owner.modality == Modality.ABSTRACT }.owner + + val fakeInstanceMethod = buildFakeOverrideMember(superType, invokeMethod, fakeClass) as IrSimpleFunction + (fakeInstanceMethod as IrFakeOverrideFunction).acquireSymbol(IrSimpleFunctionSymbolImpl()) + fakeInstanceMethod.overriddenSymbols = listOf(invokeMethod.symbol) + return IrRawFunctionReferenceImpl( + expression.startOffset, expression.endOffset, expression.type, fakeInstanceMethod.symbol + ) + } else { + val arity = expression.symbol.owner.valueParameters.size + val nonSuspendFunctionalType = context.ir.symbols.functionN(arity + 1) + + val invokeMethod = nonSuspendFunctionalType.functions.single { it.owner.modality == Modality.ABSTRACT } + return IrRawFunctionReferenceImpl( + expression.startOffset, expression.endOffset, expression.type, invokeMethod + ) + } + } else { + return super.visitRawFunctionReference(expression) + } + } + + private fun buildOrdinaryFunctionTypeFromSuspendInvokeTypes(types: List<IrType>, returnType: IrType): IrType { + val arguments = buildList { + addAll(types) + + add(context.ir.symbols.continuationClass.typeWith(returnType)) + add(context.irBuiltIns.anyNType) + } + + return context.ir.symbols.functionN(arguments.size - 1).typeWith(arguments) + } }) } @@ -365,3 +420,50 @@ private data class ParameterInfo(val field: IrField?, val type: IrType, val name: Name, val origin: IrDeclarationOrigin) { val isUsed = field != null } + +internal fun IrFunction.isTailCallSuspendLambda(): Boolean { + if (!isSuspend || origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) return false + + var isTailCall = true + + val tailCallChecker = object : IrElementVisitorVoid { + val tailCalls = mutableSetOf<IrCall>() + + override fun visitElement(element: IrElement) { + // If we already found non-tail-call element, there is no need to visit the rest of the tree + if (isTailCall) { + element.acceptChildrenVoid(this) + } + } + + override fun visitReturn(expression: IrReturn) { + if (expression.value is IrCall) { + tailCalls += expression.value as IrCall + } + super.visitReturn(expression) + } + + override fun visitCall(expression: IrCall) { + if (expression.isSuspend && expression !in tailCalls) { + isTailCall = false + } else { + super.visitCall(expression) + } + } + + override fun visitBlock(expression: IrBlock) { + // Do not cross lambda boundaries + } + + override fun visitFunction(declaration: IrFunction) { + // Do not cross function boundaries + } + + override fun visitClass(declaration: IrClass) { + // Do not cross class boundaries + } + } + + acceptChildrenVoid(tailCallChecker) + return isTailCall +} \ No newline at end of file
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt index 8aeb64d..7fe4070 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt
@@ -54,7 +54,9 @@ internal val typeOperatorLowering = makeIrFilePhase( ::TypeOperatorLowering, name = "TypeOperatorLowering", - description = "Lower IrTypeOperatorCalls to (implicit) casts and instanceof checks" + description = "Lower IrTypeOperatorCalls to (implicit) casts and instanceof checks", + // SuspendLambdaPhase replaces indy-metafactory calls with mapped suspend function types. + prerequisite = setOf(suspendLambdaPhase) ) private class TypeOperatorLowering(private val backendContext: JvmBackendContext) :
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt index 9b6ad40..5bd0203 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.jvm.ir.getSingleAbstractMethod import org.jetbrains.kotlin.backend.jvm.ir.isCompiledToJvmDefault import org.jetbrains.kotlin.backend.jvm.lower.findInterfaceImplementation +import org.jetbrains.kotlin.backend.jvm.lower.isTailCallSuspendLambda import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.DescriptorVisibilities @@ -114,8 +115,10 @@ val samMethod = samClass.getSingleAbstractMethod() ?: throw AssertionError("SAM class has no single abstract method: ${samClass.render()}") - // Can't use JDK LambdaMetafactory for fun interface with suspend fun. - if (samMethod.isSuspend) { + val isTailCallSuspendLambda = samClass.defaultType.isSuspendFunction() && reference.symbol.owner.isTailCallSuspendLambda() + + // Can't use JDK LambdaMetafactory for fun interface with suspend fun and non-tail-call suspend lambdas. + if (samMethod.isSuspend && !isTailCallSuspendLambda) { abiHazard = true }
diff --git a/compiler/testData/codegen/box/coroutines/bridges/lambdaWithLongReceiver_ir.txt b/compiler/testData/codegen/box/coroutines/bridges/lambdaWithLongReceiver_ir.txt index 15e55a9..e4af1f4 100644 --- a/compiler/testData/codegen/box/coroutines/bridges/lambdaWithLongReceiver_ir.txt +++ b/compiler/testData/codegen/box/coroutines/bridges/lambdaWithLongReceiver_ir.txt
@@ -1,17 +1,14 @@ -@kotlin.coroutines.jvm.internal.DebugMetadata @kotlin.Metadata final class LambdaWithLongReceiverKt$box$1$1 { // source: 'lambdaWithLongReceiver.kt' enclosing method LambdaWithLongReceiverKt$box$1.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; - private synthetic field J$0: long - field label: int + public final static field INSTANCE: LambdaWithLongReceiverKt$box$1$1 inner (anonymous) class LambdaWithLongReceiverKt$box$1 inner (anonymous) class LambdaWithLongReceiverKt$box$1$1 - method <init>(p0: kotlin.coroutines.Continuation): void - public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation + static method <clinit>(): void + method <init>(): void public synthetic bridge method invoke(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object - public final @org.jetbrains.annotations.Nullable method invoke(p0: long, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): java.lang.Object - public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object + public final @org.jetbrains.annotations.Nullable method invoke(p0: long, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object } @kotlin.coroutines.jvm.internal.DebugMetadata
diff --git a/compiler/testData/codegen/box/coroutines/bridges/lambdaWithMultipleParameters_ir.txt b/compiler/testData/codegen/box/coroutines/bridges/lambdaWithMultipleParameters_ir.txt index 4d64ec4..f3d3eb8 100644 --- a/compiler/testData/codegen/box/coroutines/bridges/lambdaWithMultipleParameters_ir.txt +++ b/compiler/testData/codegen/box/coroutines/bridges/lambdaWithMultipleParameters_ir.txt
@@ -1,21 +1,14 @@ -@kotlin.coroutines.jvm.internal.DebugMetadata @kotlin.Metadata final class LambdaWithMultipleParametersKt$box$1$1 { // source: 'lambdaWithMultipleParameters.kt' enclosing method LambdaWithMultipleParametersKt$box$1.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; - synthetic field J$0: long - synthetic field J$1: long - synthetic field J$2: long - synthetic field J$3: long - synthetic field J$4: long - synthetic field L$0: java.lang.Object - field label: int + public final static field INSTANCE: LambdaWithMultipleParametersKt$box$1$1 inner (anonymous) class LambdaWithMultipleParametersKt$box$1 inner (anonymous) class LambdaWithMultipleParametersKt$box$1$1 - method <init>(p0: kotlin.coroutines.Continuation): void - public final @org.jetbrains.annotations.Nullable method invoke(@org.jetbrains.annotations.NotNull p0: java.lang.String, p1: long, p2: long, p3: long, p4: long, p5: long, @org.jetbrains.annotations.Nullable p6: kotlin.coroutines.Continuation): java.lang.Object + static method <clinit>(): void + method <init>(): void + public final @org.jetbrains.annotations.Nullable method invoke(@org.jetbrains.annotations.NotNull p0: java.lang.String, p1: long, p2: long, p3: long, p4: long, p5: long, @org.jetbrains.annotations.NotNull p6: kotlin.coroutines.Continuation): java.lang.Object public synthetic bridge method invoke(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object, p4: java.lang.Object, p5: java.lang.Object, p6: 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
diff --git a/compiler/testData/codegen/box/coroutines/illegalState.kt b/compiler/testData/codegen/box/coroutines/illegalState.kt index 19116fd..3437275 100644 --- a/compiler/testData/codegen/box/coroutines/illegalState.kt +++ b/compiler/testData/codegen/box/coroutines/illegalState.kt
@@ -27,10 +27,13 @@ continuation.resume(Unit) } +suspend fun dummy() {} + fun box(): String { try { builder1 { + dummy() suspendHere() } return "fail 1" @@ -39,6 +42,7 @@ try { builder2 { + dummy() suspendHere() } return "fail 3" @@ -50,6 +54,7 @@ try { builder1 { + dummy() result = "fail 5" } return "fail 6" @@ -58,6 +63,7 @@ try { builder2 { + dummy() result = "fail 8" } return "fail 9"
diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt index 7a0b806..7b649b5 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt
@@ -30,7 +30,8 @@ var counter = 0 val result = try { - c.startCoroutineUninterceptedOrReturn(object: ContinuationAdapter<String>() { + // FIXME: UNDO + c.createCoroutineUnintercepted(object: ContinuationAdapter<String>() { override val context: CoroutineContext get() = ContinuationDispatcher { counter++ } @@ -41,12 +42,12 @@ override fun resume(value: String) { fromSuspension = value } - }) + }).resume(Unit) } catch (e: Exception) { "Exception: ${e.message}" } - if (counter != expectedCount) throw RuntimeException("fail 0") + if (counter != expectedCount) throw RuntimeException("fail 0 $counter != $expectedCount") return fromSuspension!! }
diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt index f69e714..59dc318 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt
@@ -76,12 +76,14 @@ } } -fun box(): String { - if (builder(false, 0) { "OK" } != "OK") return "fail 4" - if (builder(true, 1) { suspendHere() } != "OK") return "fail 5" +suspend fun dummy() {} - if (builder(false, 0) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6" - if (builder(true, 1) { suspendWithException() } != "Exception: OK") return "fail 7" +fun box(): String { + if (builder(false, 0) { dummy(); "OK" } != "OK") return "fail 4" + if (builder(true, 1) { dummy(); suspendHere() } != "OK") return "fail 5" + + if (builder(false, 0) { dummy(); throw RuntimeException("OK") } != "Exception: OK") return "fail 6" + if (builder(true, 1) { dummy(); suspendWithException() } != "Exception: OK") return "fail 7" return "OK" }
diff --git a/compiler/testData/codegen/box/coroutines/restrictedSuspendLambda.kt b/compiler/testData/codegen/box/coroutines/restrictedSuspendLambda.kt index f4a58ee..fef2c25 100644 --- a/compiler/testData/codegen/box/coroutines/restrictedSuspendLambda.kt +++ b/compiler/testData/codegen/box/coroutines/restrictedSuspendLambda.kt
@@ -5,7 +5,7 @@ @RestrictsSuspension interface Marker { - fun restricted() + suspend fun restricted() } var lambda: Any? = null @@ -15,7 +15,10 @@ } fun box(): String { - acceptsRestricted {} + acceptsRestricted { + restricted() + restricted() + } @Suppress("INVISIBLE_REFERENCE") return if (lambda is kotlin.coroutines.jvm.internal.RestrictedSuspendLambda) "OK" else "FAIL"
diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/class.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/class.kt new file mode 100644 index 0000000..1efaf60 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/class.kt
@@ -0,0 +1,42 @@ +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM +// FULL_JDK +// WITH_STDLIB +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendHere(v: String): String = + suspendCoroutineUninterceptedOrReturn { x -> + TailCallOptimizationChecker.saveStackTrace(x) + x.resume(v) + COROUTINE_SUSPENDED + } + +fun returnsLambda(): suspend (String) -> String = { + class C { + suspend fun foo(): String { + return suspendHere(it) + } + } + + C().foo() +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = returnsLambda()("OK") + } + TailCallOptimizationChecker.checkNoStateMachineIn("invokeSuspend", "returnsLambda$1") + TailCallOptimizationChecker.checkNoStateMachineIn("invoke", "returnsLambda$1") + + return result +}
diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/function.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/function.kt new file mode 100644 index 0000000..72da396 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/function.kt
@@ -0,0 +1,43 @@ +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM +// FULL_JDK +// WITH_STDLIB +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendHere(v: String): String = + suspendCoroutineUninterceptedOrReturn { x -> + TailCallOptimizationChecker.saveStackTrace(x) + x.resume(v) + COROUTINE_SUSPENDED + } + +suspend fun dummy() {} + +fun returnsLambda(): suspend (String) -> String = { + suspend fun foo(): String { + // Prevent tail-call optimization + dummy() + return suspendHere(it) + } + foo() +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = returnsLambda()("OK") + } + TailCallOptimizationChecker.checkNoStateMachineIn("invokeSuspend", "returnsLambda$1") + TailCallOptimizationChecker.checkNoStateMachineIn("invoke", "returnsLambda$1") + + return result +}
diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/indy.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/indy.kt new file mode 100644 index 0000000..52fad51 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/indy.kt
@@ -0,0 +1,43 @@ +// TARGET_BACKEND: JVM +// FULL_JDK +// WITH_STDLIB +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION + +// JVM_TARGET: 1.8 +// LAMBDAS: INDY +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 1 java/lang/invoke/LambdaMetafactory + +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendHere(v: String): String = + suspendCoroutineUninterceptedOrReturn { x -> + TailCallOptimizationChecker.saveStackTrace(x) + x.resume(v) + COROUTINE_SUSPENDED + } + +suspend fun runsLambda(s: String): String { + val lambda: suspend(String) -> String = { + suspendHere(it) + } + return lambda(s) +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = runsLambda("OK") + } + + return result +}
diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/inlineLambda.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/inlineLambda.kt new file mode 100644 index 0000000..f9829d0 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/inlineLambda.kt
@@ -0,0 +1,33 @@ +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM +// FULL_JDK +// WITH_STDLIB +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +fun suspendThere(v: String): suspend () -> String = { + suspendCoroutineUninterceptedOrReturn { x -> + TailCallOptimizationChecker.saveStackTrace(x) + x.resume(v) + COROUTINE_SUSPENDED + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = suspendThere("OK")() + } + TailCallOptimizationChecker.checkNoStateMachineIn("invokeSuspend", "suspendThere$1") + TailCallOptimizationChecker.checkNoStateMachineIn("invoke", "suspendThere$1") + + return result +}
diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/lambda.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/lambda.kt new file mode 100644 index 0000000..af6765d --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/lambda.kt
@@ -0,0 +1,44 @@ +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM +// FULL_JDK +// WITH_STDLIB +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendHere(v: String): String = + suspendCoroutineUninterceptedOrReturn { x -> + TailCallOptimizationChecker.saveStackTrace(x) + x.resume(v) + COROUTINE_SUSPENDED + } + +suspend fun dummy() {} + +fun returnsLambda(): suspend (String) -> String = { + val l: suspend () -> String = { + val res = suspendHere(it) + // Prevent tail-call optimization + dummy() + res + } + l() +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = returnsLambda()("OK") + } + TailCallOptimizationChecker.checkNoStateMachineIn("invokeSuspend", "returnsLambda$1") + TailCallOptimizationChecker.checkNoStateMachineIn("invoke", "returnsLambda$1") + + return result +}
diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/object.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/object.kt new file mode 100644 index 0000000..d4a8d52 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/object.kt
@@ -0,0 +1,42 @@ +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM +// FULL_JDK +// WITH_STDLIB +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendHere(v: String): String = + suspendCoroutineUninterceptedOrReturn { x -> + TailCallOptimizationChecker.saveStackTrace(x) + x.resume(v) + COROUTINE_SUSPENDED + } + +fun returnsLambda(): suspend (String) -> String = { + val o = object { + suspend fun foo(): String { + return suspendHere(it) + } + } + + o.foo() +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = returnsLambda()("OK") + } + TailCallOptimizationChecker.checkNoStateMachineIn("invokeSuspend", "returnsLambda$1") + TailCallOptimizationChecker.checkNoStateMachineIn("invoke", "returnsLambda$1") + + return result +}
diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/simple.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/simple.kt new file mode 100644 index 0000000..32ef9b9 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/simple.kt
@@ -0,0 +1,36 @@ +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM +// FULL_JDK +// WITH_STDLIB +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun suspendHere(v: String): String = + suspendCoroutineUninterceptedOrReturn { x -> + TailCallOptimizationChecker.saveStackTrace(x) + x.resume(v) + COROUTINE_SUSPENDED + } + +fun returnsLambda(): suspend (String) -> String = { + suspendHere(it) +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = returnsLambda()("OK") + } + TailCallOptimizationChecker.checkNoStateMachineIn("invokeSuspend", "returnsLambda$1") + TailCallOptimizationChecker.checkNoStateMachineIn("invoke", "returnsLambda$1") + + return result +}
diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/startCoroutine.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/startCoroutine.kt new file mode 100644 index 0000000..f21c455 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/startCoroutine.kt
@@ -0,0 +1,18 @@ +// TARGET_BACKEND: JVM +// FULL_JDK +// WITH_STDLIB +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +fun box(): String { + var result = "" + + suspend { + result = "OK" + }.startCoroutine(EmptyContinuation) + + return result +}
diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/returnInlineClass.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/returnInlineClass.kt index 773b320..beb6984 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/returnInlineClass.kt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/returnInlineClass.kt
@@ -34,6 +34,6 @@ val channel: Channel<String> = AbstractChannel<String>() res = channel.receiveOrClosed().holder as String } - TailCallOptimizationChecker.checkStateMachineIn("receiveOrClosed") + TailCallOptimizationChecker.checkStateMachineIn("receiveOrClosed-s5I_7NE") return res } \ No newline at end of file
diff --git a/compiler/testData/codegen/box/invokedynamic/lambdas/lambdaMetadataFactoryIsNotUsed.kt b/compiler/testData/codegen/box/invokedynamic/lambdas/lambdaMetadataFactoryIsNotUsed.kt index 9f8cda5..b586273 100644 --- a/compiler/testData/codegen/box/invokedynamic/lambdas/lambdaMetadataFactoryIsNotUsed.kt +++ b/compiler/testData/codegen/box/invokedynamic/lambdas/lambdaMetadataFactoryIsNotUsed.kt
@@ -7,9 +7,10 @@ // JVM_IR_TEMPLATES // 0 java/lang/invoke/LambdaMetafactory +suspend fun dummy() {} fun box(): String { - suspend {} + suspend { dummy(); dummy() } fun (a: Any, b: Any, c: Any, d: Any, e: Any, f: Any, g: Any, h: Any, i: Any, j: Any, k: Any, l: Any, m: Any, n: Any, o: Any, p: Any, q: Any, r: Any, s: Any, t: Any, u: Any, v: Any, w: Any) = "one too many"
diff --git a/compiler/testData/codegen/box/reflection/genericSignature/suspendFunctionLiteralGenericSignature.kt b/compiler/testData/codegen/box/reflection/genericSignature/suspendFunctionLiteralGenericSignature.kt index 86e00b9..ef1ee32c 100644 --- a/compiler/testData/codegen/box/reflection/genericSignature/suspendFunctionLiteralGenericSignature.kt +++ b/compiler/testData/codegen/box/reflection/genericSignature/suspendFunctionLiteralGenericSignature.kt
@@ -59,14 +59,16 @@ ) } +suspend fun dummy() {} + fun box(): String { assertGenericSuper("kotlin.jvm.functions.Function1<kotlin.coroutines.Continuation<? super kotlin.Unit>, java.lang.Object>", unitFun) assertGenericSuper("kotlin.jvm.functions.Function1<kotlin.coroutines.Continuation<? super java.lang.Integer>, java.lang.Object>", intFun) - assertStringParamFun { x: String -> } - assertListFun { l: List<String> -> l } - assertMutableListFun { l -> null!! } - assertFunWithIn { x -> } - assertExtensionFun { } - assertExtensionWithArgFun { x -> Date() } + assertStringParamFun { x: String -> dummy(); dummy() } + assertListFun { l: List<String> -> dummy(); dummy() } + assertMutableListFun { l -> dummy(); dummy(); null!! } + assertFunWithIn { x -> dummy(); dummy() } + assertExtensionFun { dummy(); dummy() } + assertExtensionWithArgFun { x -> dummy(); dummy(); Date() } return "OK" }
diff --git a/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultInlineLambda.kt b/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultInlineLambda.kt index 5282ed8..414043e 100644 --- a/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultInlineLambda.kt +++ b/compiler/testData/codegen/boxInline/suspend/defaultParameter/defaultInlineLambda.kt
@@ -1,9 +1,9 @@ // SKIP_INLINE_CHECK_IN: bar$default // WITH_STDLIB // WITH_COROUTINES -// IGNORE_BACKEND: JVM, JVM_IR +// IGNORE_BACKEND: JVM // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM, JVM_IR, JVM_MULTI_MODULE_OLD_AGAINST_IR, JVM_MULTI_MODULE_IR_AGAINST_OLD +// IGNORE_BACKEND_MULTI_MODULE: JVM, JVM_MULTI_MODULE_IR_AGAINST_OLD // IGNORE_BACKEND_FIR_MULTI_MODULE: JVM_IR JVM_IR_SERIALIZE // FILE: 1.kt package test
diff --git a/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter_ir.txt b/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter_ir.txt index f51bbbb..3373216 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter_ir.txt +++ b/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter_ir.txt
@@ -15,19 +15,16 @@ 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 BooleanParameterKt$box$lambda$1 { // source: 'booleanParameter.kt' enclosing method BooleanParameterKt.box()Ljava/lang/String; - synthetic field Z$0: boolean - field label: int + public final static field INSTANCE: BooleanParameterKt$box$lambda$1 inner (anonymous) class BooleanParameterKt$box$lambda$1 - method <init>(p0: kotlin.coroutines.Continuation): void - public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation - public final @org.jetbrains.annotations.Nullable method invoke(p0: boolean, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): java.lang.Object + static method <clinit>(): void + method <init>(): void + public final @org.jetbrains.annotations.Nullable method invoke(p0: boolean, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object public synthetic bridge method invoke(p0: java.lang.Object, p1: 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
diff --git a/compiler/testData/codegen/bytecodeListing/coroutines/spilling/field_ir.txt b/compiler/testData/codegen/bytecodeListing/coroutines/spilling/field_ir.txt index 8d7f6c0..54be1b8 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutines/spilling/field_ir.txt +++ b/compiler/testData/codegen/bytecodeListing/coroutines/spilling/field_ir.txt
@@ -1,16 +1,13 @@ -@kotlin.coroutines.jvm.internal.DebugMetadata @kotlin.Metadata final class FieldKt$test$1 { // source: 'field.kt' enclosing method FieldKt.test()V - private synthetic field L$0: java.lang.Object - field label: int + public final static field INSTANCE: FieldKt$test$1 inner (anonymous) class FieldKt$test$1 - method <init>(p0: kotlin.coroutines.Continuation): void - public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation - public final @org.jetbrains.annotations.Nullable method invoke(@org.jetbrains.annotations.NotNull p0: Foo, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): java.lang.Object + static method <clinit>(): void + method <init>(): void + public final @org.jetbrains.annotations.Nullable method invoke(@org.jetbrains.annotations.NotNull p0: Foo, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object public synthetic bridge method invoke(p0: java.lang.Object, p1: 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
diff --git a/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/withTypeParameter.kt b/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/withTypeParameter.kt index dce51e6..36759f3 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/withTypeParameter.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/withTypeParameter.kt
@@ -20,6 +20,11 @@ } } +// JVM_TEMPLATES // suspend lambdas: 4 // suspend lambdas $$forInline: 1 -// 5 TABLESWITCH \ No newline at end of file +// 5 TABLESWITCH + +// JVM_IR_TEMPLATES +// tail-call suspend lambdas: 2 +// 3 TABLESWITCH \ No newline at end of file
diff --git a/compiler/testData/debug/localVariables/suspend/underscoreNames.kt b/compiler/testData/debug/localVariables/suspend/underscoreNames.kt index 2132fed..7583d24 100644 --- a/compiler/testData/debug/localVariables/suspend/underscoreNames.kt +++ b/compiler/testData/debug/localVariables/suspend/underscoreNames.kt
@@ -18,27 +18,25 @@ // test.kt:4 <init>: // test.kt:12 box: $completion:kotlin.coroutines.Continuation=Generated_Box_MainKt$main$1 // test.kt:10 foo: a:A=A, block:kotlin.jvm.functions.Function2=TestKt$box$2, $completion:kotlin.coroutines.Continuation=Generated_Box_MainKt$main$1 + +// EXPECTATIONS JVM_IR +// test.kt:12 invoke: $completion:kotlin.coroutines.Continuation=Generated_Box_MainKt$main$1 // test.kt:5 component1: - -// EXPECTATIONS JVM -// test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A -// EXPECTATIONS JVM_IR -// test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit - -// EXPECTATIONS +// test.kt:12 invoke: $completion:kotlin.coroutines.Continuation=Generated_Box_MainKt$main$1 // test.kt:7 component3: - -// EXPECTATIONS JVM_IR -// test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, x_param:java.lang.String="O":java.lang.String -// test.kt:13 invokeSuspend: $result:java.lang.Object=kotlin.Unit, x_param:java.lang.String="O":java.lang.String, y_param:java.lang.String="K":java.lang.String +// test.kt:12 invoke: $completion:kotlin.coroutines.Continuation=Generated_Box_MainKt$main$1, x_param:java.lang.String="O":java.lang.String +// test.kt:13 invoke: $completion:kotlin.coroutines.Continuation=Generated_Box_MainKt$main$1, x_param:java.lang.String="O":java.lang.String, y_param:java.lang.String="K":java.lang.String // EXPECTATIONS JVM +// test.kt:5 component1: +// test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A +// test.kt:7 component3: // test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A // test.kt:13 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A, x_param:java.lang.String="O":java.lang.String, y_param:java.lang.String="K":java.lang.String +// test.kt:-1 invoke: + // EXPECTATIONS JVM_IR -// test.kt:-1 invoke: p1:A=A, p2:kotlin.coroutines.Continuation=Generated_Box_MainKt$main$1 // EXPECTATIONS JVM -// test.kt:-1 invoke: // EXPECTATIONS // test.kt:10 foo: a:A=A, block:kotlin.jvm.functions.Function2=TestKt$box$2, $completion:kotlin.coroutines.Continuation=Generated_Box_MainKt$main$1
diff --git a/compiler/testData/debug/stepping/suspendFunWithSuspendLambdaParameter.kt b/compiler/testData/debug/stepping/suspendFunWithSuspendLambdaParameter.kt index 1494531..024015b 100644 --- a/compiler/testData/debug/stepping/suspendFunWithSuspendLambdaParameter.kt +++ b/compiler/testData/debug/stepping/suspendFunWithSuspendLambdaParameter.kt
@@ -16,10 +16,18 @@ // That means that we never step into the lambda as that is only // called via code in the kotlin package. -// EXPECTATIONS JVM JVM_IR +// EXPECTATIONS JVM // test.kt:9 box // test.kt:5 foo // test.kt:9 box // test.kt:12 box +// EXPECTATIONS JVM_IR +// test.kt:9 box +// test.kt:5 foo +// test.kt:10 invoke +// test.kt:5 foo +// test.kt:9 box +// test.kt:12 box + // EXPECTATIONS JS_IR
diff --git a/compiler/testData/diagnostics/helpers/coroutines/TailCallOptimizationChecker.kt b/compiler/testData/diagnostics/helpers/coroutines/TailCallOptimizationChecker.kt index ad89c2c..ffcdf4f 100644 --- a/compiler/testData/diagnostics/helpers/coroutines/TailCallOptimizationChecker.kt +++ b/compiler/testData/diagnostics/helpers/coroutines/TailCallOptimizationChecker.kt
@@ -21,14 +21,24 @@ } } - fun checkNoStateMachineIn(method: String) { - stackTrace.find { it?.methodName?.startsWith(method) == true }?.let { error("tail-call optimization miss: method at " + it + " has state-machine " + - stackTrace.joinToString(separator = "\n")) } + private fun findStackTraceElement(method: String, className: String?): StackTraceElement? { + val list = stackTrace.filter { it?.methodName == method } + if (className != null) { + return list.find { it?.className?.endsWith(className) == true } + } else { + return list.firstOrNull() + } } - fun checkStateMachineIn(method: String) { - stackTrace.find { it?.methodName?.startsWith(method) == true } ?: error("tail-call optimization hit: method " + method + " has no state-machine " + - stackTrace.joinToString(separator = "\n")) + fun checkNoStateMachineIn(method: String, className: String? = null) { + findStackTraceElement(method, className)?.let { + error("tail-call optimization miss: method at $it has state-machine " + stackTrace.joinToString(separator = "\n")) + } + } + + fun checkStateMachineIn(method: String, className: String? = null) { + findStackTraceElement(method, className) + ?: error("tail-call optimization hit: method $method has no state-machine " + stackTrace.joinToString(separator = "\n")) } }
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 49ba9cf..e012d31 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java
@@ -12837,6 +12837,64 @@ } @Nested + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda") + @TestDataPath("$PROJECT_ROOT") + public class Lambda { + @Test + public void testAllFilesPresentInLambda() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @Test + @TestMetadata("class.kt") + public void testClass() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/class.kt"); + } + + @Test + @TestMetadata("function.kt") + public void testFunction() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/function.kt"); + } + + @Test + @TestMetadata("indy.kt") + public void testIndy() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/indy.kt"); + } + + @Test + @TestMetadata("inlineLambda.kt") + public void testInlineLambda() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/inlineLambda.kt"); + } + + @Test + @TestMetadata("lambda.kt") + public void testLambda() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/lambda.kt"); + } + + @Test + @TestMetadata("object.kt") + public void testObject() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/object.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/simple.kt"); + } + + @Test + @TestMetadata("startCoroutine.kt") + public void testStartCoroutine() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/startCoroutine.kt"); + } + } + + @Nested @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") @TestDataPath("$PROJECT_ROOT") public class Unit {
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index c4f19bd..d54054e 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java
@@ -12957,6 +12957,64 @@ } @Nested + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda") + @TestDataPath("$PROJECT_ROOT") + public class Lambda { + @Test + public void testAllFilesPresentInLambda() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("class.kt") + public void testClass() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/class.kt"); + } + + @Test + @TestMetadata("function.kt") + public void testFunction() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/function.kt"); + } + + @Test + @TestMetadata("indy.kt") + public void testIndy() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/indy.kt"); + } + + @Test + @TestMetadata("inlineLambda.kt") + public void testInlineLambda() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/inlineLambda.kt"); + } + + @Test + @TestMetadata("lambda.kt") + public void testLambda() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/lambda.kt"); + } + + @Test + @TestMetadata("object.kt") + public void testObject() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/object.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/simple.kt"); + } + + @Test + @TestMetadata("startCoroutine.kt") + public void testStartCoroutine() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/startCoroutine.kt"); + } + } + + @Nested @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") @TestDataPath("$PROJECT_ROOT") public class Unit {
diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 90b5749..1e14bbd 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java
@@ -10348,6 +10348,59 @@ runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/whenUnit.kt"); } + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Lambda extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInLambda() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("class.kt") + public void testClass() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/class.kt"); + } + + @TestMetadata("function.kt") + public void testFunction() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/function.kt"); + } + + @TestMetadata("indy.kt") + public void testIndy() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/indy.kt"); + } + + @TestMetadata("inlineLambda.kt") + public void testInlineLambda() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/inlineLambda.kt"); + } + + @TestMetadata("lambda.kt") + public void testLambda() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/lambda.kt"); + } + + @TestMetadata("object.kt") + public void testObject() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/object.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/simple.kt"); + } + + @TestMetadata("startCoroutine.kt") + public void testStartCoroutine() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda/startCoroutine.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java index eb77bbc..0b62a800 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java
@@ -9575,6 +9575,16 @@ } @Nested + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda") + @TestDataPath("$PROJECT_ROOT") + public class Lambda { + @Test + public void testAllFilesPresentInLambda() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); + } + } + + @Nested @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") @TestDataPath("$PROJECT_ROOT") public class Unit {
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java index 40fcd0a..5f2a399 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java
@@ -9617,6 +9617,16 @@ } @Nested + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda") + @TestDataPath("$PROJECT_ROOT") + public class Lambda { + @Test + public void testAllFilesPresentInLambda() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + } + + @Nested @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") @TestDataPath("$PROJECT_ROOT") public class Unit {
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index b17705c..f45de03 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java
@@ -8488,6 +8488,19 @@ runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatch.kt"); } + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Lambda extends AbstractIrCodegenBoxWasmTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath); + } + + public void testAllFilesPresentInLambda() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true); + } + } + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)
diff --git a/kotlin-native/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanToolRunner.kt b/kotlin-native/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanToolRunner.kt index d6c7d98..e534486 100644 --- a/kotlin-native/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanToolRunner.kt +++ b/kotlin-native/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/konan/KonanToolRunner.kt
@@ -39,6 +39,7 @@ private const val runFromDaemonPropertyName = "kotlin.native.tool.runFromDaemon" +@Suppress("DEPRECATION") internal abstract class KonanCliRunner( protected val toolName: String, project: Project, @@ -89,8 +90,7 @@ // A separate map for each build for automatic cleaning the daemon after the build have finished. @Suppress("UNCHECKED_CAST") - final override val isolatedClassLoaders get() = - project.project(":kotlin-native").ext["toolClassLoadersMap"] as ConcurrentHashMap<Any, URLClassLoader> + final override val isolatedClassLoaders get() = ConcurrentHashMap<Any, URLClassLoader>() override fun transformArgs(args: List<String>) = listOf(toolName) + args @@ -141,7 +141,7 @@ konanHome: String = project.konanHome ) : KonanCliRunner("cinterop", project, additionalJvmArgs, konanHome) { override fun transformArgs(args: List<String>): List<String> { - return super.transformArgs(args) + listOf("-Xproject-dir", project.projectDir.toString()) + return super.transformArgs(args) + listOf("-Xproject-dir", "C:\\Users\\ilmirus\\work\\kotlin2\\build\\repo") } override val execEnvironment by lazy {
diff --git a/libraries/stdlib/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt b/libraries/stdlib/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt index 3e8c432..cf56880 100644 --- a/libraries/stdlib/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt +++ b/libraries/stdlib/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt
@@ -48,14 +48,24 @@ public actual inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn( receiver: R, completion: Continuation<T> -): Any? = (this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion) +): Any? = + // For tail-call lambdas we create a wrapper continuation + if (this !is Continuation<*>) createCoroutineUnintercepted(receiver, completion).resumeWith(Result.success(Unit)) + else (this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion) @InlineOnly internal actual inline fun <R, P, T> (suspend R.(P) -> T).startCoroutineUninterceptedOrReturn( receiver: R, param: P, completion: Continuation<T> -): Any? = (this as Function3<R, P, Continuation<T>, Any?>).invoke(receiver, param, completion) +): Any? = + // For tail-call lambdas we create a wrapper continuation + if (this !is Continuation<*>) { + val intermediate: suspend R.() -> T = { + this@startCoroutineUninterceptedOrReturn.invoke(this, param) + } + intermediate.createCoroutineUnintercepted(receiver, completion).resumeWith(Result.success(Unit)) + } else (this as Function3<R, P, Continuation<T>, Any?>).invoke(receiver, param, completion) // JVM declarations
diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java index 6f925ae..8b20f03 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java
@@ -10537,6 +10537,18 @@ } @Nested + @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda") + @TestDataPath("$PROJECT_ROOT") + @Tag("codegen") + @UseExtTestCaseGroupProvider() + public class Lambda { + @Test + public void testAllFilesPresentInLambda() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambda"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true); + } + } + + @Nested @TestMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/unit") @TestDataPath("$PROJECT_ROOT") @Tag("codegen")