[IR] Add REINTERPRET_CAST operator in IrTypeOperatorCall
- fix BE - fix Serializer - implement builder - make Ir a bit more type-correct - support developer mode (lowering [dynamic]implicit cast meterialized as general cast) - fix autoboxing lowering
This commit is contained in:
+2
-1
@@ -203,7 +203,8 @@ class CheckIrElementVisitor(
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT,
|
||||
IrTypeOperator.IMPLICIT_INTEGER_COERCION,
|
||||
IrTypeOperator.SAM_CONVERSION,
|
||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST ->
|
||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST,
|
||||
IrTypeOperator.REINTERPRET_CAST ->
|
||||
typeOperand
|
||||
|
||||
IrTypeOperator.SAFE_CAST ->
|
||||
|
||||
@@ -251,6 +251,8 @@ object JsIrBuilder {
|
||||
fun buildImplicitCast(value: IrExpression, toType: IrType) =
|
||||
buildTypeOperator(toType, IrTypeOperator.IMPLICIT_CAST, value, toType)
|
||||
|
||||
fun buildReinterpretCast(value: IrExpression, toType: IrType) =
|
||||
buildTypeOperator(toType, IrTypeOperator.REINTERPRET_CAST, value, toType)
|
||||
|
||||
fun buildNull(type: IrType) = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type)
|
||||
fun buildBoolean(type: IrType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
|
||||
|
||||
+3
-8
@@ -54,14 +54,9 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
|
||||
}
|
||||
is IrGetField -> this.symbol.owner.type
|
||||
|
||||
is IrTypeOperatorCall -> when (this.operator) {
|
||||
IrTypeOperator.IMPLICIT_INTEGER_COERCION ->
|
||||
// TODO: is it a workaround for inconsistent IR?
|
||||
this.typeOperand
|
||||
|
||||
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> context.irBuiltIns.anyNType
|
||||
|
||||
else -> this.type
|
||||
is IrTypeOperatorCall -> {
|
||||
assert(operator == IrTypeOperator.REINTERPRET_CAST) { "Only REINTERPRET_CAST expected at this point" }
|
||||
this.typeOperand
|
||||
}
|
||||
|
||||
is IrGetValue -> {
|
||||
|
||||
+10
@@ -456,6 +456,16 @@ class BlockDecomposerTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||
expression.transformChildrenVoid(expressionTransformer)
|
||||
|
||||
val composite = expression.argument as? IrComposite ?: return expression
|
||||
|
||||
return materializeLastExpression(composite) {
|
||||
expression.apply { argument = it }
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitGetClass(expression: IrGetClass): IrExpression {
|
||||
expression.transformChildrenVoid(expressionTransformer)
|
||||
|
||||
|
||||
+27
-10
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpressionWithCopy
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
@@ -37,6 +38,8 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
|
||||
private val calculator = JsIrArithBuilder(context)
|
||||
|
||||
private val devMode = context.devMode
|
||||
|
||||
//NOTE: Should we define JS-own functions similar to current implementation?
|
||||
private val throwCCE = context.ir.symbols.ThrowTypeCastException
|
||||
private val throwNPE = context.ir.symbols.ThrowNullPointerException
|
||||
@@ -71,8 +74,8 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
super.visitTypeOperator(expression, data)
|
||||
|
||||
return when (expression.operator) {
|
||||
IrTypeOperator.IMPLICIT_CAST -> lowerImplicitCast(expression)
|
||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> lowerImplicitDynamicCast(expression)
|
||||
IrTypeOperator.IMPLICIT_CAST -> lowerImplicitCast(expression, data)
|
||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> lowerImplicitDynamicCast(expression, data)
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> lowerCoercionToUnit(expression)
|
||||
IrTypeOperator.IMPLICIT_INTEGER_COERCION -> lowerIntegerCoercion(expression, data)
|
||||
IrTypeOperator.IMPLICIT_NOTNULL -> lowerImplicitNotNull(expression, data)
|
||||
@@ -80,6 +83,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
IrTypeOperator.NOT_INSTANCEOF -> lowerInstanceOf(expression, data, true)
|
||||
IrTypeOperator.CAST -> lowerCast(expression, data, false)
|
||||
IrTypeOperator.SAFE_CAST -> lowerCast(expression, data, true)
|
||||
IrTypeOperator.REINTERPRET_CAST -> expression
|
||||
IrTypeOperator.SAM_CONVERSION -> TODO("SAM conversion: ${expression.render()}")
|
||||
}
|
||||
}
|
||||
@@ -98,13 +102,26 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
return expression.run { IrCompositeImpl(startOffset, endOffset, typeOperand, null, newStatements) }
|
||||
}
|
||||
|
||||
private fun needBoxingOrUnboxing(fromType: IrType, toType: IrType): Boolean {
|
||||
return ((fromType.getInlinedClass() != null) xor (toType.getInlinedClass() != null)) || (fromType.isUnit() && !toType.isUnit())
|
||||
}
|
||||
|
||||
private fun IrTypeOperatorCall.wrapWithUnsafeCast(arg: IrExpression): IrExpression {
|
||||
// TODO: there is possible some situation which could be visible for AutoboxingLowering
|
||||
// They are: 1. Inline classes, 2. Unit materialization. Using unsafe cast makes lowering work wrong.
|
||||
return if (!needBoxingOrUnboxing(arg.type, typeOperand)) {
|
||||
IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.REINTERPRET_CAST, typeOperand, arg)
|
||||
} else arg
|
||||
}
|
||||
|
||||
private fun lowerCast(
|
||||
expression: IrTypeOperatorCall,
|
||||
declaration: IrDeclarationParent,
|
||||
isSafe: Boolean
|
||||
): IrExpression {
|
||||
assert(expression.operator == IrTypeOperator.CAST || expression.operator == IrTypeOperator.SAFE_CAST)
|
||||
assert((expression.operator == IrTypeOperator.SAFE_CAST) == isSafe)
|
||||
val operator = expression.operator
|
||||
assert(operator == IrTypeOperator.CAST || operator == IrTypeOperator.SAFE_CAST || operator == IrTypeOperator.IMPLICIT_CAST || operator == IrTypeOperator.IMPLICIT_DYNAMIC_CAST)
|
||||
assert((operator == IrTypeOperator.SAFE_CAST) == isSafe)
|
||||
|
||||
val toType = expression.typeOperand
|
||||
val failResult = if (isSafe) litNull else JsIrBuilder.buildCall(throwCCE)
|
||||
@@ -113,23 +130,23 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
|
||||
val argument = cacheValue(expression.argument, newStatements, declaration)
|
||||
val check = generateTypeCheck(argument, toType)
|
||||
val castedValue = expression.wrapWithUnsafeCast(argument())
|
||||
|
||||
newStatements += JsIrBuilder.buildIfElse(expression.type, check, argument(), failResult)
|
||||
newStatements += JsIrBuilder.buildIfElse(expression.type, check, castedValue, failResult)
|
||||
|
||||
return expression.run {
|
||||
IrCompositeImpl(startOffset, endOffset, expression.type, null, newStatements)
|
||||
}
|
||||
}
|
||||
|
||||
private fun lowerImplicitCast(expression: IrTypeOperatorCall) = expression.run {
|
||||
private fun lowerImplicitCast(expression: IrTypeOperatorCall, data: IrDeclarationParent) = expression.run {
|
||||
assert(operator == IrTypeOperator.IMPLICIT_CAST)
|
||||
argument
|
||||
if (devMode) lowerCast(expression, data, false) else wrapWithUnsafeCast(argument)
|
||||
}
|
||||
|
||||
private fun lowerImplicitDynamicCast(expression: IrTypeOperatorCall) = expression.run {
|
||||
// TODO check argument
|
||||
private fun lowerImplicitDynamicCast(expression: IrTypeOperatorCall, data: IrDeclarationParent) = expression.run {
|
||||
assert(operator == IrTypeOperator.IMPLICIT_DYNAMIC_CAST)
|
||||
argument
|
||||
if (devMode) lowerCast(expression, data, false) else wrapWithUnsafeCast(argument)
|
||||
}
|
||||
|
||||
// Note: native `instanceOf` is not used which is important because of null-behaviour
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.ir.backend.js.lower.calls
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.ConversionNames
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
|
||||
@@ -87,6 +86,6 @@ class NumberConversionCallsTransformer(context: JsIrBackendContext) : CallsTrans
|
||||
}
|
||||
|
||||
private fun useDispatchReceiver(call: IrFunctionAccessExpression): IrExpression {
|
||||
return JsIrBuilder.buildImplicitCast(call.dispatchReceiver!!, call.type)
|
||||
return JsIrBuilder.buildReinterpretCast(call.dispatchReceiver!!, call.type)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -10,12 +10,10 @@ import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, JsGenerationContext> {
|
||||
@@ -192,8 +190,8 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: JsGenerationContext): JsExpression {
|
||||
return when (expression.operator) {
|
||||
IrTypeOperator.IMPLICIT_CAST -> expression.argument.accept(this, data)
|
||||
else -> error("All type operator calls except IMPLICIT_CAST should be lowered at this point")
|
||||
IrTypeOperator.REINTERPRET_CAST -> expression.argument.accept(this, data)
|
||||
else -> error("All type operator calls except REINTERPRET_CAST should be lowered at this point: ${expression.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -314,6 +314,9 @@ fun IrBuilderWithScope.irAs(argument: IrExpression, type: IrType) =
|
||||
fun IrBuilderWithScope.irImplicitCast(argument: IrExpression, type: IrType) =
|
||||
IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.IMPLICIT_CAST, type, argument)
|
||||
|
||||
fun IrBuilderWithScope.irReinterpretCast(argument: IrExpression, type: IrType) =
|
||||
IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.REINTERPRET_CAST, type, argument)
|
||||
|
||||
fun IrBuilderWithScope.irInt(value: Int) =
|
||||
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, value)
|
||||
|
||||
|
||||
@@ -49,7 +49,11 @@ enum class IrTypeOperator {
|
||||
* Implicit dynamic cast: implicit cast from `dynamic` to `T`.
|
||||
* This currently can happen in Kotlin/JS only.
|
||||
*/
|
||||
IMPLICIT_DYNAMIC_CAST;
|
||||
IMPLICIT_DYNAMIC_CAST,
|
||||
/**
|
||||
* C-like reinterpret_cast<T> using as primitive type operation in JS
|
||||
*/
|
||||
REINTERPRET_CAST;
|
||||
}
|
||||
|
||||
interface IrTypeOperatorCall : IrExpression {
|
||||
|
||||
+2
@@ -738,6 +738,8 @@ open class IrFileSerializer(
|
||||
ProtoTypeOperator.SAM_CONVERSION
|
||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST ->
|
||||
ProtoTypeOperator.IMPLICIT_DYNAMIC_CAST
|
||||
IrTypeOperator.REINTERPRET_CAST ->
|
||||
error("Unreachable execution")
|
||||
}
|
||||
|
||||
private fun serializeTypeOp(expression: IrTypeOperatorCall): ProtoTypeOp {
|
||||
|
||||
Reference in New Issue
Block a user