FIR: handle imported members from objects more correctly
This commit is contained in:
@@ -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 <T : AbstractFirBasedSymbol<*>> processElementsByName(
|
||||
token: TowerScopeLevel.Token<T>,
|
||||
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<FirSymbolProvider>().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)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import My.bar
|
||||
import My.baz
|
||||
import My.gau
|
||||
import My.wat
|
||||
import My.watwat
|
||||
|
||||
fun <T> T.foo() {}
|
||||
|
||||
interface Your<R> {
|
||||
fun wat() {}
|
||||
fun <T> T.watwat() {}
|
||||
}
|
||||
|
||||
object My : Your<Double> {
|
||||
fun <T> T.bar() {}
|
||||
fun baz()
|
||||
fun Boolean.gau() {}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
42.foo()
|
||||
"".foo()
|
||||
|
||||
42.bar()
|
||||
"".bar()
|
||||
|
||||
baz()
|
||||
true.gau()
|
||||
wat()
|
||||
false.watwat()
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
FILE: importedReceiver.kt
|
||||
public final fun <T> R|T|.foo(): R|kotlin/Unit| {
|
||||
}
|
||||
public abstract interface Your<R> : R|kotlin/Any| {
|
||||
public open fun wat(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
public open fun <T> R|T|.watwat(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
}
|
||||
public final object My : R|Your<kotlin/Double>| {
|
||||
private constructor(): R|My| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun <T> 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|<R|kotlin/Int|>()
|
||||
String().R|/foo|<R|kotlin/String|>()
|
||||
Int(42).R|/My.bar|<R|kotlin/Int|>()
|
||||
String().R|/My.bar|<R|kotlin/String|>()
|
||||
R|/My.baz|()
|
||||
Boolean(true).R|/My.gau|()
|
||||
R|FakeOverride</Your.wat: R|kotlin/Unit|>|()
|
||||
Boolean(false).<Inapplicable(WRONG_RECEIVER): [/Your.watwat]>#()
|
||||
}
|
||||
+5
@@ -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");
|
||||
|
||||
@@ -220,7 +220,7 @@ FILE fqName:<root> 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: <Unresolved name: fromInterface>#' type=IrErrorType
|
||||
arg0: ERROR_CALL 'Unresolved reference: <Inapplicable(WRONG_RECEIVER): [/I.fromInterface]>#' type=IrErrorType
|
||||
arg1: CONST Int type=kotlin.Int value=9
|
||||
then: RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in <root>'
|
||||
CONST String type=kotlin.String value="9"
|
||||
@@ -237,8 +237,8 @@ FILE fqName:<root> 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: <Unresolved name: genericFromSuper>#' type=IrErrorType
|
||||
CONST String type=kotlin.String value="11"
|
||||
arg0: CALL 'public open fun genericFromSuper (g: kotlin.String): kotlin.String declared in <root>.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 <root>'
|
||||
CONST String type=kotlin.String value="11"
|
||||
|
||||
Reference in New Issue
Block a user