diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanReflectionTypes.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanReflectionTypes.kt index b454552ce1e..91975dae15b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanReflectionTypes.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanReflectionTypes.kt @@ -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) 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 127ef2071ff..25fca1e401e 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 @@ -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.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 = diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt index 6dca81599de..90a6069c1fe 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt @@ -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() 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. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt index 67e8d9946ae..8a01b32ec2a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt @@ -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, 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 } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt index 0979f584c2a..963926e2369 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionInlining.kt @@ -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) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PostInlineLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PostInlineLowering.kt index efd8bbca3ec..46616b2f434 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PostInlineLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/PostInlineLowering.kt @@ -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 + ) }) } } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReflectionSupport.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReflectionSupport.kt index 1a7ff0a91ac..8370f4caf13 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReflectionSupport.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/ReflectionSupport.kt @@ -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, + 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 + ): 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") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt index 11ae0539121..93a26e6e218 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt @@ -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 diff --git a/runtime/src/main/kotlin/kotlin/native/internal/KTypeImpl.kt b/runtime/src/main/kotlin/kotlin/native/internal/KTypeImpl.kt index 642e761ac91..fb6d8db2c51 100644 --- a/runtime/src/main/kotlin/kotlin/native/internal/KTypeImpl.kt +++ b/runtime/src/main/kotlin/kotlin/native/internal/KTypeImpl.kt @@ -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, + 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 get() = emptyList() + override val isMarkedNullable: Boolean get() = error("Generic types are not yet supported in reflection") diff --git a/runtime/src/main/kotlin/kotlin/reflect/KType.kt b/runtime/src/main/kotlin/kotlin/reflect/KType.kt index 8c286af5dcc..c34d47d3e86 100644 --- a/runtime/src/main/kotlin/kotlin/reflect/KType.kt +++ b/runtime/src/main/kotlin/kotlin/reflect/KType.kt @@ -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` 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.Inner` the returned list is `[C, D, A, B]`. + */ + @SinceKotlin("1.1") + public val arguments: List + /** * `true` if this type was marked nullable in the source code. * @@ -37,4 +48,57 @@ public interface KType { * ``` */ public val isMarkedNullable: Boolean -} \ No newline at end of file +} + +/** + * 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` 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` 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` 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` is a covariant projection of the type of class `Number`. + */ + public fun covariant(type: KType): KTypeProjection = + KTypeProjection(KVariance.OUT, type) + } +} diff --git a/runtime/src/main/kotlin/kotlin/reflect/KVariance.kt b/runtime/src/main/kotlin/kotlin/reflect/KVariance.kt new file mode 100644 index 00000000000..babb04cc40f --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/reflect/KVariance.kt @@ -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, +} \ No newline at end of file