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 f618ad8cae4..f88d346878a 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 @@ -965,8 +965,8 @@ abstract class FirDataFlowAnalyzer( } } - fun exitBinaryLogicExpression() { - graphBuilder.exitBinaryLogicExpression().mergeBinaryLogicOperatorFlow() + fun exitBinaryLogicExpression(binaryLogicExpression: FirBinaryLogicExpression) { + graphBuilder.exitBinaryLogicExpression(binaryLogicExpression).mergeBinaryLogicOperatorFlow() } private fun AbstractBinaryExitNode.mergeBinaryLogicOperatorFlow() = mergeIncomingFlow { flow -> @@ -1045,8 +1045,8 @@ abstract class FirDataFlowAnalyzer( graphBuilder.enterInitBlock(initBlock).mergeIncomingFlow() } - fun exitInitBlock(initBlock: FirAnonymousInitializer): ControlFlowGraph { - val (node, controlFlowGraph) = graphBuilder.exitInitBlock(initBlock) + fun exitInitBlock(): ControlFlowGraph { + val (node, controlFlowGraph) = graphBuilder.exitInitBlock() node.mergeIncomingFlow() return controlFlowGraph } 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 f88da9be02d..bed42b9d3dd 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 @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.fir.resolve.dfa.cfg import org.jetbrains.kotlin.contracts.description.* import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField import org.jetbrains.kotlin.fir.declarations.utils.isLocal @@ -50,26 +49,23 @@ class ControlFlowGraphBuilder { // ----------------------------------- Node caches ----------------------------------- - private val exitTargetsForReturn: SymbolBasedNodeStorage = SymbolBasedNodeStorage() - private val exitTargetsForTry: Stack> = stackOf() + private val exitTargetsForReturn: MutableMap, FunctionExitNode> = mutableMapOf() private val enterToLocalClassesMembers: MutableMap, Pair, EdgeKind>> = mutableMapOf() //return jumps via finally blocks, target -> jumps - private val nonDirectJumps: ListMultimap, CFGNode<*>> = listMultimapOf() + private val nonDirectJumps: ListMultimap, JumpNode> = listMultimapOf() private val argumentListSplitNodes: Stack = stackOf() private val postponedAnonymousFunctionNodes = mutableMapOf, Pair, PostponedLambdaExitNode?>>() private val postponedLambdaExits: Stack, EdgeKind>>> = stackOf() - private val loopConditionEnterNodes: NodeStorage = NodeStorage() - private val loopExitNodes: NodeStorage = NodeStorage() + private val loopConditionEnterNodes: MutableMap = mutableMapOf() + private val loopExitNodes: MutableMap = mutableMapOf() private val whenExitNodes: Stack = stackOf() - private val binaryLogicExpressionExitNodes: Stack> = stackOf() - - private val tryExitNodes: NodeStorage = NodeStorage() + private val tryExitNodes: Stack = stackOf() private val catchNodes: Stack> = stackOf() private val catchBlocksInProgress: Stack = stackOf() private val finallyEnterNodes: Stack = stackOf() @@ -119,19 +115,30 @@ class ControlFlowGraphBuilder { name: String, kind: ControlFlowGraph.Kind, nodes: (E) -> Pair - ): Pair where EnterNode : CFGNode, EnterNode : GraphEnterNodeMarker, ExitNode : CFGNode, ExitNode : GraphExitNodeMarker { + ): EnterNode where EnterNode : CFGNode, EnterNode : GraphEnterNodeMarker, ExitNode : CFGNode, ExitNode : GraphExitNodeMarker { val graph = ControlFlowGraph(fir as? FirDeclaration, name, kind).also { graphs.push(it) } - return nodes(fir).also { (enterNode, exitNode) -> - graph.enterNode = enterNode - graph.exitNode = exitNode - lastNodes.push(enterNode) - } + val (enterNode, exitNode) = nodes(fir) + graph.enterNode = enterNode + graph.exitNode = exitNode + lastNodes.push(enterNode) + return enterNode } private fun popGraph(): ControlFlowGraph { return graphs.pop().also { it.complete() } } + private inline fun exitGraph(): Pair where ExitNode : CFGNode<*>, ExitNode : GraphExitNodeMarker { + val graph = graphs.pop() + val exitNode = graph.exitNode as ExitNode + popAndAddEdge(exitNode) + if (exitNode.previousNodes.size > 1) { + exitNode.updateDeadStatus() + } + graph.complete() + return exitNode to graph + } + // ----------------------------------- Regular function ----------------------------------- fun enterFunction(function: FirFunction): Pair { @@ -146,28 +153,21 @@ class ControlFlowGraphBuilder { val localFunctionNode = runIf(function.symbol.callableId.isLocal && bodyBuildingMode) { createLocalFunctionDeclarationNode(function).also { addNewSimpleNode(it) } } - val (enterNode, exitNode) = enterGraph(function, name, ControlFlowGraph.Kind.Function) { - createFunctionEnterNode(it) to createFunctionExitNode(it) + val enterNode = enterGraph(function, name, ControlFlowGraph.Kind.Function) { + createFunctionEnterNode(it) to createFunctionExitNode(it).also { exit -> exitTargetsForReturn[it.symbol] = exit } } if (localFunctionNode != null) { addEdge(localFunctionNode, enterNode) } else { - addEdgeIfLocalClassMember(function, enterNode) + addEdgeIfLocalClassMember(enterNode) } - exitTargetsForReturn.push(exitNode) - exitTargetsForTry.push(exitNode) return Pair(localFunctionNode, enterNode) } fun exitFunction(function: FirFunction): Pair { require(function !is FirAnonymousFunction) - val exitNode = exitTargetsForReturn.pop() - exitTargetsForTry.pop().also { assert(it == exitNode) } - popAndAddEdge(exitNode) - val graph = popGraph() - assert(exitNode == graph.exitNode) - exitNode.updateDeadStatus() - return exitNode to graph + exitTargetsForReturn.remove(function.symbol) + return exitGraph() } // ----------------------------------- Anonymous function ----------------------------------- @@ -228,34 +228,19 @@ class ControlFlowGraphBuilder { } fun enterAnonymousFunction(anonymousFunction: FirAnonymousFunction): FunctionEnterNode { - val symbol = anonymousFunction.symbol - val flowSourceNode = postponedAnonymousFunctionNodes.getValue(symbol).first - val (enterNode, exitNode) = enterGraph(anonymousFunction, "", ControlFlowGraph.Kind.AnonymousFunction) { - createFunctionEnterNode(it) to createFunctionExitNode(it) - } - exitTargetsForReturn.push(exitNode) - if (!anonymousFunction.invocationKind.isInPlace) { - exitTargetsForTry.push(exitNode) - } - addEdge(flowSourceNode, enterNode) - return enterNode + val graphKind = if (anonymousFunction.invocationKind.isInPlace) + ControlFlowGraph.Kind.AnonymousFunctionCalledInPlace + else + ControlFlowGraph.Kind.AnonymousFunction + return enterGraph(anonymousFunction, "", graphKind) { + createFunctionEnterNode(it) to createFunctionExitNode(it).also { exit -> exitTargetsForReturn[anonymousFunction.symbol] = exit } + }.also { addEdge(postponedAnonymousFunctionNodes.getValue(anonymousFunction.symbol).first, it) } } fun exitAnonymousFunction(anonymousFunction: FirAnonymousFunction): Triple { - val symbol = anonymousFunction.symbol - val exitNode = exitTargetsForReturn.pop().also { - if (!anonymousFunction.invocationKind.isInPlace) { - require(it == exitTargetsForTry.pop()) - } - } - popAndAddEdge(exitNode) - exitNode.updateDeadStatus() - - val graph = popGraph() - assert(graph.declaration == anonymousFunction) - assert(graph.exitNode == exitNode) - - val (splitNode, postponedExitNode) = postponedAnonymousFunctionNodes.remove(symbol)!! + exitTargetsForReturn.remove(anonymousFunction.symbol) + val (exitNode, graph) = exitGraph() + val (splitNode, postponedExitNode) = postponedAnonymousFunctionNodes.remove(anonymousFunction.symbol)!! val invocationKind = anonymousFunction.invocationKind if (postponedExitNode == null) { // Postponed exit node was needed so we could create lambda->call edges without having the subgraph ready. If it @@ -381,8 +366,8 @@ class ControlFlowGraphBuilder { } } - private fun addEdgeIfLocalClassMember(member: E, enterNode: CFGNode<*>) where E : FirControlFlowGraphOwner, E : FirDeclaration { - val (source, kind) = enterToLocalClassesMembers.remove(member.symbol) ?: return + private fun addEdgeIfLocalClassMember(enterNode: CFGNode) { + val (source, kind) = enterToLocalClassesMembers.remove(enterNode.fir.symbol) ?: return addEdge(source, enterNode, preferredKind = kind) } @@ -407,13 +392,13 @@ class ControlFlowGraphBuilder { else -> throw IllegalArgumentException("Unknown class kind: ${klass::class}") } - val (enterNode, _) = enterGraph(klass, name, ControlFlowGraph.Kind.Class) { + val enterNode = enterGraph(klass, name, ControlFlowGraph.Kind.Class) { createClassEnterNode(it) to createClassExitNode(it) } if (localClassEnterNode != null) { addEdge(localClassEnterNode, enterNode) } else { - addEdgeIfLocalClassMember(klass, enterNode) + addEdgeIfLocalClassMember(enterNode) } var foundInPlace = false @@ -517,19 +502,13 @@ class ControlFlowGraphBuilder { } fun enterScript(script: FirScript): ScriptEnterNode { - val (enterNode, exitNode) = enterGraph(script, "SCRIPT_GRAPH", ControlFlowGraph.Kind.Function) { + return enterGraph(script, "SCRIPT_GRAPH", ControlFlowGraph.Kind.Function) { createScriptEnterNode(it) to createScriptExitNode(it) } - exitTargetsForTry.push(exitNode) - return enterNode } fun exitScript(): Pair { - val exitNode = exitTargetsForTry.pop() as ScriptExitNode - addNewSimpleNode(exitNode) - val graph = popGraph() - assert(graph.exitNode == exitNode) - return exitNode to graph + return exitGraph() } // ----------------------------------- Value parameters (and it's defaults) ----------------------------------- @@ -537,24 +516,18 @@ class ControlFlowGraphBuilder { fun enterValueParameter(valueParameter: FirValueParameter): Pair? { if (valueParameter.defaultValue == null) return null - val outerEnterNode = createEnterValueParameterNode(valueParameter) - addNewSimpleNode(outerEnterNode) - val name = "default value of ${valueParameter.name}" - val (enterNode, exitNode) = enterGraph(valueParameter, name, ControlFlowGraph.Kind.DefaultArgument) { + val outerEnterNode = createEnterValueParameterNode(valueParameter).also { addNewSimpleNode(it) } + val enterNode = enterGraph(valueParameter, "default value of ${valueParameter.name}", ControlFlowGraph.Kind.DefaultArgument) { createEnterDefaultArgumentsNode(it) to createExitDefaultArgumentsNode(it) } addEdge(outerEnterNode, enterNode) - exitTargetsForTry.push(exitNode) return outerEnterNode to enterNode } fun exitValueParameter(valueParameter: FirValueParameter): Triple? { if (valueParameter.defaultValue == null) return null - val exitNode = exitTargetsForTry.pop() as ExitDefaultArgumentsNode - popAndAddEdge(exitNode) - val graph = popGraph() - require(exitNode == graph.exitNode) + val (exitNode, graph) = exitGraph() val outerExitNode = createExitValueParameterNode(valueParameter) addNewSimpleNode(outerExitNode) addEdge(exitNode, outerExitNode, propagateDeadness = false) @@ -579,43 +552,28 @@ class ControlFlowGraphBuilder { fun enterProperty(property: FirProperty): PropertyInitializerEnterNode? { if (!property.memberShouldHaveGraph) return null - val (enterNode, exitNode) = enterGraph(property, "val ${property.name}", ControlFlowGraph.Kind.PropertyInitializer) { + return enterGraph(property, "val ${property.name}", ControlFlowGraph.Kind.PropertyInitializer) { createPropertyInitializerEnterNode(it) to createPropertyInitializerExitNode(it) - } - exitTargetsForTry.push(exitNode) - addEdgeIfLocalClassMember(property, enterNode) - return enterNode + }.also { addEdgeIfLocalClassMember(it) } } fun exitProperty(property: FirProperty): Pair? { if (!property.memberShouldHaveGraph) return null - val exitNode = exitTargetsForTry.pop() as PropertyInitializerExitNode - popAndAddEdge(exitNode) - val graph = popGraph() - assert(exitNode == graph.exitNode) - return exitNode to graph + return exitGraph() } // ----------------------------------- Field ----------------------------------- fun enterField(field: FirField): FieldInitializerEnterNode? { if (!field.memberShouldHaveGraph) return null - - val (enterNode, exitNode) = enterGraph(field, "val ${field.name}", ControlFlowGraph.Kind.FieldInitializer) { + return enterGraph(field, "val ${field.name}", ControlFlowGraph.Kind.FieldInitializer) { createFieldInitializerEnterNode(it) to createFieldInitializerExitNode(it) - } - exitTargetsForTry.push(exitNode) - addEdgeIfLocalClassMember(field, enterNode) - return enterNode + }.also { addEdgeIfLocalClassMember(it) } } fun exitField(field: FirField): Pair? { if (!field.memberShouldHaveGraph) return null - val exitNode = exitTargetsForTry.pop() as FieldInitializerExitNode - popAndAddEdge(exitNode) - val graph = popGraph() - assert(exitNode == graph.exitNode) - return exitNode to graph + return exitGraph() } // ----------------------------------- Delegate ----------------------------------- @@ -673,7 +631,7 @@ class ControlFlowGraphBuilder { val nextNode = when (jump) { is FirReturnExpression -> exitTargetsForReturn[jump.target.labeledElement.symbol] - is FirContinueExpression -> loopConditionEnterNodes[jump.target.labeledElement.condition] + is FirContinueExpression -> loopConditionEnterNodes[jump.target.labeledElement] is FirBreakExpression -> loopExitNodes[jump.target.labeledElement] else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}") } ?: return node @@ -764,16 +722,16 @@ class ControlFlowGraphBuilder { fun enterWhileLoop(loop: FirLoop): Pair { val loopEnterNode = createLoopEnterNode(loop).also { addNewSimpleNode(it) } - loopExitNodes.push(createLoopExitNode(loop)) + loopExitNodes[loop] = createLoopExitNode(loop) val conditionEnterNode = createLoopConditionEnterNode(loop.condition, loop).also { addNewSimpleNode(it) } - loopConditionEnterNodes.push(conditionEnterNode) + loopConditionEnterNodes[loop] = conditionEnterNode return loopEnterNode to conditionEnterNode } fun exitWhileLoopCondition(loop: FirLoop): Pair { val conditionExitNode = createLoopConditionExitNode(loop.condition).also { addNewSimpleNode(it) } val conditionConstBooleanValue = loop.condition.booleanConstValue - addEdge(conditionExitNode, loopExitNodes.top(), propagateDeadness = false, isDead = conditionConstBooleanValue == true) + addEdge(conditionExitNode, loopExitNodes.getValue(loop), propagateDeadness = false, isDead = conditionConstBooleanValue == true) val loopBlockEnterNode = createLoopBlockEnterNode(loop) addNewSimpleNode(loopBlockEnterNode, conditionConstBooleanValue == false) return conditionExitNode to loopBlockEnterNode @@ -782,9 +740,9 @@ class ControlFlowGraphBuilder { fun exitWhileLoop(loop: FirLoop): Triple { val loopBlockExitNode = createLoopBlockExitNode(loop) popAndAddEdge(loopBlockExitNode) - val conditionEnterNode = loopConditionEnterNodes.pop() + val conditionEnterNode = loopConditionEnterNodes.remove(loop)!! addBackEdge(loopBlockExitNode, conditionEnterNode) - val loopExitNode = loopExitNodes.pop() + val loopExitNode = loopExitNodes.remove(loop)!! loopExitNode.updateDeadStatus() lastNodes.push(loopExitNode) return Triple(conditionEnterNode, loopBlockExitNode, loopExitNode) @@ -794,10 +752,10 @@ class ControlFlowGraphBuilder { fun enterDoWhileLoop(loop: FirLoop): Pair { val loopEnterNode = createLoopEnterNode(loop).also { addNewSimpleNode(it) } - loopExitNodes.push(createLoopExitNode(loop)) + loopExitNodes[loop] = createLoopExitNode(loop) val blockEnterNode = createLoopBlockEnterNode(loop).also { addNewSimpleNode(it) } lastNodes.push(blockEnterNode) // to add back edge at the end - loopConditionEnterNodes.push(createLoopConditionEnterNode(loop.condition, loop)) + loopConditionEnterNodes[loop] = createLoopConditionEnterNode(loop.condition, loop) return loopEnterNode to blockEnterNode } @@ -805,21 +763,21 @@ class ControlFlowGraphBuilder { val blockExitNode = createLoopBlockExitNode(loop).also { addNewSimpleNode(it) } // This may sound shocking, but `do...while` conditions can `continue` to themselves, // so we can't pop the node off the stack here. - val conditionEnterNode = loopConditionEnterNodes.top().also { addNewSimpleNode(it) } + val conditionEnterNode = loopConditionEnterNodes.getValue(loop).also { addNewSimpleNode(it) } // Might have had live `continue`s with an unreachable block exit, so recompute deadness. conditionEnterNode.updateDeadStatus() return blockExitNode to conditionEnterNode } fun exitDoWhileLoop(loop: FirLoop): Pair { - loopConditionEnterNodes.pop() + loopConditionEnterNodes.remove(loop) val conditionExitNode = createLoopConditionExitNode(loop.condition) val conditionBooleanValue = loop.condition.booleanConstValue popAndAddEdge(conditionExitNode) val blockEnterNode = lastNodes.pop() require(blockEnterNode is LoopBlockEnterNode) addBackEdge(conditionExitNode, blockEnterNode, isDead = conditionBooleanValue == false) - val loopExit = loopExitNodes.pop() + val loopExit = loopExitNodes.remove(loop)!! addEdge(conditionExitNode, loopExit, propagateDeadness = false, isDead = conditionBooleanValue == true) loopExit.updateDeadStatus() lastNodes.push(loopExit) @@ -829,39 +787,40 @@ class ControlFlowGraphBuilder { // ----------------------------------- Boolean operators ----------------------------------- fun enterBinaryLogicExpression(binaryLogicExpression: FirBinaryLogicExpression): CFGNode { - val (enterNode, exitNode) = when (binaryLogicExpression.kind) { - LogicOperationKind.AND -> createBinaryAndEnterNode(binaryLogicExpression) to createBinaryAndExitNode(binaryLogicExpression) - LogicOperationKind.OR -> createBinaryOrEnterNode(binaryLogicExpression) to createBinaryOrExitNode(binaryLogicExpression) - } - addNewSimpleNode(enterNode) - binaryLogicExpressionExitNodes.push(exitNode) - return enterNode + return when (binaryLogicExpression.kind) { + LogicOperationKind.AND -> createBinaryAndEnterNode(binaryLogicExpression) + LogicOperationKind.OR -> createBinaryOrEnterNode(binaryLogicExpression) + }.also { addNewSimpleNode(it) } } fun exitLeftBinaryLogicExpressionArgument( binaryLogicExpression: FirBinaryLogicExpression ): Pair, CFGNode> { - val leftBooleanConstValue = binaryLogicExpression.leftOperand.booleanConstValue val (leftExitNode, rightEnterNode) = when (binaryLogicExpression.kind) { LogicOperationKind.AND -> createBinaryAndExitLeftOperandNode(binaryLogicExpression) to createBinaryAndEnterRightOperandNode(binaryLogicExpression) LogicOperationKind.OR -> createBinaryOrExitLeftOperandNode(binaryLogicExpression) to createBinaryOrEnterRightOperandNode(binaryLogicExpression) } - val isAnd = binaryLogicExpression.kind == LogicOperationKind.AND - val lastNode = lastNodes.pop() - addEdge(lastNode, leftExitNode) - addEdge(leftExitNode, binaryLogicExpressionExitNodes.top(), propagateDeadness = false, isDead = leftBooleanConstValue == isAnd) - addEdge(leftExitNode, rightEnterNode, isDead = leftBooleanConstValue == !isAnd) - lastNodes.push(rightEnterNode) + addNewSimpleNode(leftExitNode) + lastNodes.push(leftExitNode) // to create an exit edge later + val rhsNeverExecuted = + binaryLogicExpression.leftOperand.booleanConstValue == (binaryLogicExpression.kind != LogicOperationKind.AND) + addNewSimpleNode(rightEnterNode, isDead = rhsNeverExecuted) return leftExitNode to rightEnterNode } - fun exitBinaryLogicExpression(): AbstractBinaryExitNode { - val exitNode = binaryLogicExpressionExitNodes.pop() + fun exitBinaryLogicExpression(binaryLogicExpression: FirBinaryLogicExpression): AbstractBinaryExitNode { + val exitNode = when (binaryLogicExpression.kind) { + LogicOperationKind.AND -> createBinaryAndExitNode(binaryLogicExpression) + LogicOperationKind.OR -> createBinaryOrExitNode(binaryLogicExpression) + } val rightNode = lastNodes.pop() - addEdge(rightNode, exitNode, propagateDeadness = false) - exitNode.updateDeadStatus() + val leftNode = lastNodes.pop() + val rhsAlwaysExecuted = + binaryLogicExpression.leftOperand.booleanConstValue == (binaryLogicExpression.kind == LogicOperationKind.AND) + addEdge(leftNode, exitNode, propagateDeadness = !rhsAlwaysExecuted, isDead = rhsAlwaysExecuted) + addEdge(rightNode, exitNode, propagateDeadness = rhsAlwaysExecuted) lastNodes.push(exitNode) return exitNode } @@ -959,7 +918,7 @@ class ControlFlowGraphBuilder { // try { try { throw E2() } catch (e: E1) { } finally { } } catch (e: E2) { } // \-----------------------------^ \-----------------^ // Wait, that's just `addExceptionEdgesFrom(exitNode)` again! - val nextExitLevel = exitTargetsForTry.top().level + val nextExitLevel = levelOfNextExceptionCatchingGraph() val nextFinally = finallyEnterNodes.topOrNull()?.takeIf { it.level > nextExitLevel } if (nextFinally != null) { addEdge(exitNode, nextFinally, label = UncaughtExceptionPath, propagateDeadness = false) @@ -969,25 +928,24 @@ class ControlFlowGraphBuilder { // /-----------v // f@ { try { return@f } finally { b }; c } // \-----^ - exitNode.addReturnEdges(exitTargetsForReturn, nextFinallyOrExitLevel) + exitNode.addReturnEdges(exitTargetsForReturn.values, nextFinallyOrExitLevel) // /-----------v // f@ while (x) { try { continue@f } finally { b }; c } // ^------------------------------------/ - exitNode.addReturnEdges(loopConditionEnterNodes, nextFinallyOrExitLevel) + exitNode.addReturnEdges(loopConditionEnterNodes.values, nextFinallyOrExitLevel) // /-----------v // f@ while (x) { try { break@f } finally { b }; c } // \-----^ - exitNode.addReturnEdges(loopExitNodes, nextFinallyOrExitLevel) + exitNode.addReturnEdges(loopExitNodes.values, nextFinallyOrExitLevel) return exitNode } - private fun > CFGNode<*>.addReturnEdges(nodes: Stack, minLevel: Int) { - for (node in nodes.all()) { + private fun CFGNode<*>.addReturnEdges(nodes: Iterable>, minLevel: Int) { + for (node in nodes) { when { - node.level < minLevel -> break // TODO: this check is imprecise and can add redundant edges: // x@{ try { return@x } finally {}; try {} finally { /* return@x target is in nonDirectJumps */ } - node !in nonDirectJumps -> continue + node.level < minLevel || node !in nonDirectJumps -> continue // TODO: if the input to finally with that label is dead, then so should be the exit probably node.returnPathIsBackwards -> addBackEdge(this, node, label = node.returnPathLabel) else -> addEdge(this, node, propagateDeadness = false, label = node.returnPathLabel) @@ -1023,11 +981,15 @@ class ControlFlowGraphBuilder { } } val nextFinally = finallyEnterNodes.topOrNull() - if (nextFinally != null && nextFinally.level > exitTargetsForTry.top().level) { + if (nextFinally != null && nextFinally.level > levelOfNextExceptionCatchingGraph()) { addEdge(node, nextFinally, propagateDeadness = false, label = UncaughtExceptionPath) } } + // Called-in-place function graphs are effectively inlined, exceptions go to enclosing function. + private fun levelOfNextExceptionCatchingGraph(): Int = + graphs.all().first { it.kind != ControlFlowGraph.Kind.AnonymousFunctionCalledInPlace }.exitNode.level + //this is a workaround to make function call dead when call is completed _after_ building its node in the graph //this happens when completing the last call in try/catch blocks //todo this doesn't make fully 'right' Nothing node (doesn't support going to catch and pass through finally) @@ -1155,16 +1117,13 @@ class ControlFlowGraphBuilder { // Things like annotations and `contract { ... }` use normal call resolution, but aren't real expressions // and are never evaluated. We'll push all nodes created in the process into a stub graph, then throw it away. // TODO: don't waste time creating the nodes in the first place - graphs.push(ControlFlowGraph(null, "", ControlFlowGraph.Kind.FakeCall)) - return createFakeExpressionEnterNode().also { - lastNodes.push(it) - exitTargetsForTry.push(it) // technically might create CFG loops, but the graph will never be visited anyway... + return enterGraph(null, "", ControlFlowGraph.Kind.FakeCall) { + createFakeExpressionEnterNode() to createFakeExpressionEnterNode() } } fun exitFakeExpression() { lastNodes.pop() - exitTargetsForTry.pop() graphs.pop().also { assert(it.kind == ControlFlowGraph.Kind.FakeCall) } } @@ -1182,21 +1141,13 @@ class ControlFlowGraphBuilder { fun enterInitBlock(initBlock: FirAnonymousInitializer): InitBlockEnterNode { // TODO: questionable moment that we should pass data flow from init to init - val (enterNode, exitNode) = enterGraph(initBlock, "init block", ControlFlowGraph.Kind.Function) { + return enterGraph(initBlock, "init block", ControlFlowGraph.Kind.Function) { createInitBlockEnterNode(it) to createInitBlockExitNode(it) - } - exitTargetsForTry.push(exitNode) - addEdgeIfLocalClassMember(initBlock, enterNode) - return enterNode + }.also { addEdgeIfLocalClassMember(it) } } - fun exitInitBlock(initBlock: FirAnonymousInitializer): Pair { - val exitNode = exitTargetsForTry.pop() as InitBlockExitNode - popAndAddEdge(exitNode) - val graph = popGraph() - assert(graph.declaration == initBlock) - exitNode.updateDeadStatus() - return exitNode to graph + fun exitInitBlock(): Pair { + return exitGraph() } // ----------------------------------- Safe calls ----------------------------------- diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index 73f72ce0c5f..f6eb75c7fe7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -704,7 +704,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve return context.withAnonymousInitializer(anonymousInitializer, session) { val result = transformDeclarationContent(anonymousInitializer, ResolutionMode.ContextIndependent) as FirAnonymousInitializer - val graph = dataFlowAnalyzer.exitInitBlock(result) + val graph = dataFlowAnalyzer.exitInitBlock() result.replaceControlFlowGraphReference(FirControlFlowGraphReferenceImpl(graph)) result } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 7a09a3c6b10..f702dccab88 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -854,7 +854,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT .transformLeftOperand(this, ResolutionMode.WithExpectedType(booleanType)) .also(dataFlowAnalyzer::exitLeftBinaryLogicExpressionArgument) .transformRightOperand(this, ResolutionMode.WithExpectedType(booleanType)) - .also { dataFlowAnalyzer.exitBinaryLogicExpression() } + .also(dataFlowAnalyzer::exitBinaryLogicExpression) .transformOtherChildren(transformer, ResolutionMode.WithExpectedType(booleanType)) .also { it.resultType = booleanType } } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/Stack.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/Stack.kt index c380fbf6838..b9d1fb939ac 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/Stack.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/Stack.kt @@ -5,10 +5,6 @@ package org.jetbrains.kotlin.fir.resolve.dfa -import org.jetbrains.kotlin.fir.FirElement -import org.jetbrains.kotlin.fir.declarations.FirDeclaration -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.util.asReversedFrozen abstract class Stack { @@ -47,61 +43,3 @@ private class StackImpl(vararg values: T) : Stack() { override fun all(): List = stack.asReversedFrozen() } - -class NodeStorage> : Stack(){ - private val stack: Stack = StackImpl() - private val map: MutableMap = mutableMapOf() - - override val size: Int get() = stack.size - - override fun top(): N = stack.top() - - override fun pop(): N = stack.pop().also { - map.remove(it.fir) - } - - override fun push(value: N) { - stack.push(value) - map[value.fir] = value - } - - operator fun get(key: T): N? { - return map[key] - } - - override fun reset() { - stack.reset() - map.clear() - } - - override fun all(): List = stack.all() -} - -class SymbolBasedNodeStorage> : Stack() where T : FirElement { - private val stack: Stack = StackImpl() - private val map: MutableMap, N> = mutableMapOf() - - override val size: Int get() = stack.size - - override fun top(): N = stack.top() - - override fun pop(): N = stack.pop().also { - map.remove((it.fir as FirDeclaration).symbol) - } - - override fun push(value: N) { - stack.push(value) - map[(value.fir as FirDeclaration).symbol] = value - } - - operator fun get(key: FirBasedSymbol<*>): N? { - return map[key] - } - - override fun reset() { - stack.reset() - map.clear() - } - - override fun all(): List = stack.all() -} diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt index db308974202..f2c40efb36a 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt @@ -746,7 +746,7 @@ object FirStub : FirExpression() { override fun replaceTypeRef(newTypeRef: FirTypeRef) { assert(newTypeRef.isNothing) } } -class FakeExpressionEnterNode(owner: ControlFlowGraph, level: Int) : CFGNode(owner, level) { +class FakeExpressionEnterNode(owner: ControlFlowGraph, level: Int) : CFGNode(owner, level), GraphEnterNodeMarker, GraphExitNodeMarker { init { isDead = true } override val fir: FirStub = FirStub diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index 3264dc1abcf..ba0a82ae0e0 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -37,6 +37,7 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k Class, Function, AnonymousFunction, + AnonymousFunctionCalledInPlace, PropertyInitializer, FieldInitializer, FakeCall,