[backend] Allow argument reordering in enum entry initializers (#1794)
This commit is contained in:
+27
-12
@@ -19,20 +19,16 @@ package org.jetbrains.kotlin.backend.konan.lower
|
|||||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.addSimpleDelegatingConstructor
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENUM
|
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENUM
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||||
import org.jetbrains.kotlin.backend.common.ir.addSimpleDelegatingConstructor
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
|
||||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.constructedClass
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.constructedClass
|
||||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.typeWith
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.typeWith
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
@@ -40,6 +36,7 @@ import org.jetbrains.kotlin.ir.builders.*
|
|||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.*
|
import org.jetbrains.kotlin.ir.declarations.impl.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||||
@@ -333,7 +330,13 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
|||||||
enumEntries
|
enumEntries
|
||||||
.sortedBy { it.descriptor.name }
|
.sortedBy { it.descriptor.name }
|
||||||
.map {
|
.map {
|
||||||
val entryConstructorCall = it.initializerExpression!! as IrCall
|
val initializer = it.initializerExpression
|
||||||
|
val entryConstructorCall = when {
|
||||||
|
initializer is IrCall -> initializer
|
||||||
|
initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL ->
|
||||||
|
initializer.statements.last() as IrCall
|
||||||
|
else -> error("Unexpected initializer: $initializer")
|
||||||
|
}
|
||||||
val entryConstructor = entryConstructorCall.symbol.owner as IrConstructor
|
val entryConstructor = entryConstructorCall.symbol.owner as IrConstructor
|
||||||
val entryClass = entryConstructor.constructedClass
|
val entryClass = entryConstructor.constructedClass
|
||||||
|
|
||||||
@@ -383,6 +386,12 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
|||||||
val startOffset = irClass.startOffset
|
val startOffset = irClass.startOffset
|
||||||
val endOffset = irClass.endOffset
|
val endOffset = irClass.endOffset
|
||||||
|
|
||||||
|
fun IrBlockBodyBuilder.initInstanceCall(instance: IrCall, constructor: IrCall): IrCall =
|
||||||
|
irCall(initInstanceSymbol).apply {
|
||||||
|
putValueArgument(0, instance)
|
||||||
|
putValueArgument(1, constructor)
|
||||||
|
}
|
||||||
|
|
||||||
return IrAnonymousInitializerImpl(startOffset, endOffset, DECLARATION_ORIGIN_ENUM, loweredEnum.implObject.descriptor).apply {
|
return IrAnonymousInitializerImpl(startOffset, endOffset, DECLARATION_ORIGIN_ENUM, loweredEnum.implObject.descriptor).apply {
|
||||||
body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody(irClass) {
|
body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody(irClass) {
|
||||||
val instances = irTemporary(irGetField(irGet(loweredEnum.implObject.thisReceiver!!), loweredEnum.valuesField))
|
val instances = irTemporary(irGetField(irGet(loweredEnum.implObject.thisReceiver!!), loweredEnum.valuesField))
|
||||||
@@ -394,10 +403,16 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
|||||||
dispatchReceiver = irGet(instances)
|
dispatchReceiver = irGet(instances)
|
||||||
putValueArgument(0, irInt(it.index))
|
putValueArgument(0, irInt(it.index))
|
||||||
}
|
}
|
||||||
val initializer = it.value.initializerExpression!! as IrCall
|
val initializer = it.value.initializerExpression!!
|
||||||
+irCall(initInstanceSymbol).apply {
|
when {
|
||||||
putValueArgument(0, instance)
|
initializer is IrCall -> +initInstanceCall(instance, initializer)
|
||||||
putValueArgument(1, initializer)
|
initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL -> {
|
||||||
|
val statements = initializer.statements
|
||||||
|
val constructorCall = statements.last() as IrCall
|
||||||
|
statements[statements.lastIndex] = initInstanceCall(instance, constructorCall)
|
||||||
|
+initializer
|
||||||
|
}
|
||||||
|
else -> error("Unexpected initializer: $initializer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+irCall(this@EnumClassLowering.context.ir.symbols.freeze, listOf(arrayType)).apply {
|
+irCall(this@EnumClassLowering.context.ir.symbols.freeze, listOf(arrayType)).apply {
|
||||||
|
|||||||
@@ -807,6 +807,10 @@ task enum_loop(type: RunKonanTest) {
|
|||||||
source = "codegen/enum/loop.kt"
|
source = "codegen/enum/loop.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task enum_reorderedArguments(type: RunKonanTest) {
|
||||||
|
source = "codegen/enum/reorderedArguments.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task switchLowering(type: RunKonanTest) {
|
task switchLowering(type: RunKonanTest) {
|
||||||
goldValue = "EnumA.A\nok\nok\nok\nok\nok\n"
|
goldValue = "EnumA.A\nok\nok\nok\nok\nok\n"
|
||||||
source = "codegen/enum/switchLowering.kt"
|
source = "codegen/enum/switchLowering.kt"
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package codegen.enum.reorderedArguments
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
// Regression test for https://github.com/JetBrains/kotlin-native/issues/1779
|
||||||
|
enum class Foo(val a: Int, val b: Int, val c: Int = 0) {
|
||||||
|
A(a = 1, b = 0),
|
||||||
|
B(b = 1, a = 0),
|
||||||
|
C(c = 1, b = 0, a = 0),
|
||||||
|
D(0, 0),
|
||||||
|
E(1, 1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Base<T> {
|
||||||
|
val value: T
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Bar(override val value: Foo) : Base<Foo> {
|
||||||
|
A(Foo.A),
|
||||||
|
B(Foo.B),
|
||||||
|
C(Foo.C),
|
||||||
|
D(Foo.D),
|
||||||
|
E(Foo.E)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun runTest() {
|
||||||
|
|
||||||
|
assertEquals(Foo.A.a, 1)
|
||||||
|
assertEquals(Foo.A.b, 0)
|
||||||
|
assertEquals(Foo.A.c, 0)
|
||||||
|
|
||||||
|
assertEquals(Foo.B.a, 0)
|
||||||
|
assertEquals(Foo.B.b, 1)
|
||||||
|
assertEquals(Foo.B.c, 0)
|
||||||
|
|
||||||
|
assertEquals(Foo.C.a, 0)
|
||||||
|
assertEquals(Foo.C.b, 0)
|
||||||
|
assertEquals(Foo.C.c, 1)
|
||||||
|
|
||||||
|
assertEquals(Foo.D.a, 0)
|
||||||
|
assertEquals(Foo.D.b, 0)
|
||||||
|
assertEquals(Foo.D.c, 0)
|
||||||
|
|
||||||
|
assertEquals(Foo.E.a, 1)
|
||||||
|
assertEquals(Foo.E.b, 1)
|
||||||
|
assertEquals(Foo.E.c, 1)
|
||||||
|
|
||||||
|
assertEquals(Bar.A.value.a, 1)
|
||||||
|
assertEquals(Bar.A.value.b, 0)
|
||||||
|
assertEquals(Bar.A.value.c, 0)
|
||||||
|
|
||||||
|
assertEquals(Bar.B.value.a, 0)
|
||||||
|
assertEquals(Bar.B.value.b, 1)
|
||||||
|
assertEquals(Bar.B.value.c, 0)
|
||||||
|
|
||||||
|
assertEquals(Bar.C.value.a, 0)
|
||||||
|
assertEquals(Bar.C.value.b, 0)
|
||||||
|
assertEquals(Bar.C.value.c, 1)
|
||||||
|
|
||||||
|
assertEquals(Bar.D.value.a, 0)
|
||||||
|
assertEquals(Bar.D.value.b, 0)
|
||||||
|
assertEquals(Bar.D.value.c, 0)
|
||||||
|
|
||||||
|
assertEquals(Bar.E.value.a, 1)
|
||||||
|
assertEquals(Bar.E.value.b, 1)
|
||||||
|
assertEquals(Bar.E.value.c, 1)
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user