diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt index 506c42cf724..7535c2d9b50 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorWithJum import org.jetbrains.kotlin.fir.scopes.FirPosition import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractImportingScope import org.jetbrains.kotlin.fir.scopes.impl.FirExplicitSimpleImportingScope import org.jetbrains.kotlin.fir.scopes.processClassifiersByNameWithAction import org.jetbrains.kotlin.fir.service @@ -144,7 +145,7 @@ class MemberScopeTowerLevel( // We can access here members of currently accessible scope which is not influenced by explicit receiver // We can either have no explicit receiver at all, or it can be an extension receiver // An explicit receiver never can be a dispatch receiver at this level -// So: dispatch receiver = strictly NONE +// So: dispatch receiver = strictly none (EXCEPTION: importing scopes with import from objects) // So: extension receiver = either none or explicit // (if explicit receiver exists, it always *should* be an extension receiver) class ScopeTowerLevel( @@ -152,6 +153,9 @@ class ScopeTowerLevel( val scope: FirScope, val implicitExtensionReceiver: ImplicitReceiverValue? = null ) : SessionBasedTowerLevel(session) { + private fun FirCallableSymbol<*>.hasConsistentReceivers(extensionReceiver: ReceiverValue?): Boolean = + hasConsistentExtensionReceiver(extensionReceiver) && (scope is FirAbstractImportingScope || dispatchReceiverValue() == null) + override fun > processElementsByName( token: TowerScopeLevel.Token, name: Name, @@ -165,7 +169,7 @@ class ScopeTowerLevel( @Suppress("UNCHECKED_CAST") return when (token) { TowerScopeLevel.Token.Properties -> scope.processPropertiesByName(name) { candidate -> - if (candidate.hasConsistentExtensionReceiver(extensionReceiver) && candidate.dispatchReceiverValue() == null) { + if (candidate.hasConsistentReceivers(extensionReceiver)) { processor.consumeCandidate( candidate as T, dispatchReceiverValue = null, implicitExtensionReceiverValue = implicitExtensionReceiver @@ -175,7 +179,7 @@ class ScopeTowerLevel( } } TowerScopeLevel.Token.Functions -> scope.processFunctionsByName(name) { candidate -> - if (candidate.hasConsistentExtensionReceiver(extensionReceiver) && candidate.dispatchReceiverValue() == null) { + if (candidate.hasConsistentReceivers(extensionReceiver)) { processor.consumeCandidate( candidate as T, dispatchReceiverValue = null, implicitExtensionReceiverValue = implicitExtensionReceiver @@ -240,7 +244,7 @@ fun FirCallableDeclaration<*>.dispatchReceiverValue(session: FirSession): ClassD val symbol = session.service().getClassLikeSymbolByFqName(id) as? FirClassSymbol ?: return null val regularClass = symbol.fir - // TODO: this is also not true, but objects can be also imported + // TODO: this is also not true, but objects can be also imported, companions can be also used implicitly if (regularClass.classKind == ClassKind.OBJECT) return null return ClassDispatchReceiverValue(regularClass.symbol) diff --git a/compiler/fir/resolve/testData/resolve/expresssions/importedReceiver.kt b/compiler/fir/resolve/testData/resolve/expresssions/importedReceiver.kt new file mode 100644 index 00000000000..6b4bae77c7d --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/importedReceiver.kt @@ -0,0 +1,31 @@ +import My.bar +import My.baz +import My.gau +import My.wat +import My.watwat + +fun T.foo() {} + +interface Your { + fun wat() {} + fun T.watwat() {} +} + +object My : Your { + fun T.bar() {} + fun baz() + fun Boolean.gau() {} +} + +fun test() { + 42.foo() + "".foo() + + 42.bar() + "".bar() + + baz() + true.gau() + wat() + false.watwat() +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/expresssions/importedReceiver.txt b/compiler/fir/resolve/testData/resolve/expresssions/importedReceiver.txt new file mode 100644 index 00000000000..4a3ae6aacb7 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/importedReceiver.txt @@ -0,0 +1,35 @@ +FILE: importedReceiver.kt + public final fun R|T|.foo(): R|kotlin/Unit| { + } + public abstract interface Your : R|kotlin/Any| { + public open fun wat(): R|kotlin/Unit| { + } + + public open fun R|T|.watwat(): R|kotlin/Unit| { + } + + } + public final object My : R|Your| { + private constructor(): R|My| { + super() + } + + public final fun R|T|.bar(): R|kotlin/Unit| { + } + + public final fun baz(): R|kotlin/Unit| + + public final fun R|kotlin/Boolean|.gau(): R|kotlin/Unit| { + } + + } + public final fun test(): R|kotlin/Unit| { + Int(42).R|/foo|() + String().R|/foo|() + Int(42).R|/My.bar|() + String().R|/My.bar|() + R|/My.baz|() + Boolean(true).R|/My.gau|() + R|FakeOverride|() + Boolean(false).#() + } 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 4bbc21e6036..14069eda434 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java @@ -282,6 +282,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/expresssions/dispatchReceiver.kt"); } + @TestMetadata("importedReceiver.kt") + public void testImportedReceiver() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/importedReceiver.kt"); + } + @TestMetadata("lambda.kt") public void testLambda() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/lambda.kt"); diff --git a/compiler/testData/ir/irText/expressions/useImportedMember.fir.txt b/compiler/testData/ir/irText/expressions/useImportedMember.fir.txt index 0823d7ad84c..331d57eca61 100644 --- a/compiler/testData/ir/irText/expressions/useImportedMember.fir.txt +++ b/compiler/testData/ir/irText/expressions/useImportedMember.fir.txt @@ -220,7 +220,7 @@ FILE fqName: fileName:/useImportedMember.kt BRANCH if: CALL 'public final fun not (): kotlin.Boolean declared in kotlin.Boolean' type=kotlin.Boolean origin=EXCLEQ $this: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EXCLEQ - arg0: ERROR_CALL 'Unresolved reference: #' type=IrErrorType + arg0: ERROR_CALL 'Unresolved reference: #' type=IrErrorType arg1: CONST Int type=kotlin.Int value=9 then: RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in ' CONST String type=kotlin.String value="9" @@ -237,8 +237,8 @@ FILE fqName: fileName:/useImportedMember.kt BRANCH if: CALL 'public final fun not (): kotlin.Boolean declared in kotlin.Boolean' type=kotlin.Boolean origin=EXCLEQ $this: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EXCLEQ - arg0: ERROR_CALL 'Unresolved reference: #' type=IrErrorType - CONST String type=kotlin.String value="11" + arg0: CALL 'public open fun genericFromSuper (g: kotlin.String): kotlin.String declared in .I' type=kotlin.String origin=null + g: CONST String type=kotlin.String value="11" arg1: CONST String type=kotlin.String value="11" then: RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in ' CONST String type=kotlin.String value="11"