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:
@@ -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)
|
||||
}
|
||||
|
||||
+20
@@ -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)
|
||||
}
|
||||
+30
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user