From fc43c8a74b599fc922ff10c19fae7944ee107521 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 26 Aug 2019 12:58:02 +0300 Subject: [PATCH] [FIR] Add inlining of control flow graphs of in-place anonymous functions --- .../resolve/dfa/DummyFirDataFlowAnalyzer.kt | 2 +- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 2 +- .../resolve/dfa/FirDataFlowAnalyzerImpl.kt | 2 +- .../dfa/cfg/ControlFlowGraphBuilder.kt | 59 +++++- .../dfa/cfg/ControlFlowGraphNodeBuilder.kt | 13 +- .../transformers/FirBodyResolveTransformer.kt | 5 +- .../resolve/testData/resolve/cfg/complex.txt | 2 +- .../testData/resolve/cfg/lambdas.cfg.dot | 127 ++++++++++++ .../resolve/testData/resolve/cfg/lambdas.kt | 19 ++ .../resolve/testData/resolve/cfg/lambdas.txt | 30 +++ .../resolve/cfg/propertiesAndInitBlocks.txt | 2 +- .../resolve/smartcasts/inPlaceLambdas.cfg.dot | 182 ++++++++++++++++++ .../resolve/smartcasts/inPlaceLambdas.kt | 37 ++++ .../resolve/smartcasts/inPlaceLambdas.txt | 47 +++++ .../fir/FirCfgBuildingTestGenerated.java | 10 + 15 files changed, 520 insertions(+), 19 deletions(-) create mode 100644 compiler/fir/resolve/testData/resolve/cfg/lambdas.cfg.dot create mode 100644 compiler/fir/resolve/testData/resolve/cfg/lambdas.kt create mode 100644 compiler/fir/resolve/testData/resolve/cfg/lambdas.txt create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.cfg.dot create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.kt create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.txt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt index 66e9374e403..31de901d446 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/DummyFirDataFlowAnalyzer.kt @@ -24,7 +24,7 @@ class DummyFirDataFlowAnalyzer : FirDataFlowAnalyzer() { override fun enterFunction(function: FirFunction<*>) {} - override fun exitFunction(function: FirFunction<*>): ControlFlowGraph = ControlFlowGraph(DUMMY) + override fun exitFunction(function: FirFunction<*>): ControlFlowGraph? = null override fun enterBlock(block: FirBlock) {} 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 8404cbe3e14..4ae2ade4caf 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 @@ -18,7 +18,7 @@ abstract class FirDataFlowAnalyzer { // ----------------------------------- Named function ----------------------------------- abstract fun enterFunction(function: FirFunction<*>) - abstract fun exitFunction(function: FirFunction<*>): ControlFlowGraph + abstract fun exitFunction(function: FirFunction<*>): ControlFlowGraph? // ----------------------------------- Property ----------------------------------- diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt index 3da80e9a947..defa391e7ad 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzerImpl.kt @@ -58,7 +58,7 @@ class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataF } } - override fun exitFunction(function: FirFunction<*>): ControlFlowGraph { + override fun exitFunction(function: FirFunction<*>): ControlFlowGraph? { val (node, graph) = graphBuilder.exitFunction(function) node.passFlow() for (valueParameter in function.valueParameters) { 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 0a213e3e3a8..10433bd5622 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 @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.resolve.dfa.cfg +import org.jetbrains.kotlin.contracts.description.InvocationKind import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirAbstractPropertyAccessor @@ -65,18 +66,48 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() { is FirConstructor -> function.name.asString() else -> throw IllegalArgumentException("Unknown function: ${function.render()}") } - graphs.push(ControlFlowGraph(name)) - functionExitNodes.push(createFunctionExitNode(function)) - lexicalScopes.push(stackOf()) - return createFunctionEnterNode(function).also { lastNodes.push(it) }.also { levelCounter++ } + val invocationKind = function.invocationKind + val isInplace = invocationKind.isInplace() + if (!isInplace) { + graphs.push(ControlFlowGraph(name)) + } + val enterNode = createFunctionEnterNode(function, isInplace).also { + if (isInplace) { + addNewSimpleNode(it) + } else { + lexicalScopes.push(stackOf()) + lastNodes.push(it) + } + } + val exitNode = createFunctionExitNode(function, isInplace) + + @Suppress("NON_EXHAUSTIVE_WHEN") + when (invocationKind) { + InvocationKind.AT_LEAST_ONCE -> addEdge(exitNode, enterNode) + InvocationKind.AT_MOST_ONCE -> addEdge(enterNode, exitNode) + } + + functionExitNodes.push(exitNode) + levelCounter++ + return enterNode } - fun exitFunction(function: FirFunction<*>): Pair { + fun exitFunction(function: FirFunction<*>): Pair { levelCounter-- val exitNode = functionExitNodes.pop() - addEdge(lastNodes.pop(), exitNode) - lexicalScopes.pop() - return exitNode to graphs.pop() + val isInplace = function.isInplace() + if (isInplace) { + addNewSimpleNode(exitNode) + } else { + addEdge(lastNodes.pop(), exitNode) + lexicalScopes.pop() + } + val graph = if (!isInplace) { + graphs.pop() + } else { + null + } + return exitNode to graph } // ----------------------------------- Block ----------------------------------- @@ -503,4 +534,16 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() { from.followingNodes += to to.previousNodes += from } + + private val FirFunction<*>.invocationKind: InvocationKind? + get() = (this as? FirAnonymousFunction)?.invocationKind + + private fun InvocationKind?.isInplace(): Boolean { + return this != null && this != InvocationKind.UNKNOWN + } + + private fun FirFunction<*>.isInplace(): Boolean { + val invocationKind = this.invocationKind + return invocationKind != null && invocationKind != InvocationKind.UNKNOWN + } } \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt index a36a91e62b8..4e04b5d5973 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt @@ -54,13 +54,18 @@ abstract class ControlFlowGraphNodeBuilder { protected fun createPropertyEnterNode(fir: FirProperty): PropertyEnterNode = PropertyEnterNode(graph, fir, levelCounter) - protected fun createFunctionEnterNode(fir: FirFunction<*>): FunctionEnterNode = + protected fun createFunctionEnterNode(fir: FirFunction<*>, isInPlace: Boolean): FunctionEnterNode = FunctionEnterNode(graph, fir, levelCounter).also { - graph.enterNode = it + if (!isInPlace) { + graph.enterNode = it + } } - protected fun createFunctionExitNode(fir: FirFunction<*>): FunctionExitNode = FunctionExitNode(graph, fir, levelCounter).also { - graph.exitNode = it + protected fun createFunctionExitNode(fir: FirFunction<*>, isInPlace: Boolean): FunctionExitNode = + FunctionExitNode(graph, fir, levelCounter).also { + if (!isInPlace) { + graph.exitNode = it + } } protected fun createBinaryOrEnterNode(fir: FirBinaryLogicExpression): BinaryOrEnterNode = diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt index 466a592567e..f9be62eaf6c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt @@ -603,8 +603,9 @@ open class FirBodyResolveTransformer( dataFlowAnalyzer.enterFunction(function) super.transformFunction(function, data).also { val result = it.single as FirFunction<*> - val controlFlowGraph = dataFlowAnalyzer.exitFunction(result) - result.transformControlFlowGraphReference(ControlFlowGraphReferenceTransformer, controlFlowGraph) + dataFlowAnalyzer.exitFunction(result)?.let { controlFlowGraph -> + result.transformControlFlowGraphReference(ControlFlowGraphReferenceTransformer, controlFlowGraph) + } } } } diff --git a/compiler/fir/resolve/testData/resolve/cfg/complex.txt b/compiler/fir/resolve/testData/resolve/cfg/complex.txt index 157fb98cfef..b5decbdba6d 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/complex.txt +++ b/compiler/fir/resolve/testData/resolve/cfg/complex.txt @@ -2,7 +2,7 @@ FILE: complex.kt @R|kotlin/jvm/Throws|((#), (#)) public final fun fetchPluginReleaseDate(pluginId: R|class error: Symbol not found, for `PluginId`|, version: R|kotlin/String|, channel: R|kotlin/String?|): R|class error: Symbol not found, for `LocalDate?`| { lval url: R|kotlin/String| = (String(https://plugins.jetbrains.com/api/plugins/), R|/pluginId|.#, String(/updates?version=), R|/version|) lval pluginDTOs: R|kotlin/Array| = try { - #.#(R|/url|).#( = connect@fun .(): { + #.#(R|/url|).#( = connect@fun .(): { GsonBuilder#().create#().fromJson#(it#.inputStream#.reader#(), (Array#()).java#) } ) diff --git a/compiler/fir/resolve/testData/resolve/cfg/lambdas.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/lambdas.cfg.dot new file mode 100644 index 00000000000..1ef4de5b134 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/lambdas.cfg.dot @@ -0,0 +1,127 @@ +digraph lambdas_kt { +subgraph run { + 0 [shape=box label="Enter function run"]; + 1 [shape=box label="Enter block"]; + 2 [shape=box label="Function call: R|/block|.R|FakeOverride|()"]; + 3 [shape=box label="Exit block"]; + 4 [shape=box label="Exit function run"]; + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {4}; +} + +subgraph test_1 { + 5 [shape=box label="Enter function test_1"]; + 6 [shape=box label="Enter block"]; + 7 [shape=box label="Enter when"]; + 8 [shape=box label="Enter when branch condition "]; + 9 [shape=box label="Access variable R|/x|"]; + 10 [shape=box label="Type operator: x is Int"]; + 11 [shape=box label="Exit when branch condition"]; + 12 [shape=box label="Enter block"]; + 13 [shape=box label="Enter function anonymousFunction"]; + 14 [shape=box label="Enter block"]; + 15 [shape=box label="Access variable R|/x|"]; + 16 [shape=box label="Function call: R|/x|.R|kotlin/Int.inc|()"]; + 17 [shape=box label="Exit block"]; + 18 [shape=box label="Exit function anonymousFunction"]; + 19 [shape=box label="Function call: R|/run|( = run@fun (): R|kotlin/Unit| { + R|/x|.R|kotlin/Int.inc|() +} +)"]; + 20 [shape=box label="Exit block"]; + 21 [shape=box label="Exit when branch result"]; + 22 [shape=box label="Enter when branch condition else"]; + 23 [shape=box label="Exit when branch condition"]; + 24 [shape=box label="Enter block"]; + 25 [shape=box label="Exit block"]; + 26 [shape=box label="Exit when branch result"]; + 27 [shape=box label="Exit when"]; + 28 [shape=box label="Exit block"]; + 29 [shape=box label="Exit function test_1"]; + + 5 -> {6}; + 6 -> {7}; + 7 -> {8}; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {12 22}; + 12 -> {13}; + 13 -> {14}; + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {27}; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {28}; + 28 -> {29}; +} + +subgraph { + 30 [shape=box label="Enter function anonymousFunction"]; + 31 [shape=box label="Enter block"]; + 32 [shape=box label="Access variable R|/x|"]; + 33 [shape=box label="Function call: R|/x|.#()"]; + 34 [shape=box label="Exit block"]; + 35 [shape=box label="Exit function anonymousFunction"]; + + 30 -> {31}; + 31 -> {32}; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; +} + +subgraph test_2 { + 36 [shape=box label="Enter function test_2"]; + 37 [shape=box label="Enter block"]; + 38 [shape=box label="Enter when"]; + 39 [shape=box label="Enter when branch condition "]; + 40 [shape=box label="Access variable R|/x|"]; + 41 [shape=box label="Type operator: x is Int"]; + 42 [shape=box label="Exit when branch condition"]; + 43 [shape=box label="Enter block"]; + 44 [shape=box label="Variable declaration: lval lambda: R|kotlin/Function0|"]; + 45 [shape=box label="Exit block"]; + 46 [shape=box label="Exit when branch result"]; + 47 [shape=box label="Enter when branch condition else"]; + 48 [shape=box label="Exit when branch condition"]; + 49 [shape=box label="Enter block"]; + 50 [shape=box label="Exit block"]; + 51 [shape=box label="Exit when branch result"]; + 52 [shape=box label="Exit when"]; + 53 [shape=box label="Exit block"]; + 54 [shape=box label="Exit function test_2"]; + + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 42 -> {43 47}; + 43 -> {44}; + 44 -> {45}; + 45 -> {46}; + 46 -> {52}; + 47 -> {48}; + 48 -> {49}; + 49 -> {50}; + 50 -> {51}; + 51 -> {52}; + 52 -> {53}; + 53 -> {54}; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/lambdas.kt b/compiler/fir/resolve/testData/resolve/cfg/lambdas.kt new file mode 100644 index 00000000000..eea6ea834c9 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/lambdas.kt @@ -0,0 +1,19 @@ +fun run(block: () -> Unit) { + block() +} + +fun test_1(x: Any?) { + if (x is Int) { + run { + x.inc() + } + } +} + +fun test_2(x: Any?) { + if (x is Int) { + val lambda = { + x.inc() + } + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/cfg/lambdas.txt b/compiler/fir/resolve/testData/resolve/cfg/lambdas.txt new file mode 100644 index 00000000000..aa32b32a393 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/lambdas.txt @@ -0,0 +1,30 @@ +FILE: lambdas.kt + public final fun run(block: R|kotlin/Function0|): R|kotlin/Unit| { + R|/block|.R|FakeOverride|() + } + public final fun test_1(x: R|kotlin/Any?|): R|kotlin/Unit| { + when () { + (R|/x| is R|kotlin/Int|) -> { + R|/run|( = run@fun (): R|kotlin/Unit| { + R|/x|.R|kotlin/Int.inc|() + } + ) + } + else -> { + } + } + + } + public final fun test_2(x: R|kotlin/Any?|): R|kotlin/Unit| { + when () { + (R|/x| is R|kotlin/Int|) -> { + lval lambda: R|kotlin/Function0| = fun (): { + R|/x|.#() + } + + } + else -> { + } + } + + } diff --git a/compiler/fir/resolve/testData/resolve/cfg/propertiesAndInitBlocks.txt b/compiler/fir/resolve/testData/resolve/cfg/propertiesAndInitBlocks.txt index 19e8e72b211..c437a14645d 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/propertiesAndInitBlocks.txt +++ b/compiler/fir/resolve/testData/resolve/cfg/propertiesAndInitBlocks.txt @@ -11,7 +11,7 @@ FILE: propertiesAndInitBlocks.kt public set(value: R|kotlin/Int|): R|kotlin/Unit| { F|/x2| = Int(1) } - public final val x3: R|kotlin/Unit| = R|/run|( = run@fun (): R|kotlin/Unit| { + public final val x3: R|kotlin/Unit| = R|/run|( = run@fun (): R|kotlin/Unit| { local final fun foo(): R|kotlin/Unit| { lval c: R|kotlin/Int| = Int(1).R|kotlin/Int.plus|(Int(1)) throw #() diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.cfg.dot b/compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.cfg.dot new file mode 100644 index 00000000000..cd3eff80c1a --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.cfg.dot @@ -0,0 +1,182 @@ +digraph inPlaceLambdas_kt { +subgraph foo { + 0 [shape=box label="Enter function foo"]; + 1 [shape=box label="Exit function foo"]; + + 0 -> {1}; +} + +subgraph bar { + 2 [shape=box label="Enter function bar"]; + 3 [shape=box label="Exit function bar"]; + + 2 -> {3}; +} + +subgraph run { + 4 [shape=box label="Enter function run"]; + 5 [shape=box label="Enter block"]; + 6 [shape=box label="Function call: R|/block|.R|FakeOverride|()"]; + 7 [shape=box label="Exit block"]; + 8 [shape=box label="Exit function run"]; + + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {8}; +} + +subgraph test_1 { + 9 [shape=box label="Enter function test_1"]; + 10 [shape=box label="Enter block"]; + 11 [shape=box label="Enter when"]; + 12 [shape=box label="Enter when branch condition "]; + 13 [shape=box label="Access variable R|/x|"]; + 14 [shape=box label="Type operator: x is A"]; + 15 [shape=box label="Exit when branch condition"]; + 16 [shape=box label="Enter block"]; + 17 [shape=box label="Enter function anonymousFunction"]; + 18 [shape=box label="Enter block"]; + 19 [shape=box label="Access variable R|/x|"]; + 20 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 21 [shape=box label="Exit block"]; + 22 [shape=box label="Exit function anonymousFunction"]; + 23 [shape=box label="Function call: R|/run|( = run@fun (): R|kotlin/Unit| { + R|/x|.R|/A.foo|() +} +)"]; + 24 [shape=box label="Exit block"]; + 25 [shape=box label="Exit when branch result"]; + 26 [shape=box label="Enter when branch condition else"]; + 27 [shape=box label="Exit when branch condition"]; + 28 [shape=box label="Enter block"]; + 29 [shape=box label="Exit block"]; + 30 [shape=box label="Exit when branch result"]; + 31 [shape=box label="Exit when"]; + 32 [shape=box label="Exit block"]; + 33 [shape=box label="Exit function test_1"]; + + 9 -> {10}; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {14}; + 14 -> {15}; + 15 -> {16 26}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {31}; + 26 -> {27}; + 27 -> {28}; + 28 -> {29}; + 29 -> {30}; + 30 -> {31}; + 31 -> {32}; + 32 -> {33}; +} + +subgraph test_2 { + 34 [shape=box label="Enter function test_2"]; + 35 [shape=box label="Enter block"]; + 36 [shape=box label="Enter function anonymousFunction"]; + 37 [shape=box label="Enter block"]; + 38 [shape=box label="Access variable R|/x|"]; + 39 [shape=box label="Type operator: x as B"]; + 40 [shape=box label="Exit block"]; + 41 [shape=box label="Exit function anonymousFunction"]; + 42 [shape=box label="Function call: R|/run|( = run@fun (): R|kotlin/Unit| { + (R|/x| as R|B|) +} +)"]; + 43 [shape=box label="Access variable R|/x|"]; + 44 [shape=box label="Function call: R|/x|.R|/B.bar|()"]; + 45 [shape=box label="Exit block"]; + 46 [shape=box label="Exit function test_2"]; + + 34 -> {35}; + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 42 -> {43}; + 43 -> {44}; + 44 -> {45}; + 45 -> {46}; +} + +subgraph test_3 { + 47 [shape=box label="Enter function test_3"]; + 48 [shape=box label="Enter block"]; + 49 [shape=box label="Enter when"]; + 50 [shape=box label="Enter when branch condition "]; + 51 [shape=box label="Access variable R|/x|"]; + 52 [shape=box label="Type operator: x is A"]; + 53 [shape=box label="Exit when branch condition"]; + 54 [shape=box label="Enter block"]; + 55 [shape=box label="Enter function anonymousFunction"]; + 56 [shape=box label="Enter block"]; + 57 [shape=box label="Access variable R|/x|"]; + 58 [shape=box label="Function call: R|/x|.R|/A.foo|()"]; + 59 [shape=box label="Access variable R|/x|"]; + 60 [shape=box label="Type operator: x as B"]; + 61 [shape=box label="Exit block"]; + 62 [shape=box label="Exit function anonymousFunction"]; + 63 [shape=box label="Function call: R|/run|( = run@fun (): R|kotlin/Unit| { + R|/x|.R|/A.foo|() + (R|/x| as R|B|) +} +)"]; + 64 [shape=box label="Access variable R|/x|"]; + 65 [shape=box label="Function call: R|/x|.R|/B.bar|()"]; + 66 [shape=box label="Exit block"]; + 67 [shape=box label="Exit when branch result"]; + 68 [shape=box label="Enter when branch condition else"]; + 69 [shape=box label="Exit when branch condition"]; + 70 [shape=box label="Enter block"]; + 71 [shape=box label="Exit block"]; + 72 [shape=box label="Exit when branch result"]; + 73 [shape=box label="Exit when"]; + 74 [shape=box label="Exit block"]; + 75 [shape=box label="Exit function test_3"]; + + 47 -> {48}; + 48 -> {49}; + 49 -> {50}; + 50 -> {51}; + 51 -> {52}; + 52 -> {53}; + 53 -> {54 68}; + 54 -> {55}; + 55 -> {56}; + 56 -> {57}; + 57 -> {58}; + 58 -> {59}; + 59 -> {60}; + 60 -> {61}; + 61 -> {62}; + 62 -> {63}; + 63 -> {64}; + 64 -> {65}; + 65 -> {66}; + 66 -> {67}; + 67 -> {73}; + 68 -> {69}; + 69 -> {70}; + 70 -> {71}; + 71 -> {72}; + 72 -> {73}; + 73 -> {74}; + 74 -> {75}; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.kt b/compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.kt new file mode 100644 index 00000000000..f2b1414a395 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.kt @@ -0,0 +1,37 @@ +interface A { + fun foo() +} + +interface B { + fun bar() +} + + +fun run(block: () -> Unit) { + block() +} + +fun test_1(x: Any?) { + if (x is A) { + run { + x.foo() + } + } +} + +fun test_2(x: Any?) { + run { + x as B + } + x.bar() +} + +fun test_3(x: Any?) { + if (x is A) { + run { + x.foo() + x as B + } + x.bar() + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.txt b/compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.txt new file mode 100644 index 00000000000..41cce5aee88 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.txt @@ -0,0 +1,47 @@ +FILE: inPlaceLambdas.kt + public abstract interface A : R|kotlin/Any| { + public abstract fun foo(): R|kotlin/Unit| + + } + public abstract interface B : R|kotlin/Any| { + public abstract fun bar(): R|kotlin/Unit| + + } + public final fun run(block: R|kotlin/Function0|): R|kotlin/Unit| { + R|/block|.R|FakeOverride|() + } + public final fun test_1(x: R|kotlin/Any?|): R|kotlin/Unit| { + when () { + (R|/x| is R|A|) -> { + R|/run|( = run@fun (): R|kotlin/Unit| { + R|/x|.R|/A.foo|() + } + ) + } + else -> { + } + } + + } + public final fun test_2(x: R|kotlin/Any?|): R|kotlin/Unit| { + R|/run|( = run@fun (): R|kotlin/Unit| { + (R|/x| as R|B|) + } + ) + R|/x|.R|/B.bar|() + } + public final fun test_3(x: R|kotlin/Any?|): R|kotlin/Unit| { + when () { + (R|/x| is R|A|) -> { + R|/run|( = run@fun (): R|kotlin/Unit| { + R|/x|.R|/A.foo|() + (R|/x| as R|B|) + } + ) + R|/x|.R|/B.bar|() + } + else -> { + } + } + + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java index 6344e489da8..3c2731b20d8 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirCfgBuildingTestGenerated.java @@ -51,6 +51,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest { runTest("compiler/fir/resolve/testData/resolve/cfg/jumps.kt"); } + @TestMetadata("lambdas.kt") + public void testLambdas() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/cfg/lambdas.kt"); + } + @TestMetadata("loops.kt") public void testLoops() throws Exception { runTest("compiler/fir/resolve/testData/resolve/cfg/loops.kt"); @@ -109,6 +114,11 @@ public class FirCfgBuildingTestGenerated extends AbstractFirCfgBuildingTest { runTest("compiler/fir/resolve/testData/resolve/smartcasts/equalsAndIdentity.kt"); } + @TestMetadata("inPlaceLambdas.kt") + public void testInPlaceLambdas() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/smartcasts/inPlaceLambdas.kt"); + } + @TestMetadata("nullability.kt") public void testNullability() throws Exception { runTest("compiler/fir/resolve/testData/resolve/smartcasts/nullability.kt");