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 hasDefaults = (0 until expression.valueArgumentsCount).any { expression.getValueArgument(it) == null }
if (hasDefaults) {
val ownerWithDefaults = expression.getFunctionThatContainsDefaults()
environment.createdFunctionsCache[ownerWithDefaults.symbol]?.let {
val callToDefault = it.owner.createCall().apply { expression.copyArgsInto(this) }
environment.getCachedFunction(expression.symbol, fromDelegatingCall = expression is IrDelegatingConstructorCall)?.let {
val callToDefault = it.owner.createCall().apply { environment.irBuiltIns.copyArgs(expression, this) }
callStack.addInstruction(CompoundInstruction(callToDefault))
return
}
// 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 ownerWithDefaults = expression.getFunctionThatContainsDefaults()
val visibility = when (expression) {
is IrEnumConstructorCall, is IrDelegatingConstructorCall -> DescriptorVisibilities.LOCAL
else -> ownerWithDefaults.visibility
@@ -138,14 +138,15 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro
val originalParameter = ownerWithDefaults.valueParameters[index]
val copiedParameter = originalParameter.deepCopyWithSymbols(this)
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
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)
?: 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@{
this@variable.initializer = environment.irBuiltIns.irIfNullThenElse(irGetParameter, defaultInitializer!!, irGetParameter)
this@variable.initializer = environment.irBuiltIns.irIfNullThenElse(irGetParameter, defaultInitializer, irGetParameter)
}
} else {
copiedParameter
@@ -159,8 +160,9 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro
(0 until expression.valueArgumentsCount).forEach { callWithAllArgs.putValueArgument(it, actualParameters[it]?.createGetValue()) }
defaultFun.body = (actualParameters.filterIsInstance<IrVariable>() + defaultFun.createReturn(callWithAllArgs)).wrapWithBlockBody()
environment.createdFunctionsCache[ownerWithDefaults.symbol] = defaultFun.symbol
val callToDefault = defaultFun.createCall().apply { expression.copyArgsInto(this) }
val callToDefault = environment.setCachedFunction(
expression.symbol, fromDelegatingCall = expression is IrDelegatingConstructorCall, newFunction = defaultFun.symbol
).owner.createCall().apply { environment.irBuiltIns.copyArgs(expression, this) }
callStack.addInstruction(CompoundInstruction(callToDefault))
} else {
val irFunction = expression.symbol.owner
@@ -142,12 +142,7 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
val irFunction = valueParameter.parent as IrFunction
fun isReceiver() = irFunction.dispatchReceiverParameter == valueParameter || irFunction.extensionReceiverParameter == valueParameter
val result = callStack.popState()
val state = when {
// if vararg is empty
result.isNull() && valueParameter.isVararg -> listOf<Any?>().toPrimitiveStateArray((result as Primitive<*>).type)
else -> result
}
val state = callStack.popState()
state.checkNullability(valueParameter.type, environment) {
if (isReceiver()) return@checkNullability NullPointerException()
@@ -23,7 +23,20 @@ class IrInterpreterEnvironment(
internal val irExceptions = mutableListOf<IrClass>()
internal var mapOfEnums = 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 {
mapOfObjects[irBuiltIns.unitClass] = Common(irBuiltIns.unitClass.owner)
@@ -47,4 +60,24 @@ class IrInterpreterEnvironment(
fun copyWithNewCallStack(): IrInterpreterEnvironment {
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.declarations.*
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.expressions.*
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.types.*
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.render
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)
}
internal fun IrConstructor.createConstructorCall(): IrConstructorCall {
return IrConstructorCallImpl.fromSymbolOwner(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, returnType, symbol)
internal fun IrConstructor.createConstructorCall(irType: IrType = returnType): IrConstructorCall {
return IrConstructorCallImpl.fromSymbolOwner(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, irType, symbol)
}
internal fun IrValueDeclaration.createGetValue(): IrGetValue {
@@ -165,14 +165,13 @@ internal fun IrFunctionAccessExpression.shallowCopy(copyTypeArguments: Boolean =
}
}
internal fun IrFunctionAccessExpression.copyArgsInto(newCall: IrFunctionAccessExpression) {
val symbol = this.symbol.owner
newCall.dispatchReceiver = this.dispatchReceiver
newCall.extensionReceiver = this.extensionReceiver
(0 until this.valueArgumentsCount)
.map { this.getValueArgument(it) }
internal fun IrBuiltIns.copyArgs(from: IrFunctionAccessExpression, into: IrFunctionAccessExpression) {
into.dispatchReceiver = from.dispatchReceiver
into.extensionReceiver = from.extensionReceiver
(0 until from.valueArgumentsCount)
.map { from.getValueArgument(it) }
.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 {
val nullCondition = this.irEquals(nullableArg, IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, this.anyNType))
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) }
}
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")
internal val IrElement.fqName: String
get() = when (this) {
is IrExpression -> (this as? IrSymbolOwner)?.symbol?.owner?.fqName ?: ""
else -> (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: ""
}
get() = (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: ""
internal fun IrFunction.getDispatchReceiver(): IrValueParameterSymbol? = this.dispatchReceiverParameter?.symbol
@@ -36,16 +36,14 @@ internal class KFunctionState(
private var _returnType: KType? = null
private var _typeParameters: List<KTypeParameter>? = null
val invokeSymbol: IrFunctionSymbol = environment.createdFunctionsCache
.getOrDefault(
irFunction.symbol,
createInvokeFunction(
irFunction,
irClass,
irFunction.dispatchReceiverParameter?.let { getField(it.symbol) } != null,
irFunction.extensionReceiverParameter?.let { getField(it.symbol) } != null
).symbol
val invokeSymbol: IrFunctionSymbol = run {
val hasDispatchReceiver = irFunction.dispatchReceiverParameter?.let { getField(it.symbol) } != null
val hasExtensionReceiver = irFunction.extensionReceiverParameter?.let { getField(it.symbol) } != null
environment.getCachedFunction(irFunction.symbol, hasDispatchReceiver, hasExtensionReceiver) ?: environment.setCachedFunction(
irFunction.symbol, hasDispatchReceiver, hasExtensionReceiver,
newFunction = createInvokeFunction(irFunction, irClass, hasDispatchReceiver, hasExtensionReceiver).symbol
)
}
companion object {
fun createInvokeFunction(