From 41ce88cb9b8e32da6121e79c229b2563f46fd161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Sch=C3=A4fer?= Date: Mon, 20 May 2019 13:12:28 +0200 Subject: [PATCH] Move eraseTypeParameters to IrUtils and use it in PromisedValue.coerce --- .../backend/jvm/codegen/PromisedValue.kt | 54 +++++++++---------- .../kotlin/backend/jvm/ir/IrUtils.kt | 37 +++++++++++-- .../backend/jvm/lower/BridgeLowering.kt | 33 ++---------- 3 files changed, 66 insertions(+), 58 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt index c0e39fa7a03..1568857c307 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen +import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi import org.jetbrains.kotlin.codegen.AsmUtil @@ -81,35 +82,34 @@ fun PromisedValue.discard(): PromisedValue { private val IrType.unboxed: IrType get() = InlineClassAbi.getUnderlyingType(erasedUpperBound) -fun PromisedValue.coerceInlineClasses(type: Type, irType: IrType, target: Type, irTarget: IrType): PromisedValue? { - val isFromTypeInlineClass = irType.erasedUpperBound.isInline - val isToTypeInlineClass = irTarget.erasedUpperBound.isInline - if (!isFromTypeInlineClass && !isToTypeInlineClass) return null - - val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(irType.unboxed) == type - val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(irTarget.unboxed) == target - - return when { - isFromTypeUnboxed && !isToTypeUnboxed -> object : PromisedValue(codegen, target, irTarget) { - override fun materialize() { - this@coerceInlineClasses.materialize() - // TODO: This is broken for type parameters - StackValue.boxInlineClass(irType.toKotlinType(), mv) - } - } - !isFromTypeUnboxed && isToTypeUnboxed -> object : PromisedValue(codegen, target, irTarget) { - override fun materialize() { - val value = this@coerceInlineClasses.materialized - StackValue.unboxInlineClass(value.type, irTarget.toKotlinType(), mv) - } - } - else -> null - } -} - // On materialization, cast the value to a different type. fun PromisedValue.coerce(target: Type, irTarget: IrType): PromisedValue { - coerceInlineClasses(type, irType, target, irTarget)?.let { return it } + val erasedSourceType = irType.eraseTypeParameters() + val erasedTargetType = irTarget.eraseTypeParameters() + val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline + val isToTypeInlineClass = erasedTargetType.classOrNull!!.owner.isInline + + // Coerce inline classes + if (isFromTypeInlineClass || isToTypeInlineClass) { + val isFromTypeUnboxed = isFromTypeInlineClass && typeMapper.mapType(erasedSourceType.unboxed) == type + val isToTypeUnboxed = isToTypeInlineClass && typeMapper.mapType(erasedTargetType.unboxed) == target + + when { + isFromTypeUnboxed && !isToTypeUnboxed -> return object : PromisedValue(codegen, target, irTarget) { + override fun materialize() { + this@coerce.materialize() + StackValue.boxInlineClass(erasedSourceType.toKotlinType(), mv) + } + } + !isFromTypeUnboxed && isToTypeUnboxed -> return object : PromisedValue(codegen, target, irTarget) { + override fun materialize() { + val value = this@coerce.materialized + StackValue.unboxInlineClass(value.type, erasedTargetType.toKotlinType(), mv) + } + } + } + } + return when (type) { // All unsafe coercions between irTypes should use the UnsafeCoerce intrinsic target -> this diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt index 4ef84f842a4..1158a91da3b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt @@ -9,15 +9,46 @@ import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.classifierOrNull +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl +import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.util.isFunction import org.jetbrains.kotlin.ir.util.isNullable import org.jetbrains.kotlin.ir.util.isSuspendFunctionTypeOrSubtype import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_DEFAULT_FQ_NAME +/** + * Perform as much type erasure as is significant for JVM signature generation. + * Class types are kept as is, while type parameters are replaced with their + * erased upper bounds, keeping the nullability information. + * + * For example, a type parameter `T?` where `T : Any`, `T : Comparable` is + * erased to `Any?`. + * + * Type arguments to the erased upper bound are replaced by `*`, since + * recursive erasure could loop. For example, a type parameter + * `T : Comparable` is replaced by `Comparable<*>`. + */ +fun IrType.eraseTypeParameters() = when (this) { + is IrErrorType -> this + is IrSimpleType -> + when (val owner = classifier.owner) { + is IrClass -> this + is IrTypeParameter -> { + val upperBound = owner.erasedUpperBound + IrSimpleTypeImpl( + upperBound.symbol, + isNullable(), + List(upperBound.typeParameters.size) { IrStarProjectionImpl }, // Should not affect JVM signature, but may result in an invalid type object + owner.annotations + ) + } + else -> error("Unknown IrSimpleType classifier kind: $owner") + } + else -> error("Unknown IrType kind: $this") +} + /** * Computes the erased class for this type parameter according to the java erasure rules. */ diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index d59cda0870c..35041ed3d50 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.backend.common.lower.irNot import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin -import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound +import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities @@ -34,12 +34,10 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl -import org.jetbrains.kotlin.ir.types.IrErrorType -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl -import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl -import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.util.isInterface +import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.commons.Method @@ -339,27 +337,6 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass } } - /* Perform type erasure as much as is significant for JVM signature generation. */ - // TODO: should be a type mapper functionality. - private fun IrType.eraseTypeParameters() = when (this) { - is IrErrorType -> this - is IrSimpleType -> - when (val owner = classifier.owner) { - is IrClass -> this - is IrTypeParameter -> { - val upperBound = owner.erasedUpperBound - IrSimpleTypeImpl( - upperBound.symbol, - isNullable(), - List(upperBound.typeParameters.size) { IrStarProjectionImpl }, // Should not affect JVM signature, but may result in an invalid type object - owner.annotations - ) - } - else -> error("Unknown IrSimpleType classifier kind: $owner") - } - else -> error("Unknown IrType kind: $this") - } - private fun IrSimpleFunction.findAllReachableDeclarations() = findAllReachableDeclarations(FunctionHandleForIrFunction(this)).map { it.irFunction }