FIR: handle imported members from objects more correctly

This commit is contained in:
Mikhail Glukhikh
2019-08-21 15:18:01 +03:00
parent a4d89b9829
commit c9a600c5ce
5 changed files with 82 additions and 7 deletions
@@ -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]>#()
}
@@ -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"