diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 133cac855f3..8fe0d74fe99 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -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 { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 8c5307a904b..c77afd4b112 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -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 { diff --git a/compiler/testData/diagnostics/tests/platformTypes/rawTypes/intermediateRecursion.kt b/compiler/testData/diagnostics/tests/platformTypes/rawTypes/intermediateRecursion.kt new file mode 100644 index 00000000000..0e4ff4e3f1d --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/rawTypes/intermediateRecursion.kt @@ -0,0 +1,14 @@ +// FIR_IDENTICAL +// FILE: Boo.java +public class Boo {} + +// FILE: Foo.java +public class Foo, K extends Boo, X extends Boo> { + T test2() { return null; } + static Foo test1() { return null; } +} + +// FILE: main.kt +fun main() { + val x = Foo.test1().test2() +} diff --git a/compiler/testData/diagnostics/tests/platformTypes/rawTypes/intermediateRecursion.txt b/compiler/testData/diagnostics/tests/platformTypes/rawTypes/intermediateRecursion.txt new file mode 100644 index 00000000000..39903fcab0b --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/rawTypes/intermediateRecursion.txt @@ -0,0 +1,21 @@ +package + +public fun main(): kotlin.Unit + +public open class Boo { + public constructor Boo() + 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!, /*1*/ K : Boo!, /*2*/ X : Boo!> { + public constructor Foo!, /*1*/ K : Boo!, /*2*/ X : Boo!>() + 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!>!>!, (raw) Boo!>!, (raw) Boo!>!>..Foo!>!>!, out Boo!>!, out Boo!>!>?) +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 5739530b18d..e6c41017884 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -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 { diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt index 5662c4729c2..0963a1d66aa 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt @@ -213,7 +213,7 @@ class JavaTypeResolver( * but it's wrong because Foo<*> isn't subtype of Foo> in accordance with declared upper bound of Foo. * So we should create Foo<*> in this case (CapturedType(*) is really subtype of Foo). */ - 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` @@ -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? = 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, Any>` or `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 ) } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index 037180ce0a5..18a8042082c 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -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 = - mutableSetOf().also { extractTypeParametersFromUpperBounds(this, it, upperBoundOfTypeParameter) } +fun KotlinType.extractTypeParametersFromUpperBounds(visitedTypeParameters: Set?): Set = + mutableSetOf().also { extractTypeParametersFromUpperBounds(this, it, visitedTypeParameters) } private fun KotlinType.extractTypeParametersFromUpperBounds( baseType: KotlinType, to: MutableSet, - upperBoundOfTypeParameter: TypeParameterDescriptor? + visitedTypeParameters: Set? ) { 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? = 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? ): 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, variance: Variance, - upperBoundOfTypeParameter: TypeParameterDescriptor? + visitedTypeParameters: Set? ) = 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) }