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.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]>#()
}
@@ -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"