diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 496f8940956..4a76cc9bce7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -629,7 +629,11 @@ public abstract class StackValue { } public static void coerce(@NotNull Type fromType, @NotNull Type toType, @NotNull InstructionAdapter v) { - if (toType.equals(fromType)) return; + coerce(fromType, toType, v, false); + } + + public static void coerce(@NotNull Type fromType, @NotNull Type toType, @NotNull InstructionAdapter v, boolean forceSelfCast) { + if (toType.equals(fromType) && !forceSelfCast) return; if (toType.getSort() == Type.VOID) { pop(v, fromType); diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index da0cac1c4f2..e07e062b7b4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -504,10 +504,10 @@ class ExpressionCodegen( } unboxedInlineClassIrType != null && !irFunction.isInvokeSuspendOfContinuation() -> object : PromisedValue(this, unboxedInlineClassIrType.asmType, unboxedInlineClassIrType) { - override fun materializeAt(target: Type, irTarget: IrType) { + override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) { mv.checkcast(unboxedInlineClassIrType.asmType) MaterialValue(this@ExpressionCodegen, unboxedInlineClassIrType.asmType, unboxedInlineClassIrType) - .materializeAt(target, irTarget) + .materializeAt(target, irTarget, castForReified) } override fun discard() { @@ -951,7 +951,7 @@ class ExpressionCodegen( if (!exhaustive) { result.discard() } else { - val materializedResult = result.materializedAt(expression.type) + val materializedResult = result.materializedAt(typeMapper.mapType(expression.type), expression.type, true) if (branch.condition.isTrueConst()) { // The rest of the expression is dead code. mv.mark(endLabel) @@ -1098,7 +1098,7 @@ class ExpressionCodegen( val isExpression = !aTry.type.isUnit() var savedValue: Int? = null if (isExpression) { - tryResult.materializeAt(tryAsmType, aTry.type) + tryResult.materializeAt(tryAsmType, aTry.type, true) savedValue = frameMap.enterTemp(tryAsmType) mv.store(savedValue, tryAsmType) } else { @@ -1127,7 +1127,7 @@ class ExpressionCodegen( val catchBody = clause.result val catchResult = catchBody.accept(this, data) if (savedValue != null) { - catchResult.materializeAt(tryAsmType, aTry.type) + catchResult.materializeAt(tryAsmType, aTry.type, true) mv.store(savedValue, tryAsmType) } else { catchResult.discard() @@ -1170,11 +1170,11 @@ class ExpressionCodegen( mv.mark(tryCatchBlockEnd) // TODO: generate a common `finally` for try & catch blocks here? Right now this breaks the inliner. return object : PromisedValue(this, tryAsmType, aTry.type) { - override fun materializeAt(target: Type, irTarget: IrType) { + override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) { if (savedValue != null) { mv.load(savedValue, tryAsmType) frameMap.leaveTemp(tryAsmType) - super.materializeAt(target, irTarget) + super.materializeAt(target, irTarget, castForReified) } else { unitValue.materializeAt(target, irTarget) } 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 2b04dcdac52..31824e999fa 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 @@ -11,10 +11,16 @@ import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.ir.declarations.IrTypeParameter import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType +import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.isTypeParameter import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.ir.util.substitute import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.types.model.SimpleTypeMarker +import org.jetbrains.kotlin.types.model.typeConstructor import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -24,7 +30,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val irType: IrType) { // If this value is immaterial, construct an object on the top of the stack. This // must always be done before generating other values or emitting raw bytecode. - open fun materializeAt(target: Type, irTarget: IrType) { + open fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) { val erasedSourceType = irType.eraseTypeParameters() val erasedTargetType = irTarget.eraseTypeParameters() val isFromTypeInlineClass = erasedSourceType.classOrNull!!.owner.isInline @@ -54,9 +60,10 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val } } - if (type != target) { - StackValue.coerce(type, target, mv) + if (type != target || (castForReified && irType.anyTypeArgument { it.isReified })) { + StackValue.coerce(type, target, mv, type == target) } + } abstract fun discard() @@ -68,6 +75,15 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val get() = codegen.typeMapper } + +fun IrType.anyTypeArgument(check: (IrTypeParameter) -> Boolean): Boolean { + when { + isTypeParameter() -> if (check((classifierOrNull as IrTypeParameterSymbol).owner)) return true + this is IrSimpleType -> return arguments.any { it.typeOrNull?.anyTypeArgument(check) ?: false } + } + return false +} + // A value that *has* been fully constructed. class MaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) { override fun discard() { @@ -83,7 +99,7 @@ abstract class BooleanValue(codegen: ExpressionCodegen) : abstract fun jumpIfFalse(target: Label) abstract fun jumpIfTrue(target: Label) - override fun materializeAt(target: Type, irTarget: IrType) { + override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) { val const0 = Label() val end = Label() jumpIfFalse(const0) @@ -101,7 +117,7 @@ abstract class BooleanValue(codegen: ExpressionCodegen) : class BooleanConstant(codegen: ExpressionCodegen, val value: Boolean) : BooleanValue(codegen) { override fun jumpIfFalse(target: Label) = if (value) Unit else mv.goTo(target) override fun jumpIfTrue(target: Label) = if (value) mv.goTo(target) else Unit - override fun materializeAt(target: Type, irTarget: IrType) { + override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) { mv.iconst(if (value) 1 else 0) if (Type.BOOLEAN_TYPE != target) { StackValue.coerce(Type.BOOLEAN_TYPE, target, mv) @@ -116,10 +132,10 @@ fun PromisedValue.coerceToBoolean(): BooleanValue = is BooleanValue -> this else -> object : BooleanValue(codegen) { override fun jumpIfFalse(target: Label) = - this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType).also { mv.ifeq(target) } + this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType, false).also { mv.ifeq(target) } override fun jumpIfTrue(target: Label) = - this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType).also { mv.ifne(target) } + this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType, false).also { mv.ifne(target) } override fun discard() { this@coerceToBoolean.discard() @@ -127,9 +143,12 @@ fun PromisedValue.coerceToBoolean(): BooleanValue = } } +fun PromisedValue.materializeAt(target: Type, irTarget: IrType) { + materializeAt(target, irTarget, false) +} -fun PromisedValue.materializedAt(target: Type, irTarget: IrType): MaterialValue { - materializeAt(target, irTarget) +fun PromisedValue.materializedAt(target: Type, irTarget: IrType, castForReified: Boolean = false): MaterialValue { + materializeAt(target, irTarget, castForReified) return MaterialValue(codegen, target, irTarget) } @@ -163,7 +182,7 @@ val ExpressionCodegen.unitValue: PromisedValue val ExpressionCodegen.nullConstant: PromisedValue get() = object : PromisedValue(this, AsmTypes.OBJECT_TYPE, context.irBuiltIns.nothingNType) { - override fun materializeAt(target: Type, irTarget: IrType) { + override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) { mv.aconst(null) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/UnsafeCoerce.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/UnsafeCoerce.kt index c5f095febf2..c91d376718a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/UnsafeCoerce.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/UnsafeCoerce.kt @@ -29,9 +29,9 @@ object UnsafeCoerce : IntrinsicMethod() { val arg = expression.getValueArgument(0)!! val result = arg.accept(codegen, data) return object : PromisedValue(codegen, toType, to) { - override fun materializeAt(target: Type, irTarget: IrType) { + override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) { result.materializeAt(fromType, from) - super.materializeAt(target, irTarget) + super.materializeAt(target, irTarget, castForReified) } override fun discard() {