From 608d6a37d2be3d4f5a578d695c2d05141fa88b3e Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 28 Sep 2016 17:34:44 +0300 Subject: [PATCH] Some enum-related fixes (NB: testEnumClass3 doesn't work yet, effective modality required for enum class) --- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 2 +- .../backend/jvm/codegen/ClassCodegen.kt | 7 ++- .../backend/jvm/lower/EnumClassLowering.kt | 61 +++++++++++++++++-- .../backend/jvm/lower/InitializersLowering.kt | 33 ++++------ .../kotlin/psi2ir/generators/BodyGenerator.kt | 11 ++-- .../kotlin/psi2ir/generators/CallGenerator.kt | 3 +- compiler/testData/ir/box/enumClass.kt | 17 +++++- compiler/testData/ir/box/enumClass2.kt | 21 +++++++ compiler/testData/ir/box/enumClass3.kt | 17 ++++++ .../irText/classes/enumWithSecondaryCtor.kt | 5 ++ .../irText/classes/enumWithSecondaryCtor.txt | 28 ++++++++- .../ir/IrOnlyBoxCodegenTestGenerated.java | 12 ++++ 12 files changed, 176 insertions(+), 41 deletions(-) create mode 100644 compiler/testData/ir/box/enumClass2.kt create mode 100644 compiler/testData/ir/box/enumClass3.kt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index a662a32cdbd..9e2c0c99871 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -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) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index 6c7c3a82f92..17ca612df96 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt index eb570d884b0..c6d7f88c565 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt @@ -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) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InitializersLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InitializersLowering.kt index ad7fad48486..af32df7f93b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InitializersLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InitializersLowering.kt @@ -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() val instanceInitializerStatements = ArrayList() @@ -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) { diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt index f37c9704a9b..400f48ea10c 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BodyGenerator.kt @@ -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) } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index 171a79d3daa..f05908113cb 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt @@ -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 diff --git a/compiler/testData/ir/box/enumClass.kt b/compiler/testData/ir/box/enumClass.kt index f6cff0fca61..9f6da5929bd 100644 --- a/compiler/testData/ir/box/enumClass.kt +++ b/compiler/testData/ir/box/enumClass.kt @@ -1,3 +1,16 @@ -enum class Test { OK } +fun assertEquals(actual: T, expected: T) { + if (actual != expected) { + throw java.lang.AssertionError("Assertion failed: $actual != $expected") + } +} -fun box() = Test.OK.toString() \ No newline at end of file +enum class Test { + OK +} + +fun box(): String { + assertEquals(Test.OK.ordinal, 0) + assertEquals(Test.OK.name, "OK") + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/ir/box/enumClass2.kt b/compiler/testData/ir/box/enumClass2.kt new file mode 100644 index 00000000000..14023472ae7 --- /dev/null +++ b/compiler/testData/ir/box/enumClass2.kt @@ -0,0 +1,21 @@ +fun 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" +} \ No newline at end of file diff --git a/compiler/testData/ir/box/enumClass3.kt b/compiler/testData/ir/box/enumClass3.kt new file mode 100644 index 00000000000..fb090f92d9a --- /dev/null +++ b/compiler/testData/ir/box/enumClass3.kt @@ -0,0 +1,17 @@ +fun 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" +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/classes/enumWithSecondaryCtor.kt b/compiler/testData/ir/irText/classes/enumWithSecondaryCtor.kt index 9cc49a2aa03..7ec4a50769c 100644 --- a/compiler/testData/ir/irText/classes/enumWithSecondaryCtor.kt +++ b/compiler/testData/ir/irText/classes/enumWithSecondaryCtor.kt @@ -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) diff --git a/compiler/testData/ir/irText/classes/enumWithSecondaryCtor.txt b/compiler/testData/ir/irText/classes/enumWithSecondaryCtor.txt index 3fea0492494..e25c342f719 100644 --- a/compiler/testData/ir/irText/classes/enumWithSecondaryCtor.txt +++ b/compiler/testData/ir/irText/classes/enumWithSecondaryCtor.txt @@ -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 (): kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): 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 + 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 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java index e8fabf8b5dd..f760a28893d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java @@ -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");