KT-36024 Generate adapted callable references as lambdas
Make sure both JVM and JVM_IR use the same information to determine whether a callable reference requires argument adaptation.
This commit is contained in:
committed by
Alexander Udalov
parent
ddf7f53118
commit
d1c5a42124
+38
-68
@@ -41,7 +41,7 @@ import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
|
||||
class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) {
|
||||
|
||||
@@ -72,13 +72,10 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
|
||||
val callBuilder = unwrapCallableDescriptorAndTypeArguments(resolvedCall, context.extensions.samConversion)
|
||||
|
||||
if (resolvedCall.valueArguments.isNotEmpty()) {
|
||||
val adaptedCallableReference = generateAdaptedCallableReference(ktCallableReference, callBuilder)
|
||||
if (adaptedCallableReference.hasResultAdaptation ||
|
||||
!isTrivialArgumentAdaptation(adaptedCallableReference.irAdapteeCall)
|
||||
) {
|
||||
return adaptedCallableReference.irReferenceExpression
|
||||
}
|
||||
if (resolvedCall.valueArguments.isNotEmpty() ||
|
||||
requiresCoercionToUnit(resolvedDescriptor, getTypeInferredByFrontendOrFail(ktCallableReference))
|
||||
) {
|
||||
return generateAdaptedCallableReference(ktCallableReference, callBuilder)
|
||||
}
|
||||
|
||||
return statementGenerator.generateCallReceiver(
|
||||
@@ -99,27 +96,15 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
}
|
||||
}
|
||||
|
||||
private fun isTrivialArgumentAdaptation(irAdapteeCall: IrFunctionAccessExpression): Boolean {
|
||||
for (i in 0 until irAdapteeCall.valueArgumentsCount) {
|
||||
val irValueArgument = irAdapteeCall.getValueArgument(i) ?: return false
|
||||
if (irValueArgument is IrVararg) {
|
||||
val irVarargElements = irValueArgument.elements
|
||||
if (irVarargElements.size != 1 || irVarargElements[0] !is IrSpreadElement) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
private fun requiresCoercionToUnit(descriptor: CallableDescriptor, callableReferenceType: KotlinType): Boolean {
|
||||
val ktExpectedReturnType = callableReferenceType.arguments.last().type
|
||||
return KotlinBuiltIns.isUnit(ktExpectedReturnType) && !KotlinBuiltIns.isUnit(descriptor.returnType!!)
|
||||
}
|
||||
|
||||
private class AdaptedCallableReference(
|
||||
val irReferenceExpression: IrExpression,
|
||||
val irAdapteeCall: IrFunctionAccessExpression,
|
||||
val hasResultAdaptation: Boolean
|
||||
)
|
||||
|
||||
private fun generateAdaptedCallableReference(
|
||||
ktCallableReference: KtCallableReferenceExpression,
|
||||
callBuilder: CallBuilder
|
||||
): AdaptedCallableReference {
|
||||
): IrExpressionBase {
|
||||
val adapteeDescriptor = callBuilder.descriptor
|
||||
if (adapteeDescriptor !is FunctionDescriptor) {
|
||||
throw AssertionError("Function descriptor expected in adapted callable reference: $adapteeDescriptor")
|
||||
@@ -140,10 +125,6 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
val irAdapterFun = createAdapterFun(startOffset, endOffset, adapteeDescriptor, ktExpectedParameterTypes, ktExpectedReturnType)
|
||||
val adapteeCall = createAdapteeCall(startOffset, endOffset, ktCallableReference, adapteeSymbol, callBuilder, irAdapterFun)
|
||||
val irCall = adapteeCall.callExpression
|
||||
val irAdapteeCallInner = adapteeCall.innerCallExpression
|
||||
|
||||
val tmpDispatchReceiver = adapteeCall.tmpDispatchReceiver
|
||||
val tmpExtensionReceiver = adapteeCall.tmpExtensionReceiver
|
||||
|
||||
irAdapterFun.body = IrBlockBodyImpl(startOffset, endOffset).apply {
|
||||
if (KotlinBuiltIns.isUnit(ktExpectedReturnType))
|
||||
@@ -152,33 +133,27 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
statements.add(IrReturnImpl(startOffset, endOffset, context.irBuiltIns.nothingType, irAdapterFun.symbol, irCall))
|
||||
}
|
||||
|
||||
val irBlock = IrBlockImpl(startOffset, endOffset, irFunctionalType).apply {
|
||||
statements.addIfNotNull(tmpDispatchReceiver)
|
||||
statements.addIfNotNull(tmpExtensionReceiver)
|
||||
statements.add(irAdapterFun)
|
||||
statements.add(
|
||||
IrFunctionReferenceImpl(
|
||||
startOffset, endOffset,
|
||||
irFunctionalType,
|
||||
irAdapterFun.symbol,
|
||||
typeArgumentsCount = 0,
|
||||
valueArgumentsCount = irAdapterFun.valueParameters.size,
|
||||
reflectionTarget = null
|
||||
)
|
||||
val irFunExpr = IrFunctionExpressionImpl(
|
||||
startOffset, endOffset,
|
||||
irFunctionalType,
|
||||
irAdapterFun,
|
||||
IrStatementOrigin.LAMBDA
|
||||
)
|
||||
|
||||
return if (adapteeCall.tmpReceivers.isEmpty()) {
|
||||
irFunExpr
|
||||
} else {
|
||||
IrBlockImpl(
|
||||
startOffset, endOffset, irFunctionalType,
|
||||
origin = null,
|
||||
statements = adapteeCall.tmpReceivers + irFunExpr
|
||||
)
|
||||
}
|
||||
|
||||
return AdaptedCallableReference(
|
||||
irBlock, irAdapteeCallInner,
|
||||
KotlinBuiltIns.isUnit(ktExpectedReturnType) && !KotlinBuiltIns.isUnit(adapteeDescriptor.returnType!!)
|
||||
)
|
||||
}
|
||||
|
||||
private class AdapteeCall(
|
||||
val callExpression: IrExpression,
|
||||
val innerCallExpression: IrFunctionAccessExpression,
|
||||
val tmpDispatchReceiver: IrVariable?,
|
||||
val tmpExtensionReceiver: IrVariable?
|
||||
val tmpReceivers: List<IrVariable>
|
||||
)
|
||||
|
||||
private fun createAdapteeCall(
|
||||
@@ -192,9 +167,7 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
val resolvedCall = callBuilder.original
|
||||
val resolvedDescriptor = resolvedCall.resultingDescriptor
|
||||
|
||||
var irAdapteeCall: IrFunctionAccessExpression? = null
|
||||
var tmpDispatchReceiver: IrVariable? = null
|
||||
var tmpExtensionReceiver: IrVariable? = null
|
||||
val tmpReceivers = SmartList<IrVariable>()
|
||||
|
||||
val irCall = statementGenerator.generateCallReceiver(
|
||||
ktCallableReference,
|
||||
@@ -228,7 +201,7 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
} else {
|
||||
val irVariable = statementGenerator.scope.createTemporaryVariable(irDispatchReceiver, "this")
|
||||
irAdapteeCallInner.dispatchReceiver = IrGetValueImpl(startOffset, endOffset, irVariable.symbol)
|
||||
tmpDispatchReceiver = irVariable
|
||||
tmpReceivers.add(irVariable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +211,7 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
} else {
|
||||
val irVariable = statementGenerator.scope.createTemporaryVariable(irExtensionReceiver, "receiver")
|
||||
irAdapteeCallInner.extensionReceiver = IrGetValueImpl(startOffset, endOffset, irVariable.symbol)
|
||||
tmpExtensionReceiver = irVariable
|
||||
tmpReceivers.add(irVariable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,12 +219,10 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
|
||||
putAdaptedValueArguments(startOffset, endOffset, irAdapteeCallInner, irAdapterFun, resolvedCall)
|
||||
|
||||
irAdapteeCall = irAdapteeCallInner
|
||||
|
||||
irAdapteeCallInner
|
||||
}
|
||||
|
||||
return AdapteeCall(irCall, irAdapteeCall!!, tmpDispatchReceiver, tmpExtensionReceiver)
|
||||
return AdapteeCall(irCall, tmpReceivers)
|
||||
}
|
||||
|
||||
private fun IrExpression.isSafeToUseWithoutCopying() =
|
||||
@@ -271,10 +242,6 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
resolvedCall: ResolvedCall<*>
|
||||
) {
|
||||
val adaptedArguments = resolvedCall.valueArguments
|
||||
if (adaptedArguments.isEmpty()) {
|
||||
throw AssertionError("Callable reference with adapted arguments expected: ${resolvedCall.call.callElement.text}")
|
||||
}
|
||||
|
||||
var shift = 0
|
||||
if (resolvedCall.dispatchReceiver is TransientReceiver) {
|
||||
// Unbound callable reference 'A::foo', receiver is passed as a first parameter
|
||||
@@ -309,13 +276,16 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
|
||||
is DefaultValueArgument ->
|
||||
null
|
||||
is VarargValueArgument ->
|
||||
IrVarargImpl(
|
||||
startOffset, endOffset,
|
||||
valueParameter.type.toIrType(), valueParameter.varargElementType!!.toIrType(),
|
||||
resolvedValueArgument.arguments.map {
|
||||
adaptValueArgument(startOffset, endOffset, it, irAdapterFun, shift)
|
||||
}
|
||||
)
|
||||
if (resolvedValueArgument.arguments.isEmpty())
|
||||
null
|
||||
else
|
||||
IrVarargImpl(
|
||||
startOffset, endOffset,
|
||||
valueParameter.type.toIrType(), valueParameter.varargElementType!!.toIrType(),
|
||||
resolvedValueArgument.arguments.map {
|
||||
adaptValueArgument(startOffset, endOffset, it, irAdapterFun, shift)
|
||||
}
|
||||
)
|
||||
is ExpressionValueArgument -> {
|
||||
val valueArgument = resolvedValueArgument.valueArgument!!
|
||||
|
||||
|
||||
Reference in New Issue
Block a user