K2: fix CCE during bound conversion for Java raw Kotlin-based type
#KT-56630 Fixed
This commit is contained in:
committed by
Space Team
parent
d1e0a432a5
commit
58959951d3
+6
@@ -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 {
|
||||||
|
|||||||
Vendored
+18
@@ -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|())
|
||||||
|
}
|
||||||
Vendored
+16
@@ -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())
|
||||||
|
}
|
||||||
+6
@@ -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 {
|
||||||
|
|||||||
+6
@@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user