FIR CFA: make node unionness a runtime property

Class exit node should only unify data flow if it is an anonymous
object, in which case it only has one control flow input. Coversely,
any other class exit node has normal merge semantics for its control
flow inputs, but it won't have any data flow inputs.
This commit is contained in:
pyos
2023-01-18 13:21:14 +01:00
committed by teamcity
parent 9f17b5de97
commit 166ddd9ea9
14 changed files with 59 additions and 73 deletions
@@ -158,8 +158,6 @@ object FirCallsEffectAnalyzer : FirControlFlowChecker() {
override fun visitNode(node: CFGNode<*>, data: IllegalScopeContext) {}
override fun <T> visitUnionNode(node: T, data: IllegalScopeContext) where T : CFGNode<*>, T : UnionNodeMarker {}
override fun visitFunctionEnterNode(node: FunctionEnterNode, data: IllegalScopeContext) {
// TODO: this is not how CFG works, this should be done by FIR tree traversal. Especially considering that
// none of these methods use anything from the CFG other than `node.fir`, which should've been a hint.
@@ -237,7 +235,7 @@ object FirCallsEffectAnalyzer : FirControlFlowChecker() {
node: FunctionCallNode,
data: PathAwareLambdaInvocationInfo
): PathAwareLambdaInvocationInfo {
var dataForNode = visitUnionNode(node, data)
var dataForNode = visitNode(node, data)
val functionSymbol = node.fir.toResolvedCallableSymbol() as? FirFunctionSymbol<*>?
val contractDescription = functionSymbol?.resolvedContractDescription
@@ -51,10 +51,8 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec
val reporter: DiagnosticReporter,
val context: CheckerContext
) : ControlFlowGraphVisitorVoid() {
override fun visitNode(node: CFGNode<*>) {}
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {
// TODO: f({ x = 1 }, { x = 2 }) - which to report?
override fun visitNode(node: CFGNode<*>) {
// TODO: `node.isUnion` - f({ x = 1 }, { x = 2 }) - which to report?
// Also this is currently indistinguishable from x = 1; f({}, {}).
}
@@ -45,7 +45,7 @@ private fun <I : ControlFlowInfo<I, *, *>> ControlFlowGraph.collectDataForNodeIn
TraverseDirection.Backward -> node.followingCfgNodes
}
// TODO: if data for previousNodes hasn't changed, then should be no need to recompute data for this one
val union = node is UnionNodeMarker
val union = node.isUnion
val previousData =
previousNodes.mapNotNull {
val k = when (direction) {
@@ -34,8 +34,6 @@ class LocalPropertyAndCapturedWriteCollector private constructor() : ControlFlow
override fun visitNode(node: CFGNode<*>) {}
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {}
override fun visitVariableDeclarationNode(node: VariableDeclarationNode) {
symbols[node.fir.symbol] = lambdaOrLocalFunctionStack.lastOrNull() == null
}
@@ -65,9 +65,4 @@ abstract class PathAwareControlFlowGraphVisitor<I : ControlFlowInfo<I, *, *>> :
node: CFGNode<*>,
data: PathAwareControlFlowInfo<I>
): PathAwareControlFlowInfo<I> = data
override fun <T> visitUnionNode(
node: T,
data: PathAwareControlFlowInfo<I>
): PathAwareControlFlowInfo<I> where T : CFGNode<*>, T : UnionNodeMarker = data
}
@@ -40,8 +40,6 @@ object FirTailrecFunctionChecker : FirSimpleFunctionChecker() {
graph.traverse(object : ControlFlowGraphVisitorVoid() {
override fun visitNode(node: CFGNode<*>) {}
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {}
override fun visitTryMainBlockEnterNode(node: TryMainBlockEnterNode) {
tryScopeCount++
}
@@ -82,8 +82,6 @@ object CanBeValChecker : AbstractFirPropertyInitializationChecker() {
) : ControlFlowGraphVisitorVoid() {
override fun visitNode(node: CFGNode<*>) {}
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {}
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return
if (symbol !in localProperties) return
@@ -50,8 +50,6 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
) : ControlFlowGraphVisitorVoid() {
override fun visitNode(node: CFGNode<*>) {}
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {}
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
val variableSymbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return
val dataPerNode = data[node] ?: return
@@ -182,12 +180,6 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
): PathAwareVariableStatusInfo =
super.visitNode(node, data).withAnnotationsFrom(node)
override fun <T> visitUnionNode(
node: T,
data: PathAwareVariableStatusInfo
): PathAwareVariableStatusInfo where T : CFGNode<*>, T : UnionNodeMarker =
super.visitUnionNode(node, data).withAnnotationsFrom(node)
override fun visitVariableDeclarationNode(
node: VariableDeclarationNode,
data: PathAwareVariableStatusInfo
@@ -289,7 +281,7 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
node: FunctionCallNode,
data: PathAwareVariableStatusInfo
): PathAwareVariableStatusInfo {
val dataForNode = visitUnionNode(node, data)
val dataForNode = visitNode(node, data)
val reference = node.fir.calleeReference.resolved ?: return dataForNode
val functionSymbol = reference.resolvedSymbol as? FirFunctionSymbol<*> ?: return dataForNode
val symbol = if (functionSymbol.callableId.callableName.identifier == "invoke") {