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 3a88fc707ec..98e51187b4e 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 @@ -72,6 +72,18 @@ class CallResolver( ) } } + if (implicitDispatchReceiverValue !== implicitReceiverValue) { + // Two different implicit receivers + towerDataConsumer.consume( + TowerDataKind.TOWER_LEVEL, + MemberScopeTowerLevel( + session, scopeSession = components.scopeSession, + dispatchReceiver = implicitDispatchReceiverValue, + implicitExtensionReceiver = implicitReceiverValue + ), + group++ + ) + } } for (scope in topLevelScopes) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt index f3de11c17fe..512b8a68856 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt @@ -52,11 +52,11 @@ internal object CheckExplicitReceiverConsistency : ResolutionStage() { internal sealed class CheckReceivers : ResolutionStage() { object Dispatch : CheckReceivers() { - override fun ExplicitReceiverKind.shouldBeResolvedAsImplicit(): Boolean { - return this == EXTENSION_RECEIVER || this == NO_EXPLICIT_RECEIVER + override fun ExplicitReceiverKind.shouldBeCheckedAgainstImplicit(): Boolean { + return this == EXTENSION_RECEIVER // For NO_EXPLICIT_RECEIVER we can check extension receiver only } - override fun ExplicitReceiverKind.shouldBeResolvedAsExplicit(): Boolean { + override fun ExplicitReceiverKind.shouldBeCheckedAgainstExplicit(): Boolean { return this == DISPATCH_RECEIVER || this == BOTH_RECEIVERS } @@ -66,11 +66,11 @@ internal sealed class CheckReceivers : ResolutionStage() { } object Extension : CheckReceivers() { - override fun ExplicitReceiverKind.shouldBeResolvedAsImplicit(): Boolean { + override fun ExplicitReceiverKind.shouldBeCheckedAgainstImplicit(): Boolean { return this == DISPATCH_RECEIVER || this == NO_EXPLICIT_RECEIVER } - override fun ExplicitReceiverKind.shouldBeResolvedAsExplicit(): Boolean { + override fun ExplicitReceiverKind.shouldBeCheckedAgainstExplicit(): Boolean { return this == EXTENSION_RECEIVER || this == BOTH_RECEIVERS } @@ -88,9 +88,9 @@ internal sealed class CheckReceivers : ResolutionStage() { abstract fun Candidate.getReceiverValue(): ReceiverValue? - abstract fun ExplicitReceiverKind.shouldBeResolvedAsExplicit(): Boolean + abstract fun ExplicitReceiverKind.shouldBeCheckedAgainstExplicit(): Boolean - abstract fun ExplicitReceiverKind.shouldBeResolvedAsImplicit(): Boolean + abstract fun ExplicitReceiverKind.shouldBeCheckedAgainstImplicit(): Boolean override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { val expectedReceiverParameterValue = candidate.getReceiverValue() @@ -98,7 +98,7 @@ internal sealed class CheckReceivers : ResolutionStage() { val explicitReceiverKind = candidate.explicitReceiverKind if (expectedReceiverParameterValue != null) { - if (explicitReceiverExpression != null && explicitReceiverKind.shouldBeResolvedAsExplicit()) { + if (explicitReceiverExpression != null && explicitReceiverKind.shouldBeCheckedAgainstExplicit()) { resolveArgumentExpression( candidate.csBuilder, argument = explicitReceiverExpression, @@ -113,7 +113,7 @@ internal sealed class CheckReceivers : ResolutionStage() { sink.yield() } else { val argumentExtensionReceiverValue = candidate.implicitExtensionReceiverValue - if (argumentExtensionReceiverValue != null && explicitReceiverKind.shouldBeResolvedAsImplicit()) { + if (argumentExtensionReceiverValue != null && explicitReceiverKind.shouldBeCheckedAgainstImplicit()) { resolvePlainArgumentType( candidate.csBuilder, argumentType = argumentExtensionReceiverValue.type, diff --git a/compiler/fir/resolve/testData/resolve/stdlib/implicitReceiverOrder.kt b/compiler/fir/resolve/testData/resolve/stdlib/implicitReceiverOrder.kt new file mode 100644 index 00000000000..615cee87c27 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/implicitReceiverOrder.kt @@ -0,0 +1,26 @@ +class A { + fun foo() = 1 + + fun B.bar() = 3 +} + +class B { + fun foo() = 2 + + fun A.bar() = 4 +} + +fun test(a: A, b: B) { + with(b) { + with(a) { + foo() + bar() + } + } + with(a) { + with(b) { + foo() + bar() + } + } +} diff --git a/compiler/fir/resolve/testData/resolve/stdlib/implicitReceiverOrder.txt b/compiler/fir/resolve/testData/resolve/stdlib/implicitReceiverOrder.txt new file mode 100644 index 00000000000..f59fab10732 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/implicitReceiverOrder.txt @@ -0,0 +1,47 @@ +FILE: implicitReceiverOrder.kt + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public final fun foo(): R|kotlin/Int| { + ^foo Int(1) + } + + public final fun R|B|.bar(): R|kotlin/Int| { + ^bar Int(3) + } + + } + public final class B : R|kotlin/Any| { + public constructor(): R|B| { + super() + } + + public final fun foo(): R|kotlin/Int| { + ^foo Int(2) + } + + public final fun R|A|.bar(): R|kotlin/Int| { + ^bar Int(4) + } + + } + public final fun test(a: R|A|, b: R|B|): R|kotlin/Unit| { + R|kotlin/with|(R|/b|, = with@fun R|B|.(it: R|B|): R|kotlin/Unit| { + R|kotlin/with|(R|/a|, = with@fun R|A|.(it: R|A|): R|kotlin/Unit| { + R|/A.foo|() + R|/B.bar|() + } + ) + } + ) + R|kotlin/with|(R|/a|, = with@fun R|A|.(it: R|A|): R|kotlin/Unit| { + R|kotlin/with|(R|/b|, = with@fun R|B|.(it: R|B|): R|kotlin/Unit| { + R|/B.foo|() + R|/A.bar|() + } + ) + } + ) + } diff --git a/compiler/fir/resolve/testData/resolve/stdlib/multipleImplicitReceivers.kt b/compiler/fir/resolve/testData/resolve/stdlib/multipleImplicitReceivers.kt new file mode 100644 index 00000000000..5c9810c91de --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/multipleImplicitReceivers.kt @@ -0,0 +1,23 @@ +// Copy of IR test + +object A +object B + +interface IFoo { + val A.foo: B get() = B +} + +interface IInvoke { + operator fun B.invoke() = 42 +} + +fun test(fooImpl: IFoo, invokeImpl: IInvoke) { + with(A) { + with(fooImpl) { + foo + with(invokeImpl) { + foo() + } + } + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/stdlib/multipleImplicitReceivers.txt b/compiler/fir/resolve/testData/resolve/stdlib/multipleImplicitReceivers.txt new file mode 100644 index 00000000000..e45d644d96f --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/multipleImplicitReceivers.txt @@ -0,0 +1,39 @@ +FILE: multipleImplicitReceivers.kt + public final object A : R|kotlin/Any| { + private constructor(): R|A| { + super() + } + + } + public final object B : R|kotlin/Any| { + private constructor(): R|B| { + super() + } + + } + public abstract interface IFoo : R|kotlin/Any| { + public open val R|A|.foo: R|B| + public get(): R|B| { + ^ Q|B| + } + + } + public abstract interface IInvoke : R|kotlin/Any| { + public open operator fun R|B|.invoke(): R|kotlin/Int| { + ^invoke Int(42) + } + + } + public final fun test(fooImpl: R|IFoo|, invokeImpl: R|IInvoke|): R|kotlin/Unit| { + R|kotlin/with|(Q|A|, = with@fun R|A|.(it: R|A|): R|kotlin/Unit| { + R|kotlin/with|(R|/fooImpl|, = with@fun R|IFoo|.(it: R|IFoo|): R|kotlin/Unit| { + R|/IFoo.foo| + R|kotlin/with|(R|/invokeImpl|, = with@fun R|IInvoke|.(it: R|IInvoke|): R|kotlin/Unit| { + R|/IFoo.foo|.R|/IInvoke.invoke|() + } + ) + } + ) + } + ) + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java index 34b158c2ea4..c006a9cac56 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java @@ -79,11 +79,21 @@ public class FirResolveTestCaseWithStdlibGenerated extends AbstractFirResolveTes runTest("compiler/fir/resolve/testData/resolve/stdlib/helloWorld.kt"); } + @TestMetadata("implicitReceiverOrder.kt") + public void testImplicitReceiverOrder() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/stdlib/implicitReceiverOrder.kt"); + } + @TestMetadata("mapList.kt") public void testMapList() throws Exception { runTest("compiler/fir/resolve/testData/resolve/stdlib/mapList.kt"); } + @TestMetadata("multipleImplicitReceivers.kt") + public void testMultipleImplicitReceivers() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/stdlib/multipleImplicitReceivers.kt"); + } + @TestMetadata("recursiveBug.kt") public void testRecursiveBug() throws Exception { runTest("compiler/fir/resolve/testData/resolve/stdlib/recursiveBug.kt");