[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"
|
||||
)
|
||||
|
||||
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(
|
||||
::FinallyBlocksLowering,
|
||||
name = "FinallyBlocks",
|
||||
@@ -294,7 +281,21 @@ internal val functionReferencePhase = makeKonanFileLoweringPhase(
|
||||
::FunctionReferenceLowering,
|
||||
name = "FunctionReference",
|
||||
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(
|
||||
|
||||
+4
-4
@@ -369,14 +369,14 @@ internal val allLoweringsPhase = NamedCompilerPhase(
|
||||
defaultParameterExtentPhase,
|
||||
innerClassPhase,
|
||||
dataClassesPhase,
|
||||
singleAbstractMethodPhase,
|
||||
ifNullExpressionsFusionPhase,
|
||||
builtinOperatorPhase,
|
||||
finallyBlocksPhase,
|
||||
testProcessorPhase,
|
||||
enumClassPhase,
|
||||
delegationPhase,
|
||||
functionReferencePhase,
|
||||
singleAbstractMethodPhase,
|
||||
builtinOperatorPhase,
|
||||
finallyBlocksPhase,
|
||||
enumClassPhase,
|
||||
interopPhase,
|
||||
varargPhase,
|
||||
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"))
|
||||
|
||||
|
||||
+77
-14
@@ -94,6 +94,33 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
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 {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
@@ -119,8 +146,12 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
return expression
|
||||
}
|
||||
|
||||
return transformFunctionReference(expression)
|
||||
}
|
||||
|
||||
fun transformFunctionReference(expression: IrFunctionReference, samSuperType: IrType? = null): IrExpression {
|
||||
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)
|
||||
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol,
|
||||
expression.startOffset, expression.endOffset)
|
||||
@@ -130,7 +161,8 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
}
|
||||
}
|
||||
}
|
||||
}, null)
|
||||
}, data = null)
|
||||
|
||||
irFile.declarations += generatedClasses
|
||||
}
|
||||
|
||||
@@ -146,7 +178,8 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
private val continuationClassSymbol = getContinuationSymbol.owner.returnType.classifierOrFail as IrClassSymbol
|
||||
|
||||
private inner class FunctionReferenceBuilder(val parent: IrDeclarationParent,
|
||||
val functionReference: IrFunctionReference) {
|
||||
val functionReference: IrFunctionReference,
|
||||
val samSuperType: IrType?) {
|
||||
|
||||
private val startOffset = functionReference.startOffset
|
||||
private val endOffset = functionReference.endOffset
|
||||
@@ -159,6 +192,12 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
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? =
|
||||
// TODO: Copied from JVM.
|
||||
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 kSuspendFunctionImplConstructorSymbol = kSuspendFunctionImplSymbol.constructors.single()
|
||||
|
||||
val isLambda = functionReference.origin.isLambda
|
||||
val isKFunction = functionReference.type.isKFunction()
|
||||
val isKSuspendFunction = functionReference.type.isKSuspendFunction()
|
||||
|
||||
fun build(): BuiltFunctionReference {
|
||||
val numberOfParameters = unboundFunctionParameters.size
|
||||
val functionParameterTypes = unboundFunctionParameters.map { it.type }
|
||||
@@ -267,12 +302,32 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
}
|
||||
|
||||
val constructor = buildConstructor()
|
||||
if (!isKSuspendFunction)
|
||||
val functionInvoke = if (isKSuspendFunction)
|
||||
null
|
||||
else
|
||||
buildInvokeMethod(functionClass.getInvokeFunction())
|
||||
suspendFunctionClass?.let {
|
||||
val invokeMethod = buildInvokeMethod(it.getInvokeFunction())
|
||||
if (isKSuspendFunction)
|
||||
invokeMethod.overriddenSymbols += functionClass.getInvokeFunction().symbol
|
||||
val suspendFunctionInvoke = if (suspendFunctionClass == null)
|
||||
null
|
||||
else {
|
||||
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
|
||||
@@ -320,7 +375,7 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
putValueArgument(1, irString(functionReferenceTarget.fullName))
|
||||
putValueArgument(2, receiver)
|
||||
putValueArgument(3, irInt(arity))
|
||||
putValueArgument(4, irInt(getAdaptedCallableReferenceFlags()))
|
||||
putValueArgument(4, irInt(getFlags()))
|
||||
putValueArgument(5, with(kTypeGenerator) { irKType(referencedFunction.returnType) })
|
||||
}
|
||||
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, irBuiltIns.unitType)
|
||||
@@ -335,6 +390,9 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
private val IrFunction.fullName: String
|
||||
get() = parent.fqNameForIrSerialization.child(Name.identifier(functionName)).asString()
|
||||
|
||||
private fun getFlags() =
|
||||
(if (referencedFunction.isSuspend) 1 else 0) + getAdaptedCallableReferenceFlags() shl 1
|
||||
|
||||
private fun getAdaptedCallableReferenceFlags(): Int {
|
||||
if (adaptedReferenceOriginalTarget == null) return 0
|
||||
|
||||
@@ -386,6 +444,8 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
|
||||
this.createDispatchReceiverParameter()
|
||||
|
||||
extensionReceiverParameter = superFunction.extensionReceiverParameter?.copyTo(function)
|
||||
|
||||
valueParameters += superFunction.valueParameters.mapIndexed { index, parameter ->
|
||||
parameter.copyTo(function, DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL, index,
|
||||
type = parameter.type.substitute(typeArgumentsMap))
|
||||
@@ -407,7 +467,10 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
argumentToPropertiesMap[parameter]!!
|
||||
)
|
||||
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.
|
||||
irCall(getContinuationSymbol.owner, listOf(returnType))
|
||||
else
|
||||
|
||||
+2
-3
@@ -23,6 +23,5 @@ internal class NativeSingleAbstractMethodLowering(context: Context) : SingleAbst
|
||||
return typeOperand.classOrNull?.defaultType ?: error("Unsupported SAM conversion: ${typeOperand.render()}")
|
||||
}
|
||||
|
||||
override val IrType.needEqualsHashCodeMethods: Boolean
|
||||
get() = false
|
||||
}
|
||||
override val IrType.needEqualsHashCodeMethods get() = true
|
||||
}
|
||||
@@ -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