FIR CFA: fix deadness of various nodes

* `do { continue } while (x)`: block exit is dead, condition is not;

 * `try { a?.incomplete() } catch (e) { x }`: when the call is completed
   and found to return `Nothing`, the safe call exit node is still live
   because `a` could still be null;

 * `f(a@{ g { return@a } }, x)`: if call to `g` is not completed, there
   should not be a dead data flow edge from the lambda to `f` because
   there's no such thing as a dead data flow edge (DeadForward is CF+DF).
This commit is contained in:
pyos
2022-11-28 17:33:24 +01:00
committed by Dmitriy Novozhilov
parent e7e5569539
commit bcc54deb64
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildUnitExpression
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.dfa.*
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
@@ -21,7 +20,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.util.ListMultimap
import org.jetbrains.kotlin.fir.util.listMultimapOf
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import kotlin.random.Random
@@ -342,9 +340,13 @@ class ControlFlowGraphBuilder {
}
} else {
for ((exit, kind) in currentLevelExits) {
// Since `node` is a union node, it is dead iff any input is dead. For once, `propagateDeadness`
// semantics are correct without an `updateDeadStatus`.
addEdge(exit, node, preferredKind = kind, propagateDeadness = kind.usedInCfa)
// Do not add data flow edges from non-terminating lambdas; there is no "dead data flow only"
// `EdgeKind`. TODO?
if (kind.usedInCfa || !exit.isDead) {
// Since `node` is a union node, it is dead iff any input is dead. For once, `propagateDeadness`
// semantics are correct without an `updateDeadStatus`.
addEdge(exit, node, preferredKind = kind)
}
}
}
}
@@ -854,6 +856,8 @@ class ControlFlowGraphBuilder {
// 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) }
// Might have had live `continue`s with an unreachable block exit, so recompute deadness.
conditionEnterNode.updateDeadStatus()
levelCounter++
return blockExitNode to conditionEnterNode
}
@@ -1103,7 +1107,8 @@ class ControlFlowGraphBuilder {
CFGNode.addEdge(node, stub, EdgeKind.DeadForward, propagateDeadness = false)
for ((to, edge) in edges) {
val kind = if (edge.kind.isBack) EdgeKind.DeadBackward else EdgeKind.DeadForward
CFGNode.addEdge(stub, to, kind, propagateDeadness = true, label = edge.label)
CFGNode.addEdge(stub, to, kind, propagateDeadness = false, label = edge.label)
to.updateDeadStatus()
propagateDeadnessForward(to)
}
}