[K/N] Support kType generation as ConstantValue

This commit is contained in:
Pavel Kunyavskiy
2021-07-15 17:11:28 +03:00
committed by Space
parent 504d72011b
commit 694864ae76
9 changed files with 175 additions and 126 deletions
@@ -442,7 +442,6 @@ internal class KonanSymbols(
return symbolTable.referenceEnumEntry(descriptor)
}
val getClassTypeInfo = internalFunction("getClassTypeInfo")
val getObjectTypeInfo = internalFunction("getObjectTypeInfo")
val kClassImpl = internalClass("KClassImpl")
val kClassImplConstructor by lazy { kClassImpl.constructors.single { it.descriptor.isPrimary } }
@@ -453,6 +452,7 @@ internal class KonanSymbols(
val kTypeImpl = internalClass("KTypeImpl")
val kTypeImplIntrinsicConstructor by lazy { kTypeImpl.constructors.single { it.descriptor.valueParameters.isEmpty() } }
val kTypeImplForTypeParametersWithRecursiveBounds = internalClass("KTypeImplForTypeParametersWithRecursiveBounds")
val kTypeProjectionList = internalClass("KTypeProjectionList")
val kTypeProjection = symbolTable.referenceClass(context.reflectionTypes.kTypeProjection)
@@ -484,6 +484,8 @@ internal class KonanSymbols(
val listOf = irBuiltIns.findFunctions(Name.identifier("listOf"), "kotlin", "collections")
.single { it.descriptor.valueParameters.size == 1 && it.descriptor.valueParameters[0].isVararg }
val arrayAsList = internalClass("ArrayAsList")
val threadLocal = symbolTable.referenceClass(
context.builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(KonanFqNames.threadLocal))!!)
@@ -61,7 +61,6 @@ internal enum class IntrinsicType {
OBJC_INIT_BY,
OBJC_GET_SELECTOR,
// Other
GET_CLASS_TYPE_INFO,
CREATE_UNINITIALIZED_INSTANCE,
IDENTITY,
IMMUTABLE_BLOB,
@@ -242,7 +241,6 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
IntrinsicType.OBJC_GET_MESSENGER_STRET -> emitObjCGetMessenger(args, isStret = true)
IntrinsicType.OBJC_GET_OBJC_CLASS -> emitGetObjCClass(callSite)
IntrinsicType.OBJC_CREATE_SUPER_STRUCT -> emitObjCCreateSuperStruct(args)
IntrinsicType.GET_CLASS_TYPE_INFO -> emitGetClassTypeInfo(callSite)
IntrinsicType.INTEROP_READ_BITS -> emitReadBits(args)
IntrinsicType.INTEROP_WRITE_BITS -> emitWriteBits(args)
IntrinsicType.INTEROP_READ_PRIMITIVE -> emitReadPrimitive(callSite, args)
@@ -432,19 +430,6 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
TODO("Implement me")
}
private fun FunctionGenerationContext.emitGetClassTypeInfo(callSite: IrCall): LLVMValueRef {
val typeArgument = callSite.getTypeArgument(0)!!
val typeArgumentClass = typeArgument.getClass()
return if (typeArgumentClass == null) {
// Should not happen anymore, but it is safer to handle this case.
unreachable()
kNullInt8Ptr
} else {
val typeInfo = codegen.typeInfoValue(typeArgumentClass)
LLVMConstBitCast(typeInfo, kInt8Ptr)!!
}
}
private fun FunctionGenerationContext.emitObjCCreateSuperStruct(args: List<LLVMValueRef>): LLVMValueRef {
assert(args.size == 2)
val receiver = args[0]
@@ -10,6 +10,8 @@ import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.llvm.ConstantConstructorIntrinsicType
import org.jetbrains.kotlin.backend.konan.llvm.tryGetConstantConstructorIntrinsicType
import org.jetbrains.kotlin.backend.konan.renderCompilerError
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
@@ -93,17 +95,23 @@ internal class PostInlineLowering(val context: Context) : BodyLoweringPass {
expression.startOffset, expression.endOffset,
context.irBuiltIns.stringType,
IrConstKind.String, builder.toString()))
} else if (Symbols.isTypeOfIntrinsic(expression.symbol)) {
// Inline functions themselves are not called (they have been inlined at all call sites),
// so it is ok not to build exact type parameters for them.
val needExactTypeParameters = (container as? IrSimpleFunction)?.isInline != true
return with (KTypeGenerator(context, irFile, expression, needExactTypeParameters)) {
data.at(expression).irKType(expression.getTypeArgument(0)!!, leaveReifiedForLater = false)
}
}
return expression
}
override fun visitConstantObject(expression: IrConstantObject, data: IrBuilderWithScope): IrConstantValue {
return if (tryGetConstantConstructorIntrinsicType(expression.constructor) == ConstantConstructorIntrinsicType.KTYPE_IMPL) {
// Inline functions themselves are not called (they have been inlined at all call sites),
// so it is ok not to build exact type parameters for them.
val needExactTypeParameters = (container as? IrSimpleFunction)?.isInline != true
with(KTypeGenerator(context, irFile, expression, needExactTypeParameters)) {
data.at(expression).irKType(expression.typeArguments[0], leaveReifiedForLater = false)
}
} else
super.visitConstantObject(expression, data)
}
}, data = context.createIrBuilder((container as IrSymbolOwner).symbol, irBody.startOffset, irBody.endOffset))
}
}
@@ -8,30 +8,26 @@ package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.konan.InteropFqNames
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
import org.jetbrains.kotlin.backend.konan.ir.typeWithStarProjections
import org.jetbrains.kotlin.backend.konan.ir.typeWithoutArguments
import org.jetbrains.kotlin.backend.konan.isObjCClass
import org.jetbrains.kotlin.backend.konan.llvm.computeFullName
import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.types.Variance
private fun IrBuilderWithScope.irConstantString(string: String) = irConstantPrimitive(irString(string))
private fun IrBuilderWithScope.irConstantInt(int: Int) = irConstantPrimitive(irInt(int))
private fun IrBuilderWithScope.irConstantBoolean(boolean: Boolean) = irConstantPrimitive(irBoolean(boolean))
internal class KTypeGenerator(
val context: KonanBackendContext,
val irFile: IrFile,
@@ -49,15 +45,16 @@ internal class KTypeGenerator(
type: IrType,
leaveReifiedForLater: Boolean,
seenTypeParameters: MutableSet<IrTypeParameter>
): IrExpression {
): IrConstantValue {
if (type !is IrSimpleType) {
// Represent as non-denotable type:
return irKTypeImpl(
kClassifier = irNull(),
irTypeArguments = emptyList(),
isMarkedNullable = false,
leaveReifiedForLater = leaveReifiedForLater,
seenTypeParameters = seenTypeParameters
kClassifier = irConstantPrimitive(irNull()),
irTypeArguments = emptyList(),
isMarkedNullable = false,
leaveReifiedForLater = leaveReifiedForLater,
seenTypeParameters = seenTypeParameters,
type = type,
)
}
try {
@@ -66,7 +63,7 @@ internal class KTypeGenerator(
is IrTypeParameterSymbol -> {
if (classifier.owner.isReified && leaveReifiedForLater) {
// Leave as is for reification.
return irCall(symbols.typeOf).apply { putTypeArgument(0, type) }
return irConstantObject(symbols.kTypeImplIntrinsicConstructor, emptyList(), listOf(type))
}
// Leave upper bounds of non-reified type parameters as is, even if they are reified themselves.
@@ -80,26 +77,28 @@ internal class KTypeGenerator(
irTypeArguments = type.arguments,
isMarkedNullable = type.hasQuestionMark,
leaveReifiedForLater = leaveReifiedForLater,
seenTypeParameters = seenTypeParameters
seenTypeParameters = seenTypeParameters,
type = type,
)
} catch (t: RecursiveBoundsException) {
if (needExactTypeParameters)
this@KTypeGenerator.context.reportCompilationError(t.message!!, irFile, irElement)
return irCall(symbols.kTypeImplForTypeParametersWithRecursiveBounds.constructors.single())
return irConstantObject(symbols.kTypeImplForTypeParametersWithRecursiveBounds.owner, emptyMap())
}
}
private fun IrBuilderWithScope.irKTypeImpl(
kClassifier: IrExpression,
irTypeArguments: List<IrTypeArgument>,
isMarkedNullable: Boolean,
leaveReifiedForLater: Boolean,
seenTypeParameters: MutableSet<IrTypeParameter>
): IrExpression = irCall(symbols.kTypeImpl.constructors.single()).apply {
putValueArgument(0, kClassifier)
putValueArgument(1, irKTypeProjectionsList(irTypeArguments, leaveReifiedForLater, seenTypeParameters))
putValueArgument(2, irBoolean(isMarkedNullable))
}
kClassifier: IrConstantValue,
irTypeArguments: List<IrTypeArgument>,
isMarkedNullable: Boolean,
leaveReifiedForLater: Boolean,
seenTypeParameters: MutableSet<IrTypeParameter>,
type: IrType,
): IrConstantValue = irConstantObject(symbols.kTypeImpl.owner, mapOf(
"classifier" to kClassifier,
"arguments" to irKTypeProjectionsList(irTypeArguments, leaveReifiedForLater, seenTypeParameters),
"isMarkedNullable" to irConstantPrimitive(irBoolean(isMarkedNullable)),
), listOf(type))
private fun IrBuilderWithScope.irKClass(symbol: IrClassSymbol) = irKClass(this@KTypeGenerator.context, symbol)
@@ -107,86 +106,80 @@ internal class KTypeGenerator(
typeParameter: IrTypeParameter,
leaveReifiedForLater: Boolean,
seenTypeParameters: MutableSet<IrTypeParameter>
): IrMemberAccessExpression<*> {
): IrConstantValue {
if (!seenTypeParameters.add(typeParameter))
throw RecursiveBoundsException("Non-reified type parameters with recursive bounds are not supported yet: ${typeParameter.render()}")
val result = irCall(symbols.kTypeParameterImpl.constructors.single()).apply {
putValueArgument(0, irString(typeParameter.name.asString()))
putValueArgument(1, irString(typeParameter.parentUniqueName))
putValueArgument(2, irKTypeList(typeParameter.superTypes, leaveReifiedForLater, seenTypeParameters))
putValueArgument(3, irKVariance(typeParameter.variance))
putValueArgument(4, irBoolean(typeParameter.isReified))
}
val result = irConstantObject(symbols.kTypeParameterImpl.owner, mapOf(
"name" to irConstantString(typeParameter.name.asString()),
"containerFqName" to irConstantString(typeParameter.parentUniqueName),
"upperBounds" to irKTypeList(typeParameter.superTypes, leaveReifiedForLater, seenTypeParameters),
"varianceId" to irConstantInt(mapVariance(typeParameter.variance)),
"isReified" to irConstantBoolean(typeParameter.isReified),
))
seenTypeParameters.remove(typeParameter)
return result
}
private val IrTypeParameter.parentUniqueName get() = when (val parent = parent) {
is IrFunction -> parent.computeFullName()
else -> parent.fqNameForIrSerialization.asString()
}
private fun IrBuilderWithScope.irKVariance(variance: Variance) =
IrGetEnumValueImpl(
startOffset, endOffset,
symbols.kVariance.defaultType,
when (variance) {
Variance.INVARIANT -> symbols.kVarianceInvariant
Variance.IN_VARIANCE -> symbols.kVarianceIn
Variance.OUT_VARIANCE -> symbols.kVarianceOut
}
)
private fun <T> IrBuilderWithScope.irKTypeLikeList(
types: List<T>,
itemType: IrType,
itemBuilder: (T) -> IrExpression
) = if (types.isEmpty()) {
irCall(symbols.emptyList, listOf(itemType))
} else {
irCall(symbols.listOf, listOf(itemType)).apply {
putValueArgument(0, IrVarargImpl(
startOffset, endOffset,
type = symbols.array.typeWith(itemType),
varargElementType = itemType,
elements = types.map { itemBuilder(it) }
))
private val IrTypeParameter.parentUniqueName
get() = when (val parent = parent) {
is IrFunction -> parent.computeFullName()
else -> parent.fqNameForIrSerialization.asString()
}
}
private fun IrBuilderWithScope.irKTypeList(
types: List<IrType>,
leaveReifiedForLater: Boolean,
seenTypeParameters: MutableSet<IrTypeParameter>
) = irKTypeLikeList(types, symbols.kType.defaultType) { irKType(it, leaveReifiedForLater, seenTypeParameters) }
): IrConstantValue {
val itemType = symbols.kType.defaultType
val elements = irConstantArray(symbols.array.typeWith(itemType),
types.map { irKType(it, leaveReifiedForLater, seenTypeParameters) }
)
return irConstantObject(symbols.arrayAsList.owner, mapOf(
"array" to elements
))
}
// this constants are copypasted from KVarianceMapper.Companion in KTypeImpl.kt
private fun mapVariance(variance: Variance) = when (variance) {
Variance.INVARIANT -> 0
Variance.IN_VARIANCE -> 1
Variance.OUT_VARIANCE -> 2
}
private fun IrBuilderWithScope.irKTypeProjectionsList(
irTypeArguments: List<IrTypeArgument>,
leaveReifiedForLater: Boolean,
seenTypeParameters: MutableSet<IrTypeParameter>
) = irKTypeLikeList(irTypeArguments, symbols.kTypeProjection.typeWithoutArguments) {
irKTypeProjection(it, leaveReifiedForLater, seenTypeParameters)
}
private fun IrBuilderWithScope.irKTypeProjection(
argument: IrTypeArgument,
leaveReifiedForLater: Boolean,
seenTypeParameters: MutableSet<IrTypeParameter>
) = when (argument) {
is IrTypeProjection -> irCall(symbols.kTypeProjectionFactories.getValue(argument.variance)).apply {
dispatchReceiver = irGetObject(symbols.kTypeProjectionCompanion)
putValueArgument(0, irKType(argument.type, leaveReifiedForLater, seenTypeParameters))
}
is IrStarProjection -> irCall(symbols.kTypeProjectionStar.owner.getter!!).apply {
dispatchReceiver = irGetObject(symbols.kTypeProjectionCompanion)
}
else -> error("Unexpected IrTypeArgument: $argument (${argument::class})")
): IrConstantValue {
val variance = irConstantArray(
symbols.intArrayType,
irTypeArguments.map { argument ->
when (argument) {
is IrStarProjection -> irConstantInt(-1)
is IrTypeProjection -> irConstantInt(mapVariance(argument.variance))
else -> error("Unexpected IrTypeArgument: $argument (${argument::class})")
}
})
val type = irConstantArray(
symbols.array.typeWith(symbols.kType.defaultType.makeNullable()),
irTypeArguments.map { argument ->
when (argument) {
is IrStarProjection -> irConstantPrimitive(irNull())
is IrTypeProjection -> irKType(argument.type, leaveReifiedForLater, seenTypeParameters)
else -> error("Unexpected IrTypeArgument: $argument (${argument::class})")
}
})
return irConstantObject(
symbols.kTypeProjectionList.owner,
mapOf(
"variance" to variance,
"type" to type
))
}
}
internal fun IrBuilderWithScope.irKClass(context: KonanBackendContext, symbol: IrClassSymbol): IrExpression {
internal fun IrBuilderWithScope.irKClass(context: KonanBackendContext, symbol: IrClassSymbol): IrConstantValue {
val symbols = context.ir.symbols
return when {
symbol.descriptor.isObjCClass() ->
@@ -196,13 +189,11 @@ internal fun IrBuilderWithScope.irKClass(context: KonanBackendContext, symbol: I
it is ClassDescriptor && it.fqNameUnsafe == InteropFqNames.nativePointed
} -> irKClassUnsupported(context, "KClass for interop types is not supported yet")
else -> irCall(symbols.kClassImplConstructor.owner).apply {
putValueArgument(0, irCall(symbols.getClassTypeInfo, listOf(symbol.typeWithStarProjections)))
}
else -> irConstantObject(symbols.kClassImplIntrinsicConstructor, emptyList(), listOf(symbol.typeWithStarProjections))
}
}
private fun IrBuilderWithScope.irKClassUnsupported(context: KonanBackendContext, message: String) =
irCall(context.ir.symbols.kClassUnsupportedImplConstructor.owner).apply {
putValueArgument(0, irString(message))
}
irConstantObject(context.ir.symbols.kClassUnsupportedImpl.owner, mapOf(
"message" to irConstantString(message)
))
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.native.internal
class ArrayAsList<T>(val array: Array<T>) : AbstractList<T>(), RandomAccess {
override val size: Int get() = array.size
override fun isEmpty(): Boolean = array.isEmpty()
override fun contains(element: T): Boolean = array.contains(element)
override fun get(index: Int): T = array[index]
override fun indexOf(element: T): Int = array.indexOf(element)
override fun lastIndexOf(element: T): Int = array.lastIndexOf(element)
}
@@ -46,7 +46,6 @@ class IntrinsicType {
const val OBJC_GET_SELECTOR = "OBJC_GET_SELECTOR"
// Other
const val GET_CLASS_TYPE_INFO = "GET_CLASS_TYPE_INFO"
const val INTEROP_READ_BITS = "INTEROP_READ_BITS"
const val INTEROP_WRITE_BITS = "INTEROP_WRITE_BITS"
const val CREATE_UNINITIALIZED_INSTANCE = "CREATE_UNINITIALIZED_INSTANCE"
@@ -78,10 +78,6 @@ private external fun findAssociatedObjectImpl(typeInfo: NativePtr, key: NativePt
@GCUnsafeCall("Kotlin_Any_getTypeInfo")
internal external fun getObjectTypeInfo(obj: Any): NativePtr
@ExportForCompiler
@TypedIntrinsic(IntrinsicType.GET_CLASS_TYPE_INFO)
internal external inline fun <reified T : Any> getClassTypeInfo(): NativePtr
@GCUnsafeCall("Kotlin_TypeInfo_getPackageName")
private external fun getPackageName(typeInfo: NativePtr, checkFlags: Boolean): String?
@@ -7,13 +7,60 @@ package kotlin.native.internal
import kotlin.reflect.*
internal class KTypeImpl(
internal object KVarianceMapper {
// this constants are copypasted to ReflectionSupport.kt
const val VARIANCE_STAR = -1
const val VARIANCE_INVARIANT = 0
const val VARIANCE_IN = 1
const val VARIANCE_OUT = 2
fun idByVariance(variance: KVariance) = when (variance) {
KVariance.INVARIANT -> VARIANCE_INVARIANT
KVariance.IN -> VARIANCE_IN
KVariance.OUT -> VARIANCE_OUT
}
fun varianceById(id: Int) = when (id) {
VARIANCE_STAR -> null
VARIANCE_INVARIANT -> KVariance.INVARIANT
VARIANCE_IN -> KVariance.IN
VARIANCE_OUT -> KVariance.OUT
else -> throw IllegalStateException("Unknown variance id ${id}")
}
}
/*
* This class is used to avoid having enum inside KType class
* Static initialization for enum objects is not supported yet,
* so to initialize KType statically we need to avoid them.
*
* When this issue is resolved, this class can be replaced with just ArrayList
*/
internal class KTypeProjectionList(val variance: IntArray, val type: Array<KType?>) : AbstractList<KTypeProjection>() {
override val size
get() = variance.size
override fun get(index: Int) : KTypeProjection {
AbstractList.checkElementIndex(index, size)
val kVariance = KVarianceMapper.varianceById(variance[index]) ?: return KTypeProjection.STAR
return KTypeProjection(kVariance, type[index])
}
}
internal class KTypeImpl<T>(
override val classifier: KClassifier?,
override val arguments: List<KTypeProjection>,
override val isMarkedNullable: Boolean
) : KType {
@ExportForCompiler
@ConstantConstructorIntrinsic("KTYPE_IMPL")
constructor() : this(null, TODO("This is intrinsic constructor and it shouldn't be used directly"), false)
override fun equals(other: Any?) =
other is KTypeImpl &&
other is KTypeImpl<*> &&
this.classifier == other.classifier &&
this.arguments == other.arguments &&
this.isMarkedNullable == other.isMarkedNullable
@@ -11,9 +11,15 @@ internal class KTypeParameterImpl(
override val name: String,
private val containerFqName: String,
override val upperBounds: List<KType>,
override val variance: KVariance,
val varianceId: Int, // mapping is used to make static initialization possible
override val isReified: Boolean
) : KTypeParameter {
override val variance: KVariance
get() = KVarianceMapper.varianceById(varianceId)!!
constructor(name: String, containerFqName: String, upperBounds: List<KType>, variance: KVariance, isReified: Boolean) :
this(name, containerFqName, upperBounds, KVarianceMapper.idByVariance(variance), isReified)
override fun toString(): String = when (variance) {
KVariance.INVARIANT -> ""
KVariance.IN -> "in "