diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/lambdas.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/lambdas.dot index 2ab5d119633..e7ec200ff8d 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/lambdas.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/lambdas.dot @@ -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"]; diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/LocalPropertyAndCapturedWriteCollector.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/LocalPropertyAndCapturedWriteCollector.kt index 7950e7586c9..ebf0e39df5d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/LocalPropertyAndCapturedWriteCollector.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/LocalPropertyAndCapturedWriteCollector.kt @@ -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) } 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 783845a2657..517987b979c 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 @@ -151,12 +151,12 @@ abstract class FirDataFlowAnalyzer( return variable.stability to types.toMutableList() } - fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection { - return graphBuilder.returnExpressionsOfAnonymousFunction(function) - } + fun returnExpressionsOfAnonymousFunctionOrNull(function: FirAnonymousFunction): Collection? = + graphBuilder.returnExpressionsOfAnonymousFunction(function) - fun isThereControlFlowInfoForAnonymousFunction(function: FirAnonymousFunction): Boolean = - graphBuilder.isThereControlFlowInfoForAnonymousFunction(function) + fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection = + 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) 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 d5dbea92fb7..e06f94b9381 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 @@ -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 = SymbolBasedNodeStorage() private val exitTargetsForTry: Stack> = stackOf() - private val exitsOfAnonymousFunctions: MutableMap, FunctionExitNode> = mutableMapOf() private val enterToLocalClassesMembers: MutableMap, 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? { + val exitNode = function.controlFlowGraphReference?.controlFlowGraph?.exitNode + ?: return null - // This function might throw exception if !isThereControlFlowInfoForAnonymousFunction(function) - fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection { 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 { + fun enterAnonymousFunction(anonymousFunction: FirAnonymousFunction): Pair { 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, "", 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 { 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() } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt index 61426898a45..5e9dc05e217 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt @@ -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 = - dataFlowAnalyzer.returnExpressionsOfAnonymousFunction(anonymousFunction) for (expression in returnExpressionsOfAnonymousFunction) { expression.transform(this, finalType?.toExpectedType()) } 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 a28a0003840..11537defb71 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 @@ -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(owner, level, id) { +class PostponedLambdaEnterNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunction, level: Int, id: Int) : CFGNodeWithSubgraphs(owner, level, id) { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitPostponedLambdaEnterNode(this, data) }