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.
This commit is contained in:
Igor Chevdar
2017-11-29 18:49:21 +03:00
parent faa9986489
commit 6492682b28
4 changed files with 100 additions and 22 deletions
@@ -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())
@@ -2050,6 +2050,20 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
}
}
context.ir.symbols.createUninitializedInstance.descriptor -> {
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())
}
}
@@ -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<IrEnumEntry>): 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<IrEnumEntry>): 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 {
@@ -105,6 +105,16 @@ fun <T: Enum<T>> valuesForEnum(values: Array<T>): Array<T>
return result as Array<T>
}
@Intrinsic
internal fun <T> 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.")