FIR: attach annotations to Java-converted cone types

This commit is contained in:
pyos
2021-08-11 16:16:42 +02:00
committed by teamcityserver
parent 5355d04553
commit 5704dffdbe
20 changed files with 122 additions and 332 deletions
@@ -62,33 +62,6 @@ import java.lang.annotation.Documented
import java.lang.annotation.Retention
import java.lang.annotation.Target
import java.util.*
import kotlin.Any
import kotlin.Array
import kotlin.Boolean
import kotlin.BooleanArray
import kotlin.Byte
import kotlin.ByteArray
import kotlin.Char
import kotlin.CharArray
import kotlin.Double
import kotlin.DoubleArray
import kotlin.Float
import kotlin.FloatArray
import kotlin.Int
import kotlin.IntArray
import kotlin.Long
import kotlin.LongArray
import kotlin.Short
import kotlin.ShortArray
import kotlin.String
import kotlin.Suppress
import kotlin.also
import kotlin.arrayOf
import kotlin.emptyArray
import kotlin.error
import kotlin.let
import kotlin.run
import kotlin.to
internal val JavaModifierListOwner.modality: Modality
get() = when {
@@ -157,7 +130,7 @@ internal fun JavaClassifierType.toFirResolvedTypeRef(
): FirResolvedTypeRef {
val coneType =
if (isForSupertypes)
toConeKotlinTypeForFlexibleBound(session, javaTypeParameterStack, isLowerBound = true, forTypeParameterBounds, isForSupertypes)
toConeKotlinTypeForFlexibleBound(session, javaTypeParameterStack, forTypeParameterBounds, isForSupertypes)
else
toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forTypeParameterBounds, isForSupertypes)
@@ -171,9 +144,15 @@ internal fun JavaType?.toConeKotlinTypeWithoutEnhancement(
session: FirSession,
javaTypeParameterStack: JavaTypeParameterStack,
forAnnotationMember: Boolean = false,
isForSupertypes: Boolean = false,
attributes: ConeAttributes = ConeAttributes.Empty
isForSupertypes: Boolean = false
): ConeKotlinType {
val attributes = if (this != null && annotations.isNotEmpty()) {
ConeAttributes.create(
listOf(CustomAnnotationTypeAttribute(annotations.map { it.toFirAnnotationCall(session, javaTypeParameterStack) }))
)
} else {
ConeAttributes.Empty
}
return when (this) {
is JavaClassifierType -> {
toConeKotlinTypeWithoutEnhancement(
@@ -206,14 +185,10 @@ internal fun JavaType?.toConeKotlinTypeWithoutEnhancement(
bound?.toConeKotlinTypeWithoutEnhancement(
session,
javaTypeParameterStack,
isForSupertypes = isForSupertypes,
attributes = attributes
) ?: run {
StandardClassIds.Any.toConeFlexibleType(emptyArray())
}
null -> {
StandardClassIds.Any.toConeFlexibleType(emptyArray())
}
isForSupertypes = isForSupertypes
) ?: StandardClassIds.Any.toConeFlexibleType(emptyArray(), attributes = attributes)
null ->
StandardClassIds.Any.toConeFlexibleType(emptyArray(), attributes = attributes)
else -> error("Strange JavaType: ${this::class.java}")
}
}
@@ -257,11 +232,7 @@ private fun ClassId.toConeFlexibleType(
typeArgumentsForUpper: Array<ConeTypeProjection> = typeArguments,
attributes: ConeAttributes = ConeAttributes.Empty
) = ConeFlexibleType(
toConeKotlinType(
typeArguments,
isNullable = false,
attributes
),
toConeKotlinType(typeArguments, isNullable = false, attributes),
toConeKotlinType(typeArgumentsForUpper, isNullable = true, attributes)
)
@@ -276,7 +247,6 @@ private fun JavaClassifierType.toConeKotlinTypeWithoutEnhancement(
val lowerBound = toConeKotlinTypeForFlexibleBound(
session,
javaTypeParameterStack,
isLowerBound = true,
forTypeParameterBounds,
isForSupertypes,
forAnnotationMember = forAnnotationMember,
@@ -289,7 +259,6 @@ private fun JavaClassifierType.toConeKotlinTypeWithoutEnhancement(
toConeKotlinTypeForFlexibleBound(
session,
javaTypeParameterStack,
isLowerBound = false,
forTypeParameterBounds,
isForSupertypes,
lowerBound,
@@ -398,7 +367,6 @@ private fun getErasedVersionOfFirstUpperBound(
private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound(
session: FirSession,
javaTypeParameterStack: JavaTypeParameterStack,
isLowerBound: Boolean,
forTypeParameterBounds: Boolean,
isForSupertypes: Boolean,
lowerBound: ConeLookupTagBasedType? = null,
@@ -414,15 +382,13 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound(
JavaToKotlinClassMap.mapJavaToKotlin(classifier.fqName!!)
} ?: classifier.classId!!
if (isLowerBound || argumentsMakeSenseOnlyForMutableContainer(classId, session)) {
if (lowerBound == null || argumentsMakeSenseOnlyForMutableContainer(classId, session)) {
classId = classId.readOnlyToMutable() ?: classId
}
val lookupTag = ConeClassLikeLookupTagImpl(classId)
if (!isLowerBound && !isRaw && lookupTag == lowerBound?.lookupTag) {
return lookupTag.constructClassType(
lowerBound.typeArguments, isNullable = true
)
if (lookupTag == lowerBound?.lookupTag && !isRaw) {
return lookupTag.constructClassType(lowerBound.typeArguments, isNullable = true, attributes)
}
val mappedTypeArguments = if (isRaw) {
@@ -433,7 +399,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound(
// to create a proper raw type arguments, we should take class parameters some time
defaultArgs
} else {
val position = if (isLowerBound) TypeComponentPosition.FLEXIBLE_LOWER else TypeComponentPosition.FLEXIBLE_UPPER
val position = if (lowerBound == null) TypeComponentPosition.FLEXIBLE_LOWER else TypeComponentPosition.FLEXIBLE_UPPER
val classSymbol = session.symbolProvider.getClassLikeSymbolByFqName(classId) as? FirRegularClassSymbol
classSymbol?.fir?.createRawArguments(session, defaultArgs, position) ?: defaultArgs
@@ -453,19 +419,15 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound(
}
}
lookupTag.constructClassType(
mappedTypeArguments.toTypedArray(), isNullable = !isLowerBound, attributes
)
lookupTag.constructClassType(mappedTypeArguments.toTypedArray(), isNullable = lowerBound != null, attributes)
}
is JavaTypeParameter -> {
val symbol = javaTypeParameterStack[classifier]
ConeTypeParameterTypeImpl(symbol.toLookupTag(), isNullable = !isLowerBound)
ConeTypeParameterTypeImpl(symbol.toLookupTag(), isNullable = lowerBound != null, attributes)
}
null -> {
val classId = ClassId.topLevel(FqName(this.classifierQualifiedName))
ConeClassLikeLookupTagImpl(classId).constructClassType(
emptyArray(), isNullable = !isLowerBound, attributes,
)
classId.constructClassLikeType(emptyArray(), isNullable = lowerBound != null, attributes)
}
else -> ConeKotlinErrorType(ConeSimpleDiagnostic("Unexpected classifier: $classifier", DiagnosticKind.Java))
}
@@ -681,9 +643,7 @@ private fun JavaType?.toConeProjectionWithoutEnhancement(
}
}
}
is JavaClassifierType -> toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes)
is JavaArrayType -> toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes)
else -> ConeClassErrorType(ConeSimpleDiagnostic("Unexpected type argument: $this", DiagnosticKind.Java))
else -> toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes)
}
}
@@ -12,16 +12,11 @@ import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeWithoutEnhancement
import org.jetbrains.kotlin.fir.java.toFirJavaTypeRef
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.structure.JavaClassifierType
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
import org.jetbrains.kotlin.load.java.structure.JavaWildcardType
import org.jetbrains.kotlin.load.java.typeEnhancement.*
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -38,8 +33,6 @@ internal class EnhancementSignatureParts(
) {
private val isForVarargParameter get() = typeContainer.safeAs<FirValueParameter>()?.isVararg == true
private val attributesCache = mutableMapOf<FirTypeRef?, ConeAttributes>().withDefault { ConeAttributes.Empty }
private fun ConeKotlinType.toFqNameUnsafe(): FqNameUnsafe? =
((this as? ConeLookupTagBasedType)?.lookupTag as? ConeClassLikeLookupTag)?.classId?.asSingleFqName()?.toUnsafe()
@@ -48,7 +41,10 @@ internal class EnhancementSignatureParts(
predefined: TypeEnhancementInfo? = null,
forAnnotationMember: Boolean = false
): PartEnhancementResult {
val qualifiers = computeIndexedQualifiersForOverride(session)
val typeWithoutEnhancement = current.type
.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forAnnotationMember)
val qualifiers = computeIndexedQualifiersForOverride(typeWithoutEnhancement)
val qualifiersWithPredefined = predefined?.let {
IndexedJavaTypeQualifiers(qualifiers.size) { index ->
@@ -56,12 +52,6 @@ internal class EnhancementSignatureParts(
}
}
val typeWithoutEnhancement = current.type.toConeKotlinTypeWithoutEnhancement(
session,
javaTypeParameterStack,
forAnnotationMember,
attributes = attributesCache.getValue(current)
)
val containsFunctionN = typeWithoutEnhancement.contains {
if (it is ConeClassErrorType) false
else {
@@ -87,43 +77,31 @@ internal class EnhancementSignatureParts(
}
}
private fun FirTypeRef.toConeKotlinType(session: FirSession): ConeKotlinType? =
when (this) {
is FirResolvedTypeRef -> type
is FirJavaTypeRef -> type.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack)
else -> null
}
private fun FirTypeRef.toIndexed(
typeQualifierResolver: FirAnnotationTypeQualifierResolver,
context: FirJavaEnhancementContext
): List<TypeAndDefaultQualifiers> {
private fun ConeKotlinType?.toIndexed(context: FirJavaEnhancementContext): List<TypeAndDefaultQualifiers> {
val list = ArrayList<TypeAndDefaultQualifiers>(1)
fun add(type: FirTypeRef?) {
fun add(type: ConeKotlinType?) {
// TODO: should use the context from parent type
val c = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, type?.annotations.orEmpty())
val annotations = type?.attributes?.customAnnotations.orEmpty()
val c = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, annotations)
list.add(TypeAndDefaultQualifiers(type, c.defaultTypeQualifiers?.get(AnnotationQualifierApplicabilityType.TYPE_USE)))
when (type) {
is FirJavaTypeRef -> {
for (arg in type.type.typeArguments()) {
add(arg.takeIf { it !is JavaWildcardType }?.toFirJavaTypeRef(context.session, javaTypeParameterStack))
}
}
is FirUserTypeRef -> {
for (arg in type.qualifier.lastOrNull()?.typeArgumentList?.typeArguments.orEmpty()) {
add((arg as? FirTypeProjectionWithVariance)?.typeRef)
}
}
is FirResolvedTypeRef -> {
for (arg in type.type.typeArguments) {
add(arg.type?.let { buildResolvedTypeRef { this.type = it } })
}
}
else -> Unit
}
type?.typeArguments?.forEach { add(it.type) }
}
add(this)
return list
}
private fun extractQualifiers(lower: ConeKotlinType, upper: ConeKotlinType): JavaTypeQualifiers {
private fun ConeKotlinType.extractQualifiers(): JavaTypeQualifiers {
val lower = lowerBoundIfFlexible()
val upper = upperBoundIfFlexible()
val mapping = JavaToKotlinClassMap
return JavaTypeQualifiers(
when {
@@ -140,29 +118,6 @@ internal class EnhancementSignatureParts(
)
}
private fun FirTypeRef.extractQualifiers(session: FirSession): JavaTypeQualifiers {
val (lower, upper) = when (this) {
is FirResolvedTypeRef -> {
val type = this.type
if (type is ConeFlexibleType) {
Pair(type.lowerBound, type.upperBound)
} else {
Pair(type, type)
}
}
is FirJavaTypeRef -> {
val convertedType = type.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack)
Pair(
convertedType.lowerBoundIfFlexible(),
convertedType.upperBoundIfFlexible()
)
}
else -> return JavaTypeQualifiers.NONE
}
return extractQualifiers(lower, upper)
}
private fun composeAnnotations(first: List<FirAnnotationCall>, second: List<FirAnnotationCall>): List<FirAnnotationCall> {
return when {
first.isEmpty() -> second
@@ -171,43 +126,35 @@ internal class EnhancementSignatureParts(
}
}
private fun FirTypeRef?.extractQualifiersFromAnnotations(
isHeadTypeConstructor: Boolean,
defaultQualifiersForType: JavaDefaultQualifiers?
): JavaTypeQualifiers {
val composedAnnotation =
private fun TypeAndDefaultQualifiers.extractQualifiersFromAnnotations(isHeadTypeConstructor: Boolean): JavaTypeQualifiers {
val annotations = type?.attributes?.customAnnotations.orEmpty()
val composedAnnotations =
if (isHeadTypeConstructor && typeContainer != null)
composeAnnotations(typeContainer.annotations, this?.annotations.orEmpty())
composeAnnotations(typeContainer.annotations, annotations)
else
this?.annotations.orEmpty()
annotations
val defaultTypeQualifier =
if (isHeadTypeConstructor)
context.defaultTypeQualifiers?.get(containerApplicabilityType)
else
defaultQualifiersForType
defaultQualifiers
val nullabilityInfo = typeQualifierResolver.extractNullability(composedAnnotation).also {
if (it?.qualifier == NullabilityQualifier.NOT_NULL) {
attributesCache[this] = composedAnnotation.computeTypeAttributesForJavaType()
}
} ?: defaultTypeQualifier?.nullabilityQualifier
val nullabilityInfo = typeQualifierResolver.extractNullability(composedAnnotations)
?: defaultTypeQualifier?.nullabilityQualifier
@Suppress("SimplifyBooleanWithConstants")
return JavaTypeQualifiers(
nullabilityInfo?.qualifier,
typeQualifierResolver.extractMutability(composedAnnotation),
isNotNullTypeParameter = nullabilityInfo?.qualifier == NullabilityQualifier.NOT_NULL && this.isTypeParameterBasedType(),
typeQualifierResolver.extractMutability(composedAnnotations),
isNotNullTypeParameter = nullabilityInfo?.qualifier == NullabilityQualifier.NOT_NULL &&
type?.lowerBoundIfFlexible() is ConeTypeParameterType,
isNullabilityQualifierForWarning = nullabilityInfo?.isForWarningOnly == true
)
}
private fun FirTypeRef?.isTypeParameterBasedType() =
((this as? FirJavaTypeRef)?.type as? JavaClassifierType)?.classifier is JavaTypeParameter
private fun computeIndexedQualifiersForOverride(session: FirSession): IndexedJavaTypeQualifiers {
val indexedFromSupertypes = fromOverridden.map { it.toIndexed(typeQualifierResolver, context) }
val indexedThisType = current.toIndexed(typeQualifierResolver, context)
private fun computeIndexedQualifiersForOverride(current: ConeKotlinType?): IndexedJavaTypeQualifiers {
val indexedFromSupertypes = fromOverridden.map { it.toConeKotlinType(context.session).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
@@ -218,9 +165,8 @@ internal class EnhancementSignatureParts(
val treeSize = if (onlyHeadTypeConstructor) 1 else indexedThisType.size
val computedResult = Array(treeSize) { index ->
val (type, defaultQualifiers) = indexedThisType[index]
val qualifiers = type.extractQualifiersFromAnnotations(index == 0, defaultQualifiers)
val superQualifiers = indexedFromSupertypes.mapNotNull { it.getOrNull(index)?.type?.extractQualifiers(session) }
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)
}
@@ -233,4 +179,9 @@ internal class EnhancementSignatureParts(
val wereChanges: Boolean,
val containsFunctionN: Boolean
)
private data class TypeAndDefaultQualifiers(
val type: ConeKotlinType?, // null denotes '*' here
val defaultQualifiers: JavaDefaultQualifiers?
)
}
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.utils.collectEnumEntries
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildPropertyAccessExpression
@@ -29,7 +28,6 @@ import org.jetbrains.kotlin.fir.typeContext
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.*
import org.jetbrains.kotlin.load.java.structure.JavaClassifierType
import org.jetbrains.kotlin.load.java.structure.JavaType
import org.jetbrains.kotlin.load.java.typeEnhancement.*
@@ -224,12 +222,6 @@ private fun ConeClassifierLookupTag.enhanceMutability(
return this
}
internal data class TypeAndDefaultQualifiers(
val type: FirTypeRef?, // null denotes '*' here
val defaultQualifiers: JavaDefaultQualifiers?
)
internal fun JavaType.typeArguments(): List<JavaType?> = (this as? JavaClassifierType)?.typeArguments.orEmpty()
internal fun ConeKotlinType.lexicalCastFrom(session: FirSession, value: String): FirExpression? {
@@ -282,22 +274,3 @@ internal fun ConeKotlinType.lexicalCastFrom(session: FirSession, value: String):
else -> null
}
}
internal fun List<FirAnnotationCall>.computeTypeAttributesForJavaType(): ConeAttributes =
computeTypeAttributes { classId ->
when (classId) {
CompilerConeAttributes.EnhancedNullability.ANNOTATION_CLASS_ID -> add(CompilerConeAttributes.EnhancedNullability)
in NOT_NULL_ANNOTATION_IDS -> add(CompilerConeAttributes.EnhancedNullability)
JAVAX_NONNULL_ANNOTATION_ID,
JAVAX_CHECKFORNULL_ANNOTATION_ID,
COMPATQUAL_NONNULL_ANNOTATION_ID,
ANDROIDX_RECENTLY_NON_NULL_ANNOTATION_ID
-> add(CompilerConeAttributes.EnhancedNullability)
}
}
private val NOT_NULL_ANNOTATION_IDS = NOT_NULL_ANNOTATIONS.map { ClassId.topLevel(it) }
private val JAVAX_NONNULL_ANNOTATION_ID = ClassId.topLevel(JAVAX_NONNULL_ANNOTATION)
private val JAVAX_CHECKFORNULL_ANNOTATION_ID = ClassId.topLevel(JAVAX_CHECKFORNULL_ANNOTATION)
private val COMPATQUAL_NONNULL_ANNOTATION_ID = ClassId.topLevel(COMPATQUAL_NONNULL_ANNOTATION)
private val ANDROIDX_RECENTLY_NON_NULL_ANNOTATION_ID = ClassId.topLevel(ANDROIDX_RECENTLY_NON_NULL_ANNOTATION)