diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt index 84dbbe15e9d..f5f5b277748 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt @@ -3,6 +3,7 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.FunctionLoweringPass import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.ir.IrStatement @@ -14,7 +15,9 @@ import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable /** * This lowering pass lowers some [IrTypeOperatorCall]s. @@ -31,6 +34,10 @@ private class TypeOperatorTransformer(val context: Context, val function: Functi private val builder = context.createFunctionIrBuilder(function) + val throwClassCastException by lazy { + context.builtIns.getKonanInternalFunctions("ThrowClassCastException").single() + } + override fun visitFunction(declaration: IrFunction): IrStatement { // ignore inner functions during this pass return declaration @@ -49,17 +56,44 @@ private class TypeOperatorTransformer(val context: Context, val function: Functi } private fun lowerCast(expression: IrTypeOperatorCall): IrExpression { + builder.at(expression) val typeOperand = expression.typeOperand.erasure() - return if (expression.argument.type.isSubtypeOf(typeOperand)) { - // TODO: consider the case when expression type is wrong e.g. due to generics-related unchecked casts. - expression.argument - } else if (typeOperand == expression.typeOperand) { - expression - } else { - builder.at(expression).irAs(expression.argument, typeOperand) + + assert (!TypeUtils.hasNullableSuperType(typeOperand)) // So that `isNullable()` <=> `isMarkedNullable`. + + // TODO: consider the case when expression type is wrong e.g. due to generics-related unchecked casts. + + return when { + expression.argument.type.isSubtypeOf(typeOperand) -> expression.argument + + expression.argument.type.isNullable() -> { + with (builder) { + irLet(expression.argument) { argument -> + irIfThenElse( + type = expression.type, + condition = irEqeqeq(irGet(argument), irNull()), + + thenPart = if (typeOperand.isMarkedNullable) + irNull() + else + irCall(throwClassCastException), + + elsePart = irAs(irGet(argument), typeOperand.makeNotNullable()) + ) + } + } + } + + typeOperand.isMarkedNullable -> builder.irAs(expression.argument, typeOperand.makeNotNullable()) + + typeOperand == expression.typeOperand -> expression + + else -> builder.irAs(expression.argument, typeOperand) } } + private fun KotlinType.isNullable() = TypeUtils.isNullableType(this) + private fun lowerSafeCast(expression: IrTypeOperatorCall): IrExpression { val typeOperand = expression.typeOperand.erasure()