Fix enum interpretation in case when primary constructor is missing

This commit is contained in:
Ivan Kylchik
2021-06-23 16:58:29 +03:00
committed by TeamCityServer
parent 668bb4fd71
commit 96cc74a752
3 changed files with 32 additions and 8 deletions
@@ -415,15 +415,19 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
callInterceptor.interceptEnumEntry(enumEntry) {
val enumClass = enumEntry.symbol.owner.parentAsClass
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>()
val enumSuperCall = (enumClass.primaryConstructor?.body?.statements?.firstOrNull() as? IrEnumConstructorCall)
val enumInitializer = enumEntry.initializerExpression?.expression
?: throw InterpreterError("Initializer at enum entry ${enumEntry.fqNameWhenAvailable} is null")
val enumConstructorCall = enumInitializer as? IrEnumConstructorCall
?: (enumInitializer as IrBlock).statements.filterIsInstance<IrEnumConstructorCall>().single()
val enumSuperCall = enumConstructorCall.getSuperEnumCall()
val cleanEnumSuperCall = fun() {
enumSuperCall?.apply { (0 until this.valueArgumentsCount).forEach { putValueArgument(it, null) } } // restore to null
enumSuperCall.apply { (0 until this.valueArgumentsCount).forEach { putValueArgument(it, null) } } // restore to null
callStack.popState() // result of constructor must be dropped, because next instruction will be `IrGetEnumValue`
callStack.dropSubFrame()
}
if (enumEntries.isNotEmpty() && enumSuperCall != null) {
if (enumEntries.isNotEmpty()) {
val valueArguments = listOf(
enumEntry.name.asString().toIrConst(irBuiltIns.stringType),
enumEntries.indexOf(enumEntry).toIrConst(irBuiltIns.intType)
@@ -431,11 +435,6 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
valueArguments.forEachIndexed { index, irConst -> enumSuperCall.putValueArgument(index, irConst) }
}
val enumInitializer = enumEntry.initializerExpression?.expression
?: throw InterpreterError("Initializer at enum entry ${enumEntry.fqNameWhenAvailable} is null")
val enumConstructorCall = enumInitializer as? IrEnumConstructorCall
?: (enumInitializer as IrBlock).statements.filterIsInstance<IrEnumConstructorCall>().single()
val enumClassObject = Variable(enumConstructorCall.getThisReceiver(), Common(enumEntry.correspondingClass ?: enumClass))
environment.mapOfEnums[enumEntry.symbol] = enumClassObject.state as Complex
@@ -269,4 +269,14 @@ internal fun IrType.getTypeIfReified(getType: (IrClassifierSymbol) -> IrType): I
hasQuestionMark = this.hasQuestionMark || this@getTypeIfReified.hasQuestionMark
arguments = newArguments
}
}
internal fun IrFunctionAccessExpression.getSuperEnumCall(): IrEnumConstructorCall {
val name = this.symbol.owner.parentClassOrNull?.fqNameWhenAvailable?.asString()
if (this is IrEnumConstructorCall && name == "kotlin.Enum") return this
return when (val delegatingCall = this.symbol.owner.body?.statements?.get(0)) {
is IrFunctionAccessExpression -> delegatingCall.getSuperEnumCall()
is IrTypeOperatorCall -> (delegatingCall.argument as IrFunctionAccessExpression).getSuperEnumCall()
else -> TODO("$delegatingCall is unexpected")
}
}
+15
View File
@@ -24,3 +24,18 @@ const val b3 = Color.RED.<!EVALUATED: `0xFF0000`!>getColorAsString()<!>
const val c1 = Color.BLACK.<!EVALUATED: `0`!>getColorAsInt()<!>
const val c2 = Color.RED.<!EVALUATED: `16711680`!>getColorAsInt()<!>
@CompileTimeCalculation
enum class EnumWithoutPrimary {
X(), Y(10);
val someProp: Int
constructor() : this(0) {}
constructor(value: Int) {
someProp = value
}
}
const val d1 = EnumWithoutPrimary.X.<!EVALUATED: `0`!>someProp<!>
const val d2 = EnumWithoutPrimary.Y.<!EVALUATED: `10`!>someProp<!>