Support lazy perform for candidates

This commit is contained in:
Stanislav Erokhin
2015-07-15 18:39:13 +03:00
parent 7038b9d849
commit 97d41be42e
@@ -19,121 +19,121 @@ package org.jetbrains.kotlin.resolve.calls
import com.google.common.collect.Lists import com.google.common.collect.Lists
import com.google.common.collect.Sets import com.google.common.collect.Sets
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT
import org.jetbrains.kotlin.diagnostics.Errors.SUPER_CANT_BE_EXTENSION_RECEIVER
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.* import org.jetbrains.kotlin.resolve.calls.CallTransformer.CallForImplicitInvoke
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode
import org.jetbrains.kotlin.resolve.calls.callUtil.* import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS
import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedType
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getErasedReceiverType
import org.jetbrains.kotlin.resolve.calls.context.CheckValueArgumentsMode import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnExpressionWithBothReceivers
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.resolve.calls.callUtil.isExplicitSafeCall
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.context.*
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatchStatus
import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.*
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils.canBeSmartCast
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils.getSmartCastVariantsExcludingReceiver
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils.isSubTypeBySmartCastIgnoringNullability
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils.recordSmartCastIfNecessary
import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionTask import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionTask
import org.jetbrains.kotlin.resolve.calls.tasks.* import org.jetbrains.kotlin.resolve.calls.tasks.isSynthesizedInvoke
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.TypeUtils.noExpectedType
import org.jetbrains.kotlin.types.checker.JetTypeChecker import org.jetbrains.kotlin.types.checker.JetTypeChecker
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.expressions.JetTypeInfo
import javax.inject.Inject
import java.util.ArrayList import java.util.ArrayList
import org.jetbrains.kotlin.diagnostics.Errors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT
import org.jetbrains.kotlin.diagnostics.Errors.SUPER_CANT_BE_EXTENSION_RECEIVER
import org.jetbrains.kotlin.resolve.calls.CallTransformer.CallForImplicitInvoke
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus.*
import org.jetbrains.kotlin.types.TypeUtils.noExpectedType
public class CandidateResolver( public class CandidateResolver(
private val argumentTypeResolver: ArgumentTypeResolver, private val argumentTypeResolver: ArgumentTypeResolver,
private val genericCandidateResolver: GenericCandidateResolver private val genericCandidateResolver: GenericCandidateResolver
){ ){
public fun <D : CallableDescriptor, F : D> performResolutionForCandidateCall( public fun <D : CallableDescriptor, F : D> performResolutionForCandidateCall(context: CallCandidateResolutionContext<D>,
context: CallCandidateResolutionContext<D>, task: ResolutionTask<D, F>): Unit = with(context) {
task: ResolutionTask<D, F>) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val candidateCall = context.candidateCall if (ErrorUtils.isError(candidateDescriptor)) {
val candidate = candidateCall.getCandidateDescriptor()
candidateCall.addStatus(checkReceiverTypeError(context))
if (ErrorUtils.isError(candidate)) {
candidateCall.addStatus(SUCCESS) candidateCall.addStatus(SUCCESS)
return return
} }
if (!checkOuterClassMemberIsAccessible(context)) { if (!checkOuterClassMemberIsAccessible(this)) {
candidateCall.addStatus(OTHER_ERROR) candidateCall.addStatus(OTHER_ERROR)
return return
} }
checkVisibility()
mapArguments(task)
val receiverValue = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidateCall.getDispatchReceiver(), context.trace.getBindingContext()) checkReceiverTypeError()
val invisibleMember = Visibilities.findInvisibleMember(receiverValue, candidate, context.scope.getContainingDeclaration()) checkExtensionReceiver()
if (invisibleMember != null) { checkDispatchReceiver()
candidateCall.addStatus(OTHER_ERROR)
context.tracing.invisibleMember(context.trace, invisibleMember) processTypeArguments()
checkValueArguments()
checkAbstractAndSuper()
checkNonExtensionCalledWithReceiver()
}
private fun CallCandidateResolutionContext<*>.checkValueArguments() = checkAndReport {
if (call.getTypeArguments().isEmpty()
&& !candidateDescriptor.getTypeParameters().isEmpty()
&& candidateCall.getKnownTypeParametersSubstitutor() == null
) {
genericCandidateResolver.inferTypeArguments(this)
} }
else {
if (task.checkArguments == CheckValueArgumentsMode.ENABLED) { checkAllValueArguments(this, SHAPE_FUNCTION_ARGUMENTS).status
val argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(
context.call, context.tracing, candidateCall, Sets.newLinkedHashSet<ValueArgument>())
if (!argumentMappingStatus.isSuccess()) {
candidateCall.addStatus(OTHER_ERROR)
}
} }
}
checkExtensionReceiver(context) private fun CallCandidateResolutionContext<*>.processTypeArguments() = check {
val jetTypeArguments = call.getTypeArguments()
if (!checkDispatchReceiver(context)) {
candidateCall.addStatus(OTHER_ERROR)
}
val jetTypeArguments = context.call.getTypeArguments()
if (!jetTypeArguments.isEmpty()) { if (!jetTypeArguments.isEmpty()) {
// Explicit type arguments passed // Explicit type arguments passed
val typeArguments = ArrayList<JetType>() val typeArguments = ArrayList<JetType>()
for (projection in jetTypeArguments) { for (projection in jetTypeArguments) {
if (projection.getProjectionKind() != JetProjectionKind.NONE) { if (projection.getProjectionKind() != JetProjectionKind.NONE) {
context.trace.report(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT.on(projection)) trace.report(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT.on(projection))
ModifiersChecker.checkIncompatibleVarianceModifiers(projection.getModifierList(), context.trace) ModifiersChecker.checkIncompatibleVarianceModifiers(projection.getModifierList(), trace)
} }
val type = argumentTypeResolver.resolveTypeRefWithDefault( val type = argumentTypeResolver.resolveTypeRefWithDefault(
projection.getTypeReference(), context.scope, context.trace, projection.getTypeReference(), scope, trace,
ErrorUtils.createErrorType("Star projection in a call"))!! ErrorUtils.createErrorType("Star projection in a call"))!!
ForceResolveUtil.forceResolveAllContents(type) ForceResolveUtil.forceResolveAllContents(type)
typeArguments.add(type) typeArguments.add(type)
} }
val expectedTypeArgumentCount = candidate.getTypeParameters().size() val expectedTypeArgumentCount = candidateDescriptor.getTypeParameters().size()
for (index in jetTypeArguments.size()..expectedTypeArgumentCount - 1) { for (index in jetTypeArguments.size()..expectedTypeArgumentCount - 1) {
typeArguments.add(ErrorUtils.createErrorType( typeArguments.add(ErrorUtils.createErrorType(
"Explicit type argument expected for " + candidate.getTypeParameters().get(index).getName())) "Explicit type argument expected for " + candidateDescriptor.getTypeParameters().get(index).getName()))
} }
val substitutionContext = FunctionDescriptorUtil.createSubstitutionContext(candidate as FunctionDescriptor, typeArguments) val substitutionContext = FunctionDescriptorUtil.createSubstitutionContext(candidateDescriptor as FunctionDescriptor, typeArguments)
val substitutor = TypeSubstitutor.create(substitutionContext) val substitutor = TypeSubstitutor.create(substitutionContext)
if (expectedTypeArgumentCount != jetTypeArguments.size()) { if (expectedTypeArgumentCount != jetTypeArguments.size()) {
candidateCall.addStatus(OTHER_ERROR) candidateCall.addStatus(OTHER_ERROR)
context.tracing.wrongNumberOfTypeArguments(context.trace, expectedTypeArgumentCount) tracing.wrongNumberOfTypeArguments(trace, expectedTypeArgumentCount)
} }
else { else {
checkGenericBoundsInAFunctionCall(jetTypeArguments, typeArguments, candidate, substitutor, context.trace) checkGenericBoundsInAFunctionCall(jetTypeArguments, typeArguments, candidateDescriptor, substitutor, trace)
} }
candidateCall.setResultingSubstitutor(substitutor) candidateCall.setResultingSubstitutor(substitutor)
@@ -141,59 +141,75 @@ public class CandidateResolver(
else if (candidateCall.getKnownTypeParametersSubstitutor() != null) { else if (candidateCall.getKnownTypeParametersSubstitutor() != null) {
candidateCall.setResultingSubstitutor(candidateCall.getKnownTypeParametersSubstitutor()!!) candidateCall.setResultingSubstitutor(candidateCall.getKnownTypeParametersSubstitutor()!!)
} }
if (jetTypeArguments.isEmpty() && !candidate.getTypeParameters().isEmpty() && candidateCall.getKnownTypeParametersSubstitutor() == null) {
candidateCall.addStatus(genericCandidateResolver.inferTypeArguments(context))
}
else {
candidateCall.addStatus(checkAllValueArguments(context, SHAPE_FUNCTION_ARGUMENTS).status)
}
checkAbstractAndSuper(context)
checkNonExtensionCalledWithReceiver(context)
} }
private fun <D : CallableDescriptor> checkExtensionReceiver(context: CallCandidateResolutionContext<D>) { private fun <D : CallableDescriptor, F : D> CallCandidateResolutionContext<D>.mapArguments(task: ResolutionTask<D, F>) = check {
val candidateCall = context.candidateCall if (task.checkArguments == CheckValueArgumentsMode.ENABLED) {
val receiverParameter = candidateCall.getCandidateDescriptor().getExtensionReceiverParameter() val argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(
val receiverArgument = candidateCall.getExtensionReceiver() call, tracing, candidateCall, Sets.newLinkedHashSet<ValueArgument>())
if (receiverParameter != null && !receiverArgument.exists()) { if (!argumentMappingStatus.isSuccess()) {
context.tracing.missingReceiver(candidateCall.getTrace(), receiverParameter)
candidateCall.addStatus(OTHER_ERROR)
}
if (receiverParameter == null && receiverArgument.exists()) {
context.tracing.noReceiverAllowed(candidateCall.getTrace())
if (context.call.getCalleeExpression() is JetSimpleNameExpression) {
candidateCall.addStatus(RECEIVER_PRESENCE_ERROR)
}
else {
candidateCall.addStatus(OTHER_ERROR) candidateCall.addStatus(OTHER_ERROR)
} }
} }
} }
private fun checkDispatchReceiver(context: CallCandidateResolutionContext<*>): Boolean { private fun CallCandidateResolutionContext<*>.checkVisibility() = checkAndReport {
val candidateCall = context.candidateCall val receiverValue = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidateCall.getDispatchReceiver(),
val candidateDescriptor = candidateCall.getCandidateDescriptor() trace.getBindingContext())
val invisibleMember = Visibilities.findInvisibleMember(receiverValue, candidateDescriptor, scope.getContainingDeclaration())
if (invisibleMember != null) {
tracing.invisibleMember(trace, invisibleMember)
OTHER_ERROR
} else {
SUCCESS
}
}
private fun CallCandidateResolutionContext<*>.checkExtensionReceiver() = checkAndReport {
val receiverParameter = candidateCall.getCandidateDescriptor().getExtensionReceiverParameter()
val receiverArgument = candidateCall.getExtensionReceiver()
if (receiverParameter != null && !receiverArgument.exists()) {
tracing.missingReceiver(candidateCall.getTrace(), receiverParameter)
OTHER_ERROR
}
else if (receiverParameter == null && receiverArgument.exists()) {
tracing.noReceiverAllowed(candidateCall.getTrace())
if (call.getCalleeExpression() is JetSimpleNameExpression) {
RECEIVER_PRESENCE_ERROR
}
else {
OTHER_ERROR
}
}
else {
SUCCESS
}
}
private fun CallCandidateResolutionContext<*>.checkDispatchReceiver() = checkAndReport {
val candidateDescriptor = candidateDescriptor
val dispatchReceiver = candidateCall.getDispatchReceiver() val dispatchReceiver = candidateCall.getDispatchReceiver()
if (dispatchReceiver.exists()) { if (dispatchReceiver.exists()) {
var nestedClass: ClassDescriptor? = null var nestedClass: ClassDescriptor? = null
if (candidateDescriptor is ConstructorDescriptor && DescriptorUtils.isStaticNestedClass(candidateDescriptor.getContainingDeclaration())) { if (candidateDescriptor is ConstructorDescriptor
&& DescriptorUtils.isStaticNestedClass(candidateDescriptor.getContainingDeclaration())
) {
nestedClass = candidateDescriptor.getContainingDeclaration() nestedClass = candidateDescriptor.getContainingDeclaration()
} }
else if (candidateDescriptor is FakeCallableDescriptorForObject) { else if (candidateDescriptor is FakeCallableDescriptorForObject) {
nestedClass = candidateDescriptor.getReferencedDescriptor() nestedClass = candidateDescriptor.getReferencedDescriptor()
} }
if (nestedClass != null) { if (nestedClass != null) {
context.tracing.nestedClassAccessViaInstanceReference(context.trace, nestedClass, candidateCall.getExplicitReceiverKind()) tracing.nestedClassAccessViaInstanceReference(trace, nestedClass, candidateCall.getExplicitReceiverKind())
return false return@checkAndReport OTHER_ERROR
} }
} }
assert((dispatchReceiver.exists() == (candidateCall.getResultingDescriptor().getDispatchReceiverParameter() != null))) { "Shouldn't happen because of TaskPrioritizer: $candidateDescriptor" } assert((dispatchReceiver.exists() == (candidateCall.getResultingDescriptor().getDispatchReceiverParameter() != null))) {
"Shouldn't happen because of TaskPrioritizer: $candidateDescriptor"
}
return true SUCCESS
} }
private fun checkOuterClassMemberIsAccessible(context: CallCandidateResolutionContext<*>): Boolean { private fun checkOuterClassMemberIsAccessible(context: CallCandidateResolutionContext<*>): Boolean {
@@ -206,17 +222,16 @@ public class CandidateResolver(
return DescriptorResolver.checkHasOuterClassInstance(context.scope, context.trace, context.call.getCallElement(), candidateThis) return DescriptorResolver.checkHasOuterClassInstance(context.scope, context.trace, context.call.getCallElement(), candidateThis)
} }
private fun <D : CallableDescriptor> checkAbstractAndSuper(context: CallCandidateResolutionContext<D>) { private fun CallCandidateResolutionContext<*>.checkAbstractAndSuper() = check {
val candidateCall = context.candidateCall val descriptor = candidateDescriptor
val descriptor = candidateCall.getCandidateDescriptor() val expression = candidateCall.getCall().getCalleeExpression()
val expression = context.candidateCall.getCall().getCalleeExpression()
if (expression is JetSimpleNameExpression) { if (expression is JetSimpleNameExpression) {
// 'B' in 'class A: B()' is JetConstructorCalleeExpression // 'B' in 'class A: B()' is JetConstructorCalleeExpression
if (descriptor is ConstructorDescriptor) { if (descriptor is ConstructorDescriptor) {
val modality = descriptor.getContainingDeclaration().getModality() val modality = descriptor.getContainingDeclaration().getModality()
if (modality == Modality.ABSTRACT) { if (modality == Modality.ABSTRACT) {
context.tracing.instantiationOfAbstractClass(context.trace) tracing.instantiationOfAbstractClass(trace)
} }
} }
} }
@@ -224,7 +239,7 @@ public class CandidateResolver(
val superDispatchReceiver = getReceiverSuper(candidateCall.getDispatchReceiver()) val superDispatchReceiver = getReceiverSuper(candidateCall.getDispatchReceiver())
if (superDispatchReceiver != null) { if (superDispatchReceiver != null) {
if (descriptor is MemberDescriptor && descriptor.getModality() == Modality.ABSTRACT) { if (descriptor is MemberDescriptor && descriptor.getModality() == Modality.ABSTRACT) {
context.tracing.abstractSuperCall(context.trace) tracing.abstractSuperCall(trace)
candidateCall.addStatus(OTHER_ERROR) candidateCall.addStatus(OTHER_ERROR)
} }
} }
@@ -233,17 +248,19 @@ public class CandidateResolver(
// See TaskPrioritizer for more // See TaskPrioritizer for more
val superExtensionReceiver = getReceiverSuper(candidateCall.getExtensionReceiver()) val superExtensionReceiver = getReceiverSuper(candidateCall.getExtensionReceiver())
if (superExtensionReceiver != null) { if (superExtensionReceiver != null) {
context.trace.report(SUPER_CANT_BE_EXTENSION_RECEIVER.on(superExtensionReceiver, superExtensionReceiver.getText())) trace.report(SUPER_CANT_BE_EXTENSION_RECEIVER.on(superExtensionReceiver, superExtensionReceiver.getText()))
candidateCall.addStatus(OTHER_ERROR) candidateCall.addStatus(OTHER_ERROR)
} }
} }
private fun checkNonExtensionCalledWithReceiver(context: CallCandidateResolutionContext<*>) { private fun CallCandidateResolutionContext<*>.checkNonExtensionCalledWithReceiver() = checkAndReport {
val candidateCall = context.candidateCall if (isSynthesizedInvoke(candidateCall.getCandidateDescriptor())
&& !KotlinBuiltIns.isExtensionFunctionType(candidateCall.getDispatchReceiver().getType())
if (isSynthesizedInvoke(candidateCall.getCandidateDescriptor()) && !KotlinBuiltIns.isExtensionFunctionType(candidateCall.getDispatchReceiver().getType())) { ) {
context.tracing.freeFunctionCalledAsExtension(context.trace) tracing.freeFunctionCalledAsExtension(trace)
candidateCall.addStatus(OTHER_ERROR) OTHER_ERROR
} else {
SUCCESS
} }
} }
@@ -287,8 +304,6 @@ public class CandidateResolver(
var resultStatus = SUCCESS var resultStatus = SUCCESS
val candidateCall = context.candidateCall val candidateCall = context.candidateCall
resultStatus = resultStatus.combine(checkReceiverTypeError(context))
// Comment about a very special case. // Comment about a very special case.
// Call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' should be checked two times for safe call (in 'checkReceiver'), because // Call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' should be checked two times for safe call (in 'checkReceiver'), because
// both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'. // both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'.
@@ -365,7 +380,7 @@ public class CandidateResolver(
actualType: JetType, actualType: JetType,
context: ResolutionContext<*>): JetType? { context: ResolutionContext<*>): JetType? {
val receiverToCast = ExpressionReceiver(JetPsiUtil.safeDeparenthesize(expression, false), actualType) val receiverToCast = ExpressionReceiver(JetPsiUtil.safeDeparenthesize(expression, false), actualType)
val variants = SmartCastUtils.getSmartCastVariantsExcludingReceiver(context, receiverToCast) val variants = getSmartCastVariantsExcludingReceiver(context, receiverToCast)
for (possibleType in variants) { for (possibleType in variants) {
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) { if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) {
return possibleType return possibleType
@@ -374,39 +389,31 @@ public class CandidateResolver(
return null return null
} }
private fun <D : CallableDescriptor> checkReceiverTypeError( private fun CallCandidateResolutionContext<*>.checkReceiverTypeError(): Unit = check {
context: CallCandidateResolutionContext<D>): ResolutionStatus {
val candidateCall = context.candidateCall
val candidateDescriptor = candidateCall.getCandidateDescriptor()
val extensionReceiver = candidateDescriptor.getExtensionReceiverParameter() val extensionReceiver = candidateDescriptor.getExtensionReceiverParameter()
val dispatchReceiver = candidateDescriptor.getDispatchReceiverParameter() val dispatchReceiver = candidateDescriptor.getDispatchReceiverParameter()
var status = SUCCESS
// For the expressions like '42.(f)()' where f: String.() -> Unit we'd like to generate a type mismatch error on '1', // For the expressions like '42.(f)()' where f: String.() -> Unit we'd like to generate a type mismatch error on '1',
// not to throw away the candidate, so the following check is skipped. // not to throw away the candidate, so the following check is skipped.
if (!isInvokeCallOnExpressionWithBothReceivers(context.call)) { if (!isInvokeCallOnExpressionWithBothReceivers(call)) {
status = status.combine(checkReceiverTypeError(context, extensionReceiver, candidateCall.getExtensionReceiver())) checkReceiverTypeError(extensionReceiver, candidateCall.getExtensionReceiver())
} }
status = status.combine(checkReceiverTypeError(context, dispatchReceiver, candidateCall.getDispatchReceiver())) checkReceiverTypeError(dispatchReceiver, candidateCall.getDispatchReceiver())
return status
} }
private fun <D : CallableDescriptor> checkReceiverTypeError( private fun CallCandidateResolutionContext<*>.checkReceiverTypeError(
context: CallCandidateResolutionContext<D>,
receiverParameterDescriptor: ReceiverParameterDescriptor?, receiverParameterDescriptor: ReceiverParameterDescriptor?,
receiverArgument: ReceiverValue): ResolutionStatus { receiverArgument: ReceiverValue
if (receiverParameterDescriptor == null || !receiverArgument.exists()) return SUCCESS ) = checkAndReport {
if (receiverParameterDescriptor == null || !receiverArgument.exists()) return@checkAndReport SUCCESS
val candidateDescriptor = context.candidateCall.getCandidateDescriptor()
val erasedReceiverType = getErasedReceiverType(receiverParameterDescriptor, candidateDescriptor) val erasedReceiverType = getErasedReceiverType(receiverParameterDescriptor, candidateDescriptor)
val isSubtypeBySmartCast = SmartCastUtils.isSubTypeBySmartCastIgnoringNullability(receiverArgument, erasedReceiverType, context) if (!isSubTypeBySmartCastIgnoringNullability(receiverArgument, erasedReceiverType, this)) {
if (!isSubtypeBySmartCast) { RECEIVER_TYPE_ERROR
return RECEIVER_TYPE_ERROR } else {
SUCCESS
} }
return SUCCESS
} }
private fun <D : CallableDescriptor> checkReceiver( private fun <D : CallableDescriptor> checkReceiver(
@@ -422,13 +429,13 @@ public class CandidateResolver(
if (TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) return SUCCESS if (TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) return SUCCESS
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.getCall().isExplicitSafeCall() val safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.getCall().isExplicitSafeCall()
val isSubtypeBySmartCast = SmartCastUtils.isSubTypeBySmartCastIgnoringNullability( val isSubtypeBySmartCast = isSubTypeBySmartCastIgnoringNullability(
receiverArgument, receiverParameter.getType(), context) receiverArgument, receiverParameter.getType(), context)
if (!isSubtypeBySmartCast) { if (!isSubtypeBySmartCast) {
context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument) context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument)
return OTHER_ERROR return OTHER_ERROR
} }
if (!SmartCastUtils.recordSmartCastIfNecessary(receiverArgument, receiverParameter.getType(), context, safeAccess)) { if (!recordSmartCastIfNecessary(receiverArgument, receiverParameter.getType(), context, safeAccess)) {
return OTHER_ERROR return OTHER_ERROR
} }
@@ -436,7 +443,7 @@ public class CandidateResolver(
val bindingContext = trace.getBindingContext() val bindingContext = trace.getBindingContext()
if (!safeAccess && !receiverParameter.getType().isMarkedNullable() && receiverArgumentType.isMarkedNullable()) { if (!safeAccess && !receiverParameter.getType().isMarkedNullable() && receiverArgumentType.isMarkedNullable()) {
if (!SmartCastUtils.canBeSmartCast(receiverParameter, receiverArgument, context)) { if (!canBeSmartCast(receiverParameter, receiverArgument, context)) {
context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck) context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck)
return UNSAFE_CALL_ERROR return UNSAFE_CALL_ERROR
} }
@@ -469,4 +476,20 @@ public class CandidateResolver(
} }
} }
} }
private fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.shouldContinue() =
candidateResolveMode == CandidateResolveMode.FULLY || candidateCall.getStatus().possibleTransformToSuccess()
private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>
.check(checker: CallCandidateResolutionContext<D>.() -> Unit): Unit = if (shouldContinue()) checker()
private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.
checkAndReport(checker: CallCandidateResolutionContext<D>.() -> ResolutionStatus) {
if (shouldContinue()) {
candidateCall.addStatus(checker())
}
}
private val CallCandidateResolutionContext<*>.candidateDescriptor: CallableDescriptor get() = candidateCall.getCandidateDescriptor()
} }