FIR DFA: use local function declaration nodes for non-argument lambdas
This commit is contained in:
@@ -113,7 +113,7 @@ digraph lambdas_kt {
|
||||
subgraph cluster_13 {
|
||||
color=blue
|
||||
39 [label="Enter block"];
|
||||
40 [label="Postponed enter to lambda"];
|
||||
40 [label="Local function declaration test_2"];
|
||||
41 [label="Exit anonymous function expression"];
|
||||
42 [label="Variable declaration: lval lambda: R|() -> kotlin/Int|"];
|
||||
43 [label="Exit block"];
|
||||
|
||||
-4
@@ -43,10 +43,6 @@ class LocalPropertyAndCapturedWriteCollector private constructor() : ControlFlow
|
||||
lambdaOrLocalFunctionStack.add(node.fir)
|
||||
}
|
||||
|
||||
override fun visitPostponedLambdaExitNode(node: PostponedLambdaExitNode) {
|
||||
lambdaOrLocalFunctionStack.remove(node.fir.anonymousFunction)
|
||||
}
|
||||
|
||||
override fun visitLocalFunctionDeclarationNode(node: LocalFunctionDeclarationNode, data: Nothing?) {
|
||||
lambdaOrLocalFunctionStack.add(node.fir)
|
||||
}
|
||||
|
||||
+7
-7
@@ -151,12 +151,12 @@ abstract class FirDataFlowAnalyzer(
|
||||
return variable.stability to types.toMutableList()
|
||||
}
|
||||
|
||||
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection<FirStatement> {
|
||||
return graphBuilder.returnExpressionsOfAnonymousFunction(function)
|
||||
}
|
||||
fun returnExpressionsOfAnonymousFunctionOrNull(function: FirAnonymousFunction): Collection<FirStatement>? =
|
||||
graphBuilder.returnExpressionsOfAnonymousFunction(function)
|
||||
|
||||
fun isThereControlFlowInfoForAnonymousFunction(function: FirAnonymousFunction): Boolean =
|
||||
graphBuilder.isThereControlFlowInfoForAnonymousFunction(function)
|
||||
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection<FirStatement> =
|
||||
returnExpressionsOfAnonymousFunctionOrNull(function)
|
||||
?: error("anonymous function ${function.render()} not analyzed")
|
||||
|
||||
fun dropSubgraphFromCall(call: FirFunctionCall) {
|
||||
graphBuilder.dropSubgraphFromCall(call)
|
||||
@@ -220,8 +220,8 @@ abstract class FirDataFlowAnalyzer(
|
||||
finishPostponedAnonymousFunction()
|
||||
enterLocalFunction(anonymousFunction)
|
||||
}
|
||||
val (postponedLambdaEnterNode, functionEnterNode) = graphBuilder.enterAnonymousFunction(anonymousFunction)
|
||||
postponedLambdaEnterNode?.mergeIncomingFlow()
|
||||
val (functionDeclarationNode, functionEnterNode) = graphBuilder.enterAnonymousFunction(anonymousFunction)
|
||||
functionDeclarationNode?.mergeIncomingFlow()
|
||||
functionEnterNode.mergeIncomingFlow {
|
||||
if (anonymousFunction.invocationKind?.canBeRevisited() != false) {
|
||||
enterCapturingStatement(it, anonymousFunction)
|
||||
|
||||
+13
-21
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildAnonymousFunctionExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.*
|
||||
@@ -57,7 +56,6 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
private val exitTargetsForReturn: SymbolBasedNodeStorage<FirFunction, FunctionExitNode> = SymbolBasedNodeStorage()
|
||||
private val exitTargetsForTry: Stack<CFGNode<*>> = stackOf()
|
||||
private val exitsOfAnonymousFunctions: MutableMap<FirFunctionSymbol<*>, FunctionExitNode> = mutableMapOf()
|
||||
private val enterToLocalClassesMembers: MutableMap<FirBasedSymbol<*>, CFGNode<*>?> = mutableMapOf()
|
||||
|
||||
//return jumps via finally blocks, target -> jumps
|
||||
@@ -98,12 +96,10 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
// ----------------------------------- Public API -----------------------------------
|
||||
|
||||
fun isThereControlFlowInfoForAnonymousFunction(function: FirAnonymousFunction): Boolean =
|
||||
function.controlFlowGraphReference?.controlFlowGraph != null ||
|
||||
exitsOfAnonymousFunctions.containsKey(function.symbol)
|
||||
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection<FirStatement>? {
|
||||
val exitNode = function.controlFlowGraphReference?.controlFlowGraph?.exitNode
|
||||
?: return null
|
||||
|
||||
// This function might throw exception if !isThereControlFlowInfoForAnonymousFunction(function)
|
||||
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection<FirStatement> {
|
||||
fun FirElement.extractArgument(): FirElement = when {
|
||||
this is FirReturnExpression && target.labeledElement.symbol == function.symbol -> result.extractArgument()
|
||||
else -> this
|
||||
@@ -116,10 +112,7 @@ class ControlFlowGraphBuilder {
|
||||
else -> fir.extractArgument()
|
||||
}
|
||||
|
||||
val exitNode = function.controlFlowGraphReference?.controlFlowGraph?.exitNode
|
||||
?: exitsOfAnonymousFunctions.getValue(function.symbol)
|
||||
val nonDirect = nonDirectJumps[exitNode]
|
||||
return (nonDirect + exitNode.previousNodes).mapNotNullTo(mutableSetOf()) {
|
||||
return (nonDirectJumps[exitNode] + exitNode.previousNodes).mapNotNullTo(mutableSetOf()) {
|
||||
it.extractArgument() as FirStatement?
|
||||
}
|
||||
}
|
||||
@@ -257,29 +250,27 @@ class ControlFlowGraphBuilder {
|
||||
return enterNode to exitNode
|
||||
}
|
||||
|
||||
fun enterAnonymousFunction(anonymousFunction: FirAnonymousFunction): Pair<PostponedLambdaEnterNode?, FunctionEnterNode> {
|
||||
fun enterAnonymousFunction(anonymousFunction: FirAnonymousFunction): Pair<LocalFunctionDeclarationNode?, FunctionEnterNode> {
|
||||
val symbol = anonymousFunction.symbol
|
||||
val preparedEnterNode = postponedAnonymousFunctionNodes[symbol]?.first
|
||||
val outerEnterNode = preparedEnterNode ?: createPostponedLambdaEnterNode(anonymousFunction).also { addNewSimpleNode(it) }
|
||||
val flowSourceNode = postponedAnonymousFunctionNodes[symbol]?.first
|
||||
?: createLocalFunctionDeclarationNode(anonymousFunction).also { addNewSimpleNode(it) }
|
||||
|
||||
pushGraph(ControlFlowGraph(anonymousFunction, "<anonymous>", ControlFlowGraph.Kind.AnonymousFunction), Mode.Function)
|
||||
val enterNode = createFunctionEnterNode(anonymousFunction)
|
||||
val exitNode = createFunctionExitNode(anonymousFunction)
|
||||
exitsOfAnonymousFunctions[symbol] = exitNode
|
||||
exitTargetsForReturn.push(exitNode)
|
||||
if (!anonymousFunction.invocationKind.isInPlace) {
|
||||
exitTargetsForTry.push(exitNode)
|
||||
}
|
||||
|
||||
addEdge(outerEnterNode, enterNode)
|
||||
addEdge(flowSourceNode, enterNode)
|
||||
lastNodes.push(enterNode)
|
||||
return outerEnterNode.takeIf { preparedEnterNode == null } to enterNode
|
||||
return (flowSourceNode as? LocalFunctionDeclarationNode) to enterNode
|
||||
}
|
||||
|
||||
fun exitAnonymousFunction(anonymousFunction: FirAnonymousFunction): Triple<FunctionExitNode, PostponedLambdaExitNode?, ControlFlowGraph> {
|
||||
val symbol = anonymousFunction.symbol
|
||||
val exitNode = exitsOfAnonymousFunctions.remove(symbol)!!.also {
|
||||
require(it == exitTargetsForReturn.pop())
|
||||
val exitNode = exitTargetsForReturn.pop().also {
|
||||
if (!anonymousFunction.invocationKind.isInPlace) {
|
||||
require(it == exitTargetsForTry.pop())
|
||||
}
|
||||
@@ -295,7 +286,7 @@ class ControlFlowGraphBuilder {
|
||||
}
|
||||
|
||||
val (postponedEnterNode, postponedExitNode) = postponedAnonymousFunctionNodes.remove(symbol)
|
||||
?: return Triple(exitNode, null, graph).also { (lastNode as PostponedLambdaEnterNode).owner.addSubGraph(graph) }
|
||||
?: return Triple(exitNode, null, graph).also { currentGraph.addSubGraph(graph) }
|
||||
|
||||
val invocationKind = anonymousFunction.invocationKind
|
||||
var changedExitDataFlow = false
|
||||
@@ -327,6 +318,8 @@ class ControlFlowGraphBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
postponedEnterNode.addSubGraph(graph)
|
||||
// May not be the current graph: `nearestCompletedCall(run { run { generic() } }, 1)`
|
||||
postponedEnterNode.owner.addSubGraph(graph)
|
||||
return Triple(exitNode, postponedExitNode.takeIf { changedExitDataFlow }, graph)
|
||||
}
|
||||
@@ -1324,7 +1317,6 @@ class ControlFlowGraphBuilder {
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
fun reset() {
|
||||
exitsOfAnonymousFunctions.clear()
|
||||
dataFlowSourcesForNextCompletedCall.reset()
|
||||
lastNodes.reset()
|
||||
}
|
||||
|
||||
+2
-6
@@ -572,10 +572,8 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
// Control flow info is necessary prerequisite because we collect return expressions in that function
|
||||
//
|
||||
// Example: second lambda in the call like list.filter({}, {})
|
||||
if (!dataFlowAnalyzer.isThereControlFlowInfoForAnonymousFunction(anonymousFunction)) {
|
||||
// But, don't leave implicit type refs behind
|
||||
return transformImplicitTypeRefInAnonymousFunction(anonymousFunction)
|
||||
}
|
||||
val returnExpressionsOfAnonymousFunction = dataFlowAnalyzer.returnExpressionsOfAnonymousFunctionOrNull(anonymousFunction)
|
||||
?: return transformImplicitTypeRefInAnonymousFunction(anonymousFunction)
|
||||
|
||||
val expectedType = data?.getExpectedType(anonymousFunction)?.let { expectedArgumentType ->
|
||||
// From the argument mapping, the expected type of this anonymous function would be:
|
||||
@@ -645,8 +643,6 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
|
||||
val result = transformElement(anonymousFunction, null)
|
||||
|
||||
val returnExpressionsOfAnonymousFunction: Collection<FirStatement> =
|
||||
dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(anonymousFunction)
|
||||
for (expression in returnExpressionsOfAnonymousFunction) {
|
||||
expression.transform<FirElement, ExpectedArgumentType?>(this, finalType?.toExpectedType())
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ class ExitDefaultArgumentsNode(owner: ControlFlowGraph, override val fir: FirVal
|
||||
|
||||
// ----------------------------------- Anonymous function -----------------------------------
|
||||
|
||||
class PostponedLambdaEnterNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunction, level: Int, id: Int) : CFGNodeWithCfgOwner<FirAnonymousFunction>(owner, level, id) {
|
||||
class PostponedLambdaEnterNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunction, level: Int, id: Int) : CFGNodeWithSubgraphs<FirAnonymousFunction>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitPostponedLambdaEnterNode(this, data)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user