FIR: use frontend-independent Java type qualifier computation

This commit is contained in:
pyos
2021-08-11 21:20:15 +02:00
committed by teamcityserver
parent e385484994
commit e2a48d3945
14 changed files with 149 additions and 249 deletions
@@ -1,148 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.java.enhancement
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
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.toConeKotlinTypeProbablyFlexible
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
import org.jetbrains.kotlin.load.java.AnnotationQualifierApplicabilityType
import org.jetbrains.kotlin.load.java.JavaDefaultQualifiers
import org.jetbrains.kotlin.load.java.typeEnhancement.*
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class EnhancementSignatureParts(
private val typeQualifierResolver: FirAnnotationTypeQualifierResolver,
private val typeContainer: FirAnnotationContainer?,
private val javaTypeParameterStack: JavaTypeParameterStack,
private val current: FirJavaTypeRef,
private val fromOverridden: Collection<FirTypeRef>,
private val isCovariant: Boolean,
private val context: FirJavaEnhancementContext,
private val containerApplicabilityType: AnnotationQualifierApplicabilityType
) {
private val isForVarargParameter get() = typeContainer.safeAs<FirValueParameter>()?.isVararg == true
private fun ConeKotlinType.toFqNameUnsafe(): FqNameUnsafe? =
((this as? ConeLookupTagBasedType)?.lookupTag as? ConeClassLikeLookupTag)?.classId?.asSingleFqName()?.toUnsafe()
internal fun enhance(
session: FirSession,
predefined: TypeEnhancementInfo? = null,
forAnnotationMember: Boolean = false
): FirResolvedTypeRef {
val mode = if (forAnnotationMember) FirJavaTypeConversionMode.ANNOTATION_MEMBER else FirJavaTypeConversionMode.DEFAULT
val typeWithoutEnhancement = current.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack, mode)
val qualifiers = computeIndexedQualifiersForOverride(typeWithoutEnhancement, predefined)
return buildResolvedTypeRef {
type = typeWithoutEnhancement.enhance(session, qualifiers) ?: typeWithoutEnhancement
annotations += current.annotations
}
}
private fun ConeKotlinType?.toIndexed(context: FirJavaEnhancementContext): List<TypeAndDefaultQualifiers> {
val list = ArrayList<TypeAndDefaultQualifiers>(1)
fun add(type: ConeKotlinType?) {
// TODO: should use the context from parent type
val annotations = type?.attributes?.customAnnotations.orEmpty()
val c = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, annotations)
list.add(TypeAndDefaultQualifiers(type, c.defaultTypeQualifiers?.get(AnnotationQualifierApplicabilityType.TYPE_USE)))
type?.typeArguments?.forEach { add(it.type) }
}
add(this)
return list
}
private fun ConeKotlinType.extractQualifiers(): JavaTypeQualifiers {
val lower = lowerBoundIfFlexible()
val upper = upperBoundIfFlexible()
val mapping = JavaToKotlinClassMap
return JavaTypeQualifiers(
when {
lower.isMarkedNullable -> NullabilityQualifier.NULLABLE
!upper.isMarkedNullable -> NullabilityQualifier.NOT_NULL
else -> null
},
when {
mapping.isReadOnly(lower.toFqNameUnsafe()) -> MutabilityQualifier.READ_ONLY
mapping.isMutable(upper.toFqNameUnsafe()) -> MutabilityQualifier.MUTABLE
else -> null
},
isNotNullTypeParameter = lower is ConeDefinitelyNotNullType
)
}
private fun composeAnnotations(first: List<FirAnnotationCall>, second: List<FirAnnotationCall>): List<FirAnnotationCall> {
return when {
first.isEmpty() -> second
second.isEmpty() -> first
else -> first + second
}
}
private fun TypeAndDefaultQualifiers.extractQualifiersFromAnnotations(isHeadTypeConstructor: Boolean): JavaTypeQualifiers {
val annotations = type?.attributes?.customAnnotations.orEmpty()
val composedAnnotations =
if (isHeadTypeConstructor && typeContainer != null)
composeAnnotations(typeContainer.annotations, annotations)
else
annotations
val defaultTypeQualifier =
if (isHeadTypeConstructor)
context.defaultTypeQualifiers?.get(containerApplicabilityType)
else
defaultQualifiers
val nullabilityInfo = typeQualifierResolver.extractNullability(composedAnnotations)
?: defaultTypeQualifier?.nullabilityQualifier
return JavaTypeQualifiers(
nullabilityInfo?.qualifier,
typeQualifierResolver.extractMutability(composedAnnotations),
isNotNullTypeParameter = nullabilityInfo?.qualifier == NullabilityQualifier.NOT_NULL &&
type?.lowerBoundIfFlexible() is ConeTypeParameterType,
isNullabilityQualifierForWarning = nullabilityInfo?.isForWarningOnly == true
)
}
private fun computeIndexedQualifiersForOverride(current: ConeKotlinType?, predefined: TypeEnhancementInfo?): IndexedJavaTypeQualifiers {
val indexedFromSupertypes =
fromOverridden.map { it.toConeKotlinTypeProbablyFlexible(context.session, javaTypeParameterStack).toIndexed(context) }
val indexedThisType = current.toIndexed(context)
// The covariant case may be hard, e.g. in the superclass the return may be Super<T>, but in the subclass it may be Derived, which
// is declared to extend Super<T>, and propagating data here is highly non-trivial, so we only look at the head type constructor
// (outermost type), unless the type in the subclass is interchangeable with the all the types in superclasses:
// e.g. we have (Mutable)List<String!>! in the subclass and { List<String!>, (Mutable)List<String>! } from superclasses
// Note that `this` is flexible here, so it's equal to it's bounds
val onlyHeadTypeConstructor = isCovariant && fromOverridden.any { true /*equalTypes(it, this)*/ }
val treeSize = if (onlyHeadTypeConstructor) 1 else indexedThisType.size
val computedResult = Array(treeSize) { index ->
val qualifiers = indexedThisType[index].extractQualifiersFromAnnotations(index == 0)
val superQualifiers = indexedFromSupertypes.mapNotNull { it.getOrNull(index)?.type?.extractQualifiers() }
qualifiers.computeQualifiersForOverride(superQualifiers, index == 0 && isCovariant, index == 0 && isForVarargParameter)
}
return { index -> predefined?.map?.get(index) ?: computedResult.getOrNull(index) ?: JavaTypeQualifiers.NONE }
}
private data class TypeAndDefaultQualifiers(
val type: ConeKotlinType?, // null denotes '*' here
val defaultQualifiers: JavaDefaultQualifiers?
)
}
@@ -21,13 +21,17 @@ import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.declarations.utils.visibility
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.toConeKotlinTypeProbablyFlexible
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
import org.jetbrains.kotlin.load.java.AnnotationQualifierApplicabilityType
import org.jetbrains.kotlin.load.java.typeEnhancement.*
@@ -35,6 +39,12 @@ import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.load.java.JavaTypeEnhancementState
import org.jetbrains.kotlin.load.java.JavaTypeQualifiersByElementType
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.kotlin.types.model.TypeSystemContext
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class FirSignatureEnhancement(
@@ -170,7 +180,7 @@ class FirSignatureEnhancement(
}
return enhanceMethod(firMethod, original.callableId, name)
}
private fun enhanceMethod(
firMethod: FirFunction,
methodId: CallableId,
@@ -309,14 +319,16 @@ class FirSignatureEnhancement(
overriddenMembers: List<FirCallableDeclaration>,
memberContext: FirJavaEnhancementContext
): FirResolvedTypeRef {
return ownerFunction.partsForValueParameter(
return ownerFunction.enhanceValueParameter(
typeQualifierResolver,
overriddenMembers,
// TODO: check me
parameterContainer = ownerFunction,
methodContext = memberContext,
typeInSignature = TypeInSignature.Receiver
).enhance(session)
typeInSignature = TypeInSignature.Receiver,
predefined = null,
forAnnotationMember = false
)
}
private fun enhanceValueParameterType(
@@ -331,15 +343,13 @@ class FirSignatureEnhancement(
if (ownerParameter.returnTypeRef is FirResolvedTypeRef) {
return ownerParameter.returnTypeRef as FirResolvedTypeRef
}
return ownerFunction.partsForValueParameter(
return ownerFunction.enhanceValueParameter(
typeQualifierResolver,
overriddenMembers,
parameterContainer = ownerParameter,
methodContext = memberContext,
typeInSignature = TypeInSignature.ValueParameter(hasReceiver, index)
).enhance(
session,
predefinedEnhancementInfo?.parametersInfo?.getOrNull(index),
typeInSignature = TypeInSignature.ValueParameter(hasReceiver, index),
predefined = predefinedEnhancementInfo?.parametersInfo?.getOrNull(index),
forAnnotationMember = owner.classKind == ClassKind.ANNOTATION_CLASS
)
}
@@ -350,7 +360,7 @@ class FirSignatureEnhancement(
memberContext: FirJavaEnhancementContext,
predefinedEnhancementInfo: PredefinedFunctionEnhancementInfo?
): FirResolvedTypeRef {
return owner.parts(
return owner.enhance(
typeQualifierResolver,
overriddenMembers,
typeContainer = owner, isCovariant = true,
@@ -358,9 +368,9 @@ class FirSignatureEnhancement(
containerApplicabilityType =
if (owner is FirJavaField) AnnotationQualifierApplicabilityType.FIELD
else AnnotationQualifierApplicabilityType.METHOD_RETURN_TYPE,
typeInSignature = TypeInSignature.Return
).enhance(
session, predefinedEnhancementInfo?.returnTypeInfo, forAnnotationMember = this.owner.classKind == ClassKind.ANNOTATION_CLASS
typeInSignature = TypeInSignature.Return,
predefined = predefinedEnhancementInfo?.returnTypeInfo,
forAnnotationMember = this.owner.classKind == ClassKind.ANNOTATION_CLASS
)
}
@@ -388,14 +398,16 @@ class FirSignatureEnhancement(
}
}
private fun FirFunction.partsForValueParameter(
private fun FirFunction.enhanceValueParameter(
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
overriddenMembers: List<FirCallableDeclaration>,
// TODO: investigate if it's really can be a null (check properties' with extension overrides in Java)
parameterContainer: FirAnnotationContainer?,
methodContext: FirJavaEnhancementContext,
typeInSignature: TypeInSignature
): EnhancementSignatureParts = (this as FirCallableDeclaration).parts(
typeInSignature: TypeInSignature,
predefined: TypeEnhancementInfo?,
forAnnotationMember: Boolean
): FirResolvedTypeRef = (this as FirCallableDeclaration).enhance(
typeQualifierResolver,
overriddenMembers,
parameterContainer, false,
@@ -403,33 +415,79 @@ class FirSignatureEnhancement(
methodContext.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, it.annotations)
} ?: methodContext,
AnnotationQualifierApplicabilityType.VALUE_PARAMETER,
typeInSignature
typeInSignature,
predefined,
forAnnotationMember
)
private fun FirCallableDeclaration.parts(
private fun FirCallableDeclaration.enhance(
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
overriddenMembers: List<FirCallableDeclaration>,
typeContainer: FirAnnotationContainer?,
isCovariant: Boolean,
containerContext: FirJavaEnhancementContext,
containerApplicabilityType: AnnotationQualifierApplicabilityType,
typeInSignature: TypeInSignature
): EnhancementSignatureParts {
val typeRef = typeInSignature.getTypeRef(this)
return EnhancementSignatureParts(
typeQualifierResolver,
typeContainer,
javaTypeParameterStack,
typeRef as FirJavaTypeRef,
overriddenMembers.map {
typeInSignature.getTypeRef(it)
},
isCovariant,
// recompute default type qualifiers using type annotations
containerContext.copyWithNewDefaultTypeQualifiers(
typeQualifierResolver, typeRef.annotations
),
containerApplicabilityType
typeInSignature: TypeInSignature,
predefined: TypeEnhancementInfo?,
forAnnotationMember: Boolean
): FirResolvedTypeRef {
val parts = EnhancementSignatureParts(
session, typeQualifierResolver, typeContainer, isCovariant,
containerApplicabilityType, containerContext.defaultTypeQualifiers
)
val typeRef = typeInSignature.getTypeRef(this)
val mode = if (forAnnotationMember) FirJavaTypeConversionMode.ANNOTATION_MEMBER else FirJavaTypeConversionMode.DEFAULT
val typeWithoutEnhancement = typeRef.toConeKotlinType(mode)
val typesFromOverridden = overriddenMembers.map { typeInSignature.getTypeRef(it).toConeKotlinType(mode) }
val qualifiers = with(parts) { typeWithoutEnhancement.computeIndexedQualifiers(typesFromOverridden, predefined) }
return buildResolvedTypeRef {
type = typeWithoutEnhancement.enhance(session, qualifiers) ?: typeWithoutEnhancement
annotations += typeRef.annotations
}
}
private fun FirTypeRef.toConeKotlinType(mode: FirJavaTypeConversionMode): ConeKotlinType =
toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack, mode)
}
private class EnhancementSignatureParts(
private val session: FirSession,
override val annotationTypeQualifierResolver: FirAnnotationTypeQualifierResolver,
private val typeContainer: FirAnnotationContainer?,
override val isCovariant: Boolean,
override val containerApplicabilityType: AnnotationQualifierApplicabilityType,
override val containerDefaultTypeQualifiers: JavaTypeQualifiersByElementType?
) : AbstractSignatureParts<FirAnnotationCall>() {
override val enableImprovementsInStrictMode: Boolean
get() = true
override val skipRawTypeArguments: Boolean
get() = false
override val containerAnnotations: Iterable<FirAnnotationCall>
get() = typeContainer?.annotations ?: emptyList()
override val containerIsVarargParameter: Boolean
get() = typeContainer is FirValueParameter && typeContainer.isVararg
override val typeSystem: TypeSystemContext
get() = session.typeContext
override val FirAnnotationCall.forceWarning: Boolean
get() = false // TODO: force warnings on IDEA external annotations
override val KotlinTypeMarker.annotations: Iterable<FirAnnotationCall>
get() = (this as ConeKotlinType).attributes.customAnnotations
override val KotlinTypeMarker.fqNameUnsafe: FqNameUnsafe?
get() = ((this as? ConeLookupTagBasedType)?.lookupTag as? ConeClassLikeLookupTag)?.classId?.asSingleFqName()?.toUnsafe()
override val KotlinTypeMarker.enhancedForWarnings: KotlinTypeMarker?
get() = null // TODO: implement enhancement for warnings
override fun KotlinTypeMarker.isEqual(other: KotlinTypeMarker): Boolean =
AbstractTypeChecker.equalTypes(session.typeContext, this, other)
override val TypeParameterMarker.isFromJava: Boolean
get() = (this as ConeTypeParameterLookupTag).symbol.fir.origin == FirDeclarationOrigin.Java
}
@@ -34,8 +34,6 @@ import org.jetbrains.kotlin.types.AbstractStrictEqualityTypeChecker
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.utils.extractRadix
typealias IndexedJavaTypeQualifiers = (Int) -> JavaTypeQualifiers
internal fun ConeKotlinType.enhance(session: FirSession, qualifiers: IndexedJavaTypeQualifiers): ConeKotlinType? =
enhanceConeKotlinType(session, qualifiers, 0, mutableListOf<Int>().apply { computeSubtreeSizes(this) })