diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSubstitutor.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSubstitutor.kt index 795946150b4..29d5c1ad670 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSubstitutor.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSubstitutor.kt @@ -7,41 +7,29 @@ package org.jetbrains.kotlin.ir.types import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol +import org.jetbrains.kotlin.ir.types.impl.IrCapturedType import org.jetbrains.kotlin.ir.types.impl.buildSimpleType import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.types.impl.toBuilder import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker -class IrTypeSubstitutor( - typeParameters: List, - typeArguments: List, - private val irBuiltIns: IrBuiltIns -): TypeSubstitutorMarker { - init { - assert(typeParameters.size == typeArguments.size) { - "Unexpected number of type arguments: ${typeArguments.size}\n" + - "Type parameters are:\n" + - typeParameters.joinToString(separator = "\n") { it.owner.render() } + - "Type arguments are:\n" + - typeArguments.joinToString(separator = "\n") { it.render() } - } - } +abstract class AbstractIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : TypeSubstitutorMarker { - private val substitution = typeParameters.zip(typeArguments).toMap() private fun IrType.typeParameterConstructor(): IrTypeParameterSymbol? { return if (this is IrSimpleType) classifier as? IrTypeParameterSymbol else null } - private fun getSubstitutionArgument(typeParameter: IrTypeParameterSymbol): IrTypeArgument = - substitution[typeParameter] - ?: throw AssertionError("Unsubstituted type parameter: ${typeParameter.owner.render()}") + abstract fun getSubstitutionArgument(typeParameter: IrTypeParameterSymbol): IrTypeArgument + + abstract fun isEmptySubstitution(): Boolean fun substitute(type: IrType): IrType { - if (substitution.isEmpty()) return type + if (isEmptySubstitution()) return type return type.typeParameterConstructor()?.let { when (val typeArgument = getSubstitutionArgument(it)) { @@ -84,3 +72,53 @@ class IrTypeSubstitutor( return makeTypeProjection(substituteType(typeArgument.type), typeArgument.variance) } } + + +class IrTypeSubstitutor( + typeParameters: List, + typeArguments: List, + irBuiltIns: IrBuiltIns +) : AbstractIrTypeSubstitutor(irBuiltIns) { + + init { + assert(typeParameters.size == typeArguments.size) { + "Unexpected number of type arguments: ${typeArguments.size}\n" + + "Type parameters are:\n" + + typeParameters.joinToString(separator = "\n") { it.owner.render() } + + "Type arguments are:\n" + + typeArguments.joinToString(separator = "\n") { it.render() } + } + } + + private val substitution = typeParameters.zip(typeArguments).toMap() + + override fun getSubstitutionArgument(typeParameter: IrTypeParameterSymbol): IrTypeArgument = + substitution[typeParameter] + ?: throw AssertionError("Unsubstituted type parameter: ${typeParameter.owner.render()}") + + override fun isEmptySubstitution(): Boolean = substitution.isEmpty() +} + +class IrCapturedTypeSubstitutor( + typeParameters: List, + typeArguments: List, + capturedTypes: List, + irBuiltIns: IrBuiltIns +) : AbstractIrTypeSubstitutor(irBuiltIns) { + + init { + assert(typeArguments.size == typeParameters.size) + assert(capturedTypes.size == typeParameters.size) + } + + private val oldSubstitution = typeParameters.zip(typeArguments).toMap() + private val capturedSubstitution = typeParameters.zip(capturedTypes).toMap() + + override fun getSubstitutionArgument(typeParameter: IrTypeParameterSymbol): IrTypeArgument { + return capturedSubstitution[typeParameter]?.let { makeTypeProjection(it, Variance.INVARIANT) } + ?: oldSubstitution[typeParameter] + ?: throw AssertionError("Unsubstituted type parameter: ${typeParameter.owner.render()}") + } + + override fun isEmptySubstitution(): Boolean = oldSubstitution.isEmpty() +} \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt index a832de72003..7eca8a677a8 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt @@ -184,7 +184,7 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon * * In this case Captured Type of `y` would be `Array` * - * See https://jetbrains.github.io/kotlin-spec/#type-capturing + * See https://kotlinlang.org/spec/type-system.html#type-capturing */ override fun captureFromArguments(type: SimpleTypeMarker, status: CaptureStatus): SimpleTypeMarker? { @@ -204,23 +204,49 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon if (typeArguments.all { it is IrTypeProjection && it.variance == Variance.INVARIANT }) return type - val newArguments = mutableListOf() + val capturedTypes = ArrayList(typeArguments.size) + for (index in typeArguments.indices) { - val argument = typeArguments[index] val parameter = typeParameters[index] + val argument = typeArguments[index] if (argument is IrTypeProjection && argument.variance == Variance.INVARIANT) { - newArguments += argument - continue + capturedTypes.add(null) + } else { + val lowerType = if (argument is IrTypeProjection && argument.variance == Variance.IN_VARIANCE) { + argument.type + } else null + + capturedTypes.add(IrCapturedType(status, lowerType, argument, parameter)) } + } - val additionalBounds = - if (argument is IrTypeProjection && argument.variance == Variance.OUT_VARIANCE) listOf(argument.type) else emptyList() + val newArguments = ArrayList(typeArguments.size) - newArguments += makeTypeProjection( - makeTypeIntersection(parameter.superTypes + additionalBounds), - Variance.INVARIANT - ) + val typeSubstitutor = IrCapturedTypeSubstitutor(typeParameters.map { it.symbol }, typeArguments, capturedTypes, irBuiltIns) + + for (index in typeArguments.indices) { + val oldArgument = typeArguments[index] + val parameter = typeParameters[index] + val capturedType = capturedTypes[index] + + if (capturedType == null) { + assert(oldArgument is IrTypeProjection && oldArgument.variance == Variance.INVARIANT) + newArguments.add(oldArgument) + } else { + val capturedSuperTypes = mutableListOf() + parameter.superTypes.mapTo(capturedSuperTypes) { + typeSubstitutor.substitute(it) + } + + if (oldArgument is IrTypeProjection && oldArgument.variance == Variance.OUT_VARIANCE) { + capturedSuperTypes += oldArgument.type + } + + capturedType.constructor.initSuperTypes(capturedSuperTypes) + + newArguments.add(makeTypeProjection(capturedType, Variance.INVARIANT)) + } } return IrSimpleTypeImpl(type.classifier, type.hasQuestionMark, newArguments, type.annotations)