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 ade853acc3b..06b77dd27f0 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 @@ -42767,12 +42767,24 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/when/stringOptimization/duplicatingItemsSameHashCode3.kt"); } + @Test + @TestMetadata("enhancedNullability.kt") + public void testEnhancedNullability() throws Exception { + runTest("compiler/testData/codegen/box/when/stringOptimization/enhancedNullability.kt"); + } + @Test @TestMetadata("expression.kt") public void testExpression() throws Exception { runTest("compiler/testData/codegen/box/when/stringOptimization/expression.kt"); } + @Test + @TestMetadata("flexibleNullability.kt") + public void testFlexibleNullability() throws Exception { + runTest("compiler/testData/codegen/box/when/stringOptimization/flexibleNullability.kt"); + } + @Test @TestMetadata("nullability.kt") public void testNullability() throws Exception { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt index 9b39d0dc7e7..8c67859a7a7 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/SwitchGenerator.kt @@ -9,10 +9,11 @@ import org.jetbrains.kotlin.codegen.`when`.SwitchCodegen.Companion.preferLookupO import org.jetbrains.kotlin.ir.expressions.* 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.isTrueConst +import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type -import java.util.* // TODO: eliminate the temporary variable class SwitchGenerator(private val expression: IrWhen, private val data: BlockInfo, private val codegen: ExpressionCodegen) { @@ -446,9 +447,19 @@ class SwitchGenerator(private val expression: IrWhen, private val data: BlockInf hashAndSwitchLabels.add(ValueToLabel(key, Label())) } - // Using a switch, the subject string has to be traversed at least twice (hash + comparison * N, where N is #strings hashed into the - // same bucket). The optimization isn't better than an IF cascade when #switch-targets <= 2. - override fun shouldOptimize() = hashAndSwitchLabels.size > 2 + // Using a switch, the subject string has to be traversed at least twice + // (hash + comparison * N, where N is #strings hashed into the same bucket). + // The optimization isn't better than an IF cascade when #switch-targets <= 2. + // + // Generate "optimized" version for @EnhancedNullability subject type + // to model 1.0 behavior causing NPE in case of null value. + // TODO make 'when' with String subject behavior consistent. + // see: + // box/when/stringOptimization/enhancedNullability.kt + // box/when/stringOptimization/flexibleNullability.kt + override fun shouldOptimize() = + hashAndSwitchLabels.size > 2 || + subject.type.hasAnnotation(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION) && hashAndSwitchLabels.isNotEmpty() override fun genSwitch() { with(codegen) { diff --git a/compiler/testData/codegen/box/when/kt47365.kt b/compiler/testData/codegen/box/when/kt47365.kt index fe46fc5ec47..84cfcde8548 100644 --- a/compiler/testData/codegen/box/when/kt47365.kt +++ b/compiler/testData/codegen/box/when/kt47365.kt @@ -1,4 +1,5 @@ // WITH_RUNTIME +// IGNORE_BACKEND: WASM enum class EType { A diff --git a/compiler/testData/codegen/box/when/stringOptimization/enhancedNullability.kt b/compiler/testData/codegen/box/when/stringOptimization/enhancedNullability.kt new file mode 100644 index 00000000000..e320c62c8a9 --- /dev/null +++ b/compiler/testData/codegen/box/when/stringOptimization/enhancedNullability.kt @@ -0,0 +1,75 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +// FILE: enhancedNullability.kt +fun testEmpty() { + when (J.nullString()) { + } +} + +fun testSingle() { + try { + var q = "other" + when (J.nullString()) { + "a" -> q = "A" + } + throw Exception("Should throw NPE, got '$q'") + } catch (e: NullPointerException) { + } +} + +fun testElseOnly() { + var q = "other" + when (J.nullString()) { + else -> q = "A" + } + if (q != "A") { + throw Exception("Expected: 'A', got '$q'") + } +} + +fun testSmall() { + try { + val q = when (J.nullString()) { + "a" -> "A" + else -> "other" + } + throw Exception("Should throw NPE, got '$q'") + } catch (e: NullPointerException) { + } +} + +fun testBigger() { + try { + val q = when (J.nullString()) { + "a" -> "A" + "b" -> "B" + "c" -> "C" + "d" -> "D" + "e" -> "E" + "f" -> "F" + "g" -> "G" + "h" -> "H" + else -> "other" + } + throw Exception("Should throw NPE, got '$q'") + } catch (e: NullPointerException) { + } +} + + +fun box(): String { + testEmpty() + testSingle() + testElseOnly() + testSmall() + testBigger() + return "OK" +} + +// FILE: J.java +import org.jetbrains.annotations.NotNull; + +public class J { + public static @NotNull String nullString() { return null; } +} diff --git a/compiler/testData/codegen/box/when/stringOptimization/flexibleNullability.kt b/compiler/testData/codegen/box/when/stringOptimization/flexibleNullability.kt new file mode 100644 index 00000000000..b4e50d32d39 --- /dev/null +++ b/compiler/testData/codegen/box/when/stringOptimization/flexibleNullability.kt @@ -0,0 +1,71 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +// FILE: flexibleNullability.kt +fun testEmpty() { + when (J.nullString()) { + } +} + +fun testSingle() { + var q = "other" + when (J.nullString()) { + "a" -> q = "A" + } + if (q != "other") { + throw Exception("Expected 'other', got '$q'") + } + +} + +fun testElseOnly() { + var q = "other" + when (J.nullString()) { + else -> q = "A" + } + if (q != "A") { + throw Exception("Expected: 'A', got '$q'") + } +} + +fun testSmall() { + val q = when (J.nullString()) { + "a" -> "A" + else -> "other" + } + if (q != "other") { + throw Exception("Expected 'other', got '$q'") + } +} + +fun testBigger() { + val q = when (J.nullString()) { + "a" -> "A" + "b" -> "B" + "c" -> "C" + "d" -> "D" + "e" -> "E" + "f" -> "F" + "g" -> "G" + "h" -> "H" + else -> "other" + } + if (q != "other") { + throw Exception("Expected 'other', got '$q'") + } +} + + +fun box(): String { + testEmpty() + testSingle() + testElseOnly() + testSmall() + testBigger() + return "OK" +} + +// FILE: J.java +public class J { + public static String nullString() { return 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 70bdf7e6eb4..ab5d59ba4a4 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 @@ -42725,12 +42725,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/when/stringOptimization/duplicatingItemsSameHashCode3.kt"); } + @Test + @TestMetadata("enhancedNullability.kt") + public void testEnhancedNullability() throws Exception { + runTest("compiler/testData/codegen/box/when/stringOptimization/enhancedNullability.kt"); + } + @Test @TestMetadata("expression.kt") public void testExpression() throws Exception { runTest("compiler/testData/codegen/box/when/stringOptimization/expression.kt"); } + @Test + @TestMetadata("flexibleNullability.kt") + public void testFlexibleNullability() throws Exception { + runTest("compiler/testData/codegen/box/when/stringOptimization/flexibleNullability.kt"); + } + @Test @TestMetadata("nullability.kt") public void testNullability() 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 24a0ef0fe19..0a9c0ff6dee 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 @@ -42767,12 +42767,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/when/stringOptimization/duplicatingItemsSameHashCode3.kt"); } + @Test + @TestMetadata("enhancedNullability.kt") + public void testEnhancedNullability() throws Exception { + runTest("compiler/testData/codegen/box/when/stringOptimization/enhancedNullability.kt"); + } + @Test @TestMetadata("expression.kt") public void testExpression() throws Exception { runTest("compiler/testData/codegen/box/when/stringOptimization/expression.kt"); } + @Test + @TestMetadata("flexibleNullability.kt") + public void testFlexibleNullability() throws Exception { + runTest("compiler/testData/codegen/box/when/stringOptimization/flexibleNullability.kt"); + } + @Test @TestMetadata("nullability.kt") public void testNullability() 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 b3e606a91d1..b4b356a6713 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -34445,11 +34445,21 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/when/stringOptimization/duplicatingItemsSameHashCode3.kt"); } + @TestMetadata("enhancedNullability.kt") + public void testEnhancedNullability() throws Exception { + runTest("compiler/testData/codegen/box/when/stringOptimization/enhancedNullability.kt"); + } + @TestMetadata("expression.kt") public void testExpression() throws Exception { runTest("compiler/testData/codegen/box/when/stringOptimization/expression.kt"); } + @TestMetadata("flexibleNullability.kt") + public void testFlexibleNullability() throws Exception { + runTest("compiler/testData/codegen/box/when/stringOptimization/flexibleNullability.kt"); + } + @TestMetadata("nullability.kt") public void testNullability() throws Exception { runTest("compiler/testData/codegen/box/when/stringOptimization/nullability.kt");