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:
@@ -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