JVM_IR indy-SAM conversions: use old scheme for suspend funs

KT-44278 KT-26060 KT-42621
This commit is contained in:
Dmitry Petrov
2021-01-26 14:40:55 +03:00
parent 1f16b96796
commit 4da2f3d9d4
9 changed files with 88 additions and 17 deletions
@@ -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")
@@ -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 }
@@ -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,
@@ -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
@@ -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(),
@@ -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<Unit>? = null
suspend fun suspendMe() = suspendCoroutine<Unit> { continuation ->
c = continuation
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(object: Continuation<Unit> {
override val context = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
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
}
@@ -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")
@@ -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")
@@ -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)