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:
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -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();
|
||||
|
||||
|
||||
|
||||
+22
-2
@@ -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
|
||||
|
||||
+44
-14
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user