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:
Dmitry Petrov
2020-03-19 14:03:01 +03:00
committed by Alexander Udalov
parent ddf7f53118
commit d1c5a42124
29 changed files with 478 additions and 183 deletions
@@ -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!!