FIR DFA: do not remove statements about LHS of !!

Value equality statements about the result also propagate to left-hand
side, so these statements can still be useful.
This commit is contained in:
pyos
2022-11-08 16:53:37 +01:00
committed by teamcity
parent 1ff968eca2
commit ac0137f45f
5 changed files with 10 additions and 25 deletions
@@ -678,30 +678,14 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val (node, unionNode) = graphBuilder.exitCheckNotNullCall(checkNotNullCall, callCompleted) val (node, unionNode) = graphBuilder.exitCheckNotNullCall(checkNotNullCall, callCompleted)
node.mergeIncomingFlow() node.mergeIncomingFlow()
checkNotNullCall.argument.propagateNotNullInfo(node) val argumentVariable = variableStorage.getOrCreateVariable(node.previousFlow, checkNotNullCall.argument)
node.flow.assumeNotNull(argumentVariable, shouldForkFlow = false, shouldRemoveSynthetics = false)
unionNode?.let { unionFlowFromArguments(it) } unionNode?.let { unionFlowFromArguments(it) }
} }
private fun FirExpression.propagateNotNullInfo(node: CFGNode<*>) {
val symbol = this.symbol
if (symbol != null) {
variableStorage.getOrCreateRealVariable(node.previousFlow, symbol, this)?.let { operandVariable ->
node.flow.assumeNotNull(operandVariable, shouldForkFlow = false, shouldRemoveSynthetics = true)
}
}
when (this) {
is FirSafeCallExpression -> receiver.propagateNotNullInfo(node)
is FirTypeOperatorCall -> {
if (operation == FirOperation.AS || operation == FirOperation.SAFE_AS) {
argument.propagateNotNullInfo(node)
}
}
}
}
private fun FLOW.assumeNotNull(variable: DataFlowVariable, shouldForkFlow: Boolean, shouldRemoveSynthetics: Boolean): FLOW = private fun FLOW.assumeNotNull(variable: DataFlowVariable, shouldForkFlow: Boolean, shouldRemoveSynthetics: Boolean): FLOW =
logicSystem.approveStatementsInsideFlow(this, variable notEq null, shouldForkFlow, shouldRemoveSynthetics,).also { logicSystem.approveStatementsInsideFlow(this, variable notEq null, shouldForkFlow, shouldRemoveSynthetics).also {
if (variable is RealVariable) { if (variable is RealVariable) {
it.addTypeStatement(variable typeEq any andTypeNotEq nullableNothing) it.addTypeStatement(variable typeEq any andTypeNotEq nullableNothing)
} }
@@ -1380,7 +1364,8 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
node.mergeIncomingFlow() node.mergeIncomingFlow()
mergePostponedLambdaExitsNode?.mergeIncomingFlow() mergePostponedLambdaExitsNode?.mergeIncomingFlow()
if (isLhsNotNull) { if (isLhsNotNull) {
elvisExpression.lhs.propagateNotNullInfo(node) val lhsVariable = variableStorage.getOrCreateVariable(node.previousFlow, elvisExpression.lhs)
node.flow.assumeNotNull(lhsVariable, shouldForkFlow = false, shouldRemoveSynthetics = true)
} }
if (!components.session.languageVersionSettings.supportsFeature(LanguageFeature.BooleanElvisBoundSmartCasts)) return if (!components.session.languageVersionSettings.supportsFeature(LanguageFeature.BooleanElvisBoundSmartCasts)) return
@@ -1396,7 +1381,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val lhsVariable = variableStorage.getOrCreateVariable(flow, lhs) val lhsVariable = variableStorage.getOrCreateVariable(flow, lhs)
val value = rhs.value as Boolean val value = rhs.value as Boolean
flow.addImplication(elvisVariable.eq(!value) implies (lhsVariable.notEq(null))) flow.addImplication((elvisVariable eq !value) implies (lhsVariable notEq null))
} }
} }
@@ -63,7 +63,7 @@ fun case_2(value_1: Int?) {
// TESTCASE NUMBER: 3 // TESTCASE NUMBER: 3
fun case_3(value_1: Int?) { fun case_3(value_1: Int?) {
if (contracts.case_3(value_1)!!) { if (contracts.case_3(value_1)!!) {
value_1<!UNSAFE_CALL!>.<!>inv() value_1.inv()
} }
} }
@@ -49,7 +49,7 @@ inline fun <reified T : CharSequence>case_3(x: Any?) {
// TESTCASE NUMBER: 4 // TESTCASE NUMBER: 4
inline fun <reified T : CharSequence>case_4(x: Any?) { inline fun <reified T : CharSequence>case_4(x: Any?) {
(x as? T)!! (x as? T)!!
if (x is T?) { if (<!USELESS_IS_CHECK!>x is T?<!>) {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.length <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.length
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.get(0) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.get(0)
@@ -49,7 +49,7 @@ inline fun <reified T : CharSequence>case_3(x: Any?) {
// TESTCASE NUMBER: 4 // TESTCASE NUMBER: 4
inline fun <reified T : CharSequence>case_4(x: Any?) { inline fun <reified T : CharSequence>case_4(x: Any?) {
(x as? T)!! (x as? T)!!
if (x is T?) { if (<!USELESS_IS_CHECK!>x is T?<!>) {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.length <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.length
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.get(0) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.get(0)
@@ -49,7 +49,7 @@ inline fun <reified T : CharSequence>case_3(x: Any?) {
// TESTCASE NUMBER: 4 // TESTCASE NUMBER: 4
inline fun <reified T : CharSequence>case_4(x: Any?) { inline fun <reified T : CharSequence>case_4(x: Any?) {
(x as? T)!! (x as? T)!!
if (x is T?) { if (<!USELESS_IS_CHECK!>x is T?<!>) {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.length <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.length
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.get(0) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & T?!!")!>x<!>.get(0)