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(
|
OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveFunctionCall(
|
||||||
trace, scopeForConstructor,
|
trace, scopeForConstructor,
|
||||||
CallMaker.makeCall(null, null, call), NO_EXPECTED_TYPE, outerDataFlowInfo, false);
|
CallMaker.makeConstructorCallWithoutTypeArguments(call), NO_EXPECTED_TYPE, outerDataFlowInfo, false);
|
||||||
if (results.isSuccess()) {
|
if (results.isSuccess()) {
|
||||||
KotlinType supertype = results.getResultingDescriptor().getReturnType();
|
KotlinType supertype = results.getResultingDescriptor().getReturnType();
|
||||||
recordSupertype(typeReference, supertype);
|
recordSupertype(typeReference, supertype);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.google.common.collect.Lists;
|
|||||||
import com.intellij.openapi.util.Condition;
|
import com.intellij.openapi.util.Condition;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
|
import kotlin.Pair;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlin.jvm.functions.Function0;
|
import kotlin.jvm.functions.Function0;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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.lazy.ForceResolveUtil;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
|
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.KotlinType;
|
||||||
import org.jetbrains.kotlin.types.TypeSubstitutor;
|
import org.jetbrains.kotlin.types.TypeSubstitutor;
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext;
|
||||||
@@ -381,10 +381,12 @@ public class CallResolver {
|
|||||||
context.trace.report(NO_CONSTRUCTOR.on(CallUtilKt.getValueArgumentListOrElement(context.call)));
|
context.trace.report(NO_CONSTRUCTOR.on(CallUtilKt.getValueArgumentListOrElement(context.call)));
|
||||||
return checkArgumentTypesAndFail(context);
|
return checkArgumentTypesAndFail(context);
|
||||||
}
|
}
|
||||||
TypeSubstitutor knownSubstitutor = TypeSubstitutor.create(constructedType);
|
|
||||||
Collection<ResolutionCandidate<CallableDescriptor>> candidates =
|
Pair<Collection<ResolutionCandidate<CallableDescriptor>>, BasicCallResolutionContext> candidatesAndContext =
|
||||||
taskPrioritizer.<CallableDescriptor>convertWithImpliedThisAndNoReceiver(
|
prepareCandidatesAndContextForConstructorCall(constructedType, context);
|
||||||
context.scope, constructors, context.call, knownSubstitutor);
|
|
||||||
|
Collection<ResolutionCandidate<CallableDescriptor>> candidates = candidatesAndContext.getFirst();
|
||||||
|
context = candidatesAndContext.getSecond();
|
||||||
|
|
||||||
return computeTasksFromCandidatesAndResolvedCall(context, functionReference, candidates, CallTransformer.FUNCTION_CALL_TRANSFORMER);
|
return computeTasksFromCandidatesAndResolvedCall(context, functionReference, candidates, CallTransformer.FUNCTION_CALL_TRANSFORMER);
|
||||||
}
|
}
|
||||||
@@ -460,11 +462,10 @@ public class CallResolver {
|
|||||||
calleeConstructor.getContainingDeclaration().getDefaultType() :
|
calleeConstructor.getContainingDeclaration().getDefaultType() :
|
||||||
DescriptorUtils.getSuperClassType(currentClassDescriptor);
|
DescriptorUtils.getSuperClassType(currentClassDescriptor);
|
||||||
|
|
||||||
TypeSubstitutor knownTypeParametersSubstitutor = TypeSubstitutor.create(superType);
|
Pair<Collection<ResolutionCandidate<CallableDescriptor>>, BasicCallResolutionContext> candidatesAndContext =
|
||||||
|
prepareCandidatesAndContextForConstructorCall(superType, context);
|
||||||
Collection<ResolutionCandidate<CallableDescriptor>> candidates =
|
Collection<ResolutionCandidate<CallableDescriptor>> candidates = candidatesAndContext.getFirst();
|
||||||
taskPrioritizer.<CallableDescriptor>convertWithImpliedThisAndNoReceiver(
|
context = candidatesAndContext.getSecond();
|
||||||
context.scope, constructors, context.call, knownTypeParametersSubstitutor);
|
|
||||||
|
|
||||||
TracingStrategy tracing = call.isImplicit() ?
|
TracingStrategy tracing = call.isImplicit() ?
|
||||||
new TracingStrategyForImplicitConstructorDelegationCall(call, context.call) :
|
new TracingStrategyForImplicitConstructorDelegationCall(call, context.call) :
|
||||||
@@ -481,6 +482,45 @@ public class CallResolver {
|
|||||||
return computeTasksFromCandidatesAndResolvedCall(context, candidates, CallTransformer.FUNCTION_CALL_TRANSFORMER, tracing);
|
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(
|
public OverloadResolutionResults<FunctionDescriptor> resolveCallWithKnownCandidate(
|
||||||
@NotNull final Call call,
|
@NotNull final Call call,
|
||||||
@NotNull final TracingStrategy tracing,
|
@NotNull final TracingStrategy tracing,
|
||||||
|
|||||||
@@ -264,6 +264,24 @@ public class CallMaker {
|
|||||||
return makeCallWithExpressions(nameExpression, explicitReceiver, callOperationNode, nameExpression, Collections.<KtExpression>emptyList());
|
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
|
@NotNull
|
||||||
public static Call makeCall(@Nullable final Receiver explicitReceiver, @Nullable final ASTNode callOperationNode, @NotNull final KtCallElement callElement) {
|
public static Call makeCall(@Nullable final Receiver explicitReceiver, @Nullable final ASTNode callOperationNode, @NotNull final KtCallElement callElement) {
|
||||||
return new Call() {
|
return new Call() {
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ public class A<E> {
|
|||||||
|
|
||||||
// FILE: main.kt
|
// FILE: main.kt
|
||||||
|
|
||||||
class B1(x: List<String>) : A<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><CharSequence><!>("", x)
|
class B1(x: List<String>) : A<CharSequence>("", x)
|
||||||
class B2(x: List<Int>) : A<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><CharSequence><!>("", x)
|
class B2(x: List<Int>) : <!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>A<CharSequence>("", x)<!>
|
||||||
|
|
||||||
class C : A<CharSequence> {
|
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<String>) : super("", x)
|
||||||
constructor(x: List<Int>, y: Int) : super("", <!TYPE_MISMATCH!>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);
|
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")
|
@TestMetadata("withClassTypeParameters.kt")
|
||||||
public void testWithClassTypeParameters() throws Exception {
|
public void testWithClassTypeParameters() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/genericConstructor/withClassTypeParameters.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/j+k/genericConstructor/withClassTypeParameters.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user