diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 3c2ee813f72..0c164d9fe47 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -3296,6 +3296,39 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT } } + @TestMetadata("compiler/testData/codegen/box/casts/javaInterop") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaInterop extends AbstractFirBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM_IR, testDataFilePath, "// IGNORE_BACKEND_FIR: "); + } + + public void testAllFilesPresentInJavaInterop() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/casts/javaInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("castWithWrongType.kt") + public void testCastWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/castWithWrongType.kt"); + } + + @TestMetadata("implicitNotNullWithWrongType.kt") + public void testImplicitNotNullWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/implicitNotNullWithWrongType.kt"); + } + + @TestMetadata("instanceOfWithWrongType.kt") + public void testInstanceOfWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/instanceOfWithWrongType.kt"); + } + + @TestMetadata("safeCastWithWrongType.kt") + public void testSafeCastWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/safeCastWithWrongType.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/casts/literalExpressionAsGenericArgument") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt index cba490d4fc7..10fa6c91c5a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt @@ -58,7 +58,7 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil type.isReifiedTypeParameter -> irIs(argument, type) argument.type.isNullable() && type.isNullable() -> { - irLetS(argument) { valueSymbol -> + irLetS(argument, irType = context.irBuiltIns.anyNType) { valueSymbol -> context.oror( irEqualsNull(irGet(valueSymbol.owner)), irIs(irGet(valueSymbol.owner), type.makeNotNull()) @@ -76,7 +76,7 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil builder.irAs(argument, type) argument.type.isNullable() && !type.isNullable() -> with(builder) { - irLetS(argument) { valueSymbol -> + irLetS(argument, irType = context.irBuiltIns.anyNType) { valueSymbol -> irIfNull( type, irGet(valueSymbol.owner), @@ -118,7 +118,11 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil expression.transformChildrenVoid() expression } else { - irLetS(expression.argument.transformVoid(), IrStatementOrigin.SAFE_CALL) { valueSymbol -> + irLetS( + expression.argument.transformVoid(), + IrStatementOrigin.SAFE_CALL, + irType = context.irBuiltIns.anyNType + ) { valueSymbol -> irIfThenElse( expression.type, lowerInstanceOf(irGet(valueSymbol.owner), expression.typeOperand.makeNotNull()), @@ -138,7 +142,7 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil val (startOffset, endOffset) = expression.extents() val source = sourceViewFor(parent as IrDeclaration).subSequence(startOffset, endOffset).toString() - fun checkExpressionValue(valueSymbol: IrValueSymbol): IrExpression = + irLetS(expression.argument.transformVoid(), irType = context.irBuiltIns.anyNType) { valueSymbol -> irComposite(resultType = expression.type) { +irCall(checkExpressionValueIsNotNull).apply { putValueArgument(0, irGet(valueSymbol.owner)) @@ -146,14 +150,6 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil } +irGet(valueSymbol.owner) } - - val argument = expression.argument.transformVoid() - if (argument is IrGetValue) { - checkExpressionValue(argument.symbol) - } else { - irLetS(argument) { valueSymbol -> - checkExpressionValue(valueSymbol) - } } } 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 0e6629ece70..5b833a40a8d 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 @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.util.isImmutable import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.types.KotlinType @@ -23,14 +24,23 @@ inline fun IrBuilderWithScope.irLetS( value: IrExpression, origin: IrStatementOrigin? = null, nameHint: String? = null, + irType: IrType? = null, body: (IrValueSymbol) -> IrExpression ): IrExpression { - val irTemporary = scope.createTemporaryVariable(value, nameHint) - val irResult = body(irTemporary.symbol) - val irBlock = IrBlockImpl(startOffset, endOffset, irResult.type, origin) - irBlock.statements.add(irTemporary) - irBlock.statements.add(irResult) - return irBlock + val (valueSymbol, irTemporary) = if (value is IrGetValue && value.symbol.owner.isImmutable) { + value.symbol to null + } else { + scope.createTemporaryVariable(value, nameHint, irType = irType).let { it.symbol to it } + } + val irResult = body(valueSymbol) + return if (irTemporary == null) { + irResult + } else { + val irBlock = IrBlockImpl(startOffset, endOffset, irResult.type, origin) + irBlock.statements.add(irTemporary) + irBlock.statements.add(irResult) + irBlock + } } diff --git a/compiler/testData/codegen/box/casts/javaInterop/castWithWrongType.kt b/compiler/testData/codegen/box/casts/javaInterop/castWithWrongType.kt new file mode 100644 index 00000000000..eff3e0e2f00 --- /dev/null +++ b/compiler/testData/codegen/box/casts/javaInterop/castWithWrongType.kt @@ -0,0 +1,17 @@ +// KT-39520 +// TARGET_BACKEND: JVM +// FILE: A.java +public class A { + private T value; + private A(T x) { value = x; } + public static T f() { + return ((A) new A(1)).value; + } +} + +// FILE: test.kt + +fun box(): String { + val x = A.f() as Int + return if (x == 1) "OK" else "Fail" +} diff --git a/compiler/testData/codegen/box/casts/javaInterop/implicitNotNullWithWrongType.kt b/compiler/testData/codegen/box/casts/javaInterop/implicitNotNullWithWrongType.kt new file mode 100644 index 00000000000..38a1280d124 --- /dev/null +++ b/compiler/testData/codegen/box/casts/javaInterop/implicitNotNullWithWrongType.kt @@ -0,0 +1,17 @@ +// KT-39520 +// TARGET_BACKEND: JVM +// FILE: A.java +public class A { + private T value; + private A(T x) { value = x; } + public static T f() { + return ((A) new A(1)).value; + } +} + +// FILE: test.kt + +fun box(): String { + val x: Any = A.f() + return if (x == 1) "OK" else "Fail" +} diff --git a/compiler/testData/codegen/box/casts/javaInterop/instanceOfWithWrongType.kt b/compiler/testData/codegen/box/casts/javaInterop/instanceOfWithWrongType.kt new file mode 100644 index 00000000000..8d088d370cc --- /dev/null +++ b/compiler/testData/codegen/box/casts/javaInterop/instanceOfWithWrongType.kt @@ -0,0 +1,16 @@ +// KT-39520 +// TARGET_BACKEND: JVM +// FILE: A.java +public class A { + private T value; + private A(T x) { value = x; } + public static T f() { + return ((A) new A(1)).value; + } +} + +// FILE: test.kt + +fun box(): String { + return if (A.f() is CharSequence?) "Fail" else "OK" +} diff --git a/compiler/testData/codegen/box/casts/javaInterop/safeCastWithWrongType.kt b/compiler/testData/codegen/box/casts/javaInterop/safeCastWithWrongType.kt new file mode 100644 index 00000000000..db34fa1efc1 --- /dev/null +++ b/compiler/testData/codegen/box/casts/javaInterop/safeCastWithWrongType.kt @@ -0,0 +1,16 @@ +// KT-39520 +// TARGET_BACKEND: JVM +// FILE: A.java +public class A { + private T value; + private A(T x) { value = x; } + public static T f() { + return ((A) new A(1)).value; + } +} + +// FILE: test.kt + +fun box(): String { + return A.f() as? String ?: "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index b2aca54122f..7e2215e93c0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -3316,6 +3316,39 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/casts/javaInterop") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaInterop extends AbstractBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInJavaInterop() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/casts/javaInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("castWithWrongType.kt") + public void testCastWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/castWithWrongType.kt"); + } + + @TestMetadata("implicitNotNullWithWrongType.kt") + public void testImplicitNotNullWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/implicitNotNullWithWrongType.kt"); + } + + @TestMetadata("instanceOfWithWrongType.kt") + public void testInstanceOfWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/instanceOfWithWrongType.kt"); + } + + @TestMetadata("safeCastWithWrongType.kt") + public void testSafeCastWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/safeCastWithWrongType.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/casts/literalExpressionAsGenericArgument") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index a8706a3e18c..27d2f887b61 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -3316,6 +3316,39 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } + @TestMetadata("compiler/testData/codegen/box/casts/javaInterop") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaInterop extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInJavaInterop() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/casts/javaInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("castWithWrongType.kt") + public void testCastWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/castWithWrongType.kt"); + } + + @TestMetadata("implicitNotNullWithWrongType.kt") + public void testImplicitNotNullWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/implicitNotNullWithWrongType.kt"); + } + + @TestMetadata("instanceOfWithWrongType.kt") + public void testInstanceOfWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/instanceOfWithWrongType.kt"); + } + + @TestMetadata("safeCastWithWrongType.kt") + public void testSafeCastWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/safeCastWithWrongType.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/casts/literalExpressionAsGenericArgument") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 1259ee15c24..b75fe5f761b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -3296,6 +3296,39 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/casts/javaInterop") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaInterop extends AbstractIrBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInJavaInterop() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/casts/javaInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("castWithWrongType.kt") + public void testCastWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/castWithWrongType.kt"); + } + + @TestMetadata("implicitNotNullWithWrongType.kt") + public void testImplicitNotNullWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/implicitNotNullWithWrongType.kt"); + } + + @TestMetadata("instanceOfWithWrongType.kt") + public void testInstanceOfWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/instanceOfWithWrongType.kt"); + } + + @TestMetadata("safeCastWithWrongType.kt") + public void testSafeCastWithWrongType() throws Exception { + runTest("compiler/testData/codegen/box/casts/javaInterop/safeCastWithWrongType.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/casts/literalExpressionAsGenericArgument") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index d8da68b5abc..b273e41a0e8 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -2631,6 +2631,19 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes } } + @TestMetadata("compiler/testData/codegen/box/casts/javaInterop") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaInterop extends AbstractIrJsCodegenBoxES6Test { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath); + } + + public void testAllFilesPresentInJavaInterop() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/casts/javaInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); + } + } + @TestMetadata("compiler/testData/codegen/box/casts/literalExpressionAsGenericArgument") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 57700872b9d..26862eeb4f7 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -2631,6 +2631,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/casts/javaInterop") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaInterop extends AbstractIrJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInJavaInterop() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/casts/javaInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + } + @TestMetadata("compiler/testData/codegen/box/casts/literalExpressionAsGenericArgument") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 471823fa815..de5dd479158 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -2631,6 +2631,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/casts/javaInterop") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaInterop extends AbstractJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); + } + + public void testAllFilesPresentInJavaInterop() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/casts/javaInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); + } + } + @TestMetadata("compiler/testData/codegen/box/casts/literalExpressionAsGenericArgument") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)