[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.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)