From 6492682b286b874c9f49344678fc5a6ab7a2673b Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Wed, 29 Nov 2017 18:49:21 +0300 Subject: [PATCH] Supported recursive references in enums Split enum entries initialization onto two steps: 1. Allocation of all instances and storing them in the array; 2. Calling constructors on each instance. --- .../jetbrains/kotlin/backend/konan/ir/Ir.kt | 14 +++- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 14 ++++ .../backend/konan/lower/EnumClassLowering.kt | 84 ++++++++++++++----- .../kotlin/konan/internal/RuntimeUtils.kt | 10 +++ 4 files changed, 100 insertions(+), 22 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index 8a1b2f88d0d..b6a843903ce 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -160,12 +160,24 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym symbol.descriptor to symbolTable.referenceSimpleFunction(functionDescriptor) }.toMap() - val valuesForEnum = symbolTable.referenceSimpleFunction( + val valuesForEnum = symbolTable.referenceSimpleFunction( context.getInternalFunctions("valuesForEnum").single()) val valueOfForEnum = symbolTable.referenceSimpleFunction( context.getInternalFunctions("valueOfForEnum").single()) + val enumValues = symbolTable.referenceSimpleFunction( + builtInsPackage("kotlin").getContributedFunctions(Name.identifier("enumValues"), NoLookupLocation.FROM_BACKEND).single()) + + val enumValueOf = symbolTable.referenceSimpleFunction( + builtInsPackage("kotlin").getContributedFunctions(Name.identifier("enumValueOf"), NoLookupLocation.FROM_BACKEND).single()) + + val createUninitializedInstance = symbolTable.referenceSimpleFunction( + context.getInternalFunctions("createUninitializedInstance").single()) + + val initInstance = symbolTable.referenceSimpleFunction( + context.getInternalFunctions("initInstance").single()) + val getContinuation = symbolTable.referenceSimpleFunction( context.getInternalFunctions("getContinuation").single()) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index d1adea8a496..caddea52b74 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -2050,6 +2050,20 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map { + val typeParameterT = context.ir.symbols.createUninitializedInstance.descriptor.typeParameters[0] + val enumClass = callee.getTypeArgument(typeParameterT)!! + val enumClassDescriptor = enumClass.constructor.declarationDescriptor as ClassDescriptor + functionGenerationContext.allocInstance(typeInfoForAllocation(enumClassDescriptor), lifetimes[callee]!!) + } + + context.ir.symbols.initInstance.descriptor -> { + val initializer = callee.getValueArgument(1) as IrCall + val thiz = evaluateExpression(callee.getValueArgument(0)!!) + evaluateSimpleFunctionCall(initializer.descriptor, listOf(thiz) + evaluateExplicitArgs(initializer), lifetimes[initializer]!!) + codegen.theUnitInstanceRef.llvm + } + else -> TODO(callee.descriptor.original.toString()) } } 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 index 12ee2976f5b..f784b1ac180 100644 --- 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 @@ -25,14 +25,13 @@ import org.jetbrains.kotlin.backend.jvm.descriptors.createValueParameter import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_ENUM import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName -import org.jetbrains.kotlin.backend.common.ir.createArrayOfExpression import org.jetbrains.kotlin.backend.common.ir.createFakeOverrideDescriptor import org.jetbrains.kotlin.backend.common.ir.addSimpleDelegatingConstructor +import org.jetbrains.kotlin.backend.common.ir.createArrayOfExpression import org.jetbrains.kotlin.backend.common.ir.createSimpleDelegatingConstructorDescriptor import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.* @@ -42,7 +41,6 @@ import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.util.* 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.module import org.jetbrains.kotlin.types.TypeProjectionImpl @@ -118,30 +116,32 @@ internal class EnumUsageLowering(val context: Context) override fun visitCall(expression: IrCall): IrExpression { expression.transformChildrenVoid(this) + val descriptor = expression.descriptor as? FunctionDescriptor - if (descriptor == null) return expression - if (descriptor.original != enumValuesDescriptor && descriptor.original != enumValueOfDescriptor) return expression + ?: return expression + if (descriptor.original != enumValuesDescriptor && descriptor.original != enumValueOfDescriptor) + return expression val genericT = descriptor.original.typeParameters[0] val substitutedT = expression.getTypeArgument(genericT)!! val classDescriptor = substitutedT.constructor.declarationDescriptor as? ClassDescriptor - if (classDescriptor == null) return expression // Type parameter. + ?: return expression // Type parameter. assert (classDescriptor.kind == ClassKind.ENUM_CLASS) - if (descriptor.original == enumValuesDescriptor) { - return enumSyntheticFunctionsBuilder.buildValuesExpression(expression.startOffset, expression.endOffset, classDescriptor) - } else { - val value = expression.getValueArgument(0)!! - return enumSyntheticFunctionsBuilder.buildValueOfExpression(expression.startOffset, expression.endOffset, classDescriptor, value) - } + return if (descriptor.original == enumValuesDescriptor) { + enumSyntheticFunctionsBuilder.buildValuesExpression(expression.startOffset, expression.endOffset, classDescriptor) + } else { + val value = expression.getValueArgument(0)!! + enumSyntheticFunctionsBuilder.buildValueOfExpression(expression.startOffset, expression.endOffset, classDescriptor, value) + } } - private val kotlinPackageMemberScope = context.builtIns.builtInsModule.getPackage(FqName("kotlin")).memberScope - private val enumValueOfDescriptor = kotlinPackageMemberScope.getContributedFunctions( - Name.identifier("enumValueOf"), NoLookupLocation.FROM_BACKEND).single() - private val enumValuesDescriptor = kotlinPackageMemberScope.getContributedFunctions( - Name.identifier("enumValues"), NoLookupLocation.FROM_BACKEND).single() + private val enumValueOfSymbol = context.ir.symbols.enumValueOf + private val enumValueOfDescriptor = enumValueOfSymbol.descriptor + + private val enumValuesSymbol = context.ir.symbols.enumValues + private val enumValuesDescriptor = enumValuesSymbol.descriptor private fun loadEnumEntry(startOffset: Int, endOffset: Int, enumClassDescriptor: ClassDescriptor, name: Name): IrExpression { val loweredEnum = context.specialDeclarationsFactory.getLoweredEnum(enumClassDescriptor) @@ -326,16 +326,30 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { DECLARATION_ORIGIN_ENUM) implObject.declarations.add(createSyntheticValuesPropertyDeclaration(enumEntries)) + implObject.declarations.add(createValuesPropertyInitializer(enumEntries)) irClass.declarations.add(implObject) } + private val genericCreateUninitializedInstanceSymbol = context.ir.symbols.createUninitializedInstance + private val genericCreateUninitializedInstanceDescriptor = genericCreateUninitializedInstanceSymbol.descriptor + private fun createSyntheticValuesPropertyDeclaration(enumEntries: List): IrPropertyImpl { val startOffset = irClass.startOffset val endOffset = irClass.endOffset - val irValuesInitializer = context.createArrayOfExpression(irClass.descriptor.defaultType, - enumEntries.sortedBy { it.descriptor.name }.map { it.initializerExpression!! }, startOffset, endOffset) + val irValuesInitializer = context.createArrayOfExpression(irClass.descriptor.defaultType, + enumEntries + .sortedBy { it.descriptor.name } + .map { + val enumEntryClass = ((it.initializerExpression!! as IrCall).descriptor as ConstructorDescriptor).constructedClass + val typeParameterT = genericCreateUninitializedInstanceDescriptor.typeParameters[0] + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumEntryClass.defaultType))) + val substitutedCreateUninitializedInstance = genericCreateUninitializedInstanceDescriptor.substitute(typeSubstitutor)!! + IrCallImpl(startOffset, endOffset, + genericCreateUninitializedInstanceSymbol, substitutedCreateUninitializedInstance, mapOf(typeParameterT to enumEntryClass.defaultType) + ) + }, startOffset, endOffset) val irField = loweredEnum.valuesField.apply { initializer = IrExpressionBodyImpl(startOffset, endOffset, irValuesInitializer) } @@ -348,9 +362,37 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { val returnStatement = IrReturnImpl(startOffset, endOffset, loweredEnum.valuesGetter.symbol, value) getter.body = IrBlockBodyImpl(startOffset, endOffset, listOf(returnStatement)) - val irProperty = IrPropertyImpl(startOffset, endOffset, DECLARATION_ORIGIN_ENUM, + return IrPropertyImpl(startOffset, endOffset, DECLARATION_ORIGIN_ENUM, false, loweredEnum.valuesField.descriptor, irField, getter, null) - return irProperty + } + + private val initInstanceSymbol = context.ir.symbols.initInstance + + private val arrayGetSymbol = context.ir.symbols.array.functions.single { it.descriptor.name == Name.identifier("get") } + + private fun createValuesPropertyInitializer(enumEntries: List): IrAnonymousInitializerImpl { + val startOffset = irClass.startOffset + val endOffset = irClass.endOffset + + val statements = enumEntries + .sortedBy { it.descriptor.name } + .withIndex() + .map { + val instances = IrGetFieldImpl(startOffset, endOffset, + loweredEnum.valuesField.symbol, IrGetValueImpl(startOffset, endOffset, loweredEnum.implObject.thisReceiver!!.symbol)) + val instance = IrCallImpl(startOffset, endOffset, arrayGetSymbol).apply { + dispatchReceiver = instances + putValueArgument(0, IrConstImpl(startOffset, endOffset, context.builtIns.intType, IrConstKind.Int, it.index)) + } + val initializer = it.value.initializerExpression!! as IrCall + IrCallImpl(startOffset, endOffset, initInstanceSymbol).apply { + putValueArgument(0, instance) + putValueArgument(1, initializer) + } + } + return IrAnonymousInitializerImpl(startOffset, endOffset, DECLARATION_ORIGIN_ENUM, + loweredEnum.implObject.descriptor, IrBlockBodyImpl(startOffset, endOffset, statements) + ) } private fun createSyntheticValuesMethodBody(declaration: IrFunction): IrBody { diff --git a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt index 76acdd979f5..5aabd5508cf 100644 --- a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt +++ b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt @@ -105,6 +105,16 @@ fun > valuesForEnum(values: Array): Array return result as Array } +@Intrinsic +internal fun createUninitializedInstance(): T { + throw Exception("Call to this function should've been lowered") +} + +@Intrinsic +internal fun initInstance(thiz: Any, constructorCall: Any): Unit { + throw Exception("Call to this function should've been lowered") +} + fun checkProgressionStep(step: Int) = if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.") fun checkProgressionStep(step: Long) = if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")