K2: Put boolean DFA variables under a feature flag

^KT-62547 Fixed

I also tested the commit by disabling DfaBooleanVariables in all tests
and checking what tests will fail

I didn't find anything that worked in K1, but doesn't work in K2 with
DfaBooleanVariables disabled

Related tests:
- DiagnosticCompilerTestFirTestdataTestGenerated$Resolve$Smartcasts
- LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated$Resolve$Smartcasts
- PreFirIdeSpecTestGenerated$NotLinked$Dfa
- DiagnosticCompilerTestFE10TestdataTestGenerated$Tests$SmartCasts
- FirPsiDiagnosticTestGenerated$Resolve$Expresssions.testSyntheticSmartCast
This commit is contained in:
Nikita Bobko
2024-01-31 12:25:32 +01:00
committed by Space Team
parent 118a5cee06
commit d69deb97fe
8 changed files with 170 additions and 1 deletions
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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)
}
@@ -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<!UNSAFE_CALL!>.<!>length
if (nullableString != null) nullableString.length
}
class A {
val a: String? = ""
}
fun test3(a: A) {
val test = a.a != null
if (test) a.a<!UNSAFE_CALL!>.<!>length
if (a.a != null) {
a.a.length
}
}
fun test4() {
val nullableAny: Any? = ""
val test = nullableAny != null && nullableAny is String?
if (test) nullableAny.<!UNRESOLVED_REFERENCE!>length<!>
if (nullableAny != null && nullableAny is String?) nullableAny.length
}
fun test7(bar: Any) {
val test = bar is String
if (test) bar.<!UNRESOLVED_REFERENCE!>length<!>
if (bar is String) bar.length
}
fun test8(bar: Any) {
val test = bar is String
when {
test -> bar.<!UNRESOLVED_REFERENCE!>length<!>
}
when {
bar is String -> bar.length
}
}
fun test9(bar: Any) {
bar is String && bar.length == 0
val test = bar is String
test && bar.<!UNRESOLVED_REFERENCE!>length<!>
}
fun test10(bar: Any) {
val test = bar !is String
run {
if (test) return@run
bar.<!UNRESOLVED_REFERENCE!>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.<!UNRESOLVED_REFERENCE!>length<!>
if (test2 && test3) bar.<!UNRESOLVED_REFERENCE!>length<!>
if (baz is String && test3) bar.<!UNRESOLVED_REFERENCE!>length<!>
if (test2 && baz === bar) bar.<!UNRESOLVED_REFERENCE!>length<!>
if (test4) bar.<!UNRESOLVED_REFERENCE!>length<!>
if (baz is String && baz === bar) bar.length
}
fun test12(bar: Any) {
val test = bar !is String
if (!test) bar.<!UNRESOLVED_REFERENCE!>length<!>
}
fun test13(bar: Any) {
val test = bar is String
if (test == true) bar.<!UNRESOLVED_REFERENCE!>length<!>
when (test) {
true -> bar.<!UNRESOLVED_REFERENCE!>length<!>
false -> {}
}
val test2: Boolean? = bar is String
if (test2 == true) bar.<!UNRESOLVED_REFERENCE!>length<!>
}
fun test14(bar: Any) {
val test = (bar as? String) != null
if (test) bar.<!UNRESOLVED_REFERENCE!>length<!>
}
fun test15(bar: Any) {
val test = bar is String
val test2 = test
if (test2) bar.<!UNRESOLVED_REFERENCE!>length<!>
}
fun test16(bar: Any) {
val x = bar.isString()
if (x) bar.<!UNRESOLVED_REFERENCE!>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
}
@@ -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 {
@@ -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