FIR: Add nullability smartcast after !is check
^KT-39072 Fixed
This commit is contained in:
Vendored
+17
@@ -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
|
||||||
|
}
|
||||||
Vendored
+25
@@ -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|<local>/a|?.{ $subj$.R|/A.b| }
|
||||||
|
when () {
|
||||||
|
(R|<local>/lb| !is R|C|) -> {
|
||||||
|
^main Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
R|<local>/a|.R|/foo|().R|kotlin/String.length|
|
||||||
|
}
|
||||||
Generated
+5
@@ -2152,6 +2152,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
|||||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/assignSafeCall.kt");
|
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")
|
@TestMetadata("safeCallAndEqualityToBool.kt")
|
||||||
public void testSafeCallAndEqualityToBool() throws Exception {
|
public void testSafeCallAndEqualityToBool() throws Exception {
|
||||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/safeCallAndEqualityToBool.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/safeCallAndEqualityToBool.kt");
|
||||||
|
|||||||
+5
@@ -2152,6 +2152,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
|||||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/assignSafeCall.kt");
|
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")
|
@TestMetadata("safeCallAndEqualityToBool.kt")
|
||||||
public void testSafeCallAndEqualityToBool() throws Exception {
|
public void testSafeCallAndEqualityToBool() throws Exception {
|
||||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/safeCallAndEqualityToBool.kt");
|
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/safeCallAndEqualityToBool.kt");
|
||||||
|
|||||||
+5
-4
@@ -237,7 +237,8 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
when (val operation = typeOperatorCall.operation) {
|
when (val operation = typeOperatorCall.operation) {
|
||||||
FirOperation.IS, FirOperation.NOT_IS -> {
|
FirOperation.IS, FirOperation.NOT_IS -> {
|
||||||
val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall)
|
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()) {
|
if (operandVariable.isReal()) {
|
||||||
val hasTypeInfo = operandVariable typeEq type
|
val hasTypeInfo = operandVariable typeEq type
|
||||||
val hasNotTypeInfo = operandVariable typeNotEq type
|
val hasNotTypeInfo = operandVariable typeNotEq type
|
||||||
@@ -252,13 +253,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
flow.addTypeStatement(operandVariable typeEq any)
|
flow.addTypeStatement(operandVariable typeEq any)
|
||||||
}
|
}
|
||||||
if (isNotNullCheck) {
|
if (isNotNullCheck) {
|
||||||
flow.addImplication((expressionVariable eq true) implies (operandVariable typeEq any))
|
flow.addImplication((expressionVariable eq isRegularIs) implies (operandVariable typeEq any))
|
||||||
flow.addImplication((expressionVariable eq true) implies (operandVariable notEq null))
|
flow.addImplication((expressionVariable eq isRegularIs) implies (operandVariable notEq null))
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (isNotNullCheck) {
|
if (isNotNullCheck) {
|
||||||
flow.addImplication((expressionVariable eq true) implies (operandVariable notEq null))
|
flow.addImplication((expressionVariable eq isRegularIs) implies (operandVariable notEq null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user