JVM_IR: Fix Inline CallableReferences with Varargs
- Added tests to demonstrate broken behaviour: the interaction of inline functions and callable references with varargs and defaults in various combinations. - Refactored InlineCallableReferencesToLambdaPhase to look like and use some of the infrastructure from CallableReferenceLowering. - Lifted some of this infrastructure out to be broadly reusable.
This commit is contained in:
committed by
Mikhael Bogdanov
parent
5360a7a3fe
commit
1074a0ef69
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.IrElementVisitorVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
|
||||
internal class IrInlineReferenceLocator(private val context: JvmBackendContext) : IrElementVisitorVoidWithContext() {
|
||||
val inlineReferences = mutableSetOf<IrCallableReference>()
|
||||
|
||||
// For crossinline lambdas, the call site is null as it's probably in a separate class somewhere.
|
||||
// All other lambdas are guaranteed to be inlined into the scope they are declared in.
|
||||
val lambdaToCallSite = mutableMapOf<IrFunction, IrDeclaration?>()
|
||||
|
||||
override fun visitElement(element: IrElement) = element.acceptChildrenVoid(this)
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) {
|
||||
val function = expression.symbol.owner
|
||||
if (function.isInlineFunctionCall(context)) {
|
||||
for (parameter in function.valueParameters) {
|
||||
if (!parameter.isInlineParameter())
|
||||
continue
|
||||
|
||||
val valueArgument = expression.getValueArgument(parameter.index) ?: continue
|
||||
if (!isInlineIrExpression(valueArgument))
|
||||
continue
|
||||
|
||||
if (valueArgument is IrPropertyReference) {
|
||||
inlineReferences.add(valueArgument)
|
||||
continue
|
||||
}
|
||||
|
||||
val reference = when (valueArgument) {
|
||||
is IrFunctionReference -> valueArgument
|
||||
is IrBlock -> valueArgument.statements.filterIsInstance<IrFunctionReference>().singleOrNull()
|
||||
else -> null
|
||||
} ?: continue
|
||||
|
||||
inlineReferences.add(reference)
|
||||
if (valueArgument is IrBlock && valueArgument.origin.isLambda) {
|
||||
lambdaToCallSite[reference.symbol.owner] =
|
||||
if (parameter.isCrossinline) null else currentScope!!.irElement as IrDeclaration
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitFunctionAccess(expression)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun scan(context: JvmBackendContext, element: IrElement) =
|
||||
IrInlineReferenceLocator(context).apply { element.accept(this, null) }
|
||||
}
|
||||
}
|
||||
+4
-52
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.IrElementVisitorVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
||||
@@ -15,15 +14,10 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.irArray
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isLambda
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
|
||||
@@ -33,12 +27,10 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildClass
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
@@ -49,52 +41,12 @@ internal val callableReferencePhase = makeIrFilePhase(
|
||||
description = "Handle callable references"
|
||||
)
|
||||
|
||||
internal class InlineReferenceLocator(private val context: JvmBackendContext) : IrElementVisitorVoidWithContext() {
|
||||
val inlineReferences = mutableSetOf<IrFunctionReference>()
|
||||
|
||||
// For crossinline lambdas, the call site is null as it's probably in a separate class somewhere.
|
||||
// All other lambdas are guaranteed to be inlined into the scope they are declared in.
|
||||
val lambdaToCallSite = mutableMapOf<IrFunction, IrDeclaration?>()
|
||||
|
||||
override fun visitElement(element: IrElement) = element.acceptChildrenVoid(this)
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) {
|
||||
val function = expression.symbol.owner
|
||||
if (function.isInlineFunctionCall(context)) {
|
||||
for (parameter in function.valueParameters) {
|
||||
if (!parameter.isInlineParameter())
|
||||
continue
|
||||
|
||||
val valueArgument = expression.getValueArgument(parameter.index) ?: continue
|
||||
if (!isInlineIrExpression(valueArgument))
|
||||
continue
|
||||
|
||||
val reference = when (valueArgument) {
|
||||
is IrFunctionReference -> valueArgument
|
||||
is IrBlock -> valueArgument.statements.filterIsInstance<IrFunctionReference>().singleOrNull()
|
||||
else -> null
|
||||
} ?: continue
|
||||
|
||||
inlineReferences.add(reference)
|
||||
if (valueArgument is IrBlock && valueArgument.origin.isLambda) {
|
||||
lambdaToCallSite[reference.symbol.owner] =
|
||||
if (parameter.isCrossinline) null else currentScope!!.irElement as IrDeclaration
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitFunctionAccess(expression)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun scan(context: JvmBackendContext, element: IrElement) =
|
||||
InlineReferenceLocator(context).apply { element.accept(this, null) }
|
||||
}
|
||||
}
|
||||
|
||||
// Originally copied from K/Native
|
||||
internal class CallableReferenceLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
|
||||
// This pass ignores suspend function references and function references used in inline arguments to inline functions.
|
||||
private val ignoredFunctionReferences = mutableSetOf<IrFunctionReference>()
|
||||
private val ignoredFunctionReferences = mutableSetOf<IrCallableReference>()
|
||||
|
||||
private val IrFunctionReference.isIgnored: Boolean
|
||||
get() = (!type.isFunctionOrKFunction() || ignoredFunctionReferences.contains(this)) && !isSuspendCallableReference()
|
||||
@@ -103,7 +55,7 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
private fun IrFunctionReference.isSuspendCallableReference(): Boolean = isSuspend && origin == null
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
ignoredFunctionReferences.addAll(InlineReferenceLocator.scan(context, irFile).inlineReferences)
|
||||
ignoredFunctionReferences.addAll(IrInlineReferenceLocator.scan(context, irFile).inlineReferences)
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
|
||||
|
||||
+112
-164
@@ -7,18 +7,16 @@ package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ScopeWithIr
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyValueParametersToStatic
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.irArray
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isLambda
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.BOUND_REFERENCE_RECEIVER
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
@@ -28,9 +26,7 @@ import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isSubtypeOf
|
||||
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeProjection
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -49,187 +45,139 @@ internal val inlineCallableReferenceToLambdaPhase = makeIrFilePhase(
|
||||
//
|
||||
// foo(::smth) -> foo { a -> smth(a) }
|
||||
//
|
||||
internal class InlineCallableReferenceToLambdaPhase(val context: JvmBackendContext) : FileLoweringPass {
|
||||
private val inlinableCR = mutableSetOf<IrCallableReference>()
|
||||
internal class InlineCallableReferenceToLambdaPhase(val context: JvmBackendContext) : FileLoweringPass,
|
||||
IrElementTransformerVoidWithContext() {
|
||||
|
||||
private var inlinableReferences = mutableSetOf<IrCallableReference>()
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||
val callee = expression.symbol.owner
|
||||
if (callee.isInlineFunctionCall(context)) {
|
||||
for (valueParameter in callee.valueParameters) {
|
||||
if (valueParameter.isInlineParameter()) {
|
||||
expression.getValueArgument(valueParameter.index)?.let { argument ->
|
||||
if (argument is IrCallableReference && isInlineIrExpression(argument)) {
|
||||
inlinableCR.add(argument)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.visitFunctionAccess(expression)
|
||||
}
|
||||
|
||||
|
||||
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
||||
if (expression !in inlinableCR) {
|
||||
return super.visitPropertyReference(expression)
|
||||
}
|
||||
|
||||
//Use getter if field is absent...
|
||||
val field =
|
||||
expression.field?.owner ?: return functionReferenceToLambda(currentScope!!, expression, expression.getter!!.owner)
|
||||
|
||||
//..else use field itself
|
||||
val irBuilder =
|
||||
context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
|
||||
val boundReceiver = expression.dispatchReceiver ?: expression.extensionReceiver
|
||||
return irBuilder.irBlock(expression, IrStatementOrigin.LAMBDA) {
|
||||
lateinit var variableForBoundReceiver: IrVariable
|
||||
if (boundReceiver != null) {
|
||||
variableForBoundReceiver = createTmpVariable(boundReceiver, BOUND_REFERENCE_RECEIVER)
|
||||
}
|
||||
|
||||
val newLambda = buildFun {
|
||||
setSourceRange(expression)
|
||||
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||
name = Name.identifier("stub_for_inline")
|
||||
visibility = Visibilities.LOCAL
|
||||
returnType = field.type
|
||||
isSuspend = false
|
||||
}.apply {
|
||||
|
||||
val receiver =
|
||||
when {
|
||||
field.isStatic -> null
|
||||
boundReceiver != null -> variableForBoundReceiver
|
||||
else -> addValueParameter("receiver", field.parentAsClass.defaultType)
|
||||
}
|
||||
|
||||
val lambdaBodyBuilder = this@InlineCallableReferenceToLambdaPhase.context.createIrBuilder(this.symbol)
|
||||
body = lambdaBodyBuilder.irBlockBody(startOffset, endOffset) {
|
||||
+irReturn(irGetField(if (receiver != null) irGet(receiver) else null, field))
|
||||
}
|
||||
}
|
||||
+newLambda
|
||||
|
||||
+IrFunctionReferenceImpl(
|
||||
expression.startOffset, expression.endOffset, field.type,
|
||||
newLambda.symbol, newLambda.symbol.descriptor, 0,
|
||||
IrStatementOrigin.LAMBDA
|
||||
).apply {
|
||||
copyAttributes(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
if (inlinableCR.contains(expression)) {
|
||||
val referencedFunction = expression.symbol.owner
|
||||
return functionReferenceToLambda(currentScope!!, expression, referencedFunction)
|
||||
}
|
||||
|
||||
return super.visitFunctionReference(expression)
|
||||
}
|
||||
})
|
||||
IrInlineReferenceLocator.scan(context, irFile).let {
|
||||
inlinableReferences.addAll(it.inlineReferences)
|
||||
}
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
|
||||
private fun functionReferenceToLambda(
|
||||
scope: ScopeWithIr,
|
||||
expression: IrCallableReference,
|
||||
referencedFunction: IrFunction
|
||||
): IrExpression {
|
||||
val irBuilder =
|
||||
context.createIrBuilder(scope.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
|
||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
if (expression !in inlinableReferences || expression.origin.isLambda) return expression
|
||||
|
||||
return expandInlineFunctionReferenceToLambda(expression, expression.symbol.owner)
|
||||
}
|
||||
|
||||
val boundReceiver = expression.dispatchReceiver ?: expression.extensionReceiver
|
||||
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
||||
if (expression !in inlinableReferences) return expression
|
||||
|
||||
val expectedType = expression.type
|
||||
var expectedNumValueParameters: Int
|
||||
if (expectedType.isFunctionOrKFunction() || expectedType.isSuspendFunction() || expectedType.isKSuspendFunction()) { // TODO: handle subtypes
|
||||
expectedNumValueParameters = (expectedType as IrSimpleType).arguments.size - 1 // In ...Function classes, the last argument is return type
|
||||
referencedFunction.dispatchReceiverParameter?.let { expectedNumValueParameters-- }
|
||||
referencedFunction.extensionReceiverParameter?.let { expectedNumValueParameters-- }
|
||||
boundReceiver?.let { expectedNumValueParameters++ }
|
||||
assert(referencedFunction.valueParameters.subList(expectedNumValueParameters, referencedFunction.valueParameters.size).all {
|
||||
it.defaultValue != null
|
||||
})
|
||||
return if (expression.field?.owner == null) {
|
||||
// Use getter if field is absent ...
|
||||
expandInlineFunctionReferenceToLambda(expression, expression.getter!!.owner)
|
||||
} else {
|
||||
assert(expectedType.classOrNull?.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) == true)
|
||||
expectedNumValueParameters = 0
|
||||
// ... else use field itself
|
||||
expandInlineFieldReferenceToLambda(expression, expression.field!!.owner)
|
||||
}
|
||||
}
|
||||
|
||||
private fun expandInlineFieldReferenceToLambda(expression: IrPropertyReference, field: IrField): IrExpression {
|
||||
val irBuilder = context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
|
||||
return irBuilder.irBlock(expression, IrStatementOrigin.LAMBDA) {
|
||||
lateinit var variableForBoundReceiver: IrVariable
|
||||
if (boundReceiver != null) {
|
||||
variableForBoundReceiver = createTmpVariable(boundReceiver, BOUND_REFERENCE_RECEIVER)
|
||||
}
|
||||
|
||||
val newLambda = buildFun {
|
||||
val function = buildFun {
|
||||
setSourceRange(expression)
|
||||
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||
name = Name.identifier("stub_for_inline")
|
||||
visibility = Visibilities.LOCAL
|
||||
returnType = field.type
|
||||
isSuspend = false
|
||||
}.apply {
|
||||
val boundReceiver = expression.dispatchReceiver ?: expression.extensionReceiver
|
||||
|
||||
val receiver =
|
||||
when {
|
||||
field.isStatic -> null
|
||||
boundReceiver != null -> irGet(irTemporary(boundReceiver, BOUND_REFERENCE_RECEIVER))
|
||||
else -> irGet(addValueParameter("receiver", field.parentAsClass.defaultType))
|
||||
}
|
||||
|
||||
body = this@InlineCallableReferenceToLambdaPhase.context.createIrBuilder(symbol).run {
|
||||
irExprBody(irGetField(receiver, field))
|
||||
}
|
||||
}
|
||||
|
||||
+function
|
||||
+IrFunctionReferenceImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
field.type,
|
||||
function.symbol,
|
||||
function.symbol.descriptor,
|
||||
typeArgumentsCount = 0,
|
||||
origin = IrStatementOrigin.LAMBDA
|
||||
).apply {
|
||||
copyAttributes(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun expandInlineFunctionReferenceToLambda(expression: IrCallableReference, referencedFunction: IrFunction): IrExpression {
|
||||
val irBuilder = context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
|
||||
return irBuilder.irBlock(expression, IrStatementOrigin.LAMBDA) {
|
||||
|
||||
val parameterTypes = (expression.type as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
|
||||
val argumentTypes = parameterTypes.dropLast(1)
|
||||
|
||||
val boundReceiver: Pair<IrValueParameter, IrExpression>? = expression.getArgumentsWithIr().singleOrNull()
|
||||
|
||||
val function = buildFun {
|
||||
setSourceRange(expression)
|
||||
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||
name = Name.identifier("stub_for_inlining")
|
||||
visibility = Visibilities.LOCAL
|
||||
returnType = referencedFunction.returnType
|
||||
isSuspend = false
|
||||
}.apply {
|
||||
if (referencedFunction is IrConstructor) {
|
||||
copyTypeParametersFrom(referencedFunction.parentAsClass)
|
||||
}
|
||||
copyTypeParametersFrom(referencedFunction)
|
||||
if (boundReceiver == null) {
|
||||
copyValueParametersToStatic(referencedFunction, origin, numValueParametersToCopy = expectedNumValueParameters)
|
||||
} else {
|
||||
for (oldValueParameter in referencedFunction.valueParameters) {
|
||||
if (oldValueParameter.index >= expectedNumValueParameters) break
|
||||
valueParameters.add(
|
||||
oldValueParameter.copyTo(
|
||||
this,
|
||||
origin = origin,
|
||||
index = oldValueParameter.index
|
||||
)
|
||||
)
|
||||
for ((index, argumentType) in argumentTypes.withIndex()) {
|
||||
addValueParameter {
|
||||
name = Name.identifier("p$index")
|
||||
type = argumentType
|
||||
}
|
||||
}
|
||||
val lambdaBodyBuilder = this@InlineCallableReferenceToLambdaPhase.context.createIrBuilder(this.symbol)
|
||||
body = lambdaBodyBuilder.irBlockBody(startOffset, endOffset) {
|
||||
var shift = 0
|
||||
val irCall =
|
||||
if (expression is IrPropertyReference)
|
||||
irGet(referencedFunction.returnType, null, referencedFunction.symbol)
|
||||
else irCall(referencedFunction.symbol)
|
||||
|
||||
+irReturn(
|
||||
irCall.also { call ->
|
||||
for (it in this@apply.typeParameters) {
|
||||
call.putTypeArgument(it.index, expression.getTypeArgument(it.index))
|
||||
}
|
||||
|
||||
referencedFunction.dispatchReceiverParameter?.let {
|
||||
call.dispatchReceiver =
|
||||
irGet(if (expression.dispatchReceiver != null) variableForBoundReceiver else valueParameters[shift++])
|
||||
}
|
||||
referencedFunction.extensionReceiverParameter?.let {
|
||||
call.extensionReceiver =
|
||||
irGet(if (expression.extensionReceiver != null) variableForBoundReceiver else valueParameters[shift++])
|
||||
}
|
||||
|
||||
for (it in referencedFunction.valueParameters.indices) {
|
||||
if (it >= expectedNumValueParameters) break
|
||||
call.putValueArgument(it, irGet(valueParameters[shift++]))
|
||||
}
|
||||
body = this@InlineCallableReferenceToLambdaPhase.context.createJvmIrBuilder(
|
||||
this.symbol,
|
||||
expression.startOffset,
|
||||
expression.endOffset
|
||||
).run {
|
||||
irExprBody(irCall(referencedFunction).apply {
|
||||
this@apply.descriptor.typeParameters.forEach {
|
||||
putTypeArgument(it.index, expression.getTypeArgument(it.index))
|
||||
}
|
||||
)
|
||||
|
||||
var unboundIndex = 0
|
||||
for (parameter in referencedFunction.explicitParameters) {
|
||||
when {
|
||||
boundReceiver?.first == parameter ->
|
||||
irGet(irTemporary(boundReceiver.second))
|
||||
parameter.isVararg && unboundIndex < argumentTypes.size && parameter.type == valueParameters[unboundIndex].type ->
|
||||
irGet(valueParameters[unboundIndex++])
|
||||
parameter.isVararg && (unboundIndex < argumentTypes.size || !parameter.hasDefaultValue()) ->
|
||||
irArray(parameter.type) {
|
||||
(unboundIndex until argumentTypes.size).forEach { +irGet(valueParameters[unboundIndex++]) }
|
||||
}
|
||||
unboundIndex >= argumentTypes.size ->
|
||||
null
|
||||
else ->
|
||||
irGet(valueParameters[unboundIndex++])
|
||||
}?.let { putArgument(referencedFunction, parameter, it) }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+newLambda
|
||||
|
||||
+function
|
||||
+IrFunctionReferenceImpl(
|
||||
expression.startOffset, expression.endOffset, referencedFunction.returnType,
|
||||
newLambda.symbol, newLambda.symbol.descriptor, referencedFunction.typeParameters.size,
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
referencedFunction.returnType,
|
||||
function.symbol,
|
||||
function.symbol.descriptor,
|
||||
referencedFunction.typeParameters.size,
|
||||
IrStatementOrigin.LAMBDA
|
||||
).apply {
|
||||
copyAttributes(expression)
|
||||
|
||||
+2
-1
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.common.ir.remapTypeParameters
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.receiverAndArgs
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isLambda
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.shouldBeHidden
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
@@ -46,7 +47,7 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
|
||||
private val inlineLambdaToCallSite = mutableMapOf<IrFunction, IrDeclaration?>()
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
inlineLambdaToCallSite.putAll(InlineReferenceLocator.scan(context, irFile).lambdaToCallSite)
|
||||
inlineLambdaToCallSite.putAll(IrInlineReferenceLocator.scan(context, irFile).lambdaToCallSite)
|
||||
irFile.transformChildrenVoid(this)
|
||||
pendingTransformations.forEach { it() }
|
||||
}
|
||||
|
||||
@@ -91,6 +91,10 @@ fun IrMemberAccessExpression.getArgumentsWithIr(): List<Pair<IrValueParameter, I
|
||||
val irFunction = when (this) {
|
||||
is IrFunctionAccessExpression -> this.symbol.owner
|
||||
is IrFunctionReference -> this.symbol.owner
|
||||
is IrPropertyReference -> {
|
||||
assert(this.field == null) { "Field should be null to use `getArgumentsWithIr` on IrPropertyReference: ${this.dump()}}" }
|
||||
this.getter!!.owner
|
||||
}
|
||||
else -> error(this)
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType
|
||||
// WITH_RUNTIME
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
inline fun foo(mkString: () -> String): String =
|
||||
mkString()
|
||||
|
||||
fun bar (xs: CharArray = charArrayOf('O','K')) =
|
||||
String(xs)
|
||||
|
||||
fun box(): String = foo(::bar)
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// !LANGUAGE: +NewInference
|
||||
// WITH_RUNTIME
|
||||
// IGNORE_BACKEND: JS, JS_IR
|
||||
|
||||
inline fun foo(mkString: (Char, Char) -> String): String =
|
||||
mkString('O','K')
|
||||
|
||||
fun bar (vararg xs: Char) =
|
||||
String(xs)
|
||||
|
||||
fun box(): String = foo(::bar)
|
||||
// -> { a, b -> bar(a, b) }
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// !LANGUAGE: +NewInference +FunctionReferenceWithDefaultValueAsOtherType
|
||||
// IGNORE_BACKEND: JS, JVM_IR, JS_IR
|
||||
// IGNORE_BACKEND: JS, JS_IR
|
||||
|
||||
fun foo(vararg l: Long, s: String = "OK"): String =
|
||||
if (l.size == 0) s else "Fail"
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// !LANGUAGE: +NewInference
|
||||
// WITH_RUNTIME
|
||||
// IGNORE_BACKEND: JS, JS_IR
|
||||
|
||||
inline fun foo(x: (Int, Int) -> Int): Int =
|
||||
x(120,3)
|
||||
|
||||
fun bar(vararg x: Int): Int =
|
||||
x.sum()
|
||||
|
||||
fun box(): String =
|
||||
if (foo(::bar) == 123) "OK" else "FAIL"
|
||||
+18
-3
@@ -2632,9 +2632,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/emptyVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inline.kt")
|
||||
public void testInline() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inline.kt");
|
||||
@TestMetadata("inlineDefault.kt")
|
||||
public void testInlineDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVararg.kt")
|
||||
public void testInlineVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargAndDefault.kt")
|
||||
public void testInlineVarargAndDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargInts.kt")
|
||||
public void testInlineVarargInts() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargInts.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerConstructorWithVararg.kt")
|
||||
|
||||
+18
-3
@@ -2632,9 +2632,24 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/emptyVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inline.kt")
|
||||
public void testInline() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inline.kt");
|
||||
@TestMetadata("inlineDefault.kt")
|
||||
public void testInlineDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVararg.kt")
|
||||
public void testInlineVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargAndDefault.kt")
|
||||
public void testInlineVarargAndDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargInts.kt")
|
||||
public void testInlineVarargInts() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargInts.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerConstructorWithVararg.kt")
|
||||
|
||||
+18
-3
@@ -2612,9 +2612,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/emptyVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inline.kt")
|
||||
public void testInline() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inline.kt");
|
||||
@TestMetadata("inlineDefault.kt")
|
||||
public void testInlineDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVararg.kt")
|
||||
public void testInlineVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargAndDefault.kt")
|
||||
public void testInlineVarargAndDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargInts.kt")
|
||||
public void testInlineVarargInts() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargInts.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerConstructorWithVararg.kt")
|
||||
|
||||
Generated
+18
-3
@@ -2072,9 +2072,24 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/emptyVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inline.kt")
|
||||
public void testInline() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inline.kt");
|
||||
@TestMetadata("inlineDefault.kt")
|
||||
public void testInlineDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVararg.kt")
|
||||
public void testInlineVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargAndDefault.kt")
|
||||
public void testInlineVarargAndDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargInts.kt")
|
||||
public void testInlineVarargInts() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargInts.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerConstructorWithVararg.kt")
|
||||
|
||||
+18
-3
@@ -2072,9 +2072,24 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/emptyVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inline.kt")
|
||||
public void testInline() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inline.kt");
|
||||
@TestMetadata("inlineDefault.kt")
|
||||
public void testInlineDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVararg.kt")
|
||||
public void testInlineVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargAndDefault.kt")
|
||||
public void testInlineVarargAndDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargAndDefault.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineVarargInts.kt")
|
||||
public void testInlineVarargInts() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/varargAndDefaults/inlineVarargInts.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerConstructorWithVararg.kt")
|
||||
|
||||
Reference in New Issue
Block a user