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:
committed by
Space Team
parent
b0de442d76
commit
38138bf079
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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")
|
||||
|
||||
Vendored
+1
-1
@@ -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()<!>
|
||||
}
|
||||
|
||||
+1
-1
@@ -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()<!>
|
||||
}
|
||||
|
||||
+1
-1
@@ -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()
|
||||
}
|
||||
|
||||
+17
@@ -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
|
||||
}
|
||||
+2
-2
@@ -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|
|
||||
|
||||
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user