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 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) { 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 // 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. // 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, node: FunctionCallNode,
data: PathAwareLambdaInvocationInfo data: PathAwareLambdaInvocationInfo
): PathAwareLambdaInvocationInfo { ): PathAwareLambdaInvocationInfo {
var dataForNode = visitUnionNode(node, data) var dataForNode = visitNode(node, data)
val functionSymbol = node.fir.toResolvedCallableSymbol() as? FirFunctionSymbol<*>? val functionSymbol = node.fir.toResolvedCallableSymbol() as? FirFunctionSymbol<*>?
val contractDescription = functionSymbol?.resolvedContractDescription val contractDescription = functionSymbol?.resolvedContractDescription
@@ -51,10 +51,8 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec
val reporter: DiagnosticReporter, val reporter: DiagnosticReporter,
val context: CheckerContext val context: CheckerContext
) : ControlFlowGraphVisitorVoid() { ) : ControlFlowGraphVisitorVoid() {
override fun visitNode(node: CFGNode<*>) {} override fun visitNode(node: CFGNode<*>) {
// TODO: `node.isUnion` - f({ x = 1 }, { x = 2 }) - which to report?
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {
// TODO: f({ x = 1 }, { x = 2 }) - which to report?
// Also this is currently indistinguishable from x = 1; f({}, {}). // Also this is currently indistinguishable from x = 1; f({}, {}).
} }
@@ -45,7 +45,7 @@ private fun <I : ControlFlowInfo<I, *, *>> ControlFlowGraph.collectDataForNodeIn
TraverseDirection.Backward -> node.followingCfgNodes TraverseDirection.Backward -> node.followingCfgNodes
} }
// TODO: if data for previousNodes hasn't changed, then should be no need to recompute data for this one // 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 = val previousData =
previousNodes.mapNotNull { previousNodes.mapNotNull {
val k = when (direction) { val k = when (direction) {
@@ -34,8 +34,6 @@ class LocalPropertyAndCapturedWriteCollector private constructor() : ControlFlow
override fun visitNode(node: CFGNode<*>) {} override fun visitNode(node: CFGNode<*>) {}
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {}
override fun visitVariableDeclarationNode(node: VariableDeclarationNode) { override fun visitVariableDeclarationNode(node: VariableDeclarationNode) {
symbols[node.fir.symbol] = lambdaOrLocalFunctionStack.lastOrNull() == null symbols[node.fir.symbol] = lambdaOrLocalFunctionStack.lastOrNull() == null
} }
@@ -65,9 +65,4 @@ abstract class PathAwareControlFlowGraphVisitor<I : ControlFlowInfo<I, *, *>> :
node: CFGNode<*>, node: CFGNode<*>,
data: PathAwareControlFlowInfo<I> data: PathAwareControlFlowInfo<I>
): PathAwareControlFlowInfo<I> = data ): 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() { graph.traverse(object : ControlFlowGraphVisitorVoid() {
override fun visitNode(node: CFGNode<*>) {} override fun visitNode(node: CFGNode<*>) {}
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {}
override fun visitTryMainBlockEnterNode(node: TryMainBlockEnterNode) { override fun visitTryMainBlockEnterNode(node: TryMainBlockEnterNode) {
tryScopeCount++ tryScopeCount++
} }
@@ -82,8 +82,6 @@ object CanBeValChecker : AbstractFirPropertyInitializationChecker() {
) : ControlFlowGraphVisitorVoid() { ) : ControlFlowGraphVisitorVoid() {
override fun visitNode(node: CFGNode<*>) {} override fun visitNode(node: CFGNode<*>) {}
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {}
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) { override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return
if (symbol !in localProperties) return if (symbol !in localProperties) return
@@ -50,8 +50,6 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
) : ControlFlowGraphVisitorVoid() { ) : ControlFlowGraphVisitorVoid() {
override fun visitNode(node: CFGNode<*>) {} override fun visitNode(node: CFGNode<*>) {}
override fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker {}
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) { override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
val variableSymbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return val variableSymbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return
val dataPerNode = data[node] ?: return val dataPerNode = data[node] ?: return
@@ -182,12 +180,6 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
): PathAwareVariableStatusInfo = ): PathAwareVariableStatusInfo =
super.visitNode(node, data).withAnnotationsFrom(node) 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( override fun visitVariableDeclarationNode(
node: VariableDeclarationNode, node: VariableDeclarationNode,
data: PathAwareVariableStatusInfo data: PathAwareVariableStatusInfo
@@ -289,7 +281,7 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
node: FunctionCallNode, node: FunctionCallNode,
data: PathAwareVariableStatusInfo data: PathAwareVariableStatusInfo
): PathAwareVariableStatusInfo { ): PathAwareVariableStatusInfo {
val dataForNode = visitUnionNode(node, data) val dataForNode = visitNode(node, data)
val reference = node.fir.calleeReference.resolved ?: return dataForNode val reference = node.fir.calleeReference.resolved ?: return dataForNode
val functionSymbol = reference.resolvedSymbol as? FirFunctionSymbol<*> ?: return dataForNode val functionSymbol = reference.resolvedSymbol as? FirFunctionSymbol<*> ?: return dataForNode
val symbol = if (functionSymbol.callableId.callableName.identifier == "invoke") { val symbol = if (functionSymbol.callableId.callableName.identifier == "invoke") {
@@ -1119,7 +1119,7 @@ abstract class FirDataFlowAnalyzer(
// data flow for any of them until reaching a completed call. // data flow for any of them until reaching a completed call.
if (it is MergePostponedLambdaExitsNode) it.mergeIncomingFlow() else it.flow if (it is MergePostponedLambdaExitsNode) it.mergeIncomingFlow() else it.flow
} }
val result = logicSystem.joinFlow(previousFlows, union = this is UnionNodeMarker) val result = logicSystem.joinFlow(previousFlows, isUnion)
if (graphBuilder.lastNodeOrNull == this) { if (graphBuilder.lastNodeOrNull == this) {
// Here it is, the new `lastNode`. If the previous state is the only predecessor, then there is actually // Here it is, the new `lastNode`. If the previous state is the only predecessor, then there is actually
// nothing to update; `addTypeStatement` has already ensured we have the correct information. // nothing to update; `addTypeStatement` has already ensured we have the correct information.
@@ -273,7 +273,7 @@ class ControlFlowGraphBuilder {
postponedLambdaExits.push(mutableListOf()) postponedLambdaExits.push(mutableListOf())
} }
private fun <T> unifyDataFlowFromPostponedLambdas(node: T, callCompleted: Boolean) where T : CFGNode<*>, T : UnionNodeMarker { private fun unifyDataFlowFromPostponedLambdas(node: CFGNode<*>, callCompleted: Boolean) {
val currentLevelExits = postponedLambdaExits.pop() val currentLevelExits = postponedLambdaExits.pop()
if (currentLevelExits.isEmpty()) return if (currentLevelExits.isEmpty()) return
@@ -449,10 +449,6 @@ class ControlFlowGraphBuilder {
} }
} }
// Classes are not initialized in place so no point in merging data flow - it will not be used.
val mergeDataFlow = klass is FirAnonymousObject && klass.classKind != ClassKind.ENUM_ENTRY
val exitKind = if (mergeDataFlow) EdgeKind.Forward else EdgeKind.CfgForward
val lastInPlaceExit = calledInPlace.fold<_, CFGNode<*>>(enterNode) { lastNode, graph -> val lastInPlaceExit = calledInPlace.fold<_, CFGNode<*>>(enterNode) { lastNode, graph ->
// In local classes, we already have control flow (+ data flow) edge from `enterNode` // In local classes, we already have control flow (+ data flow) edge from `enterNode`
// to first in-place initializer. // to first in-place initializer.
@@ -461,10 +457,9 @@ class ControlFlowGraphBuilder {
} }
graph.exitNode graph.exitNode
} }
if (mergeDataFlow) { if (exitNode.isUnion) {
// TODO: this is wrong. Not sure if it's possible to describe the correct data flow graph here. Things // => this is an anonymous object => there's only one constructor => can use `exitNode`
// would be much easier if it was guaranteed that constructors are analyzed after all other members: // to unify data flow from all in-place-called members, including said constructor.
// they have no implicit return type anyway, and we could then pass correct data flow to them.
for (graph in calledInPlace) { for (graph in calledInPlace) {
addEdge(graph.exitNode, exitNode, preferredKind = EdgeKind.DfgForward) addEdge(graph.exitNode, exitNode, preferredKind = EdgeKind.DfgForward)
} }
@@ -486,7 +481,7 @@ class ControlFlowGraphBuilder {
if (delegatedConstructorExit !== enterNode || delegatedConstructorExit.previousNodes.isEmpty()) { if (delegatedConstructorExit !== enterNode || delegatedConstructorExit.previousNodes.isEmpty()) {
addEdgeToSubGraph(delegatedConstructorExit, graph.enterNode) addEdgeToSubGraph(delegatedConstructorExit, graph.enterNode)
} }
addEdge(graph.exitNode, exitNode, preferredKind = exitKind) addEdge(graph.exitNode, exitNode, preferredKind = if (exitNode.isUnion) EdgeKind.Forward else EdgeKind.CfgForward)
} }
if (constructors.isEmpty()) { if (constructors.isEmpty()) {
@@ -511,7 +506,7 @@ class ControlFlowGraphBuilder {
enterNode.subGraphs = calledInPlace + constructors.values enterNode.subGraphs = calledInPlace + constructors.values
exitNode.subGraphs = calledLater exitNode.subGraphs = calledLater
return exitNode.takeIf { mergeDataFlow } to popGraph() return exitNode.takeIf { it.isUnion } to popGraph()
} }
fun exitAnonymousObjectExpression(anonymousObjectExpression: FirAnonymousObjectExpression): AnonymousObjectExpressionExitNode? { fun exitAnonymousObjectExpression(anonymousObjectExpression: FirAnonymousObjectExpression): AnonymousObjectExpressionExitNode? {
@@ -70,7 +70,7 @@ class FirControlFlowGraphRenderVisitor(
when { when {
node.isDead -> "gray" node.isDead -> "gray"
node == node.owner.enterNode || node == node.owner.exitNode -> "red" node == node.owner.enterNode || node == node.owner.exitNode -> "red"
node is UnionNodeMarker -> "yellow" node.isUnion -> "yellow"
else -> null else -> null
}?.let { }?.let {
attributes += "style=\"filled\"" attributes += "style=\"filled\""
@@ -8,6 +8,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa.cfg package org.jetbrains.kotlin.fir.resolve.dfa.cfg
import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
@@ -31,6 +32,12 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
open val canThrow: Boolean get() = false open val canThrow: Boolean get() = false
// a ---> b ---> d
// \-> c -/
// Normal CFG semantics: a, then either b or c, then d
// If d is a union node: a, then *both* b and c in some unknown order, then d
open val isUnion: Boolean get() = false
companion object { companion object {
@CfgInternals @CfgInternals
fun addEdge( fun addEdge(
@@ -107,7 +114,7 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
@CfgInternals @CfgInternals
fun updateDeadStatus() { fun updateDeadStatus() {
isDead = if (this is UnionNodeMarker) isDead = if (isUnion)
_incomingEdges?.let { map -> map.values.any { it.kind.isDead } } == true _incomingEdges?.let { map -> map.values.any { it.kind.isDead } } == true
else else
_incomingEdges?.let { map -> map.size == previousNodes.size && map.values.all { it.kind.isDead || !it.kind.usedInCfa } } == true _incomingEdges?.let { map -> map.size == previousNodes.size && map.values.all { it.kind.isDead || !it.kind.usedInCfa } } == true
@@ -128,12 +135,6 @@ interface ExitNodeMarker
interface GraphEnterNodeMarker : EnterNodeMarker interface GraphEnterNodeMarker : EnterNodeMarker
interface GraphExitNodeMarker : ExitNodeMarker interface GraphExitNodeMarker : ExitNodeMarker
// a ---> b ---> d
// \-> c -/
// Normal CFG semantics: a, then either b or c, then d
// If d is an instance of this interface: a, then *both* b and c in some unknown order, then d
interface UnionNodeMarker
// ----------------------------------- EnterNode for declaration with CFG ----------------------------------- // ----------------------------------- EnterNode for declaration with CFG -----------------------------------
sealed class CFGNodeWithSubgraphs<out E : FirElement>(owner: ControlFlowGraph, level: Int) : CFGNode<E>(owner, level) { sealed class CFGNodeWithSubgraphs<out E : FirElement>(owner: ControlFlowGraph, level: Int) : CFGNode<E>(owner, level) {
@@ -245,7 +246,10 @@ class ClassEnterNode(owner: ControlFlowGraph, override val fir: FirClass, level:
} }
class ClassExitNode(owner: ControlFlowGraph, override val fir: FirClass, level: Int) : CFGNodeWithSubgraphs<FirClass>(owner, level), class ClassExitNode(owner: ControlFlowGraph, override val fir: FirClass, level: Int) : CFGNodeWithSubgraphs<FirClass>(owner, level),
GraphExitNodeMarker, UnionNodeMarker { GraphExitNodeMarker {
override val isUnion: Boolean
get() = fir is FirAnonymousObject && fir.classKind != ClassKind.ENUM_ENTRY
@set:CfgInternals @set:CfgInternals
override lateinit var subGraphs: List<ControlFlowGraph> override lateinit var subGraphs: List<ControlFlowGraph>
@@ -305,7 +309,10 @@ class PropertyInitializerExitNode(owner: ControlFlowGraph, override val fir: Fir
class DelegateExpressionExitNode(owner: ControlFlowGraph, override val fir: FirExpression, level: Int) class DelegateExpressionExitNode(owner: ControlFlowGraph, override val fir: FirExpression, level: Int)
: CFGNode<FirExpression>(owner, level), UnionNodeMarker { : CFGNode<FirExpression>(owner, level) {
override val isUnion: Boolean get() = true
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R { override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitDelegateExpressionExitNode(this, data) return visitor.visitDelegateExpressionExitNode(this, data)
} }
@@ -610,10 +617,13 @@ class ConstExpressionNode(owner: ControlFlowGraph, override val fir: FirConstExp
// ----------------------------------- Check not null call ----------------------------------- // ----------------------------------- Check not null call -----------------------------------
class CheckNotNullCallNode(owner: ControlFlowGraph, override val fir: FirCheckNotNullCall, level: Int) class CheckNotNullCallNode(owner: ControlFlowGraph, override val fir: FirCheckNotNullCall, level: Int)
: CFGNode<FirCheckNotNullCall>(owner, level), UnionNodeMarker { : CFGNode<FirCheckNotNullCall>(owner, level) {
override val canThrow: Boolean override val canThrow: Boolean
get() = true get() = true
override val isUnion: Boolean
get() = true
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R { override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitCheckNotNullCallNode(this, data) return visitor.visitCheckNotNullCallNode(this, data)
} }
@@ -645,10 +655,14 @@ class ResolvedQualifierNode(
} }
class FunctionCallNode(owner: ControlFlowGraph, override val fir: FirFunctionCall, level: Int) class FunctionCallNode(owner: ControlFlowGraph, override val fir: FirFunctionCall, level: Int)
: CFGNode<FirFunctionCall>(owner, level), UnionNodeMarker { : CFGNode<FirFunctionCall>(owner, level) {
override val canThrow: Boolean override val canThrow: Boolean
get() = true get() = true
override val isUnion: Boolean
get() = true
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R { override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitFunctionCallNode(this, data) return visitor.visitFunctionCallNode(this, data)
} }
@@ -671,7 +685,11 @@ class GetClassCallNode(owner: ControlFlowGraph, override val fir: FirGetClassCal
} }
class DelegatedConstructorCallNode(owner: ControlFlowGraph, override val fir: FirDelegatedConstructorCall, level: Int) class DelegatedConstructorCallNode(owner: ControlFlowGraph, override val fir: FirDelegatedConstructorCall, level: Int)
: CFGNode<FirDelegatedConstructorCall>(owner, level), UnionNodeMarker { : CFGNode<FirDelegatedConstructorCall>(owner, level) {
override val isUnion: Boolean
get() = true
override val canThrow: Boolean override val canThrow: Boolean
get() = true // shouldn't matter since delegated constructor calls aren't wrapped in try-finally, but still get() = true // shouldn't matter since delegated constructor calls aren't wrapped in try-finally, but still
@@ -681,7 +699,11 @@ class DelegatedConstructorCallNode(owner: ControlFlowGraph, override val fir: Fi
} }
class StringConcatenationCallNode(owner: ControlFlowGraph, override val fir: FirStringConcatenationCall, level: Int) class StringConcatenationCallNode(owner: ControlFlowGraph, override val fir: FirStringConcatenationCall, level: Int)
: CFGNode<FirStringConcatenationCall>(owner, level), UnionNodeMarker { : CFGNode<FirStringConcatenationCall>(owner, level) {
override val isUnion: Boolean
get() = true
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R { override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitStringConcatenationCallNode(this, data) return visitor.visitStringConcatenationCallNode(this, data)
} }
@@ -804,4 +826,4 @@ class SmartCastExpressionExitNode(owner: ControlFlowGraph, override val fir: Fir
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R { override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitSmartCastExpressionExitNode(this, data) return visitor.visitSmartCastExpressionExitNode(this, data)
} }
} }
@@ -8,8 +8,6 @@ package org.jetbrains.kotlin.fir.resolve.dfa.cfg
abstract class ControlFlowGraphVisitor<out R, in D> { abstract class ControlFlowGraphVisitor<out R, in D> {
abstract fun visitNode(node: CFGNode<*>, data: D): R abstract fun visitNode(node: CFGNode<*>, data: D): R
abstract fun <T> visitUnionNode(node: T, data: D): R where T : CFGNode<*>, T : UnionNodeMarker
// ----------------------------------- Simple function ----------------------------------- // ----------------------------------- Simple function -----------------------------------
open fun visitFunctionEnterNode(node: FunctionEnterNode, data: D): R { open fun visitFunctionEnterNode(node: FunctionEnterNode, data: D): R {
@@ -103,7 +101,7 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
} }
open fun visitDelegateExpressionExitNode(node: DelegateExpressionExitNode, data: D): R { open fun visitDelegateExpressionExitNode(node: DelegateExpressionExitNode, data: D): R {
return visitUnionNode(node, data) return visitNode(node, data)
} }
// ----------------------------------- Field ----------------------------------- // ----------------------------------- Field -----------------------------------
@@ -296,7 +294,7 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
// ----------------------------------- Check not null call ----------------------------------- // ----------------------------------- Check not null call -----------------------------------
open fun visitCheckNotNullCallNode(node: CheckNotNullCallNode, data: D): R { open fun visitCheckNotNullCallNode(node: CheckNotNullCallNode, data: D): R {
return visitUnionNode(node, data) return visitNode(node, data)
} }
// ----------------------------------- Resolvable call ----------------------------------- // ----------------------------------- Resolvable call -----------------------------------
@@ -310,7 +308,7 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
} }
open fun visitFunctionCallNode(node: FunctionCallNode, data: D): R { open fun visitFunctionCallNode(node: FunctionCallNode, data: D): R {
return visitUnionNode(node, data) return visitNode(node, data)
} }
open fun visitCallableReferenceNode(node: CallableReferenceNode, data: D): R { open fun visitCallableReferenceNode(node: CallableReferenceNode, data: D): R {
@@ -322,11 +320,11 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
} }
open fun visitDelegatedConstructorCallNode(node: DelegatedConstructorCallNode, data: D): R { open fun visitDelegatedConstructorCallNode(node: DelegatedConstructorCallNode, data: D): R {
return visitUnionNode(node, data) return visitNode(node, data)
} }
open fun visitStringConcatenationCallNode(node: StringConcatenationCallNode, data: D): R { open fun visitStringConcatenationCallNode(node: StringConcatenationCallNode, data: D): R {
return visitUnionNode(node, data) return visitNode(node, data)
} }
open fun visitThrowExceptionNode(node: ThrowExceptionNode, data: D): R { open fun visitThrowExceptionNode(node: ThrowExceptionNode, data: D): R {
@@ -8,8 +8,6 @@ package org.jetbrains.kotlin.fir.resolve.dfa.cfg
abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothing?>() { abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothing?>() {
abstract fun visitNode(node: CFGNode<*>) abstract fun visitNode(node: CFGNode<*>)
abstract fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker
// ----------------------------------- Simple function ----------------------------------- // ----------------------------------- Simple function -----------------------------------
open fun visitFunctionEnterNode(node: FunctionEnterNode) { open fun visitFunctionEnterNode(node: FunctionEnterNode) {
@@ -53,7 +51,7 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
} }
open fun visitDelegateExpressionExitNode(node: DelegateExpressionExitNode) { open fun visitDelegateExpressionExitNode(node: DelegateExpressionExitNode) {
visitUnionNode(node) visitNode(node)
} }
// ----------------------------------- Init ----------------------------------- // ----------------------------------- Init -----------------------------------
@@ -236,7 +234,7 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
// ----------------------------------- Check not null call ----------------------------------- // ----------------------------------- Check not null call -----------------------------------
open fun visitCheckNotNullCallNode(node: CheckNotNullCallNode) { open fun visitCheckNotNullCallNode(node: CheckNotNullCallNode) {
visitUnionNode(node) visitNode(node)
} }
// ----------------------------------- Resolvable call ----------------------------------- // ----------------------------------- Resolvable call -----------------------------------
@@ -250,15 +248,15 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
} }
open fun visitFunctionCallNode(node: FunctionCallNode) { open fun visitFunctionCallNode(node: FunctionCallNode) {
visitUnionNode(node) visitNode(node)
} }
open fun visitDelegatedConstructorCallNode(node: DelegatedConstructorCallNode) { open fun visitDelegatedConstructorCallNode(node: DelegatedConstructorCallNode) {
visitUnionNode(node) visitNode(node)
} }
open fun visitStringConcatenationCallNode(node: StringConcatenationCallNode) { open fun visitStringConcatenationCallNode(node: StringConcatenationCallNode) {
visitUnionNode(node) visitNode(node)
} }
open fun visitThrowExceptionNode(node: ThrowExceptionNode) { open fun visitThrowExceptionNode(node: ThrowExceptionNode) {
@@ -291,10 +289,6 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
visitNode(node) visitNode(node)
} }
final override fun <T> visitUnionNode(node: T, data: Nothing?) where T : CFGNode<*>, T : UnionNodeMarker {
visitUnionNode(node)
}
// ----------------------------------- Simple function ----------------------------------- // ----------------------------------- Simple function -----------------------------------
final override fun visitFunctionEnterNode(node: FunctionEnterNode, data: Nothing?) { final override fun visitFunctionEnterNode(node: FunctionEnterNode, data: Nothing?) {