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 8a13adfe2e9..db4025c47cf 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 @@ -1850,6 +1850,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt"); } + @Test + @TestMetadata("safeCallWithElvisAndEnhancedNullability.kt") + public void testSafeCallWithElvisAndEnhancedNullability() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt"); + } + + @Test + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @Test @TestMetadata("simple.kt") public void testSimple() 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 6b3ff45e204..80544bb2174 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 @@ -730,6 +730,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvis.kt"); } + @Test + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @Test @TestMetadata("severalInlines.kt") public void testSeveralInlines() throws Exception { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/IfNullExpressionsFusionLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/IfNullExpressionsFusionLowering.kt index 8ebafc7aefc..c0cea9a9d1e 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/IfNullExpressionsFusionLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/IfNullExpressionsFusionLowering.kt @@ -18,7 +18,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.isNullable -import org.jetbrains.kotlin.ir.util.fileOrNull import org.jetbrains.kotlin.ir.util.isTrivial import org.jetbrains.kotlin.ir.util.shallowCopy import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -33,10 +32,10 @@ val ifNullExpressionsFusionPhase = class IfNullExpressionsFusionLowering(val context: CommonBackendContext) : FileLoweringPass { override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(Transformer(irFile)) + irFile.transformChildrenVoid(Transformer()) } - private inner class Transformer(private val currentFile: IrFile) : IrElementTransformerVoid() { + private inner class Transformer : IrElementTransformerVoid() { override fun visitBlock(expression: IrBlock): IrExpression = visitExpression(expression.fuseIfNull()) @@ -131,30 +130,34 @@ class IfNullExpressionsFusionLowering(val context: CommonBackendContext) : FileL private fun IrExpression.isNull(knownVariableSymbol: IrVariableSymbol, knownVariableIsNull: Boolean): Boolean? = when (this) { - is IrConst<*> -> value == null - is IrGetValue -> when { - symbol == knownVariableSymbol -> knownVariableIsNull - !type.isNullable() -> false - else -> null - } + is IrConst<*> -> + value == null + is IrGetValue -> + when { + symbol == knownVariableSymbol -> knownVariableIsNull + !type.isNullable() -> false + else -> null + } is IrConstructorCall, is IrGetSingletonValue, is IrFunctionExpression, is IrCallableReference<*>, is IrClassReference, - is IrGetClass -> false + is IrGetClass -> + false is IrCall -> - if (!type.isNullable() && symbol.owner.isStable()) false else null + if (!type.isNullable()) false else null is IrGetField -> - if (!type.isNullable() && symbol.owner.isStable()) false else null - is IrBlock -> - (statements.singleOrNull() as IrExpression?)?.takeIf { it.type == type }?.isNull(knownVariableSymbol, knownVariableIsNull) + if (!type.isNullable()) false else null + is IrBlock -> { + val singleExpr = statements.singleOrNull() + if (singleExpr is IrExpression && singleExpr.type == type) + singleExpr.isNull(knownVariableSymbol, knownVariableIsNull) + else + null + } else -> null } - - // TODO make calls to the declarations within the same module "stable" - private fun IrDeclaration.isStable() = - fileOrNull == currentFile } private class IfNullExpr( diff --git a/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt b/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt new file mode 100644 index 00000000000..5f0a2c4b362 --- /dev/null +++ b/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt @@ -0,0 +1,50 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +// FILE: safeCallWithElvisAndEnhancedNullability.kt + +import kotlin.test.assertEquals + +fun check(a : A?) : Int { + return a?.y?.x ?: (a?.x ?: 3) +} + +fun checkLeftAssoc(a : A?) : Int { + return (a?.y?.x ?: a?.x) ?: 3 +} + +fun box() : String { + val a1 = A(2, A(1, null)) + val a2 = A(2, null) + val a3 = null + + assertEquals(1, check(a1)) + assertEquals(2, check(a2)) + assertEquals(3, check(a3)) + + assertEquals(1, checkLeftAssoc(a1)) + assertEquals(2, checkLeftAssoc(a2)) + assertEquals(3, checkLeftAssoc(a3)) + + return "OK" +} + +// FILE: A.java +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class A { + private Integer x; + private A y; + + public A(Integer x, A y) { + this.x = x; + this.y = y; + } + + @NotNull + public Integer getX() { return x; } + + @Nullable + public A getY() { return y; } +} diff --git a/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt b/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt new file mode 100644 index 00000000000..f0c656569c4 --- /dev/null +++ b/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt @@ -0,0 +1,33 @@ +// WITH_RUNTIME + +// MODULE: lib +// FILE: a.kt +class A(val x : Int, val y : A?) + +// MODULE: main(lib) +// FILE: safeCallWithElvisMultipleFiles.kt +import kotlin.test.assertEquals + +fun check(a : A?) : Int { + return a?.y?.x ?: (a?.x ?: 3) +} + +fun checkLeftAssoc(a : A?) : Int { + return (a?.y?.x ?: a?.x) ?: 3 +} + +fun box() : String { + val a1 = A(2, A(1, null)) + val a2 = A(2, null) + val a3 = null + + assertEquals(1, check(a1)) + assertEquals(2, check(a2)) + assertEquals(3, check(a3)) + + assertEquals(1, checkLeftAssoc(a1)) + assertEquals(2, checkLeftAssoc(a2)) + assertEquals(3, checkLeftAssoc(a3)) + + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvisMultipleFiles.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvisMultipleFiles.kt new file mode 100644 index 00000000000..0f85e50e31c --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvisMultipleFiles.kt @@ -0,0 +1,18 @@ +// MODULE: lib +// FILE: a.kt +class A(val x : Int, val y : A?) + +// MODULE: main(lib) +// FILE: main.kt +fun check(a : A?) : Int { + return a?.y?.x ?: (a?.x ?: 3) +} + +// 0 valueOf +// 0 Value\s\(\) + +// JVM_TEMPLATES: +// 0 ACONST_NULL + +// JVM_IR_TEMPLATES: +// 1 ACONST_NULL 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 f25770f9bf8..c14fa4b03c3 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 @@ -1760,6 +1760,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt"); } + @Test + @TestMetadata("safeCallWithElvisAndEnhancedNullability.kt") + public void testSafeCallWithElvisAndEnhancedNullability() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt"); + } + + @Test + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @Test @TestMetadata("simple.kt") public void testSimple() 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 76d45f6de8f..a5fafcaaf03 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 @@ -724,6 +724,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvis.kt"); } + @Test + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @Test @TestMetadata("severalInlines.kt") public void testSeveralInlines() 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 13e147b2447..1dbe8808003 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 @@ -1850,6 +1850,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt"); } + @Test + @TestMetadata("safeCallWithElvisAndEnhancedNullability.kt") + public void testSafeCallWithElvisAndEnhancedNullability() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt"); + } + + @Test + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @Test @TestMetadata("simple.kt") public void testSimple() 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 d8e2ebd8bdd..3986d6ea758 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 @@ -730,6 +730,12 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvis.kt"); } + @Test + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @Test @TestMetadata("severalInlines.kt") public void testSeveralInlines() 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 5f51b879436..e07df345bb9 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -1555,6 +1555,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt"); } + @TestMetadata("safeCallWithElvisAndEnhancedNullability.kt") + public void testSafeCallWithElvisAndEnhancedNullability() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt"); + } + + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/boxingOptimization/simple.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 bc27f17d41c..1221a2a0dc8 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 @@ -1030,6 +1030,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt"); } + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/boxingOptimization/simple.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 68fdf0308cf..1e091e77d66 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 @@ -1030,6 +1030,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt"); } + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/boxingOptimization/simple.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 c51d6cb9eb1..78b9f9dde9c 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 @@ -1030,6 +1030,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt"); } + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/boxingOptimization/simple.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 1a2f2500906..544c84537c1 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 @@ -900,6 +900,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt"); } + @TestMetadata("safeCallWithElvisMultipleFiles.kt") + public void testSafeCallWithElvisMultipleFiles() throws Exception { + runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/testData/codegen/box/boxingOptimization/simple.kt");