Fix captured approximation for case of flexible types
#KT-9294 Fixed
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// !CHECK_TYPE
|
||||||
|
|
||||||
|
// FILE: Clazz.java
|
||||||
|
public class Clazz<T> {
|
||||||
|
public T getT() { return null; }
|
||||||
|
public Clazz<? super T> getSuperClass() { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
fun test(clazz: Clazz<*>) {
|
||||||
|
clazz.t checkType { _<Any?>() }
|
||||||
|
clazz.getSuperClass() checkType { _<Clazz<*>?>() }
|
||||||
|
clazz.getSuperClass().t checkType { _<Any?>() }
|
||||||
|
|
||||||
|
clazz.superClass checkType { _<Clazz<*>?>() }
|
||||||
|
clazz.superClass.t checkType { _<Any?>() }
|
||||||
|
|
||||||
|
// See KT-9294
|
||||||
|
if (clazz.superClass == null) {
|
||||||
|
throw NullPointerException()
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun test(/*0*/ clazz: Clazz<*>): kotlin.Unit
|
||||||
|
|
||||||
|
public open class Clazz</*0*/ T : kotlin.Any!> {
|
||||||
|
public constructor Clazz</*0*/ T : kotlin.Any!>()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open fun getSuperClass(): Clazz<in T!>!
|
||||||
|
public open fun getT(): T!
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
+2
-4
@@ -16,14 +16,12 @@ fun test1(clazz: Class<out Int>) {
|
|||||||
val foo0: Class<out Int> = A.foo(clazz)
|
val foo0: Class<out Int> = A.foo(clazz)
|
||||||
val foo1 = A.foo(clazz)
|
val foo1 = A.foo(clazz)
|
||||||
foo1 checkType { _< Class<out Int> >() }
|
foo1 checkType { _< Class<out Int> >() }
|
||||||
// should be ok
|
foo1 checkType { _< Class<out Int?> >() }
|
||||||
foo1 checkType { <!TYPE_MISMATCH!>_<!>< Class<out Int?> >() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tes2t(clazz: Class<in Int>) {
|
fun tes2t(clazz: Class<in Int>) {
|
||||||
val foo0: Class<out Class<in Int>> = A.bar(clazz)
|
val foo0: Class<out Class<in Int>> = A.bar(clazz)
|
||||||
val foo1 = A.bar(clazz)
|
val foo1 = A.bar(clazz)
|
||||||
foo1 checkType { _< Class<out Class<in Int>> >() }
|
foo1 checkType { _< Class<out Class<in Int>> >() }
|
||||||
// should be ok
|
foo1 checkType { _< Class<out Class<in Int?>> >() }
|
||||||
foo1 checkType { <!TYPE_MISMATCH!>_<!>< Class<out Class<in Int?>> >() }
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package
|
package
|
||||||
|
|
||||||
public fun foo1(/*0*/ x: A<*>): kotlin.Array<out A<out kotlin.Any!>>
|
public fun foo1(/*0*/ x: A<*>): kotlin.Array<out A<out kotlin.Any!>!>!
|
||||||
public fun foo2(/*0*/ x: A<*>): kotlin.Unit
|
public fun foo2(/*0*/ x: A<*>): kotlin.Unit
|
||||||
|
|
||||||
public open class A</*0*/ T : kotlin.Any!> {
|
public open class A</*0*/ T : kotlin.Any!> {
|
||||||
|
|||||||
@@ -7160,6 +7160,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("platformSuperClass.kt")
|
||||||
|
public void testPlatformSuperClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/platformSuperClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("recursiveUpperBoundStar.kt")
|
@TestMetadata("recursiveUpperBoundStar.kt")
|
||||||
public void testRecursiveUpperBoundStar() throws Exception {
|
public void testRecursiveUpperBoundStar() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/projectionsScope/recursiveUpperBoundStar.kt");
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ private fun TypeArgument.toTypeProjection(): TypeProjection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun TypeProjection.toTypeArgument(typeParameter: TypeParameterDescriptor) =
|
private fun TypeProjection.toTypeArgument(typeParameter: TypeParameterDescriptor) =
|
||||||
when (TypeSubstitutor.combine(typeParameter.variance, projectionKind)) {
|
when (TypeSubstitutor.combine(typeParameter.variance, this)) {
|
||||||
Variance.INVARIANT -> TypeArgument(typeParameter, type, type)
|
Variance.INVARIANT -> TypeArgument(typeParameter, type, type)
|
||||||
Variance.IN_VARIANCE -> TypeArgument(typeParameter, type, typeParameter.builtIns.nullableAnyType)
|
Variance.IN_VARIANCE -> TypeArgument(typeParameter, type, typeParameter.builtIns.nullableAnyType)
|
||||||
Variance.OUT_VARIANCE -> TypeArgument(typeParameter, typeParameter.builtIns.nothingType, type)
|
Variance.OUT_VARIANCE -> TypeArgument(typeParameter, typeParameter.builtIns.nothingType, type)
|
||||||
@@ -93,10 +93,21 @@ private fun substituteCapturedTypesWithProjections(typeProjection: TypeProjectio
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fun approximateCapturedTypes(type: KotlinType): ApproximationBounds<KotlinType> {
|
public fun approximateCapturedTypes(type: KotlinType): ApproximationBounds<KotlinType> {
|
||||||
|
if (type.isFlexible()) {
|
||||||
|
val boundsForFlexibleLower = approximateCapturedTypes(type.lowerIfFlexible())
|
||||||
|
val boundsForFlexibleUpper = approximateCapturedTypes(type.upperIfFlexible())
|
||||||
|
|
||||||
|
val extraCapabilities = type.flexibility().extraCapabilities
|
||||||
|
return ApproximationBounds(
|
||||||
|
DelegatingFlexibleType.create(
|
||||||
|
boundsForFlexibleLower.lower.lowerIfFlexible(), boundsForFlexibleUpper.lower.upperIfFlexible(), extraCapabilities),
|
||||||
|
DelegatingFlexibleType.create(
|
||||||
|
boundsForFlexibleLower.upper.lowerIfFlexible(), boundsForFlexibleUpper.upper.upperIfFlexible(), extraCapabilities))
|
||||||
|
}
|
||||||
|
|
||||||
val typeConstructor = type.constructor
|
val typeConstructor = type.constructor
|
||||||
if (type.isCaptured()) {
|
if (type.isCaptured()) {
|
||||||
val typeProjection = (typeConstructor as CapturedTypeConstructor).typeProjection
|
val typeProjection = (typeConstructor as CapturedTypeConstructor).typeProjection
|
||||||
// todo: preserve flexibility as well
|
|
||||||
fun KotlinType.makeNullableIfNeeded() = TypeUtils.makeNullableIfNeeded(this, type.isMarkedNullable)
|
fun KotlinType.makeNullableIfNeeded() = TypeUtils.makeNullableIfNeeded(this, type.isMarkedNullable)
|
||||||
val bound = typeProjection.type.makeNullableIfNeeded()
|
val bound = typeProjection.type.makeNullableIfNeeded()
|
||||||
|
|
||||||
@@ -112,9 +123,18 @@ public fun approximateCapturedTypes(type: KotlinType): ApproximationBounds<Kotli
|
|||||||
val lowerBoundArguments = ArrayList<TypeArgument>()
|
val lowerBoundArguments = ArrayList<TypeArgument>()
|
||||||
val upperBoundArguments = ArrayList<TypeArgument>()
|
val upperBoundArguments = ArrayList<TypeArgument>()
|
||||||
for ((typeProjection, typeParameter) in type.arguments.zip(typeConstructor.parameters)) {
|
for ((typeProjection, typeParameter) in type.arguments.zip(typeConstructor.parameters)) {
|
||||||
val (lower, upper) = approximateProjection(typeProjection.toTypeArgument(typeParameter))
|
val typeArgument = typeProjection.toTypeArgument(typeParameter)
|
||||||
lowerBoundArguments.add(lower)
|
|
||||||
upperBoundArguments.add(upper)
|
// Protection from infinite recursion caused by star projection
|
||||||
|
if (typeProjection.isStarProjection) {
|
||||||
|
lowerBoundArguments.add(typeArgument)
|
||||||
|
upperBoundArguments.add(typeArgument)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val (lower, upper) = approximateProjection(typeArgument)
|
||||||
|
lowerBoundArguments.add(lower)
|
||||||
|
upperBoundArguments.add(upper)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val lowerBoundIsTrivial = lowerBoundArguments.any { !it.isConsistent }
|
val lowerBoundIsTrivial = lowerBoundArguments.any { !it.isConsistent }
|
||||||
return ApproximationBounds(
|
return ApproximationBounds(
|
||||||
|
|||||||
@@ -260,6 +260,13 @@ public class TypeSubstitutor {
|
|||||||
return substitutedArguments;
|
return substitutedArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull TypeProjection typeProjection) {
|
||||||
|
if (typeProjection.isStarProjection()) return Variance.OUT_VARIANCE;
|
||||||
|
|
||||||
|
return combine(typeParameterVariance, typeProjection.getProjectionKind());
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull Variance projectionKind) {
|
public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull Variance projectionKind) {
|
||||||
if (typeParameterVariance == Variance.INVARIANT) return projectionKind;
|
if (typeParameterVariance == Variance.INVARIANT) return projectionKind;
|
||||||
|
|||||||
Reference in New Issue
Block a user