JVM_IR: have SAM wrapper constructors accept FunctionN

Otherwise, the cached instances cannot be reused for different wrapped
types. Also, if the wrapped type is regenerated during inlining, the
inliner would produce a call to a nonexistent constructor that takes the
regenerated type as an argument.
This commit is contained in:
pyos
2019-10-21 14:58:32 +02:00
committed by max-kammerer
parent 862197d713
commit 42f75b3247
2 changed files with 14 additions and 12 deletions
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.copyTo
@@ -13,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclaration
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.codegen.SamWrapperCodegen.FUNCTION_FIELD_NAME
@@ -47,7 +47,7 @@ internal val singleAbstractMethodPhase = makeIrFilePhase(
description = "Replace SAM conversions with instances of interface-implementing classes"
)
class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
class SingleAbstractMethodLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
// SAM wrappers are cached, either in the file class (if it exists), or in a top-level enclosing class.
// In the latter case, the names of SAM wrappers depend on the order of classes in the file. For example:
//
@@ -96,9 +96,9 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
if (expression.operator != IrTypeOperator.SAM_CONVERSION)
return super.visitTypeOperator(expression)
// TODO: We have to erase type parameters here, since we cache SAM wrappers based on the erased
// underlying representation. We should do the same for the underlying function type, otherwise
// we end up with wrong generic information.
// TODO: there must be exactly one wrapper per Java interface; ideally, if the interface has generic
// parameters, so should the wrapper. Currently, we just erase them and generate something that
// erases to the same result at codegen time.
val erasedSuperType = expression.typeOperand.erasedUpperBound.defaultType
val superType = if (expression.typeOperand.isNullable()) erasedSuperType.makeNullable() else erasedSuperType
val invokable = expression.argument.transform(this, null)
@@ -110,7 +110,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
val inInlineFunctionScope = allScopes.any { scope -> (scope.irElement as? IrFunction)?.isInline ?: false }
val cache = if (inInlineFunctionScope) inlineCachedImplementations else cachedImplementations
val implementation = cache.getOrPut(superType) {
createObjectProxy(superType, invokable.type, inInlineFunctionScope)
createObjectProxy(superType, inInlineFunctionScope)
}
return if (superType.isNullable() && invokable.type.isNullable()) {
@@ -138,7 +138,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
// Construct a class that wraps an invokable object into an implementation of an interface:
// class sam$n(private val invokable: F) : Interface { override fun method(...) = invokable(...) }
private fun createObjectProxy(superType: IrType, invokableType: IrType, generatePublicWrapper: Boolean): IrClass {
private fun createObjectProxy(superType: IrType, generatePublicWrapper: Boolean): IrClass {
val superClass = superType.classifierOrFail.owner as IrClass
// The language documentation prohibits casting lambdas to classes, but if it was allowed,
// the `irDelegatingConstructorCall` in the constructor below would need to be modified.
@@ -147,6 +147,11 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
val superFqName = superClass.fqNameWhenAvailable!!.asString().replace('.', '_')
val inlinePrefix = if (generatePublicWrapper) "\$i" else ""
val wrapperName = Name.identifier("sam$inlinePrefix\$$superFqName$SAM_WRAPPER_SUFFIX")
val superMethod = superClass.functions.single { it.modality == Modality.ABSTRACT }
// TODO: have psi2ir cast the argument to the correct function type. Also see the TODO
// about type parameters in `visitTypeOperator`.
val wrappedFunctionClass = context.ir.symbols.functionN(superMethod.valueParameters.size).owner
val wrappedFunctionType = wrappedFunctionClass.defaultType
val wrapperVisibility = if (generatePublicWrapper) Visibilities.PUBLIC else JavaVisibilities.PACKAGE_VISIBILITY
val subclass = buildClass {
@@ -161,7 +166,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
val field = subclass.addField {
name = Name.identifier(FUNCTION_FIELD_NAME)
type = invokableType
type = wrappedFunctionType
origin = subclass.origin
visibility = Visibilities.PRIVATE
}
@@ -184,7 +189,6 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
}
}
val superMethod = superClass.functions.single { it.modality == Modality.ABSTRACT }
subclass.addFunction {
name = superMethod.name
returnType = superMethod.returnType
@@ -194,9 +198,8 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
overriddenSymbols += superMethod.symbol
dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this)
superMethod.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
val invokableClass = invokableType.classifierOrFail.owner as IrClass
body = context.createIrBuilder(symbol).run {
irExprBody(irCall(invokableClass.functions.single { it.name == OperatorNameConventions.INVOKE }).apply {
irExprBody(irCall(wrappedFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE }).apply {
dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), field)
valueParameters.forEachIndexed { i, parameter -> putValueArgument(i, irGet(parameter)) }
})
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: 1.kt
package test