From be630f93dd5eedb84617c57a31e50fcd34ce5290 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 25 Oct 2017 11:27:23 +0300 Subject: [PATCH] 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) --- .../kotlin/resolve/calls/CandidateResolver.kt | 8 +-- .../calls/model/MutableResolvedCall.java | 6 ++ .../resolve/calls/model/ResolvedCallImpl.java | 24 +++++++- .../calls/tower/NewResolutionOldInference.kt | 58 ++++++++++++++----- 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index e3a01804a34..4dcf3bd601b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -706,15 +706,15 @@ class CandidateResolver( candidateResolveMode == CandidateResolveMode.FULLY || candidateCall.status.possibleTransformToSuccess() private inline fun CallCandidateResolutionContext.check( - checker: CallCandidateResolutionContext.() -> Unit + crossinline checker: CallCandidateResolutionContext.() -> Unit ) { - if (shouldContinue()) checker() + if (shouldContinue()) checker() else candidateCall.addRemainingTasks { checker() } } private inline fun CallCandidateResolutionContext.checkAndReport( - checker: CallCandidateResolutionContext.() -> ResolutionStatus + crossinline checker: CallCandidateResolutionContext.() -> ResolutionStatus ) { - if (shouldContinue()) { + check { candidateCall.addStatus(checker()) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/model/MutableResolvedCall.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/model/MutableResolvedCall.java index e18ad4ac416..849ae8490b5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/model/MutableResolvedCall.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/model/MutableResolvedCall.java @@ -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 extends Resol void markCallAsCompleted(); + void addRemainingTasks(Function0 task); + + void performRemainingTasks(); + boolean isCompleted(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallImpl.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallImpl.java index 73be3a9dbe0..9cb7a2d6b7a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallImpl.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/model/ResolvedCallImpl.java @@ -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 implements MutableRe private Boolean hasInferredReturnType = null; private boolean completed = false; private KotlinType smartCastDispatchReceiverType = null; + private Queue> remainingTasks = null; private ResolvedCallImpl( @NotNull ResolutionCandidate candidate, @@ -283,7 +286,7 @@ public class ResolvedCallImpl implements MutableRe for (int i = 0; i < candidateDescriptor.getValueParameters().size(); ++i) { arguments.add(null); } - + for (Map.Entry entry : valueArguments.entrySet()) { ValueParameterDescriptor parameterDescriptor = entry.getKey(); ResolvedValueArgument value = entry.getValue(); @@ -299,7 +302,7 @@ public class ResolvedCallImpl implements MutableRe return null; } } - + return arguments; } @@ -353,6 +356,23 @@ public class ResolvedCallImpl implements MutableRe constraintSystem = null; tracing = null; completed = true; + remainingTasks = null; + } + + @Override + public void addRemainingTasks(Function0 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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt index 7a5f1174eba..630d6c8e292 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt @@ -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, - 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, + val resolvedCall: MutableResolvedCall<*>, + finalDiagnosticsComputation: (() -> List)? = null ) : Candidate { - override val resultingApplicability: ResolutionCandidateApplicability = getResultApplicability(diagnostics) - override val isSuccessful get() = resultingApplicability.isSuccess + val diagnostics: List 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 + ): List = + mutableListOf().apply { + addAll(towerCandidate.diagnostics) + addAll(checkInfixAndOperator(basicCallContext.call, towerCandidate.descriptor)) + addIfNotNull(createPreviousResolveError(candidateCall.status)) + } + private fun checkInfixAndOperator(call: Call, descriptor: CallableDescriptor): List { 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 {