From 52b24ead9104f962f0a973d2255812383dce6ee0 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Wed, 13 Nov 2019 17:16:49 +0300 Subject: [PATCH] [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 --- .../backend/common/CheckIrElementVisitor.kt | 3 +- .../kotlin/ir/backend/js/ir/IrBuilder.kt | 2 + .../backend/js/lower/AutoboxingTransformer.kt | 11 ++---- .../js/lower/BlockDecomposerLowering.kt | 10 +++++ .../backend/js/lower/TypeOperatorLowering.kt | 37 ++++++++++++++----- .../calls/NumberConversionCallsTransformer.kt | 3 +- .../IrElementToJsExpressionTransformer.kt | 6 +-- .../kotlin/ir/builders/ExpressionHelpers.kt | 3 ++ .../ir/expressions/IrTypeOperatorCall.kt | 6 ++- .../common/serialization/IrFileSerializer.kt | 2 + 10 files changed, 57 insertions(+), 26 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt index 9206af7f43a..ffdd5476019 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CheckIrElementVisitor.kt @@ -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 -> diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt index 713152ae46f..f27a06a5e79 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt @@ -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) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt index 4dd202da6dc..1e77e052fb1 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt @@ -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 -> { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt index ca1049b519d..09c20f7bff4 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt @@ -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) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt index 2532087d218..bda30b419e0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt @@ -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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt index a286e7303a3..59585ea338c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt @@ -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) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index 44cee71f3cf..2a99c03f7ba 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -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 { @@ -192,8 +190,8 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer 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}") } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt index e1bbb62d320..3c792e20d97 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt @@ -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) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt index 35084d48b87..368415b192b 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTypeOperatorCall.kt @@ -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 using as primitive type operation in JS + */ + REINTERPRET_CAST; } interface IrTypeOperatorCall : IrExpression { diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileSerializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileSerializer.kt index 361736e94d6..d0ab6d2fe71 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileSerializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileSerializer.kt @@ -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 {