[IR] Fix IrType equality in case of complex variance
- reimplement type capturing on top of pure IrCaptureType - add captured type substitutor - fix #KT-43831
This commit is contained in:
@@ -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<IrTypeParameterSymbol>,
|
||||
typeArguments: List<IrTypeArgument>,
|
||||
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<IrTypeParameterSymbol>,
|
||||
typeArguments: List<IrTypeArgument>,
|
||||
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<IrTypeParameterSymbol>,
|
||||
typeArguments: List<IrTypeArgument>,
|
||||
capturedTypes: List<IrCapturedType?>,
|
||||
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()
|
||||
}
|
||||
@@ -184,7 +184,7 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon
|
||||
*
|
||||
* In this case Captured Type of `y` would be `Array<CharSequence>`
|
||||
*
|
||||
* 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<IrTypeArgument>()
|
||||
val capturedTypes = ArrayList<IrCapturedType?>(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<IrTypeArgument>(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<IrType>()
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user