FIR: fork flow on function entry, restore receivers on exit
^KT-52680 Fixed
This commit is contained in:
+6
@@ -28558,6 +28558,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt");
|
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
|
@Test
|
||||||
@TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt")
|
@TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt")
|
||||||
public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception {
|
public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception {
|
||||||
|
|||||||
+6
@@ -28558,6 +28558,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt");
|
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
|
@Test
|
||||||
@TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt")
|
@TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt")
|
||||||
public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception {
|
public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception {
|
||||||
|
|||||||
+6
@@ -28558,6 +28558,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt");
|
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
|
@Test
|
||||||
@TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt")
|
@TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt")
|
||||||
public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception {
|
public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception {
|
||||||
|
|||||||
+16
-11
@@ -269,6 +269,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
if (graphBuilder.isTopLevel()) {
|
if (graphBuilder.isTopLevel()) {
|
||||||
context.reset()
|
context.reset()
|
||||||
}
|
}
|
||||||
|
logicSystem.updateAllReceivers(graph.enterNode.computeIncomingFlow().first)
|
||||||
return FirControlFlowGraphReferenceImpl(graph, DataFlowInfo(variableStorage, flowOnNodes))
|
return FirControlFlowGraphReferenceImpl(graph, DataFlowInfo(variableStorage, flowOnNodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +283,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
val (postponedLambdaEnterNode, functionEnterNode) = graphBuilder.enterAnonymousFunction(anonymousFunction)
|
val (postponedLambdaEnterNode, functionEnterNode) = graphBuilder.enterAnonymousFunction(anonymousFunction)
|
||||||
// TODO: questionable
|
// TODO: questionable
|
||||||
postponedLambdaEnterNode?.mergeIncomingFlow()
|
postponedLambdaEnterNode?.mergeIncomingFlow()
|
||||||
functionEnterNode.mergeIncomingFlow()
|
functionEnterNode.mergeIncomingFlow(shouldForkFlow = true)
|
||||||
logicSystem.updateAllReceivers(functionEnterNode.flow)
|
logicSystem.updateAllReceivers(functionEnterNode.flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,6 +295,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
// TODO: questionable
|
// TODO: questionable
|
||||||
postponedLambdaExitNode?.mergeIncomingFlow()
|
postponedLambdaExitNode?.mergeIncomingFlow()
|
||||||
functionExitNode.mergeIncomingFlow()
|
functionExitNode.mergeIncomingFlow()
|
||||||
|
logicSystem.updateAllReceivers(graph.enterNode.computeIncomingFlow().first)
|
||||||
return FirControlFlowGraphReferenceImpl(graph)
|
return FirControlFlowGraphReferenceImpl(graph)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1512,16 +1514,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
|
|
||||||
private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) firstPreviousNode else this
|
private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) firstPreviousNode else this
|
||||||
|
|
||||||
private fun <T : CFGNode<*>> T.mergeIncomingFlow(
|
private fun <T : CFGNode<*>> T.computeIncomingFlow(): Pair<FLOW, Int> {
|
||||||
// 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 ->
|
|
||||||
val previousFlows = mutableListOf<FLOW>()
|
val previousFlows = mutableListOf<FLOW>()
|
||||||
var deadForwardCount = 0
|
var deadForwardCount = 0
|
||||||
for (previousNode in previousNodes) {
|
for (previousNode in previousNodes) {
|
||||||
val incomingEdgeKind = node.incomingEdges.getValue(previousNode).kind
|
val incomingEdgeKind = incomingEdges.getValue(previousNode).kind
|
||||||
if (node.isDead) {
|
if (isDead) {
|
||||||
if (!incomingEdgeKind.isBack) {
|
if (!incomingEdgeKind.isBack) {
|
||||||
previousFlows += previousNode.flow
|
previousFlows += previousNode.flow
|
||||||
}
|
}
|
||||||
@@ -1532,9 +1530,16 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
deadForwardCount++
|
deadForwardCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var flow = logicSystem.joinFlow(previousFlows)
|
return logicSystem.joinFlow(previousFlows) to (previousFlows.size + deadForwardCount)
|
||||||
// deadForwardCount should be added due to cases like merge after 'if (...) return else ...'
|
}
|
||||||
if (updateReceivers || previousFlows.size + deadForwardCount > 1) {
|
|
||||||
|
private fun <T : CFGNode<*>> 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)
|
logicSystem.updateAllReceivers(flow)
|
||||||
}
|
}
|
||||||
if (shouldForkFlow) {
|
if (shouldForkFlow) {
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
interface I {
|
||||||
|
val prop: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
fun f1() {
|
||||||
|
this as I
|
||||||
|
prop
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f2() {
|
||||||
|
<!UNRESOLVED_REFERENCE!>prop<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class B {
|
||||||
|
fun f() {
|
||||||
|
{
|
||||||
|
this as I
|
||||||
|
prop
|
||||||
|
}
|
||||||
|
<!UNRESOLVED_REFERENCE!>prop<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
interface I {
|
||||||
|
val prop: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
fun f1() {
|
||||||
|
this as I
|
||||||
|
<!DEBUG_INFO_IMPLICIT_RECEIVER_SMARTCAST!>prop<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f2() {
|
||||||
|
<!UNRESOLVED_REFERENCE!>prop<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class B {
|
||||||
|
fun f() {
|
||||||
|
{
|
||||||
|
this as I
|
||||||
|
<!DEBUG_INFO_IMPLICIT_RECEIVER_SMARTCAST!>prop<!>
|
||||||
|
}
|
||||||
|
<!UNRESOLVED_REFERENCE!>prop<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
fun x(): Boolean { return true }
|
fun x(): Boolean { return true }
|
||||||
|
|
||||||
public fun foo(p: String?): Int {
|
public fun foo(p: String?): Int {
|
||||||
|
|||||||
Generated
+6
@@ -28648,6 +28648,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/implicitReceiver.kt");
|
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
|
@Test
|
||||||
@TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt")
|
@TestMetadata("implicitThisOnRefInLambdaInSmartcast.kt")
|
||||||
public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception {
|
public void testImplicitThisOnRefInLambdaInSmartcast() throws Exception {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ fun case_2(x: Int?) = 10
|
|||||||
fun case_2() {
|
fun case_2() {
|
||||||
var x: Int? = 10
|
var x: Int? = 10
|
||||||
var y = { x = null }
|
var y = { x = null }
|
||||||
if (<!SENSELESS_COMPARISON!>x != null<!>) {
|
if (x != null) {
|
||||||
val z = case_2(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Nothing")!>x<!>)
|
val z = case_2(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>x<!>)
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>z<!>
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>z<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user