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.Sets
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
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.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.callUtil.*
import org.jetbrains.kotlin.resolve.calls.context.CallCandidateResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CheckValueArgumentsMode
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedType
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getErasedReceiverType
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnExpressionWithBothReceivers
import org.jetbrains.kotlin.resolve.calls.callUtil.isExplicitSafeCall
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.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.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.*
import org.jetbrains.kotlin.resolve.calls.tasks.isSynthesizedInvoke
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
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.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.expressions.JetTypeInfo
import javax.inject.Inject
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(
private val argumentTypeResolver: ArgumentTypeResolver,
private val genericCandidateResolver: GenericCandidateResolver
){
public fun <D : CallableDescriptor, F : D> performResolutionForCandidateCall(
context: CallCandidateResolutionContext<D>,
task: ResolutionTask<D, F>) {
public fun <D : CallableDescriptor, F : D> performResolutionForCandidateCall(context: CallCandidateResolutionContext<D>,
task: ResolutionTask<D, F>): Unit = with(context) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val candidateCall = context.candidateCall
val candidate = candidateCall.getCandidateDescriptor()
candidateCall.addStatus(checkReceiverTypeError(context))
if (ErrorUtils.isError(candidate)) {
if (ErrorUtils.isError(candidateDescriptor)) {
candidateCall.addStatus(SUCCESS)
return
}
if (!checkOuterClassMemberIsAccessible(context)) {
if (!checkOuterClassMemberIsAccessible(this)) {
candidateCall.addStatus(OTHER_ERROR)
return
}
checkVisibility()
mapArguments(task)
val receiverValue = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidateCall.getDispatchReceiver(), context.trace.getBindingContext())
val invisibleMember = Visibilities.findInvisibleMember(receiverValue, candidate, context.scope.getContainingDeclaration())
if (invisibleMember != null) {
candidateCall.addStatus(OTHER_ERROR)
context.tracing.invisibleMember(context.trace, invisibleMember)
checkReceiverTypeError()
checkExtensionReceiver()
checkDispatchReceiver()
processTypeArguments()
checkValueArguments()
checkAbstractAndSuper()
checkNonExtensionCalledWithReceiver()
}
private fun CallCandidateResolutionContext<*>.checkValueArguments() = checkAndReport {
if (call.getTypeArguments().isEmpty()
&& !candidateDescriptor.getTypeParameters().isEmpty()
&& candidateCall.getKnownTypeParametersSubstitutor() == null
) {
genericCandidateResolver.inferTypeArguments(this)
}
if (task.checkArguments == CheckValueArgumentsMode.ENABLED) {
val argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(
context.call, context.tracing, candidateCall, Sets.newLinkedHashSet<ValueArgument>())
if (!argumentMappingStatus.isSuccess()) {
candidateCall.addStatus(OTHER_ERROR)
}
else {
checkAllValueArguments(this, SHAPE_FUNCTION_ARGUMENTS).status
}
}
checkExtensionReceiver(context)
if (!checkDispatchReceiver(context)) {
candidateCall.addStatus(OTHER_ERROR)
}
val jetTypeArguments = context.call.getTypeArguments()
private fun CallCandidateResolutionContext<*>.processTypeArguments() = check {
val jetTypeArguments = call.getTypeArguments()
if (!jetTypeArguments.isEmpty()) {
// Explicit type arguments passed
val typeArguments = ArrayList<JetType>()
for (projection in jetTypeArguments) {
if (projection.getProjectionKind() != JetProjectionKind.NONE) {
context.trace.report(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT.on(projection))
ModifiersChecker.checkIncompatibleVarianceModifiers(projection.getModifierList(), context.trace)
trace.report(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT.on(projection))
ModifiersChecker.checkIncompatibleVarianceModifiers(projection.getModifierList(), trace)
}
val type = argumentTypeResolver.resolveTypeRefWithDefault(
projection.getTypeReference(), context.scope, context.trace,
projection.getTypeReference(), scope, trace,
ErrorUtils.createErrorType("Star projection in a call"))!!
ForceResolveUtil.forceResolveAllContents(type)
typeArguments.add(type)
}
val expectedTypeArgumentCount = candidate.getTypeParameters().size()
val expectedTypeArgumentCount = candidateDescriptor.getTypeParameters().size()
for (index in jetTypeArguments.size()..expectedTypeArgumentCount - 1) {
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)
if (expectedTypeArgumentCount != jetTypeArguments.size()) {
candidateCall.addStatus(OTHER_ERROR)
context.tracing.wrongNumberOfTypeArguments(context.trace, expectedTypeArgumentCount)
tracing.wrongNumberOfTypeArguments(trace, expectedTypeArgumentCount)
}
else {
checkGenericBoundsInAFunctionCall(jetTypeArguments, typeArguments, candidate, substitutor, context.trace)
checkGenericBoundsInAFunctionCall(jetTypeArguments, typeArguments, candidateDescriptor, substitutor, trace)
}
candidateCall.setResultingSubstitutor(substitutor)
@@ -141,59 +141,75 @@ public class CandidateResolver(
else if (candidateCall.getKnownTypeParametersSubstitutor() != null) {
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>) {
val candidateCall = context.candidateCall
val receiverParameter = candidateCall.getCandidateDescriptor().getExtensionReceiverParameter()
val receiverArgument = candidateCall.getExtensionReceiver()
if (receiverParameter != null && !receiverArgument.exists()) {
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 {
private fun <D : CallableDescriptor, F : D> CallCandidateResolutionContext<D>.mapArguments(task: ResolutionTask<D, F>) = check {
if (task.checkArguments == CheckValueArgumentsMode.ENABLED) {
val argumentMappingStatus = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(
call, tracing, candidateCall, Sets.newLinkedHashSet<ValueArgument>())
if (!argumentMappingStatus.isSuccess()) {
candidateCall.addStatus(OTHER_ERROR)
}
}
}
private fun checkDispatchReceiver(context: CallCandidateResolutionContext<*>): Boolean {
val candidateCall = context.candidateCall
val candidateDescriptor = candidateCall.getCandidateDescriptor()
private fun CallCandidateResolutionContext<*>.checkVisibility() = checkAndReport {
val receiverValue = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidateCall.getDispatchReceiver(),
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()
if (dispatchReceiver.exists()) {
var nestedClass: ClassDescriptor? = null
if (candidateDescriptor is ConstructorDescriptor && DescriptorUtils.isStaticNestedClass(candidateDescriptor.getContainingDeclaration())) {
if (candidateDescriptor is ConstructorDescriptor
&& DescriptorUtils.isStaticNestedClass(candidateDescriptor.getContainingDeclaration())
) {
nestedClass = candidateDescriptor.getContainingDeclaration()
}
else if (candidateDescriptor is FakeCallableDescriptorForObject) {
nestedClass = candidateDescriptor.getReferencedDescriptor()
}
if (nestedClass != null) {
context.tracing.nestedClassAccessViaInstanceReference(context.trace, nestedClass, candidateCall.getExplicitReceiverKind())
return false
tracing.nestedClassAccessViaInstanceReference(trace, nestedClass, candidateCall.getExplicitReceiverKind())
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 {
@@ -206,17 +222,16 @@ public class CandidateResolver(
return DescriptorResolver.checkHasOuterClassInstance(context.scope, context.trace, context.call.getCallElement(), candidateThis)
}
private fun <D : CallableDescriptor> checkAbstractAndSuper(context: CallCandidateResolutionContext<D>) {
val candidateCall = context.candidateCall
val descriptor = candidateCall.getCandidateDescriptor()
val expression = context.candidateCall.getCall().getCalleeExpression()
private fun CallCandidateResolutionContext<*>.checkAbstractAndSuper() = check {
val descriptor = candidateDescriptor
val expression = candidateCall.getCall().getCalleeExpression()
if (expression is JetSimpleNameExpression) {
// 'B' in 'class A: B()' is JetConstructorCalleeExpression
if (descriptor is ConstructorDescriptor) {
val modality = descriptor.getContainingDeclaration().getModality()
if (modality == Modality.ABSTRACT) {
context.tracing.instantiationOfAbstractClass(context.trace)
tracing.instantiationOfAbstractClass(trace)
}
}
}
@@ -224,7 +239,7 @@ public class CandidateResolver(
val superDispatchReceiver = getReceiverSuper(candidateCall.getDispatchReceiver())
if (superDispatchReceiver != null) {
if (descriptor is MemberDescriptor && descriptor.getModality() == Modality.ABSTRACT) {
context.tracing.abstractSuperCall(context.trace)
tracing.abstractSuperCall(trace)
candidateCall.addStatus(OTHER_ERROR)
}
}
@@ -233,17 +248,19 @@ public class CandidateResolver(
// See TaskPrioritizer for more
val superExtensionReceiver = getReceiverSuper(candidateCall.getExtensionReceiver())
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)
}
}
private fun checkNonExtensionCalledWithReceiver(context: CallCandidateResolutionContext<*>) {
val candidateCall = context.candidateCall
if (isSynthesizedInvoke(candidateCall.getCandidateDescriptor()) && !KotlinBuiltIns.isExtensionFunctionType(candidateCall.getDispatchReceiver().getType())) {
context.tracing.freeFunctionCalledAsExtension(context.trace)
candidateCall.addStatus(OTHER_ERROR)
private fun CallCandidateResolutionContext<*>.checkNonExtensionCalledWithReceiver() = checkAndReport {
if (isSynthesizedInvoke(candidateCall.getCandidateDescriptor())
&& !KotlinBuiltIns.isExtensionFunctionType(candidateCall.getDispatchReceiver().getType())
) {
tracing.freeFunctionCalledAsExtension(trace)
OTHER_ERROR
} else {
SUCCESS
}
}
@@ -287,8 +304,6 @@ public class CandidateResolver(
var resultStatus = SUCCESS
val candidateCall = context.candidateCall
resultStatus = resultStatus.combine(checkReceiverTypeError(context))
// 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
// 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,
context: ResolutionContext<*>): JetType? {
val receiverToCast = ExpressionReceiver(JetPsiUtil.safeDeparenthesize(expression, false), actualType)
val variants = SmartCastUtils.getSmartCastVariantsExcludingReceiver(context, receiverToCast)
val variants = getSmartCastVariantsExcludingReceiver(context, receiverToCast)
for (possibleType in variants) {
if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, expectedType)) {
return possibleType
@@ -374,39 +389,31 @@ public class CandidateResolver(
return null
}
private fun <D : CallableDescriptor> checkReceiverTypeError(
context: CallCandidateResolutionContext<D>): ResolutionStatus {
val candidateCall = context.candidateCall
val candidateDescriptor = candidateCall.getCandidateDescriptor()
private fun CallCandidateResolutionContext<*>.checkReceiverTypeError(): Unit = check {
val extensionReceiver = candidateDescriptor.getExtensionReceiverParameter()
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',
// not to throw away the candidate, so the following check is skipped.
if (!isInvokeCallOnExpressionWithBothReceivers(context.call)) {
status = status.combine(checkReceiverTypeError(context, extensionReceiver, candidateCall.getExtensionReceiver()))
if (!isInvokeCallOnExpressionWithBothReceivers(call)) {
checkReceiverTypeError(extensionReceiver, candidateCall.getExtensionReceiver())
}
status = status.combine(checkReceiverTypeError(context, dispatchReceiver, candidateCall.getDispatchReceiver()))
return status
checkReceiverTypeError(dispatchReceiver, candidateCall.getDispatchReceiver())
}
private fun <D : CallableDescriptor> checkReceiverTypeError(
context: CallCandidateResolutionContext<D>,
private fun CallCandidateResolutionContext<*>.checkReceiverTypeError(
receiverParameterDescriptor: ReceiverParameterDescriptor?,
receiverArgument: ReceiverValue): ResolutionStatus {
if (receiverParameterDescriptor == null || !receiverArgument.exists()) return SUCCESS
val candidateDescriptor = context.candidateCall.getCandidateDescriptor()
receiverArgument: ReceiverValue
) = checkAndReport {
if (receiverParameterDescriptor == null || !receiverArgument.exists()) return@checkAndReport SUCCESS
val erasedReceiverType = getErasedReceiverType(receiverParameterDescriptor, candidateDescriptor)
val isSubtypeBySmartCast = SmartCastUtils.isSubTypeBySmartCastIgnoringNullability(receiverArgument, erasedReceiverType, context)
if (!isSubtypeBySmartCast) {
return RECEIVER_TYPE_ERROR
if (!isSubTypeBySmartCastIgnoringNullability(receiverArgument, erasedReceiverType, this)) {
RECEIVER_TYPE_ERROR
} else {
SUCCESS
}
return SUCCESS
}
private fun <D : CallableDescriptor> checkReceiver(
@@ -422,13 +429,13 @@ public class CandidateResolver(
if (TypeUtils.dependsOnTypeParameters(receiverParameter.getType(), candidateDescriptor.getTypeParameters())) return SUCCESS
val safeAccess = isExplicitReceiver && !implicitInvokeCheck && candidateCall.getCall().isExplicitSafeCall()
val isSubtypeBySmartCast = SmartCastUtils.isSubTypeBySmartCastIgnoringNullability(
val isSubtypeBySmartCast = isSubTypeBySmartCastIgnoringNullability(
receiverArgument, receiverParameter.getType(), context)
if (!isSubtypeBySmartCast) {
context.tracing.wrongReceiverType(trace, receiverParameter, receiverArgument)
return OTHER_ERROR
}
if (!SmartCastUtils.recordSmartCastIfNecessary(receiverArgument, receiverParameter.getType(), context, safeAccess)) {
if (!recordSmartCastIfNecessary(receiverArgument, receiverParameter.getType(), context, safeAccess)) {
return OTHER_ERROR
}
@@ -436,7 +443,7 @@ public class CandidateResolver(
val bindingContext = trace.getBindingContext()
if (!safeAccess && !receiverParameter.getType().isMarkedNullable() && receiverArgumentType.isMarkedNullable()) {
if (!SmartCastUtils.canBeSmartCast(receiverParameter, receiverArgument, context)) {
if (!canBeSmartCast(receiverParameter, receiverArgument, context)) {
context.tracing.unsafeCall(trace, receiverArgumentType, implicitInvokeCheck)
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()
}