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:
Denis Zharkov
2017-10-03 19:36:28 +03:00
parent 9fdb702688
commit b99658b8eb
2 changed files with 36 additions and 33 deletions
@@ -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
@@ -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 {