From c26ca5e7eff47fbfec7a816845f8eb9daae262a2 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 22 Jan 2016 16:00:43 +0300 Subject: [PATCH] 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 --- .../kotlin/resolve/BodyResolver.java | 2 +- .../kotlin/resolve/calls/CallResolver.java | 60 +++++++++++++++---- .../kotlin/resolve/calls/util/CallMaker.java | 18 ++++++ .../tests/j+k/genericConstructor/superCall.kt | 8 +-- .../superCallImpossibleToInfer.kt | 20 +++++++ .../superCallImpossibleToInfer.txt | 30 ++++++++++ .../checkers/DiagnosticsTestGenerated.java | 6 ++ 7 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/j+k/genericConstructor/superCallImpossibleToInfer.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/genericConstructor/superCallImpossibleToInfer.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java index 4166045baab..6521fa09edf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java @@ -314,7 +314,7 @@ public class BodyResolver { } OverloadResolutionResults 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); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java index 8942c979995..ee270970805 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java @@ -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> candidates = - taskPrioritizer.convertWithImpliedThisAndNoReceiver( - context.scope, constructors, context.call, knownSubstitutor); + + Pair>, BasicCallResolutionContext> candidatesAndContext = + prepareCandidatesAndContextForConstructorCall(constructedType, context); + + Collection> 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> candidates = - taskPrioritizer.convertWithImpliedThisAndNoReceiver( - context.scope, constructors, context.call, knownTypeParametersSubstitutor); + Pair>, BasicCallResolutionContext> candidatesAndContext = + prepareCandidatesAndContextForConstructorCall(superType, context); + Collection> 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>, BasicCallResolutionContext> prepareCandidatesAndContextForConstructorCall( + @NotNull KotlinType superType, + @NotNull BasicCallResolutionContext context + ) { + if (!(superType.getConstructor().getDeclarationDescriptor() instanceof ClassDescriptor)) { + return new Pair>, BasicCallResolutionContext>( + Collections.>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> candidates = + taskPrioritizer.convertWithImpliedThisAndNoReceiver( + context.scope, superClass.getConstructors(), context.call, knownTypeParametersSubstitutor); + + return new Pair>, 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 resolveCallWithKnownCandidate( @NotNull final Call call, @NotNull final TracingStrategy tracing, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java index 5bfe4798001..54811533434 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/util/CallMaker.java @@ -264,6 +264,24 @@ public class CallMaker { return makeCallWithExpressions(nameExpression, explicitReceiver, callOperationNode, nameExpression, Collections.emptyList()); } + + @NotNull + public static Call makeConstructorCallWithoutTypeArguments(@NotNull KtCallElement callElement) { + return new DelegatingCall(makeCall(null, null, callElement)) { + @NotNull + @Override + public List 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() { diff --git a/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCall.kt b/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCall.kt index 47827075260..626599c15ee 100644 --- a/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCall.kt +++ b/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCall.kt @@ -8,10 +8,10 @@ public class A { // FILE: main.kt -class B1(x: List) : A("", x) -class B2(x: List) : A("", x) +class B1(x: List) : A("", x) +class B2(x: List) : A("", x) class C : A { - constructor(x: List) : super("", !; kotlin.collections.List)!>x) - constructor(x: List, y: Int) : super("", x) + constructor(x: List) : super("", x) + constructor(x: List, y: Int) : super("", x) } diff --git a/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCallImpossibleToInfer.kt b/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCallImpossibleToInfer.kt new file mode 100644 index 00000000000..c1b94c915aa --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCallImpossibleToInfer.kt @@ -0,0 +1,20 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !CHECK_TYPE +// FILE: A.java + +public class A { + public A(E x, java.util.List 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) : A("", x) +class B2(x: List) : A("", x) + +class C : A { + constructor(x: List) : super("", x) + constructor(x: List, y: Int) : super("", x) +} diff --git a/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCallImpossibleToInfer.txt b/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCallImpossibleToInfer.txt new file mode 100644 index 00000000000..cda2c393374 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/genericConstructor/superCallImpossibleToInfer.txt @@ -0,0 +1,30 @@ +package + +public open class A { + public constructor A(/*0*/ x: E!, /*1*/ y: kotlin.collections.(Mutable)List!) + 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 { + public constructor B1(/*0*/ x: kotlin.collections.List) + 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 { + public constructor B2(/*0*/ x: kotlin.collections.List) + 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 { + public constructor C(/*0*/ x: kotlin.collections.List, /*1*/ y: kotlin.Int) + public constructor C(/*0*/ x: kotlin.collections.List) + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index a7e65d369b0..bfb1e69f79a 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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");