[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:
Roman Artemev
2021-02-05 17:21:56 +03:00
parent 4ed93d3dee
commit 79f986bb75
2 changed files with 94 additions and 30 deletions
@@ -7,41 +7,29 @@ package org.jetbrains.kotlin.ir.types
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol 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.buildSimpleType
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.types.impl.toBuilder import org.jetbrains.kotlin.ir.types.impl.toBuilder
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
class IrTypeSubstitutor( abstract class AbstractIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : TypeSubstitutorMarker {
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() }
}
}
private val substitution = typeParameters.zip(typeArguments).toMap()
private fun IrType.typeParameterConstructor(): IrTypeParameterSymbol? { private fun IrType.typeParameterConstructor(): IrTypeParameterSymbol? {
return if (this is IrSimpleType) classifier as? IrTypeParameterSymbol return if (this is IrSimpleType) classifier as? IrTypeParameterSymbol
else null else null
} }
private fun getSubstitutionArgument(typeParameter: IrTypeParameterSymbol): IrTypeArgument = abstract fun getSubstitutionArgument(typeParameter: IrTypeParameterSymbol): IrTypeArgument
substitution[typeParameter]
?: throw AssertionError("Unsubstituted type parameter: ${typeParameter.owner.render()}") abstract fun isEmptySubstitution(): Boolean
fun substitute(type: IrType): IrType { fun substitute(type: IrType): IrType {
if (substitution.isEmpty()) return type if (isEmptySubstitution()) return type
return type.typeParameterConstructor()?.let { return type.typeParameterConstructor()?.let {
when (val typeArgument = getSubstitutionArgument(it)) { when (val typeArgument = getSubstitutionArgument(it)) {
@@ -84,3 +72,53 @@ class IrTypeSubstitutor(
return makeTypeProjection(substituteType(typeArgument.type), typeArgument.variance) 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>` * 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? { 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 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) { for (index in typeArguments.indices) {
val argument = typeArguments[index]
val parameter = typeParameters[index] val parameter = typeParameters[index]
val argument = typeArguments[index]
if (argument is IrTypeProjection && argument.variance == Variance.INVARIANT) { if (argument is IrTypeProjection && argument.variance == Variance.INVARIANT) {
newArguments += argument capturedTypes.add(null)
continue } 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 = val newArguments = ArrayList<IrTypeArgument>(typeArguments.size)
if (argument is IrTypeProjection && argument.variance == Variance.OUT_VARIANCE) listOf(argument.type) else emptyList()
newArguments += makeTypeProjection( val typeSubstitutor = IrCapturedTypeSubstitutor(typeParameters.map { it.symbol }, typeArguments, capturedTypes, irBuiltIns)
makeTypeIntersection(parameter.superTypes + additionalBounds),
Variance.INVARIANT 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) return IrSimpleTypeImpl(type.classifier, type.hasQuestionMark, newArguments, type.annotations)