KT-6803 Subtyping breaks on star-projections for recursive generics
Star-projections have upper bounds of the form "parameter's bound where all parameter of the same class are substituted with their star-projections" #KT-6803 Fixed
This commit is contained in:
@@ -9,10 +9,10 @@ fun main(args : Array<String>) {
|
|||||||
val <!UNUSED_VARIABLE!>a<!> : C<Int> = C();
|
val <!UNUSED_VARIABLE!>a<!> : C<Int> = C();
|
||||||
val <!UNUSED_VARIABLE!>x<!> : C<in String> = C()
|
val <!UNUSED_VARIABLE!>x<!> : C<in String> = C()
|
||||||
val <!UNUSED_VARIABLE!>y<!> : C<out String> = C()
|
val <!UNUSED_VARIABLE!>y<!> : C<out String> = C()
|
||||||
val <!UNUSED_VARIABLE!>z<!> : C<*> = C()
|
val <!UNUSED_VARIABLE!>z<!> : C<*> = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>C<!>()
|
||||||
|
|
||||||
val <!UNUSED_VARIABLE!>ba<!> : C<Int> = bar();
|
val <!UNUSED_VARIABLE!>ba<!> : C<Int> = bar();
|
||||||
val <!UNUSED_VARIABLE!>bx<!> : C<in String> = bar()
|
val <!UNUSED_VARIABLE!>bx<!> : C<in String> = bar()
|
||||||
val <!UNUSED_VARIABLE!>by<!> : C<out String> = bar()
|
val <!UNUSED_VARIABLE!>by<!> : C<out String> = bar()
|
||||||
val <!UNUSED_VARIABLE!>bz<!> : C<*> = bar()
|
val <!UNUSED_VARIABLE!>bz<!> : C<*> = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>bar<!>()
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// !CHECK_TYPE
|
||||||
|
|
||||||
|
trait A<R, T: A<R, T>> {
|
||||||
|
fun r(): R
|
||||||
|
fun t(): T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testA(a: A<*, *>) {
|
||||||
|
a.r().checkType { it : _<Any?> }
|
||||||
|
a.t().checkType { it : _<A<*, *>> }
|
||||||
|
}
|
||||||
|
|
||||||
|
trait B<R, T: B<List<R>, <!UPPER_BOUND_VIOLATED!>T<!>>> {
|
||||||
|
fun r(): R
|
||||||
|
fun t(): T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testB(b: B<*, *>) {
|
||||||
|
b.r().checkType { it : _<Any?> }
|
||||||
|
b.t().checkType { it : _<B<List<*>, *>> }
|
||||||
|
|
||||||
|
b.t().r().size()
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal fun testA(/*0*/ a: A<*, *>): kotlin.Unit
|
||||||
|
internal fun testB(/*0*/ b: B<*, *>): kotlin.Unit
|
||||||
|
|
||||||
|
internal trait A</*0*/ R, /*1*/ T : A<R, 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 abstract fun r(): R
|
||||||
|
internal abstract fun t(): T
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
internal trait B</*0*/ R, /*1*/ T : B<kotlin.List<R>, 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 abstract fun r(): R
|
||||||
|
internal abstract fun t(): T
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -44,4 +44,8 @@ trait B: Parent
|
|||||||
|
|
||||||
trait Rec<T>
|
trait Rec<T>
|
||||||
class ARec : Rec<ARec>
|
class ARec : Rec<ARec>
|
||||||
class BRec : Rec<BRec>
|
class BRec : Rec<BRec>
|
||||||
|
trait SubRec<T>: Rec<T>
|
||||||
|
|
||||||
|
trait Star<T : Star<T>>
|
||||||
|
trait SubStar<T : SubStar<T>> : Star<T>
|
||||||
@@ -10901,6 +10901,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("starProjections.kt")
|
||||||
|
public void testStarProjections() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/substitutions/starProjections.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("upperBoundsSubstitutionForOverloadResolutionWithAmbiguity.kt")
|
@TestMetadata("upperBoundsSubstitutionForOverloadResolutionWithAmbiguity.kt")
|
||||||
public void testUpperBoundsSubstitutionForOverloadResolutionWithAmbiguity() throws Exception {
|
public void testUpperBoundsSubstitutionForOverloadResolutionWithAmbiguity() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/substitutions/upperBoundsSubstitutionForOverloadResolutionWithAmbiguity.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/substitutions/upperBoundsSubstitutionForOverloadResolutionWithAmbiguity.kt");
|
||||||
|
|||||||
@@ -328,6 +328,23 @@ public class JetTypeCheckerTest extends JetLiteFixture {
|
|||||||
assertSubtype("Nothing?", "Derived_T<*>?");
|
assertSubtype("Nothing?", "Derived_T<*>?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testStars() throws Exception {
|
||||||
|
assertSubtype("SubStar<*>", "Star<*>");
|
||||||
|
assertSubtype("SubStar<SubStar<*>>", "Star<*>");
|
||||||
|
assertSubtype("SubStar<SubStar<*>>", "Star<SubStar<*>>");
|
||||||
|
assertNotSubtype("SubStar<SubStar<*>>", "Star<Star<*>>");
|
||||||
|
|
||||||
|
assertSubtype("Star<Star<*>>", "Star<*>");
|
||||||
|
assertSubtype("Star<*>", "Star<out Star<*>>");
|
||||||
|
|
||||||
|
assertNotSubtype("Star<*>", "Star<Star<*>>");
|
||||||
|
|
||||||
|
assertSubtype("SubRec<*>", "Rec<*>");
|
||||||
|
assertSubtype("SubRec<*>", "Rec<out Any?>");
|
||||||
|
assertSubtype("Rec<*>", "Rec<out Any?>");
|
||||||
|
assertNotSubtype("Rec<*>", "Rec<out Any>");
|
||||||
|
}
|
||||||
|
|
||||||
public void testThis() throws Exception {
|
public void testThis() throws Exception {
|
||||||
assertType("Derived_T<Int>", "this", "Derived_T<Int>");
|
assertType("Derived_T<Int>", "this", "Derived_T<Int>");
|
||||||
// assertType("Derived_T<Int>", "super<Base_T>", "Base_T<Int>");
|
// assertType("Derived_T<Int>", "super<Base_T>", "Base_T<Int>");
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ public class CommonSupertypes {
|
|||||||
return 1 + maxDepth(KotlinPackage.map(type.getArguments(), new Function1<TypeProjection, JetType>() {
|
return 1 + maxDepth(KotlinPackage.map(type.getArguments(), new Function1<TypeProjection, JetType>() {
|
||||||
@Override
|
@Override
|
||||||
public JetType invoke(TypeProjection projection) {
|
public JetType invoke(TypeProjection projection) {
|
||||||
|
if (projection.isStarProjection()) {
|
||||||
|
// any type is good enough for depth here
|
||||||
|
return KotlinBuiltIns.getInstance().getAnyType();
|
||||||
|
}
|
||||||
return projection.getType();
|
return projection.getType();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ public class ErrorUtils {
|
|||||||
if (type == null) return false;
|
if (type == null) return false;
|
||||||
if (type.isError()) return true;
|
if (type.isError()) return true;
|
||||||
for (TypeProjection projection : type.getArguments()) {
|
for (TypeProjection projection : type.getArguments()) {
|
||||||
if (containsErrorType(projection.getType())) return true;
|
if (!projection.isStarProjection() && containsErrorType(projection.getType())) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,35 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.types
|
package org.jetbrains.kotlin.types
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import kotlin.properties.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
|
||||||
class StarProjectionImpl(
|
class StarProjectionImpl(
|
||||||
|
private val typeParameter: TypeParameterDescriptor
|
||||||
|
) : TypeProjectionBase() {
|
||||||
|
override fun isStarProjection() = true
|
||||||
|
|
||||||
|
override fun getProjectionKind() = Variance.OUT_VARIANCE
|
||||||
|
|
||||||
|
private val _type: JetType by Delegates.lazy {
|
||||||
|
val classDescriptor = typeParameter.getContainingDeclaration() as ClassDescriptor
|
||||||
|
val typeParameters = classDescriptor.getTypeConstructor().getParameters().map { it.getTypeConstructor() }
|
||||||
|
TypeSubstitutor.create(
|
||||||
|
object : TypeSubstitution() {
|
||||||
|
override fun get(key: TypeConstructor) = when (key) {
|
||||||
|
typeParameter.getTypeConstructor() -> this@StarProjectionImpl
|
||||||
|
in typeParameters -> TypeUtils.makeStarProjection(key.getDeclarationDescriptor() as TypeParameterDescriptor)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
).substitute(typeParameter.getUpperBounds().iterator().next(), Variance.OUT_VARIANCE)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getType() = _type
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypeBasedStarProjectionImpl(
|
||||||
private val _type: JetType
|
private val _type: JetType
|
||||||
) : TypeProjectionBase() {
|
) : TypeProjectionBase() {
|
||||||
override fun isStarProjection() = true
|
override fun isStarProjection() = true
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public abstract class TypeProjectionBase implements TypeProjection {
|
|||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = getProjectionKind().hashCode();
|
int result = getProjectionKind().hashCode();
|
||||||
result = 31 * result + (getType().hashCode());
|
result = 31 * result + (isStarProjection() ? 17 : getType().hashCode());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,6 +213,9 @@ public class TypeSubstitutor {
|
|||||||
if (typeVariable != null) {
|
if (typeVariable != null) {
|
||||||
substitutedType = typeVariable.substitutionResult(replacement.getType());
|
substitutedType = typeVariable.substitutionResult(replacement.getType());
|
||||||
}
|
}
|
||||||
|
else if (replacement.isStarProjection()) {
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// this is a simple type T or T?: if it's T, we should just take replacement, if T? - we make replacement nullable
|
// this is a simple type T or T?: if it's T, we should just take replacement, if T? - we make replacement nullable
|
||||||
substitutedType = TypeUtils.makeNullableIfNeeded(replacement.getType(), type.isMarkedNullable());
|
substitutedType = TypeUtils.makeNullableIfNeeded(replacement.getType(), type.isMarkedNullable());
|
||||||
|
|||||||
@@ -549,7 +549,7 @@ public class TypeUtils {
|
|||||||
public static boolean dependsOnTypeConstructors(@NotNull JetType type, @NotNull Collection<TypeConstructor> typeParameterConstructors) {
|
public static boolean dependsOnTypeConstructors(@NotNull JetType type, @NotNull Collection<TypeConstructor> typeParameterConstructors) {
|
||||||
if (typeParameterConstructors.contains(type.getConstructor())) return true;
|
if (typeParameterConstructors.contains(type.getConstructor())) return true;
|
||||||
for (TypeProjection typeProjection : type.getArguments()) {
|
for (TypeProjection typeProjection : type.getArguments()) {
|
||||||
if (dependsOnTypeConstructors(typeProjection.getType(), typeParameterConstructors)) {
|
if (!typeProjection.isStarProjection() && dependsOnTypeConstructors(typeProjection.getType(), typeParameterConstructors)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -577,14 +577,14 @@ public class TypeUtils {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (TypeProjection projection : type.getArguments()) {
|
for (TypeProjection projection : type.getArguments()) {
|
||||||
if (containsSpecialType(projection.getType(), isSpecialType)) return true;
|
if (!projection.isStarProjection() && containsSpecialType(projection.getType(), isSpecialType)) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor) {
|
public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor) {
|
||||||
return new StarProjectionImpl(parameterDescriptor.getUpperBoundsAsType());
|
return new StarProjectionImpl(parameterDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.types.TypeProjectionImpl
|
|||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.types.DelegatingType
|
import org.jetbrains.kotlin.types.DelegatingType
|
||||||
|
|
||||||
fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
private fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||||
val declarationDescriptor = getConstructor().getDeclarationDescriptor()
|
val declarationDescriptor = getConstructor().getDeclarationDescriptor()
|
||||||
if (declarationDescriptor is TypeParameterDescriptor) return listOf(declarationDescriptor)
|
if (declarationDescriptor is TypeParameterDescriptor) return listOf(declarationDescriptor)
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
|||||||
if (flexibility != null) {
|
if (flexibility != null) {
|
||||||
return flexibility.lowerBound.getContainedTypeParameters() + flexibility.upperBound.getContainedTypeParameters()
|
return flexibility.lowerBound.getContainedTypeParameters() + flexibility.upperBound.getContainedTypeParameters()
|
||||||
}
|
}
|
||||||
return getArguments().map { it.getType() }.flatMap { it.getContainedTypeParameters() }
|
return getArguments().filter { !it.isStarProjection() }.map { it.getType() }.flatMap { it.getContainedTypeParameters() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DeclarationDescriptor.getCapturedTypeParameters(): Collection<TypeParameterDescriptor> {
|
fun DeclarationDescriptor.getCapturedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||||
|
|||||||
@@ -106,6 +106,10 @@ public class TypeCheckingProcedure {
|
|||||||
TypeProjection typeProjection1 = type1Arguments.get(i);
|
TypeProjection typeProjection1 = type1Arguments.get(i);
|
||||||
TypeParameterDescriptor typeParameter2 = constructor2.getParameters().get(i);
|
TypeParameterDescriptor typeParameter2 = constructor2.getParameters().get(i);
|
||||||
TypeProjection typeProjection2 = type2Arguments.get(i);
|
TypeProjection typeProjection2 = type2Arguments.get(i);
|
||||||
|
if (typeProjection1.isStarProjection() && typeProjection2.isStarProjection()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (capture(typeProjection1, typeProjection2, typeParameter1)) {
|
if (capture(typeProjection1, typeProjection2, typeParameter1)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -227,14 +231,16 @@ public class TypeCheckingProcedure {
|
|||||||
for (int i = 0; i < parameters.size(); i++) {
|
for (int i = 0; i < parameters.size(); i++) {
|
||||||
TypeParameterDescriptor parameter = parameters.get(i);
|
TypeParameterDescriptor parameter = parameters.get(i);
|
||||||
|
|
||||||
|
TypeProjection superArgument = superArguments.get(i);
|
||||||
|
if (superArgument.isStarProjection()) continue;
|
||||||
|
|
||||||
|
JetType superIn = getInType(parameter, superArgument);
|
||||||
|
JetType superOut = getOutType(parameter, superArgument);
|
||||||
|
|
||||||
TypeProjection subArgument = subArguments.get(i);
|
TypeProjection subArgument = subArguments.get(i);
|
||||||
JetType subIn = getInType(parameter, subArgument);
|
JetType subIn = getInType(parameter, subArgument);
|
||||||
JetType subOut = getOutType(parameter, subArgument);
|
JetType subOut = getOutType(parameter, subArgument);
|
||||||
|
|
||||||
TypeProjection superArgument = superArguments.get(i);
|
|
||||||
JetType superIn = getInType(parameter, superArgument);
|
|
||||||
JetType superOut = getOutType(parameter, superArgument);
|
|
||||||
|
|
||||||
if (capture(subArgument, superArgument, parameter)) continue;
|
if (capture(subArgument, superArgument, parameter)) continue;
|
||||||
|
|
||||||
boolean argumentIsErrorType = subArgument.getType().isError() || superArgument.getType().isError();
|
boolean argumentIsErrorType = subArgument.getType().isError() || superArgument.getType().isError();
|
||||||
|
|||||||
+1
-1
@@ -103,7 +103,7 @@ public class TypeDeserializer(
|
|||||||
protos.map { proto ->
|
protos.map { proto ->
|
||||||
val type = type(proto.getType())
|
val type = type(proto.getType())
|
||||||
if (proto.getProjection() == ProtoBuf.Type.Argument.Projection.STAR)
|
if (proto.getProjection() == ProtoBuf.Type.Argument.Projection.STAR)
|
||||||
StarProjectionImpl(type)
|
TypeBasedStarProjectionImpl(type)
|
||||||
else TypeProjectionImpl(variance(proto.getProjection()), type)
|
else TypeProjectionImpl(variance(proto.getProjection()), type)
|
||||||
}.toReadOnlyList()
|
}.toReadOnlyList()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user