Some enum-related fixes

(NB: testEnumClass3 doesn't work yet, effective modality required for enum class)
This commit is contained in:
Dmitry Petrov
2016-09-28 17:34:44 +03:00
parent de2bef910b
commit 608d6a37d2
12 changed files with 176 additions and 41 deletions
@@ -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)
}
}
@@ -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
@@ -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) {
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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");