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:
Svetlana Isakova
2012-11-09 15:50:16 +04:00
parent dda3091dfb
commit e9c8be8449
13 changed files with 126 additions and 49 deletions
@@ -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);
}
}
@@ -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);
}
@@ -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,
@@ -26,6 +26,7 @@ public enum ResolutionStatus {
UNSAFE_CALL_ERROR,
OTHER_ERROR,
STRONG_ERROR,
INCOMPLETE_TYPE_INFERENCE,
SUCCESS(true);
@SuppressWarnings("unchecked")
@@ -0,0 +1,8 @@
package f
fun g<T>(<!UNUSED_PARAMETER!>i<!>: Int, <!UNUSED_PARAMETER!>a<!>: Any): List<T> {throw Exception()}
fun g<T>(<!UNUSED_PARAMETER!>a<!>: Any, <!UNUSED_PARAMETER!>i<!>: Int): Collection<T> {throw Exception()}
fun test<T>() {
val <!UNUSED_VARIABLE!>c<!>: List<T> = <!CANNOT_COMPLETE_RESOLVE!>g<!>(1, 1)
}
@@ -0,0 +1,6 @@
package f
fun h<R>(<!UNUSED_PARAMETER!>i<!>: Int, <!UNUSED_PARAMETER!>a<!>: Any, <!UNUSED_PARAMETER!>r<!>: R, <!UNUSED_PARAMETER!>f<!>: (Boolean) -> Int) = 1
fun h<R>(<!UNUSED_PARAMETER!>a<!>: Any, <!UNUSED_PARAMETER!>i<!>: Int, <!UNUSED_PARAMETER!>r<!>: R, <!UNUSED_PARAMETER!>f<!>: (Boolean) -> Int) = 1
fun test() = <!CANNOT_COMPLETE_RESOLVE!>h<!>(1, 1, 1, { <!CANNOT_INFER_PARAMETER_TYPE!>b<!> -> 42 })
@@ -0,0 +1,10 @@
package f
fun f<T>(<!UNUSED_PARAMETER!>i<!>: Int, <!UNUSED_PARAMETER!>c<!>: Collection<T>): List<T> {throw Exception()}
fun f<T>(<!UNUSED_PARAMETER!>a<!>: Any, <!UNUSED_PARAMETER!>l<!>: List<T>): Collection<T> {throw Exception()}
fun test<T>(<!UNUSED_PARAMETER!>l<!>: List<T>) {
<!CANNOT_COMPLETE_RESOLVE!>f<!>(1, <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>emptyList<!>())
}
fun emptyList<T>(): List<T> {throw Exception()}
@@ -0,0 +1,8 @@
package f
fun f<T>(<!UNUSED_PARAMETER!>i<!>: Int, <!UNUSED_PARAMETER!>t<!>: T, <!UNUSED_PARAMETER!>c<!>: MutableCollection<T>) {}
fun f<T>(<!UNUSED_PARAMETER!>a<!>: Any, <!UNUSED_PARAMETER!>t<!>: T, <!UNUSED_PARAMETER!>l<!>: MutableList<T>) {}
fun test(l: List<Int>) {
<!NONE_APPLICABLE!>f<!>(1, "", l)
}
@@ -0,0 +1,9 @@
package f
fun h<R>(<!UNUSED_PARAMETER!>f<!>: (Boolean) -> R) = 1
fun h<R>(<!UNUSED_PARAMETER!>f<!>: (String) -> R) = 2
fun test() = <!CANNOT_COMPLETE_RESOLVE!>h<!>{ <!CANNOT_INFER_PARAMETER_TYPE!>i<!> -> getAnswer() }
fun getAnswer() = 42
@@ -10,5 +10,5 @@ import foo.*
fun f<T>(<!UNUSED_PARAMETER!>l<!>: List<T>) {}
fun test<T>(l: List<T>) {
<!OVERLOAD_RESOLUTION_AMBIGUITY!>f(l)<!>
<!CANNOT_COMPLETE_RESOLVE!>f<!>(l)
}
@@ -3,5 +3,5 @@ fun f1<T>(<!UNUSED_PARAMETER!>l<!>: <!UNRESOLVED_REFERENCE!>List2<!><T>): T {thr
fun f1<T>(<!UNUSED_PARAMETER!>c<!>: Collection<T>): T{throw Exception()}
fun test<T>(l: List<T>) {
f1(l)
<!CANNOT_COMPLETE_RESOLVE!>f1<!>(l)
}
@@ -1733,9 +1733,29 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/inference/arrayConstructor.kt");
}
@TestMetadata("cannotCompleteResolve.kt")
public void testCannotCompleteResolve() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/cannotCompleteResolve.kt");
@TestMetadata("cannotCompleteResolveAmbiguity.kt")
public void testCannotCompleteResolveAmbiguity() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/cannotCompleteResolveAmbiguity.kt");
}
@TestMetadata("cannotCompleteResolveFunctionLiteralsNoUse.kt")
public void testCannotCompleteResolveFunctionLiteralsNoUse() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/cannotCompleteResolveFunctionLiteralsNoUse.kt");
}
@TestMetadata("cannotCompleteResolveNoInfoForParameter.kt")
public void testCannotCompleteResolveNoInfoForParameter() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/cannotCompleteResolveNoInfoForParameter.kt");
}
@TestMetadata("cannotCompleteResolveNoneApplicable.kt")
public void testCannotCompleteResolveNoneApplicable() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/cannotCompleteResolveNoneApplicable.kt");
}
@TestMetadata("cannotCompleteResolveWithFunctionLiterals.kt")
public void testCannotCompleteResolveWithFunctionLiterals() throws Exception {
doTest("compiler/testData/diagnostics/tests/inference/cannotCompleteResolveWithFunctionLiterals.kt");
}
@TestMetadata("completeInferenceIfManyFailed.kt")