FIR CFG: correct target and label for rethrow in try expression

^KT-45385 Fixed
This commit is contained in:
Jinseong Jeon
2021-03-10 21:41:28 -08:00
committed by TeamCityServer
parent 4f20d2dccf
commit 55561fad37
3 changed files with 40 additions and 13 deletions
@@ -858,7 +858,7 @@ class ControlFlowGraphBuilder {
val finallyEnterNode = finallyEnterNodes.topOrNull()
// NB: Check the level to avoid adding an edge to the finally block at an upper level.
if (finallyEnterNode != null && finallyEnterNode.level == levelCounter + 1) {
// TODO: in case of return/throw in try main block, we need a unique label.
// TODO: in case of return/continue/break in try main block, we need a unique label.
addEdge(node, finallyEnterNode)
} else {
addEdge(node, tryExitNodes.top())
@@ -891,7 +891,7 @@ class ControlFlowGraphBuilder {
val finallyEnterNode = finallyEnterNodes.topOrNull()
// NB: Check the level to avoid adding an edge to the finally block at an upper level.
if (finallyEnterNode != null && finallyEnterNode.level == levelCounter + 1) {
// TODO: in case of return/rethrow in catch clause, we need a unique label.
// TODO: in case of return/continue/break in catch clause, we need a unique label.
addEdge(it, finallyEnterNode, propagateDeadness = false)
} else {
addEdge(it, tryExitNodes.top(), propagateDeadness = false)
@@ -913,7 +913,9 @@ class ControlFlowGraphBuilder {
addEdge(it, tryExitNode)
// a flow that exits to the exit target while there was an uncaught exception.
addEdge(it, exitTargetsForTry.top(), label = UncaughtExceptionPath)
// TODO: differentiate flows that return/(re)throw in try main block or catch clauses.
// 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
}
}
@@ -1248,10 +1250,36 @@ class ControlFlowGraphBuilder {
}
private fun addNodeThatReturnsNothing(node: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Forward) {
val exitNode: CFGNode<*> = exitTargetsForTry.top()
// If an expression, which returns Nothing, isn't inside a try/catch/finally, that is an uncaught exception path.
val label = if (tryExitNodes.isEmpty) UncaughtExceptionPath else NormalPath
addNodeWithJump(node, exitNode, preferredKind, label = label)
// If an expression, which returns Nothing, ...(1)
val targetNode = when {
tryExitNodes.isEmpty -> {
// (1)... isn't inside a try expression, that is an uncaught exception path.
exitTargetsForTry.top()
}
// (1)... inside a try expression...(2)
finallyEnterNodes.topOrNull()?.level == levelCounter -> {
// (2)... with finally
// Either in try-main or catch. Route to `finally`
finallyEnterNodes.top()
}
// (2)... without finally or within finally ...(3)
tryExitNodes.top().fir.finallyBlock == null -> {
// (3)... without finally ...(4)
// Either in try-main or catch.
if (tryExitNodes.size == tryMainExitNodes.size) {
// (4)... in catch, i.e., re-throw.
exitTargetsForTry.top()
} else {
// (4)... in try-main. We already have:
// edges from try-main enter node to each catch clause enter node and
// edges from catch clause enter node to exit target (w/ UncaughtExceptionPath)
return
}
}
// (3)... within finally.
else -> exitTargetsForTry.top()
}
addNodeWithJump(node, targetNode, preferredKind, label = UncaughtExceptionPath)
}
private fun addNodeWithJump(
@@ -1,4 +1,3 @@
// IGNORE_FIR_DIAGNOSTICS
// FILE: 1.kt
class A(val s: String)
@@ -1,7 +1,7 @@
fun foo(): Int = 42
object ThrowInTryWithCatch {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>private val p: String<!>
private val p: String
init {
try {
@@ -40,7 +40,7 @@ object ThrowInFinally {
}
object RethrowInCatch {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>private val p: String<!>
private val p: String
init {
try {
@@ -67,7 +67,7 @@ object RethrowInCatchWithFinally {
}
object InnerTryWithCatch {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>private val p: String<!>
private val p: String
init {
try {
@@ -117,7 +117,7 @@ object InnerTryWithCatchAndFinally {
}
object InnerCatch {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>private val p: String<!>
private val p: String
init {
try {
@@ -152,7 +152,7 @@ object InnerCatchWithFinally {
}
object InnerCatchOuterRethrow {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>private val p: String<!>
private val p: String
init {
try {