[FIR] Pass flow to local functions

This commit is contained in:
Dmitriy Novozhilov
2019-09-12 14:14:54 +03:00
parent e1947e6884
commit b76b4b0229
5 changed files with 35 additions and 13 deletions
@@ -64,7 +64,13 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
// ----------------------------------- Named function -----------------------------------
fun enterFunction(function: FirFunction<*>) {
graphBuilder.enterFunction(function).mergeIncomingFlow()
val (functionEnterNode, previousNode) = graphBuilder.enterFunction(function)
if (previousNode == null) {
functionEnterNode.mergeIncomingFlow()
} else {
assert(functionEnterNode.previousNodes.isEmpty())
functionEnterNode.flow = logicSystem.forkFlow(previousNode.flow)
}
}
fun exitFunction(function: FirFunction<*>): ControlFlowGraph? {
@@ -12,10 +12,14 @@ import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.*
class ControlFlowGraph(val name: String) {
class ControlFlowGraph(val name: String, val kind: Kind) {
val nodes = mutableListOf<CFGNode<*>>()
lateinit var enterNode: CFGNode<*>
lateinit var exitNode: CFGNode<*>
enum class Kind {
Function, ClassInitializer, PropertyInitializer, TopLevel
}
}
sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level: Int) {
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.resultType
import org.jetbrains.kotlin.fir.types.isNothing
class ControlFlowGraphBuilder {
private val graphs: Stack<ControlFlowGraph> = stackOf(ControlFlowGraph("<DUMP_GRAPH_FOR_ENUMS>"))
private val graphs: Stack<ControlFlowGraph> = stackOf(ControlFlowGraph("<DUMP_GRAPH_FOR_ENUMS>", ControlFlowGraph.Kind.TopLevel))
val graph: ControlFlowGraph get() = graphs.top()
private val lexicalScopes: Stack<Stack<CFGNode<*>>> = stackOf(stackOf())
@@ -50,7 +50,11 @@ class ControlFlowGraphBuilder {
// ----------------------------------- Named function -----------------------------------
fun enterFunction(function: FirFunction<*>): FunctionEnterNode {
/*
* Second argument of pair is not null only if function is local and it is a
* previous node before function declaration
*/
fun enterFunction(function: FirFunction<*>): Pair<FunctionEnterNode, CFGNode<*>?> {
val name = when (function) {
is FirNamedFunction -> function.name.asString()
is FirAbstractPropertyAccessor -> if (function.isGetter) "<getter>" else "<setter>"
@@ -60,9 +64,17 @@ class ControlFlowGraphBuilder {
}
val invocationKind = function.invocationKind
val isInplace = invocationKind.isInplace()
if (!isInplace) {
graphs.push(ControlFlowGraph(name))
val previousNode = if (!isInplace && graphs.topOrNull()?.let { it.kind != ControlFlowGraph.Kind.TopLevel } == true) {
lastNodes.top()
} else {
null
}
if (!isInplace) {
graphs.push(ControlFlowGraph(name, ControlFlowGraph.Kind.Function))
}
val enterNode = createFunctionEnterNode(function, isInplace).also {
if (isInplace) {
addNewSimpleNode(it)
@@ -88,7 +100,7 @@ class ControlFlowGraphBuilder {
exitNodes.push(exitNode)
}
levelCounter++
return enterNode
return enterNode to previousNode
}
fun exitFunction(function: FirFunction<*>): Pair<FunctionExitNode, ControlFlowGraph?> {
@@ -127,7 +139,7 @@ class ControlFlowGraphBuilder {
// ----------------------------------- Property -----------------------------------
fun enterProperty(property: FirProperty): PropertyInitializerEnterNode {
graphs.push(ControlFlowGraph("val ${property.name}"))
graphs.push(ControlFlowGraph("val ${property.name}", ControlFlowGraph.Kind.PropertyInitializer))
val enterNode = createPropertyInitializerEnterNode(property)
val exitNode = createPropertyInitializerExitNode(property)
topLevelVariableInitializerExitNodes.push(exitNode)
@@ -522,7 +534,7 @@ class ControlFlowGraphBuilder {
// ----------------------------------- Block -----------------------------------
fun enterInitBlock(initBlock: FirAnonymousInitializer): InitBlockEnterNode {
graphs.push(ControlFlowGraph("init block"))
graphs.push(ControlFlowGraph("init block", ControlFlowGraph.Kind.ClassInitializer))
val enterNode = createInitBlockEnterNode(initBlock).also {
lexicalScopes.push(stackOf(it))
}
+2 -2
View File
@@ -112,7 +112,7 @@ digraph lambdas_kt {
color=blue
33 [label="Enter block"];
34 [label="Access variable R|<local>/x|"];
35 [label="Function call: R|<local>/x|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()"];
35 [label="Function call: R|<local>/x|.R|kotlin/Int.inc|()"];
36 [label="Exit block"];
}
37 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
@@ -156,7 +156,7 @@ digraph lambdas_kt {
subgraph cluster_19 {
color=blue
52 [label="Enter block"];
53 [label="Variable declaration: lval lambda: R|kotlin/Function0<class error: Ambiguity: inc, [kotlin/inc, kotlin/inc]>|"];
53 [label="Variable declaration: lval lambda: R|kotlin/Function0<kotlin/Int>|"];
54 [label="Exit block"];
}
55 [label="Exit when branch result"];
+2 -2
View File
@@ -18,8 +18,8 @@ FILE: lambdas.kt
public final fun test_2(x: R|kotlin/Any?|): R|kotlin/Unit| {
when () {
(R|<local>/x| is R|kotlin/Int|) -> {
lval lambda: R|kotlin/Function0<class error: Ambiguity: inc, [kotlin/inc, kotlin/inc]>| = fun <anonymous>(): <ERROR TYPE REF: Ambiguity: inc, [kotlin/inc, kotlin/inc]> {
R|<local>/x|.<Ambiguity: inc, [kotlin/inc, kotlin/inc]>#()
lval lambda: R|kotlin/Function0<kotlin/Int>| = fun <anonymous>(): R|kotlin/Int| {
R|<local>/x|.R|kotlin/Int.inc|()
}
}