psi2ir: rework representation of bound adapted function references
This fixes the problem in JVM IR backend which didn't pass bound receiver value of an adapted function reference to the superclass (kotlin/jvm/internal/AdaptedFunctionReference), which caused equals to work incorrectly on such references (see changes in box tests). Previously, bound adapted function reference was represented as IrFunctionExpression to an adapter function which calls the callee. The value of the bound receiver in that case could only be found in the body of that adapter function. This is not very convenient, so this change makes psi2ir produce a block of the adapter function + reference to it. The bound receiver value is then found in the reference. This is basically similar to what ProvisionalFunctionExpressionLowering is doing for all function expressions. And since this IR structure is already supported in FunctionReferenceLowering, the problem in the JVM IR is fixed without any additional modifications. However, inliners do not support this IR structure yet, see KT-38535 and KT-38536.
This commit is contained in:
+113
-100
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.builtins.createFunctionType
|
||||
import org.jetbrains.kotlin.builtins.isKFunctionType
|
||||
import org.jetbrains.kotlin.builtins.isKSuspendFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||
@@ -31,9 +32,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.defaultType
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
import org.jetbrains.kotlin.ir.util.referenceClassifier
|
||||
import org.jetbrains.kotlin.ir.util.referenceFunction
|
||||
import org.jetbrains.kotlin.ir.util.withScope
|
||||
@@ -44,6 +42,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments
|
||||
import org.jetbrains.kotlin.psi2ir.intermediate.CallBuilder
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
|
||||
@@ -110,7 +109,7 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
private fun generateAdaptedCallableReference(
|
||||
ktCallableReference: KtCallableReferenceExpression,
|
||||
callBuilder: CallBuilder
|
||||
): IrExpressionBase {
|
||||
): IrExpression {
|
||||
val adapteeDescriptor = callBuilder.descriptor
|
||||
if (adapteeDescriptor !is FunctionDescriptor) {
|
||||
throw AssertionError("Function descriptor expected in adapted callable reference: $adapteeDescriptor")
|
||||
@@ -128,9 +127,9 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
val ktExpectedReturnType = ktFunctionalTypeArguments.last().type
|
||||
val ktExpectedParameterTypes = ktFunctionalTypeArguments.take(ktFunctionalTypeArguments.size - 1).map { it.type }
|
||||
|
||||
val irAdapterFun = createAdapterFun(startOffset, endOffset, adapteeDescriptor, ktExpectedParameterTypes, ktExpectedReturnType)
|
||||
val adapteeCall = createAdapteeCall(startOffset, endOffset, ktCallableReference, adapteeSymbol, callBuilder, irAdapterFun)
|
||||
val irCall = adapteeCall.callExpression
|
||||
val irAdapterFun =
|
||||
createAdapterFun(startOffset, endOffset, adapteeDescriptor, ktExpectedParameterTypes, ktExpectedReturnType, callBuilder)
|
||||
val irCall = createAdapteeCall(startOffset, endOffset, adapteeSymbol, callBuilder, irAdapterFun)
|
||||
|
||||
irAdapterFun.body = IrBlockBodyImpl(startOffset, endOffset).apply {
|
||||
if (KotlinBuiltIns.isUnit(ktExpectedReturnType))
|
||||
@@ -139,96 +138,91 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
statements.add(IrReturnImpl(startOffset, endOffset, context.irBuiltIns.nothingType, irAdapterFun.symbol, irCall))
|
||||
}
|
||||
|
||||
val irFunExpr = IrFunctionExpressionImpl(
|
||||
startOffset, endOffset,
|
||||
irFunctionalType,
|
||||
irAdapterFun,
|
||||
IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE
|
||||
)
|
||||
|
||||
return if (adapteeCall.tmpReceivers.isEmpty()) {
|
||||
irFunExpr
|
||||
} else {
|
||||
IrBlockImpl(
|
||||
startOffset, endOffset, irFunctionalType,
|
||||
origin = null,
|
||||
statements = adapteeCall.tmpReceivers + irFunExpr
|
||||
val resolvedCall = callBuilder.original
|
||||
return statementGenerator.generateCallReceiver(
|
||||
ktCallableReference,
|
||||
resolvedCall.resultingDescriptor,
|
||||
resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
|
||||
isSafe = false
|
||||
).call { dispatchReceiverValue, extensionReceiverValue ->
|
||||
val irAdapterRef = IrFunctionReferenceImpl(
|
||||
startOffset, endOffset, irFunctionalType, irAdapterFun.symbol, irAdapterFun.typeParameters.size,
|
||||
irAdapterFun.valueParameters.size, null, IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE
|
||||
)
|
||||
|
||||
val irDispatchReceiver = dispatchReceiverValue?.loadIfExists()
|
||||
val irExtensionReceiver = extensionReceiverValue?.loadIfExists()
|
||||
check(irDispatchReceiver == null || irExtensionReceiver == null) {
|
||||
"Bound callable reference cannot have both receivers: $adapteeDescriptor"
|
||||
}
|
||||
val receiver = irDispatchReceiver ?: irExtensionReceiver
|
||||
|
||||
if (receiver == null) {
|
||||
IrFunctionExpressionImpl(
|
||||
startOffset, endOffset, irFunctionalType, irAdapterFun, IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE
|
||||
)
|
||||
} else {
|
||||
val statements = SmartList<IrStatement>()
|
||||
if (receiver.isSafeToUseWithoutCopying()) {
|
||||
irAdapterRef.extensionReceiver = receiver
|
||||
} else {
|
||||
val irVariable = statementGenerator.scope.createTemporaryVariable(receiver, "receiver")
|
||||
irAdapterRef.extensionReceiver = IrGetValueImpl(startOffset, endOffset, irVariable.symbol)
|
||||
statements.add(irVariable)
|
||||
}
|
||||
statements.add(irAdapterFun)
|
||||
statements.add(irAdapterRef)
|
||||
|
||||
IrBlockImpl(startOffset, endOffset, irFunctionalType, IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE, statements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AdapteeCall(
|
||||
val callExpression: IrExpression,
|
||||
val tmpReceivers: List<IrVariable>
|
||||
)
|
||||
|
||||
private fun createAdapteeCall(
|
||||
startOffset: Int,
|
||||
endOffset: Int,
|
||||
ktCallableReference: KtCallableReferenceExpression,
|
||||
adapteeSymbol: IrFunctionSymbol,
|
||||
callBuilder: CallBuilder,
|
||||
irAdapterFun: IrSimpleFunction
|
||||
): AdapteeCall {
|
||||
): IrExpression {
|
||||
val resolvedCall = callBuilder.original
|
||||
val resolvedDescriptor = resolvedCall.resultingDescriptor
|
||||
|
||||
val tmpReceivers = SmartList<IrVariable>()
|
||||
val irType = resolvedDescriptor.returnType!!.toIrType()
|
||||
|
||||
val irCall = statementGenerator.generateCallReceiver(
|
||||
ktCallableReference,
|
||||
resolvedDescriptor,
|
||||
resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver,
|
||||
isSafe = false
|
||||
).call { dispatchReceiverValue, extensionReceiverValue ->
|
||||
val irType = resolvedDescriptor.returnType!!.toIrType()
|
||||
val irCall =
|
||||
if (resolvedDescriptor is ConstructorDescriptor)
|
||||
IrConstructorCallImpl.fromSymbolDescriptor(
|
||||
startOffset, endOffset, irType,
|
||||
adapteeSymbol as IrConstructorSymbol
|
||||
)
|
||||
else
|
||||
IrCallImpl(
|
||||
startOffset, endOffset, irType,
|
||||
adapteeSymbol,
|
||||
origin = null, superQualifierSymbol = null
|
||||
)
|
||||
|
||||
val irAdapteeCallInner =
|
||||
if (resolvedDescriptor is ConstructorDescriptor)
|
||||
IrConstructorCallImpl.fromSymbolDescriptor(
|
||||
startOffset, endOffset, irType,
|
||||
adapteeSymbol as IrConstructorSymbol
|
||||
)
|
||||
else
|
||||
IrCallImpl(
|
||||
startOffset, endOffset, irType,
|
||||
adapteeSymbol,
|
||||
origin = null, superQualifierSymbol = null
|
||||
)
|
||||
|
||||
context.callToSubstitutedDescriptorMap[irAdapteeCallInner] = resolvedDescriptor
|
||||
|
||||
val irDispatchReceiver = dispatchReceiverValue?.loadIfExists()
|
||||
val irExtensionReceiver = extensionReceiverValue?.loadIfExists()
|
||||
|
||||
if (irDispatchReceiver != null) {
|
||||
if (irDispatchReceiver.isSafeToUseWithoutCopying()) {
|
||||
irAdapteeCallInner.dispatchReceiver = irDispatchReceiver
|
||||
} else {
|
||||
val irVariable = statementGenerator.scope.createTemporaryVariable(irDispatchReceiver, "this")
|
||||
irAdapteeCallInner.dispatchReceiver = IrGetValueImpl(startOffset, endOffset, irVariable.symbol)
|
||||
tmpReceivers.add(irVariable)
|
||||
}
|
||||
val hasBoundDispatchReceiver = resolvedCall.dispatchReceiver != null && resolvedCall.dispatchReceiver !is TransientReceiver
|
||||
val hasBoundExtensionReceiver = resolvedCall.extensionReceiver != null && resolvedCall.extensionReceiver !is TransientReceiver
|
||||
if (hasBoundDispatchReceiver || hasBoundExtensionReceiver) {
|
||||
// In case of a bound reference, the receiver (which can only be one) is passed in the extension receiver parameter.
|
||||
val receiverValue = IrGetValueImpl(
|
||||
startOffset, endOffset, irAdapterFun.extensionReceiverParameter!!.symbol, IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE
|
||||
)
|
||||
when {
|
||||
hasBoundDispatchReceiver -> irCall.dispatchReceiver = receiverValue
|
||||
hasBoundExtensionReceiver -> irCall.extensionReceiver = receiverValue
|
||||
}
|
||||
|
||||
if (irExtensionReceiver != null) {
|
||||
if (irExtensionReceiver.isSafeToUseWithoutCopying()) {
|
||||
irAdapteeCallInner.extensionReceiver = irExtensionReceiver
|
||||
} else {
|
||||
val irVariable = statementGenerator.scope.createTemporaryVariable(irExtensionReceiver, "receiver")
|
||||
irAdapteeCallInner.extensionReceiver = IrGetValueImpl(startOffset, endOffset, irVariable.symbol)
|
||||
tmpReceivers.add(irVariable)
|
||||
}
|
||||
}
|
||||
|
||||
irAdapteeCallInner.putTypeArguments(callBuilder.typeArguments) { it.toIrType() }
|
||||
|
||||
putAdaptedValueArguments(startOffset, endOffset, irAdapteeCallInner, irAdapterFun, resolvedCall)
|
||||
|
||||
irAdapteeCallInner
|
||||
}
|
||||
|
||||
return AdapteeCall(irCall, tmpReceivers)
|
||||
context.callToSubstitutedDescriptorMap[irCall] = resolvedDescriptor
|
||||
|
||||
irCall.putTypeArguments(callBuilder.typeArguments) { it.toIrType() }
|
||||
|
||||
putAdaptedValueArguments(startOffset, endOffset, irCall, irAdapterFun, resolvedCall)
|
||||
|
||||
return irCall
|
||||
}
|
||||
|
||||
private fun IrExpression.isSafeToUseWithoutCopying() =
|
||||
@@ -330,7 +324,8 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
endOffset: Int,
|
||||
adapteeDescriptor: FunctionDescriptor,
|
||||
ktExpectedParameterTypes: List<KotlinType>,
|
||||
ktExpectedReturnType: KotlinType
|
||||
ktExpectedReturnType: KotlinType,
|
||||
callBuilder: CallBuilder,
|
||||
): IrSimpleFunction {
|
||||
val adapterFunctionDescriptor = WrappedSimpleFunctionDescriptor()
|
||||
|
||||
@@ -361,34 +356,52 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
irAdapterFun.metadata = MetadataSource.Function(adapteeDescriptor)
|
||||
|
||||
irAdapterFun.dispatchReceiverParameter = null
|
||||
irAdapterFun.extensionReceiverParameter = null
|
||||
|
||||
val boundReceiver = callBuilder.original.selectBoundReceiver()
|
||||
if (boundReceiver != null) {
|
||||
irAdapterFun.extensionReceiverParameter =
|
||||
createAdapterParameter(startOffset, endOffset, Name.identifier("receiver"), -1, boundReceiver.type)
|
||||
} else {
|
||||
irAdapterFun.extensionReceiverParameter = null
|
||||
}
|
||||
|
||||
irAdapterFun.valueParameters += ktExpectedParameterTypes.mapIndexed { index, ktExpectedParameterType ->
|
||||
val adapterValueParameterDescriptor = WrappedValueParameterDescriptor()
|
||||
context.symbolTable.declareValueParameter(
|
||||
startOffset, endOffset,
|
||||
IrDeclarationOrigin.ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE,
|
||||
adapterValueParameterDescriptor,
|
||||
ktExpectedParameterType.toIrType()
|
||||
) { irAdapterParameterSymbol ->
|
||||
IrValueParameterImpl(
|
||||
startOffset, endOffset,
|
||||
IrDeclarationOrigin.ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE,
|
||||
irAdapterParameterSymbol,
|
||||
Name.identifier("p$index"),
|
||||
index,
|
||||
ktExpectedParameterType.toIrType(),
|
||||
varargElementType = null, isCrossinline = false, isNoinline = false
|
||||
).also { irAdapterValueParameter ->
|
||||
adapterValueParameterDescriptor.bind(irAdapterValueParameter)
|
||||
}
|
||||
}
|
||||
createAdapterParameter(startOffset, endOffset, Name.identifier("p$index"), index, ktExpectedParameterType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ResolvedCall<*>.selectBoundReceiver(): ReceiverValue? {
|
||||
val dispatchReceiver = dispatchReceiver.takeUnless { it is TransientReceiver }
|
||||
val extensionReceiver = extensionReceiver.takeUnless { it is TransientReceiver }
|
||||
return when {
|
||||
dispatchReceiver == null -> extensionReceiver
|
||||
extensionReceiver == null -> dispatchReceiver
|
||||
else -> error("Bound callable references can't have both receivers: $resultingDescriptor")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAdapterParameter(startOffset: Int, endOffset: Int, name: Name, index: Int, type: KotlinType): IrValueParameter {
|
||||
val descriptor = WrappedValueParameterDescriptor()
|
||||
return context.symbolTable.declareValueParameter(
|
||||
startOffset, endOffset, IrDeclarationOrigin.ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE, descriptor, type.toIrType()
|
||||
) { irAdapterParameterSymbol ->
|
||||
IrValueParameterImpl(
|
||||
startOffset, endOffset,
|
||||
IrDeclarationOrigin.ADAPTER_PARAMETER_FOR_CALLABLE_REFERENCE,
|
||||
irAdapterParameterSymbol,
|
||||
name,
|
||||
index,
|
||||
type.toIrType(),
|
||||
varargElementType = null, isCrossinline = false, isNoinline = false
|
||||
).also { irAdapterValueParameter ->
|
||||
descriptor.bind(irAdapterValueParameter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun generateCallableReference(
|
||||
ktElement: KtElement,
|
||||
type: KotlinType,
|
||||
|
||||
Reference in New Issue
Block a user