From c160511244c85f3b223dd5c54400f40fe90b05ef Mon Sep 17 00:00:00 2001 From: Andrey Zinovyev Date: Mon, 12 Jul 2021 16:27:09 +0300 Subject: [PATCH] [FIR] Basic support for `break` inside try-finally blocks Support single try expression only --- .../declaration/FirPropertyAccessorChecker.kt | 1 - .../dfa/cfg/ControlFlowGraphBuilder.kt | 32 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyAccessorChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyAccessorChecker.kt index f82d33e93d8..df4b91b8bd7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyAccessorChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyAccessorChecker.kt @@ -56,7 +56,6 @@ object FirPropertyAccessorsTypesChecker : FirPropertyChecker() { reporter.reportOn(getterReturnTypeSource, FirErrors.WRONG_GETTER_RETURN_TYPE, propertyType, getterReturnType, context) } } -// FirFunctionReturnChecker.checkHasReturnIfBlock(getter, reporter, context) } } 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 8096552516f..e1ca7ba0987 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 @@ -603,7 +603,7 @@ class ControlFlowGraphBuilder { is FirBreakExpression -> loopExitNodes[jump.target.labeledElement] else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}") } - addNodeWithJump(node, nextNode, isBack = jump is FirContinueExpression) + addNodeWithJump(node, nextNode, isBack = jump is FirContinueExpression, isBreak = jump is FirBreakExpression) return node } @@ -929,6 +929,10 @@ class ControlFlowGraphBuilder { val tryExitNode = tryExitNodes.top() // a flow where either there wasn't any exception or caught if any. addEdge(it, tryExitNode) + if (tryExitNode.isDead) { + //refresh forward links, which were created before finalizing try expression (eg created by `break`) + propagateDeadnessForward(tryExitNode) + } // a flow that exits to the exit target while there was an uncaught exception. addEdge(it, exitTargetsForTry.top(), propagateDeadness = false, label = UncaughtExceptionPath) // TODO: differentiate flows that return/(re)throw in try main block and/or catch clauses @@ -1314,6 +1318,7 @@ class ControlFlowGraphBuilder { preferredKind: EdgeKind = EdgeKind.Forward, isBack: Boolean = false, label: EdgeLabel = NormalPath, + isBreak: Boolean = false ) { popAndAddEdge(node, preferredKind) if (targetNode != null) { @@ -1325,7 +1330,16 @@ class ControlFlowGraphBuilder { addBackEdge(node, targetNode, label = label) } } else { - addEdge(node, targetNode, propagateDeadness = false, label = label) + //TODO this supports single try-finally block only + //need to get all try-finally up to target + val finallyNodes = finallyBefore(targetNode) + if (finallyNodes != null && isBreak) { + val (finallyEnter, finallyExit) = finallyNodes + addEdge(node, finallyEnter, propagateDeadness = false, label = label) + addEdge(finallyExit, targetNode, propagateDeadness = false, label = label) + } else { + addEdge(node, targetNode, propagateDeadness = false, label = label) + } } } val stub = createStubNode() @@ -1333,6 +1347,10 @@ class ControlFlowGraphBuilder { lastNodes.push(stub) } + private fun finallyBefore(target: CFGNode<*>): Pair? { + return finallyEnterNodes.topOrNull()?.takeIf { it.level > target.level }?.let { it to tryExitNodes.top() } + } + private fun popAndAddEdge(to: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Forward) { addEdge(lastNodes.pop(), to, preferredKind = preferredKind) } @@ -1361,6 +1379,16 @@ class ControlFlowGraphBuilder { addEdge(from, to, propagateDeadness = false, isDead = isDead, isBack = true, preferredKind = EdgeKind.CfgBackward, label = label) } + private fun propagateDeadnessForward(node: CFGNode<*>) { + if (!node.isDead) return + node.followingNodes + .filter { node.outgoingEdges.getValue(it).kind == EdgeKind.Forward } + .forEach { target -> + CFGNode.addJustKindEdge(node, target, EdgeKind.DeadForward, false) + target.updateDeadStatus() + } + } + // ----------------------------------- Utils ----------------------------------- private inline fun Collection.forEachMember(block: (FirDeclaration) -> Unit) {