K2: fix CCE during bound conversion for Java raw Kotlin-based type

#KT-56630 Fixed
This commit is contained in:
Mikhail Glukhikh
2023-02-14 15:21:17 +01:00
committed by Space Team
parent d1e0a432a5
commit 58959951d3
8 changed files with 93 additions and 17 deletions
@@ -6699,6 +6699,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/capturedTypeInEquality.kt"); 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 @Test
@TestMetadata("DeepCopyIrTree.kt") @TestMetadata("DeepCopyIrTree.kt")
public void testDeepCopyIrTree() throws Exception { public void testDeepCopyIrTree() throws Exception {
@@ -0,0 +1,18 @@
FILE: test.kt
public abstract interface Inter : R|kotlin/Any| {
}
public open class Apple<T : R|Inter|> : R|Inter| {
public constructor<T : R|Inter|>(): R|Apple<T>| {
super<R|kotlin/Any|>()
}
}
public final class XXX : R|Usage| {
public constructor(): R|XXX| {
super<R|Usage|>()
}
}
public final fun main(): R|kotlin/Unit| {
R|kotlin/io/println|(R|/XXX.XXX|())
}
@@ -0,0 +1,16 @@
// ISSUE: KT-56630
// FILE: Usage.java
public class Usage extends Apple {
}
// FILE: test.kt
interface Inter
open class Apple<T : Inter> : Inter
class XXX : Usage()
fun main() {
println(XXX())
}
@@ -6699,6 +6699,12 @@ public class FirLightTreeDiagnosticsTestGenerated extends AbstractFirLightTreeDi
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/capturedTypeInEquality.kt"); 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 @Test
@TestMetadata("DeepCopyIrTree.kt") @TestMetadata("DeepCopyIrTree.kt")
public void testDeepCopyIrTree() throws Exception { public void testDeepCopyIrTree() throws Exception {
@@ -6699,6 +6699,12 @@ public class FirPsiDiagnosticTestGenerated extends AbstractFirPsiDiagnosticTest
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/capturedTypeInEquality.kt"); 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 @Test
@TestMetadata("DeepCopyIrTree.kt") @TestMetadata("DeepCopyIrTree.kt")
public void testDeepCopyIrTree() throws Exception { public void testDeepCopyIrTree() throws Exception {
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol 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.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
@@ -194,7 +193,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound(
// Given `C<T : X>`, `C` -> `C<X>..C<*>?`. // Given `C<T : X>`, `C` -> `C<X>..C<*>?`.
when (mode) { when (mode) {
FirJavaTypeConversionMode.ANNOTATION_MEMBER -> Array(classifier.typeParameters.size) { ConeStarProjection } FirJavaTypeConversionMode.ANNOTATION_MEMBER -> Array(classifier.typeParameters.size) { ConeStarProjection }
else -> typeParameterSymbols?.eraseToUpperBounds(session) else -> typeParameterSymbols?.getProjectionsForRawType(session)
?: Array(classifier.typeParameters.size) { ConeStarProjection } ?: Array(classifier.typeParameters.size) { ConeStarProjection }
} }
} }
@@ -236,7 +236,7 @@ class ConeRawScopeSubstitutor(
return when { return when {
type is ConeTypeParameterType -> { type is ConeTypeParameterType -> {
substituteOrSelf( 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() -> { type is ConeClassLikeType && type.typeArguments.isNotEmpty() -> {
@@ -251,7 +251,7 @@ class ConeRawScopeSubstitutor(
val firClass = type.fullyExpandedType(useSiteSession).lookupTag.toFirRegularClassSymbol(useSiteSession) ?: return null val firClass = type.fullyExpandedType(useSiteSession).lookupTag.toFirRegularClassSymbol(useSiteSession) ?: return null
ConeRawType.create( ConeRawType.create(
type.withArguments(firClass.typeParameterSymbols.eraseToUpperBounds(useSiteSession)), type.withArguments(firClass.typeParameterSymbols.getProjectionsForRawType(useSiteSession)),
type.replaceArgumentsWithStarProjections() type.replaceArgumentsWithStarProjections()
) )
} }
@@ -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.modality
import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.diagnostics.ConeRecursiveTypeParameterDuringErasureError 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.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
@@ -630,13 +631,17 @@ fun List<FirTypeParameterSymbol>.eraseToUpperBoundsAssociated(
eraseRecursively: Boolean = false eraseRecursively: Boolean = false
): Map<FirTypeParameterSymbol, ConeKotlinType> { ): Map<FirTypeParameterSymbol, ConeKotlinType> {
val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>() val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>()
return associateWith { it.fir.eraseToUpperBound(session, cache, intersectUpperBounds, eraseRecursively) } return associateWith {
it.fir.eraseToUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved = true)
}
} }
fun List<FirTypeParameterSymbol>.eraseToUpperBounds(session: FirSession): Array<ConeTypeProjection> { fun List<FirTypeParameterSymbol>.getProjectionsForRawType(session: FirSession): Array<ConeTypeProjection> {
val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>() val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>()
return Array(size) { index -> 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, session: FirSession,
cache: MutableMap<FirTypeParameter, ConeKotlinType>, cache: MutableMap<FirTypeParameter, ConeKotlinType>,
intersectUpperBounds: Boolean, intersectUpperBounds: Boolean,
eraseRecursively: Boolean eraseRecursively: Boolean,
boundsShouldBeResolved: Boolean
): ConeKotlinType { ): ConeKotlinType {
fun eraseAsUpperBound(type: FirResolvedTypeRef) = fun eraseAsUpperBound(type: FirResolvedTypeRef) =
type.coneType.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively) type.coneType.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved)
return cache.getOrPut(this) { return cache.getOrPut(this) {
// Mark to avoid loops. // Mark to avoid loops.
@@ -656,7 +662,22 @@ private fun FirTypeParameter.eraseToUpperBound(
if (intersectUpperBounds) { if (intersectUpperBounds) {
ConeTypeIntersector.intersectTypes(session.typeContext, symbol.resolvedBounds.map(::eraseAsUpperBound)) ConeTypeIntersector.intersectTypes(session.typeContext, symbol.resolvedBounds.map(::eraseAsUpperBound))
} else { } 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, typeContext: ConeInferenceContext,
cache: MutableMap<FirTypeParameter, ConeKotlinType>, cache: MutableMap<FirTypeParameter, ConeKotlinType>,
intersectUpperBounds: Boolean, intersectUpperBounds: Boolean,
boundsShouldBeResolved: Boolean
): ConeKotlinType = with(typeContext) { ): ConeKotlinType = with(typeContext) {
replaceArgumentsDeeply { typeArgument -> replaceArgumentsDeeply { typeArgument ->
if (typeArgument.isStarProjection()) if (typeArgument.isStarProjection())
@@ -676,7 +698,7 @@ private fun SimpleTypeMarker.eraseArgumentsDeeply(
typeConstructor as ConeTypeParameterLookupTag typeConstructor as ConeTypeParameterLookupTag
val erasedType = typeConstructor.typeParameterSymbol.fir.eraseToUpperBound( 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) if ((erasedType as? ConeErrorType)?.diagnostic is ConeRecursiveTypeParameterDuringErasureError)
@@ -690,12 +712,13 @@ private fun ConeKotlinType.eraseAsUpperBound(
session: FirSession, session: FirSession,
cache: MutableMap<FirTypeParameter, ConeKotlinType>, cache: MutableMap<FirTypeParameter, ConeKotlinType>,
intersectUpperBounds: Boolean, intersectUpperBounds: Boolean,
eraseRecursively: Boolean eraseRecursively: Boolean,
boundsShouldBeResolved: Boolean
): ConeKotlinType = ): ConeKotlinType =
when (this) { when (this) {
is ConeClassLikeType -> { is ConeClassLikeType -> {
if (eraseRecursively) { if (eraseRecursively) {
eraseArgumentsDeeply(session.typeContext, cache, intersectUpperBounds) eraseArgumentsDeeply(session.typeContext, cache, intersectUpperBounds, boundsShouldBeResolved)
} else { } else {
withArguments(typeArguments.map { ConeStarProjection }.toTypedArray()) withArguments(typeArguments.map { ConeStarProjection }.toTypedArray())
} }
@@ -706,17 +729,19 @@ private fun ConeKotlinType.eraseAsUpperBound(
// so there is no exponential complexity here due to cache lookups. // so there is no exponential complexity here due to cache lookups.
coneFlexibleOrSimpleType( coneFlexibleOrSimpleType(
session.typeContext, session.typeContext,
lowerBound.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively), lowerBound.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved),
upperBound.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively) upperBound.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved)
) )
is ConeTypeParameterType -> 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 if (isNullable) it.withNullability(nullability, session.typeContext) else it
} }
is ConeDefinitelyNotNullType -> is ConeDefinitelyNotNullType ->
original.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively) original.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved)
.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext) .makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext)
else -> error("unexpected Java type parameter upper bound kind: $this") else -> error("unexpected Java type parameter upper bound kind: $this")