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:
+11
-6
@@ -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.declarations.utils.isLocal
|
||||||
import org.jetbrains.kotlin.fir.expressions.*
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.builder.buildUnitExpression
|
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.render
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
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.types.*
|
||||||
import org.jetbrains.kotlin.fir.util.ListMultimap
|
import org.jetbrains.kotlin.fir.util.ListMultimap
|
||||||
import org.jetbrains.kotlin.fir.util.listMultimapOf
|
import org.jetbrains.kotlin.fir.util.listMultimapOf
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@@ -342,9 +340,13 @@ class ControlFlowGraphBuilder {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for ((exit, kind) in currentLevelExits) {
|
for ((exit, kind) in currentLevelExits) {
|
||||||
// Since `node` is a union node, it is dead iff any input is dead. For once, `propagateDeadness`
|
// Do not add data flow edges from non-terminating lambdas; there is no "dead data flow only"
|
||||||
// semantics are correct without an `updateDeadStatus`.
|
// `EdgeKind`. TODO?
|
||||||
addEdge(exit, node, preferredKind = kind, propagateDeadness = kind.usedInCfa)
|
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,
|
// This may sound shocking, but `do...while` conditions can `continue` to themselves,
|
||||||
// so we can't pop the node off the stack here.
|
// so we can't pop the node off the stack here.
|
||||||
val conditionEnterNode = loopConditionEnterNodes.top().also { addNewSimpleNode(it) }
|
val conditionEnterNode = loopConditionEnterNodes.top().also { addNewSimpleNode(it) }
|
||||||
|
// Might have had live `continue`s with an unreachable block exit, so recompute deadness.
|
||||||
|
conditionEnterNode.updateDeadStatus()
|
||||||
levelCounter++
|
levelCounter++
|
||||||
return blockExitNode to conditionEnterNode
|
return blockExitNode to conditionEnterNode
|
||||||
}
|
}
|
||||||
@@ -1103,7 +1107,8 @@ class ControlFlowGraphBuilder {
|
|||||||
CFGNode.addEdge(node, stub, EdgeKind.DeadForward, propagateDeadness = false)
|
CFGNode.addEdge(node, stub, EdgeKind.DeadForward, propagateDeadness = false)
|
||||||
for ((to, edge) in edges) {
|
for ((to, edge) in edges) {
|
||||||
val kind = if (edge.kind.isBack) EdgeKind.DeadBackward else EdgeKind.DeadForward
|
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)
|
propagateDeadnessForward(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user