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
This commit is contained in:
Ivan Kylchik
2020-04-22 15:17:23 +03:00
parent a095309e10
commit 296f343cab
5 changed files with 56 additions and 27 deletions
@@ -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")
@@ -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<Variable> {
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<Variable> {
this ?: return listOf()
return this.fields.filterNot { it.descriptor.containingDeclaration == this.irClass.descriptor }
}
@@ -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<Variable>
fun getAllTypeArguments(): List<Variable> // 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<Variable> = mutableListOf()) : Frame {
class InterpreterFrame(
private val pool: MutableList<Variable> = mutableListOf(),
private val typeArguments: List<Variable> = listOf()
) : Frame {
private val returnStack: MutableList<State> = mutableListOf()
override fun addVar(variable: Variable) {
@@ -37,7 +42,8 @@ class InterpreterFrame(val pool: MutableList<Variable> = 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<Variable> = mutableListOf()) : Fram
return pool
}
override fun getAllTypeArguments(): List<Variable> {
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) {
@@ -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<Variable>)
fun getVariableState(variableDescriptor: DeclarationDescriptor): State
fun getAll(): List<Variable>
fun getAllTypeArguments(): List<Variable>
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<Variable>, 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<Variable> {
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<Variable>) = 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
@@ -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<Variable>, superClass: Complex?, subClass: Complex?
override val irClass: IrClass, override val fields: MutableList<Variable>, val typeArguments: MutableList<Variable>,
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 {