FIR: expect Java type parameter bounds to be converted first

This commit is contained in:
pyos
2021-08-13 16:20:30 +02:00
committed by teamcityserver
parent ba1fc81b96
commit 6f4b5cc73a
@@ -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")
} }