diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 16abd1c2146..d959288e6a1 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -4685,6 +4685,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/casts/kt48987.kt"); } + @Test + @TestMetadata("kt50577.kt") + public void testKt50577() throws Exception { + runTest("compiler/testData/codegen/box/casts/kt50577.kt"); + } + @Test @TestMetadata("lambdaToUnitCast.kt") public void testLambdaToUnitCast() throws Exception { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java index 426283a5e80..4b8646b1f1c 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java @@ -4686,6 +4686,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/deterministicNotNullChecks.kt"); } + @Test + @TestMetadata("exclExclAsNotNullType.kt") + public void testExclExclAsNotNullType() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/exclExclAsNotNullType.kt"); + } + @Test @TestMetadata("expressionValueIsNotNull.kt") public void testExpressionValueIsNotNull() throws Exception { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt index d60451eb60a..8770951f569 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt @@ -83,7 +83,11 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil private fun lowerCast(argument: IrExpression, type: IrType): IrExpression = when { type.isReifiedTypeParameter -> builder.irAs(argument, type) - argument.type.isNullable() && !type.isNullable() -> + argument.type.isInlineClassType() && argument.type.isSubtypeOfClass(type.erasedUpperBound.symbol) -> + argument + type.isNullable() || argument.isDefinitelyNotNull() -> + builder.irAs(argument, type) + else -> { with(builder) { irLetS(argument, irType = context.irBuiltIns.anyNType) { valueSymbol -> irIfNull( @@ -92,14 +96,30 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil irCall(throwTypeCastException).apply { putValueArgument(0, irString("null cannot be cast to non-null type ${type.render()}")) }, - lowerCast(irGet(valueSymbol.owner), type.makeNullable()) + builder.irAs(irGet(valueSymbol.owner), type.makeNullable()) ) } } - argument.type.isInlineClassType() && argument.type.isSubtypeOfClass(type.erasedUpperBound.symbol) -> - argument - else -> - builder.irAs(argument, type) + } + } + + // TODO extract null check elimination on IR somewhere? + private fun IrExpression.isDefinitelyNotNull(): Boolean = + when (this) { + is IrGetValue -> + this.symbol.owner.isDefinitelyNotNullVal() + is IrGetClass, + is IrConstructorCall -> + true + is IrCall -> + this.symbol == context.irBuiltIns.checkNotNullSymbol + else -> + false + } + + private fun IrValueDeclaration.isDefinitelyNotNullVal(): Boolean { + val irVariable = this as? IrVariable ?: return false + return !irVariable.isVar && irVariable.initializer?.isDefinitelyNotNull() == true } private val jvmIndyLambdaMetafactoryIntrinsic = context.ir.symbols.indyLambdaMetafactoryIntrinsic diff --git a/compiler/testData/codegen/box/casts/kt50577.kt b/compiler/testData/codegen/box/casts/kt50577.kt new file mode 100644 index 00000000000..7f2f134fd04 --- /dev/null +++ b/compiler/testData/codegen/box/casts/kt50577.kt @@ -0,0 +1,26 @@ +abstract class A { + abstract val x: Any + + init { + castX(this) + } +} + +class B : A() { + override val x: Any = "abc" +} + +fun castX(a: A) { + a.x as String +} + +fun box(): String { + try { + B() + } catch (e: NullPointerException) { + return "OK" + } catch (e: ClassCastException) { + return "OK" // JS + } + return "Failed: should throw NPE" +} diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/exclExclAsNotNullType.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/exclExclAsNotNullType.kt new file mode 100644 index 00000000000..c37061e6cce --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/exclExclAsNotNullType.kt @@ -0,0 +1,8 @@ +fun test(a: Any?) = a!! as String + +// 1 checkNotNull +// JVM_IR_TEMPLATES +// 0 IFNULL +// 0 IFNONNULL +// 0 NullPointerException +// 0 ASTORE diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/noNullCheckAfterCast.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/noNullCheckAfterCast.kt index 1213e12832d..35af2351982 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/noNullCheckAfterCast.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/noNullCheckAfterCast.kt @@ -26,9 +26,4 @@ fun test3() { fun getB(): B = B() -// JVM_TEMPLATES // 1 IFNONNULL - -// There should be no null checks in the bytecode. -// JVM_IR_TEMPLATES -// 0 IFNONNULL diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 33b242dffcf..c4cac5cee96 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -4577,6 +4577,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/casts/kt48927_privateMethodOnDerivedCastToBase.kt"); } + @Test + @TestMetadata("kt50577.kt") + public void testKt50577() throws Exception { + runTest("compiler/testData/codegen/box/casts/kt50577.kt"); + } + @Test @TestMetadata("lambdaToUnitCast.kt") public void testLambdaToUnitCast() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java index 445c56a72de..f06503a7552 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java @@ -4536,6 +4536,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/deterministicNotNullChecks.kt"); } + @Test + @TestMetadata("exclExclAsNotNullType.kt") + public void testExclExclAsNotNullType() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/exclExclAsNotNullType.kt"); + } + @Test @TestMetadata("expressionValueIsNotNull.kt") public void testExpressionValueIsNotNull() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 3c2a30acec8..548f815511f 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -4685,6 +4685,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/casts/kt48987.kt"); } + @Test + @TestMetadata("kt50577.kt") + public void testKt50577() throws Exception { + runTest("compiler/testData/codegen/box/casts/kt50577.kt"); + } + @Test @TestMetadata("lambdaToUnitCast.kt") public void testLambdaToUnitCast() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java index ec576d1e830..02ca05d4a37 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java @@ -4686,6 +4686,12 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/deterministicNotNullChecks.kt"); } + @Test + @TestMetadata("exclExclAsNotNullType.kt") + public void testExclExclAsNotNullType() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/nullCheckOptimization/exclExclAsNotNullType.kt"); + } + @Test @TestMetadata("expressionValueIsNotNull.kt") public void testExpressionValueIsNotNull() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index cae07b58b7f..d25d0b9dc60 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -3996,6 +3996,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/casts/kt48927_privateMethodOnDerivedCastToBase.kt"); } + @TestMetadata("kt50577.kt") + public void testKt50577() throws Exception { + runTest("compiler/testData/codegen/box/casts/kt50577.kt"); + } + @TestMetadata("lambdaToUnitCast.kt") public void testLambdaToUnitCast() throws Exception { runTest("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java index faeec50fb47..f70e9f00d1f 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java @@ -3371,6 +3371,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/casts/kt48927_privateMethodOnDerivedCastToBase.kt"); } + @Test + @TestMetadata("kt50577.kt") + public void testKt50577() throws Exception { + runTest("compiler/testData/codegen/box/casts/kt50577.kt"); + } + @Test @TestMetadata("lambdaToUnitCast.kt") public void testLambdaToUnitCast() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java index 30d12923169..3a9a9054b02 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java @@ -3413,6 +3413,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/casts/kt48927_privateMethodOnDerivedCastToBase.kt"); } + @Test + @TestMetadata("kt50577.kt") + public void testKt50577() throws Exception { + runTest("compiler/testData/codegen/box/casts/kt50577.kt"); + } + @Test @TestMetadata("lambdaToUnitCast.kt") public void testLambdaToUnitCast() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index d7bbb21e4fc..c4cde0eb24a 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -3016,6 +3016,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/casts/kt48927_privateMethodOnDerivedCastToBase.kt"); } + @TestMetadata("kt50577.kt") + public void testKt50577() throws Exception { + runTest("compiler/testData/codegen/box/casts/kt50577.kt"); + } + @TestMetadata("lambdaToUnitCast.kt") public void testLambdaToUnitCast() throws Exception { runTest("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt"); diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/ExternalTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/ExternalTestGenerated.java index 6ff999c7eff..17818fb6a7c 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/ExternalTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/ExternalTestGenerated.java @@ -3487,6 +3487,12 @@ public class ExternalTestGenerated extends AbstractExternalNativeBlackBoxTest { runTest("compiler/testData/codegen/box/casts/kt48927_privateMethodOnDerivedCastToBase.kt"); } + @Test + @TestMetadata("kt50577.kt") + public void testKt50577() throws Exception { + runTest("compiler/testData/codegen/box/casts/kt50577.kt"); + } + @Test @TestMetadata("lambdaToUnitCast.kt") public void testLambdaToUnitCast() throws Exception {