From afb7accd5b74c9f9f95b69dd031174ecd0f4cb55 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Mon, 27 Sep 2021 18:47:43 +0300 Subject: [PATCH] [IR] Support erasure of recursive non-reified type parameters In case of `inline fun > foo(a: Any) = a as T` `T` is being erased to `I<*>` Also substitute class upper bound Fix KT-47342 See KT-31072 for more details --- .../inline/DeepCopyIrTreeWithDescriptors.kt | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/DeepCopyIrTreeWithDescriptors.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/DeepCopyIrTreeWithDescriptors.kt index 9f76c576905..f661be20651 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/DeepCopyIrTreeWithDescriptors.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/DeepCopyIrTreeWithDescriptors.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.types.impl.buildSimpleType import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.util.* @@ -66,28 +67,54 @@ internal class DeepCopyIrTreeWithSymbolsForInliner( override fun leaveScope() {} - private fun remapTypeArguments(arguments: List, erase: Boolean) = + private fun remapTypeArguments( + arguments: List, + erasedParameters: MutableSet? + ) = arguments.map { argument -> - (argument as? IrTypeProjection)?.let { makeTypeProjection(remapTypeAndOptionallyErase(it.type, erase), it.variance) } + (argument as? IrTypeProjection)?.let { proj -> + remapTypeAndOptionallyErase(proj.type, erasedParameters)?.let { newType -> + makeTypeProjection(newType, proj.variance) + } ?: IrStarProjectionImpl + } ?: argument } override fun remapType(type: IrType) = remapTypeAndOptionallyErase(type, erase = false) fun remapTypeAndOptionallyErase(type: IrType, erase: Boolean): IrType { + val erasedParams = if (erase) mutableSetOf() else null + return remapTypeAndOptionallyErase(type, erasedParams) ?: error("Cannot substitute type ${type.render()}") + } + + private fun remapTypeAndOptionallyErase(type: IrType, erasedParameters: MutableSet?): IrType? { if (type !is IrSimpleType) return type val classifier = type.classifier val substitutedType = typeArguments?.get(classifier) // Erase non-reified type parameter if asked to. - if (erase && substitutedType != null && (classifier as? IrTypeParameterSymbol)?.owner?.isReified == false) { + if (erasedParameters != null && substitutedType != null && (classifier as? IrTypeParameterSymbol)?.owner?.isReified == false) { + + if (classifier in erasedParameters) { + return null + } + + erasedParameters.add(classifier) + // Pick the (necessarily unique) non-interface upper bound if it exists. - val superClass = classifier.owner.superTypes.firstOrNull { + val superTypes = classifier.owner.superTypes + val superClass = superTypes.firstOrNull { it.classOrNull?.owner?.isInterface == false } - val erasedUpperBound = superClass - ?: remapTypeAndOptionallyErase(classifier.owner.superTypes.first(), erase = true) + + val upperBound = superClass ?: superTypes.first() + + // TODO: Think about how to reduce complexity from k^N to N^k + val erasedUpperBound = remapTypeAndOptionallyErase(upperBound, erasedParameters) + ?: error("Cannot erase upperbound ${upperBound.render()}") + + erasedParameters.remove(classifier) return if (type.hasQuestionMark) erasedUpperBound.makeNullable() else erasedUpperBound } @@ -104,7 +131,7 @@ internal class DeepCopyIrTreeWithSymbolsForInliner( return type.buildSimpleType { kotlinType = null this.classifier = symbolRemapper.getReferencedClassifier(classifier) - arguments = remapTypeArguments(type.arguments, erase) + arguments = remapTypeArguments(type.arguments, erasedParameters) annotations = type.annotations.map { it.transform(copier, null) as IrConstructorCall } } }