diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index 0396a769c2c..c0020561a8f 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -34962,6 +34962,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/smartCasts/dataFlowInfoForArguments.kt"); } + @Test + @TestMetadata("disableDfaBooleanVariables.kt") + public void testDisableDfaBooleanVariables() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/disableDfaBooleanVariables.kt"); + } + @Test @TestMetadata("doubleLambdaArgument.kt") public void testDoubleLambdaArgument() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 97c850cfbce..f3cd68a8cb6 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -34962,6 +34962,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/smartCasts/dataFlowInfoForArguments.kt"); } + @Test + @TestMetadata("disableDfaBooleanVariables.kt") + public void testDisableDfaBooleanVariables() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/disableDfaBooleanVariables.kt"); + } + @Test @TestMetadata("doubleLambdaArgument.kt") public void testDoubleLambdaArgument() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index f328df48dc2..f7703ea24ae 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -32606,6 +32606,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/smartCasts/dataFlowInfoForArguments.kt"); } + @Test + @TestMetadata("disableDfaBooleanVariables.kt") + public void testDisableDfaBooleanVariables() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/disableDfaBooleanVariables.kt"); + } + @Test @TestMetadata("doubleLambdaArgument.kt") public void testDoubleLambdaArgument() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index b728c1e6434..9d9b52cc7f9 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -32732,6 +32732,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/smartCasts/dataFlowInfoForArguments.kt"); } + @Test + @TestMetadata("disableDfaBooleanVariables.kt") + public void testDisableDfaBooleanVariables() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/disableDfaBooleanVariables.kt"); + } + @Test @TestMetadata("doubleLambdaArgument.kt") public void testDoubleLambdaArgument() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index 47e85b43cd6..ea098c6ccc5 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.resolve.dfa import kotlinx.collections.immutable.toPersistentSet +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.contracts.description.LogicOperationKind import org.jetbrains.kotlin.contracts.description.canBeRevisited import org.jetbrains.kotlin.descriptors.Modality @@ -1086,9 +1087,15 @@ abstract class FirDataFlowAnalyzer( // if (b != null) { /* a != null, but a.x could have changed */ } logicSystem.translateVariableFromConditionInStatements(flow, initializerVariable, propertyVariable) } - } else if (initializerVariable != null && propertyVariable.isStable) { + } else if (initializerVariable != null && propertyVariable.isStable && + (components.session.languageVersionSettings.supportsFeature(LanguageFeature.DfaBooleanVariables) || + !initializer.resolvedType.isBoolean) + ) { // val b = x is String // if (b) { /* x is String */ } + + // val b = x?.foo() // `x?.foo()` is synthetic + // if (b != null) { /* x != null */ } logicSystem.translateVariableFromConditionInStatements(flow, initializerVariable, propertyVariable) } diff --git a/compiler/testData/diagnostics/tests/smartCasts/disableDfaBooleanVariables.kt b/compiler/testData/diagnostics/tests/smartCasts/disableDfaBooleanVariables.kt new file mode 100644 index 00000000000..b4558d9f279 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/disableDfaBooleanVariables.kt @@ -0,0 +1,131 @@ +// FIR_IDENTICAL +// DIAGNOSTICS: -DEBUG_INFO_SMARTCAST +// LANGUAGE:-DfaBooleanVariables + +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +fun test1() { + val nullableString: String? = "" + val test = nullableString != null + if (test) nullableString.length + if (nullableString != null) nullableString.length +} + +class A { + val a: String? = "" +} +fun test3(a: A) { + val test = a.a != null + if (test) a.a.length + if (a.a != null) { + a.a.length + } +} + +fun test4() { + val nullableAny: Any? = "" + val test = nullableAny != null && nullableAny is String? + if (test) nullableAny.length + if (nullableAny != null && nullableAny is String?) nullableAny.length +} + +fun test7(bar: Any) { + val test = bar is String + if (test) bar.length + if (bar is String) bar.length +} + +fun test8(bar: Any) { + val test = bar is String + when { + test -> bar.length + } + when { + bar is String -> bar.length + } +} + +fun test9(bar: Any) { + bar is String && bar.length == 0 + val test = bar is String + test && bar.length +} + +fun test10(bar: Any) { + val test = bar !is String + run { + if (test) return@run + bar.length + } + run { + if (bar !is String) return@run + bar.length + } +} + +fun test11(bar: Any, baz: Any) { + val test = baz is String && baz === bar + val test2 = baz is String + val test3 = baz === bar + val test4 = test2 && test3 + + if (test) bar.length + if (test2 && test3) bar.length + if (baz is String && test3) bar.length + if (test2 && baz === bar) bar.length + if (test4) bar.length + + if (baz is String && baz === bar) bar.length +} + +fun test12(bar: Any) { + val test = bar !is String + if (!test) bar.length +} + +fun test13(bar: Any) { + val test = bar is String + if (test == true) bar.length + when (test) { + true -> bar.length + false -> {} + } + + val test2: Boolean? = bar is String + if (test2 == true) bar.length +} + +fun test14(bar: Any) { + val test = (bar as? String) != null + if (test) bar.length +} + +fun test15(bar: Any) { + val test = bar is String + val test2 = test + if (test2) bar.length +} + +fun test16(bar: Any) { + val x = bar.isString() + if (x) bar.length + if (bar.isString()) bar.length +} + +@OptIn(ExperimentalContracts::class) +fun Any.isString(): Boolean { + contract { + returns(true) implies (this@isString is String) + } + return this is String +} + +fun String.isEmpty(): Boolean = length == 0 +fun test17(a: String?) { + val x = a?.length + if (x != null) a.length + + val y = a?.isEmpty() + if (y == true) a.length +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index f91811db018..1daa7dfaa29 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -34962,6 +34962,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/smartCasts/dataFlowInfoForArguments.kt"); } + @Test + @TestMetadata("disableDfaBooleanVariables.kt") + public void testDisableDfaBooleanVariables() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/disableDfaBooleanVariables.kt"); + } + @Test @TestMetadata("doubleLambdaArgument.kt") public void testDoubleLambdaArgument() throws Exception { diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 6cf77803bdd..25f63127879 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -308,6 +308,7 @@ enum class LanguageFeature( ForbidInferringTypeVariablesIntoEmptyIntersection(KOTLIN_2_0, kind = BUG_FIX), // KT-51221 ProhibitDefaultArgumentsInExpectActualizedByFakeOverride(KOTLIN_2_0, kind = BUG_FIX), // KT-62036 DisableCompatibilityModeForNewInference(KOTLIN_2_0, kind = OTHER), // KT-63558 (umbrella), KT-64306, KT-64307, KT-64308 + DfaBooleanVariables(KOTLIN_2_0), // KT-25747 // 2.1