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.FirPosition
|
||||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
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.impl.FirExplicitSimpleImportingScope
|
||||||
import org.jetbrains.kotlin.fir.scopes.processClassifiersByNameWithAction
|
import org.jetbrains.kotlin.fir.scopes.processClassifiersByNameWithAction
|
||||||
import org.jetbrains.kotlin.fir.service
|
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 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
|
// 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
|
// 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
|
// So: extension receiver = either none or explicit
|
||||||
// (if explicit receiver exists, it always *should* be an extension receiver)
|
// (if explicit receiver exists, it always *should* be an extension receiver)
|
||||||
class ScopeTowerLevel(
|
class ScopeTowerLevel(
|
||||||
@@ -152,6 +153,9 @@ class ScopeTowerLevel(
|
|||||||
val scope: FirScope,
|
val scope: FirScope,
|
||||||
val implicitExtensionReceiver: ImplicitReceiverValue? = null
|
val implicitExtensionReceiver: ImplicitReceiverValue? = null
|
||||||
) : SessionBasedTowerLevel(session) {
|
) : SessionBasedTowerLevel(session) {
|
||||||
|
private fun FirCallableSymbol<*>.hasConsistentReceivers(extensionReceiver: ReceiverValue?): Boolean =
|
||||||
|
hasConsistentExtensionReceiver(extensionReceiver) && (scope is FirAbstractImportingScope || dispatchReceiverValue() == null)
|
||||||
|
|
||||||
override fun <T : AbstractFirBasedSymbol<*>> processElementsByName(
|
override fun <T : AbstractFirBasedSymbol<*>> processElementsByName(
|
||||||
token: TowerScopeLevel.Token<T>,
|
token: TowerScopeLevel.Token<T>,
|
||||||
name: Name,
|
name: Name,
|
||||||
@@ -165,7 +169,7 @@ class ScopeTowerLevel(
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return when (token) {
|
return when (token) {
|
||||||
TowerScopeLevel.Token.Properties -> scope.processPropertiesByName(name) { candidate ->
|
TowerScopeLevel.Token.Properties -> scope.processPropertiesByName(name) { candidate ->
|
||||||
if (candidate.hasConsistentExtensionReceiver(extensionReceiver) && candidate.dispatchReceiverValue() == null) {
|
if (candidate.hasConsistentReceivers(extensionReceiver)) {
|
||||||
processor.consumeCandidate(
|
processor.consumeCandidate(
|
||||||
candidate as T, dispatchReceiverValue = null,
|
candidate as T, dispatchReceiverValue = null,
|
||||||
implicitExtensionReceiverValue = implicitExtensionReceiver
|
implicitExtensionReceiverValue = implicitExtensionReceiver
|
||||||
@@ -175,7 +179,7 @@ class ScopeTowerLevel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TowerScopeLevel.Token.Functions -> scope.processFunctionsByName(name) { candidate ->
|
TowerScopeLevel.Token.Functions -> scope.processFunctionsByName(name) { candidate ->
|
||||||
if (candidate.hasConsistentExtensionReceiver(extensionReceiver) && candidate.dispatchReceiverValue() == null) {
|
if (candidate.hasConsistentReceivers(extensionReceiver)) {
|
||||||
processor.consumeCandidate(
|
processor.consumeCandidate(
|
||||||
candidate as T, dispatchReceiverValue = null,
|
candidate as T, dispatchReceiverValue = null,
|
||||||
implicitExtensionReceiverValue = implicitExtensionReceiver
|
implicitExtensionReceiverValue = implicitExtensionReceiver
|
||||||
@@ -240,7 +244,7 @@ fun FirCallableDeclaration<*>.dispatchReceiverValue(session: FirSession): ClassD
|
|||||||
val symbol = session.service<FirSymbolProvider>().getClassLikeSymbolByFqName(id) as? FirClassSymbol ?: return null
|
val symbol = session.service<FirSymbolProvider>().getClassLikeSymbolByFqName(id) as? FirClassSymbol ?: return null
|
||||||
val regularClass = symbol.fir
|
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
|
if (regularClass.classKind == ClassKind.OBJECT) return null
|
||||||
|
|
||||||
return ClassDispatchReceiverValue(regularClass.symbol)
|
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");
|
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")
|
@TestMetadata("lambda.kt")
|
||||||
public void testLambda() throws Exception {
|
public void testLambda() throws Exception {
|
||||||
runTest("compiler/fir/resolve/testData/resolve/expresssions/lambda.kt");
|
runTest("compiler/fir/resolve/testData/resolve/expresssions/lambda.kt");
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ FILE fqName:<root> fileName:/useImportedMember.kt
|
|||||||
BRANCH
|
BRANCH
|
||||||
if: CALL 'public final fun not (): kotlin.Boolean declared in kotlin.Boolean' type=kotlin.Boolean origin=EXCLEQ
|
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
|
$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
|
arg1: CONST Int type=kotlin.Int value=9
|
||||||
then: RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in <root>'
|
then: RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in <root>'
|
||||||
CONST String type=kotlin.String value="9"
|
CONST String type=kotlin.String value="9"
|
||||||
@@ -237,8 +237,8 @@ FILE fqName:<root> fileName:/useImportedMember.kt
|
|||||||
BRANCH
|
BRANCH
|
||||||
if: CALL 'public final fun not (): kotlin.Boolean declared in kotlin.Boolean' type=kotlin.Boolean origin=EXCLEQ
|
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
|
$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
|
arg0: CALL 'public open fun genericFromSuper (g: kotlin.String): kotlin.String declared in <root>.I' type=kotlin.String origin=null
|
||||||
CONST String type=kotlin.String value="11"
|
g: CONST String type=kotlin.String value="11"
|
||||||
arg1: 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>'
|
then: RETURN type=kotlin.Nothing from='public final fun box (): kotlin.String declared in <root>'
|
||||||
CONST String type=kotlin.String value="11"
|
CONST String type=kotlin.String value="11"
|
||||||
|
|||||||
Reference in New Issue
Block a user