Prevent recursion on erasion of raw types with interdependent type parameters
^KT-47480 Fixed
This commit is contained in:
committed by
TeamCityServer
parent
0774d4d734
commit
e071281b20
+6
@@ -21436,6 +21436,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/interClassesRecursion.kt");
|
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/interClassesRecursion.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("intermediateRecursion.kt")
|
||||||
|
public void testIntermediateRecursion() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/intermediateRecursion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("nonGenericRawMember.kt")
|
@TestMetadata("nonGenericRawMember.kt")
|
||||||
public void testNonGenericRawMember() throws Exception {
|
public void testNonGenericRawMember() throws Exception {
|
||||||
|
|||||||
+6
@@ -21436,6 +21436,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/interClassesRecursion.kt");
|
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/interClassesRecursion.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("intermediateRecursion.kt")
|
||||||
|
public void testIntermediateRecursion() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/intermediateRecursion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("nonGenericRawMember.kt")
|
@TestMetadata("nonGenericRawMember.kt")
|
||||||
public void testNonGenericRawMember() throws Exception {
|
public void testNonGenericRawMember() throws Exception {
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
// FILE: Boo.java
|
||||||
|
public class Boo<N> {}
|
||||||
|
|
||||||
|
// FILE: Foo.java
|
||||||
|
public class Foo<T extends Boo<K>, K extends Boo<X>, X extends Boo<K>> {
|
||||||
|
T test2() { return null; }
|
||||||
|
static Foo test1() { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
fun main() {
|
||||||
|
val x = Foo.test1().test2()
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun main(): kotlin.Unit
|
||||||
|
|
||||||
|
public open class Boo</*0*/ N : kotlin.Any!> {
|
||||||
|
public constructor Boo</*0*/ N : kotlin.Any!>()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class Foo</*0*/ T : Boo<K!>!, /*1*/ K : Boo<X!>!, /*2*/ X : Boo<K!>!> {
|
||||||
|
public constructor Foo</*0*/ T : Boo<K!>!, /*1*/ K : Boo<X!>!, /*2*/ X : Boo<K!>!>()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public/*package*/ open fun test2(): T!
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
|
||||||
|
// Static members
|
||||||
|
public/*package*/ open fun test1(): (Foo<(raw) Boo<Boo<Boo<*>!>!>!, (raw) Boo<Boo<*>!>!, (raw) Boo<Boo<*>!>!>..Foo<out Boo<out Boo<out Boo<*>!>!>!, out Boo<out Boo<*>!>!, out Boo<out Boo<*>!>!>?)
|
||||||
|
}
|
||||||
Generated
+6
@@ -21442,6 +21442,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/interClassesRecursion.kt");
|
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/interClassesRecursion.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("intermediateRecursion.kt")
|
||||||
|
public void testIntermediateRecursion() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/intermediateRecursion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("nonGenericRawMember.kt")
|
@TestMetadata("nonGenericRawMember.kt")
|
||||||
public void testNonGenericRawMember() throws Exception {
|
public void testNonGenericRawMember() throws Exception {
|
||||||
|
|||||||
+15
-14
@@ -213,7 +213,7 @@ class JavaTypeResolver(
|
|||||||
* but it's wrong because Foo<*> isn't subtype of Foo<Foo<*>> in accordance with declared upper bound of Foo.
|
* but it's wrong because Foo<*> isn't subtype of Foo<Foo<*>> in accordance with declared upper bound of Foo.
|
||||||
* So we should create Foo<*> in this case (CapturedType(*) is really subtype of Foo<CapturedType(*)>).
|
* So we should create Foo<*> in this case (CapturedType(*) is really subtype of Foo<CapturedType(*)>).
|
||||||
*/
|
*/
|
||||||
if (hasTypeParameterRecursiveBounds(parameter, selfConstructor = null, attr.upperBoundOfTypeParameter))
|
if (hasTypeParameterRecursiveBounds(parameter, selfConstructor = null, attr.visitedTypeParameters))
|
||||||
return@map StarProjectionImpl(parameter)
|
return@map StarProjectionImpl(parameter)
|
||||||
|
|
||||||
// Some activity for preventing recursion in cases like `class A<T extends A, F extends T>`
|
// Some activity for preventing recursion in cases like `class A<T extends A, F extends T>`
|
||||||
@@ -309,11 +309,12 @@ data class JavaTypeAttributes(
|
|||||||
val howThisTypeIsUsed: TypeUsage,
|
val howThisTypeIsUsed: TypeUsage,
|
||||||
val flexibility: JavaTypeFlexibility = INFLEXIBLE,
|
val flexibility: JavaTypeFlexibility = INFLEXIBLE,
|
||||||
val isForAnnotationParameter: Boolean = false,
|
val isForAnnotationParameter: Boolean = false,
|
||||||
// Current type is upper bound of this type parameter
|
// we use it to prevent happening a recursion while compute type parameter's upper bounds
|
||||||
val upperBoundOfTypeParameter: TypeParameterDescriptor? = null
|
val visitedTypeParameters: Set<TypeParameterDescriptor>? = null
|
||||||
) {
|
) {
|
||||||
fun withFlexibility(flexibility: JavaTypeFlexibility) = copy(flexibility = flexibility)
|
fun withFlexibility(flexibility: JavaTypeFlexibility) = copy(flexibility = flexibility)
|
||||||
fun withTypeParameter(upperBoundOfTypeParameter: TypeParameterDescriptor) = copy(upperBoundOfTypeParameter = upperBoundOfTypeParameter)
|
fun withNewVisitedTypeParameter(typeParameter: TypeParameterDescriptor) =
|
||||||
|
copy(visitedTypeParameters = if (visitedTypeParameters != null) visitedTypeParameters + typeParameter else setOf(typeParameter))
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class JavaTypeFlexibility {
|
enum class JavaTypeFlexibility {
|
||||||
@@ -328,7 +329,7 @@ fun TypeUsage.toAttributes(
|
|||||||
) = JavaTypeAttributes(
|
) = JavaTypeAttributes(
|
||||||
this,
|
this,
|
||||||
isForAnnotationParameter = isForAnnotationParameter,
|
isForAnnotationParameter = isForAnnotationParameter,
|
||||||
upperBoundOfTypeParameter = upperBoundForTypeParameter
|
visitedTypeParameters = upperBoundForTypeParameter?.let(::setOf)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Definition:
|
// Definition:
|
||||||
@@ -343,9 +344,9 @@ internal fun TypeParameterDescriptor.getErasedUpperBound(
|
|||||||
typeAttr: JavaTypeAttributes,
|
typeAttr: JavaTypeAttributes,
|
||||||
defaultValue: (() -> KotlinType) = { ErrorUtils.createErrorType("Can't compute erased upper bound of type parameter `$this`") }
|
defaultValue: (() -> KotlinType) = { ErrorUtils.createErrorType("Can't compute erased upper bound of type parameter `$this`") }
|
||||||
): KotlinType {
|
): KotlinType {
|
||||||
if (this === typeAttr.upperBoundOfTypeParameter) return defaultValue()
|
val visitedTypeParameters = typeAttr.visitedTypeParameters
|
||||||
|
|
||||||
val newTypeAttr = if (typeAttr.upperBoundOfTypeParameter == null) typeAttr.withTypeParameter(this) else typeAttr
|
if (visitedTypeParameters != null && this in visitedTypeParameters) return defaultValue()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We should do erasure of containing type parameters with their erasure to avoid creating inconsistent types.
|
* We should do erasure of containing type parameters with their erasure to avoid creating inconsistent types.
|
||||||
@@ -353,13 +354,13 @@ internal fun TypeParameterDescriptor.getErasedUpperBound(
|
|||||||
* but it's wrong type: projection(*) != projection(Any).
|
* but it's wrong type: projection(*) != projection(Any).
|
||||||
* So we should substitute erasure of the corresponding type parameter: `Foo<Foo<Any>, Any>` or `Foo<Foo<*>, *>`.
|
* So we should substitute erasure of the corresponding type parameter: `Foo<Foo<Any>, Any>` or `Foo<Foo<*>, *>`.
|
||||||
*/
|
*/
|
||||||
val erasedUpperBounds = defaultType.extractTypeParametersFromUpperBounds(typeAttr.upperBoundOfTypeParameter).associate {
|
val erasedUpperBounds = defaultType.extractTypeParametersFromUpperBounds(visitedTypeParameters).associate {
|
||||||
val boundProjection = if (it !== typeAttr.upperBoundOfTypeParameter) {
|
val boundProjection = if (visitedTypeParameters == null || it !in visitedTypeParameters) {
|
||||||
RawSubstitution.computeProjection(
|
RawSubstitution.computeProjection(
|
||||||
it,
|
it,
|
||||||
// if erasure happens due to invalid arguments number, use star projections instead
|
// if erasure happens due to invalid arguments number, use star projections instead
|
||||||
if (isRaw) typeAttr else typeAttr.withFlexibility(INFLEXIBLE),
|
if (isRaw) typeAttr else typeAttr.withFlexibility(INFLEXIBLE),
|
||||||
if (it !== typeAttr.upperBoundOfTypeParameter) it.getErasedUpperBound(isRaw, newTypeAttr) else it.starProjectionType()
|
it.getErasedUpperBound(isRaw, typeAttr.withNewVisitedTypeParameter(this))
|
||||||
)
|
)
|
||||||
} else makeStarProjection(it, typeAttr)
|
} else makeStarProjection(it, typeAttr)
|
||||||
|
|
||||||
@@ -374,21 +375,21 @@ internal fun TypeParameterDescriptor.getErasedUpperBound(
|
|||||||
erasedUpperBoundsSubstitutor,
|
erasedUpperBoundsSubstitutor,
|
||||||
erasedUpperBounds,
|
erasedUpperBounds,
|
||||||
OUT_VARIANCE,
|
OUT_VARIANCE,
|
||||||
typeAttr.upperBoundOfTypeParameter
|
typeAttr.visitedTypeParameters
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val stopAt = typeAttr.upperBoundOfTypeParameter ?: this
|
val stopAt = typeAttr.visitedTypeParameters ?: setOf(this)
|
||||||
var current = firstUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor
|
var current = firstUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor
|
||||||
|
|
||||||
while (current != stopAt) {
|
while (current !in stopAt) {
|
||||||
val nextUpperBound = current.upperBounds.first()
|
val nextUpperBound = current.upperBounds.first()
|
||||||
if (nextUpperBound.constructor.declarationDescriptor is ClassDescriptor) {
|
if (nextUpperBound.constructor.declarationDescriptor is ClassDescriptor) {
|
||||||
return nextUpperBound.replaceArgumentsWithStarProjectionOrMapped(
|
return nextUpperBound.replaceArgumentsWithStarProjectionOrMapped(
|
||||||
erasedUpperBoundsSubstitutor,
|
erasedUpperBoundsSubstitutor,
|
||||||
erasedUpperBounds,
|
erasedUpperBounds,
|
||||||
OUT_VARIANCE,
|
OUT_VARIANCE,
|
||||||
typeAttr.upperBoundOfTypeParameter
|
typeAttr.visitedTypeParameters
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,13 +185,13 @@ fun KotlinType.contains(predicate: (UnwrappedType) -> Boolean) = TypeUtils.conta
|
|||||||
fun KotlinType.replaceArgumentsWithStarProjections() = replaceArgumentsWith(::StarProjectionImpl)
|
fun KotlinType.replaceArgumentsWithStarProjections() = replaceArgumentsWith(::StarProjectionImpl)
|
||||||
fun KotlinType.replaceArgumentsWithNothing() = replaceArgumentsWith { it.builtIns.nothingType.asTypeProjection() }
|
fun KotlinType.replaceArgumentsWithNothing() = replaceArgumentsWith { it.builtIns.nothingType.asTypeProjection() }
|
||||||
|
|
||||||
fun KotlinType.extractTypeParametersFromUpperBounds(upperBoundOfTypeParameter: TypeParameterDescriptor?): Set<TypeParameterDescriptor> =
|
fun KotlinType.extractTypeParametersFromUpperBounds(visitedTypeParameters: Set<TypeParameterDescriptor>?): Set<TypeParameterDescriptor> =
|
||||||
mutableSetOf<TypeParameterDescriptor>().also { extractTypeParametersFromUpperBounds(this, it, upperBoundOfTypeParameter) }
|
mutableSetOf<TypeParameterDescriptor>().also { extractTypeParametersFromUpperBounds(this, it, visitedTypeParameters) }
|
||||||
|
|
||||||
private fun KotlinType.extractTypeParametersFromUpperBounds(
|
private fun KotlinType.extractTypeParametersFromUpperBounds(
|
||||||
baseType: KotlinType,
|
baseType: KotlinType,
|
||||||
to: MutableSet<TypeParameterDescriptor>,
|
to: MutableSet<TypeParameterDescriptor>,
|
||||||
upperBoundOfTypeParameter: TypeParameterDescriptor?
|
visitedTypeParameters: Set<TypeParameterDescriptor>?
|
||||||
) {
|
) {
|
||||||
val declarationDescriptor = constructor.declarationDescriptor
|
val declarationDescriptor = constructor.declarationDescriptor
|
||||||
|
|
||||||
@@ -200,16 +200,17 @@ private fun KotlinType.extractTypeParametersFromUpperBounds(
|
|||||||
to += declarationDescriptor
|
to += declarationDescriptor
|
||||||
} else {
|
} else {
|
||||||
for (upperBound in declarationDescriptor.upperBounds) {
|
for (upperBound in declarationDescriptor.upperBounds) {
|
||||||
upperBound.extractTypeParametersFromUpperBounds(baseType, to, upperBoundOfTypeParameter)
|
upperBound.extractTypeParametersFromUpperBounds(baseType, to, visitedTypeParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val typeParameters = (constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters)?.declaredTypeParameters
|
val typeParameters = (constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters)?.declaredTypeParameters
|
||||||
for ((i, argument) in arguments.withIndex()) {
|
for ((i, argument) in arguments.withIndex()) {
|
||||||
val typeParameter = typeParameters?.get(i)
|
val typeParameter = typeParameters?.get(i)
|
||||||
if (argument.isStarProjection || (typeParameter != null && typeParameter == upperBoundOfTypeParameter)) continue
|
val isTypeParameterVisited = typeParameter != null && visitedTypeParameters != null && typeParameter in visitedTypeParameters
|
||||||
|
if (isTypeParameterVisited || argument.isStarProjection) continue
|
||||||
if (argument.type.constructor.declarationDescriptor in to || argument.type.constructor == baseType.constructor) continue
|
if (argument.type.constructor.declarationDescriptor in to || argument.type.constructor == baseType.constructor) continue
|
||||||
argument.type.extractTypeParametersFromUpperBounds(baseType, to, upperBoundOfTypeParameter)
|
argument.type.extractTypeParametersFromUpperBounds(baseType, to, visitedTypeParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,24 +218,26 @@ private fun KotlinType.extractTypeParametersFromUpperBounds(
|
|||||||
fun hasTypeParameterRecursiveBounds(
|
fun hasTypeParameterRecursiveBounds(
|
||||||
typeParameter: TypeParameterDescriptor,
|
typeParameter: TypeParameterDescriptor,
|
||||||
selfConstructor: TypeConstructor? = null,
|
selfConstructor: TypeConstructor? = null,
|
||||||
upperBoundOfTypeParameter: TypeParameterDescriptor? = null
|
visitedTypeParameters: Set<TypeParameterDescriptor>? = null
|
||||||
): Boolean =
|
): Boolean =
|
||||||
typeParameter.upperBounds.any { upperBound ->
|
typeParameter.upperBounds.any { upperBound ->
|
||||||
upperBound.containsSelfTypeParameter(typeParameter.defaultType.constructor, upperBoundOfTypeParameter)
|
upperBound.containsSelfTypeParameter(typeParameter.defaultType.constructor, visitedTypeParameters)
|
||||||
&& (selfConstructor == null || upperBound.constructor == selfConstructor)
|
&& (selfConstructor == null || upperBound.constructor == selfConstructor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KotlinType.containsSelfTypeParameter(
|
private fun KotlinType.containsSelfTypeParameter(
|
||||||
baseConstructor: TypeConstructor,
|
baseConstructor: TypeConstructor,
|
||||||
upperBoundOfTypeParameter: TypeParameterDescriptor?
|
visitedTypeParameters: Set<TypeParameterDescriptor>?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (this.constructor == baseConstructor) return true
|
if (this.constructor == baseConstructor) return true
|
||||||
|
|
||||||
val typeParameters = (constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters)?.declaredTypeParameters
|
val typeParameters = (constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters)?.declaredTypeParameters
|
||||||
return arguments.withIndex().any { (i, argument) ->
|
return arguments.withIndex().any { (i, argument) ->
|
||||||
val typeParameter = typeParameters?.getOrNull(i)
|
val typeParameter = typeParameters?.getOrNull(i)
|
||||||
if ((typeParameter != null && typeParameter == upperBoundOfTypeParameter) || argument.isStarProjection) return@any false
|
val isTypeParameterVisited =
|
||||||
argument.type.containsSelfTypeParameter(baseConstructor, upperBoundOfTypeParameter)
|
typeParameter != null && visitedTypeParameters != null && typeParameter in visitedTypeParameters
|
||||||
|
if (isTypeParameterVisited || argument.isStarProjection) return@any false
|
||||||
|
argument.type.containsSelfTypeParameter(baseConstructor, visitedTypeParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,11 +245,12 @@ fun KotlinType.replaceArgumentsWithStarProjectionOrMapped(
|
|||||||
substitutor: TypeSubstitutor,
|
substitutor: TypeSubstitutor,
|
||||||
substitutionMap: Map<TypeConstructor, TypeProjection>,
|
substitutionMap: Map<TypeConstructor, TypeProjection>,
|
||||||
variance: Variance,
|
variance: Variance,
|
||||||
upperBoundOfTypeParameter: TypeParameterDescriptor?
|
visitedTypeParameters: Set<TypeParameterDescriptor>?
|
||||||
) =
|
) =
|
||||||
replaceArgumentsWith { typeParameterDescriptor ->
|
replaceArgumentsWith { typeParameterDescriptor ->
|
||||||
val argument = arguments.getOrNull(typeParameterDescriptor.index)
|
val argument = arguments.getOrNull(typeParameterDescriptor.index)
|
||||||
if (typeParameterDescriptor != upperBoundOfTypeParameter && argument != null && argument.type.constructor in substitutionMap) {
|
val isTypeParameterVisited = visitedTypeParameters != null && typeParameterDescriptor in visitedTypeParameters
|
||||||
|
if (!isTypeParameterVisited && argument != null && argument.type.constructor in substitutionMap) {
|
||||||
argument
|
argument
|
||||||
} else StarProjectionImpl(typeParameterDescriptor)
|
} else StarProjectionImpl(typeParameterDescriptor)
|
||||||
}.let { substitutor.safeSubstitute(it, variance) }
|
}.let { substitutor.safeSubstitute(it, variance) }
|
||||||
|
|||||||
Reference in New Issue
Block a user