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!>x<!> : C<in 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!>bx<!> : C<in 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>
|
||||
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);
|
||||
}
|
||||
|
||||
@TestMetadata("starProjections.kt")
|
||||
public void testStarProjections() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/substitutions/starProjections.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("upperBoundsSubstitutionForOverloadResolutionWithAmbiguity.kt")
|
||||
public void testUpperBoundsSubstitutionForOverloadResolutionWithAmbiguity() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/substitutions/upperBoundsSubstitutionForOverloadResolutionWithAmbiguity.kt");
|
||||
|
||||
@@ -328,6 +328,23 @@ public class JetTypeCheckerTest extends JetLiteFixture {
|
||||
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 {
|
||||
assertType("Derived_T<Int>", "this", "Derived_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>() {
|
||||
@Override
|
||||
public JetType invoke(TypeProjection projection) {
|
||||
if (projection.isStarProjection()) {
|
||||
// any type is good enough for depth here
|
||||
return KotlinBuiltIns.getInstance().getAnyType();
|
||||
}
|
||||
return projection.getType();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -377,7 +377,7 @@ public class ErrorUtils {
|
||||
if (type == null) return false;
|
||||
if (type.isError()) return true;
|
||||
for (TypeProjection projection : type.getArguments()) {
|
||||
if (containsErrorType(projection.getType())) return true;
|
||||
if (!projection.isStarProjection() && containsErrorType(projection.getType())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,35 @@
|
||||
|
||||
package org.jetbrains.kotlin.types
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import kotlin.properties.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
|
||||
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
|
||||
) : TypeProjectionBase() {
|
||||
override fun isStarProjection() = true
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class TypeProjectionBase implements TypeProjection {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = getProjectionKind().hashCode();
|
||||
result = 31 * result + (getType().hashCode());
|
||||
result = 31 * result + (isStarProjection() ? 17 : getType().hashCode());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,9 @@ public class TypeSubstitutor {
|
||||
if (typeVariable != null) {
|
||||
substitutedType = typeVariable.substitutionResult(replacement.getType());
|
||||
}
|
||||
else if (replacement.isStarProjection()) {
|
||||
return replacement;
|
||||
}
|
||||
else {
|
||||
// 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());
|
||||
|
||||
@@ -549,7 +549,7 @@ public class TypeUtils {
|
||||
public static boolean dependsOnTypeConstructors(@NotNull JetType type, @NotNull Collection<TypeConstructor> typeParameterConstructors) {
|
||||
if (typeParameterConstructors.contains(type.getConstructor())) return true;
|
||||
for (TypeProjection typeProjection : type.getArguments()) {
|
||||
if (dependsOnTypeConstructors(typeProjection.getType(), typeParameterConstructors)) {
|
||||
if (!typeProjection.isStarProjection() && dependsOnTypeConstructors(typeProjection.getType(), typeParameterConstructors)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -577,14 +577,14 @@ public class TypeUtils {
|
||||
return true;
|
||||
}
|
||||
for (TypeProjection projection : type.getArguments()) {
|
||||
if (containsSpecialType(projection.getType(), isSpecialType)) return true;
|
||||
if (!projection.isStarProjection() && containsSpecialType(projection.getType(), isSpecialType)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor) {
|
||||
return new StarProjectionImpl(parameterDescriptor.getUpperBoundsAsType());
|
||||
return new StarProjectionImpl(parameterDescriptor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.types.DelegatingType
|
||||
|
||||
fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||
private fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||
val declarationDescriptor = getConstructor().getDeclarationDescriptor()
|
||||
if (declarationDescriptor is TypeParameterDescriptor) return listOf(declarationDescriptor)
|
||||
|
||||
@@ -41,7 +41,7 @@ fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||
if (flexibility != null) {
|
||||
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> {
|
||||
|
||||
@@ -106,6 +106,10 @@ public class TypeCheckingProcedure {
|
||||
TypeProjection typeProjection1 = type1Arguments.get(i);
|
||||
TypeParameterDescriptor typeParameter2 = constructor2.getParameters().get(i);
|
||||
TypeProjection typeProjection2 = type2Arguments.get(i);
|
||||
if (typeProjection1.isStarProjection() && typeProjection2.isStarProjection()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (capture(typeProjection1, typeProjection2, typeParameter1)) {
|
||||
continue;
|
||||
}
|
||||
@@ -227,14 +231,16 @@ public class TypeCheckingProcedure {
|
||||
for (int i = 0; i < parameters.size(); 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);
|
||||
JetType subIn = getInType(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;
|
||||
|
||||
boolean argumentIsErrorType = subArgument.getType().isError() || superArgument.getType().isError();
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ public class TypeDeserializer(
|
||||
protos.map { proto ->
|
||||
val type = type(proto.getType())
|
||||
if (proto.getProjection() == ProtoBuf.Type.Argument.Projection.STAR)
|
||||
StarProjectionImpl(type)
|
||||
TypeBasedStarProjectionImpl(type)
|
||||
else TypeProjectionImpl(variance(proto.getProjection()), type)
|
||||
}.toReadOnlyList()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user