JVM IR: fix "null" as the first entry in MappedEnumWhenLowering
If "null" was the first entry in an optimizable "when" over enum, mapRuntimeEnumEntry was called before mapConstEnumEntry, and the $WhenMappings field was not created. Now both mapConstEnumEntry and mapRuntimeEnumEntry create this field on the first access
This commit is contained in:
+14
-13
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
|
|||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl
|
||||||
import org.jetbrains.kotlin.ir.types.getClass
|
import org.jetbrains.kotlin.ir.types.getClass
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
@@ -69,7 +68,7 @@ private class MappedEnumWhenLowering(context: CommonBackendContext) : EnumWhenLo
|
|||||||
// of the classes in which they are used. This field tracks which container is the innermost one.
|
// of the classes in which they are used. This field tracks which container is the innermost one.
|
||||||
private var state: EnumMappingState? = null
|
private var state: EnumMappingState? = null
|
||||||
|
|
||||||
private class EnumMappingState {
|
private inner class EnumMappingState {
|
||||||
val mappings = mutableMapOf<IrClass /* enum */, Pair<MutableMap<IrEnumEntry, Int>, IrField>>()
|
val mappings = mutableMapOf<IrClass /* enum */, Pair<MutableMap<IrEnumEntry, Int>, IrField>>()
|
||||||
val mappingsClass by lazy {
|
val mappingsClass by lazy {
|
||||||
buildClass {
|
buildClass {
|
||||||
@@ -79,26 +78,28 @@ private class MappedEnumWhenLowering(context: CommonBackendContext) : EnumWhenLo
|
|||||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getMappingForClass(enumClass: IrClass): Pair<MutableMap<IrEnumEntry, Int>, IrField> =
|
||||||
|
mappings.getOrPut(enumClass) {
|
||||||
|
mutableMapOf<IrEnumEntry, Int>() to mappingsClass.addField {
|
||||||
|
name = Name.identifier("\$EnumSwitchMapping\$${mappings.size}")
|
||||||
|
type = intArray.owner.defaultType
|
||||||
|
origin = JvmLoweredDeclarationOrigin.ENUM_MAPPINGS_FOR_WHEN
|
||||||
|
isFinal = true
|
||||||
|
isStatic = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mapConstEnumEntry(entry: IrEnumEntry): Int {
|
override fun mapConstEnumEntry(entry: IrEnumEntry): Int {
|
||||||
val (mapping, _) = state!!.mappings.getOrPut(entry.parentAsClass) {
|
val (mapping) = state!!.getMappingForClass(entry.parentAsClass)
|
||||||
mutableMapOf<IrEnumEntry, Int>() to state!!.mappingsClass.addField {
|
|
||||||
name = Name.identifier("\$EnumSwitchMapping\$${state!!.mappings.size}")
|
|
||||||
type = intArray.owner.defaultType
|
|
||||||
origin = JvmLoweredDeclarationOrigin.ENUM_MAPPINGS_FOR_WHEN
|
|
||||||
isFinal = true
|
|
||||||
isStatic = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Index 0 (default value for integers) is reserved for unknown ordinals.
|
// Index 0 (default value for integers) is reserved for unknown ordinals.
|
||||||
return mapping.getOrPut(entry) { mapping.size + 1 }
|
return mapping.getOrPut(entry) { mapping.size + 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun mapRuntimeEnumEntry(builder: IrBuilderWithScope, subject: IrExpression): IrExpression =
|
override fun mapRuntimeEnumEntry(builder: IrBuilderWithScope, subject: IrExpression): IrExpression =
|
||||||
builder.irCall(intArrayGet).apply {
|
builder.irCall(intArrayGet).apply {
|
||||||
val (_, field) = state!!.mappings[subject.type.getClass()!!]
|
val (_, field) = state!!.getMappingForClass(subject.type.getClass()!!)
|
||||||
?: throw AssertionError("no values mapped for enum class ${subject.type}")
|
|
||||||
dispatchReceiver = builder.irGetField(null, field)
|
dispatchReceiver = builder.irGetField(null, field)
|
||||||
putValueArgument(0, super.mapRuntimeEnumEntry(builder, subject))
|
putValueArgument(0, super.mapRuntimeEnumEntry(builder, subject))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
enum class E {
|
||||||
|
A, B;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(e: E?): String {
|
||||||
|
val c = when (e) {
|
||||||
|
null -> "Fail: null"
|
||||||
|
E.B -> "OK"
|
||||||
|
E.A -> "Fail: A"
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String = foo(E.B)
|
||||||
+5
@@ -26192,6 +26192,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullIsTheFirstEntry.kt")
|
||||||
|
public void testNullIsTheFirstEntry() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullIsTheFirstEntry.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nullability.kt")
|
@TestMetadata("nullability.kt")
|
||||||
public void testNullability() throws Exception {
|
public void testNullability() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
||||||
|
|||||||
+5
@@ -26192,6 +26192,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullIsTheFirstEntry.kt")
|
||||||
|
public void testNullIsTheFirstEntry() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullIsTheFirstEntry.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nullability.kt")
|
@TestMetadata("nullability.kt")
|
||||||
public void testNullability() throws Exception {
|
public void testNullability() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
||||||
|
|||||||
+5
@@ -25087,6 +25087,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullIsTheFirstEntry.kt")
|
||||||
|
public void testNullIsTheFirstEntry() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullIsTheFirstEntry.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nullability.kt")
|
@TestMetadata("nullability.kt")
|
||||||
public void testNullability() throws Exception {
|
public void testNullability() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
||||||
|
|||||||
Generated
+5
@@ -20172,6 +20172,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullIsTheFirstEntry.kt")
|
||||||
|
public void testNullIsTheFirstEntry() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullIsTheFirstEntry.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nullability.kt")
|
@TestMetadata("nullability.kt")
|
||||||
public void testNullability() throws Exception {
|
public void testNullability() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
||||||
|
|||||||
+5
@@ -21327,6 +21327,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nonConstantEnum.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullIsTheFirstEntry.kt")
|
||||||
|
public void testNullIsTheFirstEntry() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullIsTheFirstEntry.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nullability.kt")
|
@TestMetadata("nullability.kt")
|
||||||
public void testNullability() throws Exception {
|
public void testNullability() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
runTest("compiler/testData/codegen/box/when/enumOptimization/nullability.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user