[FIR] Perform more accurate pre-check of candidate receiver type

This commit is contained in:
Mikhail Glukhikh
2020-12-29 13:32:22 +03:00
parent 4e4293b609
commit 0c0dbd6245
12 changed files with 54 additions and 60 deletions
@@ -5,21 +5,12 @@
package org.jetbrains.kotlin.fir.resolve.calls.tower package org.jetbrains.kotlin.fir.resolve.calls.tower
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.*
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.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeStarProjection
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.constructClassType
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.types.AbstractTypeChecker
internal class CandidateFactoriesAndCollectors( internal class CandidateFactoriesAndCollectors(
// Common calls // Common calls
@@ -88,31 +79,6 @@ private class TowerScopeLevelProcessor(
scope: FirScope, scope: FirScope,
builtInExtensionFunctionReceiverValue: ReceiverValue? builtInExtensionFunctionReceiverValue: ReceiverValue?
) { ) {
// Check explicit extension receiver for default package members
if (symbol is FirNamedFunctionSymbol && dispatchReceiverValue == null &&
extensionReceiverValue != null &&
callInfo.explicitReceiver !is FirResolvedQualifier &&
symbol.callableId.packageName.startsWith(defaultPackage)
) {
val extensionReceiverType = extensionReceiverValue.type as? ConeClassLikeType
if (extensionReceiverType != null) {
val declarationReceiverType = (symbol as? FirCallableSymbol<*>)?.fir?.receiverTypeRef?.coneType
if (declarationReceiverType is ConeClassLikeType) {
if (!AbstractTypeChecker.isSubtypeOf(
candidateFactory.context.session.typeContext,
extensionReceiverType,
declarationReceiverType.lookupTag.constructClassType(
declarationReceiverType.typeArguments.map { ConeStarProjection }.toTypedArray(),
isNullable = true
)
)
) {
return
}
}
}
}
// ---
resultCollector.consumeCandidate( resultCollector.consumeCandidate(
group, candidateFactory.createCandidate( group, candidateFactory.createCandidate(
callInfo, callInfo,
@@ -15,12 +15,18 @@ import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
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.FirDefaultStarImportingScope
import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectData import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectData
import org.jetbrains.kotlin.fir.scopes.processClassifiersByName import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeStarProjection
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.constructClassType
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
abstract class TowerScopeLevel { abstract class TowerScopeLevel {
@@ -178,12 +184,6 @@ class ScopeTowerLevel(
private val extensionsOnly: Boolean, private val extensionsOnly: Boolean,
private val includeInnerConstructors: Boolean private val includeInnerConstructors: Boolean
) : SessionBasedTowerLevel(session) { ) : SessionBasedTowerLevel(session) {
private fun FirCallableSymbol<*>.hasConsistentReceivers(extensionReceiver: Receiver?): Boolean =
when {
extensionsOnly && !hasExtensionReceiver() -> false
!hasConsistentExtensionReceiver(extensionReceiver) -> false
else -> true
}
private fun dispatchReceiverValue(candidate: FirCallableSymbol<*>): ReceiverValue? { private fun dispatchReceiverValue(candidate: FirCallableSymbol<*>): ReceiverValue? {
candidate.fir.importedFromObjectData?.let { data -> candidate.fir.importedFromObjectData?.let { data ->
@@ -215,20 +215,48 @@ class ScopeTowerLevel(
} }
} }
private fun shouldSkipCandidateWithInconsistentExtensionReceiver(candidate: FirCallableSymbol<*>): Boolean {
// Pre-check explicit extension receiver for default package top-level members
if (scope is FirDefaultStarImportingScope && extensionReceiver != null) {
val extensionReceiverType = extensionReceiver.type
if (extensionReceiverType is ConeClassLikeType) {
val declarationReceiverType = candidate.fir.receiverTypeRef?.coneType
if (declarationReceiverType is ConeClassLikeType) {
if (!AbstractTypeChecker.isSubtypeOf(
session.typeContext,
extensionReceiverType,
declarationReceiverType.lookupTag.constructClassType(
declarationReceiverType.typeArguments.map { ConeStarProjection }.toTypedArray(),
isNullable = true
)
)
) {
return true
}
}
}
}
return false
}
private fun <T : AbstractFirBasedSymbol<*>> consumeCallableCandidate( private fun <T : AbstractFirBasedSymbol<*>> consumeCallableCandidate(
candidate: FirCallableSymbol<*>, candidate: FirCallableSymbol<*>,
processor: TowerScopeLevelProcessor<T> processor: TowerScopeLevelProcessor<T>
) { ) {
if (candidate.hasConsistentReceivers(extensionReceiver)) { val candidateReceiverTypeRef = candidate.fir.receiverTypeRef
val dispatchReceiverValue = dispatchReceiverValue(candidate) val receiverExpected = extensionsOnly || extensionReceiver != null
val unwrappedCandidate = candidate.fir.importedFromObjectData?.original?.symbol ?: candidate if (candidateReceiverTypeRef == null == receiverExpected) return
@Suppress("UNCHECKED_CAST") val dispatchReceiverValue = dispatchReceiverValue(candidate)
processor.consumeCandidate( if (dispatchReceiverValue == null && shouldSkipCandidateWithInconsistentExtensionReceiver(candidate)) {
unwrappedCandidate as T, dispatchReceiverValue, return
extensionReceiverValue = extensionReceiver,
scope
)
} }
val unwrappedCandidate = candidate.fir.importedFromObjectData?.original?.symbol ?: candidate
@Suppress("UNCHECKED_CAST")
processor.consumeCandidate(
unwrappedCandidate as T, dispatchReceiverValue,
extensionReceiverValue = extensionReceiver,
scope
)
} }
override fun processFunctionsByName( override fun processFunctionsByName(
@@ -1 +1 @@
val foo = <!INAPPLICABLE_CANDIDATE!>UInt<!>() val foo = <!HIDDEN!>UInt<!>()
@@ -21,7 +21,7 @@ inline fun test(crossinline c: () -> Unit) {
} }
} }
val l = { c() } val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation) c.startCoroutine(EmptyContinuation)
} }
suspend fun calculate() = "OK" suspend fun calculate() = "OK"
@@ -20,7 +20,7 @@ inline fun test(noinline c: () -> Unit) {
} }
} }
val l = { c() } val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation) c.startCoroutine(EmptyContinuation)
} }
suspend fun calculate() = "OK" suspend fun calculate() = "OK"
@@ -21,7 +21,7 @@ inline fun test(c: () -> Unit) {
} }
} }
val l = { c() } val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation) c.startCoroutine(EmptyContinuation)
} }
fun builder(c: suspend () -> Unit) { fun builder(c: suspend () -> Unit) {
@@ -20,7 +20,7 @@ suspend inline fun test(crossinline c: () -> Unit) {
} }
} }
val l = { c() } val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation) c.startCoroutine(EmptyContinuation)
} }
fun builder(c: suspend () -> Unit) { fun builder(c: suspend () -> Unit) {
@@ -20,7 +20,7 @@ suspend inline fun test(noinline c: () -> Unit) {
} }
} }
val l = { c() } val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation) c.startCoroutine(EmptyContinuation)
} }
fun builder(c: suspend () -> Unit) { fun builder(c: suspend () -> Unit) {
@@ -20,7 +20,7 @@ suspend inline fun test(c: () -> Unit) {
} }
} }
val l = { c() } val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation) c.startCoroutine(EmptyContinuation)
} }
suspend fun calculate() = "OK" suspend fun calculate() = "OK"
@@ -6,7 +6,7 @@
class Case1(val nothing: Nothing) class Case1(val nothing: Nothing)
fun case1() { fun case1() {
val res = <!INAPPLICABLE_CANDIDATE!>Case1<!>(<!INAPPLICABLE_CANDIDATE!>Nothing<!>()) val res = Case1(<!HIDDEN!>Nothing<!>())
} }
@@ -30,14 +30,14 @@ import contracts.*
// TESTCASE NUMBER: 1 // TESTCASE NUMBER: 1
fun case_1(value: Any) { fun case_1(value: Any) {
if (contracts.case_1_2(contracts.case_1_1(value is Char))) { if (contracts.case_1_2(contracts.case_1_1(value is Char))) {
println(value.<!INAPPLICABLE_CANDIDATE!>category<!>) <!AMBIGUITY!>println<!>(value.<!UNRESOLVED_REFERENCE!>category<!>)
} }
} }
// TESTCASE NUMBER: 2 // TESTCASE NUMBER: 2
fun case_2(value: Any) { fun case_2(value: Any) {
if (contracts.case_2(value is Char) is Boolean) { if (contracts.case_2(value is Char) is Boolean) {
println(value.<!INAPPLICABLE_CANDIDATE!>category<!>) <!AMBIGUITY!>println<!>(value.<!UNRESOLVED_REFERENCE!>category<!>)
} }
} }
@@ -263,7 +263,7 @@ fun case_16() {
// TESTCASE NUMBER: 17 // TESTCASE NUMBER: 17
val case_17 = if (nullableIntProperty == null == true == false) 0 else { val case_17 = if (nullableIntProperty == null == true == false) 0 else {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>nullableIntProperty<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>nullableIntProperty<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>nullableIntProperty<!>.java <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>nullableIntProperty<!>.<!UNRESOLVED_REFERENCE!>java<!>
} }
//TESTCASE NUMBER: 18 //TESTCASE NUMBER: 18