Add KType.arguments and support 'typeOf' intrinsic

#KT-29917 Fixed
 #KT-28625 Fixed

Also perform minor refactoring.
This commit is contained in:
Svyatoslav Scherbina
2019-06-17 16:29:57 +03:00
committed by SvyatoslavScherbina
parent f704fd44af
commit e0d6a4c70e
11 changed files with 277 additions and 44 deletions
@@ -41,6 +41,7 @@ class KonanReflectionTypes(module: ModuleDescriptor, internalPackage: FqName) {
val kMutableProperty0: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kMutableProperty1: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kMutableProperty2: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kTypeProjection: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kFunctionImpl: ClassDescriptor by ClassLookup(internalScope)
val kProperty0Impl: ClassDescriptor by ClassLookup(internalScope)
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
@@ -30,8 +29,10 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.Variance
import kotlin.properties.Delegates
// This is what Context collects about IR.
@@ -249,8 +250,11 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
{ findArrayExtension(it.descriptor, "contentHashCode") }
)
private val kotlinCollectionsPackageScope: MemberScope
get() = builtInsPackage("kotlin", "collections")
private fun findArrayExtension(descriptor: ClassDescriptor, name: String): IrSimpleFunctionSymbol {
val functionDescriptor = builtInsPackage("kotlin", "collections")
val functionDescriptor = kotlinCollectionsPackageScope
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
.singleOrNull {
it.valueParameters.isEmpty()
@@ -403,6 +407,41 @@ internal class KonanSymbols(context: Context, private val symbolTable: SymbolTab
val kTypeImpl = internalClass("KTypeImpl")
val kTypeImplForGenerics = internalClass("KTypeImplForGenerics")
val kTypeProjection = symbolTable.referenceClass(context.reflectionTypes.kTypeProjection)
private val kTypeProjectionCompanionDescriptor = context.reflectionTypes.kTypeProjection.companionObjectDescriptor!!
val kTypeProjectionCompanion = symbolTable.referenceClass(kTypeProjectionCompanionDescriptor)
val kTypeProjectionStar = symbolTable.referenceProperty(
kTypeProjectionCompanionDescriptor.unsubstitutedMemberScope
.getContributedVariables(Name.identifier("STAR"), NoLookupLocation.FROM_BACKEND).single()
)
val kTypeProjectionFactories: Map<Variance, IrSimpleFunctionSymbol> = Variance.values().toList().associateWith {
val factoryName = when (it) {
Variance.INVARIANT -> "invariant"
Variance.IN_VARIANCE -> "contravariant"
Variance.OUT_VARIANCE -> "covariant"
}
symbolTable.referenceSimpleFunction(
kTypeProjectionCompanionDescriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier(factoryName), NoLookupLocation.FROM_BACKEND).single()
)
}
val emptyList = symbolTable.referenceSimpleFunction(
kotlinCollectionsPackageScope
.getContributedFunctions(Name.identifier("emptyList"), NoLookupLocation.FROM_BACKEND)
.single { it.valueParameters.isEmpty() }
)
val listOf = symbolTable.referenceSimpleFunction(
kotlinCollectionsPackageScope
.getContributedFunctions(Name.identifier("listOf"), NoLookupLocation.FROM_BACKEND)
.single { it.valueParameters.size == 1 && it.valueParameters[0].isVararg }
)
val listOfInternal = internalFunction("listOfInternal")
val threadLocal =
@@ -40,6 +40,8 @@ internal class CallableReferenceLowering(val context: Context): FileLoweringPass
private object DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL : IrDeclarationOriginImpl("FUNCTION_REFERENCE_IMPL")
private val kTypeGenerator = KTypeGenerator(context)
override fun lower(irFile: IrFile) {
var generatedClasses = mutableListOf<IrClass>()
irFile.transform(object: IrElementTransformerVoidWithContext() {
@@ -266,7 +268,7 @@ internal class CallableReferenceLowering(val context: Context): FileLoweringPass
val needReceiver = boundFunctionParameters.singleOrNull()?.descriptor is ReceiverParameterDescriptor
val receiver = if (needReceiver) irGet(valueParameters.single()) else irNull()
putValueArgument(3, receiver)
putValueArgument(4, irKType(this@CallableReferenceLowering.context, referencedFunction.returnType))
putValueArgument(4, with(kTypeGenerator) { irKType(referencedFunction.returnType) })
}
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, irBuiltIns.unitType)
// Save all arguments to fields.
@@ -34,6 +34,8 @@ import org.jetbrains.kotlin.name.Name
internal class PropertyDelegationLowering(val context: Context) : FileLoweringPass {
private var tempIndex = 0
private val kTypeGenerator = KTypeGenerator(context)
private fun getKPropertyImplConstructor(receiverTypes: List<IrType>,
returnType: IrType,
isLocal: Boolean,
@@ -253,7 +255,7 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
isMutable = setterCallableReference != null)
val initializer = irCall(symbol.owner, constructorTypeArguments).apply {
putValueArgument(0, irString(propertyDescriptor.name.asString()))
putValueArgument(1, irKType(this@PropertyDelegationLowering.context, returnType))
putValueArgument(1, with(kTypeGenerator) { irKType(returnType) })
if (getterCallableReference != null)
putValueArgument(2, getterCallableReference)
if (setterCallableReference != null)
@@ -274,7 +276,7 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
isMutable = false)
val initializer = irCall(symbol.owner, constructorTypeArguments).apply {
putValueArgument(0, irString(propertyDescriptor.name.asString()))
putValueArgument(1, irKType(this@PropertyDelegationLowering.context, propertyType))
putValueArgument(1, with(kTypeGenerator) { irKType(propertyType) })
}
return initializer
}
@@ -47,6 +47,8 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
return expression
if (Symbols.isLateinitIsInitializedPropertyGetter(callee.symbol))
return expression
if (callee.isTypeOfIntrinsic())
return expression
val actualCallee = getFunctionDeclaration(callee.symbol)
@@ -9,12 +9,16 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.error
import org.jetbrains.kotlin.backend.konan.ir.typeWithoutArguments
import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.isTypeOfIntrinsic
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
/**
@@ -26,23 +30,32 @@ internal class PostInlineLowering(val context: Context) : FileLoweringPass {
private val symbols get() = context.ir.symbols
private val kTypeGenerator = KTypeGenerator(
context,
eraseTypeParameters = true // Mimic JVM BE behaviour until proper type parameter impl is ready.
)
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitClassReference(expression: IrClassReference): IrExpression {
expression.transformChildrenVoid()
val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol,
expression.startOffset, expression.endOffset)
val builder = createIrBuilder(expression)
return builder.irKClass(context, expression.symbol)
val symbol = expression.symbol
return if (symbol is IrClassSymbol) {
builder.irKClass(context, symbol)
} else {
// E.g. for `T::class` in a body of an inline function itself.
builder.irCall(context.ir.symbols.ThrowNullPointerException.owner)
}
}
override fun visitGetClass(expression: IrGetClass): IrExpression {
expression.transformChildrenVoid()
val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol,
expression.startOffset, expression.endOffset)
val builder = createIrBuilder(expression)
val typeArgument = expression.argument.type
@@ -91,10 +104,20 @@ internal class PostInlineLowering(val context: Context) : FileLoweringPass {
expression.startOffset, expression.endOffset,
context.ir.symbols.immutableBlob.typeWithoutArguments,
IrConstKind.String, builder.toString()))
} else if (expression.symbol.owner.isTypeOfIntrinsic()) {
val type = expression.getTypeArgument(0)
?: error(irFile, expression, "missing type argument")
return with (kTypeGenerator) { createIrBuilder(expression).irKType(type) }
}
return expression
}
private fun createIrBuilder(element: IrElement) = context.createIrBuilder(
currentScope!!.scope.scopeOwnerSymbol,
element.startOffset,
element.endOffset
)
})
}
}
@@ -8,50 +8,102 @@ 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.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irBoolean
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irString
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.isMarkedNullable
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
internal fun IrBuilderWithScope.irKType(context: KonanBackendContext, type: IrType): IrExpression {
val kTypeImplSymbol = context.ir.symbols.kTypeImpl
val kTypeImplForGenericsSymbol = context.ir.symbols.kTypeImplForGenerics
internal class KTypeGenerator(
private val context: KonanBackendContext,
private val eraseTypeParameters: Boolean = false
) {
private val symbols = context.ir.symbols
val kTypeImplConstructorSymbol = kTypeImplSymbol.constructors.single()
val kTypeImplForGenericsConstructorSymbol = kTypeImplForGenericsSymbol.constructors.single()
val classifierOrNull = type.classifierOrNull
return if (classifierOrNull !is IrClassSymbol) {
// IrTypeParameterSymbol
irCall(kTypeImplForGenericsConstructorSymbol)
fun IrBuilderWithScope.irKType(type: IrType): IrExpression = if (type !is IrSimpleType) {
// Represent as non-denotable type:
irKTypeImpl(
kClassifier = irNull(),
irTypeArguments = emptyList(),
isMarkedNullable = false
)
} else {
val returnKClass = irKClass(context, classifierOrNull)
irCall(kTypeImplConstructorSymbol).apply {
putValueArgument(0, returnKClass)
putValueArgument(1, irBoolean(type.isMarkedNullable()))
val classifier = type.classifier
if (classifier is IrClassSymbol) {
irKTypeImpl(
kClassifier = irKClass(classifier),
irTypeArguments = type.arguments,
isMarkedNullable = type.hasQuestionMark
)
} else {
if (eraseTypeParameters) {
irKType(context.irBuiltIns.anyNType)
} else {
irCall(symbols.kTypeImplForGenerics.constructors.single())
}
}
}
private fun IrBuilderWithScope.irKTypeImpl(
kClassifier: IrExpression,
irTypeArguments: List<IrTypeArgument>,
isMarkedNullable: Boolean
): IrExpression = irCall(symbols.kTypeImpl.constructors.single()).apply {
putValueArgument(0, kClassifier)
putValueArgument(1, irKTypeProjectionsList(irTypeArguments))
putValueArgument(2, irBoolean(isMarkedNullable))
}
private fun IrBuilderWithScope.irKClass(symbol: IrClassSymbol) = irKClass(this@KTypeGenerator.context, symbol)
private fun IrBuilderWithScope.irKTypeProjectionsList(
irTypeArguments: List<IrTypeArgument>
): IrMemberAccessExpression {
val kTypeProjectionType = symbols.kTypeProjection.typeWithoutArguments
return if (irTypeArguments.isEmpty()) {
irCall(symbols.emptyList, listOf(kTypeProjectionType))
} else {
irCall(symbols.listOf, listOf(kTypeProjectionType)).apply {
putValueArgument(0, IrVarargImpl(
startOffset,
endOffset,
type = symbols.array.typeWith(kTypeProjectionType),
varargElementType = kTypeProjectionType,
elements = irTypeArguments.map { irKTypeProjection(it) }
))
}
}
}
private fun IrBuilderWithScope.irKTypeProjection(argument: IrTypeArgument): IrExpression {
return when (argument) {
is IrTypeProjection -> irCall(symbols.kTypeProjectionFactories.getValue(argument.variance)).apply {
dispatchReceiver = irGetObject(symbols.kTypeProjectionCompanion)
putValueArgument(0, irKType(argument.type))
}
is IrStarProjection -> irCall(symbols.kTypeProjectionStar.owner.getter!!).apply {
dispatchReceiver = irGetObject(symbols.kTypeProjectionCompanion)
}
else -> error("Unexpected IrTypeArgument: $argument (${argument::class})")
}
}
}
internal fun IrBuilderWithScope.irKClass(context: KonanBackendContext, symbol: IrClassifierSymbol): IrExpression {
internal fun IrBuilderWithScope.irKClass(context: KonanBackendContext, symbol: IrClassSymbol): IrExpression {
val symbols = context.ir.symbols
return when {
symbol !is IrClassSymbol -> // E.g. for `T::class` in a body of an inline function itself.
irCall(symbols.ThrowNullPointerException.owner)
symbol.descriptor.isObjCClass() ->
irKClassUnsupported(context, "KClass for Objective-C classes is not supported yet")
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.backend.konan.KonanCompilationException
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.konan.ir.allParameters
import org.jetbrains.kotlin.backend.konan.ir.containsNull
import org.jetbrains.kotlin.builtins.KOTLIN_REFLECT_FQ_NAME
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -434,3 +435,8 @@ fun IrClass.defaultOrNullableType(hasQuestionMark: Boolean) =
fun IrFunction.isRestrictedSuspendFunction(languageVersionSettings: LanguageVersionSettings): Boolean =
this.descriptor.extensionReceiverParameter?.type?.isRestrictsSuspensionReceiver(languageVersionSettings) == true
fun IrFunction.isTypeOfIntrinsic(): Boolean =
this.name.asString() == "typeOf" &&
this.valueParameters.isEmpty() &&
(this.parent as? IrPackageFragment)?.fqName == KOTLIN_REFLECT_FQ_NAME
@@ -5,15 +5,21 @@
package kotlin.native.internal
import kotlin.reflect.KClassifier
import kotlin.reflect.KType
import kotlin.reflect.*
internal class KTypeImpl(override val classifier: KClassifier?, override val isMarkedNullable: Boolean) : KType {
internal class KTypeImpl(
override val classifier: KClassifier?,
override val arguments: List<KTypeProjection>,
override val isMarkedNullable: Boolean
) : KType {
override fun equals(other: Any?) =
other is KTypeImpl && classifier == other.classifier && isMarkedNullable == other.isMarkedNullable
other is KTypeImpl &&
this.classifier == other.classifier &&
this.arguments == other.arguments &&
this.isMarkedNullable == other.isMarkedNullable
override fun hashCode(): Int {
return (classifier?.hashCode() ?: 0) * 31 + if (isMarkedNullable) 1 else 0
return (classifier?.hashCode() ?: 0) * 31 * 31 + this.arguments.hashCode() * 31 + if (isMarkedNullable) 1 else 0
}
}
@@ -21,6 +27,8 @@ internal class KTypeImplForGenerics : KType {
override val classifier: KClassifier?
get() = error("Generic types are not yet supported in reflection")
override val arguments: List<KTypeProjection> get() = emptyList()
override val isMarkedNullable: Boolean
get() = error("Generic types are not yet supported in reflection")
@@ -19,6 +19,17 @@ public interface KType {
@SinceKotlin("1.1")
public val classifier: KClassifier?
/**
* Type arguments passed for the parameters of the classifier in this type.
* For example, in the type `Array<out Number>` the only type argument is `out Number`.
*
* In case this type is based on an inner class, the returned list contains the type arguments provided for the innermost class first,
* then its outer class, and so on.
* For example, in the type `Outer<A, B>.Inner<C, D>` the returned list is `[C, D, A, B]`.
*/
@SinceKotlin("1.1")
public val arguments: List<KTypeProjection>
/**
* `true` if this type was marked nullable in the source code.
*
@@ -37,4 +48,57 @@ public interface KType {
* ```
*/
public val isMarkedNullable: Boolean
}
}
/**
* Represents a type projection. Type projection is usually the argument to another type in a type usage.
* For example, in the type `Array<out Number>`, `out Number` is the covariant projection of the type represented by the class `Number`.
*
* Type projection is either the star projection, or an entity consisting of a specific type plus optional variance.
*
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/generics.html#type-projections)
* for more information.
*/
@SinceKotlin("1.1")
public data class KTypeProjection constructor(
/**
* The use-site variance specified in the projection, or `null` if this is a star projection.
*/
public val variance: KVariance?,
/**
* The type specified in the projection, or `null` if this is a star projection.
*/
public val type: KType?
) {
public companion object {
/**
* Star projection, denoted by the `*` character.
* For example, in the type `KClass<*>`, `*` is the star projection.
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/generics.html#star-projections)
* for more information.
*/
public val STAR: KTypeProjection = KTypeProjection(null, null)
/**
* Creates an invariant projection of a given type. Invariant projection is just the type itself,
* without any use-site variance modifiers applied to it.
* For example, in the type `Set<String>`, `String` is an invariant projection of the type represented by the class `String`.
*/
public fun invariant(type: KType): KTypeProjection =
KTypeProjection(KVariance.INVARIANT, type)
/**
* Creates a contravariant projection of a given type, denoted by the `in` modifier applied to a type.
* For example, in the type `MutableList<in Number>`, `in Number` is a contravariant projection of the type of class `Number`.
*/
public fun contravariant(type: KType): KTypeProjection =
KTypeProjection(KVariance.IN, type)
/**
* Creates a covariant projection of a given type, denoted by the `out` modifier applied to a type.
* For example, in the type `Array<out Number>`, `out Number` is a covariant projection of the type of class `Number`.
*/
public fun covariant(type: KType): KTypeProjection =
KTypeProjection(KVariance.OUT, type)
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package kotlin.reflect
/**
* Represents variance applied to a type parameter on the declaration site (*declaration-site variance*),
* or to a type in a projection (*use-site variance*).
*
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/generics.html#variance)
* for more information.
*
* @see [KTypeParameter.variance]
* @see [KTypeProjection]
*/
@SinceKotlin("1.1")
enum class KVariance {
/**
* The affected type parameter or type is *invariant*, which means it has no variance applied to it.
*/
INVARIANT,
/**
* The affected type parameter or type is *contravariant*. Denoted by the `in` modifier in the source code.
*/
IN,
/**
* The affected type parameter or type is *covariant*. Denoted by the `out` modifier in the source code.
*/
OUT,
}