FIR: enhance Java type parameter bounds
This also fixes handling of self-recursive types.
TODO: fix jspecify/{strict,warn}Mode/Captured.fir.kt
This commit is contained in:
@@ -18,22 +18,23 @@ import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.FirTypeParameterBuilder
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildConstructedClassTypeParameterRef
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildEnumEntry
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildOuterClassTypeParameterRef
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.addDefaultBoundIfNecessary
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.effectiveVisibility
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.modality
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.java.declarations.*
|
||||
import org.jetbrains.kotlin.fir.java.enhancement.FirSignatureEnhancement
|
||||
import org.jetbrains.kotlin.fir.resolve.constructType
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
@@ -96,51 +97,33 @@ class JavaSymbolProvider(
|
||||
override fun getTopLevelPropertySymbolsTo(destination: MutableList<FirPropertySymbol>, packageFqName: FqName, name: Name) {
|
||||
}
|
||||
|
||||
private fun JavaTypeParameter.toFirTypeParameterSymbol(
|
||||
javaTypeParameterStack: JavaTypeParameterStack
|
||||
): Pair<FirTypeParameterSymbol, Boolean> {
|
||||
val stored = javaTypeParameterStack.safeGet(this)
|
||||
if (stored != null) return stored to true
|
||||
val firSymbol = FirTypeParameterSymbol()
|
||||
javaTypeParameterStack.addParameter(this, firSymbol)
|
||||
return firSymbol to false
|
||||
}
|
||||
|
||||
private fun JavaTypeParameter.toFirTypeParameter(
|
||||
firSymbol: FirTypeParameterSymbol,
|
||||
javaTypeParameterStack: JavaTypeParameterStack
|
||||
): FirTypeParameter {
|
||||
return FirTypeParameterBuilder().apply {
|
||||
private fun JavaTypeParameter.toFirTypeParameter(javaTypeParameterStack: JavaTypeParameterStack): FirTypeParameter {
|
||||
return buildTypeParameter {
|
||||
moduleData = this@JavaSymbolProvider.baseModuleData
|
||||
origin = FirDeclarationOrigin.Java
|
||||
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
|
||||
this.name = this@toFirTypeParameter.name
|
||||
symbol = firSymbol
|
||||
name = this@toFirTypeParameter.name
|
||||
symbol = FirTypeParameterSymbol()
|
||||
variance = INVARIANT
|
||||
isReified = false
|
||||
addBounds(this@toFirTypeParameter, javaTypeParameterStack)
|
||||
}.build()
|
||||
}
|
||||
|
||||
private fun FirTypeParameterBuilder.addBounds(
|
||||
javaTypeParameter: JavaTypeParameter,
|
||||
stack: JavaTypeParameterStack
|
||||
) {
|
||||
for (upperBound in javaTypeParameter.upperBounds) {
|
||||
bounds += upperBound.toFirResolvedTypeRef(session, stack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND)
|
||||
}
|
||||
addDefaultBoundIfNecessary(isFlexible = true)
|
||||
}
|
||||
|
||||
private fun List<JavaTypeParameter>.convertTypeParameters(stack: JavaTypeParameterStack): List<FirTypeParameter> {
|
||||
return map { it.toFirTypeParameterSymbol(stack) }.mapIndexed { index, (symbol, initialized) ->
|
||||
// This nasty logic is required, because type parameter bound can refer other type parameter from the list
|
||||
// So we have to create symbols first, and type parameters themselves after them
|
||||
if (initialized) symbol.fir
|
||||
else this[index].toFirTypeParameter(symbol, stack)
|
||||
javaTypeParameterStack.addParameter(this@toFirTypeParameter, symbol)
|
||||
// TODO: should be lazy (in case annotations refer to the containing class)
|
||||
annotations.addFromJava(session, this@toFirTypeParameter, javaTypeParameterStack)
|
||||
for (upperBound in this@toFirTypeParameter.upperBounds) {
|
||||
bounds += upperBound.toFirJavaTypeRef(session, javaTypeParameterStack)
|
||||
}
|
||||
if (bounds.isEmpty()) {
|
||||
val builtinTypes = baseModuleData.session.builtinTypes
|
||||
bounds += buildResolvedTypeRef {
|
||||
type = ConeFlexibleType(builtinTypes.anyType.type, builtinTypes.nullableAnyType.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<JavaTypeParameter>.convertTypeParameters(stack: JavaTypeParameterStack): List<FirTypeParameter> =
|
||||
map { it.toFirTypeParameter(stack) }
|
||||
|
||||
override fun getClassLikeSymbolByFqName(classId: ClassId): FirRegularClassSymbol? {
|
||||
return try {
|
||||
if (!hasTopLevelClassOf(classId)) return null
|
||||
@@ -209,8 +192,15 @@ class JavaSymbolProvider(
|
||||
val firJavaClass = createFirJavaClass(javaClass, classSymbol, outerClassId, parentClassSymbol, classId, javaTypeParameterStack)
|
||||
parentClassTypeParameterStackCache.remove(classSymbol)
|
||||
parentClassEffectiveVisibilityCache.remove(classSymbol)
|
||||
|
||||
// There's a bit of an ordering restriction here:
|
||||
// 1. annotations should be added after the symbol is bound, as annotations can refer to the class itself;
|
||||
// 2. bound/supertype conversion should happen here for the same reason;
|
||||
// 3. type enhancement requires annotations to be already present (and supertypes can refer to type parameters).
|
||||
firJavaClass.annotations.addFromJava(session, javaClass, javaTypeParameterStack)
|
||||
val enhancement = FirSignatureEnhancement(firJavaClass, session) { emptyList() }
|
||||
enhancement.enhanceTypeParameterBounds(firJavaClass.typeParameters)
|
||||
firJavaClass.convertSuperTypes(javaClass, javaTypeParameterStack)
|
||||
firJavaClass.addAnnotationsFrom(this@JavaSymbolProvider.session, javaClass, javaTypeParameterStack)
|
||||
firJavaClass.replaceDeprecation(firJavaClass.getDeprecationInfos(session.languageVersionSettings.apiVersion))
|
||||
return firJavaClass
|
||||
}
|
||||
@@ -397,7 +387,8 @@ class JavaSymbolProvider(
|
||||
returnTypeRef = returnType.toFirJavaTypeRef(this@JavaSymbolProvider.session, javaTypeParameterStack)
|
||||
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
|
||||
origin = FirDeclarationOrigin.Java
|
||||
addAnnotationsFrom(this@JavaSymbolProvider.session, javaField, javaTypeParameterStack)
|
||||
// TODO: check if this works properly with annotations that take the enum class as an argument
|
||||
annotations.addFromJava(this@JavaSymbolProvider.session, javaField, javaTypeParameterStack)
|
||||
}.apply {
|
||||
containingClassForStaticMemberAttr = ConeClassLikeLookupTagImpl(classId)
|
||||
}
|
||||
|
||||
@@ -51,15 +51,21 @@ internal enum class FirJavaTypeConversionMode {
|
||||
DEFAULT, ANNOTATION_MEMBER, SUPERTYPE, TYPE_PARAMETER_BOUND
|
||||
}
|
||||
|
||||
internal fun FirTypeRef.resolveIfJavaType(
|
||||
session: FirSession, javaTypeParameterStack: JavaTypeParameterStack,
|
||||
mode: FirJavaTypeConversionMode = FirJavaTypeConversionMode.DEFAULT
|
||||
): FirTypeRef = when (this) {
|
||||
is FirResolvedTypeRef -> this
|
||||
is FirJavaTypeRef -> type.toFirResolvedTypeRef(session, javaTypeParameterStack, mode)
|
||||
else -> this
|
||||
}
|
||||
|
||||
internal fun FirTypeRef.toConeKotlinTypeProbablyFlexible(
|
||||
session: FirSession, javaTypeParameterStack: JavaTypeParameterStack,
|
||||
mode: FirJavaTypeConversionMode = FirJavaTypeConversionMode.DEFAULT
|
||||
): ConeKotlinType =
|
||||
when (this) {
|
||||
is FirResolvedTypeRef -> this
|
||||
is FirJavaTypeRef -> type.toFirResolvedTypeRef(session, javaTypeParameterStack, mode)
|
||||
else -> null
|
||||
}?.type ?: ConeKotlinErrorType(ConeSimpleDiagnostic("Type reference in Java not resolved: ${this::class.java}", DiagnosticKind.Java))
|
||||
(resolveIfJavaType(session, javaTypeParameterStack, mode) as? FirResolvedTypeRef)?.type
|
||||
?: ConeKotlinErrorType(ConeSimpleDiagnostic("Type reference in Java not resolved: ${this::class.java}", DiagnosticKind.Java))
|
||||
|
||||
internal fun JavaType.toFirJavaTypeRef(session: FirSession, javaTypeParameterStack: JavaTypeParameterStack): FirJavaTypeRef {
|
||||
return buildJavaTypeRef {
|
||||
@@ -199,6 +205,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound(
|
||||
classSymbol?.fir?.typeParameters?.eraseToUpperBounds(session, javaTypeParameterStack) ?: defaultArgs
|
||||
}
|
||||
} else {
|
||||
// TODO: why is this condition needed?
|
||||
val useTypeParameters = mode != FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND && mode != FirJavaTypeConversionMode.SUPERTYPE
|
||||
val typeParameters = runIf(useTypeParameters) {
|
||||
val classSymbol = session.symbolProvider.getClassLikeSymbolByFqName(classId) as? FirRegularClassSymbol
|
||||
|
||||
@@ -21,12 +21,10 @@ internal class JavaTypeParameterStack {
|
||||
}
|
||||
|
||||
operator fun get(javaTypeParameter: JavaTypeParameter): FirTypeParameterSymbol {
|
||||
return safeGet(javaTypeParameter)
|
||||
return typeParameterMap[javaTypeParameter]
|
||||
?: throw IllegalArgumentException("Cannot find Java type parameter $javaTypeParameter in stack")
|
||||
}
|
||||
|
||||
fun safeGet(javaTypeParameter: JavaTypeParameter) = typeParameterMap[javaTypeParameter]
|
||||
|
||||
companion object {
|
||||
val EMPTY: JavaTypeParameterStack = JavaTypeParameterStack()
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder
|
||||
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
|
||||
import org.jetbrains.kotlin.fir.declarations.FirConstructor
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
@@ -18,7 +16,6 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.*
|
||||
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
|
||||
import org.jetbrains.kotlin.fir.java.declarations.buildJavaValueParameter
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
|
||||
@@ -190,20 +187,7 @@ internal fun JavaAnnotation.toFirAnnotationCall(
|
||||
}
|
||||
}
|
||||
|
||||
@FirBuilderDsl
|
||||
internal fun FirAnnotationContainerBuilder.addAnnotationsFrom(
|
||||
session: FirSession, javaAnnotationOwner: JavaAnnotationOwner, javaTypeParameterStack: JavaTypeParameterStack
|
||||
) {
|
||||
annotations.addAnnotationsFrom(session, javaAnnotationOwner, javaTypeParameterStack)
|
||||
}
|
||||
|
||||
internal fun FirJavaClass.addAnnotationsFrom(
|
||||
session: FirSession, javaAnnotationOwner: JavaAnnotationOwner, javaTypeParameterStack: JavaTypeParameterStack
|
||||
) {
|
||||
annotations.addAnnotationsFrom(session, javaAnnotationOwner, javaTypeParameterStack)
|
||||
}
|
||||
|
||||
internal fun MutableList<FirAnnotationCall>.addAnnotationsFrom(
|
||||
internal fun MutableList<FirAnnotationCall>.addFromJava(
|
||||
session: FirSession,
|
||||
javaAnnotationOwner: JavaAnnotationOwner,
|
||||
javaTypeParameterStack: JavaTypeParameterStack
|
||||
|
||||
+58
-13
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.java.FirJavaTypeConversionMode
|
||||
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
|
||||
import org.jetbrains.kotlin.fir.java.declarations.*
|
||||
import org.jetbrains.kotlin.fir.java.resolveIfJavaType
|
||||
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
|
||||
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
@@ -178,6 +179,7 @@ class FirSignatureEnhancement(
|
||||
if (firMethod !is FirJavaMethod && firMethod !is FirJavaConstructor) {
|
||||
return original
|
||||
}
|
||||
enhanceTypeParameterBounds(firMethod.typeParameters)
|
||||
return enhanceMethod(firMethod, original.callableId, name)
|
||||
}
|
||||
|
||||
@@ -311,6 +313,44 @@ class FirSignatureEnhancement(
|
||||
return function.symbol
|
||||
}
|
||||
|
||||
fun enhanceTypeParameterBounds(typeParameters: List<FirTypeParameterRef>) {
|
||||
// Type parameters can have interdependencies between them. Assuming that there are no top-level cycles
|
||||
// (`A : B, B : A` - invalid), the cycles can still appear when type parameters use each other in argument
|
||||
// position (`A : C<B>, B : D<A>` - valid). In this case the precise enhancement of each bound depends on
|
||||
// the others' nullability, for which we need to enhance at least its head type constructor.
|
||||
//
|
||||
// While this is straightforward to do within a single class/method (enhance all bounds' head type
|
||||
// constructors, then enhance fully), it's not so simple when two classes depend on each other (we need
|
||||
// to enhance *both* classes' type parameters' bounds' heads first). This is why we replace each bound
|
||||
// with an unenhanced version first: this ensures that the frontend at least doesn't fail.
|
||||
//
|
||||
// TODO: find a way to partially enhance type parameters of all classes before fully enhancing anything.
|
||||
// TODO: should this be done in topological order on head type constructors?
|
||||
// I.e. for `A : B, B : C<A>` should we process `B` first?
|
||||
typeParameters.replaceBounds { _, bound ->
|
||||
bound.resolveIfJavaType(session, javaTypeParameterStack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND)
|
||||
}
|
||||
typeParameters.replaceBounds { typeParameter, bound ->
|
||||
enhanceTypeParameterBound(typeParameter, bound, forceOnlyHeadTypeConstructor = true)
|
||||
}
|
||||
typeParameters.replaceBounds { typeParameter, bound ->
|
||||
enhanceTypeParameterBound(typeParameter, bound, forceOnlyHeadTypeConstructor = false)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun List<FirTypeParameterRef>.replaceBounds(block: (FirTypeParameter, FirTypeRef) -> FirTypeRef) {
|
||||
for (typeParameter in this) {
|
||||
if (typeParameter is FirTypeParameter) {
|
||||
typeParameter.replaceBounds(typeParameter.bounds.map { block(typeParameter, it) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enhanceTypeParameterBound(typeParameter: FirTypeParameter, bound: FirTypeRef, forceOnlyHeadTypeConstructor: Boolean) =
|
||||
EnhancementSignatureParts(
|
||||
session, typeQualifierResolver, typeParameter, isCovariant = false, forceOnlyHeadTypeConstructor,
|
||||
AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS, context.defaultTypeQualifiers
|
||||
).enhance(bound, emptyList(), FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND)
|
||||
|
||||
// ================================================================================================
|
||||
|
||||
@@ -361,10 +401,9 @@ class FirSignatureEnhancement(
|
||||
predefinedEnhancementInfo: PredefinedFunctionEnhancementInfo?
|
||||
): FirResolvedTypeRef {
|
||||
return owner.enhance(
|
||||
typeQualifierResolver,
|
||||
overriddenMembers,
|
||||
typeContainer = owner, isCovariant = true,
|
||||
containerContext = memberContext,
|
||||
typeContainer = owner,
|
||||
isCovariant = true, containerContext = memberContext,
|
||||
containerApplicabilityType =
|
||||
if (owner is FirJavaField) AnnotationQualifierApplicabilityType.FIELD
|
||||
else AnnotationQualifierApplicabilityType.METHOD_RETURN_TYPE,
|
||||
@@ -408,10 +447,9 @@ class FirSignatureEnhancement(
|
||||
predefined: TypeEnhancementInfo?,
|
||||
forAnnotationMember: Boolean
|
||||
): FirResolvedTypeRef = (this as FirCallableDeclaration).enhance(
|
||||
typeQualifierResolver,
|
||||
overriddenMembers,
|
||||
parameterContainer, false,
|
||||
parameterContainer?.let {
|
||||
parameterContainer,
|
||||
false, parameterContainer?.let {
|
||||
methodContext.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, it.annotations)
|
||||
} ?: methodContext,
|
||||
AnnotationQualifierApplicabilityType.VALUE_PARAMETER,
|
||||
@@ -421,7 +459,6 @@ class FirSignatureEnhancement(
|
||||
)
|
||||
|
||||
private fun FirCallableDeclaration.enhance(
|
||||
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
overriddenMembers: List<FirCallableDeclaration>,
|
||||
typeContainer: FirAnnotationContainer?,
|
||||
isCovariant: Boolean,
|
||||
@@ -431,15 +468,22 @@ class FirSignatureEnhancement(
|
||||
predefined: TypeEnhancementInfo?,
|
||||
forAnnotationMember: Boolean
|
||||
): FirResolvedTypeRef {
|
||||
val parts = EnhancementSignatureParts(
|
||||
session, typeQualifierResolver, typeContainer, isCovariant,
|
||||
containerApplicabilityType, containerContext.defaultTypeQualifiers
|
||||
)
|
||||
val typeRef = typeInSignature.getTypeRef(this)
|
||||
val typeRefsFromOverridden = overriddenMembers.map { typeInSignature.getTypeRef(it) }
|
||||
val mode = if (forAnnotationMember) FirJavaTypeConversionMode.ANNOTATION_MEMBER else FirJavaTypeConversionMode.DEFAULT
|
||||
return EnhancementSignatureParts(
|
||||
session, typeQualifierResolver, typeContainer, isCovariant, forceOnlyHeadTypeConstructor = false,
|
||||
containerApplicabilityType, containerContext.defaultTypeQualifiers
|
||||
).enhance(typeRef, typeRefsFromOverridden, mode, predefined)
|
||||
}
|
||||
|
||||
private fun EnhancementSignatureParts.enhance(
|
||||
typeRef: FirTypeRef, typeRefsFromOverridden: List<FirTypeRef>,
|
||||
mode: FirJavaTypeConversionMode, predefined: TypeEnhancementInfo? = null
|
||||
): FirResolvedTypeRef {
|
||||
val typeWithoutEnhancement = typeRef.toConeKotlinType(mode)
|
||||
val typesFromOverridden = overriddenMembers.map { typeInSignature.getTypeRef(it).toConeKotlinType(mode) }
|
||||
val qualifiers = with(parts) { typeWithoutEnhancement.computeIndexedQualifiers(typesFromOverridden, predefined) }
|
||||
val typesFromOverridden = typeRefsFromOverridden.map { it.toConeKotlinType(mode) }
|
||||
val qualifiers = typeWithoutEnhancement.computeIndexedQualifiers(typesFromOverridden, predefined)
|
||||
return buildResolvedTypeRef {
|
||||
type = typeWithoutEnhancement.enhance(session, qualifiers) ?: typeWithoutEnhancement
|
||||
annotations += typeRef.annotations
|
||||
@@ -455,6 +499,7 @@ private class EnhancementSignatureParts(
|
||||
override val annotationTypeQualifierResolver: FirAnnotationTypeQualifierResolver,
|
||||
private val typeContainer: FirAnnotationContainer?,
|
||||
override val isCovariant: Boolean,
|
||||
override val forceOnlyHeadTypeConstructor: Boolean,
|
||||
override val containerApplicabilityType: AnnotationQualifierApplicabilityType,
|
||||
override val containerDefaultTypeQualifiers: JavaTypeQualifiersByElementType?
|
||||
) : AbstractSignatureParts<FirAnnotationCall>() {
|
||||
|
||||
Reference in New Issue
Block a user