From 9c7058c3c5ac2fbc53162b58217b75eb90f7e6af Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 22 Nov 2021 15:41:22 +0300 Subject: [PATCH] [FIR] Support fields as part of graph of class initialization ^KT-49747 Fixed --- ...nosisCompilerFirTestdataTestGenerated.java | 6 + ...TouchedTilContractsPhaseTestGenerated.java | 5 + .../smartcasts/smartcastInByClause.dot | 244 ++++++++++++++++++ .../smartcasts/smartcastInByClause.fir.txt | 46 ++++ .../resolve/smartcasts/smartcastInByClause.kt | 21 ++ .../runners/FirDiagnosticTestGenerated.java | 6 + ...DiagnosticsWithLightTreeTestGenerated.java | 6 + .../dfa/cfg/ControlFlowGraphBuilder.kt | 3 +- .../FirDeclarationsResolveTransformer.kt | 4 +- .../body/resolve/LocalClassesNavigation.kt | 4 + 10 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.dot create mode 100644 compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java index b33ca6565bd..f446231187f 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java @@ -3527,6 +3527,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartCastInInit.kt"); } + @Test + @TestMetadata("smartcastInByClause.kt") + public void testSmartcastInByClause() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.kt"); + } + @Test @TestMetadata("smartcastToNothing.kt") public void testSmartcastToNothing() throws Exception { diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java index a89f3a798b2..fedfa35fc62 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java @@ -3119,6 +3119,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartCastInInit.kt"); } + @TestMetadata("smartcastInByClause.kt") + public void testSmartcastInByClause() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.kt"); + } + @TestMetadata("smartcastToNothing.kt") public void testSmartcastToNothing() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastToNothing.kt"); diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.dot b/compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.dot new file mode 100644 index 00000000000..f225f3aec8f --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.dot @@ -0,0 +1,244 @@ +digraph smartcastInByClause_kt { + graph [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="Delegated constructor call: super()"]; + 2 [label="Exit function " style="filled" fillcolor=red]; + } + 0 -> {1}; + 1 -> {2}; + + subgraph cluster_1 { + color=red + 3 [label="Enter property" style="filled" fillcolor=red]; + 4 [label="Access variable R|/path|"]; + 5 [label="Exit property" style="filled" fillcolor=red]; + } + 3 -> {4}; + 4 -> {5}; + 5 -> {11} [color=green]; + + subgraph cluster_2 { + color=red + 6 [label="Enter property" style="filled" fillcolor=red]; + 7 [label="Access variable R|/index|"]; + 8 [label="Exit property" style="filled" fillcolor=red]; + } + 6 -> {7}; + 7 -> {8}; + 8 -> {12} [color=green]; + + subgraph cluster_3 { + color=red + 9 [label="Enter class A" style="filled" fillcolor=red]; + 10 [label="Part of class initialization"]; + 11 [label="Part of class initialization"]; + 12 [label="Exit class A" style="filled" fillcolor=red]; + } + 9 -> {10} [color=green]; + 10 -> {11} [style=dotted]; + 10 -> {3} [color=green]; + 10 -> {3} [style=dashed]; + 11 -> {12} [style=dotted]; + 11 -> {6} [color=green]; + 11 -> {6} [style=dashed]; + + subgraph cluster_4 { + color=red + 13 [label="Enter class Base" style="filled" fillcolor=red]; + 14 [label="Exit class Base" style="filled" fillcolor=red]; + } + 13 -> {14} [color=green]; + + subgraph cluster_5 { + color=red + 15 [label="Enter function " style="filled" fillcolor=red]; + 16 [label="Delegated constructor call: super()"]; + 17 [label="Exit function " style="filled" fillcolor=red]; + } + 15 -> {16}; + 16 -> {17}; + + subgraph cluster_6 { + color=red + 18 [label="Enter property" style="filled" fillcolor=red]; + 19 [label="Access variable R|/index|"]; + 20 [label="Exit property" style="filled" fillcolor=red]; + } + 18 -> {19}; + 19 -> {20}; + 20 -> {23} [color=green]; + + subgraph cluster_7 { + color=red + 21 [label="Enter class Derived" style="filled" fillcolor=red]; + 22 [label="Part of class initialization"]; + 23 [label="Exit class Derived" style="filled" fillcolor=red]; + } + 21 -> {22} [color=green]; + 22 -> {23} [style=dotted]; + 22 -> {18} [color=green]; + 22 -> {18} [style=dashed]; + + subgraph cluster_8 { + color=red + 24 [label="Enter function test" style="filled" fillcolor=red]; + subgraph cluster_9 { + color=blue + 25 [label="Enter block"]; + 26 [label="Access variable R|/a|"]; + 27 [label="Enter safe call"]; + 28 [label="Access variable R|/A.path|"]; + 29 [label="Exit safe call"]; + 30 [label="Exit lhs of ?:"]; + 31 [label="Enter rhs of ?:"]; + 32 [label="Const: Null(null)"]; + 33 [label="Jump: ^test Null(null)"]; + 34 [label="Stub" style="filled" fillcolor=gray]; + 35 [label="Lhs of ?: is not null"]; + 36 [label="Exit ?:"]; + 37 [label="Variable declaration: lval path: R|kotlin/String|"]; + 38 [label="Access variable R|/a|"]; + 39 [label="Access variable R|/A.index|"]; + 40 [label="Function call: R|/takeInt|(...)"]; + 41 [label="Exit anonymous object"]; + 42 [label="Exit anonymous object expression"]; + 43 [label="Jump: ^test object : R|Base| { + private constructor(): R|| { + super() + } + + local final field <$$delegate_0>: R|Base| = R|/Derived.Derived|(R|/a|.R|/A.index|) + + public final val x: R|kotlin/Int| = R|/a|.R|/A.index| + public get(): R|kotlin/Int| + + public final fun foo(): R|kotlin/Unit| { + R|/takeInt|(R|/a|.R|/A.index|) + } + +} +"]; + 44 [label="Stub" style="filled" fillcolor=gray]; + 45 [label="Exit block" style="filled" fillcolor=gray]; + } + 46 [label="Exit function test" style="filled" fillcolor=red]; + } + subgraph cluster_10 { + color=blue + 47 [label="Enter class " style="filled" fillcolor=red]; + 48 [label="Part of class initialization"]; + 49 [label="Part of class initialization"]; + 50 [label="Exit class " style="filled" fillcolor=red]; + } + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 26 -> {29} [color=red]; + 26 -> {31} [color=green]; + 27 -> {28}; + 28 -> {29}; + 29 -> {30}; + 30 -> {35 31}; + 31 -> {32}; + 32 -> {33}; + 33 -> {46}; + 33 -> {34} [style=dotted]; + 34 -> {36} [style=dotted]; + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 40 -> {51 54 59 63} [color=red]; + 41 -> {42}; + 41 -> {51 63 47} [color=green]; + 41 -> {51 63 47} [style=dashed]; + 42 -> {43}; + 43 -> {46}; + 43 -> {44} [style=dotted]; + 44 -> {45} [style=dotted]; + 45 -> {46} [style=dotted]; + 47 -> {48} [color=green]; + 48 -> {49} [style=dotted]; + 48 -> {54} [color=green]; + 48 -> {54} [style=dashed]; + 49 -> {50} [style=dotted]; + 49 -> {59} [color=green]; + 49 -> {59} [style=dashed]; + + subgraph cluster_11 { + color=red + 51 [label="Enter function " style="filled" fillcolor=red]; + 52 [label="Delegated constructor call: super()"]; + 53 [label="Exit function " style="filled" fillcolor=red]; + } + 51 -> {52}; + 52 -> {53}; + + subgraph cluster_12 { + color=red + 54 [label="Enter field" style="filled" fillcolor=red]; + 55 [label="Access variable R|/a|"]; + 56 [label="Access variable R|/A.index|"]; + 57 [label="Function call: R|/Derived.Derived|(...)"]; + 58 [label="Exit field" style="filled" fillcolor=red]; + } + 54 -> {55}; + 55 -> {56}; + 56 -> {57}; + 57 -> {58}; + 58 -> {49} [color=green]; + + subgraph cluster_13 { + color=red + 59 [label="Enter property" style="filled" fillcolor=red]; + 60 [label="Access variable R|/a|"]; + 61 [label="Access variable R|/A.index|"]; + 62 [label="Exit property" style="filled" fillcolor=red]; + } + 59 -> {60}; + 60 -> {61}; + 61 -> {62}; + 62 -> {50} [color=green]; + + subgraph cluster_14 { + color=red + 63 [label="Enter function foo" style="filled" fillcolor=red]; + subgraph cluster_15 { + color=blue + 64 [label="Enter block"]; + 65 [label="Access variable R|/a|"]; + 66 [label="Access variable R|/A.index|"]; + 67 [label="Function call: R|/takeInt|(...)"]; + 68 [label="Exit block"]; + } + 69 [label="Exit function foo" style="filled" fillcolor=red]; + } + 63 -> {64}; + 64 -> {65}; + 65 -> {66}; + 66 -> {67}; + 67 -> {68}; + 68 -> {69}; + + subgraph cluster_16 { + color=red + 70 [label="Enter function takeInt" style="filled" fillcolor=red]; + subgraph cluster_17 { + color=blue + 71 [label="Enter block"]; + 72 [label="Exit block"]; + } + 73 [label="Exit function takeInt" style="filled" fillcolor=red]; + } + 70 -> {71}; + 71 -> {72}; + 72 -> {73}; + +} diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.fir.txt b/compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.fir.txt new file mode 100644 index 00000000000..7c4d0f5d0aa --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.fir.txt @@ -0,0 +1,46 @@ +FILE: smartcastInByClause.kt + public final class A : R|kotlin/Any| { + public constructor(path: R|kotlin/String?|, index: R|kotlin/Int|): R|A| { + super() + } + + public final val path: R|kotlin/String?| = R|/path| + public get(): R|kotlin/String?| + + public final val index: R|kotlin/Int| = R|/index| + public get(): R|kotlin/Int| + + } + public abstract interface Base : R|kotlin/Any| { + } + public final class Derived : R|Base| { + public constructor(index: R|kotlin/Int|): R|Derived| { + super() + } + + public final val index: R|kotlin/Int| = R|/index| + public get(): R|kotlin/Int| + + } + public final fun test(a: R|A?|): R|Base?| { + lval path: R|kotlin/String| = R|/a|?.{ $subj$.R|/A.path| } ?: ^test Null(null) + R|/takeInt|(R|/a|.R|/A.index|) + ^test object : R|Base| { + private constructor(): R|| { + super() + } + + local final field <$$delegate_0>: R|Base| = R|/Derived.Derived|(R|/a|.R|/A.index|) + + public final val x: R|kotlin/Int| = R|/a|.R|/A.index| + public get(): R|kotlin/Int| + + public final fun foo(): R|kotlin/Unit| { + R|/takeInt|(R|/a|.R|/A.index|) + } + + } + + } + public final fun takeInt(x: R|kotlin/Int|): R|kotlin/Unit| { + } diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.kt b/compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.kt new file mode 100644 index 00000000000..0ce7e645d12 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.kt @@ -0,0 +1,21 @@ +// ISSUE: KT-49747 +// DUMP_CFG + +class A(val path: String?, val index: Int) + +interface Base +class Derived(val index: Int) : Base + +fun test(a: A?): Base? { + val path = a?.path ?: return null + takeInt(a.index) // should be ok + return object : Base by Derived(a.index) { + val x: Int = a.index + + fun foo() { + takeInt(a.index) + } + } +} + +fun takeInt(x: Int) {} diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index 28a6f69f82f..1b3d221b651 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -3527,6 +3527,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartCastInInit.kt"); } + @Test + @TestMetadata("smartcastInByClause.kt") + public void testSmartcastInByClause() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.kt"); + } + @Test @TestMetadata("smartcastToNothing.kt") public void testSmartcastToNothing() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index 9f92744ad77..fc20616fc73 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -3527,6 +3527,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartCastInInit.kt"); } + @Test + @TestMetadata("smartcastInByClause.kt") + public void testSmartcastInByClause() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/smartcastInByClause.kt"); + } + @Test @TestMetadata("smartcastToNothing.kt") public void testSmartcastToNothing() throws Exception { 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 e5859663db3..b02d248d65d 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 @@ -408,6 +408,7 @@ class ControlFlowGraphBuilder { for (declaration in klass.declarations) { val graph = when (declaration) { is FirProperty -> declaration.controlFlowGraphReference?.controlFlowGraph + is FirField -> declaration.controlFlowGraphReference?.controlFlowGraph is FirAnonymousInitializer -> declaration.controlFlowGraphReference?.controlFlowGraph else -> null } ?: continue @@ -1556,7 +1557,7 @@ class ControlFlowGraphBuilder { private fun FirDeclaration.unwrap(): List = when (this) { - is FirFunction, is FirAnonymousInitializer -> listOf(this) + is FirFunction, is FirAnonymousInitializer, is FirField -> listOf(this) is FirProperty -> listOfNotNull(this.getter, this.setter, this) else -> emptyList() } 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 45a94d2907c..fb22d3156cf 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 @@ -215,7 +215,9 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor if (field.initializer != null) { storeVariableReturnType(field) } - dataFlowAnalyzer.exitField(field) + dataFlowAnalyzer.exitField(field)?.let { + field.replaceControlFlowGraphReference(FirControlFlowGraphReferenceImpl(it)) + } field } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/LocalClassesNavigation.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/LocalClassesNavigation.kt index c868892b8d3..8f2e7e9bc30 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/LocalClassesNavigation.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/LocalClassesNavigation.kt @@ -75,6 +75,10 @@ private class NavigationInfoVisitor : FirDefaultVisitor() { visitCallableDeclaration(property, null) } + override fun visitField(field: FirField, data: Any?) { + visitCallableDeclaration(field, null) + } + override fun visitConstructor(constructor: FirConstructor, data: Any?) { visitCallableDeclaration(constructor, null) }