Turn on inference when resolving constructor super-calls

It's only needed when one of candidates has it's own type parameters
Otherwise it does not make sense as arguments are already specified
This commit is contained in:
Denis Zharkov
2016-01-22 16:00:43 +03:00
parent 6542d091ee
commit c26ca5e7ef
7 changed files with 129 additions and 15 deletions
@@ -314,7 +314,7 @@ public class BodyResolver {
}
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveFunctionCall(
trace, scopeForConstructor,
CallMaker.makeCall(null, null, call), NO_EXPECTED_TYPE, outerDataFlowInfo, false);
CallMaker.makeConstructorCallWithoutTypeArguments(call), NO_EXPECTED_TYPE, outerDataFlowInfo, false);
if (results.isSuccess()) {
KotlinType supertype = results.getResultingDescriptor().getReturnType();
recordSupertype(typeReference, supertype);
@@ -20,6 +20,7 @@ import com.google.common.collect.Lists;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.ContainerUtil;
import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import org.jetbrains.annotations.NotNull;
@@ -52,7 +53,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil;
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeSubstitutor;
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
@@ -381,10 +381,12 @@ public class CallResolver {
context.trace.report(NO_CONSTRUCTOR.on(CallUtilKt.getValueArgumentListOrElement(context.call)));
return checkArgumentTypesAndFail(context);
}
TypeSubstitutor knownSubstitutor = TypeSubstitutor.create(constructedType);
Collection<ResolutionCandidate<CallableDescriptor>> candidates =
taskPrioritizer.<CallableDescriptor>convertWithImpliedThisAndNoReceiver(
context.scope, constructors, context.call, knownSubstitutor);
Pair<Collection<ResolutionCandidate<CallableDescriptor>>, BasicCallResolutionContext> candidatesAndContext =
prepareCandidatesAndContextForConstructorCall(constructedType, context);
Collection<ResolutionCandidate<CallableDescriptor>> candidates = candidatesAndContext.getFirst();
context = candidatesAndContext.getSecond();
return computeTasksFromCandidatesAndResolvedCall(context, functionReference, candidates, CallTransformer.FUNCTION_CALL_TRANSFORMER);
}
@@ -460,11 +462,10 @@ public class CallResolver {
calleeConstructor.getContainingDeclaration().getDefaultType() :
DescriptorUtils.getSuperClassType(currentClassDescriptor);
TypeSubstitutor knownTypeParametersSubstitutor = TypeSubstitutor.create(superType);
Collection<ResolutionCandidate<CallableDescriptor>> candidates =
taskPrioritizer.<CallableDescriptor>convertWithImpliedThisAndNoReceiver(
context.scope, constructors, context.call, knownTypeParametersSubstitutor);
Pair<Collection<ResolutionCandidate<CallableDescriptor>>, BasicCallResolutionContext> candidatesAndContext =
prepareCandidatesAndContextForConstructorCall(superType, context);
Collection<ResolutionCandidate<CallableDescriptor>> candidates = candidatesAndContext.getFirst();
context = candidatesAndContext.getSecond();
TracingStrategy tracing = call.isImplicit() ?
new TracingStrategyForImplicitConstructorDelegationCall(call, context.call) :
@@ -481,6 +482,45 @@ public class CallResolver {
return computeTasksFromCandidatesAndResolvedCall(context, candidates, CallTransformer.FUNCTION_CALL_TRANSFORMER, tracing);
}
@NotNull
private
Pair<Collection<ResolutionCandidate<CallableDescriptor>>, BasicCallResolutionContext> prepareCandidatesAndContextForConstructorCall(
@NotNull KotlinType superType,
@NotNull BasicCallResolutionContext context
) {
if (!(superType.getConstructor().getDeclarationDescriptor() instanceof ClassDescriptor)) {
return new Pair<Collection<ResolutionCandidate<CallableDescriptor>>, BasicCallResolutionContext>(
Collections.<ResolutionCandidate<CallableDescriptor>>emptyList(), context);
}
ClassDescriptor superClass = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
// If any constructor has type parameter (currently it only can be true for ones from Java), try to infer arguments for them
// Otherwise use NO_EXPECTED_TYPE and knownTypeParametersSubstitutor
boolean anyConstructorHasDeclaredTypeParameters =
anyConstructorHasDeclaredTypeParameters(superType.getConstructor().getDeclarationDescriptor());
TypeSubstitutor knownTypeParametersSubstitutor = anyConstructorHasDeclaredTypeParameters ? null : TypeSubstitutor.create(superType);
if (anyConstructorHasDeclaredTypeParameters) {
context = context.replaceExpectedType(superType);
}
Collection<ResolutionCandidate<CallableDescriptor>> candidates =
taskPrioritizer.<CallableDescriptor>convertWithImpliedThisAndNoReceiver(
context.scope, superClass.getConstructors(), context.call, knownTypeParametersSubstitutor);
return new Pair<Collection<ResolutionCandidate<CallableDescriptor>>, BasicCallResolutionContext>(candidates, context);
}
private static boolean anyConstructorHasDeclaredTypeParameters(@Nullable ClassifierDescriptor classDescriptor) {
if (!(classDescriptor instanceof ClassDescriptor)) return false;
for (ConstructorDescriptor constructor : ((ClassDescriptor) classDescriptor).getConstructors()) {
if (constructor.getTypeParameters().size() > constructor.getContainingDeclaration().getDeclaredTypeParameters().size()) return true;
}
return false;
}
public OverloadResolutionResults<FunctionDescriptor> resolveCallWithKnownCandidate(
@NotNull final Call call,
@NotNull final TracingStrategy tracing,
@@ -264,6 +264,24 @@ public class CallMaker {
return makeCallWithExpressions(nameExpression, explicitReceiver, callOperationNode, nameExpression, Collections.<KtExpression>emptyList());
}
@NotNull
public static Call makeConstructorCallWithoutTypeArguments(@NotNull KtCallElement callElement) {
return new DelegatingCall(makeCall(null, null, callElement)) {
@NotNull
@Override
public List<KtTypeProjection> getTypeArguments() {
return Collections.emptyList();
}
@Nullable
@Override
public KtTypeArgumentList getTypeArgumentList() {
return null;
}
};
}
@NotNull
public static Call makeCall(@Nullable final Receiver explicitReceiver, @Nullable final ASTNode callOperationNode, @NotNull final KtCallElement callElement) {
return new Call() {
@@ -8,10 +8,10 @@ public class A<E> {
// FILE: main.kt
class B1(x: List<String>) : A<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><CharSequence><!>("", x)
class B2(x: List<Int>) : A<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><CharSequence><!>("", x)
class B1(x: List<String>) : A<CharSequence>("", x)
class B2(x: List<Int>) : <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>A<CharSequence>("", x)<!>
class C : A<CharSequence> {
constructor(x: List<String>) : super("", <!TYPE_MISMATCH(kotlin.collections.\(Mutable\)List<T!>!; kotlin.collections.List<kotlin.String>)!>x<!>)
constructor(x: List<Int>, y: Int) : super("", <!TYPE_MISMATCH!>x<!>)
constructor(x: List<String>) : super("", x)
constructor(x: List<Int>, y: Int) : <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>super<!>("", x)
}
@@ -0,0 +1,20 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// !CHECK_TYPE
// FILE: A.java
public class A<E> {
public <T extends E, Q> A(E x, java.util.List<E> y) {}
}
// FILE: main.kt
// TODO: It's effectively impossible to perform super call to such constructor
// if there is not enough information to infer corresponding arguments
// May be we could add some special syntax for such arguments
class B1(x: List<String>) : <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>A<!><CharSequence>("", x)
class B2(x: List<Int>) : <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>A<CharSequence>("", x)<!>
class C : A<CharSequence> {
constructor(x: List<String>) : <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>super<!>("", x)
constructor(x: List<Int>, y: Int) : <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>super<!>("", x)
}
@@ -0,0 +1,30 @@
package
public open class A</*0*/ E : kotlin.Any!> {
public constructor A</*0*/ E : kotlin.Any!, /*1*/ T : E!, /*2*/ Q : kotlin.Any!>(/*0*/ x: E!, /*1*/ y: kotlin.collections.(Mutable)List<E!>!)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class B1 : A<kotlin.CharSequence> {
public constructor B1(/*0*/ x: kotlin.collections.List<kotlin.String>)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class B2 : A<kotlin.CharSequence> {
public constructor B2(/*0*/ x: kotlin.collections.List<kotlin.Int>)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class C : A<kotlin.CharSequence> {
public constructor C(/*0*/ x: kotlin.collections.List<kotlin.Int>, /*1*/ y: kotlin.Int)
public constructor C(/*0*/ x: kotlin.collections.List<kotlin.String>)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -10247,6 +10247,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("superCallImpossibleToInfer.kt")
public void testSuperCallImpossibleToInfer() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/genericConstructor/superCallImpossibleToInfer.kt");
doTest(fileName);
}
@TestMetadata("withClassTypeParameters.kt")
public void testWithClassTypeParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/genericConstructor/withClassTypeParameters.kt");