KT-6081 Chained generic method calls: wrong type inference
#KT-6081 Fixed
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
//KT-6081 Chained generic method calls: wrong type inference
|
||||||
|
|
||||||
|
class Bar<T>
|
||||||
|
|
||||||
|
fun bar<T>(): Bar<T> = null!!
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
fun <R> add(bar: Bar<R>): Foo {return this}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doesNotWork(bi: Bar<Int>, bs: Bar<String>) {
|
||||||
|
Foo().add(bi).add(bs)
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal fun </*0*/ T> bar(): Bar<T>
|
||||||
|
internal fun doesNotWork(/*0*/ bi: Bar<kotlin.Int>, /*1*/ bs: Bar<kotlin.String>): kotlin.Unit
|
||||||
|
|
||||||
|
internal final class Bar</*0*/ T> {
|
||||||
|
public constructor Bar</*0*/ T>()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final class Foo {
|
||||||
|
public constructor Foo()
|
||||||
|
internal final fun </*0*/ R> add(/*0*/ bar: Bar<R>): Foo
|
||||||
|
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
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
class A<T> {
|
||||||
|
fun useT(t: T) = t
|
||||||
|
|
||||||
|
fun <U> newA(): A<U> = A()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test1() {
|
||||||
|
A<Int>().newA<String>().useT("")
|
||||||
|
A<Int>().newA<String>().useT(<!CONSTANT_EXPECTED_TYPE_MISMATCH!>1<!>)
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal fun test1(): kotlin.Unit
|
||||||
|
|
||||||
|
internal final class A</*0*/ T> {
|
||||||
|
public constructor A</*0*/ T>()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
internal final fun </*0*/ U> newA(): A<U>
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
internal final fun useT(/*0*/ t: T): T
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
class Test {
|
||||||
|
private fun <T : Any> T.self() = object {
|
||||||
|
fun bar(): T {
|
||||||
|
return this@self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun test() {
|
||||||
|
1.self().bar() + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Foo<R> {
|
||||||
|
private fun <T> bar() = object {
|
||||||
|
fun baz(): Foo<R> {
|
||||||
|
return this@Foo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getR(r: R) = r
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
Foo<Int>().bar<String>().baz().getR(1)
|
||||||
|
Foo<Int>().bar<String>().baz().getR(<!TYPE_MISMATCH!>""<!>)
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal final class Foo</*0*/ R> {
|
||||||
|
public constructor Foo</*0*/ R>()
|
||||||
|
private final fun </*0*/ T> bar(): Foo.bar.<no name provided>
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
internal final fun getR(/*0*/ r: R): R
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
internal final fun test(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final class Test {
|
||||||
|
public constructor Test()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
internal final fun test(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
private final fun </*0*/ T : kotlin.Any> T.self(): Test.self.<no name provided>
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
|
class Outer<T> {
|
||||||
|
inner class Inner<I> {
|
||||||
|
fun getOuter(): Outer<T> = this@Outer
|
||||||
|
fun <R> genericFun(r: R): Outer<T> = this@Outer
|
||||||
|
}
|
||||||
|
fun useOuterParam(t: T) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test1() {
|
||||||
|
Outer<Int>().Inner<String>().getOuter().useOuterParam(22)
|
||||||
|
Outer<Int>().Inner<String>().getOuter().useOuterParam(<!TYPE_MISMATCH!>""<!>)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class A
|
||||||
|
class B
|
||||||
|
|
||||||
|
fun test1(a: A, b: B) {
|
||||||
|
Outer<Int>().Inner<String>().genericFun(a).Inner<Double>().genericFun(b)
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal fun test1(): kotlin.Unit
|
||||||
|
internal fun test1(/*0*/ a: A, /*1*/ b: B): kotlin.Unit
|
||||||
|
|
||||||
|
internal final class A {
|
||||||
|
public constructor A()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final class B {
|
||||||
|
public constructor B()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final class Outer</*0*/ T> {
|
||||||
|
public constructor Outer</*0*/ T>()
|
||||||
|
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
|
||||||
|
internal final fun useOuterParam(/*0*/ t: T): kotlin.Unit
|
||||||
|
|
||||||
|
internal final inner class Inner</*0*/ I> {
|
||||||
|
public constructor Inner</*0*/ I>()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
internal final fun </*0*/ R> genericFun(/*0*/ r: R): Outer<T>
|
||||||
|
internal final fun getOuter(): Outer<T>
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4806,7 +4806,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
|
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/inference")
|
@TestMetadata("compiler/testData/diagnostics/tests/inference")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@InnerTestClasses({Inference.Constraints.class, Inference.NestedCalls.class, Inference.Regressions.class, Inference.ReportingImprovements.class, Inference.UpperBounds.class, Inference.Varargs.class})
|
@InnerTestClasses({Inference.Constraints.class, Inference.NestedCalls.class, Inference.Regressions.class, Inference.ReportingImprovements.class, Inference.Substitutions.class, Inference.UpperBounds.class, Inference.Varargs.class})
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class Inference extends AbstractJetDiagnosticsTest {
|
public static class Inference extends AbstractJetDiagnosticsTest {
|
||||||
public void testAllFilesPresentInInference() throws Exception {
|
public void testAllFilesPresentInInference() throws Exception {
|
||||||
@@ -5419,6 +5419,39 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/diagnostics/tests/inference/substitutions")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Substitutions extends AbstractJetDiagnosticsTest {
|
||||||
|
public void testAllFilesPresentInSubstitutions() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/inference/substitutions"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt6081SubstituteIntoClassCorrectly.kt")
|
||||||
|
public void testKt6081SubstituteIntoClassCorrectly() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/substitutions/kt6081SubstituteIntoClassCorrectly.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleSubstitutionCheckTypeArgumentsNotTypeParameters.kt")
|
||||||
|
public void testSimpleSubstitutionCheckTypeArgumentsNotTypeParameters() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/substitutions/simpleSubstitutionCheckTypeArgumentsNotTypeParameters.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("substitutionIntoAnonymousClass.kt")
|
||||||
|
public void testSubstitutionIntoAnonymousClass() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/substitutions/substitutionIntoAnonymousClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("substitutionIntoInnerClass.kt")
|
||||||
|
public void testSubstitutionIntoInnerClass() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/substitutions/substitutionIntoInnerClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/inference/upperBounds")
|
@TestMetadata("compiler/testData/diagnostics/tests/inference/upperBounds")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+7
-1
@@ -106,7 +106,13 @@ public class LazySubstitutingClassDescriptor implements ClassDescriptor {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public JetType getDefaultType() {
|
public JetType getDefaultType() {
|
||||||
throw new UnsupportedOperationException(); // TODO
|
List<TypeProjection> typeProjections = TypeUtils.getDefaultTypeProjections(getTypeConstructor().getParameters());
|
||||||
|
return new JetTypeImpl(
|
||||||
|
getAnnotations(),
|
||||||
|
getTypeConstructor(),
|
||||||
|
false,
|
||||||
|
typeProjections,
|
||||||
|
getMemberScope(typeProjections));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -21,11 +21,9 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.SubstitutingScope;
|
import org.jetbrains.jet.lang.resolve.scopes.SubstitutingScope;
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||||
|
import org.jetbrains.jet.lang.types.typeUtil.TypeUtilPackage;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class TypeSubstitutor {
|
public class TypeSubstitutor {
|
||||||
|
|
||||||
@@ -202,18 +200,41 @@ public class TypeSubstitutor {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
// The type is not within the substitution range, i.e. Foo, Bar<T> etc.
|
||||||
// The type is not within the substitution range, i.e. Foo, Bar<T> etc.
|
return substituteCompoundType(type, originalProjectionKind, recursionDepth);
|
||||||
List<TypeProjection> substitutedArguments = substituteTypeArguments(
|
}
|
||||||
type.getConstructor().getParameters(), type.getArguments(), recursionDepth);
|
|
||||||
|
|
||||||
JetType substitutedType = new JetTypeImpl(type.getAnnotations(), // Old annotations. This is questionable
|
private TypeProjection substituteCompoundType(
|
||||||
type.getConstructor(), // The same constructor
|
final JetType type,
|
||||||
type.isNullable(), // Same nullability
|
Variance projectionKind,
|
||||||
substitutedArguments,
|
int recursionDepth
|
||||||
new SubstitutingScope(type.getMemberScope(), this));
|
) throws SubstitutionException {
|
||||||
return new TypeProjectionImpl(originalProjectionKind, substitutedType);
|
List<TypeProjection> substitutedArguments = substituteTypeArguments(
|
||||||
}
|
type.getConstructor().getParameters(), type.getArguments(), recursionDepth);
|
||||||
|
|
||||||
|
// Only type parameters of the corresponding class (or captured type parameters of outer declaration) are substituted
|
||||||
|
// e.g. for return type Foo of 'add(..)' in 'class Foo { fun <R> add(bar: Bar<R>): Foo }' R shouldn't be substituted in the scope
|
||||||
|
TypeSubstitution substitutionFilteringTypeParameters = new TypeSubstitution() {
|
||||||
|
private final Collection<TypeConstructor> containedOrCapturedTypeParameters =
|
||||||
|
TypeUtilPackage.getContainedAndCapturedTypeParameterConstructors(type);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public TypeProjection get(TypeConstructor key) {
|
||||||
|
return containedOrCapturedTypeParameters.contains(key) ? substitution.get(key) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return substitution.isEmpty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
JetType substitutedType = new JetTypeImpl(type.getAnnotations(), // Old annotations. This is questionable
|
||||||
|
type.getConstructor(), // The same constructor
|
||||||
|
type.isNullable(), // Same nullability
|
||||||
|
substitutedArguments,
|
||||||
|
new SubstitutingScope(type.getMemberScope(), create(substitutionFilteringTypeParameters)));
|
||||||
|
return new TypeProjectionImpl(projectionKind, substitutedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<TypeProjection> substituteTypeArguments(
|
private List<TypeProjection> substituteTypeArguments(
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2014 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.jet.lang.types.typeUtil
|
||||||
|
|
||||||
|
import java.util.LinkedHashSet
|
||||||
|
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
|
||||||
|
import org.jetbrains.jet.lang.types.JetType
|
||||||
|
import org.jetbrains.jet.lang.types.Flexibility
|
||||||
|
import org.jetbrains.jet.lang.types.TypeConstructor
|
||||||
|
import org.jetbrains.jet.utils.toReadOnlyList
|
||||||
|
|
||||||
|
fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||||
|
val declarationDescriptor = getConstructor().getDeclarationDescriptor()
|
||||||
|
if (declarationDescriptor is TypeParameterDescriptor) return listOf(declarationDescriptor)
|
||||||
|
|
||||||
|
val flexibility = getCapability(javaClass<Flexibility>())
|
||||||
|
if (flexibility != null) {
|
||||||
|
return flexibility.lowerBound.getContainedTypeParameters() + flexibility.upperBound.getContainedTypeParameters()
|
||||||
|
}
|
||||||
|
return getArguments().map { it.getType() }.flatMap { it.getContainedTypeParameters() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun DeclarationDescriptor.getCapturedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||||
|
val result = LinkedHashSet<TypeParameterDescriptor>()
|
||||||
|
val containingDeclaration = this.getContainingDeclaration()
|
||||||
|
|
||||||
|
if (containingDeclaration is ClassDescriptor) {
|
||||||
|
result.addAll(containingDeclaration.getDefaultType().getContainedTypeParameters())
|
||||||
|
}
|
||||||
|
else if (containingDeclaration is CallableDescriptor) {
|
||||||
|
result.addAll(containingDeclaration.getTypeParameters())
|
||||||
|
}
|
||||||
|
if (containingDeclaration != null) {
|
||||||
|
result.addAll(containingDeclaration.getCapturedTypeParameters())
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun JetType.getContainedAndCapturedTypeParameterConstructors(): Collection<TypeConstructor> {
|
||||||
|
// todo type arguments (instead of type parameters) of the type of outer class must be considered; KT-6325
|
||||||
|
val typeParameters = getContainedTypeParameters() + getConstructor().getDeclarationDescriptor().getCapturedTypeParameters()
|
||||||
|
return typeParameters.map { it.getTypeConstructor() }.toReadOnlyList()
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user