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 ee0015cadb7..89fc66c70f7 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 @@ -39842,6 +39842,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/safeCall/safeCallEqPrimitive.kt"); } + @Test + @TestMetadata("safeCallIOnUninitializedNonNullValue.kt") + public void testSafeCallIOnUninitializedNonNullValue() throws Exception { + runTest("compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt"); + } + @Test @TestMetadata("safeCallNotEqPrimitive.kt") public void testSafeCallNotEqPrimitive() throws Exception { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmSafeCallChainFoldingLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmSafeCallChainFoldingLowering.kt index 54c9a16d305..9446676d7c8 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmSafeCallChainFoldingLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmSafeCallChainFoldingLowering.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.ir.util.isTrivial import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.load.java.JvmAnnotationNames @@ -83,6 +84,7 @@ class JvmSafeCallChainFoldingLowering(val context: JvmBackendContext) : FileLowe // c // } // which can be further simplified depending on the nullability of subexpressions in 'a?.foo() ?: b?.bar()?.qux() ?: c'. + // We should perform such simplification carefully, since a value of a non-null type can be uninitialized at runtime. // In bytecode this produces a chain of temporary STORE-LOAD and IFNULL checks that can be optimized to a compact sequence // of stack operations and IFNULL checks. @@ -119,7 +121,7 @@ class JvmSafeCallChainFoldingLowering(val context: JvmBackendContext) : FileLowe IrConstImpl.boolean(startOffset, endOffset, context.irBuiltIns.booleanType, false) private fun irValNotNull(startOffset: Int, endOffset: Int, irVariable: IrVariable): IrExpression = - if (irVariable.type.isJvmNullable()) + if (irVariable.type.isJvmNullable() || irVariable.initializer?.isTrivial() != true) IrGetValueImpl(startOffset, endOffset, irVariable.symbol).irEqEqNull().irNot() else irTrue(startOffset, endOffset) diff --git a/compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt b/compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt new file mode 100644 index 00000000000..72bab9b37f4 --- /dev/null +++ b/compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt @@ -0,0 +1,18 @@ +abstract class Base() { + init { + foo() + } + + abstract fun foo() +} + +class Derived(val x: String) : Base() { + override fun foo() { + x?.length + } +} + +fun box(): String { + Derived("") + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/safeCallAndElvisChains.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/safeCallAndElvisChains.kt index f2904a71e69..15dfcf23d51 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/safeCallAndElvisChains.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/safeCallAndElvisChains.kt @@ -14,5 +14,5 @@ class A(val x: String) { // JVM_IR_TEMPLATES // 4 IFNULL -// 0 IFNONNULL +// 2 IFNONNULL // 0 ACONST_NULL diff --git a/compiler/testData/codegen/bytecodeText/temporaryVals/notNullReceiversInChain.kt b/compiler/testData/codegen/bytecodeText/temporaryVals/notNullReceiversInChain.kt index d6e064aaf13..0f2b7374798 100644 --- a/compiler/testData/codegen/bytecodeText/temporaryVals/notNullReceiversInChain.kt +++ b/compiler/testData/codegen/bytecodeText/temporaryVals/notNullReceiversInChain.kt @@ -6,8 +6,8 @@ fun test(na: A?) = na?.b?.c?.s // JVM_IR_TEMPLATES -// 1 DUP -// 1 IFNULL +// 3 DUP +// 3 IFNULL // 0 IFNONNULL // 1 ACONST_NULL diff --git a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChain1.kt b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChain1.kt index ea11b7b7fca..d78b05f6887 100644 --- a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChain1.kt +++ b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChain1.kt @@ -6,6 +6,6 @@ fun test(an: A?) = an?.b?.c?.s // JVM_IR_TEMPLATES // 0 ASTORE -// 1 IFNULL +// 3 IFNULL // 0 IFNONNULL // 1 ACONST_NULL \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChainMemberExt1.kt b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChainMemberExt1.kt index 371b59cd72b..6d2ec29cbad 100644 --- a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChainMemberExt1.kt +++ b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChainMemberExt1.kt @@ -13,5 +13,5 @@ object Host { // JVM_IR_TEMPLATES // 0 ASTORE // 1 ACONST_NULL -// 1 IFNULL +// 3 IFNULL // 0 IFNONNULL \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChainMemberExt2.kt b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChainMemberExt2.kt index c212874612e..68348cb1f54 100644 --- a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChainMemberExt2.kt +++ b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallChainMemberExt2.kt @@ -17,5 +17,5 @@ fun test(an: A?) = an?.b?.c?.s // JVM_IR_TEMPLATES // 0 ASTORE // 1 ACONST_NULL -// 1 IFNULL +// 3 IFNULL // 0 IFNONNULL \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallElvisSafeCallElvisSomething.kt b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallElvisSafeCallElvisSomething.kt index d66af2f6b28..ae972b73300 100644 --- a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallElvisSafeCallElvisSomething.kt +++ b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallElvisSafeCallElvisSomething.kt @@ -3,7 +3,7 @@ fun test(a: Any?, b: Any?, c: String) = // JVM_IR_TEMPLATES // 2 IFNULL -// 0 IFNONNULL +// 1 IFNONNULL // 0 ACONST_NULL // JVM_TEMPLATES diff --git a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallWithElvis.kt b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallWithElvis.kt index c7023427017..51316dfb148 100644 --- a/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallWithElvis.kt +++ b/compiler/testData/codegen/bytecodeText/temporaryVals/safeCallWithElvis.kt @@ -9,8 +9,8 @@ fun test(a: Any?) = // 0 valueOf // JVM_IR_TEMPLATES -// 1 DUP -// 1 IFNULL +// 2 DUP +// 2 IFNULL // 0 ACONST_NULL // 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 90da13c7af2..1fecf8fc3f8 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 @@ -39686,6 +39686,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/safeCall/safeCallEqPrimitive.kt"); } + @Test + @TestMetadata("safeCallIOnUninitializedNonNullValue.kt") + public void testSafeCallIOnUninitializedNonNullValue() throws Exception { + runTest("compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt"); + } + @Test @TestMetadata("safeCallNotEqPrimitive.kt") public void testSafeCallNotEqPrimitive() 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 eae9d1cc442..afc0f5b5900 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 @@ -39842,6 +39842,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/safeCall/safeCallEqPrimitive.kt"); } + @Test + @TestMetadata("safeCallIOnUninitializedNonNullValue.kt") + public void testSafeCallIOnUninitializedNonNullValue() throws Exception { + runTest("compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt"); + } + @Test @TestMetadata("safeCallNotEqPrimitive.kt") public void testSafeCallNotEqPrimitive() 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 90e2fda0bfc..1304d78f581 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -31768,6 +31768,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/safeCall/safeCallEqPrimitive.kt"); } + @TestMetadata("safeCallIOnUninitializedNonNullValue.kt") + public void testSafeCallIOnUninitializedNonNullValue() throws Exception { + runTest("compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt"); + } + @TestMetadata("safeCallNotEqPrimitive.kt") public void testSafeCallNotEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/safeCall/safeCallNotEqPrimitive.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index f6b8e76e541..fcc55570328 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -26918,6 +26918,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes runTest("compiler/testData/codegen/box/safeCall/safeCallEqPrimitive.kt"); } + @TestMetadata("safeCallIOnUninitializedNonNullValue.kt") + public void testSafeCallIOnUninitializedNonNullValue() throws Exception { + runTest("compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt"); + } + @TestMetadata("safeCallNotEqPrimitive.kt") public void testSafeCallNotEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/safeCall/safeCallNotEqPrimitive.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 460b09565c5..1f1adfdda53 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -26324,6 +26324,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/safeCall/safeCallEqPrimitive.kt"); } + @TestMetadata("safeCallIOnUninitializedNonNullValue.kt") + public void testSafeCallIOnUninitializedNonNullValue() throws Exception { + runTest("compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt"); + } + @TestMetadata("safeCallNotEqPrimitive.kt") public void testSafeCallNotEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/safeCall/safeCallNotEqPrimitive.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 6a6744d9ee7..559d91a3745 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -26249,6 +26249,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/safeCall/safeCallEqPrimitive.kt"); } + @TestMetadata("safeCallIOnUninitializedNonNullValue.kt") + public void testSafeCallIOnUninitializedNonNullValue() throws Exception { + runTest("compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt"); + } + @TestMetadata("safeCallNotEqPrimitive.kt") public void testSafeCallNotEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/safeCall/safeCallNotEqPrimitive.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 114dc67623a..a4a2744f0ff 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -15618,6 +15618,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/safeCall/safeCallEqPrimitive.kt"); } + @TestMetadata("safeCallIOnUninitializedNonNullValue.kt") + public void testSafeCallIOnUninitializedNonNullValue() throws Exception { + runTest("compiler/testData/codegen/box/safeCall/safeCallIOnUninitializedNonNullValue.kt"); + } + @TestMetadata("safeCallNotEqPrimitive.kt") public void testSafeCallNotEqPrimitive() throws Exception { runTest("compiler/testData/codegen/box/safeCall/safeCallNotEqPrimitive.kt");