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 fe9d0ffb168..95815415ce3 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 @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.processClassifiersByNameWithAction import org.jetbrains.kotlin.fir.service import org.jetbrains.kotlin.fir.symbols.* +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.Name @@ -140,12 +141,12 @@ class MemberScopeTowerLevel( private fun processMembers( output: TowerScopeLevel.TowerScopeLevelProcessor, - takeMembers: FirScope.(processor: (T) -> ProcessorAction) -> ProcessorAction + processScopeMembers: FirScope.(processor: (T) -> ProcessorAction) -> ProcessorAction ): ProcessorAction { val scope = dispatchReceiver.type.scope(session) ?: return ProcessorAction.NEXT - if (scope.takeMembers { output.consumeCandidate(it, dispatchReceiver) }.stop()) return ProcessorAction.STOP + if (scope.processScopeMembers { output.consumeCandidate(it, dispatchReceiver) }.stop()) return ProcessorAction.STOP val withSynthetic = FirSyntheticPropertiesScope(session, scope, ReturnTypeCalculatorWithJump(session)) - return withSynthetic.takeMembers { output.consumeCandidate(it, dispatchReceiver) } + return withSynthetic.processScopeMembers { output.consumeCandidate(it, dispatchReceiver) } } override fun processElementsByName( @@ -163,6 +164,8 @@ class MemberScopeTowerLevel( } +private fun ConeCallableSymbol.hasExtensionReceiver(): Boolean = (this as? FirCallableSymbol)?.fir?.receiverTypeRef != null + class ScopeTowerLevel( val session: FirSession, val scope: FirScope @@ -175,12 +178,26 @@ class ScopeTowerLevel( ): ProcessorAction { return when (token) { - TowerScopeLevel.Token.Properties -> scope.processPropertiesByName(name) { processor.consumeCandidate(it as T, null) } - TowerScopeLevel.Token.Functions -> scope.processFunctionsByName(name) { processor.consumeCandidate(it as T, null) } + TowerScopeLevel.Token.Properties -> scope.processPropertiesByName(name) { candidate -> + val candidateHasExtensionReceiver = candidate.hasExtensionReceiver() + if (candidateHasExtensionReceiver == (extensionReceiver != null)) { + processor.consumeCandidate(candidate as T, boundDispatchReceiver = null) + } else { + ProcessorAction.NEXT + } + } + TowerScopeLevel.Token.Functions -> scope.processFunctionsByName(name) { candidate -> + val candidateHasExtensionReceiver = candidate.hasExtensionReceiver() + if (candidateHasExtensionReceiver == (extensionReceiver != null)) { + processor.consumeCandidate(candidate as T, boundDispatchReceiver = null) + } else { + ProcessorAction.NEXT + } + } TowerScopeLevel.Token.Objects -> scope.processClassifiersByNameWithAction(name, FirPosition.OTHER) { processor.consumeCandidate( it as T, - null + boundDispatchReceiver = null ) } } @@ -319,8 +336,8 @@ class ExplicitReceiverTowerDataConsumer( MemberScopeTowerLevel(session, explicitReceiver).processElementsByName( token, name, - null, - object : TowerScopeLevel.TowerScopeLevelProcessor { + extensionReceiver = null, + processor = object : TowerScopeLevel.TowerScopeLevelProcessor { override fun consumeCandidate(symbol: T, boundDispatchReceiver: ReceiverValueWithPossibleTypes?): ProcessorAction { resultCollector.consumeCandidate( group, @@ -339,8 +356,8 @@ class ExplicitReceiverTowerDataConsumer( towerScopeLevel.processElementsByName( token, name, - explicitReceiver, - object : TowerScopeLevel.TowerScopeLevelProcessor { + extensionReceiver = explicitReceiver, + processor = object : TowerScopeLevel.TowerScopeLevelProcessor { override fun consumeCandidate(symbol: T, boundDispatchReceiver: ReceiverValueWithPossibleTypes?): ProcessorAction { resultCollector.consumeCandidate( group, 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 b5dc3800f1e..b7f62438bbb 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 @@ -7,7 +7,10 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* import java.lang.IllegalStateException @@ -17,6 +20,39 @@ abstract class ResolutionStage { abstract class CheckerStage : ResolutionStage() +internal object CheckExplicitReceiverConsistency : ResolutionStage() { + override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { + val receiverKind = candidate.receiverKind + val explicitReceiver = callInfo.explicitReceiver + // TODO: add invoke cases + when (receiverKind) { + NO_EXPLICIT_RECEIVER -> { + if (explicitReceiver != null) return sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER) + } + EXTENSION_RECEIVER, DISPATCH_RECEIVER -> { + if (explicitReceiver == null) return sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER) + } + BOTH_RECEIVERS -> { + if (explicitReceiver == null) return sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER) + // Here we should also check additional invoke receiver + } + } + } + +} + +internal sealed class CheckReceivers : ResolutionStage() { + object Dispatch : CheckReceivers() + + object Extension : CheckReceivers() + + override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { + val callableSymbol = candidate.symbol as? FirCallableSymbol ?: return + val callableId = callableSymbol.callableId + val callable = callableSymbol.fir + } +} + internal object MapArguments : ResolutionStage() { override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { val symbol = candidate.symbol as? FirFunctionSymbol ?: return sink.reportApplicability(CandidateApplicability.HIDDEN) @@ -47,9 +83,13 @@ internal object CheckArguments : CheckerStage() { } -internal fun functionCallResolutionSequence() = - listOf(MapArguments, CreateFreshTypeVariableSubstitutorStage, CheckArguments) +internal fun functionCallResolutionSequence() = listOf( + CheckExplicitReceiverConsistency, CheckReceivers.Dispatch, CheckReceivers.Extension, + MapArguments, CreateFreshTypeVariableSubstitutorStage, CheckArguments +) -internal fun qualifiedAccessResolutionSequence() = - listOf(CreateFreshTypeVariableSubstitutorStage) \ No newline at end of file +internal fun qualifiedAccessResolutionSequence() = listOf( + CheckExplicitReceiverConsistency, CheckReceivers.Dispatch, CheckReceivers.Extension, + CreateFreshTypeVariableSubstitutorStage +) \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.txt b/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.txt index 28317e5f249..c7c34afe614 100644 --- a/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.txt +++ b/compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.txt @@ -15,5 +15,5 @@ FILE: localImplicitBodies.kt } - lval g: = R|/x|.R|/sss|() + lval g: = R|/x|.#() } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/receiverConsistency.kt b/compiler/fir/resolve/testData/resolve/expresssions/receiverConsistency.kt new file mode 100644 index 00000000000..b41054cdb68 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/receiverConsistency.kt @@ -0,0 +1,21 @@ +fun foo() {} + +class C { + fun bar() {} + fun err() {} + + class Nested { + fun test() { + err() + } + } +} + +fun test() { + val c = C() + foo() + c.bar() + + val err = C() + err.foo() +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/receiverConsistency.txt b/compiler/fir/resolve/testData/resolve/expresssions/receiverConsistency.txt new file mode 100644 index 00000000000..10391b82a6e --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/receiverConsistency.txt @@ -0,0 +1,33 @@ +FILE: receiverConsistency.kt + public final fun foo(): R|kotlin/Unit| { + } + public final class C : R|kotlin/Any| { + public constructor(): R|C| { + super() + } + + public final fun bar(): R|kotlin/Unit| { + } + + public final fun err(): R|kotlin/Unit| { + } + + public final class Nested : R|kotlin/Any| { + public constructor(): R|C.Nested| { + super() + } + + public final fun test(): R|kotlin/Unit| { + R|/C.err|() + } + + } + + } + public final fun test(): R|kotlin/Unit| { + lval c: R|C| = R|/C.C|() + R|/foo|() + R|/c|.R|/C.bar|() + lval err: R|C| = R|/C.C|() + R|/err|.#() + } diff --git a/compiler/fir/resolve/testData/resolve/nested/inner.kt b/compiler/fir/resolve/testData/resolve/nested/inner.kt new file mode 100644 index 00000000000..dba41526db9 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/nested/inner.kt @@ -0,0 +1,38 @@ +class Owner { + + fun foo() { + bar() + this.bar() + } + + fun bar() { + val i = Inner() + i.baz() + } + + fun err() {} + + inner class Inner { + fun baz() { + gau() + this.gau() + } + + fun gau() { + val o = Owner() + o.foo() + foo() + this@Owner.foo() + this.err() + } + } +} + +fun test() { + val o = Owner() + o.foo() + val err = Owner.Inner() + err.baz() + val i = o.Inner() + i.gau() +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/nested/inner.txt b/compiler/fir/resolve/testData/resolve/nested/inner.txt new file mode 100644 index 00000000000..1c290990da7 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/nested/inner.txt @@ -0,0 +1,48 @@ +FILE: inner.kt + public final class Owner : R|kotlin/Any| { + public constructor(): R|Owner| { + super() + } + + public final fun foo(): R|kotlin/Unit| { + R|/Owner.bar|() + this#.R|/Owner.bar|() + } + + public final fun bar(): R|kotlin/Unit| { + lval i: R|Owner.Inner| = R|/Owner.Inner.Inner|() + R|/i|.R|/Owner.Inner.baz|() + } + + public final fun err(): R|kotlin/Unit| { + } + + public final inner class Inner : R|kotlin/Any| { + public constructor(): R|Owner.Inner| { + super() + } + + public final fun baz(): R|kotlin/Unit| { + R|/Owner.Inner.gau|() + this#.R|/Owner.Inner.gau|() + } + + public final fun gau(): R|kotlin/Unit| { + lval o: R|Owner| = R|/Owner.Owner|() + R|/o|.R|/Owner.foo|() + R|/Owner.foo|() + this@Owner.R|/Owner.foo|() + this#.#() + } + + } + + } + public final fun test(): R|kotlin/Unit| { + lval o: R|Owner| = R|/Owner.Owner|() + R|/o|.R|/Owner.foo|() + lval err: R|Owner.Inner| = R|/Owner|.R|/Owner.Inner.Inner|() + R|/err|.R|/Owner.Inner.baz|() + lval i: R|Owner.Inner| = R|/o|.R|/Owner.Inner.Inner|() + R|/i|.R|/Owner.Inner.gau|() + } diff --git a/compiler/fir/resolve/testData/resolve/nested/simple.kt b/compiler/fir/resolve/testData/resolve/nested/simple.kt index 939db107888..7fe3ba6591f 100644 --- a/compiler/fir/resolve/testData/resolve/nested/simple.kt +++ b/compiler/fir/resolve/testData/resolve/nested/simple.kt @@ -20,6 +20,11 @@ class Owner { val o = Owner() o.foo() } + + fun err() { + foo() + this.foo() + } } } diff --git a/compiler/fir/resolve/testData/resolve/nested/simple.txt b/compiler/fir/resolve/testData/resolve/nested/simple.txt index e0957d081db..b7f65680a77 100644 --- a/compiler/fir/resolve/testData/resolve/nested/simple.txt +++ b/compiler/fir/resolve/testData/resolve/nested/simple.txt @@ -29,6 +29,11 @@ FILE: simple.kt R|/o|.R|/Owner.foo|() } + public final fun err(): R|kotlin/Unit| { + R|/Owner.foo|() + this#.#() + } + } } 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 7f5c915ae2e..9aa99991a1f 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java @@ -227,6 +227,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/expresssions/objects.kt"); } + @TestMetadata("receiverConsistency.kt") + public void testReceiverConsistency() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/receiverConsistency.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/simple.kt"); @@ -432,6 +437,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve/nested"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("inner.kt") + public void testInner() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/nested/inner.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("compiler/fir/resolve/testData/resolve/nested/simple.kt");