Prevent recursion on erasion of raw types with interdependent type parameters

^KT-47480 Fixed
This commit is contained in:
Victor Petukhov
2021-06-28 13:12:36 +03:00
committed by TeamCityServer
parent 0774d4d734
commit e071281b20
7 changed files with 85 additions and 27 deletions
@@ -21436,6 +21436,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
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
@TestMetadata("nonGenericRawMember.kt")
public void testNonGenericRawMember() throws Exception {
@@ -21436,6 +21436,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
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
@TestMetadata("nonGenericRawMember.kt")
public void testNonGenericRawMember() throws Exception {
@@ -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()
}
@@ -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<*>!>!>?)
}
@@ -21442,6 +21442,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
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
@TestMetadata("nonGenericRawMember.kt")
public void testNonGenericRawMember() throws Exception {
@@ -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.
* 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)
// 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 flexibility: JavaTypeFlexibility = INFLEXIBLE,
val isForAnnotationParameter: Boolean = false,
// Current type is upper bound of this type parameter
val upperBoundOfTypeParameter: TypeParameterDescriptor? = null
// we use it to prevent happening a recursion while compute type parameter's upper bounds
val visitedTypeParameters: Set<TypeParameterDescriptor>? = null
) {
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 {
@@ -328,7 +329,7 @@ fun TypeUsage.toAttributes(
) = JavaTypeAttributes(
this,
isForAnnotationParameter = isForAnnotationParameter,
upperBoundOfTypeParameter = upperBoundForTypeParameter
visitedTypeParameters = upperBoundForTypeParameter?.let(::setOf)
)
// Definition:
@@ -343,9 +344,9 @@ internal fun TypeParameterDescriptor.getErasedUpperBound(
typeAttr: JavaTypeAttributes,
defaultValue: (() -> KotlinType) = { ErrorUtils.createErrorType("Can't compute erased upper bound of type parameter `$this`") }
): 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.
@@ -353,13 +354,13 @@ internal fun TypeParameterDescriptor.getErasedUpperBound(
* 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<*>, *>`.
*/
val erasedUpperBounds = defaultType.extractTypeParametersFromUpperBounds(typeAttr.upperBoundOfTypeParameter).associate {
val boundProjection = if (it !== typeAttr.upperBoundOfTypeParameter) {
val erasedUpperBounds = defaultType.extractTypeParametersFromUpperBounds(visitedTypeParameters).associate {
val boundProjection = if (visitedTypeParameters == null || it !in visitedTypeParameters) {
RawSubstitution.computeProjection(
it,
// if erasure happens due to invalid arguments number, use star projections instead
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)
@@ -374,21 +375,21 @@ internal fun TypeParameterDescriptor.getErasedUpperBound(
erasedUpperBoundsSubstitutor,
erasedUpperBounds,
OUT_VARIANCE,
typeAttr.upperBoundOfTypeParameter
typeAttr.visitedTypeParameters
)
}
val stopAt = typeAttr.upperBoundOfTypeParameter ?: this
val stopAt = typeAttr.visitedTypeParameters ?: setOf(this)
var current = firstUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor
while (current != stopAt) {
while (current !in stopAt) {
val nextUpperBound = current.upperBounds.first()
if (nextUpperBound.constructor.declarationDescriptor is ClassDescriptor) {
return nextUpperBound.replaceArgumentsWithStarProjectionOrMapped(
erasedUpperBoundsSubstitutor,
erasedUpperBounds,
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.replaceArgumentsWithNothing() = replaceArgumentsWith { it.builtIns.nothingType.asTypeProjection() }
fun KotlinType.extractTypeParametersFromUpperBounds(upperBoundOfTypeParameter: TypeParameterDescriptor?): Set<TypeParameterDescriptor> =
mutableSetOf<TypeParameterDescriptor>().also { extractTypeParametersFromUpperBounds(this, it, upperBoundOfTypeParameter) }
fun KotlinType.extractTypeParametersFromUpperBounds(visitedTypeParameters: Set<TypeParameterDescriptor>?): Set<TypeParameterDescriptor> =
mutableSetOf<TypeParameterDescriptor>().also { extractTypeParametersFromUpperBounds(this, it, visitedTypeParameters) }
private fun KotlinType.extractTypeParametersFromUpperBounds(
baseType: KotlinType,
to: MutableSet<TypeParameterDescriptor>,
upperBoundOfTypeParameter: TypeParameterDescriptor?
visitedTypeParameters: Set<TypeParameterDescriptor>?
) {
val declarationDescriptor = constructor.declarationDescriptor
@@ -200,16 +200,17 @@ private fun KotlinType.extractTypeParametersFromUpperBounds(
to += declarationDescriptor
} else {
for (upperBound in declarationDescriptor.upperBounds) {
upperBound.extractTypeParametersFromUpperBounds(baseType, to, upperBoundOfTypeParameter)
upperBound.extractTypeParametersFromUpperBounds(baseType, to, visitedTypeParameters)
}
}
} else {
val typeParameters = (constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters)?.declaredTypeParameters
for ((i, argument) in arguments.withIndex()) {
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
argument.type.extractTypeParametersFromUpperBounds(baseType, to, upperBoundOfTypeParameter)
argument.type.extractTypeParametersFromUpperBounds(baseType, to, visitedTypeParameters)
}
}
}
@@ -217,24 +218,26 @@ private fun KotlinType.extractTypeParametersFromUpperBounds(
fun hasTypeParameterRecursiveBounds(
typeParameter: TypeParameterDescriptor,
selfConstructor: TypeConstructor? = null,
upperBoundOfTypeParameter: TypeParameterDescriptor? = null
visitedTypeParameters: Set<TypeParameterDescriptor>? = null
): Boolean =
typeParameter.upperBounds.any { upperBound ->
upperBound.containsSelfTypeParameter(typeParameter.defaultType.constructor, upperBoundOfTypeParameter)
upperBound.containsSelfTypeParameter(typeParameter.defaultType.constructor, visitedTypeParameters)
&& (selfConstructor == null || upperBound.constructor == selfConstructor)
}
private fun KotlinType.containsSelfTypeParameter(
baseConstructor: TypeConstructor,
upperBoundOfTypeParameter: TypeParameterDescriptor?
visitedTypeParameters: Set<TypeParameterDescriptor>?
): Boolean {
if (this.constructor == baseConstructor) return true
val typeParameters = (constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters)?.declaredTypeParameters
return arguments.withIndex().any { (i, argument) ->
val typeParameter = typeParameters?.getOrNull(i)
if ((typeParameter != null && typeParameter == upperBoundOfTypeParameter) || argument.isStarProjection) return@any false
argument.type.containsSelfTypeParameter(baseConstructor, upperBoundOfTypeParameter)
val isTypeParameterVisited =
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,
substitutionMap: Map<TypeConstructor, TypeProjection>,
variance: Variance,
upperBoundOfTypeParameter: TypeParameterDescriptor?
visitedTypeParameters: Set<TypeParameterDescriptor>?
) =
replaceArgumentsWith { typeParameterDescriptor ->
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
} else StarProjectionImpl(typeParameterDescriptor)
}.let { substitutor.safeSubstitute(it, variance) }