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()
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(
checker: CallCandidateResolutionContext<D>.() -> ResolutionStatus
crossinline checker: CallCandidateResolutionContext<D>.() -> ResolutionStatus
) {
if (shouldContinue()) {
check {
candidateCall.addStatus(checker())
}
}
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.resolve.calls.model;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
@@ -42,6 +44,10 @@ public interface MutableResolvedCall<D extends CallableDescriptor> extends Resol
void markCallAsCompleted();
void addRemainingTasks(Function0<Unit> task);
void performRemainingTasks();
boolean isCompleted();
@@ -19,6 +19,8 @@ package org.jetbrains.kotlin.resolve.calls.model;
import com.google.common.collect.Maps;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.SmartList;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
@@ -81,6 +83,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
private Boolean hasInferredReturnType = null;
private boolean completed = false;
private KotlinType smartCastDispatchReceiverType = null;
private Queue<Function0<Unit>> remainingTasks = null;
private ResolvedCallImpl(
@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) {
arguments.add(null);
}
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : valueArguments.entrySet()) {
ValueParameterDescriptor parameterDescriptor = entry.getKey();
ResolvedValueArgument value = entry.getValue();
@@ -299,7 +302,7 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
return null;
}
}
return arguments;
}
@@ -353,6 +356,23 @@ public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableRe
constraintSystem = null;
tracing = null;
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
@@ -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.context.*
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.results.OverloadResolutionResultsImpl
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.isDynamic
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.sure
import java.lang.IllegalStateException
import java.util.*
@@ -208,12 +208,15 @@ class NewResolutionOldInference(
val callCandidateResolutionContext = CallCandidateResolutionContext.create(
resolvedCall, basicCallContext, candidateTrace, tracing, basicCallContext.call,
CandidateResolveMode.FULLY // todo
CandidateResolveMode.EXIT_ON_FIRST_ERROR
)
candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo
val diagnostics = listOfNotNull(createPreviousResolveError(resolvedCall.status))
MyCandidate(diagnostics, resolvedCall)
MyCandidate(diagnostics, resolvedCall) {
resolvedCall.performRemainingTasks()
listOfNotNull(createPreviousResolveError(resolvedCall.status))
}
}
if (basicCallContext.collectAllCandidates) {
val allCandidates = towerResolver.runWithEmptyTowerData(KnownResultProcessor(resolvedCandidates),
@@ -315,12 +318,26 @@ class NewResolutionOldInference(
override val isDebuggerContext: Boolean get() = resolutionContext.isDebuggerContext
}
internal data class MyCandidate(
val diagnostics: List<KotlinCallDiagnostic>,
val resolvedCall: MutableResolvedCall<*>
internal class MyCandidate(
// Diagnostics that are already computed
// 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 {
override val resultingApplicability: ResolutionCandidateApplicability = getResultApplicability(diagnostics)
override val isSuccessful get() = resultingApplicability.isSuccess
val diagnostics: List<KotlinCallDiagnostic> by lazy(LazyThreadSafetyMode.NONE) {
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(
@@ -366,16 +383,27 @@ class NewResolutionOldInference(
val callCandidateResolutionContext = CallCandidateResolutionContext.create(
candidateCall, basicCallContext, candidateTrace, tracing, basicCallContext.call,
CandidateResolveMode.FULLY // todo
CandidateResolveMode.EXIT_ON_FIRST_ERROR
)
candidateResolver.performResolutionForCandidateCall(callCandidateResolutionContext, basicCallContext.checkArguments) // todo
val diagnostics = (towerCandidate.diagnostics +
checkInfixAndOperator(basicCallContext.call, towerCandidate.descriptor) +
createPreviousResolveError(candidateCall.status)).filterNotNull() // todo
return MyCandidate(diagnostics, candidateCall)
val diagnostics = createDiagnosticsForCandidate(towerCandidate, candidateCall)
return MyCandidate(diagnostics, candidateCall) {
candidateCall.performRemainingTasks()
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> {
if (descriptor !is FunctionDescriptor || ErrorUtils.isError(descriptor)) return emptyList()
if (descriptor.name != name && (name == OperatorNameConventions.UNARY_PLUS || name == OperatorNameConventions.UNARY_MINUS)) {
@@ -405,7 +433,9 @@ class NewResolutionOldInference(
"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> {