From 7c4f59dfcbe5586d0d749e23416ef982ff13871b Mon Sep 17 00:00:00 2001 From: "simon.ogorodnik" Date: Tue, 25 Feb 2020 17:45:49 +0300 Subject: [PATCH] [FIR] KT-37009: Fix loss of bound smart-cast due to synthetic removal --- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 2 +- .../fir/resolve/dfa/PersistentLogicSystem.kt | 1 + .../boundSmartcasts/functionCallBound.dot | 139 ++++++++++++++++++ .../boundSmartcasts/functionCallBound.kt | 18 +++ .../boundSmartcasts/functionCallBound.txt | 30 ++++ .../fir/FirDiagnosticsTestGenerated.java | 5 + ...DiagnosticsWithLightTreeTestGenerated.java | 5 + .../smartCasts/castchecks/basicOff.fir.kt | 4 +- .../smartCasts/castchecks/basicOn.fir.kt | 4 +- .../smartCasts/castchecks/variables.fir.kt | 4 +- .../safeAccessReceiverNotNull.fir.kt | 2 +- 11 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.dot create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.kt create mode 100644 compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.txt 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 22d805bff06..024902eefe9 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 @@ -552,7 +552,7 @@ abstract class FirDataFlowAnalyzer( flow, variable notEq null, shouldFork, - shouldRemoveSynthetics = true + shouldRemoveSynthetics = false ) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt index c14a89329bd..badb441b6e1 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/PersistentLogicSystem.kt @@ -257,6 +257,7 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste } override fun removeAllAboutVariable(flow: PersistentFlow, variable: RealVariable) { + flow.logicStatements -= variable flow.approvedTypeStatements -= variable flow.approvedTypeStatementsDiff -= variable // TODO: should we search variable in all logic statements? diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.dot b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.dot new file mode 100644 index 00000000000..c5df2f6f227 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.dot @@ -0,0 +1,139 @@ +digraph functionCallBound_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 " style="filled" fillcolor=red]; + 3 [label="Exit function " style="filled" fillcolor=red]; + } + + 2 -> {3}; + + subgraph cluster_2 { + color=red + 4 [label="Enter function getter" style="filled" fillcolor=red]; + 5 [label="Exit function getter" style="filled" fillcolor=red]; + } + + 4 -> {5}; + + subgraph cluster_3 { + color=red + 6 [label="Enter property" style="filled" fillcolor=red]; + 7 [label="Access variable R|/data|"]; + 8 [label="Exit property" style="filled" fillcolor=red]; + } + + 6 -> {7}; + 7 -> {8}; + + subgraph cluster_4 { + color=red + 9 [label="Enter function isOk" style="filled" fillcolor=red]; + 10 [label="Const: Boolean(true)"]; + 11 [label="Jump: ^isOk Boolean(true)"]; + 12 [label="Stub" style="filled" fillcolor=gray]; + 13 [label="Exit function isOk" style="filled" fillcolor=red]; + } + + 9 -> {10}; + 10 -> {11}; + 11 -> {13}; + 11 -> {12} [style=dotted]; + 12 -> {13} [style=dotted]; + + subgraph cluster_5 { + color=red + 14 [label="Enter function check" style="filled" fillcolor=red]; + subgraph cluster_6 { + color=blue + 15 [label="Enter when"]; + subgraph cluster_7 { + color=blue + 16 [label="Enter when branch condition "]; + 17 [label="Access variable R|/base|"]; + 18 [label="Type operator: (R|/base| as? R|Sub|)"]; + 19 [label="Enter safe call"]; + 20 [label="Function call: (R|/base| as? R|Sub|)?.R|/isOk|()"]; + 21 [label="Exit safe call"]; + 22 [label="Const: Boolean(true)"]; + 23 [label="Operator =="]; + 24 [label="Exit when branch condition"]; + } + subgraph cluster_8 { + color=blue + 25 [label="Enter when branch condition else"]; + 26 [label="Exit when branch condition"]; + } + 27 [label="Enter when branch result"]; + subgraph cluster_9 { + color=blue + 28 [label="Enter block"]; + 29 [label="Access variable R|/base|"]; + 30 [label="Exit block"]; + } + 31 [label="Exit when branch result"]; + 32 [label="Enter when branch result"]; + subgraph cluster_10 { + color=blue + 33 [label="Enter block"]; + 34 [label="Access variable R|/base|"]; + 35 [label="Access variable R|/Sub.data|"]; + 36 [label="Exit block"]; + } + 37 [label="Exit when branch result"]; + 38 [label="Exit when"]; + } + 39 [label="Jump: ^check when () { + ==((R|/base| as? R|Sub|)?.R|/isOk|(), Boolean(true)) -> { + R|/base|.R|/Sub.data| + } + else -> { + R|/base| + } +} +"]; + 40 [label="Stub" style="filled" fillcolor=gray]; + 41 [label="Exit function check" style="filled" fillcolor=red]; + } + + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19 21}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {23}; + 23 -> {24}; + 24 -> {32 25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {28}; + 28 -> {29}; + 29 -> {30}; + 30 -> {31}; + 31 -> {38}; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {41}; + 39 -> {40} [style=dotted]; + 40 -> {41} [style=dotted]; + +} diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.kt b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.kt new file mode 100644 index 00000000000..d40567e90db --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.kt @@ -0,0 +1,18 @@ +// !DUMP_CFG + +open class Base +class Sub(val data: Base): Base() + +fun Sub.isOk() = true + +fun check(base: Base): Base = + when { + (base as? Sub)?.isOk() == true -> { + base.data + } + else -> { + base + } + } + + diff --git a/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.txt b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.txt new file mode 100644 index 00000000000..968bc4758ac --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.txt @@ -0,0 +1,30 @@ +FILE: functionCallBound.kt + public open class Base : R|kotlin/Any| { + public constructor(): R|Base| { + super() + } + + } + public final class Sub : R|Base| { + public constructor(data: R|Base|): R|Sub| { + super() + } + + public final val data: R|Base| = R|/data| + public get(): R|Base| + + } + public final fun R|Sub|.isOk(): R|kotlin/Boolean| { + ^isOk Boolean(true) + } + public final fun check(base: R|Base|): R|Base| { + ^check when () { + ==((R|/base| as? R|Sub|)?.R|/isOk|(), Boolean(true)) -> { + R|/base|.R|/Sub.data| + } + else -> { + R|/base| + } + } + + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 4a9f985089e..071cbfc2265 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -1535,6 +1535,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { public void testBoundSmartcastsInBranches() throws Exception { runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/boundSmartcastsInBranches.kt"); } + + @TestMetadata("functionCallBound.kt") + public void testFunctionCallBound() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.kt"); + } } @TestMetadata("compiler/fir/resolve/testData/resolve/smartcasts/controlStructures") diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index 856e697c43a..334f742249f 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -1535,6 +1535,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos public void testBoundSmartcastsInBranches() throws Exception { runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/boundSmartcastsInBranches.kt"); } + + @TestMetadata("functionCallBound.kt") + public void testFunctionCallBound() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/smartcasts/boundSmartcasts/functionCallBound.kt"); + } } @TestMetadata("compiler/fir/resolve/testData/resolve/smartcasts/controlStructures") diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/basicOff.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/basicOff.fir.kt index 76e4a3875e1..59df9520ef5 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/basicOff.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/basicOff.fir.kt @@ -19,8 +19,8 @@ fun g(a: SomeClass?) { val b = (a as? SomeSubClass)?.foo if (b != null) { // 'a' can be cast to SomeSubClass - a.hashCode() - a.foo + a.hashCode() + a.foo (a as? SomeSubClass).foo (a as SomeSubClass).foo } diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/basicOn.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/basicOn.fir.kt index ad78556a7ef..dba21c5cad0 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/basicOn.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/basicOn.fir.kt @@ -19,8 +19,8 @@ fun g(a: SomeClass?) { val b = (a as? SomeSubClass)?.foo if (b != null) { // 'a' can be cast to SomeSubClass - a.hashCode() - a.foo + a.hashCode() + a.foo (a as? SomeSubClass).foo (a as SomeSubClass).foo } diff --git a/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt index 489665f3578..254fdd6b891 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/castchecks/variables.fir.kt @@ -27,8 +27,8 @@ fun g(a: SomeClass?) { c = Impl if (c != null) { // 'a' cannot be cast to SomeSubClass - a.hashCode() - a.foo + a.hashCode() + a.foo (a as? SomeSubClass).foo c.hashCode() c.foo diff --git a/compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.fir.kt index c374fd5a0f6..a38f0c4c6a0 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/safecalls/safeAccessReceiverNotNull.fir.kt @@ -63,7 +63,7 @@ fun kt4565_2(a: SomeClass?) { } val extra = (a as? SubClass)?.extra if (extra != null) { - a.extra.hashCode() + a.extra.hashCode() } }