[IR] Equals/hashCode for SAM implementers
Fixes https://youtrack.jetbrains.com/issue/KT-39798
This commit is contained in:
+15
-14
@@ -251,19 +251,6 @@ internal val dataClassesPhase = makeKonanFileLoweringPhase(
|
|||||||
description = "Data classes lowering"
|
description = "Data classes lowering"
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val singleAbstractMethodPhase = makeKonanFileLoweringPhase(
|
|
||||||
::NativeSingleAbstractMethodLowering,
|
|
||||||
name = "SingleAbstractMethod",
|
|
||||||
description = "Replace SAM conversions with instances of interface-implementing classes"
|
|
||||||
)
|
|
||||||
|
|
||||||
internal val builtinOperatorPhase = makeKonanFileLoweringPhase(
|
|
||||||
::BuiltinOperatorLowering,
|
|
||||||
name = "BuiltinOperators",
|
|
||||||
description = "BuiltIn operators lowering",
|
|
||||||
prerequisite = setOf(defaultParameterExtentPhase, singleAbstractMethodPhase)
|
|
||||||
)
|
|
||||||
|
|
||||||
internal val finallyBlocksPhase = makeKonanFileLoweringPhase(
|
internal val finallyBlocksPhase = makeKonanFileLoweringPhase(
|
||||||
::FinallyBlocksLowering,
|
::FinallyBlocksLowering,
|
||||||
name = "FinallyBlocks",
|
name = "FinallyBlocks",
|
||||||
@@ -294,7 +281,21 @@ internal val functionReferencePhase = makeKonanFileLoweringPhase(
|
|||||||
::FunctionReferenceLowering,
|
::FunctionReferenceLowering,
|
||||||
name = "FunctionReference",
|
name = "FunctionReference",
|
||||||
description = "Function references lowering",
|
description = "Function references lowering",
|
||||||
prerequisite = setOf(delegationPhase) // TODO: make weak dependency on `testProcessorPhase`
|
prerequisite = setOf(delegationPhase, localFunctionsPhase) // TODO: make weak dependency on `testProcessorPhase`
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val singleAbstractMethodPhase = makeKonanFileLoweringPhase(
|
||||||
|
::NativeSingleAbstractMethodLowering,
|
||||||
|
name = "SingleAbstractMethod",
|
||||||
|
description = "Replace SAM conversions with instances of interface-implementing classes",
|
||||||
|
prerequisite = setOf(functionReferencePhase)
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val builtinOperatorPhase = makeKonanFileLoweringPhase(
|
||||||
|
::BuiltinOperatorLowering,
|
||||||
|
name = "BuiltinOperators",
|
||||||
|
description = "BuiltIn operators lowering",
|
||||||
|
prerequisite = setOf(defaultParameterExtentPhase, singleAbstractMethodPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
internal val interopPhase = makeKonanFileLoweringPhase(
|
internal val interopPhase = makeKonanFileLoweringPhase(
|
||||||
|
|||||||
+4
-4
@@ -369,14 +369,14 @@ internal val allLoweringsPhase = NamedCompilerPhase(
|
|||||||
defaultParameterExtentPhase,
|
defaultParameterExtentPhase,
|
||||||
innerClassPhase,
|
innerClassPhase,
|
||||||
dataClassesPhase,
|
dataClassesPhase,
|
||||||
singleAbstractMethodPhase,
|
|
||||||
ifNullExpressionsFusionPhase,
|
ifNullExpressionsFusionPhase,
|
||||||
builtinOperatorPhase,
|
|
||||||
finallyBlocksPhase,
|
|
||||||
testProcessorPhase,
|
testProcessorPhase,
|
||||||
enumClassPhase,
|
|
||||||
delegationPhase,
|
delegationPhase,
|
||||||
functionReferencePhase,
|
functionReferencePhase,
|
||||||
|
singleAbstractMethodPhase,
|
||||||
|
builtinOperatorPhase,
|
||||||
|
finallyBlocksPhase,
|
||||||
|
enumClassPhase,
|
||||||
interopPhase,
|
interopPhase,
|
||||||
varargPhase,
|
varargPhase,
|
||||||
compileTimeEvaluatePhase,
|
compileTimeEvaluatePhase,
|
||||||
|
|||||||
+1
-1
@@ -422,7 +422,7 @@ internal class KonanSymbols(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
override val functionAdapter: IrClassSymbol get() = error("Not supported yet")
|
override val functionAdapter = symbolTable.referenceClass(context.getKonanInternalClass("FunctionAdapter"))
|
||||||
|
|
||||||
val refClass = symbolTable.referenceClass(context.getKonanInternalClass("Ref"))
|
val refClass = symbolTable.referenceClass(context.getKonanInternalClass("Ref"))
|
||||||
|
|
||||||
|
|||||||
+77
-14
@@ -94,6 +94,33 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle SAM conversions which wrap a function reference:
|
||||||
|
// class sam$n(private val receiver: R) : Interface { override fun method(...) = receiver.target(...) }
|
||||||
|
//
|
||||||
|
// This avoids materializing an invokable KFunction representing, thus producing one less class.
|
||||||
|
// This is actually very common, as `Interface { something }` is a local function + a SAM-conversion
|
||||||
|
// of a reference to it into an implementation.
|
||||||
|
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||||
|
if (expression.operator == IrTypeOperator.SAM_CONVERSION) {
|
||||||
|
val invokable = expression.argument
|
||||||
|
val reference = if (invokable is IrFunctionReference) {
|
||||||
|
invokable
|
||||||
|
} else if (invokable is IrBlock && (invokable.origin.isLambda)
|
||||||
|
&& invokable.statements.last() is IrFunctionReference) {
|
||||||
|
// By this point the lambda's function has been replaced with empty IrComposite by LocalDeclarationsLowering.
|
||||||
|
val statements = invokable.statements
|
||||||
|
require(statements.size == 2)
|
||||||
|
require((statements[0] as? IrComposite)?.statements?.isEmpty() == true)
|
||||||
|
statements[1] as IrFunctionReference
|
||||||
|
} else {
|
||||||
|
return super.visitTypeOperator(expression)
|
||||||
|
}
|
||||||
|
reference.transformChildrenVoid()
|
||||||
|
return transformFunctionReference(reference, expression.typeOperand)
|
||||||
|
}
|
||||||
|
return super.visitTypeOperator(expression)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
@@ -119,8 +146,12 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return transformFunctionReference(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transformFunctionReference(expression: IrFunctionReference, samSuperType: IrType? = null): IrExpression {
|
||||||
val parent: IrDeclarationContainer = (currentClass?.irElement as? IrClass) ?: irFile
|
val parent: IrDeclarationContainer = (currentClass?.irElement as? IrClass) ?: irFile
|
||||||
val loweredFunctionReference = FunctionReferenceBuilder(parent, expression).build()
|
val loweredFunctionReference = FunctionReferenceBuilder(parent, expression, samSuperType).build()
|
||||||
generatedClasses.add(loweredFunctionReference.functionReferenceClass)
|
generatedClasses.add(loweredFunctionReference.functionReferenceClass)
|
||||||
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol,
|
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol,
|
||||||
expression.startOffset, expression.endOffset)
|
expression.startOffset, expression.endOffset)
|
||||||
@@ -130,7 +161,8 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, null)
|
}, data = null)
|
||||||
|
|
||||||
irFile.declarations += generatedClasses
|
irFile.declarations += generatedClasses
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +178,8 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
private val continuationClassSymbol = getContinuationSymbol.owner.returnType.classifierOrFail as IrClassSymbol
|
private val continuationClassSymbol = getContinuationSymbol.owner.returnType.classifierOrFail as IrClassSymbol
|
||||||
|
|
||||||
private inner class FunctionReferenceBuilder(val parent: IrDeclarationParent,
|
private inner class FunctionReferenceBuilder(val parent: IrDeclarationParent,
|
||||||
val functionReference: IrFunctionReference) {
|
val functionReference: IrFunctionReference,
|
||||||
|
val samSuperType: IrType?) {
|
||||||
|
|
||||||
private val startOffset = functionReference.startOffset
|
private val startOffset = functionReference.startOffset
|
||||||
private val endOffset = functionReference.endOffset
|
private val endOffset = functionReference.endOffset
|
||||||
@@ -159,6 +192,12 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
typeParam.symbol to functionReference.getTypeArgument(typeParam.index)!!
|
typeParam.symbol to functionReference.getTypeArgument(typeParam.index)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val isLambda = functionReference.origin.isLambda
|
||||||
|
private val isKFunction = functionReference.type.isKFunction()
|
||||||
|
private val isKSuspendFunction = functionReference.type.isKSuspendFunction()
|
||||||
|
|
||||||
|
private val samSuperClass = samSuperType?.let { it.classOrNull ?: error("Expected a class but was: ${it.render()}") }
|
||||||
|
|
||||||
private val adapteeCall: IrFunctionAccessExpression? =
|
private val adapteeCall: IrFunctionAccessExpression? =
|
||||||
// TODO: Copied from JVM.
|
// TODO: Copied from JVM.
|
||||||
if (referencedFunction.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE) {
|
if (referencedFunction.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE) {
|
||||||
@@ -228,10 +267,6 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
private val kSuspendFunctionImplSymbol = symbols.kSuspendFunctionImpl
|
private val kSuspendFunctionImplSymbol = symbols.kSuspendFunctionImpl
|
||||||
private val kSuspendFunctionImplConstructorSymbol = kSuspendFunctionImplSymbol.constructors.single()
|
private val kSuspendFunctionImplConstructorSymbol = kSuspendFunctionImplSymbol.constructors.single()
|
||||||
|
|
||||||
val isLambda = functionReference.origin.isLambda
|
|
||||||
val isKFunction = functionReference.type.isKFunction()
|
|
||||||
val isKSuspendFunction = functionReference.type.isKSuspendFunction()
|
|
||||||
|
|
||||||
fun build(): BuiltFunctionReference {
|
fun build(): BuiltFunctionReference {
|
||||||
val numberOfParameters = unboundFunctionParameters.size
|
val numberOfParameters = unboundFunctionParameters.size
|
||||||
val functionParameterTypes = unboundFunctionParameters.map { it.type }
|
val functionParameterTypes = unboundFunctionParameters.map { it.type }
|
||||||
@@ -267,12 +302,32 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
}
|
}
|
||||||
|
|
||||||
val constructor = buildConstructor()
|
val constructor = buildConstructor()
|
||||||
if (!isKSuspendFunction)
|
val functionInvoke = if (isKSuspendFunction)
|
||||||
|
null
|
||||||
|
else
|
||||||
buildInvokeMethod(functionClass.getInvokeFunction())
|
buildInvokeMethod(functionClass.getInvokeFunction())
|
||||||
suspendFunctionClass?.let {
|
val suspendFunctionInvoke = if (suspendFunctionClass == null)
|
||||||
val invokeMethod = buildInvokeMethod(it.getInvokeFunction())
|
null
|
||||||
if (isKSuspendFunction)
|
else {
|
||||||
invokeMethod.overriddenSymbols += functionClass.getInvokeFunction().symbol
|
buildInvokeMethod(suspendFunctionClass.getInvokeFunction()).also {
|
||||||
|
if (isKSuspendFunction)
|
||||||
|
it.overriddenSymbols += functionClass.getInvokeFunction().symbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
samSuperType?.let { superTypes += it }
|
||||||
|
val sam = samSuperClass?.functions?.single { it.owner.modality == Modality.ABSTRACT }
|
||||||
|
if (sam != null) {
|
||||||
|
if (sam.owner.extensionReceiverParameter != null)
|
||||||
|
buildInvokeMethod(sam.owner)
|
||||||
|
else {
|
||||||
|
// The signatures of SAM and [invoke] coincide - no need to build additional function.
|
||||||
|
val properInvoke = if (sam.isSuspend)
|
||||||
|
suspendFunctionInvoke
|
||||||
|
else
|
||||||
|
functionInvoke
|
||||||
|
if (properInvoke != null)
|
||||||
|
properInvoke.overriddenSymbols += sam
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
functionReferenceClass.superTypes += superTypes
|
functionReferenceClass.superTypes += superTypes
|
||||||
@@ -320,7 +375,7 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
putValueArgument(1, irString(functionReferenceTarget.fullName))
|
putValueArgument(1, irString(functionReferenceTarget.fullName))
|
||||||
putValueArgument(2, receiver)
|
putValueArgument(2, receiver)
|
||||||
putValueArgument(3, irInt(arity))
|
putValueArgument(3, irInt(arity))
|
||||||
putValueArgument(4, irInt(getAdaptedCallableReferenceFlags()))
|
putValueArgument(4, irInt(getFlags()))
|
||||||
putValueArgument(5, with(kTypeGenerator) { irKType(referencedFunction.returnType) })
|
putValueArgument(5, with(kTypeGenerator) { irKType(referencedFunction.returnType) })
|
||||||
}
|
}
|
||||||
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, irBuiltIns.unitType)
|
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, irBuiltIns.unitType)
|
||||||
@@ -335,6 +390,9 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
private val IrFunction.fullName: String
|
private val IrFunction.fullName: String
|
||||||
get() = parent.fqNameForIrSerialization.child(Name.identifier(functionName)).asString()
|
get() = parent.fqNameForIrSerialization.child(Name.identifier(functionName)).asString()
|
||||||
|
|
||||||
|
private fun getFlags() =
|
||||||
|
(if (referencedFunction.isSuspend) 1 else 0) + getAdaptedCallableReferenceFlags() shl 1
|
||||||
|
|
||||||
private fun getAdaptedCallableReferenceFlags(): Int {
|
private fun getAdaptedCallableReferenceFlags(): Int {
|
||||||
if (adaptedReferenceOriginalTarget == null) return 0
|
if (adaptedReferenceOriginalTarget == null) return 0
|
||||||
|
|
||||||
@@ -386,6 +444,8 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
|
|
||||||
this.createDispatchReceiverParameter()
|
this.createDispatchReceiverParameter()
|
||||||
|
|
||||||
|
extensionReceiverParameter = superFunction.extensionReceiverParameter?.copyTo(function)
|
||||||
|
|
||||||
valueParameters += superFunction.valueParameters.mapIndexed { index, parameter ->
|
valueParameters += superFunction.valueParameters.mapIndexed { index, parameter ->
|
||||||
parameter.copyTo(function, DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL, index,
|
parameter.copyTo(function, DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL, index,
|
||||||
type = parameter.type.substitute(typeArgumentsMap))
|
type = parameter.type.substitute(typeArgumentsMap))
|
||||||
@@ -407,7 +467,10 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
argumentToPropertiesMap[parameter]!!
|
argumentToPropertiesMap[parameter]!!
|
||||||
)
|
)
|
||||||
else {
|
else {
|
||||||
if (function.isSuspend && unboundIndex == valueParameters.size)
|
if (parameter == referencedFunction.extensionReceiverParameter
|
||||||
|
&& extensionReceiverParameter != null)
|
||||||
|
irGet(extensionReceiverParameter!!)
|
||||||
|
else if (function.isSuspend && unboundIndex == valueParameters.size)
|
||||||
// For suspend functions the last argument is continuation and it is implicit.
|
// For suspend functions the last argument is continuation and it is implicit.
|
||||||
irCall(getContinuationSymbol.owner, listOf(returnType))
|
irCall(getContinuationSymbol.owner, listOf(returnType))
|
||||||
else
|
else
|
||||||
|
|||||||
+2
-3
@@ -23,6 +23,5 @@ internal class NativeSingleAbstractMethodLowering(context: Context) : SingleAbst
|
|||||||
return typeOperand.classOrNull?.defaultType ?: error("Unsupported SAM conversion: ${typeOperand.render()}")
|
return typeOperand.classOrNull?.defaultType ?: error("Unsupported SAM conversion: ${typeOperand.render()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override val IrType.needEqualsHashCodeMethods: Boolean
|
override val IrType.needEqualsHashCodeMethods get() = true
|
||||||
get() = false
|
}
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.native.internal
|
||||||
|
|
||||||
|
import kotlin.Function
|
||||||
|
|
||||||
|
internal interface FunctionAdapter {
|
||||||
|
fun getFunctionDelegate(): Function<*>
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user