[FIR] Unbind type parameter erasing from java

This commit is contained in:
Victor Petukhov
2022-04-04 19:25:32 +03:00
committed by teamcity
parent f31cf90de2
commit 313da6f41e
2 changed files with 92 additions and 35 deletions
@@ -224,38 +224,3 @@ private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer(
return mutableLastParameterVariance != Variance.OUT_VARIANCE
}
private fun List<FirTypeParameterSymbol>.eraseToUpperBounds(session: FirSession): Array<ConeTypeProjection> {
val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>()
return Array(size) { index -> this[index].fir.eraseToUpperBound(session, cache) }
}
private fun FirTypeParameter.eraseToUpperBound(session: FirSession, cache: MutableMap<FirTypeParameter, ConeKotlinType>): ConeKotlinType {
return cache.getOrPut(this) {
// Mark to avoid loops.
cache[this] = ConeErrorType(ConeIntermediateDiagnostic("self-recursive type parameter $name"))
// We can assume that Java type parameter bounds are already converted.
symbol.resolvedBounds.first().coneType.eraseAsUpperBound(session, cache)
}
}
private fun ConeKotlinType.eraseAsUpperBound(session: FirSession, cache: MutableMap<FirTypeParameter, ConeKotlinType>): ConeKotlinType =
when (this) {
is ConeClassLikeType ->
withArguments(typeArguments.map { ConeStarProjection }.toTypedArray())
is ConeFlexibleType ->
// If one bound is a type parameter, the other is probably the same type parameter,
// so there is no exponential complexity here due to cache lookups.
coneFlexibleOrSimpleType(
session.typeContext,
lowerBound.eraseAsUpperBound(session, cache),
upperBound.eraseAsUpperBound(session, cache)
)
is ConeTypeParameterType ->
lookupTag.typeParameterSymbol.fir.eraseToUpperBound(session, cache).let {
if (isNullable) it.withNullability(nullability, session.typeContext) else it
}
is ConeDefinitelyNotNullType ->
original.eraseAsUpperBound(session, cache).makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext)
else -> error("unexpected Java type parameter upper bound kind: $this")
}
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
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.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
@@ -681,3 +682,94 @@ private fun lowerThanBound(context: ConeInferenceContext, argument: ConeKotlinTy
fun KotlinTypeMarker.isSubtypeOf(context: TypeCheckerProviderContext, type: KotlinTypeMarker?): Boolean =
type != null && AbstractTypeChecker.isSubtypeOf(context, this, type)
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) }
}
fun List<FirTypeParameterSymbol>.eraseToUpperBounds(session: FirSession): Array<ConeTypeProjection> {
val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>()
return Array(size) { index ->
this[index].fir.eraseToUpperBound(session, cache, intersectUpperBounds = false, eraseRecursively = false)
}
}
private fun FirTypeParameter.eraseToUpperBound(
session: FirSession,
cache: MutableMap<FirTypeParameter, ConeKotlinType>,
intersectUpperBounds: Boolean,
eraseRecursively: Boolean
): ConeKotlinType {
fun eraseAsUpperBound(type: FirResolvedTypeRef) =
type.coneType.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively)
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) {
ConeTypeIntersector.intersectTypes(session.typeContext, symbol.resolvedBounds.map(::eraseAsUpperBound))
} else {
eraseAsUpperBound(symbol.resolvedBounds.first())
}
}
}
private fun SimpleTypeMarker.eraseArgumentsDeeply(
typeContext: ConeInferenceContext,
cache: MutableMap<FirTypeParameter, ConeKotlinType>,
intersectUpperBounds: Boolean,
): ConeKotlinType = with(typeContext) {
replaceArgumentsDeeply { typeArgument ->
val typeConstructor = typeArgument.getType().typeConstructor().takeIf { it.isTypeParameterTypeConstructor() }
?: return@replaceArgumentsDeeply typeArgument
typeConstructor as ConeTypeParameterLookupTag
val erasedType = typeConstructor.typeParameterSymbol.fir.eraseToUpperBound(
session, cache, intersectUpperBounds, eraseRecursively = true
)
if ((erasedType as? ConeErrorType)?.diagnostic is ConeRecursiveTypeParameterDuringErasureError)
return@replaceArgumentsDeeply ConeStarProjection
erasedType.toTypeProjection(ProjectionKind.OUT)
} as ConeKotlinType
}
private fun ConeKotlinType.eraseAsUpperBound(
session: FirSession,
cache: MutableMap<FirTypeParameter, ConeKotlinType>,
intersectUpperBounds: Boolean,
eraseRecursively: Boolean
): ConeKotlinType =
when (this) {
is ConeClassLikeType -> {
if (eraseRecursively) {
eraseArgumentsDeeply(session.typeContext, cache, intersectUpperBounds)
} else {
withArguments(typeArguments.map { ConeStarProjection }.toTypedArray())
}
}
is ConeFlexibleType ->
// If one bound is a type parameter, the other is probably the same type parameter,
// 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)
)
is ConeTypeParameterType ->
lookupTag.typeParameterSymbol.fir.eraseToUpperBound(session, cache, intersectUpperBounds, eraseRecursively).let {
if (isNullable) it.withNullability(nullability, session.typeContext) else it
}
is ConeDefinitelyNotNullType ->
original.eraseAsUpperBound(session, cache, intersectUpperBounds, eraseRecursively)
.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext)
else -> error("unexpected Java type parameter upper bound kind: $this")
}