Fix interpretation of default varargs

This commit is contained in:
Ivan Kylchik
2021-07-29 02:57:46 +03:00
committed by TeamCityServer
parent 0ce3dd117e
commit 3932acf843
6 changed files with 80 additions and 39 deletions
@@ -113,15 +113,15 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro
val callStack = environment.callStack val callStack = environment.callStack
val hasDefaults = (0 until expression.valueArgumentsCount).any { expression.getValueArgument(it) == null } val hasDefaults = (0 until expression.valueArgumentsCount).any { expression.getValueArgument(it) == null }
if (hasDefaults) { if (hasDefaults) {
val ownerWithDefaults = expression.getFunctionThatContainsDefaults() environment.getCachedFunction(expression.symbol, fromDelegatingCall = expression is IrDelegatingConstructorCall)?.let {
environment.createdFunctionsCache[ownerWithDefaults.symbol]?.let { val callToDefault = it.owner.createCall().apply { environment.irBuiltIns.copyArgs(expression, this) }
val callToDefault = it.owner.createCall().apply { expression.copyArgsInto(this) }
callStack.addInstruction(CompoundInstruction(callToDefault)) callStack.addInstruction(CompoundInstruction(callToDefault))
return return
} }
// if some arguments are not defined, then it is necessary to create temp function where defaults will be evaluated // if some arguments are not defined, then it is necessary to create temp function where defaults will be evaluated
val actualParameters = MutableList<IrValueDeclaration?>(expression.valueArgumentsCount) { null } val actualParameters = MutableList<IrValueDeclaration?>(expression.valueArgumentsCount) { null }
val ownerWithDefaults = expression.getFunctionThatContainsDefaults()
val visibility = when (expression) { val visibility = when (expression) {
is IrEnumConstructorCall, is IrDelegatingConstructorCall -> DescriptorVisibilities.LOCAL is IrEnumConstructorCall, is IrDelegatingConstructorCall -> DescriptorVisibilities.LOCAL
else -> ownerWithDefaults.visibility else -> ownerWithDefaults.visibility
@@ -138,14 +138,15 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro
val originalParameter = ownerWithDefaults.valueParameters[index] val originalParameter = ownerWithDefaults.valueParameters[index]
val copiedParameter = originalParameter.deepCopyWithSymbols(this) val copiedParameter = originalParameter.deepCopyWithSymbols(this)
this.valueParameters += copiedParameter this.valueParameters += copiedParameter
actualParameters[index] = if (copiedParameter.defaultValue != null) { actualParameters[index] = if (copiedParameter.defaultValue != null || copiedParameter.isVararg) {
copiedParameter.type = copiedParameter.type.makeNullable() // make nullable type to keep consistency; parameter can be null if it is missing copiedParameter.type = copiedParameter.type.makeNullable() // make nullable type to keep consistency; parameter can be null if it is missing
val irGetParameter = copiedParameter.createGetValue() val irGetParameter = copiedParameter.createGetValue()
// if parameter is vararg and it is missing then create constructor call for empty array
val defaultInitializer = originalParameter.getDefaultWithActualParameters(this@apply, actualParameters) val defaultInitializer = originalParameter.getDefaultWithActualParameters(this@apply, actualParameters)
?: expression.getVarargType(index)?.let { null.toIrConst(it) } // if parameter is vararg and it is missing ?: environment.irBuiltIns.emptyArrayConstructor(expression.getVarargType(index)!!.getTypeIfReified(callStack))
copiedParameter.createTempVariable().apply variable@{ copiedParameter.createTempVariable().apply variable@{
this@variable.initializer = environment.irBuiltIns.irIfNullThenElse(irGetParameter, defaultInitializer!!, irGetParameter) this@variable.initializer = environment.irBuiltIns.irIfNullThenElse(irGetParameter, defaultInitializer, irGetParameter)
} }
} else { } else {
copiedParameter copiedParameter
@@ -159,8 +160,9 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro
(0 until expression.valueArgumentsCount).forEach { callWithAllArgs.putValueArgument(it, actualParameters[it]?.createGetValue()) } (0 until expression.valueArgumentsCount).forEach { callWithAllArgs.putValueArgument(it, actualParameters[it]?.createGetValue()) }
defaultFun.body = (actualParameters.filterIsInstance<IrVariable>() + defaultFun.createReturn(callWithAllArgs)).wrapWithBlockBody() defaultFun.body = (actualParameters.filterIsInstance<IrVariable>() + defaultFun.createReturn(callWithAllArgs)).wrapWithBlockBody()
environment.createdFunctionsCache[ownerWithDefaults.symbol] = defaultFun.symbol val callToDefault = environment.setCachedFunction(
val callToDefault = defaultFun.createCall().apply { expression.copyArgsInto(this) } expression.symbol, fromDelegatingCall = expression is IrDelegatingConstructorCall, newFunction = defaultFun.symbol
).owner.createCall().apply { environment.irBuiltIns.copyArgs(expression, this) }
callStack.addInstruction(CompoundInstruction(callToDefault)) callStack.addInstruction(CompoundInstruction(callToDefault))
} else { } else {
val irFunction = expression.symbol.owner val irFunction = expression.symbol.owner
@@ -142,12 +142,7 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
val irFunction = valueParameter.parent as IrFunction val irFunction = valueParameter.parent as IrFunction
fun isReceiver() = irFunction.dispatchReceiverParameter == valueParameter || irFunction.extensionReceiverParameter == valueParameter fun isReceiver() = irFunction.dispatchReceiverParameter == valueParameter || irFunction.extensionReceiverParameter == valueParameter
val result = callStack.popState() val state = callStack.popState()
val state = when {
// if vararg is empty
result.isNull() && valueParameter.isVararg -> listOf<Any?>().toPrimitiveStateArray((result as Primitive<*>).type)
else -> result
}
state.checkNullability(valueParameter.type, environment) { state.checkNullability(valueParameter.type, environment) {
if (isReceiver()) return@checkNullability NullPointerException() if (isReceiver()) return@checkNullability NullPointerException()
@@ -23,7 +23,20 @@ class IrInterpreterEnvironment(
internal val irExceptions = mutableListOf<IrClass>() internal val irExceptions = mutableListOf<IrClass>()
internal var mapOfEnums = mutableMapOf<IrSymbol, Complex>() internal var mapOfEnums = mutableMapOf<IrSymbol, Complex>()
internal var mapOfObjects = mutableMapOf<IrSymbol, Complex>() internal var mapOfObjects = mutableMapOf<IrSymbol, Complex>()
internal var createdFunctionsCache = mutableMapOf<IrFunctionSymbol, IrFunctionSymbol>()
private data class CacheFunctionSignature(
val symbol: IrFunctionSymbol,
// must create different invoke function for function expression with and without receivers
val hasDispatchReceiver: Boolean,
val hasExtensionReceiver: Boolean,
// must create different default functions for constructor call and delegating call;
// their symbols are the same but calls are different, so default function must return different calls
val fromDelegatingCall: Boolean
)
private var functionCache = mutableMapOf<CacheFunctionSignature, IrFunctionSymbol>()
init { init {
mapOfObjects[irBuiltIns.unitClass] = Common(irBuiltIns.unitClass.owner) mapOfObjects[irBuiltIns.unitClass] = Common(irBuiltIns.unitClass.owner)
@@ -47,4 +60,24 @@ class IrInterpreterEnvironment(
fun copyWithNewCallStack(): IrInterpreterEnvironment { fun copyWithNewCallStack(): IrInterpreterEnvironment {
return IrInterpreterEnvironment(this) return IrInterpreterEnvironment(this)
} }
internal fun getCachedFunction(
symbol: IrFunctionSymbol,
hasDispatchReceiver: Boolean = false,
hasExtensionReceiver: Boolean = false,
fromDelegatingCall: Boolean = false
): IrFunctionSymbol? {
return functionCache[CacheFunctionSignature(symbol, hasDispatchReceiver, hasExtensionReceiver, fromDelegatingCall)]
}
internal fun setCachedFunction(
symbol: IrFunctionSymbol,
hasDispatchReceiver: Boolean = false,
hasExtensionReceiver: Boolean = false,
fromDelegatingCall: Boolean = false,
newFunction: IrFunctionSymbol
): IrFunctionSymbol {
functionCache[CacheFunctionSignature(symbol, hasDispatchReceiver, hasExtensionReceiver, fromDelegatingCall)] = newFunction
return newFunction
}
} }
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
@@ -28,6 +27,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -104,8 +104,8 @@ internal fun IrFunction.createCall(origin: IrStatementOrigin? = null): IrCall {
return IrCallImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, returnType, symbol, typeParameters.size, valueParameters.size, origin) return IrCallImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, returnType, symbol, typeParameters.size, valueParameters.size, origin)
} }
internal fun IrConstructor.createConstructorCall(): IrConstructorCall { internal fun IrConstructor.createConstructorCall(irType: IrType = returnType): IrConstructorCall {
return IrConstructorCallImpl.fromSymbolOwner(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, returnType, symbol) return IrConstructorCallImpl.fromSymbolOwner(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, irType, symbol)
} }
internal fun IrValueDeclaration.createGetValue(): IrGetValue { internal fun IrValueDeclaration.createGetValue(): IrGetValue {
@@ -165,14 +165,13 @@ internal fun IrFunctionAccessExpression.shallowCopy(copyTypeArguments: Boolean =
} }
} }
internal fun IrFunctionAccessExpression.copyArgsInto(newCall: IrFunctionAccessExpression) { internal fun IrBuiltIns.copyArgs(from: IrFunctionAccessExpression, into: IrFunctionAccessExpression) {
val symbol = this.symbol.owner into.dispatchReceiver = from.dispatchReceiver
newCall.dispatchReceiver = this.dispatchReceiver into.extensionReceiver = from.extensionReceiver
newCall.extensionReceiver = this.extensionReceiver (0 until from.valueArgumentsCount)
(0 until this.valueArgumentsCount) .map { from.getValueArgument(it) }
.map { this.getValueArgument(it) }
.forEachIndexed { i, arg -> .forEachIndexed { i, arg ->
newCall.putValueArgument(i, arg ?: IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, symbol.valueParameters[i].type)) into.putValueArgument(i, arg ?: IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, this.anyNType))
} }
} }
@@ -186,7 +185,24 @@ internal fun IrBuiltIns.irEquals(arg1: IrExpression, arg2: IrExpression): IrCall
internal fun IrBuiltIns.irIfNullThenElse(nullableArg: IrExpression, ifTrue: IrExpression, ifFalse: IrExpression): IrWhen { internal fun IrBuiltIns.irIfNullThenElse(nullableArg: IrExpression, ifTrue: IrExpression, ifFalse: IrExpression): IrWhen {
val nullCondition = this.irEquals(nullableArg, IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, this.anyNType)) val nullCondition = this.irEquals(nullableArg, IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, this.anyNType))
val trueBranch = IrBranchImpl(nullCondition, ifTrue) // use default val trueBranch = IrBranchImpl(nullCondition, ifTrue) // use default
val elseBranch = IrElseBranchImpl(IrConstImpl.constTrue(0, 0, this.booleanType), ifFalse) val elseBranch = IrElseBranchImpl(IrConstImpl.constTrue(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, this.booleanType), ifFalse)
return IrIfThenElseImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, ifTrue.type).apply { branches += listOf(trueBranch, elseBranch) } return IrIfThenElseImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, ifTrue.type).apply { branches += listOf(trueBranch, elseBranch) }
} }
internal fun IrBuiltIns.emptyArrayConstructor(arrayType: IrType): IrConstructorCall {
val arrayClass = arrayType.classOrNull!!.owner
val constructor = arrayClass.constructors.firstOrNull { it.valueParameters.size == 1 } ?: arrayClass.constructors.first()
val constructorCall = constructor.createConstructorCall(arrayType)
constructorCall.putValueArgument(0, 0.toIrConst(this.intType))
if (constructor.valueParameters.size == 2) {
// TODO find a way to avoid creation of empty lambda
val tempFunction = createTempFunction(Name.identifier("TempForVararg"), this.anyType)
tempFunction.parent = arrayClass // can be anything, will not be used in any case
val initLambda = IrFunctionExpressionImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, constructor.valueParameters[1].type, tempFunction, IrStatementOrigin.LAMBDA)
constructorCall.putValueArgument(1, initLambda)
constructorCall.putTypeArgument(0, (arrayType as IrSimpleType).arguments.singleOrNull()?.typeOrNull)
}
return constructorCall
}
@@ -33,10 +33,7 @@ val evaluateIntrinsicAnnotation = FqName("kotlin.EvaluateIntrinsic")
val contractsDslAnnotation = FqName("kotlin.internal.ContractsDsl") val contractsDslAnnotation = FqName("kotlin.internal.ContractsDsl")
internal val IrElement.fqName: String internal val IrElement.fqName: String
get() = when (this) { get() = (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: ""
is IrExpression -> (this as? IrSymbolOwner)?.symbol?.owner?.fqName ?: ""
else -> (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: ""
}
internal fun IrFunction.getDispatchReceiver(): IrValueParameterSymbol? = this.dispatchReceiverParameter?.symbol internal fun IrFunction.getDispatchReceiver(): IrValueParameterSymbol? = this.dispatchReceiverParameter?.symbol
@@ -36,16 +36,14 @@ internal class KFunctionState(
private var _returnType: KType? = null private var _returnType: KType? = null
private var _typeParameters: List<KTypeParameter>? = null private var _typeParameters: List<KTypeParameter>? = null
val invokeSymbol: IrFunctionSymbol = environment.createdFunctionsCache val invokeSymbol: IrFunctionSymbol = run {
.getOrDefault( val hasDispatchReceiver = irFunction.dispatchReceiverParameter?.let { getField(it.symbol) } != null
irFunction.symbol, val hasExtensionReceiver = irFunction.extensionReceiverParameter?.let { getField(it.symbol) } != null
createInvokeFunction( environment.getCachedFunction(irFunction.symbol, hasDispatchReceiver, hasExtensionReceiver) ?: environment.setCachedFunction(
irFunction, irFunction.symbol, hasDispatchReceiver, hasExtensionReceiver,
irClass, newFunction = createInvokeFunction(irFunction, irClass, hasDispatchReceiver, hasExtensionReceiver).symbol
irFunction.dispatchReceiverParameter?.let { getField(it.symbol) } != null,
irFunction.extensionReceiverParameter?.let { getField(it.symbol) } != null
).symbol
) )
}
companion object { companion object {
fun createInvokeFunction( fun createInvokeFunction(