Optimize CandidateResolver::checkReceiver
- Do not calculate smart cast variants unless they're not necessary - Avoid calling subtyping twice just to know if smart cast is necessary (now the needed info is contained in ReceiverSmartCastResult)
This commit is contained in:
@@ -448,7 +448,7 @@ class CandidateResolver(
|
|||||||
|
|
||||||
val erasedReceiverType = getErasedReceiverType(receiverParameterDescriptor, candidateDescriptor)
|
val erasedReceiverType = getErasedReceiverType(receiverParameterDescriptor, candidateDescriptor)
|
||||||
|
|
||||||
if (!smartCastManager.isSubTypeBySmartCastIgnoringNullability(receiverArgument, erasedReceiverType, this)) {
|
if (smartCastManager.getSmartCastReceiverResult(receiverArgument, erasedReceiverType, this) == null) {
|
||||||
RECEIVER_TYPE_ERROR
|
RECEIVER_TYPE_ERROR
|
||||||
} else {
|
} else {
|
||||||
SUCCESS
|
SUCCESS
|
||||||
@@ -505,22 +505,21 @@ class CandidateResolver(
|
|||||||
val candidateDescriptor = candidateCall.candidateDescriptor
|
val candidateDescriptor = candidateCall.candidateDescriptor
|
||||||
if (TypeUtils.dependsOnTypeParameters(receiverParameter.type, candidateDescriptor.typeParameters)) return SUCCESS
|
if (TypeUtils.dependsOnTypeParameters(receiverParameter.type, candidateDescriptor.typeParameters)) return SUCCESS
|
||||||
|
|
||||||
val isSubtypeBySmartCastIgnoringNullability = smartCastManager.isSubTypeBySmartCastIgnoringNullability(
|
// Here we know that receiver is OK ignoring nullability and check that nullability is OK too
|
||||||
receiverArgument, receiverParameter.type, this)
|
// Doing it simply as full subtyping check (receiverValueType <: receiverParameterType)
|
||||||
|
val call = candidateCall.call
|
||||||
|
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && call.isExplicitSafeCall()
|
||||||
|
val expectedReceiverParameterType = if (safeAccess) TypeUtils.makeNullable(receiverParameter.type) else receiverParameter.type
|
||||||
|
|
||||||
if (!isSubtypeBySmartCastIgnoringNullability) {
|
val smartCastSubtypingResult = smartCastManager.getSmartCastReceiverResult(receiverArgument, expectedReceiverParameterType, this)
|
||||||
|
if (smartCastSubtypingResult == null) {
|
||||||
tracing.wrongReceiverType(
|
tracing.wrongReceiverType(
|
||||||
trace, receiverParameter, receiverArgument,
|
trace, receiverParameter, receiverArgument,
|
||||||
this.replaceCallPosition(CallPosition.ExtensionReceiverPosition(candidateCall)))
|
this.replaceCallPosition(CallPosition.ExtensionReceiverPosition(candidateCall)))
|
||||||
return OTHER_ERROR
|
return OTHER_ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we know that receiver is OK ignoring nullability and check that nullability is OK too
|
val notNullReceiverExpected = smartCastSubtypingResult != SmartCastManager.ReceiverSmartCastResult.OK
|
||||||
// Doing it simply as full subtyping check (receiverValueType <: receiverParameterType)
|
|
||||||
val call = candidateCall.call
|
|
||||||
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && call.isExplicitSafeCall()
|
|
||||||
val expectedReceiverParameterType = if (safeAccess) TypeUtils.makeNullable(receiverParameter.type) else receiverParameter.type
|
|
||||||
val notNullReceiverExpected = !ArgumentTypeResolver.isSubtypeOfForArgumentType(receiverArgument.type, expectedReceiverParameterType)
|
|
||||||
val smartCastNeeded =
|
val smartCastNeeded =
|
||||||
notNullReceiverExpected || !isCandidateVisibleOrExtensionReceiver(receiverArgument, null, isDispatchReceiver)
|
notNullReceiverExpected || !isCandidateVisibleOrExtensionReceiver(receiverArgument, null, isDispatchReceiver)
|
||||||
var reportUnsafeCall = false
|
var reportUnsafeCall = false
|
||||||
|
|||||||
+27
-23
@@ -30,18 +30,11 @@ import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
|||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeIntersector
|
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class SmartCastManager {
|
class SmartCastManager {
|
||||||
|
|
||||||
private fun getSmartCastVariants(
|
|
||||||
receiverToCast: ReceiverValue,
|
|
||||||
context: ResolutionContext<*>
|
|
||||||
): List<KotlinType> =
|
|
||||||
getSmartCastVariants(receiverToCast, context.trace.bindingContext, context.scope.ownerDescriptor, context.dataFlowInfo)
|
|
||||||
|
|
||||||
fun getSmartCastVariants(
|
fun getSmartCastVariants(
|
||||||
receiverToCast: ReceiverValue,
|
receiverToCast: ReceiverValue,
|
||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
@@ -81,29 +74,40 @@ class SmartCastManager {
|
|||||||
return dataFlowInfo.getCollectedTypes(dataFlowValue)
|
return dataFlowInfo.getCollectedTypes(dataFlowValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSubTypeBySmartCastIgnoringNullability(
|
fun getSmartCastReceiverResult(
|
||||||
receiverArgument: ReceiverValue,
|
receiverArgument: ReceiverValue,
|
||||||
receiverParameterType: KotlinType,
|
receiverParameterType: KotlinType,
|
||||||
context: ResolutionContext<*>
|
context: ResolutionContext<*>
|
||||||
): Boolean {
|
): ReceiverSmartCastResult? {
|
||||||
val smartCastTypes = getSmartCastVariants(receiverArgument, context)
|
getSmartCastReceiverResultWithGivenNullability(receiverArgument, receiverParameterType, context)?.let {
|
||||||
return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
val nullableParameterType = TypeUtils.makeNullable(receiverParameterType)
|
||||||
|
return when {
|
||||||
|
getSmartCastReceiverResultWithGivenNullability(receiverArgument, nullableParameterType, context) == null -> null
|
||||||
|
else -> ReceiverSmartCastResult.SMARTCAST_NEEDED_OR_NOT_NULL_EXPECTED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSmartCastSubType(
|
private fun getSmartCastReceiverResultWithGivenNullability(
|
||||||
|
receiverArgument: ReceiverValue,
|
||||||
receiverParameterType: KotlinType,
|
receiverParameterType: KotlinType,
|
||||||
smartCastTypes: Collection<KotlinType>
|
context: ResolutionContext<*>
|
||||||
): KotlinType? {
|
): ReceiverSmartCastResult? =
|
||||||
val subTypes = smartCastTypes
|
when {
|
||||||
.filter { ArgumentTypeResolver.isSubtypeOfForArgumentType(it, receiverParameterType) }
|
ArgumentTypeResolver.isSubtypeOfForArgumentType(receiverArgument.type, receiverParameterType) ->
|
||||||
.distinct()
|
ReceiverSmartCastResult.OK
|
||||||
if (subTypes.isEmpty()) return null
|
getSmartCastVariantsExcludingReceiver(context, receiverArgument).any {
|
||||||
|
ArgumentTypeResolver.isSubtypeOfForArgumentType(it, receiverParameterType)
|
||||||
|
} ->
|
||||||
|
ReceiverSmartCastResult.SMARTCAST_NEEDED_OR_NOT_NULL_EXPECTED
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
val intersection = TypeIntersector.intersectTypes(subTypes)
|
enum class ReceiverSmartCastResult {
|
||||||
if (intersection == null || !intersection.constructor.isDenotable) {
|
OK,
|
||||||
return receiverParameterType
|
SMARTCAST_NEEDED_OR_NOT_NULL_EXPECTED
|
||||||
}
|
|
||||||
return intersection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
Reference in New Issue
Block a user