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:
Andrey Breslav
2015-03-03 18:26:47 +03:00
parent 11b693c3be
commit e0b7e4efca
15 changed files with 127 additions and 17 deletions
@@ -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
}
+5 -1
View File
@@ -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();
@@ -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()