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 540052ca4d6..47770b5654f 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 @@ -366,7 +366,7 @@ abstract class FirDataFlowAnalyzer( val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression) if (syntheticElseNode != null) { syntheticElseNode.mergeIncomingFlow() - val previousConditionExitNode = syntheticElseNode.previousNodes.single() as? WhenBranchConditionExitNode + val previousConditionExitNode = syntheticElseNode.firstPreviousNode as? WhenBranchConditionExitNode // previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches // in that case there will be when enter or subject access node if (previousConditionExitNode != null) { @@ -474,11 +474,11 @@ abstract class FirDataFlowAnalyzer( private fun enterSafeCall(qualifiedAccess: FirQualifiedAccess) { if (!qualifiedAccess.safe) return val node = graphBuilder.enterSafeCall(qualifiedAccess).mergeIncomingFlow() - val previousNode = node.alivePreviousNodes.first() + val previousNode = node.firstPreviousNode val shouldFork: Boolean var flow = if (previousNode is ExitSafeCallNode) { shouldFork = false - previousNode.alivePreviousNodes.getOrNull(1)?.flow ?: node.flow + previousNode.secondPreviousNode?.flow ?: node.flow } else { shouldFork = true node.flow @@ -684,9 +684,9 @@ abstract class FirDataFlowAnalyzer( } private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) { - val parentFlow = leftNode.alivePreviousNodes.first().flow + val parentFlow = leftNode.firstPreviousNode.flow leftNode.flow = logicSystem.forkFlow(parentFlow) - val leftOperandVariable = variableStorage.getOrCreateVariable(leftNode.previousNodes.first().fir) + val leftOperandVariable = variableStorage.getOrCreateVariable(leftNode.firstPreviousNode.fir) rightNode.flow = logicSystem.approveStatementsInsideFlow( parentFlow, leftOperandVariable eq isAnd, @@ -751,7 +751,7 @@ abstract class FirDataFlowAnalyzer( private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) { - val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.previousNodes.first().fir) + val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.firstPreviousNode.fir) val variable = variableStorage.getOrCreateVariable(functionCall) logicSystem.replaceVariableFromConditionInStatements( node.flow, @@ -789,10 +789,13 @@ abstract class FirDataFlowAnalyzer( flowOnNodes[this.origin] = value } - private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) previousNodes.first() else this + private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) firstPreviousNode else this private fun > T.mergeIncomingFlow(updateReceivers: Boolean = false): T = this.also { node -> - val previousFlows = node.alivePreviousNodes.map { it.flow } + val previousFlows = if (node.isDead) + node.previousNodes.map { it.flow } + else + node.previousNodes.mapNotNull { prev -> prev.takeIf { !it.isDead }?.flow } val flow = logicSystem.joinFlow(previousFlows) if (updateReceivers) { logicSystem.updateAllReceivers(flow) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index 9ec9825ed7c..1d512b3a78b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -32,6 +32,8 @@ sealed class CFGNode(val owner: ControlFlowGraph, val level: val previousNodes = mutableListOf>() val followingNodes = mutableListOf>() + val firstPreviousNode: CFGNode<*> get() = previousNodes.first() + abstract val fir: E var isDead: Boolean = false @@ -45,8 +47,6 @@ sealed class CFGNode(val owner: ControlFlowGraph, val level: } } -val CFGNode<*>.aliveFollowingNodes: List> get() = if (isDead) followingNodes else followingNodes.filterNot { it.isDead } -val CFGNode<*>.alivePreviousNodes: List> get() = if (isDead) previousNodes else previousNodes.filterNot { it.isDead } interface EnterNode interface ExitNode @@ -173,7 +173,10 @@ class EnterContractNode(owner: ControlFlowGraph, override val fir: FirFunctionCa class ExitContractNode(owner: ControlFlowGraph, override val fir: FirFunctionCall, level: Int, id: Int) : CFGNode(owner, level, id), ExitNode class EnterSafeCallNode(owner: ControlFlowGraph, override val fir: FirQualifiedAccess, level: Int, id: Int) : CFGNode(owner, level, id) -class ExitSafeCallNode(owner: ControlFlowGraph, override val fir: FirQualifiedAccess, level: Int, id: Int) : CFGNode(owner, level, id) +class ExitSafeCallNode(owner: ControlFlowGraph, override val fir: FirQualifiedAccess, level: Int, id: Int) : CFGNode(owner, level, id) { + val lastPreviousNode: CFGNode<*> get() = previousNodes.last() + val secondPreviousNode: CFGNode<*>? get() = previousNodes.getOrNull(1) +} // ----------------------------------- Other ----------------------------------- diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index 31c62ead693..a4f2e421d85 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -148,8 +148,8 @@ class ControlFlowGraphBuilder { fun CFGNode<*>.extractArgument(): FirElement? = when(this) { is FunctionEnterNode, is TryMainBlockEnterNode, is CatchClauseEnterNode -> null - is ExitSafeCallNode -> previousNodes.last().extractArgument() - is StubNode -> previousNodes.first().extractArgument() + is ExitSafeCallNode -> lastPreviousNode.extractArgument() + is StubNode -> firstPreviousNode.extractArgument() else -> fir.extractArgument() } return exitsOfAnonymousFunctions.getValue(function).previousNodes.mapNotNull {