complete type inference only for one candidate
(the most specific) to avoid exponential resolve of value arguments for several candidates where type inference is incomplete (or depends on expected type)
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.jet.lang.resolve.calls;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
@@ -28,10 +30,7 @@ import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
|
||||
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
|
||||
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
|
||||
import org.jetbrains.jet.lang.resolve.calls.results.ResolutionDebugInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.results.ResolutionResultsHandler;
|
||||
import org.jetbrains.jet.lang.resolve.calls.results.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.tasks.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.util.DelegatingCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.util.ExpressionAsFunctionDescriptor;
|
||||
@@ -305,27 +304,42 @@ public class CallResolver {
|
||||
@NotNull OverloadResolutionResults<D> resultsWithIncompleteTypeInference,
|
||||
@NotNull TracingStrategy tracing
|
||||
) {
|
||||
if (resultsWithIncompleteTypeInference.getResultCode() != OverloadResolutionResults.Code.INCOMPLETE_TYPE_INFERENCE)
|
||||
if (resultsWithIncompleteTypeInference.getResultCode() != OverloadResolutionResults.Code.INCOMPLETE_TYPE_INFERENCE) {
|
||||
return resultsWithIncompleteTypeInference;
|
||||
Set<ResolvedCallWithTrace<D>> successful = Sets.newLinkedHashSet();
|
||||
Set<ResolvedCallWithTrace<D>> failed = Sets.newLinkedHashSet();
|
||||
for (ResolvedCall<? extends D> call : resultsWithIncompleteTypeInference.getResultingCalls()) {
|
||||
if (!(call instanceof ResolvedCallImpl)) continue;
|
||||
ResolvedCallImpl<D> resolvedCall = CallResolverUtil.copy((ResolvedCallImpl<D>) call, context);
|
||||
if (!resolvedCall.hasUnknownTypeParameters()) {
|
||||
if (resolvedCall.getStatus().isSuccess()) {
|
||||
successful.add(resolvedCall);
|
||||
}
|
||||
else {
|
||||
failed.add(resolvedCall);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
candidateResolver.completeTypeInferenceDependentOnExpectedTypeForCall(
|
||||
CallResolutionContext.create(context, tracing, resolvedCall), successful, failed);
|
||||
}
|
||||
OverloadResolutionResultsImpl<D> results = ResolutionResultsHandler.INSTANCE.computeResultAndReportErrors(context.trace, tracing, successful,
|
||||
failed);
|
||||
|
||||
Set<ResolvedCallWithTrace<D>> candidates = Sets.newLinkedHashSet();
|
||||
Set<ResolvedCallWithTrace<D>> incompleteCalls = Sets.newLinkedHashSet();
|
||||
for (ResolvedCall<? extends D> cachedCall : resultsWithIncompleteTypeInference.getResultingCalls()) {
|
||||
if (cachedCall instanceof ResolvedCallImpl) {
|
||||
ResolvedCallImpl<D> resolvedCall = (ResolvedCallImpl<D>) cachedCall;
|
||||
if (resolvedCall.hasUnknownTypeParameters()) {
|
||||
ResolvedCallImpl<D> copy = CallResolverUtil.copy(resolvedCall, context);
|
||||
incompleteCalls.add(copy);
|
||||
candidates.add(copy);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
candidates.add((ResolvedCallWithTrace<D>) cachedCall);
|
||||
}
|
||||
ResolvedCallWithTrace<D> maximallySpecific = OverloadingConflictResolver.INSTANCE.findMaximallySpecific(incompleteCalls, false);
|
||||
if (maximallySpecific != null) {
|
||||
candidateResolver.completeTypeInferenceDependentOnExpectedTypeForCall(
|
||||
CallResolutionContext.create(context, tracing, (ResolvedCallImpl<D>) maximallySpecific));
|
||||
for (ResolvedCallWithTrace<D> callWithUnknownTypeParameters : incompleteCalls) {
|
||||
if (callWithUnknownTypeParameters != maximallySpecific) {
|
||||
((ResolvedCallImpl<D>) callWithUnknownTypeParameters).addStatus(ResolutionStatus.OTHER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (ResolvedCallWithTrace<D> callWithUnknownTypeParameters : incompleteCalls) {
|
||||
ResolvedCallImpl<D> resolvedCall = (ResolvedCallImpl<D>) callWithUnknownTypeParameters;
|
||||
resolvedCall.addStatus(ResolutionStatus.INCOMPLETE_TYPE_INFERENCE);
|
||||
}
|
||||
}
|
||||
OverloadResolutionResultsImpl<D> results = ResolutionResultsHandler.INSTANCE.computeResultAndReportErrors(
|
||||
context.trace, tracing, candidates);
|
||||
if (!results.isSingleResult()) {
|
||||
candidateResolver.checkTypesWithNoCallee(context);
|
||||
}
|
||||
|
||||
@@ -198,9 +198,7 @@ public class CandidateResolver {
|
||||
}
|
||||
|
||||
public <D extends CallableDescriptor> void completeTypeInferenceDependentOnExpectedTypeForCall(
|
||||
CallResolutionContext<D, D> context,
|
||||
Set<ResolvedCallWithTrace<D>> successful,
|
||||
Set<ResolvedCallWithTrace<D>> failed
|
||||
CallResolutionContext<D, D> context
|
||||
) {
|
||||
ResolvedCallImpl<D> resolvedCall = context.candidateCall;
|
||||
assert resolvedCall.hasUnknownTypeParameters();
|
||||
@@ -247,7 +245,6 @@ public class CandidateResolver {
|
||||
context.expectedType),
|
||||
constraintSystemWithoutExpectedTypeConstraint);
|
||||
resolvedCall.addStatus(ResolutionStatus.OTHER_ERROR);
|
||||
failed.add(resolvedCall);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -257,12 +254,8 @@ public class CandidateResolver {
|
||||
|
||||
checkBounds(resolvedCall, constraintSystem, resolvedCall.getTrace(), context.tracing);
|
||||
resolvedCall.setHasUnknownTypeParameters(false);
|
||||
if (resolvedCall.getStatus().isSuccess() || resolvedCall.getStatus() == ResolutionStatus.UNKNOWN_STATUS) {
|
||||
if (resolvedCall.getStatus() == ResolutionStatus.UNKNOWN_STATUS) {
|
||||
resolvedCall.addStatus(ResolutionStatus.SUCCESS);
|
||||
successful.add(resolvedCall);
|
||||
}
|
||||
else {
|
||||
failed.add(resolvedCall);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ public class OverloadResolutionResultsImpl<D extends CallableDescriptor> impleme
|
||||
return new OverloadResolutionResultsImpl<D>(Code.AMBIGUITY, candidates);
|
||||
}
|
||||
|
||||
private static <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> incompleteTypeInference(Collection<ResolvedCallWithTrace<D>> candidates) {
|
||||
public static <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> incompleteTypeInference(Collection<ResolvedCallWithTrace<D>> candidates) {
|
||||
return new OverloadResolutionResultsImpl<D>(Code.INCOMPLETE_TYPE_INFERENCE, candidates);
|
||||
}
|
||||
|
||||
|
||||
+19
-11
@@ -31,9 +31,7 @@ import java.util.Set;
|
||||
|
||||
import static org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl.MAP_TO_CANDIDATE;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl.MAP_TO_RESULT;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.SEVERITY_LEVELS;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.STRONG_ERROR;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.UNKNOWN_STATUS;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.*;
|
||||
|
||||
public class ResolutionResultsHandler {
|
||||
public static ResolutionResultsHandler INSTANCE = new ResolutionResultsHandler();
|
||||
@@ -48,27 +46,30 @@ public class ResolutionResultsHandler {
|
||||
) {
|
||||
Set<ResolvedCallWithTrace<D>> successfulCandidates = Sets.newLinkedHashSet();
|
||||
Set<ResolvedCallWithTrace<D>> failedCandidates = Sets.newLinkedHashSet();
|
||||
Set<ResolvedCallWithTrace<D>> incompleteCandidates = Sets.newLinkedHashSet();
|
||||
for (ResolvedCallWithTrace<D> candidateCall : candidates) {
|
||||
ResolutionStatus status = candidateCall.getStatus();
|
||||
assert status != UNKNOWN_STATUS : "No resolution for " + candidateCall.getCandidateDescriptor();
|
||||
if (status.isSuccess()) {
|
||||
successfulCandidates.add(candidateCall);
|
||||
}
|
||||
else {
|
||||
assert status != UNKNOWN_STATUS : "No resolution for " + candidateCall.getCandidateDescriptor();
|
||||
if (candidateCall.getStatus() != STRONG_ERROR) {
|
||||
failedCandidates.add(candidateCall);
|
||||
}
|
||||
else if (status == INCOMPLETE_TYPE_INFERENCE) {
|
||||
incompleteCandidates.add(candidateCall);
|
||||
}
|
||||
else if (candidateCall.getStatus() != STRONG_ERROR) {
|
||||
failedCandidates.add(candidateCall);
|
||||
}
|
||||
}
|
||||
return computeResultAndReportErrors(trace, tracing, successfulCandidates, failedCandidates);
|
||||
return computeResultAndReportErrors(trace, tracing, successfulCandidates, failedCandidates, incompleteCandidates);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeResultAndReportErrors(
|
||||
private <D extends CallableDescriptor> OverloadResolutionResultsImpl<D> computeResultAndReportErrors(
|
||||
@NotNull BindingTrace trace,
|
||||
@NotNull TracingStrategy tracing,
|
||||
@NotNull Set<ResolvedCallWithTrace<D>> successfulCandidates,
|
||||
@NotNull Set<ResolvedCallWithTrace<D>> failedCandidates
|
||||
@NotNull Set<ResolvedCallWithTrace<D>> failedCandidates,
|
||||
@NotNull Set<ResolvedCallWithTrace<D>> incompleteCandidates
|
||||
) {
|
||||
// TODO : maybe it's better to filter overrides out first, and only then look for the maximally specific
|
||||
|
||||
@@ -87,6 +88,13 @@ public class ResolutionResultsHandler {
|
||||
}
|
||||
return results;
|
||||
}
|
||||
else if (!incompleteCandidates.isEmpty()) {
|
||||
assert incompleteCandidates.size() > 1 :
|
||||
"One incomplete candidate should have been chosen as maximally specific and completed earlier";
|
||||
tracing.cannotCompleteResolve(trace, incompleteCandidates);
|
||||
tracing.recordAmbiguity(trace, incompleteCandidates);
|
||||
return OverloadResolutionResultsImpl.incompleteTypeInference(incompleteCandidates);
|
||||
}
|
||||
else if (!failedCandidates.isEmpty()) {
|
||||
if (failedCandidates.size() != 1) {
|
||||
// This is needed when there are several overloads some of which are OK but for nullability of the receiver,
|
||||
|
||||
+1
@@ -26,6 +26,7 @@ public enum ResolutionStatus {
|
||||
UNSAFE_CALL_ERROR,
|
||||
OTHER_ERROR,
|
||||
STRONG_ERROR,
|
||||
INCOMPLETE_TYPE_INFERENCE,
|
||||
SUCCESS(true);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
Reference in New Issue
Block a user