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)
|
||||
|
||||
if (!smartCastManager.isSubTypeBySmartCastIgnoringNullability(receiverArgument, erasedReceiverType, this)) {
|
||||
if (smartCastManager.getSmartCastReceiverResult(receiverArgument, erasedReceiverType, this) == null) {
|
||||
RECEIVER_TYPE_ERROR
|
||||
} else {
|
||||
SUCCESS
|
||||
@@ -505,22 +505,21 @@ class CandidateResolver(
|
||||
val candidateDescriptor = candidateCall.candidateDescriptor
|
||||
if (TypeUtils.dependsOnTypeParameters(receiverParameter.type, candidateDescriptor.typeParameters)) return SUCCESS
|
||||
|
||||
val isSubtypeBySmartCastIgnoringNullability = smartCastManager.isSubTypeBySmartCastIgnoringNullability(
|
||||
receiverArgument, receiverParameter.type, this)
|
||||
// Here we know that receiver is OK ignoring nullability and check that nullability is OK too
|
||||
// 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(
|
||||
trace, receiverParameter, receiverArgument,
|
||||
this.replaceCallPosition(CallPosition.ExtensionReceiverPosition(candidateCall)))
|
||||
return OTHER_ERROR
|
||||
}
|
||||
|
||||
// Here we know that receiver is OK ignoring nullability and check that nullability is OK too
|
||||
// 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 notNullReceiverExpected = smartCastSubtypingResult != SmartCastManager.ReceiverSmartCastResult.OK
|
||||
val smartCastNeeded =
|
||||
notNullReceiverExpected || !isCandidateVisibleOrExtensionReceiver(receiverArgument, null, isDispatchReceiver)
|
||||
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.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeIntersector
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import java.util.*
|
||||
|
||||
class SmartCastManager {
|
||||
|
||||
private fun getSmartCastVariants(
|
||||
receiverToCast: ReceiverValue,
|
||||
context: ResolutionContext<*>
|
||||
): List<KotlinType> =
|
||||
getSmartCastVariants(receiverToCast, context.trace.bindingContext, context.scope.ownerDescriptor, context.dataFlowInfo)
|
||||
|
||||
fun getSmartCastVariants(
|
||||
receiverToCast: ReceiverValue,
|
||||
bindingContext: BindingContext,
|
||||
@@ -81,29 +74,40 @@ class SmartCastManager {
|
||||
return dataFlowInfo.getCollectedTypes(dataFlowValue)
|
||||
}
|
||||
|
||||
fun isSubTypeBySmartCastIgnoringNullability(
|
||||
fun getSmartCastReceiverResult(
|
||||
receiverArgument: ReceiverValue,
|
||||
receiverParameterType: KotlinType,
|
||||
context: ResolutionContext<*>
|
||||
): Boolean {
|
||||
val smartCastTypes = getSmartCastVariants(receiverArgument, context)
|
||||
return getSmartCastSubType(TypeUtils.makeNullable(receiverParameterType), smartCastTypes) != null
|
||||
): ReceiverSmartCastResult? {
|
||||
getSmartCastReceiverResultWithGivenNullability(receiverArgument, receiverParameterType, context)?.let {
|
||||
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,
|
||||
smartCastTypes: Collection<KotlinType>
|
||||
): KotlinType? {
|
||||
val subTypes = smartCastTypes
|
||||
.filter { ArgumentTypeResolver.isSubtypeOfForArgumentType(it, receiverParameterType) }
|
||||
.distinct()
|
||||
if (subTypes.isEmpty()) return null
|
||||
context: ResolutionContext<*>
|
||||
): ReceiverSmartCastResult? =
|
||||
when {
|
||||
ArgumentTypeResolver.isSubtypeOfForArgumentType(receiverArgument.type, receiverParameterType) ->
|
||||
ReceiverSmartCastResult.OK
|
||||
getSmartCastVariantsExcludingReceiver(context, receiverArgument).any {
|
||||
ArgumentTypeResolver.isSubtypeOfForArgumentType(it, receiverParameterType)
|
||||
} ->
|
||||
ReceiverSmartCastResult.SMARTCAST_NEEDED_OR_NOT_NULL_EXPECTED
|
||||
else -> null
|
||||
}
|
||||
|
||||
val intersection = TypeIntersector.intersectTypes(subTypes)
|
||||
if (intersection == null || !intersection.constructor.isDenotable) {
|
||||
return receiverParameterType
|
||||
}
|
||||
return intersection
|
||||
enum class ReceiverSmartCastResult {
|
||||
OK,
|
||||
SMARTCAST_NEEDED_OR_NOT_NULL_EXPECTED
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
Reference in New Issue
Block a user