[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
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
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.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.types.AbstractTypeChecker
internal class CandidateFactoriesAndCollectors(
// Common calls
@@ -88,31 +79,6 @@ private class TowerScopeLevelProcessor(
scope: FirScope,
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(
group, candidateFactory.createCandidate(
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.scopes.FirScope
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.processClassifiersByName
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
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.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.types.AbstractTypeChecker
import org.jetbrains.kotlin.util.OperatorNameConventions
abstract class TowerScopeLevel {
@@ -178,12 +184,6 @@ class ScopeTowerLevel(
private val extensionsOnly: Boolean,
private val includeInnerConstructors: Boolean
) : SessionBasedTowerLevel(session) {
private fun FirCallableSymbol<*>.hasConsistentReceivers(extensionReceiver: Receiver?): Boolean =
when {
extensionsOnly && !hasExtensionReceiver() -> false
!hasConsistentExtensionReceiver(extensionReceiver) -> false
else -> true
}
private fun dispatchReceiverValue(candidate: FirCallableSymbol<*>): ReceiverValue? {
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(
candidate: FirCallableSymbol<*>,
processor: TowerScopeLevelProcessor<T>
) {
if (candidate.hasConsistentReceivers(extensionReceiver)) {
val dispatchReceiverValue = dispatchReceiverValue(candidate)
val unwrappedCandidate = candidate.fir.importedFromObjectData?.original?.symbol ?: candidate
@Suppress("UNCHECKED_CAST")
processor.consumeCandidate(
unwrappedCandidate as T, dispatchReceiverValue,
extensionReceiverValue = extensionReceiver,
scope
)
val candidateReceiverTypeRef = candidate.fir.receiverTypeRef
val receiverExpected = extensionsOnly || extensionReceiver != null
if (candidateReceiverTypeRef == null == receiverExpected) return
val dispatchReceiverValue = dispatchReceiverValue(candidate)
if (dispatchReceiverValue == null && shouldSkipCandidateWithInconsistentExtensionReceiver(candidate)) {
return
}
val unwrappedCandidate = candidate.fir.importedFromObjectData?.original?.symbol ?: candidate
@Suppress("UNCHECKED_CAST")
processor.consumeCandidate(
unwrappedCandidate as T, dispatchReceiverValue,
extensionReceiverValue = extensionReceiver,
scope
)
}
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() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
c.startCoroutine(EmptyContinuation)
}
suspend fun calculate() = "OK"
@@ -20,7 +20,7 @@ inline fun test(noinline c: () -> Unit) {
}
}
val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
c.startCoroutine(EmptyContinuation)
}
suspend fun calculate() = "OK"
@@ -21,7 +21,7 @@ inline fun test(c: () -> Unit) {
}
}
val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
c.startCoroutine(EmptyContinuation)
}
fun builder(c: suspend () -> Unit) {
@@ -20,7 +20,7 @@ suspend inline fun test(crossinline c: () -> Unit) {
}
}
val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
c.startCoroutine(EmptyContinuation)
}
fun builder(c: suspend () -> Unit) {
@@ -20,7 +20,7 @@ suspend inline fun test(noinline c: () -> Unit) {
}
}
val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
c.startCoroutine(EmptyContinuation)
}
fun builder(c: suspend () -> Unit) {
@@ -20,7 +20,7 @@ suspend inline fun test(c: () -> Unit) {
}
}
val l = { c() }
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
c.startCoroutine(EmptyContinuation)
}
suspend fun calculate() = "OK"
@@ -6,7 +6,7 @@
class Case1(val nothing: Nothing)
fun case1() {
val res = <!INAPPLICABLE_CANDIDATE!>Case1<!>(<!INAPPLICABLE_CANDIDATE!>Nothing<!>())
val res = Case1(<!HIDDEN!>Nothing<!>())
}
@@ -30,14 +30,14 @@ import contracts.*
// TESTCASE NUMBER: 1
fun case_1(value: Any) {
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
fun case_2(value: Any) {
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
val case_17 = if (nullableIntProperty == null == true == false) 0 else {
<!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