From 6a2d74e211af8df402138d89c2913ac38053aa35 Mon Sep 17 00:00:00 2001 From: pyos Date: Tue, 8 Nov 2022 12:10:33 +0100 Subject: [PATCH] FIR DFA: generate type implications on all null comparisons --- .../booleans/jumpFromRhsOfOperator.dot | 86 ++++---- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 192 +++++------------- .../kotlin/fir/resolve/dfa/LogicSystem.kt | 10 - .../jetbrains/kotlin/fir/resolve/dfa/model.kt | 27 ++- .../tests/smartCasts/elvis/basicOn.fir.kt | 4 +- .../diagnostics/tests/when/When.fir.kt | 2 +- .../p-2/neg/11.2.fir.kt | 2 +- .../diagnostics/notLinked/dfa/neg/2.fir.kt | 8 +- .../diagnostics/notLinked/dfa/neg/41.fir.kt | 8 +- .../diagnostics/notLinked/dfa/pos/4.fir.kt | 2 +- .../diagnostics/notLinked/dfa/pos/40.fir.kt | 8 +- .../diagnostics/notLinked/dfa/pos/6.fir.kt | 4 +- .../diagnostics/notLinked/dfa/pos/66.fir.kt | 24 +-- 13 files changed, 145 insertions(+), 232 deletions(-) diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans/jumpFromRhsOfOperator.dot b/compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans/jumpFromRhsOfOperator.dot index a6c351b20a7..7738dfcf264 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans/jumpFromRhsOfOperator.dot +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/booleans/jumpFromRhsOfOperator.dot @@ -388,11 +388,10 @@ digraph jumpFromRhsOfOperator_kt { 153 [label="Exit when"]; } 154 [label="Access variable R|/a|"]; - 155 [label="Smart cast: R|/a|"]; - 156 [label="Function call: R|/a|.#()"]; - 157 [label="Exit block"]; + 155 [label="Function call: R|/a|.#()"]; + 156 [label="Exit block"]; } - 158 [label="Exit function test_7" style="filled" fillcolor=red]; + 157 [label="Exit function test_7" style="filled" fillcolor=red]; } 130 -> {131}; 131 -> {132}; @@ -405,7 +404,7 @@ digraph jumpFromRhsOfOperator_kt { 138 -> {143 139}; 139 -> {140}; 140 -> {141}; - 141 -> {158} [label=onUncaughtException]; + 141 -> {157} [label=onUncaughtException]; 141 -> {142} [style=dotted]; 142 -> {143} [style=dotted]; 143 -> {144}; @@ -422,55 +421,54 @@ digraph jumpFromRhsOfOperator_kt { 154 -> {155}; 155 -> {156}; 156 -> {157}; - 157 -> {158}; subgraph cluster_32 { color=red - 159 [label="Enter function test_8" style="filled" fillcolor=red]; + 158 [label="Enter function test_8" style="filled" fillcolor=red]; subgraph cluster_33 { color=blue - 160 [label="Enter block"]; + 159 [label="Enter block"]; subgraph cluster_34 { color=blue - 161 [label="Enter when"]; + 160 [label="Enter when"]; subgraph cluster_35 { color=blue - 162 [label="Enter when branch condition "]; + 161 [label="Enter when branch condition "]; subgraph cluster_36 { color=blue - 163 [label="Enter &&"]; - 164 [label="Access variable R|/a|"]; - 165 [label="Const: Null(null)"]; - 166 [label="Equality operator !="]; - 167 [label="Exit left part of &&"]; - 168 [label="Enter right part of &&"]; - 169 [label="Function call: R|java/lang/Exception.Exception|()"]; - 170 [label="Throw: throw R|java/lang/Exception.Exception|()"]; - 171 [label="Stub" style="filled" fillcolor=gray]; - 172 [label="Exit &&"]; + 162 [label="Enter &&"]; + 163 [label="Access variable R|/a|"]; + 164 [label="Const: Null(null)"]; + 165 [label="Equality operator !="]; + 166 [label="Exit left part of &&"]; + 167 [label="Enter right part of &&"]; + 168 [label="Function call: R|java/lang/Exception.Exception|()"]; + 169 [label="Throw: throw R|java/lang/Exception.Exception|()"]; + 170 [label="Stub" style="filled" fillcolor=gray]; + 171 [label="Exit &&"]; } - 173 [label="Exit when branch condition"]; + 172 [label="Exit when branch condition"]; } - 174 [label="Synthetic else branch"]; - 175 [label="Enter when branch result"]; + 173 [label="Synthetic else branch"]; + 174 [label="Enter when branch result"]; subgraph cluster_37 { color=blue - 176 [label="Enter block"]; - 177 [label="Access variable R|/a|"]; - 178 [label="Smart cast: R|/a|"]; - 179 [label="Function call: R|/a|.#()"]; - 180 [label="Exit block"]; + 175 [label="Enter block"]; + 176 [label="Access variable R|/a|"]; + 177 [label="Smart cast: R|/a|"]; + 178 [label="Function call: R|/a|.#()"]; + 179 [label="Exit block"]; } - 181 [label="Exit when branch result"]; - 182 [label="Exit when"]; + 180 [label="Exit when branch result"]; + 181 [label="Exit when"]; } - 183 [label="Access variable R|/a|"]; - 184 [label="Smart cast: R|/a|"]; - 185 [label="Function call: R|/a|.#()"]; - 186 [label="Exit block"]; + 182 [label="Access variable R|/a|"]; + 183 [label="Function call: R|/a|.#()"]; + 184 [label="Exit block"]; } - 187 [label="Exit function test_8" style="filled" fillcolor=red]; + 185 [label="Exit function test_8" style="filled" fillcolor=red]; } + 158 -> {159}; 159 -> {160}; 160 -> {161}; 161 -> {162}; @@ -478,16 +476,16 @@ digraph jumpFromRhsOfOperator_kt { 163 -> {164}; 164 -> {165}; 165 -> {166}; - 166 -> {167}; - 167 -> {172 168}; + 166 -> {171 167}; + 167 -> {168}; 168 -> {169}; - 169 -> {170}; - 170 -> {187} [label=onUncaughtException]; + 169 -> {185} [label=onUncaughtException]; + 169 -> {170} [style=dotted]; 170 -> {171} [style=dotted]; - 171 -> {172} [style=dotted]; - 172 -> {173}; - 173 -> {175 174}; - 174 -> {182}; + 171 -> {172}; + 172 -> {174 173}; + 173 -> {181}; + 174 -> {175}; 175 -> {176}; 176 -> {177}; 177 -> {178}; @@ -498,7 +496,5 @@ digraph jumpFromRhsOfOperator_kt { 182 -> {183}; 183 -> {184}; 184 -> {185}; - 185 -> {186}; - 186 -> {187}; } 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 c0e5ca122cf..12876b6fc9a 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 @@ -153,7 +153,7 @@ abstract class FirDataFlowAnalyzer( protected abstract val logicSystem: LogicSystem private val graphBuilder get() = context.graphBuilder - protected val variableStorage get() = context.variableStorage + private val variableStorage get() = context.variableStorage private var contractDescriptionVisitingMode = false @@ -423,30 +423,21 @@ abstract class FirDataFlowAnalyzer( when (val operation = typeOperatorCall.operation) { FirOperation.IS, FirOperation.NOT_IS -> { - val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall) - val isNotNullCheck = !type.canBeNull - val isRegularIs = operation == FirOperation.IS - if (operandVariable.isReal()) { - val hasTypeInfo = operandVariable typeEq type - val hasNotTypeInfo = operandVariable typeNotEq type - - fun chooseInfo(trueBranch: Boolean) = - if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) hasTypeInfo else hasNotTypeInfo - - flow.addImplication((expressionVariable eq true) implies chooseInfo(true)) - flow.addImplication((expressionVariable eq false) implies chooseInfo(false)) - - if (operation == FirOperation.NOT_IS && type == nullableNothing) { - flow.addTypeStatement(operandVariable typeEq any) - } - if (isNotNullCheck) { - 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 isRegularIs) implies (operandVariable notEq null)) + val isType = operation == FirOperation.IS + when (type) { + // x is Nothing? <=> x == null + nullableNothing -> processEqNull(node, typeOperatorCall.argument, isType) + // x is Any <=> x != null + any -> processEqNull(node, typeOperatorCall.argument, !isType) + else -> { + val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall) + if (operandVariable.isReal()) { + flow.addImplication((expressionVariable eq isType) implies (operandVariable typeEq type)) + flow.addImplication((expressionVariable notEq isType) implies (operandVariable typeNotEq type)) + } + if (!type.canBeNull) { + flow.addImplication((expressionVariable eq isType) implies (operandVariable notEq null)) + } } } } @@ -511,8 +502,8 @@ abstract class FirDataFlowAnalyzer( when { leftConst != null && rightConst != null -> return - leftIsNull -> processEqNull(node, rightOperand, operation) - rightIsNull -> processEqNull(node, leftOperand, operation) + leftIsNull -> processEqNull(node, rightOperand, operation.isEq()) + rightIsNull -> processEqNull(node, leftOperand, operation.isEq()) leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation) rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation) else -> processEq(node, leftOperand, rightOperand, operation) @@ -581,121 +572,43 @@ abstract class FirDataFlowAnalyzer( } } - private fun processEq( - node: EqualityOperatorCallNode, leftOperand: FirExpression, rightOperand: FirExpression, operation: FirOperation - ) { - val leftIsNullable = leftOperand.coneType.isMarkedNullable - val rightIsNullable = rightOperand.coneType.isMarkedNullable - - if (leftIsNullable || rightIsNullable) { - if (leftIsNullable && rightIsNullable) return - processEqNull( - node, - if (leftIsNullable) leftOperand else rightOperand, - operation.invert(), - checkAddImplicationForStatement = true - ) - } - - processPossibleIdentity(node, leftOperand, rightOperand, operation) - } - - /* - * Process x == null in general: add implications for both cases. - * E.g., say d1 is an eq operator call node, d2 represents `x`, the variable inside the operator call. So, d1: x == null - * This util adds: "d1 == True -> d2 == null" and "d1 == False -> d2 != null" - * so that both branches after the operator call can infer the type of x. - * - * However, users can specify what conditions are of interest. - * E.g., say left == right _and_ right != null, then we can conclude left != null. - * In this example, say d1 is an eq operator call (left == right), and d2 is left. - * Unlike general cases, we want to add: "d1 == True -> d2 != null", and nothing more because the counter part, - * "d1 == False -> d2 == null" doesn't hold. That is, left != right and right != null don't mean left == null. It just means, left is - * something different from right, including null. By filtering "d1 == True" condition only, all the remaining logic can be shared. - */ - private fun processEqNull( - node: EqualityOperatorCallNode, - operand: FirExpression, - operation: FirOperation, - checkAddImplicationForStatement: Boolean = false - ) { + private fun processEqNull(node: CFGNode<*>, operand: FirExpression, isEq: Boolean) { val flow = node.flow val expressionVariable = variableStorage.createSyntheticVariable(node.fir) val operandVariable = variableStorage.getOrCreateVariable(node.previousFlow, operand) - - val isEq = operation.isEq() - - val predicate = when (isEq) { - true -> operandVariable eq null - false -> operandVariable notEq null - } - - // left == right && right not null -> left != null - // [processEqNull] adds both implications: operator call could be true or false. We definitely need the matched case only. - // TODO: this is incomprehensible - the comments below say what the equivalent expression is - fun shouldAddImplicationForStatement(operationStatement: OperationStatement): Boolean { - if (!checkAddImplicationForStatement) return true - // Only if operation statement is == True, i.e., left == right - val operationStatementOp = operationStatement.operation - return !isEq && operationStatementOp == Operation.EqTrue || isEq && operationStatementOp == Operation.EqFalse - } - - // !checkAddImplicationForStatement || !isEq - if (shouldAddImplicationForStatement(expressionVariable eq true)) { - logicSystem.approveOperationStatement(flow, predicate).forEach { effect -> - flow.addImplication((expressionVariable eq true) implies effect) - } - } - - // !checkAddImplicationForStatement || isEq - if (shouldAddImplicationForStatement(expressionVariable eq false)) { - logicSystem.approveOperationStatement(flow, predicate.invert()).forEach { effect -> - flow.addImplication((expressionVariable eq false) implies effect) - } - } - - val expressionVariableIsEq = shouldAddImplicationForStatement(expressionVariable eq isEq) // !checkAddImplicationForStatement - val expressionVariableIsNotEq = shouldAddImplicationForStatement(expressionVariable notEq isEq) // true - - if (expressionVariableIsEq) { - flow.addImplication((expressionVariable eq isEq) implies (operandVariable eq null)) - } - if (expressionVariableIsNotEq) { - flow.addImplication((expressionVariable notEq isEq) implies (operandVariable notEq null)) - } - - if (operandVariable.isReal()) { - if (expressionVariableIsEq) { - flow.addImplication((expressionVariable eq isEq) implies (operandVariable typeNotEq any)) - } - if (expressionVariableIsNotEq) { - flow.addImplication((expressionVariable notEq isEq) implies (operandVariable typeEq any)) - } - // true - if (shouldAddImplicationForStatement(expressionVariable eq !isEq)) { - flow.addImplication((expressionVariable eq !isEq) implies (operandVariable typeNotEq nullableNothing)) - } - // !checkAddImplicationForStatement - if (shouldAddImplicationForStatement(expressionVariable notEq !isEq)) { - flow.addImplication((expressionVariable notEq !isEq) implies (operandVariable typeEq nullableNothing)) - } - } - - node.flow = flow + flow.addImplication((expressionVariable eq isEq) implies (operandVariable eq null)) + flow.addImplication((expressionVariable notEq isEq) implies (operandVariable notEq null)) } - private fun processPossibleIdentity( + private fun processEq( node: EqualityOperatorCallNode, leftOperand: FirExpression, rightOperand: FirExpression, operation: FirOperation, ) { - val flow = node.flow - val expressionVariable = variableStorage.getOrCreateVariable(node.previousFlow, node.fir) - val leftOperandVariable = variableStorage.getOrCreateVariable(node.previousFlow, leftOperand) - val rightOperandVariable = variableStorage.getOrCreateVariable(node.previousFlow, rightOperand) + val isEq = operation.isEq() val leftOperandType = leftOperand.coneType val rightOperandType = rightOperand.coneType + val leftIsNullable = leftOperandType.isMarkedNullable + val rightIsNullable = rightOperandType.isMarkedNullable + + if (leftIsNullable && rightIsNullable) { + // The logic system is not complex enough to express a second level of implications this creates: + // if either `== null` then this creates the same implications as a constant null comparison, + // otherwise the same as if the corresponding `...IsNullable` is false. + return + } + + val flow = node.flow + val expressionVariable = variableStorage.createSyntheticVariable(node.fir) + val leftOperandVariable = variableStorage.getOrCreateVariable(node.previousFlow, leftOperand) + val rightOperandVariable = variableStorage.getOrCreateVariable(node.previousFlow, rightOperand) + + if (leftIsNullable || rightIsNullable) { + // `a == b:Any` => `a != null`; the inverse is not true - we don't know when `a` *is* `null` + val nullableOperand = if (leftIsNullable) leftOperandVariable else rightOperandVariable + flow.addImplication((expressionVariable eq isEq) implies (nullableOperand notEq null)) + } if (!leftOperandVariable.isReal() && !rightOperandVariable.isReal()) return @@ -703,8 +616,6 @@ abstract class FirDataFlowAnalyzer( if (hasOverriddenEquals(leftOperandType)) return } - val isEq = operation.isEq() - if (leftOperandVariable.isReal()) { flow.addImplication((expressionVariable eq isEq) implies (leftOperandVariable typeEq rightOperandType)) flow.addImplication((expressionVariable notEq isEq) implies (leftOperandVariable typeNotEq rightOperandType)) @@ -714,8 +625,6 @@ abstract class FirDataFlowAnalyzer( flow.addImplication((expressionVariable eq isEq) implies (rightOperandVariable typeEq leftOperandType)) flow.addImplication((expressionVariable notEq isEq) implies (rightOperandVariable typeNotEq leftOperandType)) } - - node.flow = flow } private fun hasOverriddenEquals(type: ConeKotlinType): Boolean { @@ -1051,9 +960,9 @@ abstract class FirDataFlowAnalyzer( is RealVariable -> variable.explicitReceiverVariable ?: return is SyntheticVariable -> variableStorage.getOrCreateVariable(flow, safeCall.receiver) } - logicSystem.addImplication(flow, (variable notEq null) implies (receiverVariable notEq null)) + flow.addImplication((variable notEq null) implies (receiverVariable notEq null)) if (receiverVariable.isReal()) { - logicSystem.addImplication(flow, (variable notEq null) implies (receiverVariable typeEq any)) + flow.addImplication((variable notEq null) implies (receiverVariable typeEq any)) } } @@ -1575,6 +1484,17 @@ abstract class FirDataFlowAnalyzer( } private fun FLOW.addImplication(statement: Implication) { + val effect = statement.effect + if (effect is OperationStatement) { + val variable = effect.variable + if (variable.isReal()) { + when (effect.operation) { + Operation.EqNull -> variable typeEq nullableNothing andTypeNotEq any + Operation.NotEqNull -> variable typeEq any andTypeNotEq nullableNothing + else -> null + }?.let { logicSystem.addImplication(this, statement.condition implies it) } + } + } logicSystem.addImplication(this, statement) } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt index d3b096a60e5..4e00e9a6ac8 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/LogicSystem.kt @@ -72,16 +72,6 @@ abstract class LogicSystem(protected val context: ConeInferenceCont statements: Collection, ) - /** - * Recursively collects all TypeStatements approved by [approvedStatement] and all predicates - * that has been implied by it - * TODO: or not recursively? - */ - fun approveOperationStatement(flow: FLOW, approvedStatement: OperationStatement): Collection { - val statements = getImplicationsWithVariable(flow, approvedStatement.variable) - return approveOperationStatement(flow, approvedStatement, statements).values - } - fun orForTypeStatements(left: TypeStatements, right: TypeStatements): MutableTypeStatements { if (left.isEmpty() || right.isEmpty()) return mutableMapOf() val map = mutableMapOf() diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt index 6f3804aee06..c4cdc8024aa 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt @@ -60,18 +60,25 @@ infix fun DataFlowVariable.notEq(constant: Boolean?): OperationStatement { infix fun OperationStatement.implies(effect: Statement<*>): Implication = Implication(this, effect) -infix fun RealVariable.typeEq(type: ConeKotlinType): TypeStatement = - if (type !is ConeErrorType) { - MutableTypeStatement(this, linkedSetOf().apply { this += type }, HashSet()) - } else { - MutableTypeStatement(this) +infix fun RealVariable.typeEq(type: ConeKotlinType): MutableTypeStatement = + MutableTypeStatement(this) andTypeEq type + +infix fun RealVariable.typeNotEq(type: ConeKotlinType): MutableTypeStatement = + MutableTypeStatement(this) andTypeNotEq type + +infix fun MutableTypeStatement.andTypeEq(type: ConeKotlinType): MutableTypeStatement = + this.apply { + if (type !is ConeErrorType) { + exactType += type + } } -infix fun RealVariable.typeNotEq(type: ConeKotlinType): TypeStatement = - if (type !is ConeErrorType) { - MutableTypeStatement(this, linkedSetOf(), LinkedHashSet().apply { this += type }) - } else { - MutableTypeStatement(this) +infix fun MutableTypeStatement.andTypeNotEq(type: ConeKotlinType): MutableTypeStatement = + this.apply { + require(exactNotType.isEmpty()) { "statement $this already has a negation; use `logicSystem.and`" } + if (type !is ConeErrorType) { + exactNotType += type + } } // --------------------------------------- Utils --------------------------------------- diff --git a/compiler/testData/diagnostics/tests/smartCasts/elvis/basicOn.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/elvis/basicOn.fir.kt index 38f0f15dc5e..2e8927e1636 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/elvis/basicOn.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/elvis/basicOn.fir.kt @@ -39,12 +39,12 @@ fun bar(o: Any) { fun baz(o: Boolean?) { if (o ?: false) { - o.hashCode() + o.hashCode() } if (o ?: true) { } else { - o.hashCode() + o.hashCode() } } diff --git a/compiler/testData/diagnostics/tests/when/When.fir.kt b/compiler/testData/diagnostics/tests/when/When.fir.kt index d9b6955b9bb..424056e94cd 100644 --- a/compiler/testData/diagnostics/tests/when/When.fir.kt +++ b/compiler/testData/diagnostics/tests/when/When.fir.kt @@ -22,7 +22,7 @@ fun foo() : Int { !is Int -> 1 is Any? -> 1 is Any -> 1 - s -> 1 + s -> 1 1 -> 1 1 + a -> 1 in 1..a -> 1 diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.2.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.2.fir.kt index 014eec0ea2a..700c9a08c70 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.2.fir.kt @@ -70,7 +70,7 @@ fun case_8(value_1: SealedClassMixed?): String = when(valu */ fun case_9(value_1: Any?): String = when (value_1) { is Any -> "" - null -> "" + null -> "" } /* diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/2.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/2.fir.kt index 738a2cc1663..f066c301f2f 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/2.fir.kt @@ -36,8 +36,8 @@ fun case_4(x: Any) { // TESTCASE NUMBER: 5 fun case_5(x: Any?) { if (!(x !is Nothing?)) { - x - x?.inv() + x + x?.inv() } } @@ -68,8 +68,8 @@ fun case_8(x: Any?) { // TESTCASE NUMBER: 9 fun case_9(x: Any?) { if (!!(x !is Nothing?)) else { - x - x?.inv() + x + x?.inv() } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/41.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/41.fir.kt index 9521e6c7a4c..5031459d00b 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/41.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/neg/41.fir.kt @@ -22,8 +22,8 @@ inline fun case_2(x: Any?) { x.equals(10) } is T -> { - x - x.equals(10) + x + x.equals(10) } else -> return } @@ -80,8 +80,8 @@ inline fun case_7(x: Any?) { x x.equals(10) } else if (x is T) { - x - x.equals(10) + x + x.equals(10) } else return x x.equals(10) diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/4.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/4.fir.kt index fc8cdaa6fbf..f10e197ec29 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/4.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/4.fir.kt @@ -102,7 +102,7 @@ fun case_11(x: TypealiasString, y: TypealiasString) { if (y == null) { if (stringProperty != null) { if (false || false || false || z == null || false) { - x + x } } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/40.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/40.fir.kt index 5ca85c0b3cc..e3013f90025 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/40.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/40.fir.kt @@ -134,8 +134,8 @@ fun case_14(x: Any?) { if (x == null) { x x?.equals(10) - x!!.equals(10) - x.equals(10) + x!!.equals(10) + x.equals(10) } } @@ -148,8 +148,8 @@ fun case_15(x: Any?) { if (x !== null) else { x x?.equals(10) - x!!.equals(10) - x.equals(10) + x!!.equals(10) + x.equals(10) } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/6.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/6.fir.kt index f37dde6069f..e74948d4bdc 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/6.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/6.fir.kt @@ -840,8 +840,8 @@ fun case_46(x: TypealiasNullableString?, y: TypealiasNu if (y === nullableNothingProperty) { if (z != nullableStringProperty) { if (z === t || t == nullableNothingProperty) { - x - x.hashCode() + x + x.hashCode() } } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/66.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/66.fir.kt index 8503d28287a..cf0756976ad 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/66.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/66.fir.kt @@ -20,8 +20,8 @@ fun case_1(x: Any?) { */ fun case_2(x: Pair<*, *>?) { if (x is Nothing?) return - ?")!>x - ?")!>x.equals(10) + ? & kotlin.Pair<*, *>")!>x + ? & kotlin.Pair<*, *>")!>x.equals(10) } /* @@ -31,8 +31,8 @@ fun case_2(x: Pair<*, *>?) { */ fun case_3(x: Any?) { if (x is Nothing?) throw Exception() - x - x.equals(10) + x + x.equals(10) } /* @@ -44,12 +44,12 @@ fun case_4(x: Pair<*, *>?) { when (x) { is Nothing? -> return else -> { - ?")!>x - ?")!>x.equals(10) + ? & kotlin.Pair<*, *>")!>x + ? & kotlin.Pair<*, *>")!>x.equals(10) } } - ?")!>x - ?")!>x.equals(10) + ? & kotlin.Pair<*, *>")!>x + ? & kotlin.Pair<*, *>")!>x.equals(10) } /* @@ -78,10 +78,10 @@ fun case_6(x: Any?) { when (x) { is Nothing? -> return is Any? -> { - x - x.equals(10) + x + x.equals(10) } } - x - x.equals(10) + x + x.equals(10) }