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:
pyos
2021-08-12 18:55:44 +02:00
committed by teamcityserver
parent e2a48d3945
commit 03304bce96
30 changed files with 222 additions and 382 deletions
@@ -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
@@ -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>() {