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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
+6
@@ -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 })
|
||||
+10
@@ -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)
|
||||
}
|
||||
+9
@@ -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
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
+1
-1
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user