FIR resolve: add receiver consistency checking

NB: looks like checker itself may be not needed, but it's important
to check extension receiver presence during candidate collection
This commit is contained in:
Mikhail Glukhikh
2019-04-12 13:21:25 +03:00
parent 2e7d655b20
commit 5bf41594ec
10 changed files with 232 additions and 15 deletions
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.processClassifiersByNameWithAction
import org.jetbrains.kotlin.fir.service
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.Name
@@ -140,12 +141,12 @@ class MemberScopeTowerLevel(
private fun <T : ConeSymbol> processMembers(
output: TowerScopeLevel.TowerScopeLevelProcessor<T>,
takeMembers: FirScope.(processor: (T) -> ProcessorAction) -> ProcessorAction
processScopeMembers: FirScope.(processor: (T) -> ProcessorAction) -> ProcessorAction
): ProcessorAction {
val scope = dispatchReceiver.type.scope(session) ?: return ProcessorAction.NEXT
if (scope.takeMembers { output.consumeCandidate(it, dispatchReceiver) }.stop()) return ProcessorAction.STOP
if (scope.processScopeMembers { output.consumeCandidate(it, dispatchReceiver) }.stop()) return ProcessorAction.STOP
val withSynthetic = FirSyntheticPropertiesScope(session, scope, ReturnTypeCalculatorWithJump(session))
return withSynthetic.takeMembers { output.consumeCandidate(it, dispatchReceiver) }
return withSynthetic.processScopeMembers { output.consumeCandidate(it, dispatchReceiver) }
}
override fun <T : ConeSymbol> processElementsByName(
@@ -163,6 +164,8 @@ class MemberScopeTowerLevel(
}
private fun ConeCallableSymbol.hasExtensionReceiver(): Boolean = (this as? FirCallableSymbol)?.fir?.receiverTypeRef != null
class ScopeTowerLevel(
val session: FirSession,
val scope: FirScope
@@ -175,12 +178,26 @@ class ScopeTowerLevel(
): ProcessorAction {
return when (token) {
TowerScopeLevel.Token.Properties -> scope.processPropertiesByName(name) { processor.consumeCandidate(it as T, null) }
TowerScopeLevel.Token.Functions -> scope.processFunctionsByName(name) { processor.consumeCandidate(it as T, null) }
TowerScopeLevel.Token.Properties -> scope.processPropertiesByName(name) { candidate ->
val candidateHasExtensionReceiver = candidate.hasExtensionReceiver()
if (candidateHasExtensionReceiver == (extensionReceiver != null)) {
processor.consumeCandidate(candidate as T, boundDispatchReceiver = null)
} else {
ProcessorAction.NEXT
}
}
TowerScopeLevel.Token.Functions -> scope.processFunctionsByName(name) { candidate ->
val candidateHasExtensionReceiver = candidate.hasExtensionReceiver()
if (candidateHasExtensionReceiver == (extensionReceiver != null)) {
processor.consumeCandidate(candidate as T, boundDispatchReceiver = null)
} else {
ProcessorAction.NEXT
}
}
TowerScopeLevel.Token.Objects -> scope.processClassifiersByNameWithAction(name, FirPosition.OTHER) {
processor.consumeCandidate(
it as T,
null
boundDispatchReceiver = null
)
}
}
@@ -319,8 +336,8 @@ class ExplicitReceiverTowerDataConsumer<T : ConeSymbol>(
MemberScopeTowerLevel(session, explicitReceiver).processElementsByName(
token,
name,
null,
object : TowerScopeLevel.TowerScopeLevelProcessor<T> {
extensionReceiver = null,
processor = object : TowerScopeLevel.TowerScopeLevelProcessor<T> {
override fun consumeCandidate(symbol: T, boundDispatchReceiver: ReceiverValueWithPossibleTypes?): ProcessorAction {
resultCollector.consumeCandidate(
group,
@@ -339,8 +356,8 @@ class ExplicitReceiverTowerDataConsumer<T : ConeSymbol>(
towerScopeLevel.processElementsByName(
token,
name,
explicitReceiver,
object : TowerScopeLevel.TowerScopeLevelProcessor<T> {
extensionReceiver = explicitReceiver,
processor = object : TowerScopeLevel.TowerScopeLevelProcessor<T> {
override fun consumeCandidate(symbol: T, boundDispatchReceiver: ReceiverValueWithPossibleTypes?): ProcessorAction {
resultCollector.consumeCandidate(
group,
@@ -7,7 +7,10 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import java.lang.IllegalStateException
@@ -17,6 +20,39 @@ abstract class ResolutionStage {
abstract class CheckerStage : ResolutionStage()
internal object CheckExplicitReceiverConsistency : ResolutionStage() {
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val receiverKind = candidate.receiverKind
val explicitReceiver = callInfo.explicitReceiver
// TODO: add invoke cases
when (receiverKind) {
NO_EXPLICIT_RECEIVER -> {
if (explicitReceiver != null) return sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER)
}
EXTENSION_RECEIVER, DISPATCH_RECEIVER -> {
if (explicitReceiver == null) return sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER)
}
BOTH_RECEIVERS -> {
if (explicitReceiver == null) return sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER)
// Here we should also check additional invoke receiver
}
}
}
}
internal sealed class CheckReceivers : ResolutionStage() {
object Dispatch : CheckReceivers()
object Extension : CheckReceivers()
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val callableSymbol = candidate.symbol as? FirCallableSymbol ?: return
val callableId = callableSymbol.callableId
val callable = callableSymbol.fir
}
}
internal object MapArguments : ResolutionStage() {
override fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val symbol = candidate.symbol as? FirFunctionSymbol ?: return sink.reportApplicability(CandidateApplicability.HIDDEN)
@@ -47,9 +83,13 @@ internal object CheckArguments : CheckerStage() {
}
internal fun functionCallResolutionSequence() =
listOf<ResolutionStage>(MapArguments, CreateFreshTypeVariableSubstitutorStage, CheckArguments)
internal fun functionCallResolutionSequence() = listOf(
CheckExplicitReceiverConsistency, CheckReceivers.Dispatch, CheckReceivers.Extension,
MapArguments, CreateFreshTypeVariableSubstitutorStage, CheckArguments
)
internal fun qualifiedAccessResolutionSequence() =
listOf<ResolutionStage>(CreateFreshTypeVariableSubstitutorStage)
internal fun qualifiedAccessResolutionSequence() = listOf(
CheckExplicitReceiverConsistency, CheckReceivers.Dispatch, CheckReceivers.Extension,
CreateFreshTypeVariableSubstitutorStage
)
@@ -15,5 +15,5 @@ FILE: localImplicitBodies.kt
}
lval g: <ERROR TYPE REF: Unresolved name: abc> = R|<local>/x|.R|/sss|()
lval g: <ERROR TYPE REF: Unresolved name: sss> = R|<local>/x|.<Unresolved name: sss>#()
}
@@ -0,0 +1,21 @@
fun foo() {}
class C {
fun bar() {}
fun err() {}
class Nested {
fun test() {
err()
}
}
}
fun test() {
val c = C()
foo()
c.bar()
val err = C()
err.foo()
}
@@ -0,0 +1,33 @@
FILE: receiverConsistency.kt
public final fun foo(): R|kotlin/Unit| {
}
public final class C : R|kotlin/Any| {
public constructor(): R|C| {
super<R|kotlin/Any|>()
}
public final fun bar(): R|kotlin/Unit| {
}
public final fun err(): R|kotlin/Unit| {
}
public final class Nested : R|kotlin/Any| {
public constructor(): R|C.Nested| {
super<R|kotlin/Any|>()
}
public final fun test(): R|kotlin/Unit| {
R|/C.err|()
}
}
}
public final fun test(): R|kotlin/Unit| {
lval c: R|C| = R|/C.C|()
R|/foo|()
R|<local>/c|.R|/C.bar|()
lval err: R|C| = R|/C.C|()
R|<local>/err|.<Unresolved name: foo>#()
}
+38
View File
@@ -0,0 +1,38 @@
class Owner {
fun foo() {
bar()
this.bar()
}
fun bar() {
val i = Inner()
i.baz()
}
fun err() {}
inner class Inner {
fun baz() {
gau()
this.gau()
}
fun gau() {
val o = Owner()
o.foo()
foo()
this@Owner.foo()
this.err()
}
}
}
fun test() {
val o = Owner()
o.foo()
val err = Owner.Inner()
err.baz()
val i = o.Inner()
i.gau()
}
+48
View File
@@ -0,0 +1,48 @@
FILE: inner.kt
public final class Owner : R|kotlin/Any| {
public constructor(): R|Owner| {
super<R|kotlin/Any|>()
}
public final fun foo(): R|kotlin/Unit| {
R|/Owner.bar|()
this#.R|/Owner.bar|()
}
public final fun bar(): R|kotlin/Unit| {
lval i: R|Owner.Inner| = R|/Owner.Inner.Inner|()
R|<local>/i|.R|/Owner.Inner.baz|()
}
public final fun err(): R|kotlin/Unit| {
}
public final inner class Inner : R|kotlin/Any| {
public constructor(): R|Owner.Inner| {
super<R|kotlin/Any|>()
}
public final fun baz(): R|kotlin/Unit| {
R|/Owner.Inner.gau|()
this#.R|/Owner.Inner.gau|()
}
public final fun gau(): R|kotlin/Unit| {
lval o: R|Owner| = R|/Owner.Owner|()
R|<local>/o|.R|/Owner.foo|()
R|/Owner.foo|()
this@Owner.R|/Owner.foo|()
this#.<Unresolved name: err>#()
}
}
}
public final fun test(): R|kotlin/Unit| {
lval o: R|Owner| = R|/Owner.Owner|()
R|<local>/o|.R|/Owner.foo|()
lval err: R|Owner.Inner| = R|/Owner|.R|/Owner.Inner.Inner|()
R|<local>/err|.R|/Owner.Inner.baz|()
lval i: R|Owner.Inner| = R|<local>/o|.R|/Owner.Inner.Inner|()
R|<local>/i|.R|/Owner.Inner.gau|()
}
@@ -20,6 +20,11 @@ class Owner {
val o = Owner()
o.foo()
}
fun err() {
foo()
this.foo()
}
}
}
@@ -29,6 +29,11 @@ FILE: simple.kt
R|<local>/o|.R|/Owner.foo|()
}
public final fun err(): R|kotlin/Unit| {
R|/Owner.foo|()
this#.<Unresolved name: foo>#()
}
}
}
@@ -227,6 +227,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
runTest("compiler/fir/resolve/testData/resolve/expresssions/objects.kt");
}
@TestMetadata("receiverConsistency.kt")
public void testReceiverConsistency() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/receiverConsistency.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/simple.kt");
@@ -432,6 +437,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve/nested"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("inner.kt")
public void testInner() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/nested/inner.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/nested/simple.kt");