diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index b053ab04fff..c498716f469 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -18481,6 +18481,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt"); } + @Test + @TestMetadata("suspendFunInterface.kt") + public void testSuspendFunInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/suspendFunInterface.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt index e0839712178..efacfe337ae 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt @@ -389,3 +389,9 @@ private fun JvmBackendContext.makeRawTypeAnnotation() = fun IrClass.rawType(context: JvmBackendContext): IrType = defaultType.addAnnotations(listOf(context.makeRawTypeAnnotation())) + +fun IrType.getSingleAbstractMethod(): IrSimpleFunction? = + getClass()?.getSingleAbstractMethod() + +fun IrClass.getSingleAbstractMethod(): IrSimpleFunction? = + functions.singleOrNull { it.modality == Modality.ABSTRACT } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index afecc5eaefc..72c541077a1 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt @@ -105,14 +105,15 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) } reference.transformChildrenVoid() - return if (shouldUseIndySamConversions && canUseIndySamConversion(reference)) { + val samSuperType = expression.typeOperand + return if (shouldUseIndySamConversions && canUseIndySamConversion(reference, samSuperType)) { wrapSamConversionArgumentWithIndySamConversion(expression) } else { - FunctionReferenceBuilder(reference, expression.typeOperand).build() + FunctionReferenceBuilder(reference, samSuperType).build() } } - private fun canUseIndySamConversion(reference: IrFunctionReference): Boolean { + private fun canUseIndySamConversion(reference: IrFunctionReference, samSuperType: IrType): Boolean { // Can't use indy for regular function references by default (because of 'equals'). // TODO special mode that would generate indy everywhere? if (reference.origin != IrStatementOrigin.LAMBDA) @@ -122,6 +123,10 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) if (context.irIntrinsics.getIntrinsic(reference.symbol) != null) return false + // Can't use JDK LambdaMetafactory for fun interface with suspend fun + if (samSuperType.getSingleAbstractMethod()?.isSuspend == true) + return false + // Can't use JDK LambdaMetafactory if lambda signature contains an inline class mapped to a non-null reference type. val target = reference.symbol.owner if (target.extensionReceiverParameter?.run { type.isProhibitedTypeForIndySamConversion() } == true || @@ -201,10 +206,8 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) if (irLambda.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) throw AssertionError("Can't patch a signature of a non-lambda: ${irLambda.render()}") - val samClass = samType.classOrNull?.owner - ?: throw AssertionError("SAM type should be a class type: '${samType.render()}'") - val samMethod = samClass.functions.singleOrNull { it.modality == Modality.ABSTRACT } - ?: throw AssertionError("SAM method not found:\n${samClass.dump()}") + val samMethod = samType.getSingleAbstractMethod() + ?: throw AssertionError("SAM method not found:\n${samType.render()}") val samMethodParameters = collectValueParameters(samMethod) val irLambdaParameters = collectValueParameters(irLambda) @@ -265,7 +268,8 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) else context.ir.symbols.getJvmFunctionClass(argumentTypes.size) private val superMethod = - functionSuperClass.functions.single { it.owner.modality == Modality.ABSTRACT } + functionSuperClass.owner.getSingleAbstractMethod() + ?: throw AssertionError("Not a SAM class: ${functionSuperClass.owner.render()}") private val useOptimizedSuperClass = context.state.generateOptimizedCallableReferenceSuperClasses @@ -546,12 +550,12 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) // For functions with inline class return type we need to mangle the invoke method. // Otherwise, bridge lowering may fail to generate bridges for inline class types erasing to Any. val suffix = InlineClassAbi.hashReturnSuffix(callee) - Name.identifier("${superMethod.owner.name.asString()}-${suffix}") - } else superMethod.owner.name + Name.identifier("${superMethod.name.asString()}-${suffix}") + } else superMethod.name returnType = callee.returnType isSuspend = callee.isSuspend }.apply { - overriddenSymbols += superMethod + overriddenSymbols += superMethod.symbol dispatchReceiverParameter = buildReceiverParameter( this, IrDeclarationOrigin.INSTANCE_RECEIVER, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt index e6eac2bdb43..44e90ea38ea 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.codegen.fileParent import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound -import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.backend.jvm.ir.getSingleAbstractMethod import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.dump -import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.isInlined import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid @@ -178,9 +177,7 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil val samType = call.getTypeArgument(0) as? IrSimpleType ?: fail("'samType' is expected to be a simple type") - val samClassSymbol = samType.classOrNull - ?: fail("'samType' is expected to be a class type: '${samType.render()}'") - val samMethod = samClassSymbol.owner.functions.singleOrNull { it.modality == Modality.ABSTRACT } + val samMethod = samType.getSingleAbstractMethod() ?: fail("'${samType.render()}' is not a SAM-type") val irFunRef = call.getValueArgument(0) as? IrFunctionReference diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrFunction.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrFunction.kt index d00c6c18dc3..05e9f6a524a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrFunction.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrFunction.kt @@ -26,7 +26,6 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.transformIfNeeded import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementVisitor -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource abstract class IrFunction : IrDeclarationBase(), diff --git a/compiler/testData/codegen/box/invokedynamic/sam/suspendFunInterface.kt b/compiler/testData/codegen/box/invokedynamic/sam/suspendFunInterface.kt new file mode 100644 index 00000000000..23047631362 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/suspendFunInterface.kt @@ -0,0 +1,42 @@ +// !LANGUAGE: +SuspendFunctionsInFunInterfaces +JvmIrEnabledByDefault +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.* + +var c: Continuation? = null + +suspend fun suspendMe() = suspendCoroutine { continuation -> + c = continuation +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(object: Continuation { + override val context = EmptyCoroutineContext + override fun resumeWith(result: Result) { + result.getOrThrow() + } + }) +} + +fun interface SuspendFoo { + suspend fun foo() +} + +fun box(): String { + var test = "" + val lambda = SuspendFoo { + suspendMe() + test += "O" + suspendMe() + test += "K" + } + builder { + lambda.foo() + } + c?.resume(Unit) + c?.resume(Unit) + return test +} 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 cdce1fbec58..f34dd817d01 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 @@ -18481,6 +18481,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt"); } + @Test + @TestMetadata("suspendFunInterface.kt") + public void testSuspendFunInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/suspendFunInterface.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") 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 43130867dd2..40c583ba90d 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 @@ -18481,6 +18481,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt"); } + @Test + @TestMetadata("suspendFunInterface.kt") + public void testSuspendFunInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/suspendFunInterface.kt"); + } + @Nested @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index e747e044247..d609c82ae46 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -16203,6 +16203,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt"); } + @TestMetadata("suspendFunInterface.kt") + public void testSuspendFunInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/suspendFunInterface.kt"); + } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)