Postpone full resolution for a candidate known to be failed

It's necessary for performance, because there are some resolution
parts that actually can be omitted and at the same time they aren't
very cheap (like inference or value arguments types checking)
This commit is contained in:
Denis Zharkov
2017-10-25 11:27:23 +03:00
parent 6a43743c98
commit be630f93dd
4 changed files with 76 additions and 20 deletions
@@ -706,15 +706,15 @@ class CandidateResolver(
candidateResolveMode == CandidateResolveMode.FULLY || candidateCall.status.possibleTransformToSuccess() candidateResolveMode == CandidateResolveMode.FULLY || candidateCall.status.possibleTransformToSuccess()
private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.check( private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.check(
checker: CallCandidateResolutionContext<D>.() -> Unit crossinline checker: CallCandidateResolutionContext<D>.() -> Unit
) { ) {
if (shouldContinue()) checker() if (shouldContinue()) checker() else candidateCall.addRemainingTasks { checker() }
} }
private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkAndReport( private inline fun <D : CallableDescriptor> CallCandidateResolutionContext<D>.checkAndReport(
checker: CallCandidateResolutionContext<D>.() -> ResolutionStatus crossinline checker: CallCandidateResolutionContext<D>.() -> ResolutionStatus
) { ) {
if (shouldContinue()) { check {
candidateCall.addStatus(checker()) candidateCall.addStatus(checker())
} }
} }
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.resolve.calls.model; package org.jetbrains.kotlin.resolve.calls.model;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.CallableDescriptor;
@@ -42,6 +44,10 @@ public interface MutableResolvedCall<D extends CallableDescriptor> extends Resol
void markCallAsCompleted(); void markCallAsCompleted();
void addRemainingTasks(Function0<Unit> task);
void performRemainingTasks();
boolean isCompleted(); boolean isCompleted();
@@ -19,6 +19,8 @@ package org.jetbrains.kotlin.resolve.calls.model;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.SmartList; import com.intellij.util.SmartList;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.CallableDescriptor;
@@ -81,6 +83,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
private Boolean hasInferredReturnType = null; private Boolean hasInferredReturnType = null;
private boolean completed = false; private boolean completed = false;
private KotlinType smartCastDispatchReceiverType = null; private KotlinType smartCastDispatchReceiverType = null;
private Queue<Function0<Unit>> remainingTasks = null;
private ResolvedCallImpl( private ResolvedCallImpl(
@NotNull ResolutionCandidate<D> candidate, @NotNull ResolutionCandidate<D> candidate,
@@ -283,7 +286,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
for (int i = 0; i < candidateDescriptor.getValueParameters().size(); ++i) { for (int i = 0; i < candidateDescriptor.getValueParameters().size(); ++i) {
arguments.add(null); arguments.add(null);
} }
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : valueArguments.entrySet()) { for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : valueArguments.entrySet()) {
ValueParameterDescriptor parameterDescriptor = entry.getKey(); ValueParameterDescriptor parameterDescriptor = entry.getKey();
ResolvedValueArgument value = entry.getValue(); ResolvedValueArgument value = entry.getValue();
@@ -299,7 +302,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
return null; return null;
} }
} }
return arguments; return arguments;
} }
@@ -353,6 +356,23 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
constraintSystem = null; constraintSystem = null;
tracing = null; tracing = null;
completed = true; completed = true;
remainingTasks = null;
}
@Override
public void addRemainingTasks(Function0<Unit> task) {
if (remainingTasks == null) {
remainingTasks = new ArrayDeque<>();
}
remainingTasks.add(task);
}
@Override
public void performRemainingTasks() {
if (remainingTasks == null) return;
while (!remainingTasks.isEmpty()) {
remainingTasks.poll().invoke();
}
} }
@Override @Override
@@ -38,7 +38,6 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInfixCall
import org.jetbrains.kotlin.resolve.calls.callUtil.createLookupLocation import org.jetbrains.kotlin.resolve.calls.callUtil.createLookupLocation
import org.jetbrains.kotlin.resolve.calls.context.* import org.jetbrains.kotlin.resolve.calls.context.*
import org.jetbrains.kotlin.resolve.calls.inference.CoroutineInferenceSupport import org.jetbrains.kotlin.resolve.calls.inference.CoroutineInferenceSupport
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallKind
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl
import org.jetbrains.kotlin.resolve.calls.results.ResolutionResultsHandler import org.jetbrains.kotlin.resolve.calls.results.ResolutionResultsHandler
@@ -56,6 +55,7 @@ import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.isDynamic import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.sure import org.jetbrains.kotlin.utils.sure
import java.lang.IllegalStateException import java.lang.IllegalStateException
import java.util.* import java.util.*
@@ -208,12 +208,15 @@ class NewResolutionOldInference(
val callCandidateResolutionContext = CallCandidateResolutionContext.create( val callCandidateResolutionContext = CallCandidateResolutionContext.create(
resolvedCall, basicCallContext, candidateTrace, tracing, basicCallContext.call, resolvedCall, basicCallContext, candidateTrace, tracing, basicCallContext.call,
CandidateResolveMode.FULLY // todo CandidateResolveMode.EXIT_ON_FIRST_ERROR
) )
candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo
val diagnostics = listOfNotNull(createPreviousResolveError(resolvedCall.status)) val diagnostics = listOfNotNull(createPreviousResolveError(resolvedCall.status))
MyCandidate(diagnostics, resolvedCall) MyCandidate(diagnostics, resolvedCall) {
resolvedCall.performRemainingTasks()
listOfNotNull(createPreviousResolveError(resolvedCall.status))
}
} }
if (basicCallContext.collectAllCandidates) { if (basicCallContext.collectAllCandidates) {
val allCandidates = towerResolver.runWithEmptyTowerData(KnownResultProcessor(resolvedCandidates), val allCandidates = towerResolver.runWithEmptyTowerData(KnownResultProcessor(resolvedCandidates),
@@ -315,12 +318,26 @@ class NewResolutionOldInference(
override val isDebuggerContext: Boolean get() = resolutionContext.isDebuggerContext override val isDebuggerContext: Boolean get() = resolutionContext.isDebuggerContext
} }
internal data class MyCandidate( internal class MyCandidate(
val diagnostics: List<KotlinCallDiagnostic>, // Diagnostics that are already computed
val resolvedCall: MutableResolvedCall<*> // if resultingApplicability is successful they must be the same as `diagnostics`,
// otherwise they might be a bit different but result remains unsuccessful
val eagerDiagnostics: List<KotlinCallDiagnostic>,
val resolvedCall: MutableResolvedCall<*>,
finalDiagnosticsComputation: (() -> List<KotlinCallDiagnostic>)? = null
) : Candidate { ) : Candidate {
override val resultingApplicability: ResolutionCandidateApplicability = getResultApplicability(diagnostics) val diagnostics: List<KotlinCallDiagnostic> by lazy(LazyThreadSafetyMode.NONE) {
override val isSuccessful get() = resultingApplicability.isSuccess finalDiagnosticsComputation?.invoke() ?: eagerDiagnostics
}
operator fun component1() = diagnostics
operator fun component2() = resolvedCall
override val resultingApplicability: ResolutionCandidateApplicability by lazy(LazyThreadSafetyMode.NONE) {
getResultApplicability(diagnostics)
}
override val isSuccessful = getResultApplicability(eagerDiagnostics).isSuccess
} }
private inner class CandidateFactoryImpl( private inner class CandidateFactoryImpl(
@@ -366,16 +383,27 @@ class NewResolutionOldInference(
val callCandidateResolutionContext = CallCandidateResolutionContext.create( val callCandidateResolutionContext = CallCandidateResolutionContext.create(
candidateCall, basicCallContext, candidateTrace, tracing, basicCallContext.call, candidateCall, basicCallContext, candidateTrace, tracing, basicCallContext.call,
CandidateResolveMode.FULLY // todo CandidateResolveMode.EXIT_ON_FIRST_ERROR
) )
candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo
val diagnostics = (towerCandidate.diagnostics + val diagnostics = createDiagnosticsForCandidate(towerCandidate, candidateCall)
checkInfixAndOperator(basicCallContext.call, towerCandidate.descriptor) + return MyCandidate(diagnostics, candidateCall) {
createPreviousResolveError(candidateCall.status)).filterNotNull() // todo candidateCall.performRemainingTasks()
return MyCandidate(diagnostics, candidateCall) createDiagnosticsForCandidate(towerCandidate, candidateCall)
}
} }
private fun createDiagnosticsForCandidate(
towerCandidate: CandidateWithBoundDispatchReceiver,
candidateCall: ResolvedCallImpl<CallableDescriptor>
): List<ResolutionDiagnostic> =
mutableListOf<ResolutionDiagnostic>().apply {
addAll(towerCandidate.diagnostics)
addAll(checkInfixAndOperator(basicCallContext.call, towerCandidate.descriptor))
addIfNotNull(createPreviousResolveError(candidateCall.status))
}
private fun checkInfixAndOperator(call: Call, descriptor: CallableDescriptor): List<ResolutionDiagnostic> { private fun checkInfixAndOperator(call: Call, descriptor: CallableDescriptor): List<ResolutionDiagnostic> {
if (descriptor !is FunctionDescriptor || ErrorUtils.isError(descriptor)) return emptyList() if (descriptor !is FunctionDescriptor || ErrorUtils.isError(descriptor)) return emptyList()
if (descriptor.name != name && (name == OperatorNameConventions.UNARY_PLUS || name == OperatorNameConventions.UNARY_MINUS)) { if (descriptor.name != name && (name == OperatorNameConventions.UNARY_PLUS || name == OperatorNameConventions.UNARY_MINUS)) {
@@ -405,7 +433,9 @@ class NewResolutionOldInference(
"Variable call must be success: $variable" "Variable call must be success: $variable"
} }
return MyCandidate(variable.diagnostics + invoke.diagnostics, resolvedCallImpl) return MyCandidate(variable.eagerDiagnostics + invoke.eagerDiagnostics, resolvedCallImpl) {
variable.diagnostics + invoke.diagnostics
}
} }
override fun factoryForVariable(stripExplicitReceiver: Boolean): CandidateFactory<MyCandidate> { override fun factoryForVariable(stripExplicitReceiver: Boolean): CandidateFactory<MyCandidate> {