Some enum-related fixes
(NB: testEnumClass3 doesn't work yet, effective modality required for enum class)
This commit is contained in:
@@ -34,7 +34,7 @@ class JvmLower(val context: JvmBackendContext) {
|
||||
InterfaceDelegationLowering(context.state).runOnFile(irFile)
|
||||
EnumClassLowering(context).runOnFile(irFile)
|
||||
ObjectClassLowering(context).runOnFile(irFile)
|
||||
InitializersLowering().runOnFile(irFile)
|
||||
InitializersLowering(context).runOnFile(irFile)
|
||||
SingletonReferencesLowering(context).runOnFile(irFile)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -154,7 +154,7 @@ private fun MemberDescriptor.calcModalityFlag(): Int {
|
||||
flags = flags.or(Opcodes.ACC_ABSTRACT)
|
||||
}
|
||||
Modality.FINAL -> {
|
||||
if (this !is ConstructorDescriptor) {
|
||||
if (this !is ConstructorDescriptor && !DescriptorUtils.isEnumClass(this)) {
|
||||
flags = flags.or(Opcodes.ACC_FINAL)
|
||||
}
|
||||
}
|
||||
@@ -172,6 +172,11 @@ private fun MemberDescriptor.calcModalityFlag(): Int {
|
||||
return flags
|
||||
}
|
||||
|
||||
//val MemberDescriptor.effectiveModality: Modality
|
||||
// get() {
|
||||
//
|
||||
// }
|
||||
|
||||
val DeclarationDescriptorWithSource.psiElement: PsiElement?
|
||||
get() = (source as? PsiSourceElement)?.psi
|
||||
|
||||
|
||||
+55
-6
@@ -56,6 +56,7 @@ class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||
|
||||
private interface EnumConstructorCallTransformer {
|
||||
fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression
|
||||
fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression
|
||||
}
|
||||
|
||||
private val unsubstitutedArrayOfFun = context.builtIns.findSingleFunction(Name.identifier("arrayOf"))
|
||||
@@ -286,6 +287,28 @@ class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression {
|
||||
val descriptor = delegatingConstructorCall.descriptor
|
||||
val startOffset = delegatingConstructorCall.startOffset
|
||||
val endOffset = delegatingConstructorCall.endOffset
|
||||
|
||||
val loweredDelegatedConstructor = loweredEnumConstructors.getOrElse(descriptor) {
|
||||
throw AssertionError("Constructor called in enum entry initializer should've been lowered: $descriptor")
|
||||
}
|
||||
|
||||
val result = IrDelegatingConstructorCallImpl(startOffset, endOffset, loweredDelegatedConstructor)
|
||||
|
||||
result.putValueArgument(0, IrGetVariableImpl(startOffset, endOffset, enumClassConstructor.valueParameters[0]))
|
||||
result.putValueArgument(1, IrGetVariableImpl(startOffset, endOffset, enumClassConstructor.valueParameters[1]))
|
||||
|
||||
descriptor.valueParameters.forEach { valueParameter ->
|
||||
val i = valueParameter.index
|
||||
result.putValueArgument(i + 2, delegatingConstructorCall.getValueArgument(i))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private abstract inner class InEnumEntry(private val enumEntry: ClassDescriptor) : EnumConstructorCallTransformer {
|
||||
@@ -314,6 +337,10 @@ class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||
return result
|
||||
}
|
||||
|
||||
override fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression {
|
||||
throw AssertionError("Unexpected delegating constructor call within enum entry: $enumEntry")
|
||||
}
|
||||
|
||||
abstract fun createConstructorCall(startOffset: Int, endOffset: Int, loweredConstructor: ClassConstructorDescriptor): IrMemberAccessExpression
|
||||
}
|
||||
|
||||
@@ -331,8 +358,11 @@ class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||
private var enumConstructorCallTransformer: EnumConstructorCallTransformer? = null
|
||||
|
||||
override fun visitField(declaration: IrField): IrStatement {
|
||||
val enumEntry = enumEntriesByField[declaration.descriptor] ?:
|
||||
return declaration
|
||||
val enumEntry = enumEntriesByField[declaration.descriptor]
|
||||
if (enumEntry == null) {
|
||||
declaration.transformChildrenVoid(this)
|
||||
return declaration
|
||||
}
|
||||
|
||||
assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}"}
|
||||
|
||||
@@ -378,10 +408,29 @@ class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||
return callTransformer.transform(expression)
|
||||
}
|
||||
|
||||
override fun visitGetVariable(expression: IrGetVariable): IrExpression =
|
||||
loweredEnumConstructorParameters[expression.descriptor]?.let { loweredParameter ->
|
||||
IrGetVariableImpl(expression.startOffset, expression.endOffset, loweredParameter, expression.origin)
|
||||
} ?: expression
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
if (expression.descriptor.containingDeclaration.kind == ClassKind.ENUM_CLASS) {
|
||||
val callTransformer = enumConstructorCallTransformer ?:
|
||||
throw AssertionError("Enum constructor call outside of enum entry initialization or enum class constructor:\n" +
|
||||
irClass.dump())
|
||||
|
||||
return callTransformer.transform(expression)
|
||||
}
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitGetVariable(expression: IrGetVariable): IrExpression {
|
||||
val loweredParameter = loweredEnumConstructorParameters[expression.descriptor]
|
||||
if (loweredParameter != null) {
|
||||
return IrGetVariableImpl(expression.startOffset, expression.endOffset, loweredParameter, expression.origin)
|
||||
}
|
||||
else {
|
||||
return expression
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSyntheticBody(body: IrSyntheticBody): IrBody {
|
||||
return when (body.kind) {
|
||||
|
||||
+12
-21
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.getMemberOwnerKind
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
@@ -35,15 +36,18 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrThisReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.util.DeepCopyIrTree
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import java.util.*
|
||||
|
||||
|
||||
class InitializersLowering : ClassLoweringPass {
|
||||
class InitializersLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||
override fun lower(irClass: IrClass) {
|
||||
val classInitializersBuilder = ClassInitializersBuilder(irClass)
|
||||
irClass.acceptChildrenVoid(classInitializersBuilder)
|
||||
@@ -53,9 +57,7 @@ class InitializersLowering : ClassLoweringPass {
|
||||
classInitializersBuilder.createStaticInitializationMethod(irClass)
|
||||
}
|
||||
|
||||
private class ClassInitializersBuilder(val irClass: IrClass) : IrElementVisitorVoid {
|
||||
val classMemberOwnerKind = irClass.descriptor.getMemberOwnerKind()
|
||||
|
||||
private inner class ClassInitializersBuilder(val irClass: IrClass) : IrElementVisitorVoid {
|
||||
val staticInitializerStatements = ArrayList<IrStatement>()
|
||||
|
||||
val instanceInitializerStatements = ArrayList<IrStatement>()
|
||||
@@ -65,7 +67,7 @@ class InitializersLowering : ClassLoweringPass {
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField) {
|
||||
val irFieldInitializer = declaration.initializer?.let { it.expression } ?: return
|
||||
val irFieldInitializer = declaration.initializer?.expression ?: return
|
||||
|
||||
val receiver =
|
||||
if (declaration.descriptor.dispatchReceiverParameter != null) // TODO isStaticField
|
||||
@@ -93,23 +95,12 @@ class InitializersLowering : ClassLoweringPass {
|
||||
}
|
||||
|
||||
fun transformInstanceInitializerCallsInConstructors(irClass: IrClass) {
|
||||
for (irDeclaration in irClass.declarations) {
|
||||
if (irDeclaration !is IrConstructor) continue
|
||||
val irBody = irDeclaration.body as IrBlockBody
|
||||
if (irBody.statements.any { it is IrInstanceInitializerCall }) {
|
||||
val newStatements = irBody.statements.map { irStatement ->
|
||||
if (irStatement is IrInstanceInitializerCall) {
|
||||
IrBlockImpl(irClass.startOffset, irClass.endOffset, irDeclaration.descriptor.builtIns.unitType, null,
|
||||
instanceInitializerStatements.map { it.copy() })
|
||||
}
|
||||
else {
|
||||
irStatement
|
||||
}
|
||||
}
|
||||
irBody.statements.clear()
|
||||
irBody.statements.addAll(newStatements)
|
||||
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall): IrExpression {
|
||||
return IrBlockImpl(irClass.startOffset, irClass.endOffset, context.builtIns.unitType, null,
|
||||
instanceInitializerStatements.map { it.copy() })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun createStaticInitializationMethod(irClass: IrClass) {
|
||||
|
||||
@@ -235,7 +235,7 @@ class BodyGenerator(val scopeOwner: DeclarationDescriptor, override val context:
|
||||
}
|
||||
|
||||
private fun generateEnumEntrySuperConstructorCall(ktEnumEntry: KtEnumEntry, enumEntryDescriptor: ClassDescriptor): IrExpression {
|
||||
return generateEnumConstructorCallOrSuperCall(ktEnumEntry, enumEntryDescriptor.containingDeclaration as ClassDescriptor, null)
|
||||
return generateEnumConstructorCallOrSuperCall(ktEnumEntry, enumEntryDescriptor.containingDeclaration as ClassDescriptor)
|
||||
}
|
||||
|
||||
fun generateAnonymousInitializerBody(ktAnonymousInitializer: KtAnonymousInitializer): IrBlockBody {
|
||||
@@ -256,13 +256,12 @@ class BodyGenerator(val scopeOwner: DeclarationDescriptor, override val context:
|
||||
return IrEnumConstructorCallImpl(ktEnumEntry.startOffset, ktEnumEntry.endOffset, enumEntryConstructor)
|
||||
}
|
||||
|
||||
return generateEnumConstructorCallOrSuperCall(ktEnumEntry, enumEntryDescriptor.containingDeclaration as ClassDescriptor, enumEntryDescriptor)
|
||||
return generateEnumConstructorCallOrSuperCall(ktEnumEntry, enumEntryDescriptor.containingDeclaration as ClassDescriptor)
|
||||
}
|
||||
|
||||
private fun generateEnumConstructorCallOrSuperCall(
|
||||
ktEnumEntry: KtEnumEntry,
|
||||
enumClassDescriptor: ClassDescriptor,
|
||||
enumEntryOrNull: ClassDescriptor?
|
||||
enumClassDescriptor: ClassDescriptor
|
||||
): IrExpression {
|
||||
val statementGenerator = createStatementGenerator()
|
||||
|
||||
@@ -271,12 +270,12 @@ class BodyGenerator(val scopeOwner: DeclarationDescriptor, override val context:
|
||||
val enumConstructorCall = statementGenerator.pregenerateCall(getResolvedCall(ktSuperCallElement)!!)
|
||||
return CallGenerator(statementGenerator).generateEnumConstructorSuperCall(
|
||||
ktEnumEntry.startOffset, ktEnumEntry.endOffset,
|
||||
enumConstructorCall, enumEntryOrNull)
|
||||
enumConstructorCall)
|
||||
|
||||
}
|
||||
|
||||
// No-argument enum entry constructor
|
||||
val enumClassConstructor = enumClassDescriptor.unsubstitutedPrimaryConstructor!!
|
||||
val enumClassConstructor = enumClassDescriptor.constructors.find { it.valueParameters.isEmpty() }!!
|
||||
return IrEnumConstructorCallImpl(ktEnumEntry.startOffset, ktEnumEntry.endOffset, enumClassConstructor)
|
||||
}
|
||||
|
||||
|
||||
@@ -68,8 +68,7 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE
|
||||
}
|
||||
}
|
||||
|
||||
fun generateEnumConstructorSuperCall(startOffset: Int, endOffset: Int, call: CallBuilder,
|
||||
enumEntryDescriptor: ClassDescriptor?) : IrExpression {
|
||||
fun generateEnumConstructorSuperCall(startOffset: Int, endOffset: Int, call: CallBuilder) : IrExpression {
|
||||
val constructorDescriptor = call.descriptor
|
||||
if (constructorDescriptor !is ClassConstructorDescriptor) throw AssertionError("Constructor expected: $constructorDescriptor")
|
||||
val classDescriptor = constructorDescriptor.containingDeclaration
|
||||
|
||||
+15
-2
@@ -1,3 +1,16 @@
|
||||
enum class Test { OK }
|
||||
fun <T> assertEquals(actual: T, expected: T) {
|
||||
if (actual != expected) {
|
||||
throw java.lang.AssertionError("Assertion failed: $actual != $expected")
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = Test.OK.toString()
|
||||
enum class Test {
|
||||
OK
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(Test.OK.ordinal, 0)
|
||||
assertEquals(Test.OK.name, "OK")
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
fun <T> assertEquals(actual: T, expected: T) {
|
||||
if (actual != expected) {
|
||||
throw java.lang.AssertionError("Assertion failed: $actual != $expected")
|
||||
}
|
||||
}
|
||||
|
||||
enum class TestEnumClass(val x: Int) {
|
||||
ZERO
|
||||
// {
|
||||
// init {
|
||||
// }
|
||||
// }
|
||||
;
|
||||
constructor(): this(0)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(TestEnumClass.ZERO.x, 0)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
fun <T> assertEquals(actual: T, expected: T) {
|
||||
if (actual != expected) {
|
||||
throw java.lang.AssertionError("Assertion failed: $actual != $expected")
|
||||
}
|
||||
}
|
||||
|
||||
enum class TestEnumClass {
|
||||
ZERO {
|
||||
override fun describe() = "nothing"
|
||||
};
|
||||
abstract fun describe(): String
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(TestEnumClass.ZERO.describe(), "nothing")
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
enum class Test0(val x: Int) {
|
||||
ZERO;
|
||||
constructor() : this(0)
|
||||
}
|
||||
|
||||
enum class Test1(val x: Int) {
|
||||
ZERO, ONE(1);
|
||||
constructor() : this(0)
|
||||
|
||||
@@ -1,4 +1,28 @@
|
||||
FILE /enumWithSecondaryCtor.kt
|
||||
CLASS ENUM_CLASS Test0
|
||||
CONSTRUCTOR private constructor Test0(x: kotlin.Int)
|
||||
BLOCK_BODY
|
||||
ENUM_CONSTRUCTOR_CALL 'constructor Enum(String, Int)'
|
||||
INSTANCE_INITIALIZER_CALL classDescriptor='Test0'
|
||||
PROPERTY public final val x: kotlin.Int
|
||||
FIELD PROPERTY_BACKING_FIELD public final val x: kotlin.Int
|
||||
EXPRESSION_BODY
|
||||
GET_VAR 'value-parameter x: Int' type=kotlin.Int origin=INITIALIZE_PROPERTY_FROM_PARAMETER
|
||||
FUN DEFAULT_PROPERTY_ACCESSOR public final fun <get-x>(): kotlin.Int
|
||||
BLOCK_BODY
|
||||
RETURN type=kotlin.Nothing from='<get-x>(): Int'
|
||||
GET_FIELD 'x: Int' type=kotlin.Int origin=null
|
||||
receiver: THIS of 'Test0' type=Test0
|
||||
ENUM_ENTRY enum entry ZERO
|
||||
init: ENUM_CONSTRUCTOR_CALL 'constructor Test0()'
|
||||
CONSTRUCTOR private constructor Test0()
|
||||
BLOCK_BODY
|
||||
DELEGATING_CONSTRUCTOR_CALL 'constructor Test0(Int)'
|
||||
x: CONST Int type=kotlin.Int value='0'
|
||||
FUN ENUM_CLASS_SPECIAL_MEMBER public final fun values(): kotlin.Array<Test0>
|
||||
SYNTHETIC_BODY kind=ENUM_VALUES
|
||||
FUN ENUM_CLASS_SPECIAL_MEMBER public final fun valueOf(value: kotlin.String): Test0
|
||||
SYNTHETIC_BODY kind=ENUM_VALUEOF
|
||||
CLASS ENUM_CLASS Test1
|
||||
CONSTRUCTOR private constructor Test1(x: kotlin.Int)
|
||||
BLOCK_BODY
|
||||
@@ -14,7 +38,7 @@ FILE /enumWithSecondaryCtor.kt
|
||||
GET_FIELD 'x: Int' type=kotlin.Int origin=null
|
||||
receiver: THIS of 'Test1' type=Test1
|
||||
ENUM_ENTRY enum entry ZERO
|
||||
init: ENUM_CONSTRUCTOR_CALL 'constructor Test1(Int)'
|
||||
init: ENUM_CONSTRUCTOR_CALL 'constructor Test1()'
|
||||
ENUM_ENTRY enum entry ONE
|
||||
init: ENUM_CONSTRUCTOR_CALL 'constructor Test1(Int)'
|
||||
x: CONST Int type=kotlin.Int value='1'
|
||||
@@ -45,7 +69,7 @@ FILE /enumWithSecondaryCtor.kt
|
||||
class: CLASS ENUM_ENTRY ZERO
|
||||
CONSTRUCTOR private constructor ZERO()
|
||||
BLOCK_BODY
|
||||
ENUM_CONSTRUCTOR_CALL 'constructor Test2(Int)'
|
||||
ENUM_CONSTRUCTOR_CALL 'constructor Test2()'
|
||||
INSTANCE_INITIALIZER_CALL classDescriptor='ZERO'
|
||||
FUN public open override fun foo(): kotlin.Unit
|
||||
BLOCK_BODY
|
||||
|
||||
@@ -47,6 +47,18 @@ public class IrOnlyBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTest
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumClass2.kt")
|
||||
public void testEnumClass2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/box/enumClass2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumClass3.kt")
|
||||
public void testEnumClass3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/box/enumClass3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fileClassInitializers.kt")
|
||||
public void testFileClassInitializers() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/box/fileClassInitializers.kt");
|
||||
|
||||
Reference in New Issue
Block a user