diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/propertiesAndInitBlocks.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/propertiesAndInitBlocks.dot index 00f95891f71..6bec998d82e 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/propertiesAndInitBlocks.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/propertiesAndInitBlocks.dot @@ -135,149 +135,153 @@ digraph propertiesAndInitBlocks_kt { subgraph cluster_11 { color=red 42 [label="Enter function getter" style="filled" fillcolor=red]; - 43 [label="Exit function getter" style="filled" fillcolor=red]; + 43 [label="Exit local class "]; + 44 [label="Exit function getter" style="filled" fillcolor=red]; } 42 -> {43}; + 43 -> {44}; subgraph cluster_12 { color=red - 44 [label="Enter function " style="filled" fillcolor=red]; - 45 [label="Delegated constructor call: super()"]; - 46 [label="Exit function " style="filled" fillcolor=red]; + 45 [label="Enter function " style="filled" fillcolor=red]; + 46 [label="Delegated constructor call: super()"]; + 47 [label="Exit function " style="filled" fillcolor=red]; } - 44 -> {45}; 45 -> {46}; + 46 -> {47}; subgraph cluster_13 { color=red - 47 [label="Enter init block" style="filled" fillcolor=red]; + 48 [label="Enter init block" style="filled" fillcolor=red]; subgraph cluster_14 { color=blue - 48 [label="Enter block"]; - 49 [label="Function call: R|java/lang/Exception.Exception|()"]; - 50 [label="Throw: throw R|java/lang/Exception.Exception|()"]; - 51 [label="Stub" style="filled" fillcolor=gray]; - 52 [label="Exit block" style="filled" fillcolor=gray]; + 49 [label="Enter block"]; + 50 [label="Function call: R|java/lang/Exception.Exception|()"]; + 51 [label="Throw: throw R|java/lang/Exception.Exception|()"]; + 52 [label="Stub" style="filled" fillcolor=gray]; + 53 [label="Exit block" style="filled" fillcolor=gray]; } - 53 [label="Exit init block" style="filled" fillcolor=red]; + 54 [label="Exit init block" style="filled" fillcolor=red]; } - 47 -> {48}; 48 -> {49}; 49 -> {50}; - 50 -> {53}; - 50 -> {51} [style=dotted]; + 50 -> {51}; + 51 -> {54}; 51 -> {52} [style=dotted]; 52 -> {53} [style=dotted]; + 53 -> {54} [style=dotted]; subgraph cluster_15 { color=red - 54 [label="Enter class LocalClass" style="filled" fillcolor=red]; - 55 [label="Exit class LocalClass" style="filled" fillcolor=red]; + 55 [label="Enter class LocalClass" style="filled" fillcolor=red]; + 56 [label="Exit class LocalClass" style="filled" fillcolor=red]; } subgraph cluster_16 { color=red - 56 [label="Enter property" style="filled" fillcolor=red]; - 57 [label="Postponed enter to lambda"]; + 57 [label="Enter property" style="filled" fillcolor=red]; + 58 [label="Postponed enter to lambda"]; subgraph cluster_17 { color=blue - 58 [label="Enter function anonymousFunction"]; - 59 [label="Function call: R|java/lang/Exception.Exception|()"]; - 60 [label="Throw: throw R|java/lang/Exception.Exception|()"]; - 61 [label="Stub" style="filled" fillcolor=gray]; - 62 [label="Exit function anonymousFunction"]; + 59 [label="Enter function anonymousFunction"]; + 60 [label="Exit local class val x3"]; + 61 [label="Function call: R|java/lang/Exception.Exception|()"]; + 62 [label="Throw: throw R|java/lang/Exception.Exception|()"]; + 63 [label="Stub" style="filled" fillcolor=gray]; + 64 [label="Exit function anonymousFunction"]; } - 63 [label="Postponed exit from lambda"]; - 64 [label="Function call: R|/run|(...)"]; - 65 [label="Exit property" style="filled" fillcolor=red]; + 65 [label="Postponed exit from lambda"]; + 66 [label="Function call: R|/run|(...)"]; + 67 [label="Exit property" style="filled" fillcolor=red]; } - 56 -> {57}; 57 -> {58}; - 57 -> {63} [color=red]; - 58 -> {62 59}; - 59 -> {60}; - 60 -> {65}; - 60 -> {61} [style=dotted]; - 61 -> {62} [style=dotted]; - 62 -> {58}; - 62 -> {63} [color=green]; - 63 -> {64}; - 64 -> {65}; + 58 -> {59}; + 58 -> {65} [color=red]; + 59 -> {64 60}; + 60 -> {61}; + 61 -> {62}; + 62 -> {67}; + 62 -> {63} [style=dotted]; + 63 -> {64} [style=dotted]; + 64 -> {59}; + 64 -> {65} [color=green]; + 65 -> {66}; + 66 -> {67}; subgraph cluster_18 { color=red - 66 [label="Enter function getter" style="filled" fillcolor=red]; - 67 [label="Exit function getter" style="filled" fillcolor=red]; - } - - 66 -> {67}; - - subgraph cluster_19 { - color=red - 68 [label="Enter property" style="filled" fillcolor=red]; - subgraph cluster_20 { - color=blue - 69 [label="Try expression enter"]; - subgraph cluster_21 { - color=blue - 70 [label="Try main block enter"]; - subgraph cluster_22 { - color=blue - 71 [label="Enter block"]; - 72 [label="Const: Int(1)"]; - 73 [label="Exit block"]; - } - 74 [label="Try main block exit"]; - } - subgraph cluster_23 { - color=blue - 75 [label="Enter finally"]; - subgraph cluster_24 { - color=blue - 76 [label="Enter block"]; - 77 [label="Const: Int(0)"]; - 78 [label="Exit block"]; - } - 79 [label="Exit finally"]; - } - subgraph cluster_25 { - color=blue - 80 [label="Catch enter"]; - subgraph cluster_26 { - color=blue - 81 [label="Enter block"]; - 82 [label="Const: Int(2)"]; - 83 [label="Exit block"]; - } - 84 [label="Catch exit"]; - } - 85 [label="Try expression exit"]; - } - 86 [label="Exit property" style="filled" fillcolor=red]; + 68 [label="Enter function getter" style="filled" fillcolor=red]; + 69 [label="Exit function getter" style="filled" fillcolor=red]; } 68 -> {69}; - 69 -> {70}; - 70 -> {86 80 75 71}; + + subgraph cluster_19 { + color=red + 70 [label="Enter property" style="filled" fillcolor=red]; + subgraph cluster_20 { + color=blue + 71 [label="Try expression enter"]; + subgraph cluster_21 { + color=blue + 72 [label="Try main block enter"]; + subgraph cluster_22 { + color=blue + 73 [label="Enter block"]; + 74 [label="Const: Int(1)"]; + 75 [label="Exit block"]; + } + 76 [label="Try main block exit"]; + } + subgraph cluster_23 { + color=blue + 77 [label="Enter finally"]; + subgraph cluster_24 { + color=blue + 78 [label="Enter block"]; + 79 [label="Const: Int(0)"]; + 80 [label="Exit block"]; + } + 81 [label="Exit finally"]; + } + subgraph cluster_25 { + color=blue + 82 [label="Catch enter"]; + subgraph cluster_26 { + color=blue + 83 [label="Enter block"]; + 84 [label="Const: Int(2)"]; + 85 [label="Exit block"]; + } + 86 [label="Catch exit"]; + } + 87 [label="Try expression exit"]; + } + 88 [label="Exit property" style="filled" fillcolor=red]; + } + + 70 -> {71}; 71 -> {72}; - 72 -> {73}; + 72 -> {88 82 77 73}; 73 -> {74}; - 74 -> {85}; + 74 -> {75}; 75 -> {76}; - 76 -> {77}; + 76 -> {87}; 77 -> {78}; 78 -> {79}; - 79 -> {85}; - 80 -> {86 81}; - 81 -> {82}; - 82 -> {83}; + 79 -> {80}; + 80 -> {81}; + 81 -> {87}; + 82 -> {88 83}; 83 -> {84}; 84 -> {85}; 85 -> {86}; + 86 -> {87}; + 87 -> {88}; } 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 4fc1f81288d..2f436204351 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 @@ -126,13 +126,7 @@ abstract class FirDataFlowAnalyzer( fun enterFunction(function: FirFunction<*>) { val (functionEnterNode, previousNode) = graphBuilder.enterFunction(function) - if (previousNode == null) { - functionEnterNode.mergeIncomingFlow() - } else { - // Enter anonymous function - assert(functionEnterNode.previousNodes.isEmpty()) - functionEnterNode.flow = logicSystem.forkFlow(previousNode.flow) - } + functionEnterNode.mergeIncomingFlow(shouldForkFlow = previousNode != null) } fun exitFunction(function: FirFunction<*>): ControlFlowGraph? { @@ -961,15 +955,21 @@ abstract class FirDataFlowAnalyzer( private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) firstPreviousNode else this - private fun > T.mergeIncomingFlow(updateReceivers: Boolean = false): T = this.also { node -> + private fun > T.mergeIncomingFlow( + updateReceivers: Boolean = false, + shouldForkFlow: Boolean = false + ): T = this.also { node -> val previousFlows = if (node.isDead) node.previousNodes.map { it.flow } else node.previousNodes.mapNotNull { prev -> prev.takeIf { node.incomingEdges.getValue(it).usedInDfa }?.flow } - val flow = logicSystem.joinFlow(previousFlows) + var flow = logicSystem.joinFlow(previousFlows) if (updateReceivers) { logicSystem.updateAllReceivers(flow) } + if (shouldForkFlow) { + flow = flow.fork() + } node.flow = flow } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index e37bcd2f166..6c4884208f1 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -28,8 +28,22 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k lateinit var exitNode: CFGNode<*> internal set + var owner: ControlFlowGraph? = null + private set + + private val _subGraphs: MutableList = mutableListOf() + val subGraphs: List get() = _subGraphs + + internal fun addSubGraph(graph: ControlFlowGraph) { + assert(graph.owner == null) { + "SubGraph already has owner" + } + graph.owner = this + _subGraphs += graph + } + enum class Kind { - Function, ClassInitializer, PropertyInitializer, TopLevel + Function, ClassInitializer, TopLevel } } 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 a00cd882fa8..05d7a5f32c9 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 @@ -83,13 +83,13 @@ class ControlFlowGraphBuilder { val invocationKind = function.invocationKind val isInplace = invocationKind.isInplace() - val previousNode = if (!isInplace && graphs.topOrNull()?.let { it.kind != ControlFlowGraph.Kind.TopLevel } == true) { - entersToPostponedAnonymousFunctions[function.symbol] - ?: enterToLocalClassesMembers[function.symbol] - ?: lastNodes.top() - } else { - null - } + val previousNode = entersToPostponedAnonymousFunctions[function.symbol] + ?: enterToLocalClassesMembers[function.symbol] + ?: if (!isInplace && graphs.topOrNull()?.let { it.kind == ControlFlowGraph.Kind.Function } == true) { + lastNodes.top() + } else { + null + } if (!isInplace) { graphs.push(ControlFlowGraph(function, name, ControlFlowGraph.Kind.Function)) @@ -109,6 +109,10 @@ class ControlFlowGraphBuilder { lastNodes.push(it) } } + + if (previousNode != null && !isInplace) { + addEdge(previousNode, enterNode, preferredKind = EdgeKind.Dfg) + } val exitNode = createFunctionExitNode(function, isInplace) if (function is FirAnonymousFunction) { exitsOfAnonymousFunctions[function] = exitNode @@ -170,6 +174,12 @@ class ControlFlowGraphBuilder { } else { null } + if (graph != null) { + val previousGraph = graphs.top() + if (previousGraph.kind == ControlFlowGraph.Kind.Function) { + previousGraph.addSubGraph(graph) + } + } return exitNode to graph } @@ -210,10 +220,12 @@ class ControlFlowGraphBuilder { fun enterClass() { levelCounter++ + graphs.push(ControlFlowGraph(null, "STUB_CLASS_GRAPH", ControlFlowGraph.Kind.ClassInitializer)) } fun exitClass() { levelCounter-- + graphs.pop() } fun exitClass(klass: FirClass<*>): ControlFlowGraph { @@ -225,7 +237,8 @@ class ControlFlowGraphBuilder { } else -> throw IllegalArgumentException("Unknown class kind: ${klass::class}") } - graphs.push(ControlFlowGraph(klass, name, ControlFlowGraph.Kind.ClassInitializer)) + val classGraph = ControlFlowGraph(klass, name, ControlFlowGraph.Kind.ClassInitializer) + graphs.push(classGraph) var node: CFGNode<*> = createClassEnterNode(klass) for (declaration in klass.declarations) { val graph = when (declaration) { @@ -235,6 +248,7 @@ class ControlFlowGraphBuilder { } ?: continue addEdge(node, graph.enterNode, preferredKind = EdgeKind.Cfg) node = graph.exitNode + classGraph.addSubGraph(graph) } val exitNode = createClassExitNode(klass) addEdge(node, exitNode, preferredKind = EdgeKind.Cfg) @@ -314,7 +328,7 @@ class ControlFlowGraphBuilder { // ----------------------------------- Property ----------------------------------- fun enterProperty(property: FirProperty): PropertyInitializerEnterNode { - graphs.push(ControlFlowGraph(property, "val ${property.name}", ControlFlowGraph.Kind.PropertyInitializer)) + graphs.push(ControlFlowGraph(property, "val ${property.name}", ControlFlowGraph.Kind.Function)) val enterNode = createPropertyInitializerEnterNode(property) val exitNode = createPropertyInitializerExitNode(property) topLevelVariableInitializerExitNodes.push(exitNode) @@ -782,7 +796,7 @@ class ControlFlowGraphBuilder { fun enterAnnotationCall(annotationCall: FirAnnotationCall): AnnotationEnterNode { return createAnnotationEnterNode(annotationCall).also { - if (graphs.size > 1) { + if (graphs.top().kind == ControlFlowGraph.Kind.Function) { addNewSimpleNode(it) } else { lastNodes.push(it) 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 376ac663a4c..6f395d8170a 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 @@ -16,8 +16,10 @@ import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.buildReturnExpression import org.jetbrains.kotlin.fir.expressions.builder.buildUnitExpression import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.references.impl.FirEmptyControlFlowGraphReference import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate +import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl import org.jetbrains.kotlin.fir.resolve.inference.FirDelegatedPropertyInferenceSession import org.jetbrains.kotlin.fir.resolve.inference.extractLambdaInfoFromFunctionalType import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor @@ -324,7 +326,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer) var result = withLabelAndReceiverType(null, anonymousObject, type) { transformDeclarationContent(anonymousObject, data).single as FirAnonymousObject } - if (!implicitTypeOnly) { + if (!implicitTypeOnly && result.controlFlowGraphReference == FirEmptyControlFlowGraphReference) { val graph = dataFlowAnalyzer.exitAnonymousObject(result) result = result.transformControlFlowGraphReference(ControlFlowGraphReferenceTransformer, graph) } else {