FIR CFA: compute subgraph relationships automatically

No more `addSubGraph`. Also no more `owner` in graphs.

^KT-40526 Obsolete
^KT-40582 Obsolete
This commit is contained in:
pyos
2022-12-09 14:11:12 +01:00
committed by Dmitriy Novozhilov
parent 7ff5ad1ad0
commit 4c6eff9174
9 changed files with 97 additions and 141 deletions
@@ -67,10 +67,10 @@ digraph defaultArguments_kt {
}
7 -> {8};
8 -> {9 16};
8 -> {16 16} [style=dashed];
8 -> {16} [style=dashed];
9 -> {10};
10 -> {11 20};
10 -> {20 20} [style=dashed];
10 -> {20} [style=dashed];
11 -> {12};
12 -> {13};
13 -> {14};
@@ -426,14 +426,14 @@ digraph flowFromInplaceLambda2_kt {
153 [label="Postponed enter to lambda"];
subgraph cluster_40 {
color=blue
187 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
180 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
subgraph cluster_41 {
color=blue
188 [label="Enter block"];
189 [label="Function call: R|/n|<R|kotlin/Nothing?|>()" style="filled" fillcolor=yellow];
190 [label="Exit block"];
181 [label="Enter block"];
182 [label="Function call: R|/n|<R|kotlin/Nothing?|>()" style="filled" fillcolor=yellow];
183 [label="Exit block"];
}
191 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
184 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
}
154 [label="Postponed exit from lambda"];
155 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)" style="filled" fillcolor=yellow];
@@ -447,16 +447,16 @@ digraph flowFromInplaceLambda2_kt {
160 [label="Postponed enter to lambda"];
subgraph cluster_43 {
color=blue
180 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
185 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
subgraph cluster_44 {
color=blue
181 [label="Enter block"];
182 [label="Const: Null(null)"];
183 [label="Assignment: R|<local>/p|"];
184 [label="Function call: R|/n|<R|kotlin/Nothing?|>()" style="filled" fillcolor=yellow];
185 [label="Exit block"];
186 [label="Enter block"];
187 [label="Const: Null(null)"];
188 [label="Assignment: R|<local>/p|"];
189 [label="Function call: R|/n|<R|kotlin/Nothing?|>()" style="filled" fillcolor=yellow];
190 [label="Exit block"];
}
186 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
191 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
}
161 [label="Postponed exit from lambda"];
162 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)" style="filled" fillcolor=yellow];
@@ -518,9 +518,9 @@ digraph flowFromInplaceLambda2_kt {
150 -> {151};
151 -> {152};
152 -> {153};
153 -> {155 187};
153 -> {155 180};
153 -> {154} [style=dotted];
153 -> {187} [style=dashed];
153 -> {180} [style=dashed];
154 -> {155} [color=green];
154 -> {171} [color=red];
155 -> {156};
@@ -528,9 +528,9 @@ digraph flowFromInplaceLambda2_kt {
157 -> {165};
158 -> {159};
159 -> {160};
160 -> {162 180};
160 -> {162 185};
160 -> {161} [style=dotted];
160 -> {180} [style=dashed];
160 -> {185} [style=dashed];
161 -> {162} [color=green];
161 -> {171} [color=red];
162 -> {163};
@@ -557,14 +557,14 @@ digraph flowFromInplaceLambda2_kt {
181 -> {182};
182 -> {183};
183 -> {184};
184 -> {185};
184 -> {154};
185 -> {186};
186 -> {161};
186 -> {187};
187 -> {188};
188 -> {189};
189 -> {190};
190 -> {191};
191 -> {154};
191 -> {161};
192 -> {193};
193 -> {194};
194 -> {195};
@@ -685,16 +685,16 @@ digraph flowFromInplaceLambda2_kt {
240 [label="Postponed enter to lambda"];
subgraph cluster_59 {
color=blue
271 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
264 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
subgraph cluster_60 {
color=blue
272 [label="Enter block"];
273 [label="Access variable R|<local>/x|"];
274 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
275 [label="Function call: R|/n|<R|kotlin/Nothing?|>()" style="filled" fillcolor=yellow];
276 [label="Exit block"];
265 [label="Enter block"];
266 [label="Access variable R|<local>/x|"];
267 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
268 [label="Function call: R|/n|<R|kotlin/Nothing?|>()" style="filled" fillcolor=yellow];
269 [label="Exit block"];
}
277 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
270 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
}
241 [label="Postponed exit from lambda"];
242 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)" style="filled" fillcolor=yellow];
@@ -708,16 +708,16 @@ digraph flowFromInplaceLambda2_kt {
247 [label="Postponed enter to lambda"];
subgraph cluster_62 {
color=blue
264 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
271 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
subgraph cluster_63 {
color=blue
265 [label="Enter block"];
266 [label="Access variable R|<local>/x|"];
267 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
268 [label="Function call: R|/n|<R|kotlin/Nothing?|>()" style="filled" fillcolor=yellow];
269 [label="Exit block"];
272 [label="Enter block"];
273 [label="Access variable R|<local>/x|"];
274 [label="Type operator: (R|<local>/x| as R|kotlin/String|)"];
275 [label="Function call: R|/n|<R|kotlin/Nothing?|>()" style="filled" fillcolor=yellow];
276 [label="Exit block"];
}
270 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
277 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
}
248 [label="Postponed exit from lambda"];
249 [label="Function call: R|kotlin/run|<R|kotlin/Nothing?|>(...)" style="filled" fillcolor=yellow];
@@ -762,9 +762,9 @@ digraph flowFromInplaceLambda2_kt {
237 -> {238};
238 -> {239};
239 -> {240};
240 -> {242 271};
240 -> {242 264};
240 -> {241} [style=dotted];
240 -> {271} [style=dashed];
240 -> {264} [style=dashed];
241 -> {242} [color=green];
241 -> {258} [color=red];
242 -> {243};
@@ -772,9 +772,9 @@ digraph flowFromInplaceLambda2_kt {
244 -> {252};
245 -> {246};
246 -> {247};
247 -> {249 264};
247 -> {249 271};
247 -> {248} [style=dotted];
247 -> {264} [style=dashed];
247 -> {271} [style=dashed];
248 -> {249} [color=green];
248 -> {258} [color=red];
249 -> {250};
@@ -800,14 +800,14 @@ digraph flowFromInplaceLambda2_kt {
267 -> {268};
268 -> {269};
269 -> {270};
270 -> {248};
270 -> {241};
271 -> {272};
272 -> {273};
273 -> {274};
274 -> {275};
275 -> {276};
276 -> {277};
277 -> {241};
277 -> {248};
278 -> {279};
279 -> {280};
280 -> {281};
@@ -70,7 +70,7 @@ digraph lambdaInWhenBranch_kt {
11 -> {12};
13 -> {14};
14 -> {15 17};
14 -> {17 17} [style=dashed];
14 -> {17} [style=dashed];
15 -> {16};
17 -> {18};
18 -> {19};
@@ -25,7 +25,7 @@ class ControlFlowAnalysisDiagnosticComponent(
private fun analyze(declaration: FirControlFlowGraphOwner, context: CheckerContext) {
val graph = declaration.controlFlowGraphReference?.controlFlowGraph ?: return
if (graph.owner != null) return
if (graph.isSubGraph) return
cfaCheckers.forEach { it.analyze(graph, reporter, context) }
val (properties, capturedWrites) = LocalPropertyAndCapturedWriteCollector.collect(graph)
@@ -50,7 +50,7 @@ class ControlFlowGraphBuilder {
private val argumentListSplitNodes: Stack<SplitPostponedLambdasNode?> = stackOf()
private val postponedAnonymousFunctionNodes =
mutableMapOf<FirFunctionSymbol<*>, Pair<CFGNodeWithSubgraphs<*>, PostponedLambdaExitNode?>>()
mutableMapOf<FirFunctionSymbol<*>, Pair<CFGNode<*>, PostponedLambdaExitNode?>>()
private val postponedLambdaExits: Stack<MutableList<Pair<CFGNode<*>, EdgeKind>>> = stackOf()
private val loopConditionEnterNodes: NodeStorage<FirElement, LoopConditionEnterNode> = NodeStorage()
@@ -134,16 +134,12 @@ class ControlFlowGraphBuilder {
is FirConstructor -> "<init>"
else -> throw IllegalArgumentException("Unknown function: ${function.render()}")
}
val graph = ControlFlowGraph(function, name, ControlFlowGraph.Kind.Function)
// function is local
val localFunctionNode = runIf(function.symbol.callableId.isLocal && currentGraph.kind.withBody) {
currentGraph.addSubGraph(graph)
createLocalFunctionDeclarationNode(function).also {
addNewSimpleNode(it)
}
createLocalFunctionDeclarationNode(function).also { addNewSimpleNode(it) }
}
pushGraph(graph)
pushGraph(ControlFlowGraph(function, name, ControlFlowGraph.Kind.Function))
val enterNode = createFunctionEnterNode(function).also {
lastNodes.push(it)
@@ -266,10 +262,6 @@ class ControlFlowGraphBuilder {
assert(graph.exitNode == exitNode)
val (splitNode, postponedExitNode) = postponedAnonymousFunctionNodes.remove(symbol)!!
splitNode.addSubGraph(graph)
// May not be the current graph: `select(run { run { generic() } }, 1)`
splitNode.owner.addSubGraph(graph)
val invocationKind = anonymousFunction.invocationKind
if (postponedExitNode == null) {
// Postponed exit node was needed so we could create lambda->call edges without having the subgraph ready. If it
@@ -411,7 +403,6 @@ class ControlFlowGraphBuilder {
var prevInitPartNode: CFGNode<*>? = null
for (graph in calledInPlace) {
createPartOfClassInitializationNode(graph.declaration as FirControlFlowGraphOwner).also {
currentGraph.addSubGraph(graph)
addEdge(node, it, preferredKind = EdgeKind.CfgForward)
addEdge(it, graph.enterNode, preferredKind = EdgeKind.CfgForward)
node = graph.exitNode
@@ -435,10 +426,9 @@ class ControlFlowGraphBuilder {
// println(x)
for (graph in calledLater) {
addEdge(exitNode, graph.enterNode, preferredKind = EdgeKind.CfgForward)
exitNode.addSubGraph(graph)
currentGraph.addSubGraph(graph)
}
exitNode.subGraphs = calledLater
return popGraph()
}
@@ -455,9 +445,7 @@ class ControlFlowGraphBuilder {
}
fun exitLocalClass(klass: FirRegularClass): Pair<LocalClassExitNode, ControlFlowGraph> {
val graph = exitClass(klass).also {
currentGraph.addSubGraph(it)
}
val graph = exitClass(klass)
val node = createLocalClassExitNode(klass).also {
addNewSimpleNodeIfPossible(it)
}
@@ -475,9 +463,7 @@ class ControlFlowGraphBuilder {
}
fun exitAnonymousObject(anonymousObject: FirAnonymousObject): Pair<AnonymousObjectExitNode, ControlFlowGraph> {
val graph = exitClass(anonymousObject).also {
currentGraph.addSubGraph(it)
}
val graph = exitClass(anonymousObject)
val enterNode = lastNodes.popOrNull()
if (enterNode !is AnonymousObjectEnterNode) {
throw AssertionError("anonymous object exit should be preceded by anonymous object enter, but got $enterNode")
@@ -535,12 +521,9 @@ class ControlFlowGraphBuilder {
popAndAddEdge(exitNode)
val graph = popGraph()
require(exitNode == graph.exitNode)
val outerEnterNode = lastNode as EnterValueParameterNode
val outerExitNode = createExitValueParameterNode(valueParameter)
addNewSimpleNode(outerExitNode)
addEdge(exitNode, outerExitNode, propagateDeadness = false)
outerEnterNode.addSubGraph(graph)
currentGraph.addSubGraph(graph)
return Triple(exitNode, outerExitNode, graph)
}
@@ -154,7 +154,7 @@ class FirControlFlowGraphRenderVisitor(
private fun initializeNodes(graph: ControlFlowGraph) {
if (graph in allGraphs) return
graph.collectNodes()
if (graph.owner == null) {
if (!graph.isSubGraph) {
topLevelGraphs += graph
}
allGraphs += graph
@@ -142,21 +142,12 @@ interface UnionNodeMarker
// ----------------------------------- EnterNode for declaration with CFG -----------------------------------
sealed class CFGNodeWithSubgraphs<out E : FirElement>(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<E>(owner, level, id) {
private val _subGraphs = mutableListOf<ControlFlowGraph>()
fun addSubGraph(graph: ControlFlowGraph){
_subGraphs += graph
}
open val subGraphs: List<ControlFlowGraph>
get() = _subGraphs
abstract val subGraphs: List<ControlFlowGraph>
}
sealed class CFGNodeWithCfgOwner<out E : FirControlFlowGraphOwner>(owner: ControlFlowGraph, level: Int, id: Int) : CFGNodeWithSubgraphs<E>(owner, level, id) {
override val subGraphs: List<ControlFlowGraph> by lazy {
fir.controlFlowGraphReference?.controlFlowGraph?.run(::addSubGraph)
super.subGraphs
}
final override val subGraphs: List<ControlFlowGraph>
get() = listOfNotNull(fir.controlFlowGraphReference?.controlFlowGraph)
}
// ----------------------------------- Named function -----------------------------------
@@ -222,6 +213,10 @@ class ExitValueParameterNode(owner: ControlFlowGraph, override val fir: FirValue
class SplitPostponedLambdasNode(owner: ControlFlowGraph, override val fir: FirStatement, val lambdas: List<FirAnonymousFunction>, level: Int, id: Int)
: CFGNodeWithSubgraphs<FirStatement>(owner, level, id) {
override val subGraphs: List<ControlFlowGraph>
get() = lambdas.mapNotNull { it.controlFlowGraphReference?.controlFlowGraph }
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitSplitPostponedLambdasNode(this, data)
}
@@ -240,6 +235,9 @@ class MergePostponedLambdaExitsNode(owner: ControlFlowGraph, override val fir: F
}
class AnonymousFunctionExpressionNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunctionExpression, level: Int, id: Int) : CFGNodeWithSubgraphs<FirAnonymousFunctionExpression>(owner, level, id) {
override val subGraphs: List<ControlFlowGraph>
get() = listOfNotNull(fir.anonymousFunction.controlFlowGraphReference?.controlFlowGraph)
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitAnonymousFunctionExpressionNode(this, data)
}
@@ -266,6 +264,8 @@ class ClassExitNode(owner: ControlFlowGraph, override val fir: FirClass, level:
owner.exitNode = this
}
lateinit override var subGraphs: List<ControlFlowGraph>
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitClassExitNode(this, data)
}
@@ -26,14 +26,14 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
@set:CfgInternals
lateinit var exitNode: CFGNode<*>
var owner: ControlFlowGraph? = null
private set
val isSubGraph: Boolean
get() = enterNode.previousNodes.isNotEmpty()
var state: State = State.Building
private set
private val _subGraphs: MutableList<ControlFlowGraph> = mutableListOf()
val subGraphs: List<ControlFlowGraph> get() = _subGraphs
val subGraphs: List<ControlFlowGraph>
get() = _nodes.flatMap { (it as? CFGNodeWithSubgraphs<*>)?.subGraphs ?: emptyList() }
@CfgInternals
fun complete() {
@@ -50,15 +50,6 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
_nodes.addAll(sortedNodes)
}
@CfgInternals
fun addSubGraph(graph: ControlFlowGraph) {
assert(graph.owner == null) {
"SubGraph already has owner"
}
graph.owner = this
_subGraphs += graph
}
private fun assertState(state: State) {
assert(this.state == state) {
"This action can not be performed at $this state"
@@ -171,34 +162,16 @@ private fun ControlFlowGraph.orderNodes(): LinkedHashSet<CFGNode<*>> {
continue
}
if (!visitedNodes.add(node)) continue
val followingNodes = node.followingNodes
for (followingNode in followingNodes) {
// NOTE: this intentionally does not walk through subgraphs. If the only path from A to B
// is through a subgraph, add a dead edge between them to enforce ordering.
for (followingNode in node.followingNodes) {
if (followingNode.owner == this) {
if (followingNode !in visitedNodes) {
stack.addFirst(followingNode)
}
} else {
walkThrowSubGraphs(followingNode.owner, visitedNodes, stack)
}
}
}
return visitedNodes
}
private fun ControlFlowGraph.walkThrowSubGraphs(
otherGraph: ControlFlowGraph,
visitedNodes: Set<CFGNode<*>>,
stack: ArrayDeque<CFGNode<*>>
) {
if (otherGraph.owner != this) return
for (otherNode in otherGraph.exitNode.followingNodes) {
if (otherNode.owner == this) {
if (otherNode !in visitedNodes) {
stack.addFirst(otherNode)
}
} else if (otherNode.owner != otherGraph) {
walkThrowSubGraphs(otherNode.owner, visitedNodes, stack)
}
}
}
+27 -27
View File
@@ -235,7 +235,7 @@ digraph kt44814_kt {
71 -> {75} [color=green];
50 -> {51};
51 -> {52 55};
51 -> {55 55} [style=dashed];
51 -> {55} [style=dashed];
52 -> {53};
53 -> {54};
55 -> {56};
@@ -576,20 +576,20 @@ digraph kt44814_kt {
193 [label="Postponed enter to lambda [8]"];
subgraph cluster_62 {
color=blue
240 [label="Enter function anonymousFunction [9]" style="filled" fillcolor=red];
234 [label="Enter function anonymousFunction [9]" style="filled" fillcolor=red];
subgraph cluster_63 {
color=blue
241 [label="Enter block [9]"];
242 [label="Access variable R|<local>/it| [9]"];
243 [label="Enter safe call [9]"];
244 [label="Access variable R|/LighterASTNode.tokenType| [9]"];
245 [label="Exit safe call [9]"];
246 [label="Access qualifier /TokenType [9]"];
247 [label="Access variable R|/TokenType.Companion.MODIFIER_LIST| [9]"];
248 [label="Equality operator == [9]"];
249 [label="Exit block [9]"];
235 [label="Enter block [9]"];
236 [label="Access variable R|<local>/it| [9]"];
237 [label="Enter safe call [9]"];
238 [label="Access variable R|/LighterASTNode.tokenType| [9]"];
239 [label="Exit safe call [9]"];
240 [label="Access qualifier /TokenType [9]"];
241 [label="Access variable R|/TokenType.Companion.MODIFIER_LIST| [9]"];
242 [label="Equality operator == [9]"];
243 [label="Exit block [9]"];
}
250 [label="Exit function anonymousFunction [9]" style="filled" fillcolor=red];
244 [label="Exit function anonymousFunction [9]" style="filled" fillcolor=red];
}
194 [label="Postponed exit from lambda [8]"];
195 [label="Function call: this@R|/FirModifierList.Companion.getModifierList|.R|/FirLightSourceElement.lighterASTNode|.R|/LighterASTNode.getChildren|(...).R|kotlin/collections/find|<R|LighterASTNode?|>(...) [7]" style="filled" fillcolor=yellow];
@@ -619,15 +619,15 @@ digraph kt44814_kt {
216 [label="Postponed enter to lambda [7]"];
subgraph cluster_65 {
color=blue
234 [label="Enter function anonymousFunction [8]" style="filled" fillcolor=red];
245 [label="Enter function anonymousFunction [8]" style="filled" fillcolor=red];
subgraph cluster_66 {
color=blue
235 [label="Enter block [8]"];
236 [label="Access variable R|<local>/it| [9]"];
237 [label="Function call: R|/FirModifierList.FirPsiModifierList.FirPsiModifierList|(...) [8]" style="filled" fillcolor=yellow];
238 [label="Exit block [8]"];
246 [label="Enter block [8]"];
247 [label="Access variable R|<local>/it| [9]"];
248 [label="Function call: R|/FirModifierList.FirPsiModifierList.FirPsiModifierList|(...) [8]" style="filled" fillcolor=yellow];
249 [label="Exit block [8]"];
}
239 [label="Exit function anonymousFunction [8]" style="filled" fillcolor=red];
250 [label="Exit function anonymousFunction [8]" style="filled" fillcolor=red];
}
217 [label="Postponed exit from lambda [7]"];
218 [label="Function call: $subj$.R|kotlin/let|<R|KtModifierList|, R|FirModifierList.FirPsiModifierList|>(...) [6]" style="filled" fillcolor=yellow];
@@ -792,8 +792,8 @@ digraph kt44814_kt {
190 -> {191};
191 -> {192};
192 -> {193};
193 -> {194 195 240};
193 -> {240} [style=dashed];
193 -> {194 195 234};
193 -> {234} [style=dashed];
194 -> {195};
194 -> {193} [color=green style=dashed];
195 -> {196};
@@ -818,9 +818,9 @@ digraph kt44814_kt {
213 -> {214};
214 -> {215 219};
215 -> {216};
216 -> {218 234};
216 -> {218 245};
216 -> {217} [style=dotted];
216 -> {234} [style=dashed];
216 -> {245} [style=dashed];
217 -> {218} [color=green];
217 -> {223} [color=red];
218 -> {220};
@@ -841,21 +841,21 @@ digraph kt44814_kt {
232 -> {233} [style=dotted];
234 -> {235};
235 -> {236};
236 -> {237};
236 -> {237 239};
237 -> {238};
238 -> {239};
239 -> {217};
239 -> {240};
240 -> {241};
241 -> {242};
242 -> {243 245};
242 -> {243};
243 -> {244};
244 -> {245};
244 -> {194};
245 -> {246};
246 -> {247};
247 -> {248};
248 -> {249};
249 -> {250};
250 -> {194};
250 -> {217};
251 -> {252};
252 -> {253};
253 -> {254};