From c6a4ec2ff44b025b5227f936756efe3b5389974b Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Wed, 25 Jan 2017 12:39:36 +0300 Subject: [PATCH] Add enumeration support. (#128) * Add enumeration support. * Enum classes lowering. * merge fix * merge fix * used arrayOf from irModule * changed lowering quant from body to file * refactoring * review fixes * lowering for GET_OBJECT in enums * enabled tests * added tests * moved tests to separate dir * renamed test files * moved phase up * review fixes * reformat --- .../jetbrains/kotlin/backend/konan/Context.kt | 1 + .../kotlin/backend/konan/KonanLower.kt | 3 + .../kotlin/backend/konan/KonanPhases.kt | 1 + .../konan/descriptors/DescriptorUtils.kt | 3 +- .../kotlin/backend/konan/descriptors/utils.kt | 2 + .../backend/konan/llvm/CodeGenerator.kt | 7 +- .../kotlin/backend/konan/llvm/NameUtils.kt | 2 + .../backend/konan/lower/EnumClassLowering.kt | 605 ++++++++++++++++++ backend.native/tests/build.gradle | 35 + .../tests/codegen/enum/companionObject.kt | 24 + backend.native/tests/codegen/enum/test0.kt | 9 + backend.native/tests/codegen/enum/test1.kt | 8 + .../tests/codegen/enum/vCallNoEntryClass.kt | 13 + .../tests/codegen/enum/vCallWithEntryClass.kt | 15 + backend.native/tests/codegen/enum/valueOf.kt | 8 + backend.native/tests/codegen/enum/values.kt | 8 + .../codegen/blackbox/enum/emptyConstructor.kt | 2 +- .../codegen/blackbox/enum/kt9711_2.kt | 2 +- .../kotlin/konan/internal/RuntimeUtils.kt | 15 +- runtime/src/main/kotlin/kotlin/Enum.kt | 37 +- 20 files changed, 774 insertions(+), 26 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt create mode 100644 backend.native/tests/codegen/enum/companionObject.kt create mode 100644 backend.native/tests/codegen/enum/test0.kt create mode 100644 backend.native/tests/codegen/enum/test1.kt create mode 100644 backend.native/tests/codegen/enum/vCallNoEntryClass.kt create mode 100644 backend.native/tests/codegen/enum/vCallWithEntryClass.kt create mode 100644 backend.native/tests/codegen/enum/valueOf.kt create mode 100644 backend.native/tests/codegen/enum/values.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index a6531e24a51..ce3152e07b7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -23,6 +23,7 @@ internal final class Context(val config: KonanConfig) : KonanBackendContext() { var moduleDescriptor: ModuleDescriptor? = null + // TODO: make lateinit? var irModule: IrModuleFragment? = null set(module: IrModuleFragment?) { if (field != null) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index cb9a3b4660e..c7b3ed3aca1 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -17,6 +17,9 @@ internal class KonanLower(val context: Context) { fun lower(irFile: IrFile) { val phaser = PhaseManager(context) + phaser.phase(KonanPhase.LOWER_ENUMS) { + EnumClassLowering(context).run(irFile) + } phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) { DefaultParameterStubGenerator(context).runOnFilePostfix(irFile) DefaultParameterInjector(context).runOnFilePostfix(irFile) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index 2443f321cb6..dd6c3d07253 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -18,6 +18,7 @@ enum class KonanPhase(val description: String, /* ... ... */ LOWER_CALLABLES("Callable references Lowering"), /* ... ... */ LOWER_INLINE("Functions inlining"), /* ... ... */ AUTOBOX("Autoboxing of primitive types"), + /* ... ... */ LOWER_ENUMS("Enum classes lowering"), /* ... */ BITCODE("LLVM BitCode Generation"), /* ... ... */ RTTI("RTTI Generation"), /* ... ... */ CODEGEN("Code Generation"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index cbc1a30bf48..26ad0898868 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -171,6 +171,7 @@ internal val ClassDescriptor.сontributedMethods: List } fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT + || this.kind == ClassKind.ENUM_CLASS // TODO: optimize val ClassDescriptor.vtableEntries: List @@ -186,7 +187,7 @@ val ClassDescriptor.vtableEntries: List val methods = this.сontributedMethods val inheritedVtableSlots = superVtableEntries.map { superMethod -> - methods.single { OverridingUtil.overrides(it, superMethod) } + methods.singleOrNull { OverridingUtil.overrides(it, superMethod) } ?: superMethod } return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt index 5ab5c6fbc63..85725449391 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/utils.kt @@ -39,3 +39,5 @@ val PropertyDescriptor.backingField: PropertyDescriptor? fun DeclarationDescriptor.deepPrint() { this.accept(DeepPrintVisitor(PrintVisitor()), 0) } + +internal val String.synthesizedName get() = Name.identifier("\$" + this) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index d98d465d1b7..a4e0402556e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -314,8 +314,11 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { } fun countParams(fn: FunctionDescriptor) = LLVMCountParams(fn.llvmFunction) - fun indexInClass(p:PropertyDescriptor):Int = (p.containingDeclaration as ClassDescriptor).fields.indexOf(p) - + fun indexInClass(p:PropertyDescriptor):Int { + val index = (p.containingDeclaration as ClassDescriptor).fields.indexOf(p) + assert(index >= 0) { "Unable to find property $p" } + return index + } fun basicBlock(name: String = "label_"): LLVMBasicBlockRef { val currentBlock = this.currentBlock diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt index bb0fb5c62b0..8b155bc9733 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/NameUtils.kt @@ -89,6 +89,8 @@ internal val ClassDescriptor.symbolName: String CLASS -> "kclass:" INTERFACE -> "kinf:" OBJECT -> "kclass:" + ENUM_CLASS -> "kclass:" + ENUM_ENTRY -> "kclass:" else -> TODO("fixme: " + this.kind) } + fqNameSafe diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt new file mode 100644 index 00000000000..1b66013bf7c --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt @@ -0,0 +1,605 @@ +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.lower.SimpleMemberScope +import org.jetbrains.kotlin.backend.common.runOnFilePostfix +import org.jetbrains.kotlin.backend.jvm.descriptors.createValueParameter +import org.jetbrains.kotlin.backend.jvm.descriptors.initialize +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.KonanBuiltIns +import org.jetbrains.kotlin.backend.konan.KonanPlatform +import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions +import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.transform +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.utils.Printer +import org.jetbrains.kotlin.utils.addToStdlib.singletonList +import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList + +internal data class LoweredSyntheticFunction(val functionDescriptor: FunctionDescriptor, val containingClass: ClassDescriptor) + +internal data class LoweredEnumEntry(val implObject: ClassDescriptor, val valuesProperty: PropertyDescriptor, + val itemGetter: FunctionDescriptor, val entriesMap: Map) + +internal class EnumUsageLowering(val context: Context, + val loweredFunctions: Map, + val loweredEnumEntries: Map) + : IrElementTransformerVoid(), FileLoweringPass { + + override fun lower(irFile: IrFile) { + visitFile(irFile) + } + + override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression { + val enumClassDescriptor = expression.descriptor.containingDeclaration as ClassDescriptor + enumClassDescriptor.companionObjectDescriptor + return loadEnumEntry(expression.startOffset, expression.endOffset, enumClassDescriptor, expression.descriptor.name) + } + + override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { + if(expression.descriptor.kind != ClassKind.ENUM_ENTRY) + return super.visitGetObjectValue(expression) + val enumClassDescriptor = expression.descriptor.containingDeclaration as ClassDescriptor + return loadEnumEntry(expression.startOffset, expression.endOffset, enumClassDescriptor, expression.descriptor.name) + } + + private fun loadEnumEntry(startOffset: Int, endOffset: Int, enumClassDescriptor: ClassDescriptor, name: Name): IrExpression { + val loweredEnumEntry = loweredEnumEntries[enumClassDescriptor]!! + val implObject = loweredEnumEntry.implObject + val implObjectGetter = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, implObject.classValueType!!, implObject) + val valuesGetter = IrGetFieldImpl(startOffset, endOffset, loweredEnumEntry.valuesProperty).apply { + receiver = implObjectGetter + } + val ordinal = loweredEnumEntry.entriesMap[name]!! + return IrCallImpl(startOffset, endOffset, loweredEnumEntry.itemGetter).apply { + dispatchReceiver = valuesGetter + putValueArgument(0, IrConstImpl.int(startOffset, endOffset, enumClassDescriptor.module.builtIns.intType, ordinal)) + } + } + + override fun visitCall(expression: IrCall): IrExpression { + val loweredFunction = loweredFunctions[expression.descriptor] ?: return super.visitCall(expression) + return IrCallImpl(expression.startOffset, expression.endOffset, loweredFunction.functionDescriptor).apply { + val containingClass = loweredFunction.containingClass + dispatchReceiver = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, containingClass.classValueType!!, containingClass) + expression.descriptor.valueParameters.forEach { p -> putValueArgument(p, expression.getValueArgument(p)) } + } + } +} + +internal class EnumClassLowering(val context: Context) : ClassLoweringPass { + val loweredFunctions = mutableMapOf() + val loweredEnumEntries = mutableMapOf() + + fun run(irFile: IrFile) { + runOnFilePostfix(irFile) + EnumUsageLowering(context, loweredFunctions, loweredEnumEntries).lower(irFile) + } + + override fun lower(irClass: IrClass) { + val descriptor = irClass.descriptor + if (descriptor.kind != ClassKind.ENUM_CLASS) + return + EnumClassTransformer(irClass).run() + } + + private interface EnumConstructorCallTransformer { + fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression + fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression + } + + private inner class EnumClassTransformer(val irClass: IrClass) { + private val enumEntryOrdinals = mutableMapOf() + private val loweredEnumConstructors = mutableMapOf() + private val defaultEnumEntryConstructors = mutableMapOf() + private val loweredEnumConstructorParameters = mutableMapOf() + + private lateinit var valuesFieldDescriptor: PropertyDescriptor + private lateinit var valuesFunctionDescriptor: FunctionDescriptor + private lateinit var valueOfFunctionDescriptor: FunctionDescriptor + + fun run() { + assignOrdinalsToEnumEntries() + lowerEnumConstructors(irClass) + lowerEnumEntriesClasses() + val defaultClass = createDefaultClassForEnumEntries() + lowerEnumClassBody() + if (defaultClass != null) + irClass.declarations.add(defaultClass) + createImplObject() + } + + private fun assignOrdinalsToEnumEntries() { + var ordinal = 0 + irClass.declarations.forEach { + if (it is IrEnumEntry) { + enumEntryOrdinals.put(it.descriptor, ordinal) + ordinal++ + } + } + } + + private fun lowerEnumEntriesClasses() { + irClass.declarations.transformFlat { declaration -> + if (declaration is IrEnumEntry) { + declaration.singletonList() + lowerEnumEntryClass(declaration.correspondingClass).singletonOrEmptyList() + } else null + } + } + + private fun lowerEnumEntryClass(enumEntryClass: IrClass?): IrClass? { + if (enumEntryClass == null) return null + + lowerEnumConstructors(enumEntryClass) + + return enumEntryClass + } + + private fun createDefaultClassForEnumEntries(): IrClass? { + if (!irClass.declarations.any({ it is IrEnumEntry && it.correspondingClass == null })) return null + val descriptor = irClass.descriptor + val defaultClassDescriptor = ClassDescriptorImpl(descriptor, "DEFAULT".synthesizedName, Modality.FINAL, + ClassKind.CLASS, descriptor.defaultType.singletonList(), SourceElement.NO_SOURCE, false) + val defaultClass = IrClassImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, defaultClassDescriptor) + + val constructors = mutableSetOf() + + descriptor.constructors.forEach { + val loweredEnumConstructor = loweredEnumConstructors[it]!! + val (constructorDescriptor, constructor) = createSimpleDelegatingConstructor(defaultClassDescriptor, loweredEnumConstructor) + constructors.add(constructorDescriptor) + defaultClass.declarations.add(constructor) + defaultEnumEntryConstructors.put(loweredEnumConstructor, constructorDescriptor) + } + + val memberScope = SimpleMemberScope(listOf()) + defaultClassDescriptor.initialize(memberScope, constructors, null) + + return defaultClass + } + + private fun createImplObject() { + val descriptor = irClass.descriptor + val implObjectDescriptor = ClassDescriptorImpl(descriptor, "OBJECT".synthesizedName, Modality.FINAL, + ClassKind.OBJECT, KonanPlatform.builtIns.anyType.singletonList(), SourceElement.NO_SOURCE, false) + val implObject = IrClassImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, implObjectDescriptor) + + val enumEntries = mutableListOf() + var i = 0 + while (i < irClass.declarations.size) { + val declaration = irClass.declarations[i] + var delete = false + when (declaration) { + is IrEnumEntry -> { + enumEntries.add(declaration) + delete = true + } + is IrFunction -> { + var copiedDescriptor = tryCopySyntheticBodyDeclaration(implObjectDescriptor, declaration, IrSyntheticBodyKind.ENUM_VALUES) + if(copiedDescriptor != null) + { + valuesFunctionDescriptor = copiedDescriptor + delete = true + } + copiedDescriptor = tryCopySyntheticBodyDeclaration(implObjectDescriptor, declaration, IrSyntheticBodyKind.ENUM_VALUEOF) + if(copiedDescriptor != null) + { + valueOfFunctionDescriptor = copiedDescriptor + delete = true + } + } + } + if (delete) + irClass.declarations.removeAt(i) + else + ++i + } + + val memberScope = SimpleMemberScope(listOf(valuesFunctionDescriptor, valueOfFunctionDescriptor)) + + val constructorOfAny = irClass.descriptor.module.builtIns.any.constructors.first() + val (constructorDescriptor, constructor) = createSimpleDelegatingConstructor(implObjectDescriptor, constructorOfAny) + + implObjectDescriptor.initialize(memberScope, setOf(constructorDescriptor), constructorDescriptor) + + implObject.declarations.add(constructor) + implObject.declarations.add(createSyntheticValuesFieldDeclaration(implObjectDescriptor, enumEntries)) + implObject.declarations.add(createSyntheticValuesMethodDeclaration(implObjectDescriptor)) + implObject.declarations.add(createSyntheticValueOfMethodDeclaration(implObjectDescriptor)) + + irClass.declarations.add(implObject) + } + + private fun tryCopySyntheticBodyDeclaration(implObjectDescriptor: ClassDescriptor, + declaration: IrFunction, + kind: IrSyntheticBodyKind): FunctionDescriptor? { + if (!declaration.body.let { it is IrSyntheticBody && it.kind == kind }) + return null + val newDescriptor = declaration.descriptor + .newCopyBuilder() + .setOwner(implObjectDescriptor) + .setName(declaration.descriptor.name.identifier.synthesizedName) + .setDispatchReceiverParameter(implObjectDescriptor.thisAsReceiverParameter) + .build()!! + loweredFunctions.put(declaration.descriptor, LoweredSyntheticFunction(newDescriptor, implObjectDescriptor)) + return newDescriptor + } + + private fun createSimpleDelegatingConstructor(classDescriptor: ClassDescriptor, + superConstructorDescriptor: ClassConstructorDescriptor) + : Pair { + val constructorDescriptor = ClassConstructorDescriptorImpl.createSynthesized( + classDescriptor, + Annotations.EMPTY, + superConstructorDescriptor.isPrimary, + SourceElement.NO_SOURCE) + val valueParameters = superConstructorDescriptor.valueParameters.map { + it.copy(constructorDescriptor, it.name, it.index) + } + constructorDescriptor.initialize(valueParameters, superConstructorDescriptor.visibility) + constructorDescriptor.returnType = superConstructorDescriptor.returnType + + val body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + listOf( + IrDelegatingConstructorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, superConstructorDescriptor).apply { + valueParameters.forEachIndexed { idx, parameter -> + putValueArgument(idx, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, parameter)) + } + }, + IrInstanceInitializerCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, classDescriptor) + ) + ) + val constructor = IrConstructorImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, constructorDescriptor, body) + + return Pair(constructorDescriptor, constructor) + } + + private fun createSyntheticValuesFieldDeclaration(implObjectDescriptor: ClassDescriptor, + enumEntries: List): IrFieldImpl { + val valuesArrayType = context.builtIns.getArrayType(Variance.INVARIANT, irClass.descriptor.defaultType) + valuesFieldDescriptor = createSyntheticValuesFieldDescriptor(implObjectDescriptor, valuesArrayType) + + val getter = genericArrayType.unsubstitutedMemberScope.getContributedFunctions(Name.identifier("get"), NoLookupLocation.FROM_BACKEND).single() + + val typeParameterT = genericArrayType.declaredTypeParameters[0] + val enumClassType = irClass.descriptor.defaultType + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType))) + val substitutedValueOf = getter.substitute(typeSubstitutor)!! + + val entriesMap = enumEntries.associateBy({ it -> it.descriptor.name }, { it -> enumEntryOrdinals[it.descriptor]!! }).toMap() + val loweredEnumEntry = LoweredEnumEntry(implObjectDescriptor, valuesFieldDescriptor, substitutedValueOf, entriesMap) + loweredEnumEntries.put(irClass.descriptor, loweredEnumEntry) + + val irValuesInitializer = createArrayOfExpression(irClass.descriptor.defaultType, enumEntries.map { it.initializerExpression }) + + val irField = IrFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, + valuesFieldDescriptor, + IrExpressionBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irValuesInitializer)) + return irField + } + + private fun createSyntheticValuesFieldDescriptor(implObjectDescriptor: ClassDescriptor, valuesArrayType: SimpleType): PropertyDescriptorImpl { + val receiver = ReceiverParameterDescriptorImpl(implObjectDescriptor, ImplicitClassReceiver(implObjectDescriptor)) + return PropertyDescriptorImpl.create(implObjectDescriptor, Annotations.EMPTY, Modality.FINAL, Visibilities.PRIVATE, + false, "VALUES".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, irClass.descriptor.source, + false, false, false, false, false).initialize(valuesArrayType, dispatchReceiverParameter = receiver) + } + + private val kotlinPackage = context.irModule!!.descriptor.getPackage(FqName("kotlin")) + + private val genericArrayOfFun = kotlinPackage.memberScope.getContributedFunctions(Name.identifier("arrayOf"), NoLookupLocation.FROM_BACKEND).first() + + private val genericValueOfFun = context.builtIns.getKonanInternalFunctions("valueOfForEnum").single() + + private val genericValuesFun = context.builtIns.getKonanInternalFunctions("valuesForEnum").single() + + private val genericArrayType = kotlinPackage.memberScope.getContributedClassifier(Name.identifier("Array"), NoLookupLocation.FROM_BACKEND) as ClassDescriptor + + private fun createArrayOfExpression(arrayElementType: KotlinType, arrayElements: List): IrExpression { + val typeParameter0 = genericArrayOfFun.typeParameters[0] + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameter0.typeConstructor to TypeProjectionImpl(arrayElementType))) + val substitutedArrayOfFun = genericArrayOfFun.substitute(typeSubstitutor)!! + + val typeArguments = mapOf(typeParameter0 to arrayElementType) + + val valueParameter0 = substitutedArrayOfFun.valueParameters[0] + val arg0VarargType = valueParameter0.type + val arg0VarargElementType = valueParameter0.varargElementType!! + val arg0 = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arg0VarargType, arg0VarargElementType, arrayElements) + + return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedArrayOfFun, typeArguments).apply { + putValueArgument(0, arg0) + } + } + + private fun createSyntheticValuesMethodDeclaration(companionObjectDescriptor: ClassDescriptor): IrFunction { + val typeParameterT = genericValuesFun.typeParameters[0] + val enumClassType = irClass.descriptor.defaultType + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType))) + val substitutedValueOf = genericValuesFun.substitute(typeSubstitutor)!! + + val irValuesCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedValueOf, mapOf(typeParameterT to enumClassType)) + .apply { + val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, companionObjectDescriptor.thisAsReceiverParameter) + putValueArgument(0, IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFieldDescriptor, receiver)) + } + + val body = IrBlockBodyImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + listOf(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFunctionDescriptor, irValuesCall)) + ) + return IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, valuesFunctionDescriptor, body) + } + + private fun createSyntheticValueOfMethodDeclaration(companionObjectDescriptor: ClassDescriptor): IrFunction { + val typeParameterT = genericValueOfFun.typeParameters[0] + val enumClassType = irClass.descriptor.defaultType + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType))) + val substitutedValueOf = genericValueOfFun.substitute(typeSubstitutor)!! + + val irValueOfCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedValueOf, mapOf(typeParameterT to enumClassType)) + .apply { + putValueArgument(0, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valueOfFunctionDescriptor.valueParameters[0])) + val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, companionObjectDescriptor.thisAsReceiverParameter) + putValueArgument(1, IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFieldDescriptor, receiver)) + } + + val body = IrBlockBodyImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + listOf(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valueOfFunctionDescriptor, irValueOfCall)) + ) + return IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, valueOfFunctionDescriptor, body) + } + + private fun lowerEnumConstructors(irClass: IrClass) { + irClass.declarations.transform { declaration -> + if (declaration is IrConstructor) + transformEnumConstructor(declaration) + else + declaration + } + } + + private fun transformEnumConstructor(enumConstructor: IrConstructor): IrConstructor { + val constructorDescriptor = enumConstructor.descriptor + val loweredConstructorDescriptor = lowerEnumConstructor(constructorDescriptor) + val loweredEnumConstructor = IrConstructorImpl( + enumConstructor.startOffset, enumConstructor.endOffset, enumConstructor.origin, + loweredConstructorDescriptor, + enumConstructor.body!! // will be transformed later + ) + return loweredEnumConstructor + } + + private fun lowerEnumConstructor(constructorDescriptor: ClassConstructorDescriptor): ClassConstructorDescriptor { + val loweredConstructorDescriptor = ClassConstructorDescriptorImpl.createSynthesized( + constructorDescriptor.containingDeclaration, + constructorDescriptor.annotations, + constructorDescriptor.isPrimary, + constructorDescriptor.source + ) + + val valueParameters = + listOf( + loweredConstructorDescriptor.createValueParameter(0, "name", context.builtIns.stringType), + loweredConstructorDescriptor.createValueParameter(1, "ordinal", context.builtIns.intType) + ) + + constructorDescriptor.valueParameters.map { + lowerConstructorValueParameter(loweredConstructorDescriptor, it) + } + loweredConstructorDescriptor.initialize(valueParameters, Visibilities.PROTECTED) + + loweredConstructorDescriptor.returnType = constructorDescriptor.returnType + + loweredEnumConstructors[constructorDescriptor] = loweredConstructorDescriptor + + return loweredConstructorDescriptor + } + + private fun lowerConstructorValueParameter( + loweredConstructorDescriptor: ClassConstructorDescriptor, + valueParameterDescriptor: ValueParameterDescriptor + ): ValueParameterDescriptor { + val loweredValueParameterDescriptor = valueParameterDescriptor.copy( + loweredConstructorDescriptor, + valueParameterDescriptor.name, + valueParameterDescriptor.index + 2 + ) + loweredEnumConstructorParameters[valueParameterDescriptor] = loweredValueParameterDescriptor + return loweredValueParameterDescriptor + } + + private fun lowerEnumClassBody() { + irClass.transformChildrenVoid(EnumClassBodyTransformer()) + } + + private inner class InEnumClassConstructor(val enumClassConstructor: ClassConstructorDescriptor) : + EnumConstructorCallTransformer { + override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression { + val startOffset = enumConstructorCall.startOffset + val endOffset = enumConstructorCall.endOffset + val origin = enumConstructorCall.origin + + val result = IrDelegatingConstructorCallImpl(startOffset, endOffset, enumConstructorCall.descriptor) + + assert(result.descriptor.valueParameters.size == 2) { + "Enum(String, Int) constructor call expected:\n${result.dump()}" + } + + val nameParameter = enumClassConstructor.valueParameters.getOrElse(0) { + throw AssertionError("No 'name' parameter in enum constructor: $enumClassConstructor") + } + + val ordinalParameter = enumClassConstructor.valueParameters.getOrElse(1) { + throw AssertionError("No 'ordinal' parameter in enum constructor: $enumClassConstructor") + } + + result.putValueArgument(0, IrGetValueImpl(startOffset, endOffset, nameParameter, origin)) + result.putValueArgument(1, IrGetValueImpl(startOffset, endOffset, ordinalParameter, origin)) + + 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, IrGetValueImpl(startOffset, endOffset, enumClassConstructor.valueParameters[0])) + result.putValueArgument(1, IrGetValueImpl(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 { + override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression { + val name = enumEntry.name.asString() + val ordinal = enumEntryOrdinals[enumEntry]!! + + val descriptor = enumConstructorCall.descriptor + val startOffset = enumConstructorCall.startOffset + val endOffset = enumConstructorCall.endOffset + + val loweredConstructor = loweredEnumConstructors.getOrElse(descriptor) { + throw AssertionError("Constructor called in enum entry initializer should've been lowered: $descriptor") + } + + val result = createConstructorCall(startOffset, endOffset, loweredConstructor) + + result.putValueArgument(0, IrConstImpl.string(startOffset, endOffset, context.builtIns.stringType, name)) + result.putValueArgument(1, IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, ordinal)) + + descriptor.valueParameters.forEach { valueParameter -> + val i = valueParameter.index + result.putValueArgument(i + 2, enumConstructorCall.getValueArgument(i)) + } + + 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 + } + + private inner class InEnumEntryClassConstructor(enumEntry: ClassDescriptor) : InEnumEntry(enumEntry) { + override fun createConstructorCall(startOffset: Int, endOffset: Int, loweredConstructor: ClassConstructorDescriptor) + = IrDelegatingConstructorCallImpl(startOffset, endOffset, loweredConstructor) + } + + private inner class InEnumEntryInitializer(enumEntry: ClassDescriptor) : InEnumEntry(enumEntry) { + override fun createConstructorCall(startOffset: Int, endOffset: Int, loweredConstructor: ClassConstructorDescriptor) + = IrCallImpl(startOffset, endOffset, defaultEnumEntryConstructors[loweredConstructor] ?: loweredConstructor) + } + + private inner class EnumClassBodyTransformer : IrElementTransformerVoid() { + private var enumConstructorCallTransformer: EnumConstructorCallTransformer? = null + + override fun visitEnumEntry(declaration: IrEnumEntry): IrStatement { + assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}" } + + enumConstructorCallTransformer = InEnumEntryInitializer(declaration.descriptor) + + var result: IrEnumEntry = IrEnumEntryImpl(declaration.startOffset, declaration.endOffset, declaration.origin, + declaration.descriptor, null, declaration.initializerExpression) + result = super.visitEnumEntry(result) as IrEnumEntry + + enumConstructorCallTransformer = null + + return result + } + + override fun visitConstructor(declaration: IrConstructor): IrStatement { + val constructorDescriptor = declaration.descriptor + val containingClass = constructorDescriptor.containingDeclaration + + // TODO local (non-enum) class in enum class constructor? + val previous = enumConstructorCallTransformer + + if (containingClass.kind == ClassKind.ENUM_ENTRY) { + assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}" } + enumConstructorCallTransformer = InEnumEntryClassConstructor(containingClass) + } else if (containingClass.kind == ClassKind.ENUM_CLASS) { + assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}" } + enumConstructorCallTransformer = InEnumClassConstructor(constructorDescriptor) + } + + val result = super.visitConstructor(declaration) + + enumConstructorCallTransformer = previous + + return result + } + + override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression { + expression.transformChildrenVoid(this) + + val callTransformer = enumConstructorCallTransformer ?: + throw AssertionError("Enum constructor call outside of enum entry initialization or enum class constructor:\n" + irClass.dump()) + + + return callTransformer.transform(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 visitGetValue(expression: IrGetValue): IrExpression { + val loweredParameter = loweredEnumConstructorParameters[expression.descriptor] + if (loweredParameter != null) + return IrGetValueImpl(expression.startOffset, expression.endOffset, loweredParameter, expression.origin) + else + return expression + } + } + } +} \ No newline at end of file diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 80604dab017..493d14e8893 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -296,6 +296,41 @@ task empty_substring(type: RunKonanTest) { source = "runtime/basic/empty_substring.kt" } +task enum0(type: RunKonanTest) { + goldValue = "VALUE\n" + source = "codegen/enum/test0.kt" +} + +task enum1(type: RunKonanTest) { + goldValue = "z12\n" + source = "codegen/enum/test1.kt" +} + +task enum_valueOf(type: RunKonanTest) { + goldValue = "E1\n" + source = "codegen/enum/valueOf.kt" +} + +task enum_values(type: RunKonanTest) { + goldValue = "E2\n" + source = "codegen/enum/values.kt" +} + +task enum_vCallNoEntryClass(type: RunKonanTest) { + goldValue = "('z3', 3)\n" + source = "codegen/enum/vCallNoEntryClass.kt" +} + +task enum_vCallWithEntryClass(type: RunKonanTest) { + goldValue = "z1z2\n" + source = "codegen/enum/vCallWithEntryClass.kt" +} + +task enum_companionObject(type: RunKonanTest) { + goldValue = "OK\n" + source = "codegen/enum/companionObject.kt" +} + task array0(type: RunKonanTest) { goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n" source = "runtime/collections/array0.kt" diff --git a/backend.native/tests/codegen/enum/companionObject.kt b/backend.native/tests/codegen/enum/companionObject.kt new file mode 100644 index 00000000000..3aef194b852 --- /dev/null +++ b/backend.native/tests/codegen/enum/companionObject.kt @@ -0,0 +1,24 @@ +enum class Game { + ROCK, + PAPER, + SCISSORS; + + companion object { + fun foo() = ROCK + val bar = PAPER + val values2 = values() + val scissors = valueOf("SCISSORS") + } +} + +fun box(): String { + if (Game.foo() != Game.ROCK) return "Fail 1" + if (Game.bar != Game.PAPER) return "Fail 2: ${Game.bar}" + if (Game.values().size != 3) return "Fail 3" + if (Game.valueOf("SCISSORS") != Game.SCISSORS) return "Fail 4" + if (Game.values2.size != 3) return "Fail 5" + if (Game.scissors != Game.SCISSORS) return "Fail 6" + return "OK" +} + +fun main(args: Array) = println(box()) \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/test0.kt b/backend.native/tests/codegen/enum/test0.kt new file mode 100644 index 00000000000..ff525debbc8 --- /dev/null +++ b/backend.native/tests/codegen/enum/test0.kt @@ -0,0 +1,9 @@ +val TOP_LEVEL = 5 + +enum class MyEnum(value: Int) { + VALUE(TOP_LEVEL) +} + +fun main(args: Array) { + println(MyEnum.VALUE.toString()) +} diff --git a/backend.native/tests/codegen/enum/test1.kt b/backend.native/tests/codegen/enum/test1.kt new file mode 100644 index 00000000000..b9638bbb3bc --- /dev/null +++ b/backend.native/tests/codegen/enum/test1.kt @@ -0,0 +1,8 @@ +enum class Zzz(val zzz: String, val x: Int) { + Z1("z1", 1), + Z2("z2", 2) +} + +fun main(args: Array) { + println(Zzz.Z1.zzz + Zzz.Z2.x) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/vCallNoEntryClass.kt b/backend.native/tests/codegen/enum/vCallNoEntryClass.kt new file mode 100644 index 00000000000..ebfc475e302 --- /dev/null +++ b/backend.native/tests/codegen/enum/vCallNoEntryClass.kt @@ -0,0 +1,13 @@ +enum class Zzz(val zzz: String, val x: Int) { + Z1("z1", 1), + Z2("z2", 2), + Z3("z3", 3); + + override fun toString(): String{ + return "('$zzz', $x)" + } +} + +fun main(args: Array) { + println(Zzz.Z3.toString()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/vCallWithEntryClass.kt b/backend.native/tests/codegen/enum/vCallWithEntryClass.kt new file mode 100644 index 00000000000..c3b509f2258 --- /dev/null +++ b/backend.native/tests/codegen/enum/vCallWithEntryClass.kt @@ -0,0 +1,15 @@ +enum class Zzz { + Z1 { + override fun f() = "z1" + }, + + Z2 { + override fun f() = "z2" + }; + + open fun f() = "" +} + +fun main(args: Array) { + println(Zzz.Z1.f() + Zzz.Z2.f()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/valueOf.kt b/backend.native/tests/codegen/enum/valueOf.kt new file mode 100644 index 00000000000..9883e06369d --- /dev/null +++ b/backend.native/tests/codegen/enum/valueOf.kt @@ -0,0 +1,8 @@ +enum class E { + E1, + E2 +} + +fun main(args: Array) { + println(E.valueOf("E1").toString()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/values.kt b/backend.native/tests/codegen/enum/values.kt new file mode 100644 index 00000000000..aebaa636140 --- /dev/null +++ b/backend.native/tests/codegen/enum/values.kt @@ -0,0 +1,8 @@ +enum class E { + E1, + E2 +} + +fun main(args: Array) { + println(E.values()[1].toString()) +} \ No newline at end of file diff --git a/backend.native/tests/external/codegen/blackbox/enum/emptyConstructor.kt b/backend.native/tests/external/codegen/blackbox/enum/emptyConstructor.kt index df1c476bfb0..412cf9a2801 100644 --- a/backend.native/tests/external/codegen/blackbox/enum/emptyConstructor.kt +++ b/backend.native/tests/external/codegen/blackbox/enum/emptyConstructor.kt @@ -1,5 +1,5 @@ // TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS, NATIVE +// IGNORE_BACKEND: JS package test diff --git a/backend.native/tests/external/codegen/blackbox/enum/kt9711_2.kt b/backend.native/tests/external/codegen/blackbox/enum/kt9711_2.kt index c16ffd7ae3c..8642ed92784 100644 --- a/backend.native/tests/external/codegen/blackbox/enum/kt9711_2.kt +++ b/backend.native/tests/external/codegen/blackbox/enum/kt9711_2.kt @@ -1,5 +1,5 @@ // TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS, NATIVE +// IGNORE_BACKEND: JS enum class IssueState { diff --git a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt index c7587ced3f0..dc2fa175581 100644 --- a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt +++ b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt @@ -25,4 +25,17 @@ internal fun ThrowNoWhenBranchMatchedException(): Nothing { } @ExportForCppRuntime -internal fun TheEmptyString() = "" \ No newline at end of file +internal fun TheEmptyString() = "" + +internal fun > valueOfForEnum(name: String, arr: Array) : T +{ + for (x in arr) + if (x.name == name) + return x + throw Exception("Invalid enum name: $name") +} + +internal fun > valuesForEnum(values: Array): Array +{ + return values.clone() as Array +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/Enum.kt b/runtime/src/main/kotlin/kotlin/Enum.kt index 8c9d22bf3cd..f88065d160a 100644 --- a/runtime/src/main/kotlin/kotlin/Enum.kt +++ b/runtime/src/main/kotlin/kotlin/Enum.kt @@ -5,30 +5,27 @@ package kotlin * See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/enum-classes.html) for more * information on enum classes. */ -/* -public abstract class Enum>(name: String, ordinal: Int): Comparable { - companion object {} +public abstract class Enum>(public val name: String, public val ordinal: Int): Comparable { - /** - * Returns the name of this enum constant, exactly as declared in its enum declaration. - */ - public final val name: String - - /** - * Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant - * is assigned an ordinal of zero). - */ - public final val ordinal: Int - - public override final fun compareTo(other: E): Int + public override final fun compareTo(other: E): Int { return ordinal - other.ordinal } /** * Throws an exception since enum constants cannot be cloned. * This method prevents enum classes from inheriting from [Cloneable]. */ - protected final fun clone(): Any + protected final fun clone(): Any { + throw UnsupportedOperationException() + } - public override final fun equals(other: Any?): Boolean - public override final fun hashCode(): Int - public override fun toString(): String -} */ + public override final fun equals(other: Any?): Boolean { + return other is Enum<*> && ordinal == other.ordinal + } + + public override final fun hashCode(): Int { + return ordinal + } + + public override fun toString(): String { + return name + } +}