[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_COERCION_TO_UNIT,
|
||||||
IrTypeOperator.IMPLICIT_INTEGER_COERCION,
|
IrTypeOperator.IMPLICIT_INTEGER_COERCION,
|
||||||
IrTypeOperator.SAM_CONVERSION,
|
IrTypeOperator.SAM_CONVERSION,
|
||||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST ->
|
IrTypeOperator.IMPLICIT_DYNAMIC_CAST,
|
||||||
|
IrTypeOperator.REINTERPRET_CAST ->
|
||||||
typeOperand
|
typeOperand
|
||||||
|
|
||||||
IrTypeOperator.SAFE_CAST ->
|
IrTypeOperator.SAFE_CAST ->
|
||||||
|
|||||||
@@ -251,6 +251,8 @@ object JsIrBuilder {
|
|||||||
fun buildImplicitCast(value: IrExpression, toType: IrType) =
|
fun buildImplicitCast(value: IrExpression, toType: IrType) =
|
||||||
buildTypeOperator(toType, IrTypeOperator.IMPLICIT_CAST, value, toType)
|
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 buildNull(type: IrType) = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type)
|
||||||
fun buildBoolean(type: IrType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
|
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 IrGetField -> this.symbol.owner.type
|
||||||
|
|
||||||
is IrTypeOperatorCall -> when (this.operator) {
|
is IrTypeOperatorCall -> {
|
||||||
IrTypeOperator.IMPLICIT_INTEGER_COERCION ->
|
assert(operator == IrTypeOperator.REINTERPRET_CAST) { "Only REINTERPRET_CAST expected at this point" }
|
||||||
// TODO: is it a workaround for inconsistent IR?
|
this.typeOperand
|
||||||
this.typeOperand
|
|
||||||
|
|
||||||
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> context.irBuiltIns.anyNType
|
|
||||||
|
|
||||||
else -> this.type
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is IrGetValue -> {
|
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 {
|
override fun visitGetClass(expression: IrGetClass): IrExpression {
|
||||||
expression.transformChildrenVoid(expressionTransformer)
|
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.IrTypeOperator
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
|
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.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
@@ -37,6 +38,8 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
|||||||
|
|
||||||
private val calculator = JsIrArithBuilder(context)
|
private val calculator = JsIrArithBuilder(context)
|
||||||
|
|
||||||
|
private val devMode = context.devMode
|
||||||
|
|
||||||
//NOTE: Should we define JS-own functions similar to current implementation?
|
//NOTE: Should we define JS-own functions similar to current implementation?
|
||||||
private val throwCCE = context.ir.symbols.ThrowTypeCastException
|
private val throwCCE = context.ir.symbols.ThrowTypeCastException
|
||||||
private val throwNPE = context.ir.symbols.ThrowNullPointerException
|
private val throwNPE = context.ir.symbols.ThrowNullPointerException
|
||||||
@@ -71,8 +74,8 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
|||||||
super.visitTypeOperator(expression, data)
|
super.visitTypeOperator(expression, data)
|
||||||
|
|
||||||
return when (expression.operator) {
|
return when (expression.operator) {
|
||||||
IrTypeOperator.IMPLICIT_CAST -> lowerImplicitCast(expression)
|
IrTypeOperator.IMPLICIT_CAST -> lowerImplicitCast(expression, data)
|
||||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> lowerImplicitDynamicCast(expression)
|
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> lowerImplicitDynamicCast(expression, data)
|
||||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> lowerCoercionToUnit(expression)
|
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> lowerCoercionToUnit(expression)
|
||||||
IrTypeOperator.IMPLICIT_INTEGER_COERCION -> lowerIntegerCoercion(expression, data)
|
IrTypeOperator.IMPLICIT_INTEGER_COERCION -> lowerIntegerCoercion(expression, data)
|
||||||
IrTypeOperator.IMPLICIT_NOTNULL -> lowerImplicitNotNull(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.NOT_INSTANCEOF -> lowerInstanceOf(expression, data, true)
|
||||||
IrTypeOperator.CAST -> lowerCast(expression, data, false)
|
IrTypeOperator.CAST -> lowerCast(expression, data, false)
|
||||||
IrTypeOperator.SAFE_CAST -> lowerCast(expression, data, true)
|
IrTypeOperator.SAFE_CAST -> lowerCast(expression, data, true)
|
||||||
|
IrTypeOperator.REINTERPRET_CAST -> expression
|
||||||
IrTypeOperator.SAM_CONVERSION -> TODO("SAM conversion: ${expression.render()}")
|
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) }
|
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(
|
private fun lowerCast(
|
||||||
expression: IrTypeOperatorCall,
|
expression: IrTypeOperatorCall,
|
||||||
declaration: IrDeclarationParent,
|
declaration: IrDeclarationParent,
|
||||||
isSafe: Boolean
|
isSafe: Boolean
|
||||||
): IrExpression {
|
): IrExpression {
|
||||||
assert(expression.operator == IrTypeOperator.CAST || expression.operator == IrTypeOperator.SAFE_CAST)
|
val operator = expression.operator
|
||||||
assert((expression.operator == IrTypeOperator.SAFE_CAST) == isSafe)
|
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 toType = expression.typeOperand
|
||||||
val failResult = if (isSafe) litNull else JsIrBuilder.buildCall(throwCCE)
|
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 argument = cacheValue(expression.argument, newStatements, declaration)
|
||||||
val check = generateTypeCheck(argument, toType)
|
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 {
|
return expression.run {
|
||||||
IrCompositeImpl(startOffset, endOffset, expression.type, null, newStatements)
|
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)
|
assert(operator == IrTypeOperator.IMPLICIT_CAST)
|
||||||
argument
|
if (devMode) lowerCast(expression, data, false) else wrapWithUnsafeCast(argument)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lowerImplicitDynamicCast(expression: IrTypeOperatorCall) = expression.run {
|
private fun lowerImplicitDynamicCast(expression: IrTypeOperatorCall, data: IrDeclarationParent) = expression.run {
|
||||||
// TODO check argument
|
|
||||||
assert(operator == IrTypeOperator.IMPLICIT_DYNAMIC_CAST)
|
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
|
// 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.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.ConversionNames
|
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.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||||
|
|
||||||
@@ -87,6 +86,6 @@ class NumberConversionCallsTransformer(context: JsIrBackendContext) : CallsTrans
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun useDispatchReceiver(call: IrFunctionAccessExpression): IrExpression {
|
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.backend.js.utils.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
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.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.types.isUnit
|
import org.jetbrains.kotlin.ir.types.isUnit
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
|
||||||
|
|
||||||
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||||
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, JsGenerationContext> {
|
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, JsGenerationContext> {
|
||||||
@@ -192,8 +190,8 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
|
|
||||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: JsGenerationContext): JsExpression {
|
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: JsGenerationContext): JsExpression {
|
||||||
return when (expression.operator) {
|
return when (expression.operator) {
|
||||||
IrTypeOperator.IMPLICIT_CAST -> expression.argument.accept(this, data)
|
IrTypeOperator.REINTERPRET_CAST -> expression.argument.accept(this, data)
|
||||||
else -> error("All type operator calls except IMPLICIT_CAST should be lowered at this point")
|
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) =
|
fun IrBuilderWithScope.irImplicitCast(argument: IrExpression, type: IrType) =
|
||||||
IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.IMPLICIT_CAST, type, argument)
|
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) =
|
fun IrBuilderWithScope.irInt(value: Int) =
|
||||||
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, value)
|
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, value)
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,11 @@ enum class IrTypeOperator {
|
|||||||
* Implicit dynamic cast: implicit cast from `dynamic` to `T`.
|
* Implicit dynamic cast: implicit cast from `dynamic` to `T`.
|
||||||
* This currently can happen in Kotlin/JS only.
|
* 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 {
|
interface IrTypeOperatorCall : IrExpression {
|
||||||
|
|||||||
+2
@@ -738,6 +738,8 @@ open class IrFileSerializer(
|
|||||||
ProtoTypeOperator.SAM_CONVERSION
|
ProtoTypeOperator.SAM_CONVERSION
|
||||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST ->
|
IrTypeOperator.IMPLICIT_DYNAMIC_CAST ->
|
||||||
ProtoTypeOperator.IMPLICIT_DYNAMIC_CAST
|
ProtoTypeOperator.IMPLICIT_DYNAMIC_CAST
|
||||||
|
IrTypeOperator.REINTERPRET_CAST ->
|
||||||
|
error("Unreachable execution")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun serializeTypeOp(expression: IrTypeOperatorCall): ProtoTypeOp {
|
private fun serializeTypeOp(expression: IrTypeOperatorCall): ProtoTypeOp {
|
||||||
|
|||||||
Reference in New Issue
Block a user