From 9f4cfa2c50d329f37cc1bcbcc506eeb91ae36610 Mon Sep 17 00:00:00 2001 From: pyos Date: Mon, 28 Nov 2022 11:14:07 +0100 Subject: [PATCH] FIR CFG: ignore exception edges when looking for return values ^KT-54668 Fixed --- .../lazyValWithElvisToNothingInside.fir.txt | 2 +- .../lazyValWithElvisToNothingInside.kt | 4 ++-- .../resolve/samConversions/kotlinSam.fir.txt | 4 ++-- .../notSamBecauseOfSupertype.fir.txt | 2 +- .../resolve/dfa/cfg/ControlFlowGraphBuilder.kt | 17 +++++++++++------ 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/compiler/fir/analysis-tests/testData/resolve/properties/lazyValWithElvisToNothingInside.fir.txt b/compiler/fir/analysis-tests/testData/resolve/properties/lazyValWithElvisToNothingInside.fir.txt index f832eb4a5c0..e95c7939cde 100644 --- a/compiler/fir/analysis-tests/testData/resolve/properties/lazyValWithElvisToNothingInside.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/properties/lazyValWithElvisToNothingInside.fir.txt @@ -15,5 +15,5 @@ FILE: lazyValWithElvisToNothingInside.kt } ) public get(): R|kotlin/collections/Map| { - ^ D|/x|.#|>(Null(null), ::R|/x|) + ^ D|/x|.R|kotlin/getValue||>(Null(null), ::R|/x|) } diff --git a/compiler/fir/analysis-tests/testData/resolve/properties/lazyValWithElvisToNothingInside.kt b/compiler/fir/analysis-tests/testData/resolve/properties/lazyValWithElvisToNothingInside.kt index f6661721113..eb0b5b350bb 100644 --- a/compiler/fir/analysis-tests/testData/resolve/properties/lazyValWithElvisToNothingInside.kt +++ b/compiler/fir/analysis-tests/testData/resolve/properties/lazyValWithElvisToNothingInside.kt @@ -7,8 +7,8 @@ interface A { fun getA(): A? = null -val x by lazy { +val x by lazy { (getA() ?: error("error")).list.associateBy { it } -} +} diff --git a/compiler/fir/analysis-tests/testData/resolve/samConversions/kotlinSam.fir.txt b/compiler/fir/analysis-tests/testData/resolve/samConversions/kotlinSam.fir.txt index 7b3f4fbb941..f4049bcc241 100644 --- a/compiler/fir/analysis-tests/testData/resolve/samConversions/kotlinSam.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/samConversions/kotlinSam.fir.txt @@ -38,12 +38,12 @@ FILE: kotlinSam.kt } ) R|/foo1|(R|/f|) - #( = foo2@fun (x: R|kotlin/Nothing|): R|kotlin/Nothing| { + #( = foo2@fun (x: R|kotlin/Nothing|): R|kotlin/Boolean| { ^ CMP(>, R|/x|.#(Int(1))) } ) #(R|/f|) - #( = foo3@fun (x: R|kotlin/Nothing|): R|kotlin/Nothing| { + #( = foo3@fun (x: R|kotlin/Nothing|): R|kotlin/Boolean| { ^ CMP(>, R|/x|.#(Int(1))) } ) diff --git a/compiler/fir/analysis-tests/testData/resolve/samConversions/notSamBecauseOfSupertype.fir.txt b/compiler/fir/analysis-tests/testData/resolve/samConversions/notSamBecauseOfSupertype.fir.txt index 51fe118c5ba..aac658e208a 100644 --- a/compiler/fir/analysis-tests/testData/resolve/samConversions/notSamBecauseOfSupertype.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/samConversions/notSamBecauseOfSupertype.fir.txt @@ -2,7 +2,7 @@ FILE: main.kt public final fun foo(m: R|MyRunnable|): R|kotlin/Unit| { } public final fun main(): R|kotlin/Unit| { - Q|JavaUsage|.#( = foo@fun (x: R|kotlin/Nothing|): R|kotlin/Nothing| { + Q|JavaUsage|.#( = foo@fun (x: R|kotlin/Nothing|): R|kotlin/Boolean| { ^ CMP(>, R|/x|.#(Int(1))) } ) 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 aeacb40d6ce..61c826d739e 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 @@ -97,24 +97,29 @@ class ControlFlowGraphBuilder { // ----------------------------------- Public API ----------------------------------- fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection? { - val exitNode = function.controlFlowGraphReference?.controlFlowGraph?.exitNode - ?: return null + val exitNode = function.controlFlowGraphReference?.controlFlowGraph?.exitNode ?: return null fun FirElement.extractArgument(): FirElement = when { this is FirReturnExpression && target.labeledElement.symbol == function.symbol -> result.extractArgument() else -> this } - fun CFGNode<*>.extractArgument(): FirElement? = when (this) { + fun CFGNode<*>.extractArgument(): FirStatement? = when (this) { is FunctionEnterNode, is TryMainBlockEnterNode, is FinallyBlockExitNode, is CatchClauseEnterNode -> null is BlockExitNode -> if (function.isLambda || isDead) firstPreviousNode.extractArgument() else null is StubNode -> firstPreviousNode.extractArgument() - else -> fir.extractArgument() + else -> fir.extractArgument() as FirStatement? } - return (nonDirectJumps[exitNode] + exitNode.previousNodes).mapNotNullTo(mutableSetOf()) { - it.extractArgument() as FirStatement? + val returnValues = exitNode.previousNodes.mapNotNullTo(mutableSetOf()) { + val edge = exitNode.incomingEdges.getValue(it) + // * NormalPath: last expression = return value + // * UncaughtExceptionPath: last expression = whatever threw, *not* a return value + // * ReturnPath(this lambda): these go from `finally` blocks, so that's not the return value; + // look in `nonDirectJumps` instead. + it.takeIf { edge.kind.usedInCfa && edge.label == NormalPath }?.extractArgument() } + return nonDirectJumps[exitNode].mapNotNullTo(returnValues) { it.extractArgument() } } @OptIn(CfgBuilderInternals::class)