FIR: expect Java type parameter bounds to be converted first
This commit is contained in:
@@ -7,18 +7,15 @@ package org.jetbrains.kotlin.fir.java
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
|
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
|
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
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.symbolProvider
|
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.typeContext
|
import org.jetbrains.kotlin.fir.typeContext
|
||||||
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
|
||||||
@@ -31,7 +28,6 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.StandardClassIds
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
|
||||||
|
|
||||||
private fun ClassId.toLookupTag(): ConeClassLikeLookupTag =
|
private fun ClassId.toLookupTag(): ConeClassLikeLookupTag =
|
||||||
ConeClassLikeLookupTagImpl(this)
|
ConeClassLikeLookupTagImpl(this)
|
||||||
@@ -190,33 +186,23 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val lookupTag = ConeClassLikeLookupTagImpl(classId)
|
val lookupTag = ConeClassLikeLookupTagImpl(classId)
|
||||||
if (lookupTag == lowerBound?.lookupTag && !isRaw) {
|
// When converting type parameter bounds we should not attempt to load any classes, as this may trigger
|
||||||
return lookupTag.constructClassType(lowerBound.typeArguments, isNullable = true, attributes)
|
// enhancement of type parameter bounds on some other class that depends on this one. Also, in case of raw
|
||||||
}
|
// types specifically there could be an infinite recursion on the type parameter itself.
|
||||||
|
val typeParameters = lookupTag.takeIf { mode != FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND }
|
||||||
val mappedTypeArguments = if (isRaw) {
|
?.toFirRegularClass(session)?.typeParameters
|
||||||
val defaultArgs = Array(classifier.typeParameters.size) { ConeStarProjection }
|
val mappedTypeArguments = when {
|
||||||
// This isn't entirely correct, but it prevents infinite recursion in cases like A<T extends A>,
|
isRaw ->
|
||||||
// where the upper bound would be an infinite type `X = A<X>..A<*>?`.
|
// Given `C<T : X>`, `C` -> `C<X>..C<*>?`.
|
||||||
if (lowerBound != null || mode == FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND) {
|
typeParameters.takeIf { lowerBound == null }?.eraseToUpperBounds(session)
|
||||||
defaultArgs
|
?: Array(classifier.typeParameters.size) { ConeStarProjection }
|
||||||
} else {
|
lookupTag != lowerBound?.lookupTag ->
|
||||||
val classSymbol = session.symbolProvider.getClassLikeSymbolByFqName(classId) as? FirRegularClassSymbol
|
Array(typeArguments.size) { index ->
|
||||||
classSymbol?.fir?.typeParameters?.eraseToUpperBounds(session, javaTypeParameterStack) ?: defaultArgs
|
val argument = typeArguments[index]
|
||||||
}
|
val parameter = typeParameters?.getOrNull(index)?.symbol?.fir
|
||||||
} else {
|
argument.toConeProjectionWithoutEnhancement(session, javaTypeParameterStack, parameter, mode)
|
||||||
// TODO: why is this condition needed?
|
}
|
||||||
val useTypeParameters = mode != FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND && mode != FirJavaTypeConversionMode.SUPERTYPE
|
else -> lowerBound.typeArguments
|
||||||
val typeParameters = runIf(useTypeParameters) {
|
|
||||||
val classSymbol = session.symbolProvider.getClassLikeSymbolByFqName(classId) as? FirRegularClassSymbol
|
|
||||||
classSymbol?.fir?.typeParameters
|
|
||||||
} ?: emptyList()
|
|
||||||
|
|
||||||
Array(typeArguments.size) { index ->
|
|
||||||
val argument = typeArguments[index]
|
|
||||||
val parameter = typeParameters.getOrNull(index)?.symbol?.fir
|
|
||||||
argument.toConeProjectionWithoutEnhancement(session, javaTypeParameterStack, parameter, mode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lookupTag.constructClassType(mappedTypeArguments, isNullable = lowerBound != null, attributes)
|
lookupTag.constructClassType(mappedTypeArguments, isNullable = lowerBound != null, attributes)
|
||||||
@@ -246,34 +232,27 @@ private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer(
|
|||||||
|
|
||||||
if (!typeArguments.lastOrNull().isSuperWildcard()) return false
|
if (!typeArguments.lastOrNull().isSuperWildcard()) return false
|
||||||
val mutableLastParameterVariance =
|
val mutableLastParameterVariance =
|
||||||
(mutableClassId.toLookupTag().toSymbol(session)?.fir as? FirRegularClass)?.typeParameters?.lastOrNull()?.symbol?.fir?.variance
|
mutableClassId.toLookupTag().toFirRegularClass(session)?.typeParameters?.lastOrNull()?.symbol?.fir?.variance
|
||||||
?: return false
|
?: return false
|
||||||
|
|
||||||
return mutableLastParameterVariance != Variance.OUT_VARIANCE
|
return mutableLastParameterVariance != Variance.OUT_VARIANCE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<FirTypeParameterRef>.eraseToUpperBounds(
|
private fun List<FirTypeParameterRef>.eraseToUpperBounds(session: FirSession): Array<ConeTypeProjection> {
|
||||||
session: FirSession, javaTypeParameterStack: JavaTypeParameterStack
|
|
||||||
): Array<ConeTypeProjection> {
|
|
||||||
val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>()
|
val cache = mutableMapOf<FirTypeParameter, ConeKotlinType>()
|
||||||
return Array(size) { index -> this[index].symbol.fir.eraseToUpperBound(session, javaTypeParameterStack, cache) }
|
return Array(size) { index -> this[index].symbol.fir.eraseToUpperBound(session, cache) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirTypeParameter.eraseToUpperBound(
|
private fun FirTypeParameter.eraseToUpperBound(session: FirSession, cache: MutableMap<FirTypeParameter, ConeKotlinType>): ConeKotlinType {
|
||||||
session: FirSession, javaTypeParameterStack: JavaTypeParameterStack,
|
|
||||||
cache: MutableMap<FirTypeParameter, ConeKotlinType>
|
|
||||||
): ConeKotlinType {
|
|
||||||
return cache.getOrPut(this) {
|
return cache.getOrPut(this) {
|
||||||
cache[this] = ConeKotlinErrorType(ConeIntermediateDiagnostic("self-recursive type parameter $name")) // mark to avoid loops
|
// Mark to avoid loops.
|
||||||
bounds.first().toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack)
|
cache[this] = ConeKotlinErrorType(ConeIntermediateDiagnostic("self-recursive type parameter $name"))
|
||||||
.eraseAsUpperBound(session, javaTypeParameterStack, cache)
|
// We can assume that Java type parameter bounds are already converted.
|
||||||
|
bounds.first().coneType.eraseAsUpperBound(session, cache)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ConeKotlinType.eraseAsUpperBound(
|
private fun ConeKotlinType.eraseAsUpperBound(session: FirSession, cache: MutableMap<FirTypeParameter, ConeKotlinType>): ConeKotlinType =
|
||||||
session: FirSession, javaTypeParameterStack: JavaTypeParameterStack,
|
|
||||||
cache: MutableMap<FirTypeParameter, ConeKotlinType>
|
|
||||||
): ConeKotlinType =
|
|
||||||
when (this) {
|
when (this) {
|
||||||
is ConeClassLikeType ->
|
is ConeClassLikeType ->
|
||||||
withArguments(typeArguments.map { ConeStarProjection }.toTypedArray())
|
withArguments(typeArguments.map { ConeStarProjection }.toTypedArray())
|
||||||
@@ -282,16 +261,15 @@ 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, javaTypeParameterStack, cache),
|
lowerBound.eraseAsUpperBound(session, cache),
|
||||||
upperBound.eraseAsUpperBound(session, javaTypeParameterStack, cache)
|
upperBound.eraseAsUpperBound(session, cache)
|
||||||
)
|
)
|
||||||
is ConeTypeParameterType ->
|
is ConeTypeParameterType ->
|
||||||
lookupTag.typeParameterSymbol.fir.eraseToUpperBound(session, javaTypeParameterStack, cache).let {
|
lookupTag.typeParameterSymbol.fir.eraseToUpperBound(session, cache).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, javaTypeParameterStack, cache)
|
original.eraseAsUpperBound(session, cache).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