diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/CfgUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/CfgUtils.kt index 4bb81e29793..7f5b21df662 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/CfgUtils.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/CfgUtils.kt @@ -19,11 +19,6 @@ fun ControlFlowGraph.getNodesInOrder(direction: TraverseDirection): List nodes.asReversed() } -fun CFGNode<*>.isEnterNode(direction: TraverseDirection): Boolean = when (direction) { - TraverseDirection.Forward -> owner.enterNode == this - TraverseDirection.Backward -> owner.exitNode == this -} - val CFGNode<*>.previousCfgNodes: List> get() = previousNodes.filter { val kind = edgeFrom(it).kind diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index b2269cc499b..d86d0313c2c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -231,12 +231,13 @@ abstract class FirDataFlowAnalyzer( // ----------------------------------- Scripts ------------------------------------------ fun enterScript(script: FirScript) { - val res = graphBuilder.enterScript(script) - res.mergeIncomingFlow() + graphBuilder.enterScript(script).mergeIncomingFlow() } - fun exitScript(script: FirScript) { - graphBuilder.exitScript(script) + fun exitScript(): ControlFlowGraph { + val (node, graph) = graphBuilder.exitScript() + node.mergeIncomingFlow() + return graph } // ----------------------------------- Value parameters (and it's defaults) ----------------------------------- diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index 2d9f37689c7..d5347cbd68e 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -113,13 +113,24 @@ class ControlFlowGraphBuilder { // ----------------------------------- Utils ----------------------------------- - private fun pushGraph(graph: ControlFlowGraph) { - graphs.push(graph) + private inline fun enterGraph( + fir: E, + name: String, + kind: ControlFlowGraph.Kind, + nodes: (E) -> Pair + ): Pair where EnterNode : CFGNode, EnterNode : GraphEnterNodeMarker, ExitNode : CFGNode, ExitNode : GraphExitNodeMarker { + graphs.push(ControlFlowGraph(fir as? FirDeclaration, name, kind)) levelCounter++ + return nodes(fir).also { (enterNode, exitNode) -> + currentGraph.enterNode = enterNode + currentGraph.exitNode = exitNode + lastNodes.push(enterNode) + } } private fun popGraph(): ControlFlowGraph { levelCounter-- + // TODO: count nodes per graph, validate the list after sorting return graphs.pop().also { it.complete() } } @@ -137,24 +148,16 @@ class ControlFlowGraphBuilder { val localFunctionNode = runIf(function.symbol.callableId.isLocal && currentGraph.kind.withBody) { createLocalFunctionDeclarationNode(function).also { addNewSimpleNode(it) } } - - pushGraph(ControlFlowGraph(function, name, ControlFlowGraph.Kind.Function)) - - val enterNode = createFunctionEnterNode(function).also { - lastNodes.push(it) + val (enterNode, exitNode) = enterGraph(function, name, ControlFlowGraph.Kind.Function) { + createFunctionEnterNode(it) to createFunctionExitNode(it) } - if (localFunctionNode != null) { addEdge(localFunctionNode, enterNode) } else { enterToLocalClassesMembers.remove(function.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) } } - - createFunctionExitNode(function).also { - exitTargetsForReturn.push(it) - exitTargetsForTry.push(it) - } - + exitTargetsForReturn.push(exitNode) + exitTargetsForTry.push(exitNode) return Pair(localFunctionNode, enterNode) } @@ -230,15 +233,14 @@ class ControlFlowGraphBuilder { fun enterAnonymousFunction(anonymousFunction: FirAnonymousFunction): FunctionEnterNode { val symbol = anonymousFunction.symbol val flowSourceNode = postponedAnonymousFunctionNodes.getValue(symbol).first - pushGraph(ControlFlowGraph(anonymousFunction, "", ControlFlowGraph.Kind.AnonymousFunction)) - val enterNode = createFunctionEnterNode(anonymousFunction) - val exitNode = createFunctionExitNode(anonymousFunction) + val (enterNode, exitNode) = enterGraph(anonymousFunction, "", ControlFlowGraph.Kind.AnonymousFunction) { + createFunctionEnterNode(it) to createFunctionExitNode(it) + } exitTargetsForReturn.push(exitNode) if (!anonymousFunction.invocationKind.isInPlace) { exitTargetsForTry.push(exitNode) } addEdge(flowSourceNode, enterNode) - lastNodes.push(enterNode) return enterNode } @@ -364,11 +366,12 @@ class ControlFlowGraphBuilder { fun enterClass(klass: FirClass, buildGraph: Boolean): Pair?, ClassEnterNode>? { if (!buildGraph) { - pushGraph(ControlFlowGraph(null, "STUB_CLASS_GRAPH", ControlFlowGraph.Kind.Stub)) + graphs.push(ControlFlowGraph(null, "", ControlFlowGraph.Kind.ClassInitializer)) + levelCounter++ return null } - val enterNode = when { + val localClassEnterNode = when { // TODO: enum classes cannot be local so this is mostly fine, but it looks hacky. Maybe handle FirEnumEntry? klass is FirAnonymousObject && klass.classKind != ClassKind.ENUM_ENTRY -> createAnonymousObjectEnterNode(klass) // Local classes are only initialized on first use, so they look pretty much like named functions: @@ -382,33 +385,34 @@ class ControlFlowGraphBuilder { is FirRegularClass -> klass.name.asString() else -> throw IllegalArgumentException("Unknown class kind: ${klass::class}") } - pushGraph(ControlFlowGraph(klass, name, ControlFlowGraph.Kind.ClassInitializer)) - val graphEnterNode = createClassEnterNode(klass) - lastNodes.push(graphEnterNode) - if (enterNode != null) { - addEdge(enterNode, graphEnterNode) + val (enterNode, _) = enterGraph(klass, name, ControlFlowGraph.Kind.ClassInitializer) { + createClassEnterNode(it) to createClassExitNode(it) + } + if (localClassEnterNode != null) { + addEdge(localClassEnterNode, enterNode) } else { - enterToLocalClassesMembers.remove(klass.symbol)?.let { addEdge(it, graphEnterNode, preferredKind = EdgeKind.DfgForward) } + enterToLocalClassesMembers.remove(klass.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) } } - if (graphEnterNode.previousNodes.isNotEmpty()) { + if (enterNode.previousNodes.isNotEmpty()) { for (member in klass.declarations) { if (member is FirFunction || member is FirAnonymousInitializer || member is FirField || member is FirClass || member is FirProperty) { - enterToLocalClassesMembers[member.symbol] = graphEnterNode + enterToLocalClassesMembers[member.symbol] = enterNode } if (member is FirProperty) { - member.getter?.let { enterToLocalClassesMembers[it.symbol] = graphEnterNode } - member.setter?.let { enterToLocalClassesMembers[it.symbol] = graphEnterNode } + member.getter?.let { enterToLocalClassesMembers[it.symbol] = enterNode } + member.setter?.let { enterToLocalClassesMembers[it.symbol] = enterNode } } } } - return enterNode to graphEnterNode + return localClassEnterNode to enterNode } fun exitClass(): Pair? { - if (currentGraph.kind == ControlFlowGraph.Kind.Stub) { - popGraph() + if (currentGraph.declaration == null) { + levelCounter-- + graphs.pop().also { assert(it.kind == ControlFlowGraph.Kind.ClassInitializer) } return null } @@ -468,7 +472,7 @@ class ControlFlowGraphBuilder { prevInitPartNode = partNode } - val exitNode = createClassExitNode(klass) + val exitNode = currentGraph.exitNode as ClassExitNode addEdge(node, exitNode, preferredKind = EdgeKind.CfgForward) if (prevInitPartNode != null) { addEdge(prevInitPartNode, exitNode, preferredKind = EdgeKind.DeadForward, propagateDeadness = false) @@ -500,16 +504,19 @@ class ControlFlowGraphBuilder { } fun enterScript(script: FirScript): ScriptEnterNode { - pushGraph(ControlFlowGraph(null, "SCRIPT_GRAPH", ControlFlowGraph.Kind.Function)) - val enterNode = createScriptEnterNode(script) - lastNodes.push(enterNode) + val (enterNode, exitNode) = enterGraph(script, "SCRIPT_GRAPH", ControlFlowGraph.Kind.Function) { + createScriptEnterNode(it) to createScriptExitNode(it) + } + exitTargetsForTry.push(exitNode) return enterNode } - fun exitScript(script: FirScript): ScriptExitNode { - return createScriptExitNode(script).also { - addNewSimpleNodeIfPossible(it) - } + fun exitScript(): Pair { + val exitNode = exitTargetsForTry.pop() as ScriptExitNode + addNewSimpleNode(exitNode) + val graph = popGraph() + assert(graph.exitNode == exitNode) + return exitNode to graph } // ----------------------------------- Value parameters (and it's defaults) ----------------------------------- @@ -518,14 +525,14 @@ class ControlFlowGraphBuilder { if (valueParameter.defaultValue == null) return null val outerEnterNode = createEnterValueParameterNode(valueParameter) - pushGraph(ControlFlowGraph(valueParameter, "default value of ${valueParameter.name}", ControlFlowGraph.Kind.DefaultArgument)) - val innerExitNode = createExitDefaultArgumentsNode(valueParameter) - val innerEnterNode = createEnterDefaultArgumentsNode(valueParameter) addNewSimpleNode(outerEnterNode) - addEdge(outerEnterNode, innerEnterNode) - lastNodes.push(innerEnterNode) - exitTargetsForTry.push(innerExitNode) - return outerEnterNode to innerEnterNode + val name = "default value of ${valueParameter.name}" + val (enterNode, exitNode) = enterGraph(valueParameter, name, ControlFlowGraph.Kind.DefaultArgument) { + createEnterDefaultArgumentsNode(it) to createExitDefaultArgumentsNode(it) + } + addEdge(outerEnterNode, enterNode) + exitTargetsForTry.push(exitNode) + return outerEnterNode to enterNode } fun exitValueParameter(valueParameter: FirValueParameter): Triple? { @@ -563,13 +570,11 @@ class ControlFlowGraphBuilder { fun enterProperty(property: FirProperty): PropertyInitializerEnterNode? { if (!property.hasInitialization) return null - pushGraph(ControlFlowGraph(property, "val ${property.name}", ControlFlowGraph.Kind.PropertyInitializer)) - - val enterNode = createPropertyInitializerEnterNode(property) - val exitNode = createPropertyInitializerExitNode(property) + val (enterNode, exitNode) = enterGraph(property, "val ${property.name}", ControlFlowGraph.Kind.PropertyInitializer) { + createPropertyInitializerEnterNode(it) to createPropertyInitializerExitNode(it) + } exitTargetsForTry.push(exitNode) enterToLocalClassesMembers.remove(property.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) } - lastNodes.push(enterNode) return enterNode } @@ -587,13 +592,11 @@ class ControlFlowGraphBuilder { fun enterField(field: FirField): FieldInitializerEnterNode? { if (field.initializer == null) return null - pushGraph(ControlFlowGraph(field, "val ${field.name}", ControlFlowGraph.Kind.FieldInitializer)) - - val enterNode = createFieldInitializerEnterNode(field) - val exitNode = createFieldInitializerExitNode(field) + val (enterNode, exitNode) = enterGraph(field, "val ${field.name}", ControlFlowGraph.Kind.FieldInitializer) { + createFieldInitializerEnterNode(it) to createFieldInitializerExitNode(it) + } exitTargetsForTry.push(exitNode) enterToLocalClassesMembers.remove(field.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) } - lastNodes.push(enterNode) return enterNode } @@ -1192,7 +1195,8 @@ class ControlFlowGraphBuilder { // Things like annotations and `contract { ... }` use normal call resolution, but aren't real expressions // and are never evaluated. We'll push all nodes created in the process into a stub graph, then throw it away. // TODO: don't waste time creating the nodes in the first place - pushGraph(ControlFlowGraph(null, "STUB_GRAPH_FOR_FAKE_EXPRESSION", ControlFlowGraph.Kind.FakeCall)) + graphs.push(ControlFlowGraph(null, "", ControlFlowGraph.Kind.FakeCall)) + levelCounter++ return createFakeExpressionEnterNode().also { lastNodes.push(it) exitTargetsForTry.push(it) // technically might create CFG loops, but the graph will never be visited anyway... @@ -1200,9 +1204,10 @@ class ControlFlowGraphBuilder { } fun exitFakeExpression() { + levelCounter-- lastNodes.pop() exitTargetsForTry.pop() - popGraph() + graphs.pop().also { assert(it.kind == ControlFlowGraph.Kind.FakeCall) } } // ----------------------------------- Callable references ----------------------------------- @@ -1219,13 +1224,11 @@ class ControlFlowGraphBuilder { fun enterInitBlock(initBlock: FirAnonymousInitializer): InitBlockEnterNode { // TODO: questionable moment that we should pass data flow from init to init - pushGraph(ControlFlowGraph(initBlock, "init block", ControlFlowGraph.Kind.Function)) - - val enterNode = createInitBlockEnterNode(initBlock) - val exitNode = createInitBlockExitNode(initBlock) + val (enterNode, exitNode) = enterGraph(initBlock, "init block", ControlFlowGraph.Kind.Function) { + createInitBlockEnterNode(it) to createInitBlockExitNode(it) + } exitTargetsForTry.push(exitNode) enterToLocalClassesMembers.remove(initBlock.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) } - lastNodes.push(enterNode) return enterNode } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt index be7e11d0c1d..72376469a04 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt @@ -67,17 +67,11 @@ fun ControlFlowGraphBuilder.createFieldInitializerExitNode(fir: FirField): Field fun ControlFlowGraphBuilder.createFieldInitializerEnterNode(fir: FirField): FieldInitializerEnterNode = FieldInitializerEnterNode(currentGraph, fir, levelCounter, createId()) -@OptIn(CfgInternals::class) fun ControlFlowGraphBuilder.createFunctionEnterNode(fir: FirFunction): FunctionEnterNode = - FunctionEnterNode(currentGraph, fir, levelCounter, createId()).also { - currentGraph.enterNode = it - } + FunctionEnterNode(currentGraph, fir, levelCounter, createId()) -@OptIn(CfgInternals::class) fun ControlFlowGraphBuilder.createFunctionExitNode(fir: FirFunction): FunctionExitNode = - FunctionExitNode(currentGraph, fir, levelCounter, createId()).also { - currentGraph.exitNode = it - } + FunctionExitNode(currentGraph, fir, levelCounter, createId()) fun ControlFlowGraphBuilder.createLocalFunctionDeclarationNode(fir: FirFunction): LocalFunctionDeclarationNode = LocalFunctionDeclarationNode(currentGraph, fir, levelCounter, createId()) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index 9c20c9fe65c..73f72ce0c5f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -513,9 +513,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve val result = context.withScopesForScript(script, components) { transformDeclarationContent(script, data) as FirScript } - - dataFlowAnalyzer.exitScript(script) - + dataFlowAnalyzer.exitScript() // TODO: FirScript should be a FirControlFlowGraphOwner return result } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt index 444ff5554bf..fa95006df0a 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt @@ -65,11 +65,6 @@ sealed class CFGNode(val owner: ControlFlowGraph, val level: } } - init { - @Suppress("LeakingThis") - owner.addNode(this) - } - private val _previousNodes: MutableList> = SmartList() private val _followingNodes: MutableList> = SmartList() @@ -132,6 +127,8 @@ val CFGNode<*>.lastPreviousNode: CFGNode<*> get() = previousNodes.last() interface EnterNodeMarker interface ExitNodeMarker +interface GraphEnterNodeMarker : EnterNodeMarker +interface GraphExitNodeMarker : ExitNodeMarker // a ---> b ---> d // \-> c -/ @@ -153,24 +150,25 @@ sealed class CFGNodeWithCfgOwner(owner: Contro // ----------------------------------- Named function ----------------------------------- class FunctionEnterNode(owner: ControlFlowGraph, override val fir: FirFunction, level: Int, id: Int) : CFGNode(owner, level, id), - EnterNodeMarker { + GraphEnterNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitFunctionEnterNode(this, data) } } + class FunctionExitNode(owner: ControlFlowGraph, override val fir: FirFunction, level: Int, id: Int) : CFGNode(owner, level, id), - ExitNodeMarker { + GraphExitNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitFunctionExitNode(this, data) } } + class LocalFunctionDeclarationNode(owner: ControlFlowGraph, override val fir: FirFunction, level: Int, id: Int) : CFGNodeWithCfgOwner(owner, level, id) { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitLocalFunctionDeclarationNode(this, data) } } - // ----------------------------------- Default arguments ----------------------------------- class EnterValueParameterNode(owner: ControlFlowGraph, override val fir: FirValueParameter, level: Int, id: Int) : CFGNodeWithCfgOwner(owner, level, id) { @@ -179,25 +177,15 @@ class EnterValueParameterNode(owner: ControlFlowGraph, override val fir: FirValu } } -@OptIn(CfgInternals::class) class EnterDefaultArgumentsNode(owner: ControlFlowGraph, override val fir: FirValueParameter, level: Int, id: Int) : CFGNode(owner, level, id), - EnterNodeMarker { - init { - owner.enterNode = this - } - + GraphEnterNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitEnterDefaultArgumentsNode(this, data) } } -@OptIn(CfgInternals::class) class ExitDefaultArgumentsNode(owner: ControlFlowGraph, override val fir: FirValueParameter, level: Int, id: Int) : CFGNode(owner, level, id), - ExitNodeMarker { - init { - owner.exitNode = this - } - + GraphExitNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitExitDefaultArgumentsNode(this, data) } @@ -245,24 +233,15 @@ class AnonymousFunctionExpressionNode(owner: ControlFlowGraph, override val fir: // ----------------------------------- Classes ----------------------------------- -@OptIn(CfgInternals::class) class ClassEnterNode(owner: ControlFlowGraph, override val fir: FirClass, level: Int, id: Int) : CFGNode(owner, level, id), - EnterNodeMarker { - init { - owner.enterNode = this - } - + GraphEnterNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitClassEnterNode(this, data) } } -@OptIn(CfgInternals::class) class ClassExitNode(owner: ControlFlowGraph, override val fir: FirClass, level: Int, id: Int) : CFGNodeWithSubgraphs(owner, level, id), - ExitNodeMarker { - init { - owner.exitNode = this - } + GraphExitNodeMarker { lateinit override var subGraphs: List @@ -297,13 +276,13 @@ class AnonymousObjectExpressionExitNode(owner: ControlFlowGraph, override val fi // ----------------------------------- Scripts ------------------------------------------ -class ScriptEnterNode(owner: ControlFlowGraph, override val fir: FirScript, level: Int, id: Int) : CFGNode(owner, level, id) { +class ScriptEnterNode(owner: ControlFlowGraph, override val fir: FirScript, level: Int, id: Int) : CFGNode(owner, level, id), GraphEnterNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitScriptEnterNode(this, data) } } -class ScriptExitNode(owner: ControlFlowGraph, override val fir: FirScript, level: Int, id: Int) : CFGNode(owner, level, id) { +class ScriptExitNode(owner: ControlFlowGraph, override val fir: FirScript, level: Int, id: Int) : CFGNode(owner, level, id), GraphExitNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitScriptExitNode(this, data) } @@ -319,25 +298,15 @@ class PartOfClassInitializationNode(owner: ControlFlowGraph, override val fir: F // ----------------------------------- Property ----------------------------------- -@OptIn(CfgInternals::class) class PropertyInitializerEnterNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode(owner, level, id), - EnterNodeMarker { - init { - owner.enterNode = this - } - + GraphEnterNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitPropertyInitializerEnterNode(this, data) } } -@OptIn(CfgInternals::class) class PropertyInitializerExitNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode(owner, level, id), - ExitNodeMarker { - init { - owner.exitNode = this - } - + GraphExitNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitPropertyInitializerExitNode(this, data) } @@ -353,25 +322,15 @@ class DelegateExpressionExitNode(owner: ControlFlowGraph, override val fir: FirE // ----------------------------------- Field ----------------------------------- -@OptIn(CfgInternals::class) class FieldInitializerEnterNode(owner: ControlFlowGraph, override val fir: FirField, level: Int, id: Int) : CFGNode(owner, level, id), - EnterNodeMarker { - init { - owner.enterNode = this - } - + GraphEnterNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitFieldInitializerEnterNode(this, data) } } -@OptIn(CfgInternals::class) class FieldInitializerExitNode(owner: ControlFlowGraph, override val fir: FirField, level: Int, id: Int) : CFGNode(owner, level, id), - ExitNodeMarker { - init { - owner.exitNode = this - } - + GraphExitNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitFieldInitializerExitNode(this, data) } @@ -379,25 +338,15 @@ class FieldInitializerExitNode(owner: ControlFlowGraph, override val fir: FirFie // ----------------------------------- Init ----------------------------------- -@OptIn(CfgInternals::class) class InitBlockEnterNode(owner: ControlFlowGraph, override val fir: FirAnonymousInitializer, level: Int, id: Int) : CFGNode(owner, level, id), - EnterNodeMarker { - init { - owner.enterNode = this - } - + GraphEnterNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitInitBlockEnterNode(this, data) } } -@OptIn(CfgInternals::class) class InitBlockExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousInitializer, level: Int, id: Int) : CFGNode(owner, level, id), - ExitNodeMarker { - init { - owner.exitNode = this - } - + GraphExitNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitInitBlockExitNode(this, data) } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index 90fe3f92bff..05b4d86b2d9 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -10,15 +10,8 @@ import org.jetbrains.kotlin.fir.expressions.FirLoop import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val kind: Kind) { - private var _nodes: MutableList> = mutableListOf() - - val nodes: List> - get() = _nodes - - internal fun addNode(node: CFGNode<*>) { - assertState(State.Building) - _nodes.add(node) - } + lateinit var nodes: List> + private set @set:CfgInternals lateinit var enterNode: CFGNode<*> @@ -29,36 +22,12 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k val isSubGraph: Boolean get() = enterNode.previousNodes.isNotEmpty() - var state: State = State.Building - private set - val subGraphs: List - get() = _nodes.flatMap { (it as? CFGNodeWithSubgraphs<*>)?.subGraphs ?: emptyList() } + get() = nodes.flatMap { (it as? CFGNodeWithSubgraphs<*>)?.subGraphs ?: emptyList() } @CfgInternals fun complete() { - assertState(State.Building) - state = State.Completed - if (kind == Kind.Stub) return - val sortedNodes = orderNodes() - // TODO Fix this -// assert(sortedNodes.size == _nodes.size) -// for (node in _nodes) { -// assert(node in sortedNodes) -// } - _nodes.clear() - _nodes.addAll(sortedNodes) - } - - private fun assertState(state: State) { - assert(this.state == state) { - "This action can not be performed at $this state" - } - } - - enum class State { - Building, - Completed; + nodes = orderNodes() } enum class Kind(val withBody: Boolean) { @@ -70,7 +39,6 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k TopLevel(withBody = false), FakeCall(withBody = true), DefaultArgument(withBody = true), - Stub(withBody = false) } } @@ -146,7 +114,7 @@ enum class EdgeKind( DeadBackward(usedInDfa = false, usedInDeadDfa = false, usedInCfa = true, isBack = true, isDead = true) } -private fun ControlFlowGraph.orderNodes(): LinkedHashSet> { +private fun ControlFlowGraph.orderNodes(): List> { val visitedNodes = linkedSetOf>() /* * [delayedNodes] is needed to accomplish next order contract: @@ -173,5 +141,5 @@ private fun ControlFlowGraph.orderNodes(): LinkedHashSet> { } } } - return visitedNodes + return visitedNodes.toList() } diff --git a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/fir/FirCfgConsistencyChecker.kt b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/fir/FirCfgConsistencyChecker.kt index 45b086aa2ad..645cd3e5e9f 100644 --- a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/fir/FirCfgConsistencyChecker.kt +++ b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/fir/FirCfgConsistencyChecker.kt @@ -9,6 +9,8 @@ import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.GraphEnterNodeMarker +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.GraphExitNodeMarker import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.test.Assertions @@ -19,7 +21,8 @@ class FirCfgConsistencyChecker(private val assertions: Assertions) : FirVisitorV override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference) { val graph = (controlFlowGraphReference as? FirControlFlowGraphReferenceImpl)?.controlFlowGraph ?: return - assertions.assertEquals(ControlFlowGraph.State.Completed, graph.state) + assertions.assertEquals(graph.nodes.single { it is GraphEnterNodeMarker }, graph.enterNode) + assertions.assertEquals(graph.nodes.single { it is GraphExitNodeMarker }, graph.exitNode) checkConsistency(graph) checkOrder(graph) }