diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java index 3d0df71383b..3c1a7600268 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java @@ -6699,6 +6699,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/capturedTypeInEquality.kt"); } + @Test + @TestMetadata("classCastWithRawKotlinType.kt") + public void testClassCastWithRawKotlinType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/classCastWithRawKotlinType.kt"); + } + @Test @TestMetadata("DeepCopyIrTree.kt") public void testDeepCopyIrTree() throws Exception { diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/classCastWithRawKotlinType.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/classCastWithRawKotlinType.fir.txt new file mode 100644 index 00000000000..8040271ce33 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/classCastWithRawKotlinType.fir.txt @@ -0,0 +1,18 @@ +FILE: test.kt + public abstract interface Inter : R|kotlin/Any| { + } + public open class Apple : R|Inter| { + public constructor(): R|Apple| { + super() + } + + } + public final class XXX : R|Usage| { + public constructor(): R|XXX| { + super() + } + + } + public final fun main(): R|kotlin/Unit| { + R|kotlin/io/println|(R|/XXX.XXX|()) + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/classCastWithRawKotlinType.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/classCastWithRawKotlinType.kt new file mode 100644 index 00000000000..92591e938c1 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/classCastWithRawKotlinType.kt @@ -0,0 +1,16 @@ +// ISSUE: KT-56630 +// FILE: Usage.java + +public class Usage extends Apple { + +} + +// FILE: test.kt + +interface Inter +open class Apple : Inter +class XXX : Usage() + +fun main() { + println(XXX()) +} diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java index 760dde7afaa..1cc9d73cc1b 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java @@ -6699,6 +6699,12 @@ public class FirLightTreeDiagnosticsTestGenerated extends AbstractFirLightTreeDi runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/capturedTypeInEquality.kt"); } + @Test + @TestMetadata("classCastWithRawKotlinType.kt") + public void testClassCastWithRawKotlinType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/classCastWithRawKotlinType.kt"); + } + @Test @TestMetadata("DeepCopyIrTree.kt") public void testDeepCopyIrTree() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java index 6feee4406f0..bfd5b1b36be 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java @@ -6699,6 +6699,12 @@ public class FirPsiDiagnosticTestGenerated extends AbstractFirPsiDiagnosticTest runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/capturedTypeInEquality.kt"); } + @Test + @TestMetadata("classCastWithRawKotlinType.kt") + public void testClassCastWithRawKotlinType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/classCastWithRawKotlinType.kt"); + } + @Test @TestMetadata("DeepCopyIrTree.kt") public void testDeepCopyIrTree() throws Exception { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt index 89543d79447..ac83e584fbe 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt @@ -11,7 +11,6 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol -import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl @@ -194,7 +193,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( // Given `C`, `C` -> `C..C<*>?`. when (mode) { FirJavaTypeConversionMode.ANNOTATION_MEMBER -> Array(classifier.typeParameters.size) { ConeStarProjection } - else -> typeParameterSymbols?.eraseToUpperBounds(session) + else -> typeParameterSymbols?.getProjectionsForRawType(session) ?: Array(classifier.typeParameters.size) { ConeStarProjection } } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt index 716250f8b25..f39e080287f 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt @@ -236,7 +236,7 @@ class ConeRawScopeSubstitutor( return when { type is ConeTypeParameterType -> { substituteOrSelf( - listOf(type.lookupTag.symbol).eraseToUpperBounds(useSiteSession)[0] as ConeKotlinType + listOf(type.lookupTag.symbol).getProjectionsForRawType(useSiteSession)[0] as ConeKotlinType ) } type is ConeClassLikeType && type.typeArguments.isNotEmpty() -> { @@ -251,7 +251,7 @@ class ConeRawScopeSubstitutor( val firClass = type.fullyExpandedType(useSiteSession).lookupTag.toFirRegularClassSymbol(useSiteSession) ?: return null ConeRawType.create( - type.withArguments(firClass.typeParameterSymbols.eraseToUpperBounds(useSiteSession)), + type.withArguments(firClass.typeParameterSymbols.getProjectionsForRawType(useSiteSession)), type.replaceArgumentsWithStarProjections() ) } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt index 379d1a1d5a2..ca7482d0fe9 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isExpect import org.jetbrains.kotlin.fir.declarations.utils.modality import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.diagnostics.ConeRecursiveTypeParameterDuringErasureError +import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.resolve.toSymbol @@ -630,13 +631,17 @@ fun List.eraseToUpperBoundsAssociated( eraseRecursively: Boolean = false ): Map { val cache = mutableMapOf() - return associateWith { it.fir.eraseToUpperBound(session, cache, intersectUpperBounds, eraseRecursively) } + return associateWith { + it.fir.eraseToUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved = true) + } } -fun List.eraseToUpperBounds(session: FirSession): Array { +fun List.getProjectionsForRawType(session: FirSession): Array { val cache = mutableMapOf() return Array(size) { index -> - this[index].fir.eraseToUpperBound(session, cache, intersectUpperBounds = false, eraseRecursively = false) + this[index].fir.eraseToUpperBound( + session, cache, intersectUpperBounds = false, eraseRecursively = false, boundsShouldBeResolved = false + ) } } @@ -644,10 +649,11 @@ private fun FirTypeParameter.eraseToUpperBound( session: FirSession, cache: MutableMap, intersectUpperBounds: Boolean, - eraseRecursively: Boolean + eraseRecursively: Boolean, + boundsShouldBeResolved: Boolean ): ConeKotlinType { fun eraseAsUpperBound(type: FirResolvedTypeRef) = - type.coneType.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively) + type.coneType.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved) return cache.getOrPut(this) { // Mark to avoid loops. @@ -656,7 +662,22 @@ private fun FirTypeParameter.eraseToUpperBound( if (intersectUpperBounds) { ConeTypeIntersector.intersectTypes(session.typeContext, symbol.resolvedBounds.map(::eraseAsUpperBound)) } else { - eraseAsUpperBound(symbol.fir.bounds.first() as FirResolvedTypeRef) + val boundTypeRef = bounds.first() + when { + boundTypeRef is FirResolvedTypeRef -> eraseAsUpperBound(boundTypeRef) + // As we currently know, a raw supertype is the only example + // when bounds can be unresolved here (see the branch below). + // This exception is expected to find other possible occurrences, if any. + boundsShouldBeResolved -> throw IllegalStateException( + "Bounds of ${render()} type parameter of ${containingDeclarationSymbol.fir.render()} aren't resolved" + ) + // While resolving raw supertype in Java we may encounter a situation + // when this supertype constructor has some type parameters and + // their bounds aren't yet resolved. See KT-56630 and comments inside. + // Yet we are replacing these bounds with just 'Any'. + // TODO: think how can we replace it with more correct decision. + else -> session.builtinTypes.anyType.type + } } } } @@ -665,6 +686,7 @@ private fun SimpleTypeMarker.eraseArgumentsDeeply( typeContext: ConeInferenceContext, cache: MutableMap, intersectUpperBounds: Boolean, + boundsShouldBeResolved: Boolean ): ConeKotlinType = with(typeContext) { replaceArgumentsDeeply { typeArgument -> if (typeArgument.isStarProjection()) @@ -676,7 +698,7 @@ private fun SimpleTypeMarker.eraseArgumentsDeeply( typeConstructor as ConeTypeParameterLookupTag val erasedType = typeConstructor.typeParameterSymbol.fir.eraseToUpperBound( - session, cache, intersectUpperBounds, eraseRecursively = true + session, cache, intersectUpperBounds, eraseRecursively = true, boundsShouldBeResolved = boundsShouldBeResolved ) if ((erasedType as? ConeErrorType)?.diagnostic is ConeRecursiveTypeParameterDuringErasureError) @@ -690,12 +712,13 @@ private fun ConeKotlinType.eraseAsUpperBound( session: FirSession, cache: MutableMap, intersectUpperBounds: Boolean, - eraseRecursively: Boolean + eraseRecursively: Boolean, + boundsShouldBeResolved: Boolean ): ConeKotlinType = when (this) { is ConeClassLikeType -> { if (eraseRecursively) { - eraseArgumentsDeeply(session.typeContext, cache, intersectUpperBounds) + eraseArgumentsDeeply(session.typeContext, cache, intersectUpperBounds, boundsShouldBeResolved) } else { withArguments(typeArguments.map { ConeStarProjection }.toTypedArray()) } @@ -706,17 +729,19 @@ private fun ConeKotlinType.eraseAsUpperBound( // so there is no exponential complexity here due to cache lookups. coneFlexibleOrSimpleType( session.typeContext, - lowerBound.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively), - upperBound.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively) + lowerBound.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved), + upperBound.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved) ) is ConeTypeParameterType -> - lookupTag.typeParameterSymbol.fir.eraseToUpperBound(session, cache, intersectUpperBounds, eraseRecursively).let { + lookupTag.typeParameterSymbol.fir.eraseToUpperBound( + session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved + ).let { if (isNullable) it.withNullability(nullability, session.typeContext) else it } is ConeDefinitelyNotNullType -> - original.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively) + original.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved) .makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext) else -> error("unexpected Java type parameter upper bound kind: $this")