From 7b61bf91784ea990e72d82dc397121e1832ea6d4 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Thu, 11 Jun 2020 13:44:09 +0300 Subject: [PATCH] FIR: Add nullability smartcast after !is check ^KT-39072 Fixed --- .../safeCalls/boundSafeCallAndIsCheck.kt | 17 +++++++++++++ .../safeCalls/boundSafeCallAndIsCheck.txt | 25 +++++++++++++++++++ .../fir/FirDiagnosticsTestGenerated.java | 5 ++++ ...DiagnosticsWithLightTreeTestGenerated.java | 5 ++++ .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 9 ++++--- 5 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.txt diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.kt b/compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.kt new file mode 100644 index 00000000000..11a109e75c6 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.kt @@ -0,0 +1,17 @@ +interface A { + val b: B +} + +interface B +interface C : B { + fun q(): Boolean +} + +fun A.foo(): String = "" + +fun main(a: A?) { + val lb = a?.b + if (lb !is C) return + + a.foo().length +} diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.txt b/compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.txt new file mode 100644 index 00000000000..e9e22307dde --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.txt @@ -0,0 +1,25 @@ +FILE: boundSafeCallAndIsCheck.kt + public abstract interface A : R|kotlin/Any| { + public abstract val b: R|B| + public get(): R|B| + + } + public abstract interface B : R|kotlin/Any| { + } + public abstract interface C : R|B| { + public abstract fun q(): R|kotlin/Boolean| + + } + public final fun R|A|.foo(): R|kotlin/String| { + ^foo String() + } + public final fun main(a: R|A?|): R|kotlin/Unit| { + lval lb: R|B?| = R|/a|?.{ $subj$.R|/A.b| } + when () { + (R|/lb| !is R|C|) -> { + ^main Unit + } + } + + R|/a|.R|/foo|().R|kotlin/String.length| + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 59ae935d5c4..8cae7a39dd8 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -2152,6 +2152,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/assignSafeCall.kt"); } + @TestMetadata("boundSafeCallAndIsCheck.kt") + public void testBoundSafeCallAndIsCheck() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.kt"); + } + @TestMetadata("safeCallAndEqualityToBool.kt") public void testSafeCallAndEqualityToBool() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/safeCallAndEqualityToBool.kt"); diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index b69d322b458..369f1baa2b7 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -2152,6 +2152,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/assignSafeCall.kt"); } + @TestMetadata("boundSafeCallAndIsCheck.kt") + public void testBoundSafeCallAndIsCheck() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.kt"); + } + @TestMetadata("safeCallAndEqualityToBool.kt") public void testSafeCallAndEqualityToBool() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/safeCallAndEqualityToBool.kt"); 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 97e8a4c74b3..748343504ff 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 @@ -237,7 +237,8 @@ abstract class FirDataFlowAnalyzer( when (val operation = typeOperatorCall.operation) { FirOperation.IS, FirOperation.NOT_IS -> { val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall) - val isNotNullCheck = operation == FirOperation.IS && type.nullability == ConeNullability.NOT_NULL + val isNotNullCheck = type.nullability == ConeNullability.NOT_NULL + val isRegularIs = operation == FirOperation.IS if (operandVariable.isReal()) { val hasTypeInfo = operandVariable typeEq type val hasNotTypeInfo = operandVariable typeNotEq type @@ -252,13 +253,13 @@ abstract class FirDataFlowAnalyzer( flow.addTypeStatement(operandVariable typeEq any) } if (isNotNullCheck) { - flow.addImplication((expressionVariable eq true) implies (operandVariable typeEq any)) - flow.addImplication((expressionVariable eq true) implies (operandVariable notEq null)) + flow.addImplication((expressionVariable eq isRegularIs) implies (operandVariable typeEq any)) + flow.addImplication((expressionVariable eq isRegularIs) implies (operandVariable notEq null)) } } else { if (isNotNullCheck) { - flow.addImplication((expressionVariable eq true) implies (operandVariable notEq null)) + flow.addImplication((expressionVariable eq isRegularIs) implies (operandVariable notEq null)) } } }