From 2d61b9a477ef9a624962efe126789623ed461746 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Tue, 10 Oct 2023 19:02:50 +0200 Subject: [PATCH] [FIR] Elvis operator: flexible rhs type implies flexible type of whole elvis It's required for further removing of enhancement of final static fields Also, it's k2-potential feature ^KT-62467 Fixed --- ...cCompilerTestFirTestdataTestGenerated.java | 6 +++ ...osticCompilerFirTestDataTestGenerated.java | 6 +++ .../flexibleTypeAtRightSideOfElvis.fir.txt | 28 +++++++++++++ .../flexibleTypeAtRightSideOfElvis.kt | 36 +++++++++++++++++ .../FirLightTreeDiagnosticsTestGenerated.java | 6 +++ .../FirPsiDiagnosticTestGenerated.java | 6 +++ ...ControlFlowStatementsResolveTransformer.kt | 40 ++++++++++++++++--- 7 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java index 0482b4d5966..a663572ddc3 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java @@ -3351,6 +3351,12 @@ public class DiagnosticCompilerTestFirTestdataTestGenerated extends AbstractDiag runTest("compiler/fir/analysis-tests/testData/resolve/inference/extensionCallableReferences.kt"); } + @Test + @TestMetadata("flexibleTypeAtRightSideOfElvis.kt") + public void testFlexibleTypeAtRightSideOfElvis() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.kt"); + } + @Test @TestMetadata("integerLiteralAsComparable.kt") public void testIntegerLiteralAsComparable() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java index b04b442df9b..fac495e3564 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java @@ -3351,6 +3351,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated runTest("compiler/fir/analysis-tests/testData/resolve/inference/extensionCallableReferences.kt"); } + @Test + @TestMetadata("flexibleTypeAtRightSideOfElvis.kt") + public void testFlexibleTypeAtRightSideOfElvis() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.kt"); + } + @Test @TestMetadata("integerLiteralAsComparable.kt") public void testIntegerLiteralAsComparable() throws Exception { diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.fir.txt b/compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.fir.txt new file mode 100644 index 00000000000..90c7ad6fac4 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.fir.txt @@ -0,0 +1,28 @@ +FILE: main.kt + public final fun testWithStatic1(x1: R|kotlin/String?|): R|kotlin/String| { + lval x: R|kotlin/String?| + R|/x| = R|/x1| ?: Q|JavaClass|.R|/JavaClass.X*s| + ^testWithStatic1 R|/x| + } + public final fun testWithStatic2(x1: R|kotlin/String?|): R|kotlin/String| { + lvar x: R|kotlin/String?| = Null(null) + R|/x| = R|/x1| ?: Q|JavaClass|.R|/JavaClass.f*s|() + ^testWithStatic2 R|/x| + } + public final fun testWithStatic3(x1: R|kotlin/String?|): R|kotlin/String| { + lval x: R|kotlin/String?| = R|/x1| ?: Q|JavaClass|.R|/JavaClass.X*s| + ^testWithStatic3 R|/x| + } + public final fun testWithStatic4(x1: R|kotlin/String?|, w: R|kotlin/Int|): R|kotlin/String| { + lvar x: R|kotlin/String?| + R|/x| = R|/x1| ?: when (R|/w|) { + ==($subj$, Int(42)) -> { + Q|JavaClass|.R|/JavaClass.X*s| + } + else -> { + Q|JavaClass|.R|/JavaClass.f*s|() + } + } + + ^testWithStatic4 R|/x| + } diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.kt b/compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.kt new file mode 100644 index 00000000000..0b6535c0203 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.kt @@ -0,0 +1,36 @@ +// ISSUE: KT-62467 + +// FILE: JavaClass.java + +public class JavaClass { + public static String X = null; + public static String f() { return null; } +} + +// FILE: main.kt + +fun testWithStatic1(x1: String?): String { + val x: String? + x = x1 ?: JavaClass.X + return x +} + +fun testWithStatic2(x1: String?): String { + var x: String? = null + x = x1 ?: JavaClass.f() + return x +} + +fun testWithStatic3(x1: String?): String { + val x: String? = x1 ?: JavaClass.X + return x +} + +fun testWithStatic4(x1: String?, w: Int): String { + var x: String? + x = x1 ?: when (w) { + 42 -> JavaClass.X + else -> JavaClass.f() + } + return x +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java index 02831d0ea81..6ffec6428ad 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java @@ -3351,6 +3351,12 @@ public class FirLightTreeDiagnosticsTestGenerated extends AbstractFirLightTreeDi runTest("compiler/fir/analysis-tests/testData/resolve/inference/extensionCallableReferences.kt"); } + @Test + @TestMetadata("flexibleTypeAtRightSideOfElvis.kt") + public void testFlexibleTypeAtRightSideOfElvis() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.kt"); + } + @Test @TestMetadata("integerLiteralAsComparable.kt") public void testIntegerLiteralAsComparable() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java index 87a1b223b19..9937c805e6f 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java @@ -3351,6 +3351,12 @@ public class FirPsiDiagnosticTestGenerated extends AbstractFirPsiDiagnosticTest runTest("compiler/fir/analysis-tests/testData/resolve/inference/extensionCallableReferences.kt"); } + @Test + @TestMetadata("flexibleTypeAtRightSideOfElvis.kt") + public void testFlexibleTypeAtRightSideOfElvis() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/inference/flexibleTypeAtRightSideOfElvis.kt"); + } + @Test @TestMetadata("integerLiteralAsComparable.kt") public void testIntegerLiteralAsComparable() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt index b65cc4c40e9..0b243ed0888 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt @@ -271,16 +271,44 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes } session.typeContext.run { - if (result.resolvedType.isNullableType() && !result.rhs.resolvedType.isNullableType()) { - // Sometimes return type for special call for elvis operator might be nullable, - // but result is not nullable if the right type is not nullable - result.replaceConeTypeOrNull( - result.resolvedType.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext) - ) + if (result.resolvedType.isNullableType()) { + val rhsResolvedType = result.rhs.resolvedType + // This part of the code is a kind of workaround, and it probably will be resolved by KT-55692 + if (!rhsResolvedType.isNullableType()) { + // It's definitely not a flexible with nullable bound + // Sometimes return type for special call for elvis operator might be nullable, + // but result is not nullable if the right type is not nullable + result.replaceConeTypeOrNull(result.resolvedType.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext)) + } else if (rhsResolvedType is ConeFlexibleType && !rhsResolvedType.lowerBound.isNullableType()) { + result.replaceConeTypeOrNull(result.resultType.makeConeFlexibleTypeWithNotNullableLowerBound(session.typeContext)) + } } } dataFlowAnalyzer.exitElvis(elvisExpression, isLhsNotNull, data.forceFullCompletion) return result } + + private fun ConeKotlinType.makeConeFlexibleTypeWithNotNullableLowerBound(typeContext: ConeTypeContext): ConeKotlinType { + with(typeContext) { + return when (this@makeConeFlexibleTypeWithNotNullableLowerBound) { + is ConeDefinitelyNotNullType -> + error("It can't happen because of the previous `isNullableType` check") + is ConeFlexibleType -> { + if (!lowerBound.isNullableType()) { + this@makeConeFlexibleTypeWithNotNullableLowerBound + } else { + ConeFlexibleType(lowerBound.makeConeTypeDefinitelyNotNullOrNotNull(typeContext) as ConeSimpleKotlinType, upperBound) + } + } + is ConeIntersectionType -> ConeIntersectionType( + intersectedTypes.map { it.makeConeFlexibleTypeWithNotNullableLowerBound(typeContext) } + ) + is ConeSimpleKotlinType -> ConeFlexibleType( + makeConeTypeDefinitelyNotNullOrNotNull(typeContext) as ConeSimpleKotlinType, + this@makeConeFlexibleTypeWithNotNullableLowerBound + ) + } + } + } }