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)
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
@@ -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 {