From 6dd4164f1e4f5b1f9492ff1caf85808d6430debe Mon Sep 17 00:00:00 2001 From: Andrey Zinovyev Date: Thu, 5 Aug 2021 12:32:16 +0300 Subject: [PATCH] [FIR][CFG] Link finally exit to jump target Instead of try exit node. So try exit is used only for real exits from try expressions. --- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 4 ++-- .../dfa/cfg/ControlFlowGraphBuilder.kt | 24 +++++++++++-------- ...ControlFlowStatementsResolveTransformer.kt | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) 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 323c0fab84c..22bd425271e 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 @@ -817,8 +817,8 @@ abstract class FirDataFlowAnalyzer( graphBuilder.enterFinallyBlock().mergeIncomingFlow(updateReceivers = true, shouldForkFlow = true) } - fun exitFinallyBlock(tryExpression: FirTryExpression) { - graphBuilder.exitFinallyBlock(tryExpression).mergeIncomingFlow() + fun exitFinallyBlock() { + graphBuilder.exitFinallyBlock().mergeIncomingFlow() } fun exitTryExpression(callCompleted: Boolean) { 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 392f90fb427..67f2fc9e69a 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 @@ -96,6 +96,7 @@ class ControlFlowGraphBuilder { private val catchNodeStorage: NodeStorage get() = catchNodeStorages.top() private val catchExitNodeStorages: Stack> = stackOf() private val finallyEnterNodes: Stack = stackOf() + private val finallyExitNodes: NodeStorage = NodeStorage() private val initBlockExitNodes: Stack = stackOf() @@ -872,6 +873,7 @@ class ControlFlowGraphBuilder { // a flow where an uncaught exception is thrown before executing any of try-main block. addEdge(enterTryExpressionNode, finallyEnterNode, propagateDeadness = false, label = UncaughtExceptionPath) finallyEnterNodes.push(finallyEnterNode) + finallyExitNodes.push(createFinallyBlockExitNode(tryExpression)) } return enterTryExpressionNode to enterTryNodeBlock @@ -938,18 +940,19 @@ class ControlFlowGraphBuilder { return enterNode } - fun exitFinallyBlock(tryExpression: FirTryExpression): FinallyBlockExitNode { - return createFinallyBlockExitNode(tryExpression).also { - popAndAddEdge(it) + fun exitFinallyBlock(): FinallyBlockExitNode { + return finallyExitNodes.pop().also { finallyExit -> + popAndAddEdge(finallyExit) val tryExitNode = tryExitNodes.top() // a flow where either there wasn't any exception or caught if any. - addEdge(it, tryExitNode) - if (tryExitNode.isDead) { + addEdge(finallyExit, tryExitNode) + if (finallyExit.isDead) { //refresh forward links, which were created before finalizing try expression (eg created by `break`) - propagateDeadnessForward(tryExitNode) + propagateDeadnessForward(finallyExit) } // a flow that exits to the exit target while there was an uncaught exception. - addEdge(it, exitTargetsForTry.top(), propagateDeadness = false, label = UncaughtExceptionPath) + //todo this edge might exist already if try has jump outside, so we effectively lose labeled edge here + addEdgeIfNotExist(finallyExit, exitTargetsForTry.top(), propagateDeadness = false, label = UncaughtExceptionPath) // TODO: differentiate flows that return/(re)throw in try main block and/or catch clauses // To do so, we need mappings from such distinct label to original exit target (fun exit or loop) // Also, CFG should support multiple edges towards the same destination node @@ -1423,9 +1426,10 @@ class ControlFlowGraphBuilder { lastNodes.push(stub) } - private fun finallyBefore(target: CFGNode<*>): List> { - return finallyEnterNodes.all().takeWhile { it.level > target.level }.map { - it to tryExitNodes[it.fir]!! + private fun finallyBefore(target: CFGNode<*>): List> { + return finallyEnterNodes.all().takeWhile { it.level > target.level }.map { finallyEnter -> + val finallyExit = finallyExitNodes[finallyEnter.fir]!! + finallyEnter to finallyExit } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt index b0de52933bf..43578ed0ef9 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt @@ -154,7 +154,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirBodyResolveTran result = if (result.finallyBlock != null) { result.also { dataFlowAnalyzer.enterFinallyBlock() } .transformFinallyBlock(transformer, ResolutionMode.ContextIndependent) - .also(dataFlowAnalyzer::exitFinallyBlock) + .also { dataFlowAnalyzer.exitFinallyBlock() } } else { result }