backend: handle nullability in cast
This commit is contained in:
committed by
SvyatoslavScherbina
parent
97ce2227d5
commit
183155a8a1
+41
-7
@@ -3,6 +3,7 @@ package org.jetbrains.kotlin.backend.konan.lower
|
|||||||
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
|
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.lower.*
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
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.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
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.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
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.isSubtypeOf
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This lowering pass lowers some [IrTypeOperatorCall]s.
|
* 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)
|
private val builder = context.createFunctionIrBuilder(function)
|
||||||
|
|
||||||
|
val throwClassCastException by lazy {
|
||||||
|
context.builtIns.getKonanInternalFunctions("ThrowClassCastException").single()
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitFunction(declaration: IrFunction): IrStatement {
|
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||||
// ignore inner functions during this pass
|
// ignore inner functions during this pass
|
||||||
return declaration
|
return declaration
|
||||||
@@ -49,17 +56,44 @@ private class TypeOperatorTransformer(val context: Context, val function: Functi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun lowerCast(expression: IrTypeOperatorCall): IrExpression {
|
private fun lowerCast(expression: IrTypeOperatorCall): IrExpression {
|
||||||
|
builder.at(expression)
|
||||||
val typeOperand = expression.typeOperand.erasure()
|
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.
|
assert (!TypeUtils.hasNullableSuperType(typeOperand)) // So that `isNullable()` <=> `isMarkedNullable`.
|
||||||
expression.argument
|
|
||||||
} else if (typeOperand == expression.typeOperand) {
|
// TODO: consider the case when expression type is wrong e.g. due to generics-related unchecked casts.
|
||||||
expression
|
|
||||||
} else {
|
return when {
|
||||||
builder.at(expression).irAs(expression.argument, typeOperand)
|
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 {
|
private fun lowerSafeCast(expression: IrTypeOperatorCall): IrExpression {
|
||||||
val typeOperand = expression.typeOperand.erasure()
|
val typeOperand = expression.typeOperand.erasure()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user