diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt index 98e51187b4e..1580d30b115 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt @@ -28,23 +28,39 @@ class CallResolver( private fun processImplicitReceiver( towerDataConsumer: TowerDataConsumer, implicitReceiverValue: ImplicitReceiverValue, - collector: CandidateCollector, oldGroup: Int ): Int { var group = oldGroup + // Member (no explicit receiver) / extension member (with explicit receiver) access via implicit receiver + // class Foo(val x: Int) { + // fun Bar.baz() {} + // fun test() { x } + // fun test(b: Bar) { b.baz() } + // } towerDataConsumer.consume( TowerDataKind.TOWER_LEVEL, MemberScopeTowerLevel(session, implicitReceiverValue, scopeSession = components.scopeSession), group++ ) - // This is an equivalent to the old "BothTowerLevelAndImplicitReceiver" + // Same receiver is dispatch & extension +// class Foo { +// fun Foo.bar() {} +// fun test() { bar() } +// } towerDataConsumer.consume( TowerDataKind.TOWER_LEVEL, MemberScopeTowerLevel(session, implicitReceiverValue, implicitReceiverValue, components.scopeSession), group++ ) + // Local scope extensions via implicit receiver + // class Foo { + // fun test() { + // fun Foo.bar() {} + // bar() + // } + // } for (scope in localScopes) { towerDataConsumer.consume( TowerDataKind.TOWER_LEVEL, @@ -53,9 +69,18 @@ class CallResolver( ) } + var blockDispatchReceivers = false + for (implicitDispatchReceiverValue in implicitReceiverValues) { val implicitScope = implicitDispatchReceiverValue.implicitScope if (implicitScope != null) { + // Extensions in outer object + // object Outer { + // fun Nested.foo() {} + // class Nested { + // fun test() { foo() } + // } + // } towerDataConsumer.consume( TowerDataKind.TOWER_LEVEL, ScopeTowerLevel(session, implicitScope, implicitExtensionReceiver = implicitReceiverValue), @@ -65,15 +90,33 @@ class CallResolver( if (implicitDispatchReceiverValue is ImplicitDispatchReceiverValue) { val implicitCompanionScope = implicitDispatchReceiverValue.implicitCompanionScope if (implicitCompanionScope != null) { + // Extension in companion + // class My { + // companion object { fun My.foo() {} } + // fun test() { foo() } + // } towerDataConsumer.consume( TowerDataKind.TOWER_LEVEL, ScopeTowerLevel(session, implicitCompanionScope, implicitExtensionReceiver = implicitReceiverValue), group++ ) } + if (blockDispatchReceivers) { + continue + } + if (!implicitDispatchReceiverValue.boundSymbol.fir.isInner) { + blockDispatchReceivers = true + } } if (implicitDispatchReceiverValue !== implicitReceiverValue) { - // Two different implicit receivers + // Two different implicit receivers (dispatch & extension) + // class A + // class B { + // fun A.foo() {} + // } + // fun test(a: A, b: B) { + // with(a) { with(b) { foo() } } + // } towerDataConsumer.consume( TowerDataKind.TOWER_LEVEL, MemberScopeTowerLevel( @@ -86,6 +129,11 @@ class CallResolver( } } + // Top-level extensions via implicit receiver + // fun Foo.bar() {} + // class Foo { + // fun test() { bar() } + // } for (scope in topLevelScopes) { towerDataConsumer.consume( TowerDataKind.TOWER_LEVEL, @@ -106,25 +154,38 @@ class CallResolver( towerDataConsumer = consumer var group = 0 - // Member of explicit receiver' type + // Member of explicit receiver' type (this stage does nothing without explicit receiver) + // class Foo(val x: Int) + // fun test(f: Foo) { f.x } towerDataConsumer.consume(TowerDataKind.EMPTY, TowerScopeLevel.Empty, group++) // Member of local scope + // fun test(x: Int) = x for (scope in localScopes) { towerDataConsumer.consume(TowerDataKind.TOWER_LEVEL, ScopeTowerLevel(session, scope), group++) } var blockDispatchReceivers = false - // Member of implicit receiver' type + // Member of implicit receiver' type *and* relevant scope for (implicitReceiverValue in implicitReceiverValues) { val implicitScope = implicitReceiverValue.implicitScope if (implicitScope != null) { + // Regular implicit receiver scope (outer objects only?) + // object Outer { + // val x = 0 + // class Nested { val y = x } + // } towerDataConsumer.consume(TowerDataKind.TOWER_LEVEL, ScopeTowerLevel(session, implicitScope), group++) } if (implicitReceiverValue is ImplicitDispatchReceiverValue) { val implicitCompanionScope = implicitReceiverValue.implicitCompanionScope if (implicitCompanionScope != null) { + // Companion scope bound to implicit receiver scope + // class Outer { + // companion object { val x = 0 } + // class Nested { val y = x } + // } towerDataConsumer.consume(TowerDataKind.TOWER_LEVEL, ScopeTowerLevel(session, implicitCompanionScope), group++) } if (blockDispatchReceivers) { @@ -134,10 +195,13 @@ class CallResolver( blockDispatchReceivers = true } } - group = processImplicitReceiver(towerDataConsumer, implicitReceiverValue, collector, group) + // Direct use of implicit receiver (see inside) + group = processImplicitReceiver(towerDataConsumer, implicitReceiverValue, group) } - // Member of top-level scope + // Member of top-level scope & importing scope + // val x = 0 + // fun test() { x } for (scope in topLevelScopes) { towerDataConsumer.consume(TowerDataKind.TOWER_LEVEL, ScopeTowerLevel(session, scope), group++) } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/companionExtension.kt b/compiler/fir/resolve/testData/resolve/expresssions/companionExtension.kt new file mode 100644 index 00000000000..331a2640158 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/companionExtension.kt @@ -0,0 +1,9 @@ +class My { + companion object { + fun My.foo() {} + } + + fun test() { + foo() + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/companionExtension.txt b/compiler/fir/resolve/testData/resolve/expresssions/companionExtension.txt new file mode 100644 index 00000000000..51fe6223fcf --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/companionExtension.txt @@ -0,0 +1,21 @@ +FILE: companionExtension.kt + public final class My : R|kotlin/Any| { + public constructor(): R|My| { + super() + } + + public final companion object Companion : R|kotlin/Any| { + private constructor(): R|My.Companion| { + super() + } + + public final fun R|My|.foo(): R|kotlin/Unit| { + } + + } + + public final fun test(): R|kotlin/Unit| { + R|/My.Companion.foo|() + } + + } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/localExtension.kt b/compiler/fir/resolve/testData/resolve/expresssions/localExtension.kt new file mode 100644 index 00000000000..fbb385a688f --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/localExtension.kt @@ -0,0 +1,6 @@ + class Foo { + fun test() { + fun Foo.bar() {} + bar() + } + } \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/localExtension.txt b/compiler/fir/resolve/testData/resolve/expresssions/localExtension.txt new file mode 100644 index 00000000000..936c16306ab --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/localExtension.txt @@ -0,0 +1,14 @@ +FILE: localExtension.kt + public final class Foo : R|kotlin/Any| { + public constructor(): R|Foo| { + super() + } + + public final fun test(): R|kotlin/Unit| { + local final fun R|Foo|.bar(): R|kotlin/Unit| { + } + + R|/bar|() + } + + } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/outerObject.kt b/compiler/fir/resolve/testData/resolve/expresssions/outerObject.kt new file mode 100644 index 00000000000..fd99ecf1d29 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/outerObject.kt @@ -0,0 +1,10 @@ + object Outer { + val x = 0 + fun Nested.foo() {} + class Nested { + val y = x + fun test() { + foo() + } + } + } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/outerObject.txt b/compiler/fir/resolve/testData/resolve/expresssions/outerObject.txt new file mode 100644 index 00000000000..617d2f04dc2 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/outerObject.txt @@ -0,0 +1,27 @@ +FILE: outerObject.kt + public final object Outer : R|kotlin/Any| { + private constructor(): R|Outer| { + super() + } + + public final val x: R|kotlin/Int| = Int(0) + public get(): R|kotlin/Int| + + public final fun R|Outer.Nested|.foo(): R|kotlin/Unit| { + } + + public final class Nested : R|kotlin/Any| { + public constructor(): R|Outer.Nested| { + super() + } + + public final val y: R|kotlin/Int| = R|/Outer.x| + public get(): R|kotlin/Int| + + public final fun test(): R|kotlin/Unit| { + R|/Outer.foo|() + } + + } + + } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/sameReceiver.kt b/compiler/fir/resolve/testData/resolve/expresssions/sameReceiver.kt new file mode 100644 index 00000000000..dfdbecf2746 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/sameReceiver.kt @@ -0,0 +1,7 @@ +class Foo { + fun Foo.bar() {} + + fun test() { + bar() + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/sameReceiver.txt b/compiler/fir/resolve/testData/resolve/expresssions/sameReceiver.txt new file mode 100644 index 00000000000..d791499a3ba --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/sameReceiver.txt @@ -0,0 +1,14 @@ +FILE: sameReceiver.kt + public final class Foo : R|kotlin/Any| { + public constructor(): R|Foo| { + super() + } + + public final fun R|Foo|.bar(): R|kotlin/Unit| { + } + + public final fun test(): R|kotlin/Unit| { + R|/Foo.bar|() + } + + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java index 14069eda434..3768150d57a 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java @@ -272,6 +272,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/expresssions/companion.kt"); } + @TestMetadata("companionExtension.kt") + public void testCompanionExtension() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/companionExtension.kt"); + } + @TestMetadata("constructor.kt") public void testConstructor() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/constructor.kt"); @@ -292,6 +297,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/expresssions/lambda.kt"); } + @TestMetadata("localExtension.kt") + public void testLocalExtension() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/localExtension.kt"); + } + @TestMetadata("localImplicitBodies.kt") public void testLocalImplicitBodies() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.kt"); @@ -312,6 +322,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/expresssions/objects.kt"); } + @TestMetadata("outerObject.kt") + public void testOuterObject() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/outerObject.kt"); + } + @TestMetadata("qualifiedExpressions.kt") public void testQualifiedExpressions() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/qualifiedExpressions.kt"); @@ -322,6 +337,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/expresssions/receiverConsistency.kt"); } + @TestMetadata("sameReceiver.kt") + public void testSameReceiver() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/sameReceiver.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/simple.kt");