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 36e6b02086b..75e5974f115 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 @@ -37,13 +37,12 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils import org.jetbrains.kotlin.psi2ir.containsNull import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext -import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker -import org.jetbrains.kotlin.types.isError -import org.jetbrains.kotlin.types.isNullabilityFlexible +import org.jetbrains.kotlin.types.typeUtil.isNullableAny +import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNullable -import org.jetbrains.kotlin.types.upperIfFlexible fun insertImplicitCasts(element: IrElement, context: GeneratorContext) { element.transformChildren(InsertImplicitCasts(context.builtIns, context.irBuiltIns, context.typeTranslator), null) @@ -94,10 +93,10 @@ open class InsertImplicitCasts( statements.forEachIndexed { i, irStatement -> if (irStatement is IrExpression) { statements[i] = - if (i == lastIndex) - irStatement.cast(type) - else - irStatement.coerceToUnit() + if (i == lastIndex) + irStatement.cast(type) + else + irStatement.coerceToUnit() } } } @@ -219,13 +218,17 @@ open class InsertImplicitCasts( val valueType = this.type.originalKotlinType!! return when { - KotlinBuiltIns.isUnit(expectedType) -> + expectedType.isUnit() -> coerceToUnit() - valueType.isNullabilityFlexible() && valueType.containsNull() && !expectedType.containsNull() -> { - val nonNullValueType = valueType.upperIfFlexible().makeNotNullable() - implicitCast(nonNullValueType, IrTypeOperator.IMPLICIT_NOTNULL).cast(expectedType) - } + valueType.isDynamic() && !expectedType.isDynamic() -> + if (expectedType.isNullableAny()) + this + else + implicitCast(expectedType, IrTypeOperator.IMPLICIT_CAST) + + valueType.isNullabilityFlexible() && valueType.containsNull() && !expectedType.containsNull() -> + implicitNonNull(valueType, expectedType) KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType.makeNullable()) -> this @@ -243,6 +246,11 @@ open class InsertImplicitCasts( } } + private fun IrExpression.implicitNonNull(valueType: KotlinType, expectedType: KotlinType): IrExpression { + val nonNullValueType = valueType.upperIfFlexible().makeNotNullable() + return implicitCast(nonNullValueType, IrTypeOperator.IMPLICIT_NOTNULL).cast(expectedType) + } + private fun IrExpression.implicitCast( targetType: KotlinType, typeOperator: IrTypeOperator diff --git a/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.kt b/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.kt new file mode 100644 index 00000000000..b9885ea1172 --- /dev/null +++ b/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun test1(d: dynamic) = d.toString() + +fun test2(d: dynamic) = d.hashCode() + +fun test3(d: dynamic) = d.equals(42) \ No newline at end of file diff --git a/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.txt b/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.txt new file mode 100644 index 00000000000..34ba16c501b --- /dev/null +++ b/compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.txt @@ -0,0 +1,26 @@ +FILE fqName: fileName:/dynamicAndMembersOfAny.kt + FUN name:test1 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.String flags: + VALUE_PARAMETER name:d index:0 type:dynamic flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test1(dynamic): String' + CALL 'toString(): String' type=kotlin.String origin=null + $this: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: superTypes:[] + GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null + FUN name:test2 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Int flags: + VALUE_PARAMETER name:d index:0 type:dynamic flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test2(dynamic): Int' + CALL 'hashCode(): Int' type=kotlin.Int origin=null + $this: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: superTypes:[] + GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null + FUN name:test3 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Boolean flags: + VALUE_PARAMETER name:d index:0 type:dynamic flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test3(dynamic): Boolean' + CALL 'equals(Any?): Boolean' type=kotlin.Boolean origin=null + $this: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: superTypes:[] + GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null + other: CONST Int type=kotlin.Int value=42 diff --git a/compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.kt b/compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.kt new file mode 100644 index 00000000000..dfb2f7f0e1b --- /dev/null +++ b/compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test1(d: dynamic) = if (d is String) d.length else -1 + +fun test2(d: dynamic) = if (d is Array<*>) d.size else -1 \ No newline at end of file diff --git a/compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.txt b/compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.txt new file mode 100644 index 00000000000..cbae1d55903 --- /dev/null +++ b/compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.txt @@ -0,0 +1,33 @@ +FILE fqName: fileName:/dynamicWithImplicitCast.kt + FUN name:test1 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Int flags: + VALUE_PARAMETER name:d index:0 type:dynamic flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test1(dynamic): Int' + WHEN type=kotlin.Int origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.String + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:String modality:FINAL visibility:public flags: superTypes:[kotlin.Comparable; kotlin.CharSequence] + GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null + then: CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY + $this: TYPE_OP type=kotlin.String origin=IMPLICIT_CAST typeOperand=kotlin.String + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:String modality:FINAL visibility:public flags: superTypes:[kotlin.Comparable; kotlin.CharSequence] + GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Int type=kotlin.Int value=-1 + FUN name:test2 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Int flags: + VALUE_PARAMETER name:d index:0 type:dynamic flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test2(dynamic): Int' + WHEN type=kotlin.Int origin=null + BRANCH + if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Array<*> + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Array modality:FINAL visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null + then: CALL '(): Int' type=kotlin.Int origin=GET_PROPERTY + $this: TYPE_OP type=kotlin.Array origin=IMPLICIT_CAST typeOperand=kotlin.Array + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Array modality:FINAL visibility:public flags: superTypes:[kotlin.Any] + GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: CONST Int type=kotlin.Int value=-1 diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrJsTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrJsTextTestCaseGenerated.java index b585633606d..761b0f34948 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrJsTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrJsTextTestCaseGenerated.java @@ -41,6 +41,11 @@ public class IrJsTextTestCaseGenerated extends AbstractIrJsTextTestCase { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irJsText/dynamic"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("dynamicAndMembersOfAny.kt") + public void testDynamicAndMembersOfAny() throws Exception { + runTest("compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.kt"); + } + @TestMetadata("dynamicCall.kt") public void testDynamicCall() throws Exception { runTest("compiler/testData/ir/irJsText/dynamic/dynamicCall.kt"); @@ -51,6 +56,11 @@ public class IrJsTextTestCaseGenerated extends AbstractIrJsTextTestCase { runTest("compiler/testData/ir/irJsText/dynamic/dynamicMemberAccess.kt"); } + @TestMetadata("dynamicWithImplicitCast.kt") + public void testDynamicWithImplicitCast() throws Exception { + runTest("compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.kt"); + } + @TestMetadata("invokeOperator.kt") public void testInvokeOperator() throws Exception { runTest("compiler/testData/ir/irJsText/dynamic/invokeOperator.kt");