From 9968fa252aa1fb0734407abe39da39f240d1bc1f Mon Sep 17 00:00:00 2001 From: pyos Date: Thu, 9 Jun 2022 11:34:59 +0200 Subject: [PATCH] FIR: fork flow on function entry, restore receivers on exit ^KT-52680 Fixed --- ...CompilerTestFE10TestdataTestGenerated.java | 6 +++++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 +++++ .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 27 +++++++++++-------- .../implicitReceiverAcrossFunctions.fir.kt | 24 +++++++++++++++++ .../implicitReceiverAcrossFunctions.kt | 24 +++++++++++++++++ .../implicitReceiverAcrossFunctions.txt | 25 +++++++++++++++++ .../smartCasts/loops/doWhileLiteral.fir.kt | 10 ------- .../tests/smartCasts/loops/doWhileLiteral.kt | 1 + .../test/runners/DiagnosticTestGenerated.java | 6 +++++ .../diagnostics/notLinked/dfa/pos/53.fir.kt | 4 +-- 11 files changed, 116 insertions(+), 23 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.fir.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.txt delete mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.fir.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 6dace391812..dee506d13f4 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -28558,6 +28558,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt"); } + @Test + @TestMetadata("implicitReceiverAcrossFunctions.kt") + public void testImplicitReceiverAcrossFunctions() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.kt"); + } + @Test @TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt") public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index cae8672a7ae..4171c41577e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -28558,6 +28558,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt"); } + @Test + @TestMetadata("implicitReceiverAcrossFunctions.kt") + public void testImplicitReceiverAcrossFunctions() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.kt"); + } + @Test @TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt") public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index e4a82448ecb..3306ab7d052 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -28558,6 +28558,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt"); } + @Test + @TestMetadata("implicitReceiverAcrossFunctions.kt") + public void testImplicitReceiverAcrossFunctions() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.kt"); + } + @Test @TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt") public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception { 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 ba16b6123e6..d26f2b271b6 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 @@ -269,6 +269,7 @@ abstract class FirDataFlowAnalyzer( if (graphBuilder.isTopLevel()) { context.reset() } + logicSystem.updateAllReceivers(graph.enterNode.computeIncomingFlow().first) return FirControlFlowGraphReferenceImpl(graph, DataFlowInfo(variableStorage, flowOnNodes)) } @@ -282,7 +283,7 @@ abstract class FirDataFlowAnalyzer( val (postponedLambdaEnterNode, functionEnterNode) = graphBuilder.enterAnonymousFunction(anonymousFunction) // TODO: questionable postponedLambdaEnterNode?.mergeIncomingFlow() - functionEnterNode.mergeIncomingFlow() + functionEnterNode.mergeIncomingFlow(shouldForkFlow = true) logicSystem.updateAllReceivers(functionEnterNode.flow) } @@ -294,6 +295,7 @@ abstract class FirDataFlowAnalyzer( // TODO: questionable postponedLambdaExitNode?.mergeIncomingFlow() functionExitNode.mergeIncomingFlow() + logicSystem.updateAllReceivers(graph.enterNode.computeIncomingFlow().first) return FirControlFlowGraphReferenceImpl(graph) } @@ -1512,16 +1514,12 @@ abstract class FirDataFlowAnalyzer( private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) firstPreviousNode else this - private fun > T.mergeIncomingFlow( - // This flag should be set true if we're changing flow branches from one to another (e.g. in when, try->catch) - updateReceivers: Boolean = false, - shouldForkFlow: Boolean = false - ): T = this.also { node -> + private fun > T.computeIncomingFlow(): Pair { val previousFlows = mutableListOf() var deadForwardCount = 0 for (previousNode in previousNodes) { - val incomingEdgeKind = node.incomingEdges.getValue(previousNode).kind - if (node.isDead) { + val incomingEdgeKind = incomingEdges.getValue(previousNode).kind + if (isDead) { if (!incomingEdgeKind.isBack) { previousFlows += previousNode.flow } @@ -1532,9 +1530,16 @@ abstract class FirDataFlowAnalyzer( deadForwardCount++ } } - var flow = logicSystem.joinFlow(previousFlows) - // deadForwardCount should be added due to cases like merge after 'if (...) return else ...' - if (updateReceivers || previousFlows.size + deadForwardCount > 1) { + return logicSystem.joinFlow(previousFlows) to (previousFlows.size + deadForwardCount) + } + + private fun > T.mergeIncomingFlow( + // This flag should be set true if we're changing flow branches from one to another (e.g. in when, try->catch) + updateReceivers: Boolean = false, + shouldForkFlow: Boolean = false + ): T = this.also { node -> + var (flow, incomingEdges) = computeIncomingFlow() + if (updateReceivers || incomingEdges > 1) { logicSystem.updateAllReceivers(flow) } if (shouldForkFlow) { diff --git a/compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.fir.kt new file mode 100644 index 00000000000..c6d94b68000 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.fir.kt @@ -0,0 +1,24 @@ +interface I { + val prop: Int +} + +open class A { + fun f1() { + this as I + prop + } + + fun f2() { + prop + } +} + +open class B { + fun f() { + { + this as I + prop + } + prop + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.kt b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.kt new file mode 100644 index 00000000000..77c065b146e --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.kt @@ -0,0 +1,24 @@ +interface I { + val prop: Int +} + +open class A { + fun f1() { + this as I + prop + } + + fun f2() { + prop + } +} + +open class B { + fun f() { + { + this as I + prop + } + prop + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.txt b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.txt new file mode 100644 index 00000000000..10222178bf5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.txt @@ -0,0 +1,25 @@ +package + +public open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun f1(): kotlin.Unit + public final fun f2(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class B { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I { + public abstract val prop: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.fir.kt deleted file mode 100644 index 901f189edd5..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.fir.kt +++ /dev/null @@ -1,10 +0,0 @@ -fun x(): Boolean { return true } - -public fun foo(p: String?): Int { - // Exotic variant with unused literal - do { -> - p!!.length - } while (!x()) - // Literal is not called so p.length is unsafe - return p.length -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.kt index f1b81d051c9..f932893f475 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/doWhileLiteral.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun x(): Boolean { return true } public fun foo(p: String?): Int { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 15594ef0165..15a17fc9fee 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -28648,6 +28648,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt"); } + @Test + @TestMetadata("implicitReceiverAcrossFunctions.kt") + public void testImplicitReceiverAcrossFunctions() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiverAcrossFunctions.kt"); + } + @Test @TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt") public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception { diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt index 084beb278d0..501fe32d597 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt @@ -19,8 +19,8 @@ fun case_2(x: Int?) = 10 fun case_2() { var x: Int? = 10 var y = { x = null } - if (x != null) { - val z = case_2(x) + if (x != null) { + val z = case_2(x) z } }