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 024902eefe9..95b472752c5 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 @@ -145,6 +145,12 @@ abstract class FirDataFlowAnalyzer( exitNode.mergeIncomingFlow() } + // ----------------------------------- Anonymous object ----------------------------------- + + fun exitAnonymousObject(anonymousObject: FirAnonymousObject) { + graphBuilder.exitAnonymousObject(anonymousObject).mergeIncomingFlow() + } + // ----------------------------------- Property ----------------------------------- fun enterProperty(property: FirProperty) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index 13cc768952a..0f1e5c9f77d 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -9,10 +9,7 @@ package org.jetbrains.kotlin.fir.resolve.dfa.cfg import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSourceElement -import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction -import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer -import org.jetbrains.kotlin.fir.declarations.FirFunction -import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.visitors.FirTransformer import org.jetbrains.kotlin.fir.visitors.FirVisitor @@ -108,6 +105,10 @@ class FunctionExitNode(owner: ControlFlowGraph, override val fir: FirFunction<*> class PostponedLambdaEnterNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunction, level: Int, id: Int) : CFGNode(owner, level, id) class PostponedLambdaExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousFunction, level: Int, id: Int) : CFGNode(owner, level, id) +// ----------------------------------- Anonymous function ----------------------------------- + +class AnonymousObjectExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousObject, level: Int, id: Int) : CFGNode(owner, level, id) + // ----------------------------------- Property ----------------------------------- class PropertyInitializerEnterNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode(owner, level, id), EnterNodeMarker 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 8b497f7a6f8..c3fd46f4d5e 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 @@ -194,6 +194,15 @@ class ControlFlowGraphBuilder { return enterNode to exitNode } + // ----------------------------------- Anonymous object ----------------------------------- + + fun exitAnonymousObject(anonymousObject: FirAnonymousObject): AnonymousObjectExitNode { + return createAnonymousObjectExitNode(anonymousObject).also { + // Hack for initializers of enum entries + addNewSimpleNodeIfPossible(it) + } + } + // ----------------------------------- Block ----------------------------------- fun enterBlock(block: FirBlock): BlockEnterNode? { @@ -712,6 +721,11 @@ class ControlFlowGraphBuilder { return oldNode } + private fun addNewSimpleNodeIfPossible(newNode: CFGNode<*>, isDead: Boolean = false): CFGNode<*>? { + if (lastNodes.isEmpty) return null + return addNewSimpleNode(newNode, isDead) + } + private fun addEdge(from: CFGNode<*>, to: CFGNode<*>, propagateDeadness: Boolean = true, isDead: Boolean = false) { val kind = if (isDead || from.isDead || to.isDead) EdgeKind.Dead else EdgeKind.Simple CFGNode.addEdge(from, to, kind, propagateDeadness) 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 57cd3ad3240..34cfcb4f44d 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 @@ -5,10 +5,7 @@ package org.jetbrains.kotlin.fir.resolve.dfa.cfg -import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction -import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer -import org.jetbrains.kotlin.fir.declarations.FirFunction -import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.* fun ControlFlowGraphBuilder.createStubNode(): StubNode = StubNode(graph, levelCounter, createId()) @@ -187,3 +184,6 @@ fun ControlFlowGraphBuilder.createPostponedLambdaExitNode(fir: FirAnonymousFunct fun ControlFlowGraphBuilder.createPostponedLambdaEnterNode(fir: FirAnonymousFunction): PostponedLambdaEnterNode = PostponedLambdaEnterNode(graph, fir, levelCounter, createId()) + +fun ControlFlowGraphBuilder.createAnonymousObjectExitNode(fir: FirAnonymousObject): AnonymousObjectExitNode = + AnonymousObjectExitNode(graph, fir, levelCounter, createId()) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt index 326d414832a..c20015db3bb 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt @@ -214,6 +214,8 @@ private fun CFGNode<*>.render(): String = is PostponedLambdaEnterNode -> "Postponed enter to lambda" is PostponedLambdaExitNode -> "Postponed exit from lambda" + is AnonymousObjectExitNode -> "Exit anonymous object" + else -> TODO(this@render.toString()) }, ) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index 4afb66c138a..f6508927a95 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -331,6 +331,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer) val result = withLabelAndReceiverType(null, anonymousObject, type) { transformDeclaration(anonymousObject, data) } + dataFlowAnalyzer.exitAnonymousObject(result.single as FirAnonymousObject) @Suppress("UNCHECKED_CAST") result as CompositeTransformResult } diff --git a/compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.dot b/compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.dot new file mode 100644 index 00000000000..75e3d8b1c35 --- /dev/null +++ b/compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.dot @@ -0,0 +1,170 @@ +digraph delegateWithAnonymousObject_kt { + graph [splines=ortho nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function " style="filled" fillcolor=red]; + 1 [label="Exit function " style="filled" fillcolor=red]; + } + + 0 -> {1}; + + subgraph cluster_1 { + color=red + 2 [label="Enter function delegate" style="filled" fillcolor=red]; + 3 [label="Const: Null(null)"]; + 4 [label="Check not null: Null(null)!!"]; + 5 [label="Jump: ^delegate Null(null)!!"]; + 6 [label="Stub" style="filled" fillcolor=gray]; + 7 [label="Exit function delegate" style="filled" fillcolor=red]; + } + + 2 -> {3}; + 3 -> {4}; + 4 -> {5}; + 5 -> {7}; + 5 -> {6} [style=dotted]; + 6 -> {7} [style=dotted]; + + subgraph cluster_2 { + color=red + 8 [label="Enter function " style="filled" fillcolor=red]; + 9 [label="Exit function " style="filled" fillcolor=red]; + } + + 8 -> {9}; + + subgraph cluster_3 { + color=red + 10 [label="Enter function updateFrom" style="filled" fillcolor=red]; + 11 [label="Exit function updateFrom" style="filled" fillcolor=red]; + } + + 10 -> {11}; + + subgraph cluster_4 { + color=red + 12 [label="Enter function " style="filled" fillcolor=red]; + 13 [label="Exit function " style="filled" fillcolor=red]; + } + + 12 -> {13}; + + subgraph cluster_5 { + color=red + 14 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + 15 [label="Exit anonymous object"]; + 16 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + + 14 -> {15}; + 15 -> {16}; + + subgraph cluster_6 { + color=red + 17 [label="Enter function " style="filled" fillcolor=red]; + 18 [label="Exit function " style="filled" fillcolor=red]; + } + + 17 -> {18}; + + subgraph cluster_7 { + color=red + 19 [label="Enter function getValue" style="filled" fillcolor=red]; + 20 [label="Function call: R|/IssueListView.IssueListView|()"]; + 21 [label="Jump: ^getValue R|/IssueListView.IssueListView|()"]; + 22 [label="Stub" style="filled" fillcolor=gray]; + 23 [label="Exit function getValue" style="filled" fillcolor=red]; + } + + 19 -> {20}; + 20 -> {21}; + 21 -> {23}; + 21 -> {22} [style=dotted]; + 22 -> {23} [style=dotted]; + + subgraph cluster_8 { + color=red + 24 [label="Enter function setValue" style="filled" fillcolor=red]; + 25 [label="Function call: R|/IssueListView.IssueListView|()"]; + 26 [label="Access variable R|/value|"]; + 27 [label="Function call: R|/IssueListView.IssueListView|().R|/IssueListView.updateFrom|(R|/value|)"]; + 28 [label="Jump: ^setValue R|/IssueListView.IssueListView|().R|/IssueListView.updateFrom|(R|/value|)"]; + 29 [label="Stub" style="filled" fillcolor=gray]; + 30 [label="Exit function setValue" style="filled" fillcolor=red]; + } + + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {28}; + 28 -> {30}; + 28 -> {29} [style=dotted]; + 29 -> {30} [style=dotted]; + + subgraph cluster_9 { + color=red + 31 [label="Enter function getter" style="filled" fillcolor=red]; + 32 [label="Access variable D|/IssuesListUserProfile.issueListView|"]; + 33 [label="Access variable this@R|/IssuesListUserProfile|"]; + 34 [label="Function call: D|/IssuesListUserProfile.issueListView|.R|FakeOverride|(this@R|/IssuesListUserProfile|, ::R|/IssuesListUserProfile.issueListView|)"]; + 35 [label="Jump: ^ D|/IssuesListUserProfile.issueListView|.R|FakeOverride|(this@R|/IssuesListUserProfile|, ::R|/IssuesListUserProfile.issueListView|)"]; + 36 [label="Stub" style="filled" fillcolor=gray]; + 37 [label="Exit function getter" style="filled" fillcolor=red]; + } + + 31 -> {32}; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; + 35 -> {37}; + 35 -> {36} [style=dotted]; + 36 -> {37} [style=dotted]; + + subgraph cluster_10 { + color=red + 38 [label="Enter function setter" style="filled" fillcolor=red]; + 39 [label="Access variable D|/IssuesListUserProfile.issueListView|"]; + 40 [label="Access variable this@R|/IssuesListUserProfile|"]; + 41 [label="Access variable R|/issueListView|"]; + 42 [label="Function call: D|/IssuesListUserProfile.issueListView|.R|FakeOverride|(this@R|/IssuesListUserProfile|, ::R|/IssuesListUserProfile.issueListView|, R|/issueListView|)"]; + 43 [label="Exit function setter" style="filled" fillcolor=red]; + } + + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 42 -> {43}; + + subgraph cluster_11 { + color=red + 44 [label="Enter property" style="filled" fillcolor=red]; + 45 [label="Postponed enter to lambda"]; + 46 [label="Postponed exit from lambda"]; + 47 [label="Function call: this@R|/IssuesListUserProfile|.R|/delegate|( = delegate@fun (): R|kotlin/properties/ReadWriteProperty|)"]; + 48 [label="Access variable this@R|/IssuesListUserProfile|"]; + 49 [label="Access variable this@R|/IssuesListUserProfile|"]; + 50 [label="Access variable this@R|/IssuesListUserProfile|"]; + 51 [label="Function call: this@R|/IssuesListUserProfile|.R|/delegate|( = delegate@fun (): R|kotlin/properties/ReadWriteProperty|).#(this@R|/IssuesListUserProfile|, ::R|/IssuesListUserProfile.issueListView|)"]; + 52 [label="Postponed enter to lambda"]; + 53 [label="Postponed exit from lambda"]; + 54 [label="Function call: this@R|/IssuesListUserProfile|.R|/delegate|( = delegate@fun (): R|kotlin/properties/ReadWriteProperty|)"]; + 55 [label="Exit property" style="filled" fillcolor=red]; + } + + 44 -> {45}; + 45 -> {46 46} [color=green]; + 46 -> {47}; + 47 -> {48}; + 48 -> {49}; + 49 -> {50}; + 50 -> {51}; + 51 -> {52}; + 52 -> {53 53} [color=green]; + 53 -> {54}; + 54 -> {55}; + +} diff --git a/compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.kt b/compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.kt new file mode 100644 index 00000000000..8bfe0d4bc69 --- /dev/null +++ b/compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.kt @@ -0,0 +1,28 @@ +// !DUMP_CFG + +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +abstract class DelegateProvider + +fun , Target : Any> Type.delegate( + factory: () -> ReadWriteProperty +): ReadWriteProperty = null!! + +class IssueListView : DelegateProvider() { + fun updateFrom(any: Any) {} +} + +class IssuesListUserProfile : DelegateProvider() { + var issueListView by delegate { + object : ReadWriteProperty { + override fun getValue(thisRef: IssuesListUserProfile, property: KProperty<*>): IssueListView { + return IssueListView() + } + + override fun setValue(thisRef: IssuesListUserProfile, property: KProperty<*>, value: IssueListView) { + return IssueListView().updateFrom(value) + } + } + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.txt b/compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.txt new file mode 100644 index 00000000000..4434aad01eb --- /dev/null +++ b/compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.txt @@ -0,0 +1,50 @@ +FILE: delegateWithAnonymousObject.kt + public abstract class DelegateProvider : R|kotlin/Any| { + public constructor(): R|DelegateProvider| { + super() + } + + } + public final fun |, Target : R|kotlin/Any|> R|Type|.delegate(factory: R|() -> kotlin/properties/ReadWriteProperty|): R|kotlin/properties/ReadWriteProperty| { + ^delegate Null(null)!! + } + public final class IssueListView : R|DelegateProvider| { + public constructor(): R|IssueListView| { + super|>() + } + + public final fun updateFrom(any: R|kotlin/Any|): R|kotlin/Unit| { + } + + } + public final class IssuesListUserProfile : R|DelegateProvider| { + public constructor(): R|IssuesListUserProfile| { + super|>() + } + + public final var issueListView: R|IssueListView|by this@R|/IssuesListUserProfile|.R|/delegate|( = delegate@fun (): R|kotlin/properties/ReadWriteProperty| { + ^ object : R|kotlin/properties/ReadWriteProperty| { + private constructor(): R|kotlin/Any| { + super() + } + + public final override fun getValue(thisRef: R|IssuesListUserProfile|, property: R|kotlin/reflect/KProperty<*>|): R|IssueListView| { + ^getValue R|/IssueListView.IssueListView|() + } + + public final override fun setValue(thisRef: R|IssuesListUserProfile|, property: R|kotlin/reflect/KProperty<*>|, value: R|IssueListView|): R|kotlin/Unit| { + ^setValue R|/IssueListView.IssueListView|().R|/IssueListView.updateFrom|(R|/value|) + } + + } + + } + ) + public get(): R|IssueListView| { + ^ D|/IssuesListUserProfile.issueListView|.R|FakeOverride|(this@R|/IssuesListUserProfile|, ::R|/IssuesListUserProfile.issueListView|) + } + public set(: R|IssueListView|): R|kotlin/Unit| { + D|/IssuesListUserProfile.issueListView|.R|FakeOverride|(this@R|/IssuesListUserProfile|, ::R|/IssuesListUserProfile.issueListView|, R|/issueListView|) + } + + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java index 6339b7b5981..d0c87906b83 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java @@ -88,6 +88,11 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic runTest("compiler/fir/resolve/testData/resolveWithStdlib/delegateTypeMismatch.kt"); } + @TestMetadata("delegateWithAnonymousObject.kt") + public void testDelegateWithAnonymousObject() throws Exception { + runTest("compiler/fir/resolve/testData/resolveWithStdlib/delegateWithAnonymousObject.kt"); + } + @TestMetadata("delegationByMap.kt") public void testDelegationByMap() throws Exception { runTest("compiler/fir/resolve/testData/resolveWithStdlib/delegationByMap.kt"); diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/delegates/kt31679.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/delegates/kt31679.fir.kt index 9770f4460a4..5714e3a6e27 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/inference/delegates/kt31679.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/delegates/kt31679.fir.kt @@ -16,5 +16,5 @@ private val privateObj by MyDelegate { } fun test() { - privateObj.x + privateObj.x }