From 760806a1ac18ccdccec5733d4edf020a8c413667 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Tue, 16 Apr 2019 12:52:22 +0300 Subject: [PATCH] IR: IMPLICIT_DYNAMIC_CAST Generate IMPLICIT_DYNAMIC_CAST for implicit casts from `dynamic` to `T` --- .../backend/common/CheckIrElementVisitor.kt | 10 +++-- .../backend/js/lower/TypeOperatorLowering.kt | 6 +++ .../transformations/InsertImplicitCasts.kt | 2 +- .../ir/expressions/IrTypeOperatorCall.kt | 7 +++- .../serialization.common/src/KotlinIr.proto | 1 + .../serialization/IrModuleDeserializer.kt | 38 ++++++++++--------- .../serialization/IrModuleSerializer.kt | 38 ++++++++++--------- .../common/serialization/KotlinIr.java | 9 +++++ .../dynamic/dynamicAndMembersOfAny.txt | 6 +-- .../irJsText/dynamic/dynamicWithSmartCast.txt | 4 +- .../dynamic/implicitCastFromDynamic.txt | 8 ++-- 11 files changed, 79 insertions(+), 50 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 4e275f223ca..023f93e4e18 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 @@ -182,11 +182,15 @@ class CheckIrElementVisitor( IrTypeOperator.IMPLICIT_NOTNULL, IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, IrTypeOperator.IMPLICIT_INTEGER_COERCION, - IrTypeOperator.SAM_CONVERSION -> typeOperand + IrTypeOperator.SAM_CONVERSION, + IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> + typeOperand - IrTypeOperator.SAFE_CAST -> typeOperand.makeNullable() + IrTypeOperator.SAFE_CAST -> + typeOperand.makeNullable() - IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> irBuiltIns.booleanType + IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF -> + irBuiltIns.booleanType } if (operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT && !typeOperand.isUnit()) { 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 4eb4d64f0e7..ae07eb569b7 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 @@ -69,6 +69,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { return when (expression.operator) { IrTypeOperator.IMPLICIT_CAST -> lowerImplicitCast(expression) + IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> lowerImplicitDynamicCast(expression) IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> lowerCoercionToUnit(expression) IrTypeOperator.IMPLICIT_INTEGER_COERCION -> lowerIntegerCoercion(expression, data) IrTypeOperator.IMPLICIT_NOTNULL -> lowerImplicitNotNull(expression, data) @@ -122,6 +123,11 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { argument } + private fun lowerImplicitDynamicCast(expression: IrTypeOperatorCall) = expression.run { + assert(operator == IrTypeOperator.IMPLICIT_DYNAMIC_CAST) + argument + } + // Note: native `instanceOf` is not used which is important because of null-behaviour private fun advancedCheckRequired(type: IrType) = type.isInterface() || type.isArray() || diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt index df1f6d67991..90a2bdce911 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt @@ -291,7 +291,7 @@ open class InsertImplicitCasts( if (expectedType.isNullableAny()) { this } else { - implicitCast(expectedType, IrTypeOperator.IMPLICIT_CAST) + implicitCast(expectedType, IrTypeOperator.IMPLICIT_DYNAMIC_CAST) } } 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 f2b3017e84e..35084d48b87 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 @@ -44,7 +44,12 @@ enum class IrTypeOperator { * SAM conversion: value of functional type F is used where Single Abstract Method interface value is expected. * Currently this is possible in Kotlin/JVM only, however, there's a big demand for SAM conversion for Kotlin interfaces. */ - SAM_CONVERSION; + SAM_CONVERSION, + /** + * Implicit dynamic cast: implicit cast from `dynamic` to `T`. + * This currently can happen in Kotlin/JS only. + */ + IMPLICIT_DYNAMIC_CAST; } interface IrTypeOperatorCall : IrExpression { diff --git a/compiler/ir/serialization.common/src/KotlinIr.proto b/compiler/ir/serialization.common/src/KotlinIr.proto index dad764c507a..22b08a6d313 100644 --- a/compiler/ir/serialization.common/src/KotlinIr.proto +++ b/compiler/ir/serialization.common/src/KotlinIr.proto @@ -512,6 +512,7 @@ enum IrTypeOperator { INSTANCEOF = 7; NOT_INSTANCEOF = 8; SAM_CONVERSION = 9; + IMPLICIT_DYNAMIC_CAST = 10; } diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt index dddaf5b87fb..1f43fcd4d0d 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt @@ -530,24 +530,26 @@ abstract class IrModuleDeserializer( } private fun deserializeTypeOperator(operator: KotlinIr.IrTypeOperator) = when (operator) { - KotlinIr.IrTypeOperator.CAST - -> IrTypeOperator.CAST - KotlinIr.IrTypeOperator.IMPLICIT_CAST - -> IrTypeOperator.IMPLICIT_CAST - KotlinIr.IrTypeOperator.IMPLICIT_NOTNULL - -> IrTypeOperator.IMPLICIT_NOTNULL - KotlinIr.IrTypeOperator.IMPLICIT_COERCION_TO_UNIT - -> IrTypeOperator.IMPLICIT_COERCION_TO_UNIT - KotlinIr.IrTypeOperator.IMPLICIT_INTEGER_COERCION - -> IrTypeOperator.IMPLICIT_INTEGER_COERCION - KotlinIr.IrTypeOperator.SAFE_CAST - -> IrTypeOperator.SAFE_CAST - KotlinIr.IrTypeOperator.INSTANCEOF - -> IrTypeOperator.INSTANCEOF - KotlinIr.IrTypeOperator.NOT_INSTANCEOF - -> IrTypeOperator.NOT_INSTANCEOF - KotlinIr.IrTypeOperator.SAM_CONVERSION - -> IrTypeOperator.SAM_CONVERSION + KotlinIr.IrTypeOperator.CAST -> + IrTypeOperator.CAST + KotlinIr.IrTypeOperator.IMPLICIT_CAST -> + IrTypeOperator.IMPLICIT_CAST + KotlinIr.IrTypeOperator.IMPLICIT_NOTNULL -> + IrTypeOperator.IMPLICIT_NOTNULL + KotlinIr.IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> + IrTypeOperator.IMPLICIT_COERCION_TO_UNIT + KotlinIr.IrTypeOperator.IMPLICIT_INTEGER_COERCION -> + IrTypeOperator.IMPLICIT_INTEGER_COERCION + KotlinIr.IrTypeOperator.SAFE_CAST -> + IrTypeOperator.SAFE_CAST + KotlinIr.IrTypeOperator.INSTANCEOF -> + IrTypeOperator.INSTANCEOF + KotlinIr.IrTypeOperator.NOT_INSTANCEOF -> + IrTypeOperator.NOT_INSTANCEOF + KotlinIr.IrTypeOperator.SAM_CONVERSION -> + IrTypeOperator.SAM_CONVERSION + KotlinIr.IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> + IrTypeOperator.IMPLICIT_DYNAMIC_CAST } private fun deserializeTypeOp(proto: KotlinIr.IrTypeOp, start: Int, end: Int, type: IrType): IrTypeOperatorCall { diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleSerializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleSerializer.kt index babc8e304d0..57c94c78c19 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleSerializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleSerializer.kt @@ -601,24 +601,26 @@ open class IrModuleSerializer( } private fun serializeTypeOperator(operator: IrTypeOperator): KotlinIr.IrTypeOperator = when (operator) { - IrTypeOperator.CAST - -> KotlinIr.IrTypeOperator.CAST - IrTypeOperator.IMPLICIT_CAST - -> KotlinIr.IrTypeOperator.IMPLICIT_CAST - IrTypeOperator.IMPLICIT_NOTNULL - -> KotlinIr.IrTypeOperator.IMPLICIT_NOTNULL - IrTypeOperator.IMPLICIT_COERCION_TO_UNIT - -> KotlinIr.IrTypeOperator.IMPLICIT_COERCION_TO_UNIT - IrTypeOperator.IMPLICIT_INTEGER_COERCION - -> KotlinIr.IrTypeOperator.IMPLICIT_INTEGER_COERCION - IrTypeOperator.SAFE_CAST - -> KotlinIr.IrTypeOperator.SAFE_CAST - IrTypeOperator.INSTANCEOF - -> KotlinIr.IrTypeOperator.INSTANCEOF - IrTypeOperator.NOT_INSTANCEOF - -> KotlinIr.IrTypeOperator.NOT_INSTANCEOF - IrTypeOperator.SAM_CONVERSION - -> KotlinIr.IrTypeOperator.SAM_CONVERSION + IrTypeOperator.CAST -> + KotlinIr.IrTypeOperator.CAST + IrTypeOperator.IMPLICIT_CAST -> + KotlinIr.IrTypeOperator.IMPLICIT_CAST + IrTypeOperator.IMPLICIT_NOTNULL -> + KotlinIr.IrTypeOperator.IMPLICIT_NOTNULL + IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> + KotlinIr.IrTypeOperator.IMPLICIT_COERCION_TO_UNIT + IrTypeOperator.IMPLICIT_INTEGER_COERCION -> + KotlinIr.IrTypeOperator.IMPLICIT_INTEGER_COERCION + IrTypeOperator.SAFE_CAST -> + KotlinIr.IrTypeOperator.SAFE_CAST + IrTypeOperator.INSTANCEOF -> + KotlinIr.IrTypeOperator.INSTANCEOF + IrTypeOperator.NOT_INSTANCEOF -> + KotlinIr.IrTypeOperator.NOT_INSTANCEOF + IrTypeOperator.SAM_CONVERSION -> + KotlinIr.IrTypeOperator.SAM_CONVERSION + IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> + KotlinIr.IrTypeOperator.IMPLICIT_DYNAMIC_CAST } private fun serializeTypeOp(expression: IrTypeOperatorCall): KotlinIr.IrTypeOp { diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIr.java b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIr.java index 907e08d5e34..9fff85ed649 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIr.java +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIr.java @@ -588,6 +588,10 @@ public final class KotlinIr { * SAM_CONVERSION = 9; */ SAM_CONVERSION(8, 9), + /** + * IMPLICIT_DYNAMIC_CAST = 10; + */ + IMPLICIT_DYNAMIC_CAST(9, 10), ; /** @@ -626,6 +630,10 @@ public final class KotlinIr { * SAM_CONVERSION = 9; */ public static final int SAM_CONVERSION_VALUE = 9; + /** + * IMPLICIT_DYNAMIC_CAST = 10; + */ + public static final int IMPLICIT_DYNAMIC_CAST_VALUE = 10; public final int getNumber() { return value; } @@ -641,6 +649,7 @@ public final class KotlinIr { case 7: return INSTANCEOF; case 8: return NOT_INSTANCEOF; case 9: return SAM_CONVERSION; + case 10: return IMPLICIT_DYNAMIC_CAST; default: return null; } } diff --git a/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.txt b/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.txt index 9b5a6780b84..4389ae84ca1 100644 --- a/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.txt +++ b/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.txt @@ -4,20 +4,20 @@ FILE fqName: fileName:/dynamicAndMembersOfAny.kt BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test1 (d: dynamic): kotlin.String declared in ' CALL 'public open fun toString (): kotlin.String declared in kotlin.Any' type=kotlin.String origin=null - $this: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any + $this: TYPE_OP type=kotlin.Any origin=IMPLICIT_DYNAMIC_CAST typeOperand=kotlin.Any GET_VAR 'd: dynamic declared in .test1' type=dynamic origin=null FUN name:test2 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Int VALUE_PARAMETER name:d index:0 type:dynamic BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test2 (d: dynamic): kotlin.Int declared in ' CALL 'public open fun hashCode (): kotlin.Int declared in kotlin.Any' type=kotlin.Int origin=null - $this: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any + $this: TYPE_OP type=kotlin.Any origin=IMPLICIT_DYNAMIC_CAST typeOperand=kotlin.Any GET_VAR 'd: dynamic declared in .test2' type=dynamic origin=null FUN name:test3 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Boolean VALUE_PARAMETER name:d index:0 type:dynamic BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test3 (d: dynamic): kotlin.Boolean declared in ' CALL 'public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in kotlin.Any' type=kotlin.Boolean origin=null - $this: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any + $this: TYPE_OP type=kotlin.Any origin=IMPLICIT_DYNAMIC_CAST typeOperand=kotlin.Any GET_VAR 'd: dynamic declared in .test3' type=dynamic origin=null other: CONST Int type=kotlin.Int value=42 diff --git a/compiler/testData/ir/irJsText/dynamic/dynamicWithSmartCast.txt b/compiler/testData/ir/irJsText/dynamic/dynamicWithSmartCast.txt index b3a34717875..8658c394f86 100644 --- a/compiler/testData/ir/irJsText/dynamic/dynamicWithSmartCast.txt +++ b/compiler/testData/ir/irJsText/dynamic/dynamicWithSmartCast.txt @@ -8,7 +8,7 @@ FILE fqName: fileName:/dynamicWithSmartCast.kt if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.String GET_VAR 'd: dynamic declared in .test1' type=dynamic origin=null then: CALL 'public open fun (): kotlin.Int declared in kotlin.String' type=kotlin.Int origin=GET_PROPERTY - $this: TYPE_OP type=kotlin.String origin=IMPLICIT_CAST typeOperand=kotlin.String + $this: TYPE_OP type=kotlin.String origin=IMPLICIT_DYNAMIC_CAST typeOperand=kotlin.String GET_VAR 'd: dynamic declared in .test1' type=dynamic origin=null BRANCH if: CONST Boolean type=kotlin.Boolean value=true @@ -22,7 +22,7 @@ FILE fqName: fileName:/dynamicWithSmartCast.kt if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Array<*> GET_VAR 'd: dynamic declared in .test2' type=dynamic origin=null then: CALL 'public final fun (): kotlin.Int declared in kotlin.Array' type=kotlin.Int origin=GET_PROPERTY - $this: TYPE_OP type=kotlin.Array origin=IMPLICIT_CAST typeOperand=kotlin.Array + $this: TYPE_OP type=kotlin.Array origin=IMPLICIT_DYNAMIC_CAST typeOperand=kotlin.Array GET_VAR 'd: dynamic declared in .test2' type=dynamic origin=null BRANCH if: CONST Boolean type=kotlin.Boolean value=true diff --git a/compiler/testData/ir/irJsText/dynamic/implicitCastFromDynamic.txt b/compiler/testData/ir/irJsText/dynamic/implicitCastFromDynamic.txt index e960d9ab169..20ed84e826a 100644 --- a/compiler/testData/ir/irJsText/dynamic/implicitCastFromDynamic.txt +++ b/compiler/testData/ir/irJsText/dynamic/implicitCastFromDynamic.txt @@ -11,7 +11,7 @@ FILE fqName: fileName:/implicitCastFromDynamic.kt PROPERTY name:p visibility:public modality:FINAL [val] FIELD PROPERTY_BACKING_FIELD name:p type:kotlin.Int visibility:public [final,static] EXPRESSION_BODY - TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + TYPE_OP type=kotlin.Int origin=IMPLICIT_DYNAMIC_CAST typeOperand=kotlin.Int CALL 'public final fun (): dynamic declared in ' type=dynamic origin=GET_PROPERTY FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:kotlin.Int correspondingProperty: PROPERTY name:p visibility:public modality:FINAL [val] @@ -22,13 +22,13 @@ FILE fqName: fileName:/implicitCastFromDynamic.kt VALUE_PARAMETER name:d index:0 type:dynamic BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test1 (d: dynamic): kotlin.Int declared in ' - TYPE_OP type=kotlin.Int origin=IMPLICIT_CAST typeOperand=kotlin.Int + TYPE_OP type=kotlin.Int origin=IMPLICIT_DYNAMIC_CAST typeOperand=kotlin.Int GET_VAR 'd: dynamic declared in .test1' type=dynamic origin=null FUN name:test2 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Any VALUE_PARAMETER name:d index:0 type:dynamic BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test2 (d: dynamic): kotlin.Any declared in ' - TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any + TYPE_OP type=kotlin.Any origin=IMPLICIT_DYNAMIC_CAST typeOperand=kotlin.Any GET_VAR 'd: dynamic declared in .test2' type=dynamic origin=null FUN name:test3 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Any? VALUE_PARAMETER name:d index:0 type:dynamic @@ -39,7 +39,7 @@ FILE fqName: fileName:/implicitCastFromDynamic.kt VALUE_PARAMETER name:d index:0 type:dynamic BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test4 (d: dynamic): kotlin.String declared in ' - TYPE_OP type=kotlin.String origin=IMPLICIT_CAST typeOperand=kotlin.String + TYPE_OP type=kotlin.String origin=IMPLICIT_DYNAMIC_CAST typeOperand=kotlin.String DYN_OP operator=INVOKE type=dynamic receiver: DYN_MEMBER memberName='member' type=dynamic GET_VAR 'd: dynamic declared in .test4' type=dynamic origin=null