K2: Fix bounds erasure for raw types

Basically, this commit reworks eraseToUpperBound* stuff
Instead of a bunch of vague flags, we introduce two modes
(rawTypes/intersection emptiness checker) which defined those flags.

Also, it makes choosing `eraseArgumentsDeeply` option always because
that how it works in K1 and also use invariant projection inside
`eraseArgumentsDeeply` for raw types for the same reason.

^KT-57198 Fixed
This commit is contained in:
Denis.Zharkov
2023-03-16 16:54:16 +01:00
committed by Space Team
parent b0de442d76
commit 38138bf079
11 changed files with 72 additions and 42 deletions
@@ -25043,6 +25043,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/nonGenericRawMember.kt");
}
@Test
@TestMetadata("nonProjectedInnerErasure.kt")
public void testNonProjectedInnerErasure() throws Exception {
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/nonProjectedInnerErasure.kt");
}
@Test
@TestMetadata("nonRawArraysInRawType.kt")
public void testNonRawArraysInRawType() throws Exception {
@@ -25043,6 +25043,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/nonGenericRawMember.kt");
}
@Test
@TestMetadata("nonProjectedInnerErasure.kt")
public void testNonProjectedInnerErasure() throws Exception {
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/nonProjectedInnerErasure.kt");
}
@Test
@TestMetadata("nonRawArraysInRawType.kt")
public void testNonRawArraysInRawType() throws Exception {
@@ -25049,6 +25049,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/nonGenericRawMember.kt");
}
@Test
@TestMetadata("nonProjectedInnerErasure.kt")
public void testNonProjectedInnerErasure() throws Exception {
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/nonProjectedInnerErasure.kt");
}
@Test
@TestMetadata("nonRawArraysInRawType.kt")
public void testNonRawArraysInRawType() throws Exception {
@@ -395,7 +395,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
override fun KotlinTypeMarker.eraseContainingTypeParameters(): KotlinTypeMarker {
val typeParameterErasureMap = this.extractTypeParameters()
.map { (it as ConeTypeParameterLookupTag).typeParameterSymbol }
.eraseToUpperBoundsAssociated(session, intersectUpperBounds = true, eraseRecursively = true)
.eraseToUpperBoundsAssociated(session)
val substitutor by lazy { ConeSubstitutorByMap(typeParameterErasureMap, session) }
val typeWithErasedTypeParameters = if (argumentsCount() != 0) {
replaceArgumentsDeeply {
@@ -18,7 +18,6 @@ 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
@@ -627,12 +626,10 @@ fun KotlinTypeMarker.isSubtypeOf(context: TypeCheckerProviderContext, type: Kotl
fun List<FirTypeParameterSymbol>.eraseToUpperBoundsAssociated(
session: FirSession,
intersectUpperBounds: Boolean = false,
eraseRecursively: Boolean = false
): Map<FirTypeParameterSymbol, ConeKotlinType> {
val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>()
return associateWith {
it.fir.eraseToUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved = true)
it.fir.eraseToUpperBound(session, cache, mode = EraseUpperBoundMode.FOR_EMPTY_INTERSECTION_CHECK)
}
}
@@ -640,37 +637,32 @@ fun List<FirTypeParameterSymbol>.getProjectionsForRawType(session: FirSession):
val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>()
return Array(size) { index ->
this[index].fir.eraseToUpperBound(
session, cache, intersectUpperBounds = false, eraseRecursively = false, boundsShouldBeResolved = false
session, cache, mode = EraseUpperBoundMode.FOR_RAW_TYPE_ERASURE
)
}
}
private enum class EraseUpperBoundMode {
FOR_RAW_TYPE_ERASURE,
FOR_EMPTY_INTERSECTION_CHECK
}
private fun FirTypeParameter.eraseToUpperBound(
session: FirSession,
cache: MutableMap<FirTypeParameter, ConeKotlinType>,
intersectUpperBounds: Boolean,
eraseRecursively: Boolean,
boundsShouldBeResolved: Boolean
mode: EraseUpperBoundMode,
): ConeKotlinType {
fun eraseAsUpperBound(type: FirResolvedTypeRef) =
type.coneType.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved)
type.coneType.eraseAsUpperBound(session, cache, mode)
return cache.getOrPut(this) {
// Mark to avoid loops.
cache[this] = ConeErrorType(ConeRecursiveTypeParameterDuringErasureError(name))
// We can assume that Java type parameter bounds are already converted.
if (intersectUpperBounds) {
if (mode == EraseUpperBoundMode.FOR_EMPTY_INTERSECTION_CHECK) {
ConeTypeIntersector.intersectTypes(session.typeContext, symbol.resolvedBounds.map(::eraseAsUpperBound))
} else {
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"
)
when (val boundTypeRef = bounds.first()) {
is FirResolvedTypeRef -> eraseAsUpperBound(boundTypeRef)
// 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.
@@ -685,8 +677,7 @@ private fun FirTypeParameter.eraseToUpperBound(
private fun SimpleTypeMarker.eraseArgumentsDeeply(
typeContext: ConeInferenceContext,
cache: MutableMap<FirTypeParameter, ConeKotlinType>,
intersectUpperBounds: Boolean,
boundsShouldBeResolved: Boolean
mode: EraseUpperBoundMode,
): ConeKotlinType = with(typeContext) {
replaceArgumentsDeeply { typeArgument ->
if (typeArgument.isStarProjection())
@@ -698,30 +689,28 @@ private fun SimpleTypeMarker.eraseArgumentsDeeply(
typeConstructor as ConeTypeParameterLookupTag
val erasedType = typeConstructor.typeParameterSymbol.fir.eraseToUpperBound(
session, cache, intersectUpperBounds, eraseRecursively = true, boundsShouldBeResolved = boundsShouldBeResolved
session, cache, mode = mode
)
if ((erasedType as? ConeErrorType)?.diagnostic is ConeRecursiveTypeParameterDuringErasureError)
return@replaceArgumentsDeeply ConeStarProjection
erasedType.toTypeProjection(ProjectionKind.OUT)
// See the similar semantics at RawProjectionComputer::computeProjection
if (mode == EraseUpperBoundMode.FOR_RAW_TYPE_ERASURE)
erasedType
else
erasedType.toTypeProjection(ProjectionKind.OUT)
} as ConeKotlinType
}
private fun ConeKotlinType.eraseAsUpperBound(
session: FirSession,
cache: MutableMap<FirTypeParameter, ConeKotlinType>,
intersectUpperBounds: Boolean,
eraseRecursively: Boolean,
boundsShouldBeResolved: Boolean
mode: EraseUpperBoundMode,
): ConeKotlinType =
when (this) {
is ConeClassLikeType -> {
if (eraseRecursively) {
eraseArgumentsDeeply(session.typeContext, cache, intersectUpperBounds, boundsShouldBeResolved)
} else {
withArguments(typeArguments.map { ConeStarProjection }.toTypedArray())
}
eraseArgumentsDeeply(session.typeContext, cache, mode)
}
is ConeFlexibleType ->
@@ -729,19 +718,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, boundsShouldBeResolved),
upperBound.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved)
lowerBound.eraseAsUpperBound(session, cache, mode),
upperBound.eraseAsUpperBound(session, cache, mode)
)
is ConeTypeParameterType ->
lookupTag.typeParameterSymbol.fir.eraseToUpperBound(
session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved
session, cache, mode
).let {
if (isNullable) it.withNullability(nullability, session.typeContext) else it
}
is ConeDefinitelyNotNullType ->
original.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively, boundsShouldBeResolved)
original.eraseAsUpperBound(session, cache, mode)
.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext)
else -> error("unexpected Java type parameter upper bound kind: $this")
@@ -8,5 +8,5 @@ public class Foo<P1 extends Boo<P2, P3, P4>, P2 extends Boo<P1, P3, P4>, P3 exte
// FILE: main.kt
fun main() {
val x = <!DEBUG_INFO_EXPRESSION_TYPE("Foo<Boo<*>..Boo<*>?!, Boo<*>..Boo<*>?!, Boo<*>..Boo<*>?!, Boo<*>..Boo<*>?!>..Foo<*, *, *, *>?!")!>Foo.test1()<!>
val x = <!DEBUG_INFO_EXPRESSION_TYPE("Foo<Boo<Boo<*>..Boo<*>?!>..Boo<Boo<*>..Boo<*>?!>?!, Boo<*>..Boo<*>?!, Boo<Boo<Boo<*>..Boo<*>?!>..Boo<Boo<*>..Boo<*>?!>?!>..Boo<Boo<Boo<*>..Boo<*>?!>..Boo<Boo<*>..Boo<*>?!>?!>?!, Boo<Boo<Boo<*>..Boo<*>?!>..Boo<Boo<*>..Boo<*>?!>?!>..Boo<Boo<Boo<*>..Boo<*>?!>..Boo<Boo<*>..Boo<*>?!>?!>?!>..Foo<*, *, *, *>?!")!>Foo.test1()<!>
}
@@ -8,5 +8,5 @@ class Foo<P1 : Boo<P2, P3, P4>, P2 : Boo<P1, P3, P4>, P3 : Boo<P1, P2, P4>, P4 :
// FILE: main.kt
fun main() {
val x = <!DEBUG_INFO_EXPRESSION_TYPE("Foo<Boo<*, *, *>, Boo<*, *, *>, Boo<*, *, *>, Boo<*, *, *>>..Foo<*, *, *, *>?!")!>Boo.test1()<!>
val x = <!DEBUG_INFO_EXPRESSION_TYPE("Foo<Boo<Boo<*, Boo<*, *, Boo<*, *, *>>, Boo<*, *, *>>, Boo<*, *, Boo<*, *, *>>, Boo<*, *, *>>, Boo<*, Boo<*, *, Boo<*, *, *>>, Boo<*, *, *>>, Boo<*, *, Boo<*, *, *>>, Boo<*, *, *>>..Foo<*, *, *, *>?!")!>Boo.test1()<!>
}
@@ -9,5 +9,5 @@ public class Foo<T extends Boo<K>, K extends Boo<X>, X extends Boo<K>> {
// FILE: main.kt
fun main() {
val x = <!DEBUG_INFO_EXPRESSION_TYPE("Foo<Boo<*>..Boo<*>?!, Boo<*>..Boo<*>?!, Boo<*>..Boo<*>?!>..Foo<*, *, *>?!")!>Foo.test1()<!>.test2()
val x = <!DEBUG_INFO_EXPRESSION_TYPE("Foo<Boo<Boo<Boo<*>..Boo<*>?!>..Boo<Boo<*>..Boo<*>?!>?!>..Boo<Boo<Boo<*>..Boo<*>?!>..Boo<Boo<*>..Boo<*>?!>?!>?!, Boo<Boo<*>..Boo<*>?!>..Boo<Boo<*>..Boo<*>?!>?!, Boo<*>..Boo<*>?!>..Foo<*, *, *>?!")!>Foo.test1()<!>.test2()
}
@@ -0,0 +1,17 @@
// FIR_IDENTICAL
// ISSUE: KT-57198
// FILE: CustomGdbServerRunConfiguration.java
public class CustomGdbServerRunConfiguration implements CidrRunConfiguration {}
// FILE: main.kt
interface CidrBuildTarget<BC>
interface CidrRunConfiguration<BC, TARGET: CidrBuildTarget<BC>>
fun applyEditorTo(arg: CidrRunConfiguration<Any?, CidrBuildTarget<Any?>>) {}
fun main() {
// Previously, for CidrRunConfiguration raw type, it's lower bound was resolved as CidrRunConfiguration<Any?, CidrBuildTarget<*>> in K2
// That is not a subtype of CidrRunConfiguration<Any?, CidrBuildTarget<Any?>>
applyEditorTo(CustomGdbServerRunConfiguration()) // K1: ok, K2: was ARGUMENT_TYPE_MISMATCH
}
@@ -1,10 +1,10 @@
public open class RawSuperTypeWithRecursiveBoundMultipleParameters : R|kotlin/Any| {
public constructor(): R|test/RawSuperTypeWithRecursiveBoundMultipleParameters|
public open inner class Derived : R|kotlin/Any|, R|Raw type test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<kotlin/Any!, ft<test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<*, *>, test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<*, *>?>>| {
public open inner class Derived : R|kotlin/Any|, R|Raw type test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<kotlin/Any!, ft<test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<kotlin/Any!, *>, test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<kotlin/Any!, *>?>>| {
public open fun foo(o: R|kotlin/Any!|, o1: R|kotlin/Any!|): R|kotlin/Unit|
@R|java/lang/Override|() public open fun foo(r: R|kotlin/Any!|, t: R|ft<Raw type test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<kotlin/Any!, ft<test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<*, *>, test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<*, *>?>>, test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<*, *>?>|): R|kotlin/Unit|
@R|java/lang/Override|() public open fun foo(r: R|kotlin/Any!|, t: R|ft<Raw type test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<kotlin/Any!, ft<test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<kotlin/Any!, *>, test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<kotlin/Any!, *>?>>, test/RawSuperTypeWithRecursiveBoundMultipleParameters.Super<*, *>?>|): R|kotlin/Unit|
@R|java/lang/Override|() public open fun dummy(): R|kotlin/Unit|
@@ -25049,6 +25049,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/nonGenericRawMember.kt");
}
@Test
@TestMetadata("nonProjectedInnerErasure.kt")
public void testNonProjectedInnerErasure() throws Exception {
runTest("compiler/testData/diagnostics/tests/platformTypes/rawTypes/nonProjectedInnerErasure.kt");
}
@Test
@TestMetadata("nonRawArraysInRawType.kt")
public void testNonRawArraysInRawType() throws Exception {