Added switch (enum-only when) lowering
Added enum ordinal to serialization
This commit is contained in:
committed by
Sergey Bogolepov
parent
1afc5e5e90
commit
5ad749d56b
+25
@@ -51,10 +51,13 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
|||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
|
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
|
||||||
|
import org.jetbrains.kotlin.metadata.KonanLinkData
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
|
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.getName
|
||||||
import java.lang.System.out
|
import java.lang.System.out
|
||||||
import kotlin.LazyThreadSafetyMode.PUBLICATION
|
import kotlin.LazyThreadSafetyMode.PUBLICATION
|
||||||
|
|
||||||
@@ -63,6 +66,7 @@ internal class SpecialDeclarationsFactory(val context: Context) {
|
|||||||
private val outerThisFields = mutableMapOf<ClassDescriptor, IrField>()
|
private val outerThisFields = mutableMapOf<ClassDescriptor, IrField>()
|
||||||
private val bridgesDescriptors = mutableMapOf<Pair<IrSimpleFunction, BridgeDirections>, IrSimpleFunction>()
|
private val bridgesDescriptors = mutableMapOf<Pair<IrSimpleFunction, BridgeDirections>, IrSimpleFunction>()
|
||||||
private val loweredEnums = mutableMapOf<ClassDescriptor, LoweredEnum>()
|
private val loweredEnums = mutableMapOf<ClassDescriptor, LoweredEnum>()
|
||||||
|
private val ordinals = mutableMapOf<IrClass, Map<ClassDescriptor, Int>>()
|
||||||
|
|
||||||
object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS :
|
object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS :
|
||||||
IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS")
|
IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS")
|
||||||
@@ -123,6 +127,27 @@ internal class SpecialDeclarationsFactory(val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assignOrdinalsToEnumEntries(irClass: IrClass): Map<ClassDescriptor, Int> {
|
||||||
|
val enumEntryOrdinals = mutableMapOf<ClassDescriptor, Int>()
|
||||||
|
irClass.declarations.filterIsInstance<IrEnumEntry>().forEachIndexed { index, entry ->
|
||||||
|
enumEntryOrdinals[entry.descriptor] = index
|
||||||
|
}
|
||||||
|
return enumEntryOrdinals
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getEnumEntryOrdinal(entryDescriptor: ClassDescriptor): Int {
|
||||||
|
val enumClassDescriptor = entryDescriptor.containingDeclaration as ClassDescriptor
|
||||||
|
// If enum came from another module then we need to get serialized ordinal number.
|
||||||
|
// We serialize ordinal because current serialization cannot preserve enum entry order.
|
||||||
|
if (enumClassDescriptor is DeserializedClassDescriptor) {
|
||||||
|
return enumClassDescriptor.classProto.enumEntryList
|
||||||
|
.first { entryDescriptor.name == enumClassDescriptor.c.nameResolver.getName(it.name) }
|
||||||
|
.getExtension(KonanLinkData.enumEntryOrdinal)
|
||||||
|
}
|
||||||
|
val enumClass = context.ir.getEnum(enumClassDescriptor)
|
||||||
|
return ordinals.getOrPut(enumClass) { assignOrdinalsToEnumEntries(enumClass) }[entryDescriptor]!!
|
||||||
|
}
|
||||||
|
|
||||||
private fun initializeBridgeDescriptor(bridgeDescriptor: SimpleFunctionDescriptorImpl,
|
private fun initializeBridgeDescriptor(bridgeDescriptor: SimpleFunctionDescriptorImpl,
|
||||||
descriptor: FunctionDescriptor,
|
descriptor: FunctionDescriptor,
|
||||||
bridgeDirections: Array<BridgeDirection>) {
|
bridgeDirections: Array<BridgeDirection>) {
|
||||||
|
|||||||
-1
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.backend.konan
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||||
import org.jetbrains.kotlin.backend.common.lower.*
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
import org.jetbrains.kotlin.backend.common.validateIrModule
|
|
||||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.referenceAllTypeExternalClassifiers
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.referenceAllTypeExternalClassifiers
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.*
|
import org.jetbrains.kotlin.backend.konan.lower.*
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.DefaultArgumentStubGenerator
|
import org.jetbrains.kotlin.backend.konan.lower.DefaultArgumentStubGenerator
|
||||||
|
|||||||
+1
@@ -98,6 +98,7 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
|
|||||||
val nothing = symbolTable.referenceClass(builtIns.nothing)
|
val nothing = symbolTable.referenceClass(builtIns.nothing)
|
||||||
val throwable = symbolTable.referenceClass(builtIns.throwable)
|
val throwable = symbolTable.referenceClass(builtIns.throwable)
|
||||||
val string = symbolTable.referenceClass(builtIns.string)
|
val string = symbolTable.referenceClass(builtIns.string)
|
||||||
|
val enum = symbolTable.referenceClass(builtIns.enum)
|
||||||
|
|
||||||
val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context))
|
val arrayList = symbolTable.referenceClass(getArrayListClassDescriptor(context))
|
||||||
|
|
||||||
|
|||||||
+7
-17
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.ir.declarations.impl.*
|
|||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
@@ -47,9 +46,8 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
|
||||||
import org.jetbrains.kotlin.types.Variance
|
|
||||||
|
|
||||||
internal class EnumSyntheticFunctionsBuilder(val context: Context) {
|
internal class EnumSyntheticFunctionsBuilder(val context: Context) {
|
||||||
fun buildValuesExpression(startOffset: Int, endOffset: Int,
|
fun buildValuesExpression(startOffset: Int, endOffset: Int,
|
||||||
@@ -159,8 +157,12 @@ internal class EnumUsageLowering(val context: Context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
||||||
|
|
||||||
fun run(irFile: IrFile) {
|
fun run(irFile: IrFile) {
|
||||||
runOnFilePostfix(irFile)
|
runOnFilePostfix(irFile)
|
||||||
|
// EnumWhenLowering should be performed before EnumUsageLowering because
|
||||||
|
// the latter performs lowering of IrGetEnumValue
|
||||||
|
EnumWhenLowering(context).lower(irFile)
|
||||||
EnumUsageLowering(context).lower(irFile)
|
EnumUsageLowering(context).lower(irFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +179,6 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
|||||||
|
|
||||||
private inner class EnumClassTransformer(val irClass: IrClass) {
|
private inner class EnumClassTransformer(val irClass: IrClass) {
|
||||||
private val loweredEnum = context.specialDeclarationsFactory.getLoweredEnum(irClass.descriptor)
|
private val loweredEnum = context.specialDeclarationsFactory.getLoweredEnum(irClass.descriptor)
|
||||||
private val enumEntryOrdinals = mutableMapOf<ClassDescriptor, Int>()
|
|
||||||
private val loweredEnumConstructors = mutableMapOf<ClassConstructorDescriptor, IrConstructor>()
|
private val loweredEnumConstructors = mutableMapOf<ClassConstructorDescriptor, IrConstructor>()
|
||||||
private val descriptorToIrConstructorWithDefaultArguments = mutableMapOf<ClassConstructorDescriptor, IrConstructor>()
|
private val descriptorToIrConstructorWithDefaultArguments = mutableMapOf<ClassConstructorDescriptor, IrConstructor>()
|
||||||
private val defaultEnumEntryConstructors = mutableMapOf<ClassConstructorDescriptor, IrConstructor>()
|
private val defaultEnumEntryConstructors = mutableMapOf<ClassConstructorDescriptor, IrConstructor>()
|
||||||
@@ -186,7 +187,6 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
|||||||
|
|
||||||
fun run() {
|
fun run() {
|
||||||
insertInstanceInitializerCall()
|
insertInstanceInitializerCall()
|
||||||
assignOrdinalsToEnumEntries()
|
|
||||||
lowerEnumConstructors(irClass)
|
lowerEnumConstructors(irClass)
|
||||||
lowerEnumEntriesClasses()
|
lowerEnumEntriesClasses()
|
||||||
val defaultClass = createDefaultClassForEnumEntries()
|
val defaultClass = createDefaultClassForEnumEntries()
|
||||||
@@ -221,16 +221,6 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignOrdinalsToEnumEntries() {
|
|
||||||
var ordinal = 0
|
|
||||||
irClass.declarations.forEach {
|
|
||||||
if (it is IrEnumEntry) {
|
|
||||||
enumEntryOrdinals.put(it.descriptor, ordinal)
|
|
||||||
ordinal++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun lowerEnumEntriesClasses() {
|
private fun lowerEnumEntriesClasses() {
|
||||||
irClass.declarations.transformFlat { declaration ->
|
irClass.declarations.transformFlat { declaration ->
|
||||||
if (declaration is IrEnumEntry) {
|
if (declaration is IrEnumEntry) {
|
||||||
@@ -563,7 +553,7 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
|
|||||||
private abstract inner class InEnumEntry(private val enumEntry: ClassDescriptor) : EnumConstructorCallTransformer {
|
private abstract inner class InEnumEntry(private val enumEntry: ClassDescriptor) : EnumConstructorCallTransformer {
|
||||||
override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression {
|
override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression {
|
||||||
val name = enumEntry.name.asString()
|
val name = enumEntry.name.asString()
|
||||||
val ordinal = enumEntryOrdinals[enumEntry]!!
|
val ordinal = context.specialDeclarationsFactory.getEnumEntryOrdinal(enumEntry)
|
||||||
|
|
||||||
val descriptor = enumConstructorCall.descriptor
|
val descriptor = enumConstructorCall.descriptor
|
||||||
val startOffset = enumConstructorCall.startOffset
|
val startOffset = enumConstructorCall.startOffset
|
||||||
|
|||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.peek
|
||||||
|
import org.jetbrains.kotlin.backend.common.pop
|
||||||
|
import org.jetbrains.kotlin.backend.common.push
|
||||||
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||||
|
import org.jetbrains.kotlin.ir.util.getArguments
|
||||||
|
import org.jetbrains.kotlin.ir.util.getPropertyGetter
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.types.isNullable
|
||||||
|
|
||||||
|
/** Look for when-constructs where subject is enum entry.
|
||||||
|
* Replace branches that are comparisons with compile-time known enum entries
|
||||||
|
* with comparisons of ordinals.
|
||||||
|
*/
|
||||||
|
internal class EnumWhenLowering(private val context: Context) : IrElementTransformerVoid(), FileLoweringPass {
|
||||||
|
|
||||||
|
private val subjectWithOrdinalStack = mutableListOf<Pair<IrVariable, Lazy<IrVariable>>>()
|
||||||
|
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
visitFile(irFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks that irBlock satisfies all constrains of this lowering.
|
||||||
|
// 1. Block's origin is WHEN
|
||||||
|
// 2. Subject of `when` is variable of enum type
|
||||||
|
// NB: See BranchingExpressionGenerator in Kotlin sources to get insight about
|
||||||
|
// `when` block translation to IR.
|
||||||
|
private fun shouldLower(irBlock: IrBlock): Boolean {
|
||||||
|
if (irBlock.origin != IrStatementOrigin.WHEN) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// when-block with subject should have two children: temporary variable and when itself.
|
||||||
|
if (irBlock.statements.size != 2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val subject = irBlock.statements[0] as IrVariable
|
||||||
|
// Subject should not be nullable because we will access the `ordinal` property.
|
||||||
|
if (subject.type.isNullable()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Check that subject is enum entry.
|
||||||
|
val enumClass = subject.type.constructor.declarationDescriptor as? ClassDescriptor
|
||||||
|
?: return false
|
||||||
|
return enumClass.kind == ClassKind.ENUM_CLASS
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitBlock(expression: IrBlock): IrExpression {
|
||||||
|
if (!shouldLower(expression)) {
|
||||||
|
return super.visitBlock(expression)
|
||||||
|
}
|
||||||
|
// Will be initialized only when we found a branch that compares
|
||||||
|
// subject with compile-time known enum entry.
|
||||||
|
val subject = expression.statements[0] as IrVariable
|
||||||
|
val subjectOrdinalProvider = lazy {
|
||||||
|
createEnumOrdinalVariable(subject)
|
||||||
|
}
|
||||||
|
subjectWithOrdinalStack.push(Pair(subject, subjectOrdinalProvider))
|
||||||
|
// Process nested `when` and comparisons.
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
// If variable was initialized then it was actually used and we need to insert it
|
||||||
|
// into the block's IR.
|
||||||
|
if (subjectOrdinalProvider.isInitialized()) {
|
||||||
|
expression.statements.add(1, subjectOrdinalProvider.value)
|
||||||
|
}
|
||||||
|
subjectWithOrdinalStack.pop()
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createEnumOrdinalVariable(enumVariable: IrVariable): IrVariable {
|
||||||
|
val ordinalPropertyGetter = context.ir.symbols.enum.getPropertyGetter("ordinal")!!
|
||||||
|
val getOrdinal = IrCallImpl(enumVariable.startOffset, enumVariable.endOffset, ordinalPropertyGetter).apply {
|
||||||
|
dispatchReceiver = IrGetValueImpl(enumVariable.startOffset, enumVariable.endOffset, enumVariable.symbol)
|
||||||
|
}
|
||||||
|
// Create temporary variable for subject's ordinal.
|
||||||
|
val ordinalDescriptor = IrTemporaryVariableDescriptorImpl(enumVariable.descriptor.containingDeclaration,
|
||||||
|
Name.identifier(enumVariable.name.asString() + "_ordinal"), context.builtIns.intType)
|
||||||
|
return IrVariableImpl(enumVariable.startOffset, enumVariable.endOffset,
|
||||||
|
IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, ordinalDescriptor, getOrdinal)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
// Try to do actual lowering.
|
||||||
|
return tryLower(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val areEqualByValue = context.ir.symbols.areEqualByValue.first {
|
||||||
|
it.owner.valueParameters[0].type == context.builtIns.intType
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are looking for branch that is a comparison of the subject and another enum entry.
|
||||||
|
private fun tryLower(call: IrCall): IrExpression {
|
||||||
|
if (call.origin != IrStatementOrigin.EQEQ) {
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
val callArgs = call.getArguments()
|
||||||
|
if (callArgs.size != 2) {
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
val lhs = callArgs[0].second
|
||||||
|
val rhs = callArgs[1].second
|
||||||
|
// Both entries should belong to the same class.
|
||||||
|
if (lhs.type != rhs.type) {
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
// If there is nothing on stack then nothing we can do.
|
||||||
|
val (topmostSubject, topmostOrdinalProvider) = subjectWithOrdinalStack.peek()
|
||||||
|
?: return call
|
||||||
|
if (lhs is IrValueAccessExpression && lhs.symbol.owner == topmostSubject && rhs is IrGetEnumValue) {
|
||||||
|
val entryOrdinal = context.specialDeclarationsFactory.getEnumEntryOrdinal(rhs.descriptor)
|
||||||
|
val subjectOrdinal = topmostOrdinalProvider.value
|
||||||
|
return IrCallImpl(call.startOffset, call.endOffset, areEqualByValue).apply {
|
||||||
|
putValueArgument(0, IrGetValueImpl(lhs.startOffset, lhs.endOffset, subjectOrdinal.symbol))
|
||||||
|
putValueArgument(1, IrConstImpl.int(rhs.startOffset, rhs.endOffset, context.builtIns.intType, entryOrdinal))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -44,6 +44,7 @@ extend Property {
|
|||||||
|
|
||||||
extend EnumEntry {
|
extend EnumEntry {
|
||||||
repeated Annotation enum_entry_annotation = 170;
|
repeated Annotation enum_entry_annotation = 170;
|
||||||
|
optional int32 enum_entry_ordinal = 171;
|
||||||
}
|
}
|
||||||
|
|
||||||
extend ValueParameter {
|
extend ValueParameter {
|
||||||
|
|||||||
+3
-1
@@ -48,7 +48,9 @@ internal class KonanSerializerExtension(val context: Context) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun serializeEnumEntry(descriptor: ClassDescriptor, proto: ProtoBuf.EnumEntry.Builder) {
|
override fun serializeEnumEntry(descriptor: ClassDescriptor, proto: ProtoBuf.EnumEntry.Builder) {
|
||||||
|
// Serialization doesn't preserve enum entry order, so we need to serialize ordinal.
|
||||||
|
val ordinal = context.specialDeclarationsFactory.getEnumEntryOrdinal(descriptor)
|
||||||
|
proto.setExtension(KonanLinkData.enumEntryOrdinal, ordinal)
|
||||||
super.serializeEnumEntry(descriptor, proto)
|
super.serializeEnumEntry(descriptor, proto)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -763,6 +763,11 @@ task enum_loop(type: RunKonanTest) {
|
|||||||
source = "codegen/enum/loop.kt"
|
source = "codegen/enum/loop.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task switchLowering(type: RunKonanTest) {
|
||||||
|
goldValue = "EnumA.A\nok\nok\nok\nok\nok\n"
|
||||||
|
source = "codegen/enum/switchLowering.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task mangling(type: LinkKonanTest) {
|
task mangling(type: LinkKonanTest) {
|
||||||
goldValue =
|
goldValue =
|
||||||
"Int direct [1, 2, 3, 4]\n" +
|
"Int direct [1, 2, 3, 4]\n" +
|
||||||
@@ -2420,6 +2425,12 @@ task serialized_no_typemap(type: RunStandaloneKonanTest) {
|
|||||||
goldValue = "OK\n"
|
goldValue = "OK\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task serialized_enum_ordinal(type: LinkKonanTest) {
|
||||||
|
source = "serialization/enum_ordinal/main.kt"
|
||||||
|
lib = "serialization/enum_ordinal/library.kt"
|
||||||
|
goldValue = "0\n1\n2\nb\n"
|
||||||
|
}
|
||||||
|
|
||||||
task testing_annotations(type: RunStandaloneKonanTest) {
|
task testing_annotations(type: RunStandaloneKonanTest) {
|
||||||
source = "testing/annotations.kt"
|
source = "testing/annotations.kt"
|
||||||
flags = ['-tr']
|
flags = ['-tr']
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package codegen.enum.switchLowering
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
enum class EnumA {
|
||||||
|
A, B, C
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class EnumB {
|
||||||
|
A, B
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class E {
|
||||||
|
ONE, TWO, THREE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun produceEntry() = EnumA.A
|
||||||
|
|
||||||
|
// Check that we fail on comparison of different enum types.
|
||||||
|
fun differentEnums() {
|
||||||
|
println(when (produceEntry()) {
|
||||||
|
EnumB.A -> "EnumB.A"
|
||||||
|
EnumA.A -> "EnumA.A"
|
||||||
|
EnumA.B -> "EnumA.B"
|
||||||
|
else -> "nah"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nullable subject shouldn't be lowered.
|
||||||
|
fun nullable() {
|
||||||
|
val x: EnumA? = null
|
||||||
|
when(x) {
|
||||||
|
EnumA.A -> println("fail")
|
||||||
|
else -> println("ok")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operator overloading won't trick us!
|
||||||
|
fun operatorOverloading() {
|
||||||
|
operator fun E.contains(other: E): Boolean = false
|
||||||
|
|
||||||
|
val y = E.ONE
|
||||||
|
when(y) {
|
||||||
|
in E.ONE -> println("Should not reach here")
|
||||||
|
else -> println("ok")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun smoke1() {
|
||||||
|
when (produceEntry()) {
|
||||||
|
EnumA.B -> println("error")
|
||||||
|
EnumA.A -> println("ok")
|
||||||
|
EnumA.C -> println("error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun smoke2() {
|
||||||
|
when (produceEntry()) {
|
||||||
|
EnumA.B -> println("error")
|
||||||
|
else -> println("ok")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun eA() = EnumA.A
|
||||||
|
|
||||||
|
fun eB() = EnumA.B
|
||||||
|
|
||||||
|
|
||||||
|
fun nestedWhen() {
|
||||||
|
println(when (eA()) {
|
||||||
|
EnumA.A, EnumA.C -> when (eB()) {
|
||||||
|
EnumA.B -> "ok"
|
||||||
|
else -> "nope"
|
||||||
|
}
|
||||||
|
else -> "nope"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun runTest() {
|
||||||
|
differentEnums()
|
||||||
|
nullable()
|
||||||
|
operatorOverloading()
|
||||||
|
smoke1()
|
||||||
|
smoke2()
|
||||||
|
nestedWhen()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
enum class Color {
|
||||||
|
RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW
|
||||||
|
}
|
||||||
|
|
||||||
|
fun determineColor(code: Int): Color = when (code) {
|
||||||
|
0 -> Color.BLUE
|
||||||
|
1 -> Color.MAGENTA
|
||||||
|
else -> Color.CYAN
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
println(Color.RED.ordinal)
|
||||||
|
println(Color.GREEN.ordinal)
|
||||||
|
println(Color.BLUE.ordinal)
|
||||||
|
val color = when (determineColor(args.size)) {
|
||||||
|
Color.RED -> println("r")
|
||||||
|
Color.GREEN -> println("g")
|
||||||
|
Color.BLUE -> println("b")
|
||||||
|
Color.CYAN -> println("c")
|
||||||
|
Color.MAGENTA -> println("m")
|
||||||
|
Color.YELLOW -> println("y")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -94,8 +94,7 @@ fun ReportUnhandledException(e: Throwable) {
|
|||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
internal fun TheEmptyString() = ""
|
internal fun TheEmptyString() = ""
|
||||||
|
|
||||||
fun <T: Enum<T>> valueOfForEnum(name: String, values: Array<T>) : T
|
fun <T: Enum<T>> valueOfForEnum(name: String, values: Array<T>) : T {
|
||||||
{
|
|
||||||
var left = 0
|
var left = 0
|
||||||
var right = values.size - 1
|
var right = values.size - 1
|
||||||
while (left <= right) {
|
while (left <= right) {
|
||||||
@@ -110,8 +109,7 @@ fun <T: Enum<T>> valueOfForEnum(name: String, values: Array<T>) : T
|
|||||||
throw Exception("Invalid enum name: $name")
|
throw Exception("Invalid enum name: $name")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T: Enum<T>> valuesForEnum(values: Array<T>): Array<T>
|
fun <T: Enum<T>> valuesForEnum(values: Array<T>): Array<T> {
|
||||||
{
|
|
||||||
val result = @Suppress("TYPE_PARAMETER_AS_REIFIED") Array<T?>(values.size)
|
val result = @Suppress("TYPE_PARAMETER_AS_REIFIED") Array<T?>(values.size)
|
||||||
for (value in values)
|
for (value in values)
|
||||||
result[value.ordinal] = value
|
result[value.ordinal] = value
|
||||||
|
|||||||
Reference in New Issue
Block a user