diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index ee98596a225..e3a01804a34 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt index b6ad7266e69..34e82ce88e1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt @@ -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 = - 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? { - 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 {