From 296f343cab3e4c5b8023408717b2accd854d0937 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Wed, 22 Apr 2020 15:17:23 +0300 Subject: [PATCH] Change conditions for saving type arguments into stack For now all type arguments are saved. This is necessary for several reason: 1. ir type operator call need to get right type argument class 2. if class is local then information about type argument cannot be lost --- .../common/interpreter/IrInterpreter.kt | 34 ++++++++----------- .../backend/common/interpreter/Utils.kt | 15 ++++++++ .../backend/common/interpreter/stack/Frame.kt | 16 +++++++-- .../backend/common/interpreter/stack/Stack.kt | 11 +++++- .../common/interpreter/state/Common.kt | 7 ++-- 5 files changed, 56 insertions(+), 27 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt index 4aa8c862714..39806e5ae3b 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt @@ -309,19 +309,11 @@ class IrInterpreter(irModule: IrModuleFragment) { interpretValueParameters(expression, irFunction, valueArguments).check { return it } - // TODO fun saveReifiedParameters - irFunction.takeIf { it.isInline }?.typeParameters?.forEachIndexed { index, typeParameter -> - if (typeParameter.isReified) { - val typeArgumentState = Common(expression.getTypeArgument(index)?.classOrNull!!.owner) - valueArguments.add(Variable(typeParameter.descriptor, typeArgumentState)) - } - } + valueArguments.addAll(getTypeArguments(irFunction, expression) { stack.getVariableState(it) }) + if (dispatchReceiver is Common) valueArguments.addAll(dispatchReceiver.typeArguments) + if (extensionReceiver is Common) valueArguments.addAll(extensionReceiver.typeArguments) - // load data from declaration if it is local - if (dispatchReceiver != null && (irFunction.isLocal || dispatchReceiver.irClass.isLocal)) { - with(dispatchReceiver) { this.fields.filterNot { it.descriptor.containingDeclaration == this.irClass.descriptor } } - .apply { valueArguments.addAll(this) } - } + if (irFunction.isLocal) valueArguments.addAll(dispatchReceiver.extractNonLocalDeclarations()) return stack.newFrame(asSubFrame = irFunction.isInline || irFunction.isLocal, initPool = valueArguments) { val isWrapper = dispatchReceiver is Wrapper && rawExtensionReceiver == null @@ -376,9 +368,10 @@ class IrInterpreter(irModule: IrModuleFragment) { } val state = Common(parent) + state.typeArguments.addAll(getTypeArguments(parent, constructorCall) { stack.getVariableState(it) } + stack.getAllTypeArguments()) if (parent.isLocal) state.fields.addAll(stack.getAll()) // TODO save only necessary declarations valueArguments.add(Variable(constructorCall.getThisAsReceiver(), state)) //used to set up fields in body - return stack.newFrame(initPool = valueArguments) { + return stack.newFrame(initPool = valueArguments + state.typeArguments) { val statements = constructorCall.getBody()!!.statements // enum entry use IrTypeOperatorCall with IMPLICIT_COERCION_TO_UNIT as delegation call, but we need the value ((statements[0] as? IrTypeOperatorCall)?.argument ?: statements[0]).interpret().check { return@newFrame it } @@ -550,7 +543,7 @@ class IrInterpreter(irModule: IrModuleFragment) { val objectState = when { owner.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.getCompanionObject(owner) - else -> Common(owner).apply { setSuperClassRecursive() } + else -> Common(owner).apply { setSuperClassRecursive() } // TODO test type arguments } mapOfObjects[objectSignature] = objectState stack.pushReturnValue(objectState) @@ -600,29 +593,30 @@ class IrInterpreter(irModule: IrModuleFragment) { private suspend fun interpretTypeOperatorCall(expression: IrTypeOperatorCall): ExecutionResult { val executionResult = expression.argument.interpret().check { return it } + val typeOperandDescriptor = expression.typeOperand.classifierOrFail.descriptor + val typeOperandClass = expression.typeOperand.classOrNull?.owner ?: stack.getVariableState(typeOperandDescriptor).irClass when (expression.operator) { // coercion to unit means that return value isn't used IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> stack.popReturnValue() IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> { - if (!stack.peekReturnValue().irClass.defaultType.isSubtypeOf(expression.type, irBuiltIns)) { + if (!stack.peekReturnValue().irClass.isSubclassOf(typeOperandClass)) { val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable - val castClassName = expression.type.classOrNull?.owner?.fqNameWhenAvailable - throw ClassCastException("$convertibleClassName cannot be cast to $castClassName") + throw ClassCastException("$convertibleClassName cannot be cast to ${typeOperandClass.fqNameWhenAvailable}") } } IrTypeOperator.SAFE_CAST -> { - if (!stack.peekReturnValue().irClass.defaultType.isSubtypeOf(expression.type, irBuiltIns)) { + if (!stack.peekReturnValue().irClass.isSubclassOf(typeOperandClass)) { stack.popReturnValue() stack.pushReturnValue(null.toState(irBuiltIns.nothingType)) } } IrTypeOperator.INSTANCEOF -> { - val isInstance = stack.popReturnValue().irClass.defaultType.isSubtypeOf(expression.typeOperand, irBuiltIns) + val isInstance = stack.popReturnValue().irClass.isSubclassOf(typeOperandClass) stack.pushReturnValue(isInstance.toState(irBuiltIns.nothingType)) } IrTypeOperator.NOT_INSTANCEOF -> { - val isInstance = stack.popReturnValue().irClass.defaultType.isSubtypeOf(expression.typeOperand, irBuiltIns) + val isInstance = stack.popReturnValue().irClass.isSubclassOf(typeOperandClass) stack.pushReturnValue((!isInstance).toState(irBuiltIns.nothingType)) } else -> TODO("${expression.operator} not implemented") diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt index 7069ddf77c6..78597910444 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt @@ -74,6 +74,7 @@ private fun DeclarationDescriptor.isSubtypeOf(other: DeclarationDescriptor): Boo private fun DeclarationDescriptor.hasSameNameAs(other: DeclarationDescriptor): Boolean { return (this is VariableDescriptor && other is VariableDescriptor && this.name == other.name) || + (this is TypeParameterDescriptor && other is TypeParameterDescriptor && this.name == other.name) || (this is FunctionDescriptor && other is FunctionDescriptor && //this == other this.valueParameters.map { it.type.toString() } == other.valueParameters.map { it.type.toString() } && this.name == other.name) @@ -239,4 +240,18 @@ fun IrFunctionAccessExpression.getVarargType(index: Int): IrType? { val varargType = this.symbol.owner.valueParameters[index].varargElementType ?: return null val typeParameter = varargType.classifierOrFail.owner as IrTypeParameter return this.getTypeArgument(typeParameter.index) +} + +fun getTypeArguments( + container: IrTypeParametersContainer, expression: IrFunctionAccessExpression, mapper: (TypeParameterDescriptor) -> State +): List { + return container.typeParameters.mapIndexed { index, typeParameter -> + val argumentState = expression.getTypeArgument(index)?.classOrNull?.owner?.let { Common(it) } ?: mapper(typeParameter.descriptor) + Variable(typeParameter.descriptor, argumentState) + } +} + +fun State?.extractNonLocalDeclarations(): List { + this ?: return listOf() + return this.fields.filterNot { it.descriptor.containingDeclaration == this.irClass.descriptor } } \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Frame.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Frame.kt index e2827b3d563..db895ab097c 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Frame.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Frame.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.common.interpreter.stack import org.jetbrains.kotlin.backend.common.interpreter.equalTo import org.jetbrains.kotlin.backend.common.interpreter.state.State import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import kotlin.NoSuchElementException interface Frame { @@ -16,6 +17,7 @@ interface Frame { fun getVariableState(variableDescriptor: DeclarationDescriptor): State fun tryGetVariableState(variableDescriptor: DeclarationDescriptor): State? fun getAll(): List + fun getAllTypeArguments(): List // TODO try to get rid of this method; possibly by finding all type arguments in local class fun contains(descriptor: DeclarationDescriptor): Boolean fun pushReturnValue(state: State) fun pushReturnValue(frame: Frame) // TODO rename to getReturnValueFrom @@ -25,7 +27,10 @@ interface Frame { } // TODO replace exceptions with InterpreterException -class InterpreterFrame(val pool: MutableList = mutableListOf()) : Frame { +class InterpreterFrame( + private val pool: MutableList = mutableListOf(), + private val typeArguments: List = listOf() +) : Frame { private val returnStack: MutableList = mutableListOf() override fun addVar(variable: Variable) { @@ -37,7 +42,8 @@ class InterpreterFrame(val pool: MutableList = mutableListOf()) : Fram } override fun tryGetVariableState(variableDescriptor: DeclarationDescriptor): State? { - return pool.firstOrNull { it.descriptor.equalTo(variableDescriptor) }?.state + return (if (variableDescriptor is TypeParameterDescriptor) typeArguments else pool) + .firstOrNull { it.descriptor.equalTo(variableDescriptor) }?.state } override fun getVariableState(variableDescriptor: DeclarationDescriptor): State { @@ -49,8 +55,12 @@ class InterpreterFrame(val pool: MutableList = mutableListOf()) : Fram return pool } + override fun getAllTypeArguments(): List { + return typeArguments + } + override fun contains(descriptor: DeclarationDescriptor): Boolean { - return pool.any { it.descriptor == descriptor } + return (typeArguments + pool).any { it.descriptor == descriptor } } override fun pushReturnValue(state: State) { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Stack.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Stack.kt index 5545d0729d2..8b39ce5fe85 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Stack.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Stack.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.interpreter.ExecutionResult import org.jetbrains.kotlin.backend.common.interpreter.exceptions.InterpreterException import org.jetbrains.kotlin.backend.common.interpreter.state.State import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.name import org.jetbrains.kotlin.ir.util.file @@ -29,6 +30,7 @@ interface Stack { fun addAll(variables: List) fun getVariableState(variableDescriptor: DeclarationDescriptor): State fun getAll(): List + fun getAllTypeArguments(): List fun contains(descriptor: DeclarationDescriptor): Boolean fun hasReturnValue(): Boolean @@ -42,7 +44,9 @@ class StackImpl : Stack { private fun getCurrentFrame() = frameList.last() override suspend fun newFrame(asSubFrame: Boolean, initPool: List, block: suspend () -> ExecutionResult): ExecutionResult { - val newFrame = InterpreterFrame(initPool.toMutableList()) + val typeArgumentsPool = initPool.filter { it.descriptor is TypeParameterDescriptor } + val valueArguments = initPool.filter { it.descriptor !is TypeParameterDescriptor } + val newFrame = InterpreterFrame(valueArguments.toMutableList(), typeArgumentsPool) if (asSubFrame) getCurrentFrame().addSubFrame(newFrame) else frameList.add(FrameContainer(newFrame)) return try { @@ -90,6 +94,10 @@ class StackImpl : Stack { return getCurrentFrame().getAll() } + override fun getAllTypeArguments(): List { + return getCurrentFrame().getAllTypeArguments() + } + override fun contains(descriptor: DeclarationDescriptor): Boolean { return getCurrentFrame().contains(descriptor) } @@ -128,6 +136,7 @@ private class FrameContainer(current: Frame = InterpreterFrame()) { fun addVar(variable: Variable) = getTopFrame().addVar(variable) fun addAll(variables: List) = getTopFrame().addAll(variables) fun getAll() = innerStack.flatMap { it.getAll() } + fun getAllTypeArguments() = innerStack.flatMap { it.getAllTypeArguments() } fun getVariableState(variableDescriptor: DeclarationDescriptor): State { return innerStack.firstNotNullResult { it.tryGetVariableState(variableDescriptor) } ?: throw InterpreterException("$variableDescriptor not found") // TODO better message diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Common.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Common.kt index e845d8f5ee6..39d1995598d 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Common.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Common.kt @@ -14,10 +14,11 @@ import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.isInterface class Common private constructor( - override val irClass: IrClass, override val fields: MutableList, superClass: Complex?, subClass: Complex? + override val irClass: IrClass, override val fields: MutableList, val typeArguments: MutableList, + superClass: Complex?, subClass: Complex? ) : Complex(irClass, fields, superClass, subClass) { - constructor(irClass: IrClass) : this(irClass, mutableListOf(), null, null) + constructor(irClass: IrClass) : this(irClass, mutableListOf(), mutableListOf(), null, null) fun setSuperClassRecursive() { var thisClass: Common? = this @@ -48,7 +49,7 @@ class Common private constructor( } override fun copy(): State { - return Common(irClass, fields, superClass, subClass ?: this) + return Common(irClass, fields, typeArguments, superClass, subClass ?: this) } override fun toString(): String {