Move eraseTypeParameters to IrUtils and use it in PromisedValue.coerce

This commit is contained in:
Steven Schäfer
2019-05-20 13:12:28 +02:00
committed by Alexander Udalov
parent 3ce0c336bc
commit 41ce88cb9b
3 changed files with 66 additions and 58 deletions
@@ -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
@@ -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<T>` 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<T>` 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.
*/
@@ -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 }