diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt new file mode 100644 index 00000000000..718328afb9d --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt @@ -0,0 +1,440 @@ +/* + * Copyright 2010-2021 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 + +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +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.diagnostics.ConeIntermediateDiagnostic +import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic +import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind +import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable +import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents +import org.jetbrains.kotlin.fir.resolve.symbolProvider +import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.firUnsafe +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag +import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl +import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef +import org.jetbrains.kotlin.fir.types.jvm.buildJavaTypeRef +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.load.java.typeEnhancement.TypeComponentPosition +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly +import org.jetbrains.kotlin.utils.addToStdlib.runIf + +private fun ClassId.toLookupTag(): ConeClassLikeLookupTag { + return ConeClassLikeLookupTagImpl(this) +} + +internal fun ClassId.toConeKotlinType( + typeArguments: Array, + isNullable: Boolean, + attributes: ConeAttributes = ConeAttributes.Empty +): ConeLookupTagBasedType { + return ConeClassLikeTypeImpl(toLookupTag(), typeArguments, isNullable, attributes) +} + +internal fun FirTypeRef.toConeKotlinTypeProbablyFlexible( + session: FirSession, javaTypeParameterStack: JavaTypeParameterStack +): ConeKotlinType = + when (this) { + is FirResolvedTypeRef -> type + is FirJavaTypeRef -> type.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = false) + else -> ConeKotlinErrorType( + ConeSimpleDiagnostic("Unexpected type reference in JavaClassUseSiteMemberScope: ${this::class.java}", DiagnosticKind.Java) + ) + } + +internal fun JavaType.toFirJavaTypeRef(session: FirSession, javaTypeParameterStack: JavaTypeParameterStack): FirJavaTypeRef { + return buildJavaTypeRef { + annotationBuilder = { + (this@toFirJavaTypeRef as? JavaClassifierType)?.annotations.orEmpty().map { + it.toFirAnnotationCall(session, javaTypeParameterStack) + } + } + type = this@toFirJavaTypeRef + } +} + +internal fun JavaClassifierType.toFirResolvedTypeRef( + session: FirSession, + javaTypeParameterStack: JavaTypeParameterStack, + isForSupertypes: Boolean, + forTypeParameterBounds: Boolean +): FirResolvedTypeRef { + val coneType = + if (isForSupertypes) + toConeKotlinTypeForFlexibleBound(session, javaTypeParameterStack, forTypeParameterBounds, isForSupertypes) + else + toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forTypeParameterBounds, isForSupertypes) + + return buildResolvedTypeRef { + type = coneType + this@toFirResolvedTypeRef.annotations.mapTo(annotations) { it.toFirAnnotationCall(session, javaTypeParameterStack) } + } +} + +internal fun JavaType?.toConeKotlinTypeWithoutEnhancement( + session: FirSession, + javaTypeParameterStack: JavaTypeParameterStack, + forAnnotationMember: Boolean = false, + 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( + session, + javaTypeParameterStack, + forAnnotationMember = forAnnotationMember, + attributes = attributes + ) + } + is JavaPrimitiveType -> { + val primitiveType = type + val kotlinPrimitiveName = when (val javaName = primitiveType?.typeName?.asString()) { + null -> "Unit" + else -> javaName.capitalizeAsciiOnly() + } + + val classId = StandardClassIds.byName(kotlinPrimitiveName) + classId.toConeKotlinType(emptyArray(), isNullable = false, attributes) + } + is JavaArrayType -> { + toConeKotlinTypeWithoutEnhancement( + session, + javaTypeParameterStack, + forAnnotationMember, + isForSupertypes, + attributes = attributes + ) + } + is JavaWildcardType -> + bound?.toConeKotlinTypeWithoutEnhancement( + session, + javaTypeParameterStack, + isForSupertypes = isForSupertypes + ) ?: StandardClassIds.Any.toConeFlexibleType(emptyArray(), attributes = attributes) + null -> + StandardClassIds.Any.toConeFlexibleType(emptyArray(), attributes = attributes) + else -> error("Strange JavaType: ${this::class.java}") + } +} + +private fun JavaArrayType.toConeKotlinTypeWithoutEnhancement( + session: FirSession, + javaTypeParameterStack: JavaTypeParameterStack, + forAnnotationValueParameter: Boolean = false, + isForSupertypes: Boolean, + attributes: ConeAttributes = ConeAttributes.Empty +): ConeKotlinType { + val componentType = componentType + return if (componentType !is JavaPrimitiveType) { + val classId = StandardClassIds.Array + val argumentType = componentType.toConeKotlinTypeWithoutEnhancement( + session, javaTypeParameterStack, forAnnotationValueParameter, isForSupertypes + ) + if (forAnnotationValueParameter) { + classId.constructClassLikeType(arrayOf(argumentType), isNullable = false, attributes = attributes) + } else { + classId.toConeFlexibleType( + arrayOf(argumentType), + typeArgumentsForUpper = arrayOf(ConeKotlinTypeProjectionOut(argumentType)), + attributes = attributes + ) + } + } else { + val javaComponentName = componentType.type?.typeName?.asString()?.capitalizeAsciiOnly() ?: error("Array of voids") + val classId = StandardClassIds.byName(javaComponentName + "Array") + + if (forAnnotationValueParameter) { + classId.constructClassLikeType(emptyArray(), isNullable = false, attributes = attributes) + } else { + classId.toConeFlexibleType(emptyArray(), attributes = attributes) + } + } +} + +private fun ClassId.toConeFlexibleType( + typeArguments: Array, + typeArgumentsForUpper: Array = typeArguments, + attributes: ConeAttributes = ConeAttributes.Empty +) = ConeFlexibleType( + toConeKotlinType(typeArguments, isNullable = false, attributes), + toConeKotlinType(typeArgumentsForUpper, isNullable = true, attributes) +) + +private fun JavaClassifierType.toConeKotlinTypeWithoutEnhancement( + session: FirSession, + javaTypeParameterStack: JavaTypeParameterStack, + forTypeParameterBounds: Boolean = false, + isForSupertypes: Boolean = false, + forAnnotationMember: Boolean = false, + attributes: ConeAttributes = ConeAttributes.Empty +): ConeKotlinType { + val lowerBound = toConeKotlinTypeForFlexibleBound( + session, + javaTypeParameterStack, + forTypeParameterBounds, + isForSupertypes, + forAnnotationMember = forAnnotationMember, + attributes = attributes + ) + if (forAnnotationMember) { + return lowerBound + } + val upperBound = + toConeKotlinTypeForFlexibleBound( + session, + javaTypeParameterStack, + forTypeParameterBounds, + isForSupertypes, + lowerBound, + forAnnotationMember = forAnnotationMember, + attributes = attributes + ) + + return if (isRaw) + ConeRawType(lowerBound, upperBound) + else + ConeFlexibleType(lowerBound, upperBound) +} + +private fun computeRawProjection( + session: FirSession, + parameter: FirTypeParameter, + attr: TypeComponentPosition, + erasedUpperBound: ConeKotlinType = parameter.getErasedUpperBound(session) +) = when (attr) { + // Raw(List) => (List..List<*>) + // Raw(Enum) => (Enum>..Enum>) + // In the last case upper bound is equal to star projection `Enum<*>`, + // but we want to keep matching tree structure of flexible bounds (at least they should have the same size) + TypeComponentPosition.FLEXIBLE_LOWER -> { + // T : String -> String + // in T : String -> String + // T : Enum -> Enum<*> + erasedUpperBound + } + TypeComponentPosition.FLEXIBLE_UPPER, TypeComponentPosition.INFLEXIBLE -> { + if (!parameter.variance.allowsOutPosition) + // in T -> Comparable + session.builtinTypes.nothingType.type + else if (erasedUpperBound is ConeClassLikeType && + erasedUpperBound.lookupTag.toSymbol(session)!!.firUnsafe().typeParameters.isNotEmpty() + ) + // T : Enum -> out Enum<*> + ConeKotlinTypeProjectionOut(erasedUpperBound) + else + // T : String -> * + ConeStarProjection + } +} + +// Definition: +// ErasedUpperBound(T : G) = G<*> // UpperBound(T) is a type G with arguments +// ErasedUpperBound(T : A) = A // UpperBound(T) is a type A without arguments +// ErasedUpperBound(T : F) = UpperBound(F) // UB(T) is another type parameter F +private fun FirTypeParameter.getErasedUpperBound( + session: FirSession, + // Calculation of `potentiallyRecursiveTypeParameter.upperBounds` may recursively depend on `this.getErasedUpperBound` + // E.g. `class A` + // To prevent recursive calls return defaultValue() instead + potentiallyRecursiveTypeParameter: FirTypeParameter? = null, + defaultValue: (() -> ConeKotlinType) = { + ConeKotlinErrorType(ConeIntermediateDiagnostic("Can't compute erased upper bound of type parameter `$this`")) + } +): ConeKotlinType { + if (this === potentiallyRecursiveTypeParameter) return defaultValue() + + val firstUpperBound = this.bounds.first().coneType + + return getErasedVersionOfFirstUpperBound(session, firstUpperBound, mutableSetOf(this, potentiallyRecursiveTypeParameter), defaultValue) +} + +private fun getErasedVersionOfFirstUpperBound( + session: FirSession, + firstUpperBound: ConeKotlinType, + alreadyVisitedParameters: MutableSet, + defaultValue: () -> ConeKotlinType +): ConeKotlinType = + when (firstUpperBound) { + is ConeClassLikeType -> + firstUpperBound.withArguments(firstUpperBound.typeArguments.map { ConeStarProjection }.toTypedArray()) + + is ConeFlexibleType -> { + val lowerBound = + getErasedVersionOfFirstUpperBound(session, firstUpperBound.lowerBound, alreadyVisitedParameters, defaultValue) + .lowerBoundIfFlexible() + if (firstUpperBound.upperBound is ConeTypeParameterType) { + // Avoid exponential complexity + ConeFlexibleType( + lowerBound, + lowerBound.withNullability(ConeNullability.NULLABLE, session.inferenceComponents.ctx) + ) + } else { + ConeFlexibleType( + lowerBound, + getErasedVersionOfFirstUpperBound(session, firstUpperBound.upperBound, alreadyVisitedParameters, defaultValue) + ) + } + } + is ConeTypeParameterType -> { + val current = firstUpperBound.lookupTag.typeParameterSymbol.fir + + if (alreadyVisitedParameters.add(current)) { + val nextUpperBound = current.bounds.first().coneType + getErasedVersionOfFirstUpperBound(session, nextUpperBound, alreadyVisitedParameters, defaultValue) + } else { + defaultValue() + } + } + else -> error("Unexpected kind of firstUpperBound: $firstUpperBound [${firstUpperBound::class}]") + } + +private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( + session: FirSession, + javaTypeParameterStack: JavaTypeParameterStack, + forTypeParameterBounds: Boolean, + isForSupertypes: Boolean, + lowerBound: ConeLookupTagBasedType? = null, + forAnnotationMember: Boolean = false, + attributes: ConeAttributes = ConeAttributes.Empty +): ConeLookupTagBasedType { + return when (val classifier = classifier) { + is JavaClass -> { + //val classId = classifier.classId!! + var classId = if (forAnnotationMember) { + JavaToKotlinClassMap.mapJavaToKotlinIncludingClassMapping(classifier.fqName!!) + } else { + JavaToKotlinClassMap.mapJavaToKotlin(classifier.fqName!!) + } ?: classifier.classId!! + + if (lowerBound == null || argumentsMakeSenseOnlyForMutableContainer(classId, session)) { + classId = classId.readOnlyToMutable() ?: classId + } + + val lookupTag = ConeClassLikeLookupTagImpl(classId) + if (lookupTag == lowerBound?.lookupTag && !isRaw) { + return lookupTag.constructClassType(lowerBound.typeArguments, isNullable = true, attributes) + } + + val mappedTypeArguments = if (isRaw) { + val defaultArgs = (1..classifier.typeParameters.size).map { ConeStarProjection } + + if (forTypeParameterBounds) { + // This is not fully correct, but it's a simple fix for some time to avoid recursive definition: + // to create a proper raw type arguments, we should take class parameters some time + defaultArgs + } else { + 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 + } + } else { + val typeParameters = runIf(!forTypeParameterBounds && !isForSupertypes) { + val classSymbol = session.symbolProvider.getClassLikeSymbolByFqName(classId) as? FirRegularClassSymbol + classSymbol?.fir?.typeParameters + } ?: emptyList() + + typeArguments.indices.map { index -> + val argument = typeArguments[index] + val parameter = typeParameters.getOrNull(index)?.symbol?.fir + argument.toConeProjectionWithoutEnhancement( + session, javaTypeParameterStack, boundTypeParameter = parameter, isForSupertypes = isForSupertypes + ) + } + } + + lookupTag.constructClassType(mappedTypeArguments.toTypedArray(), isNullable = lowerBound != null, attributes) + } + is JavaTypeParameter -> { + val symbol = javaTypeParameterStack[classifier] + ConeTypeParameterTypeImpl(symbol.toLookupTag(), isNullable = lowerBound != null, attributes) + } + null -> { + val classId = ClassId.topLevel(FqName(this.classifierQualifiedName)) + classId.constructClassLikeType(emptyArray(), isNullable = lowerBound != null, attributes) + } + else -> ConeKotlinErrorType(ConeSimpleDiagnostic("Unexpected classifier: $classifier", DiagnosticKind.Java)) + } +} + +// Returns true for covariant read-only container that has mutable pair with invariant parameter +// List does not make sense, but MutableList does +// Same for Map +// But both Iterable, MutableIterable don't make sense as they are covariant, so return false +private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer( + classId: ClassId, + session: FirSession, +): Boolean { + if (!JavaToKotlinClassMap.isReadOnly(classId.asSingleFqName().toUnsafe())) return false + val mutableClassId = classId.readOnlyToMutable() ?: return false + + if (!typeArguments.lastOrNull().isSuperWildcard()) return false + val mutableLastParameterVariance = + (mutableClassId.toLookupTag().toSymbol(session)?.fir as? FirRegularClass)?.typeParameters?.lastOrNull()?.symbol?.fir?.variance + ?: return false + + return mutableLastParameterVariance != Variance.OUT_VARIANCE +} + + +private fun FirRegularClass.createRawArguments( + session: FirSession, + defaultArgs: List, + position: TypeComponentPosition +): List = typeParameters.filterIsInstance().map { typeParameter -> + val erasedUpperBound = typeParameter.getErasedUpperBound(session) { + defaultType().withArguments(defaultArgs.toTypedArray()) + } + computeRawProjection(session, typeParameter, position, erasedUpperBound) +} + +private fun JavaType?.toConeProjectionWithoutEnhancement( + session: FirSession, + javaTypeParameterStack: JavaTypeParameterStack, + boundTypeParameter: FirTypeParameter?, + isForSupertypes: Boolean = false +): ConeTypeProjection { + return when (this) { + null -> ConeStarProjection + is JavaWildcardType -> { + val bound = this.bound + val argumentVariance = if (isExtends) Variance.OUT_VARIANCE else Variance.IN_VARIANCE + val parameterVariance = boundTypeParameter?.variance ?: Variance.INVARIANT + if (bound == null || parameterVariance != Variance.INVARIANT && parameterVariance != argumentVariance) { + ConeStarProjection + } else { + val boundType = bound.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes) + if (argumentVariance == Variance.OUT_VARIANCE) { + ConeKotlinTypeProjectionOut(boundType) + } else { + ConeKotlinTypeProjectionIn(boundType) + } + } + } + else -> toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes) + } +} diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt index e021c858d04..3ec1d791ab4 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaUtils.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.fir.java import org.jetbrains.kotlin.builtins.StandardNames -import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fir.* @@ -14,54 +13,37 @@ 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.FirTypeParameter import org.jetbrains.kotlin.fir.declarations.FirValueParameter -import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic 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.java.enhancement.readOnlyToMutable import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference import org.jetbrains.kotlin.fir.references.impl.FirReferencePlaceholderForResolvedAnnotations import org.jetbrains.kotlin.fir.resolve.bindSymbolToLookupTag -import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError -import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents import org.jetbrains.kotlin.fir.resolve.providers.getClassDeclaredPropertySymbols import org.jetbrains.kotlin.fir.resolve.symbolProvider -import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.expectedConeType -import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.firUnsafe -import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl -import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl -import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl -import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef -import org.jetbrains.kotlin.fir.types.jvm.buildJavaTypeRef import org.jetbrains.kotlin.load.java.structure.* import org.jetbrains.kotlin.load.java.structure.impl.JavaElementImpl -import org.jetbrains.kotlin.load.java.typeEnhancement.TypeComponentPosition import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.types.ConstantValueKind -import org.jetbrains.kotlin.types.Variance.* -import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly -import org.jetbrains.kotlin.utils.addToStdlib.runIf import java.lang.Deprecated import java.lang.annotation.Documented import java.lang.annotation.Retention import java.lang.annotation.Target -import java.util.* +import java.util.EnumSet internal val JavaModifierListOwner.modality: Modality get() = when { @@ -86,384 +68,6 @@ internal val JavaClass.classKind: ClassKind else -> ClassKind.CLASS } -internal fun ClassId.toLookupTag(): ConeClassLikeLookupTag { - return ConeClassLikeLookupTagImpl(this) -} - -internal fun ClassId.toConeKotlinType( - typeArguments: Array, - isNullable: Boolean, - attributes: ConeAttributes = ConeAttributes.Empty -): ConeLookupTagBasedType { - return ConeClassLikeTypeImpl(toLookupTag(), typeArguments, isNullable, attributes) -} - -internal fun FirTypeRef.toConeKotlinTypeProbablyFlexible( - session: FirSession, javaTypeParameterStack: JavaTypeParameterStack -): ConeKotlinType = - when (this) { - is FirResolvedTypeRef -> type - is FirJavaTypeRef -> { - type.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = false) - } - else -> ConeKotlinErrorType( - ConeSimpleDiagnostic("Unexpected type reference in JavaClassUseSiteMemberScope: ${this::class.java}", DiagnosticKind.Java) - ) - } - -internal fun JavaType.toFirJavaTypeRef(session: FirSession, javaTypeParameterStack: JavaTypeParameterStack): FirJavaTypeRef { - return buildJavaTypeRef { - annotationBuilder = { - (this@toFirJavaTypeRef as? JavaClassifierType)?.annotations.orEmpty().map { - it.toFirAnnotationCall(session, javaTypeParameterStack) - } - } - type = this@toFirJavaTypeRef - } -} - -internal fun JavaClassifierType.toFirResolvedTypeRef( - session: FirSession, - javaTypeParameterStack: JavaTypeParameterStack, - isForSupertypes: Boolean, - forTypeParameterBounds: Boolean -): FirResolvedTypeRef { - val coneType = - if (isForSupertypes) - toConeKotlinTypeForFlexibleBound(session, javaTypeParameterStack, forTypeParameterBounds, isForSupertypes) - else - toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, forTypeParameterBounds, isForSupertypes) - - return buildResolvedTypeRef { - type = coneType - this@toFirResolvedTypeRef.annotations.mapTo(annotations) { it.toFirAnnotationCall(session, javaTypeParameterStack) } - } -} - -internal fun JavaType?.toConeKotlinTypeWithoutEnhancement( - session: FirSession, - javaTypeParameterStack: JavaTypeParameterStack, - forAnnotationMember: Boolean = false, - 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( - session, - javaTypeParameterStack, - forAnnotationMember = forAnnotationMember, - attributes = attributes - ) - } - is JavaPrimitiveType -> { - val primitiveType = type - val kotlinPrimitiveName = when (val javaName = primitiveType?.typeName?.asString()) { - null -> "Unit" - else -> javaName.capitalizeAsciiOnly() - } - - val classId = StandardClassIds.byName(kotlinPrimitiveName) - classId.toConeKotlinType(emptyArray(), isNullable = false, attributes) - } - is JavaArrayType -> { - toConeKotlinTypeWithoutEnhancement( - session, - javaTypeParameterStack, - forAnnotationMember, - isForSupertypes, - attributes = attributes - ) - } - is JavaWildcardType -> - bound?.toConeKotlinTypeWithoutEnhancement( - session, - javaTypeParameterStack, - isForSupertypes = isForSupertypes - ) ?: StandardClassIds.Any.toConeFlexibleType(emptyArray(), attributes = attributes) - null -> - StandardClassIds.Any.toConeFlexibleType(emptyArray(), attributes = attributes) - else -> error("Strange JavaType: ${this::class.java}") - } -} - -private fun JavaArrayType.toConeKotlinTypeWithoutEnhancement( - session: FirSession, - javaTypeParameterStack: JavaTypeParameterStack, - forAnnotationValueParameter: Boolean = false, - isForSupertypes: Boolean, - attributes: ConeAttributes = ConeAttributes.Empty -): ConeKotlinType { - val componentType = componentType - return if (componentType !is JavaPrimitiveType) { - val classId = StandardClassIds.Array - val argumentType = componentType.toConeKotlinTypeWithoutEnhancement( - session, javaTypeParameterStack, forAnnotationValueParameter, isForSupertypes - ) - if (forAnnotationValueParameter) { - classId.constructClassLikeType(arrayOf(argumentType), isNullable = false, attributes = attributes) - } else { - classId.toConeFlexibleType( - arrayOf(argumentType), - typeArgumentsForUpper = arrayOf(ConeKotlinTypeProjectionOut(argumentType)), - attributes = attributes - ) - } - } else { - val javaComponentName = componentType.type?.typeName?.asString()?.capitalizeAsciiOnly() ?: error("Array of voids") - val classId = StandardClassIds.byName(javaComponentName + "Array") - - if (forAnnotationValueParameter) { - classId.constructClassLikeType(emptyArray(), isNullable = false, attributes = attributes) - } else { - classId.toConeFlexibleType(emptyArray(), attributes = attributes) - } - } -} - -private fun ClassId.toConeFlexibleType( - typeArguments: Array, - typeArgumentsForUpper: Array = typeArguments, - attributes: ConeAttributes = ConeAttributes.Empty -) = ConeFlexibleType( - toConeKotlinType(typeArguments, isNullable = false, attributes), - toConeKotlinType(typeArgumentsForUpper, isNullable = true, attributes) -) - -private fun JavaClassifierType.toConeKotlinTypeWithoutEnhancement( - session: FirSession, - javaTypeParameterStack: JavaTypeParameterStack, - forTypeParameterBounds: Boolean = false, - isForSupertypes: Boolean = false, - forAnnotationMember: Boolean = false, - attributes: ConeAttributes = ConeAttributes.Empty -): ConeKotlinType { - val lowerBound = toConeKotlinTypeForFlexibleBound( - session, - javaTypeParameterStack, - forTypeParameterBounds, - isForSupertypes, - forAnnotationMember = forAnnotationMember, - attributes = attributes - ) - if (forAnnotationMember) { - return lowerBound - } - val upperBound = - toConeKotlinTypeForFlexibleBound( - session, - javaTypeParameterStack, - forTypeParameterBounds, - isForSupertypes, - lowerBound, - forAnnotationMember = forAnnotationMember, - attributes = attributes - ) - - return if (isRaw) - ConeRawType(lowerBound, upperBound) - else - ConeFlexibleType(lowerBound, upperBound) -} - -private fun computeRawProjection( - session: FirSession, - parameter: FirTypeParameter, - attr: TypeComponentPosition, - erasedUpperBound: ConeKotlinType = parameter.getErasedUpperBound(session) -) = when (attr) { - // Raw(List) => (List..List<*>) - // Raw(Enum) => (Enum>..Enum>) - // In the last case upper bound is equal to star projection `Enum<*>`, - // but we want to keep matching tree structure of flexible bounds (at least they should have the same size) - TypeComponentPosition.FLEXIBLE_LOWER -> { - // T : String -> String - // in T : String -> String - // T : Enum -> Enum<*> - erasedUpperBound - } - TypeComponentPosition.FLEXIBLE_UPPER, TypeComponentPosition.INFLEXIBLE -> { - if (!parameter.variance.allowsOutPosition) - // in T -> Comparable - session.builtinTypes.nothingType.type - else if (erasedUpperBound is ConeClassLikeType && - erasedUpperBound.lookupTag.toSymbol(session)!!.firUnsafe().typeParameters.isNotEmpty() - ) - // T : Enum -> out Enum<*> - ConeKotlinTypeProjectionOut(erasedUpperBound) - else - // T : String -> * - ConeStarProjection - } -} - -// Definition: -// ErasedUpperBound(T : G) = G<*> // UpperBound(T) is a type G with arguments -// ErasedUpperBound(T : A) = A // UpperBound(T) is a type A without arguments -// ErasedUpperBound(T : F) = UpperBound(F) // UB(T) is another type parameter F -private fun FirTypeParameter.getErasedUpperBound( - session: FirSession, - // Calculation of `potentiallyRecursiveTypeParameter.upperBounds` may recursively depend on `this.getErasedUpperBound` - // E.g. `class A` - // To prevent recursive calls return defaultValue() instead - potentiallyRecursiveTypeParameter: FirTypeParameter? = null, - defaultValue: (() -> ConeKotlinType) = { - ConeKotlinErrorType(ConeIntermediateDiagnostic("Can't compute erased upper bound of type parameter `$this`")) - } -): ConeKotlinType { - if (this === potentiallyRecursiveTypeParameter) return defaultValue() - - val firstUpperBound = this.bounds.first().coneType - - return getErasedVersionOfFirstUpperBound(session, firstUpperBound, mutableSetOf(this, potentiallyRecursiveTypeParameter), defaultValue) -} - -private fun getErasedVersionOfFirstUpperBound( - session: FirSession, - firstUpperBound: ConeKotlinType, - alreadyVisitedParameters: MutableSet, - defaultValue: () -> ConeKotlinType -): ConeKotlinType = - when (firstUpperBound) { - is ConeClassLikeType -> - firstUpperBound.withArguments(firstUpperBound.typeArguments.map { ConeStarProjection }.toTypedArray()) - - is ConeFlexibleType -> { - val lowerBound = - getErasedVersionOfFirstUpperBound(session, firstUpperBound.lowerBound, alreadyVisitedParameters, defaultValue) - .lowerBoundIfFlexible() - if (firstUpperBound.upperBound is ConeTypeParameterType) { - // Avoid exponential complexity - ConeFlexibleType( - lowerBound, - lowerBound.withNullability(ConeNullability.NULLABLE, session.inferenceComponents.ctx) - ) - } else { - ConeFlexibleType( - lowerBound, - getErasedVersionOfFirstUpperBound(session, firstUpperBound.upperBound, alreadyVisitedParameters, defaultValue) - ) - } - } - is ConeTypeParameterType -> { - val current = firstUpperBound.lookupTag.typeParameterSymbol.fir - - if (alreadyVisitedParameters.add(current)) { - val nextUpperBound = current.bounds.first().coneType - getErasedVersionOfFirstUpperBound(session, nextUpperBound, alreadyVisitedParameters, defaultValue) - } else { - defaultValue() - } - } - else -> error("Unexpected kind of firstUpperBound: $firstUpperBound [${firstUpperBound::class}]") - } - -private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( - session: FirSession, - javaTypeParameterStack: JavaTypeParameterStack, - forTypeParameterBounds: Boolean, - isForSupertypes: Boolean, - lowerBound: ConeLookupTagBasedType? = null, - forAnnotationMember: Boolean = false, - attributes: ConeAttributes = ConeAttributes.Empty -): ConeLookupTagBasedType { - return when (val classifier = classifier) { - is JavaClass -> { - //val classId = classifier.classId!! - var classId = if (forAnnotationMember) { - JavaToKotlinClassMap.mapJavaToKotlinIncludingClassMapping(classifier.fqName!!) - } else { - JavaToKotlinClassMap.mapJavaToKotlin(classifier.fqName!!) - } ?: classifier.classId!! - - if (lowerBound == null || argumentsMakeSenseOnlyForMutableContainer(classId, session)) { - classId = classId.readOnlyToMutable() ?: classId - } - - val lookupTag = ConeClassLikeLookupTagImpl(classId) - if (lookupTag == lowerBound?.lookupTag && !isRaw) { - return lookupTag.constructClassType(lowerBound.typeArguments, isNullable = true, attributes) - } - - val mappedTypeArguments = if (isRaw) { - val defaultArgs = (1..classifier.typeParameters.size).map { ConeStarProjection } - - if (forTypeParameterBounds) { - // This is not fully correct, but it's a simple fix for some time to avoid recursive definition: - // to create a proper raw type arguments, we should take class parameters some time - defaultArgs - } else { - 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 - } - } else { - val typeParameters = runIf(!forTypeParameterBounds && !isForSupertypes) { - val classSymbol = session.symbolProvider.getClassLikeSymbolByFqName(classId) as? FirRegularClassSymbol - classSymbol?.fir?.typeParameters - } ?: emptyList() - - typeArguments.indices.map { index -> - val argument = typeArguments[index] - val parameter = typeParameters.getOrNull(index)?.symbol?.fir - argument.toConeProjectionWithoutEnhancement( - session, javaTypeParameterStack, boundTypeParameter = parameter, isForSupertypes = isForSupertypes - ) - } - } - - lookupTag.constructClassType(mappedTypeArguments.toTypedArray(), isNullable = lowerBound != null, attributes) - } - is JavaTypeParameter -> { - val symbol = javaTypeParameterStack[classifier] - ConeTypeParameterTypeImpl(symbol.toLookupTag(), isNullable = lowerBound != null, attributes) - } - null -> { - val classId = ClassId.topLevel(FqName(this.classifierQualifiedName)) - classId.constructClassLikeType(emptyArray(), isNullable = lowerBound != null, attributes) - } - else -> ConeKotlinErrorType(ConeSimpleDiagnostic("Unexpected classifier: $classifier", DiagnosticKind.Java)) - } -} - -// Returns true for covariant read-only container that has mutable pair with invariant parameter -// List does not make sense, but MutableList does -// Same for Map -// But both Iterable, MutableIterable don't make sense as they are covariant, so return false -private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer( - classId: ClassId, - session: FirSession, -): Boolean { - if (!JavaToKotlinClassMap.isReadOnly(classId.asSingleFqName().toUnsafe())) return false - val mutableClassId = classId.readOnlyToMutable() ?: return false - - if (!typeArguments.lastOrNull().isSuperWildcard()) return false - val mutableLastParameterVariance = - (mutableClassId.toLookupTag().toSymbol(session)?.fir as? FirRegularClass)?.typeParameters?.lastOrNull()?.symbol?.fir?.variance - ?: return false - - return mutableLastParameterVariance != OUT_VARIANCE -} - - -private fun FirRegularClass.createRawArguments( - session: FirSession, - defaultArgs: List, - position: TypeComponentPosition -): List = typeParameters.filterIsInstance().map { typeParameter -> - val erasedUpperBound = typeParameter.getErasedUpperBound(session) { - defaultType().withArguments(defaultArgs.toTypedArray()) - } - computeRawProjection(session, typeParameter, position, erasedUpperBound) -} - private fun buildEnumCall(session: FirSession, classId: ClassId?, entryName: Name?) = buildFunctionCall { val calleeReference = if (classId != null && entryName != null) { @@ -620,33 +224,6 @@ internal fun JavaValueParameter.toFirValueParameter( } } -private fun JavaType?.toConeProjectionWithoutEnhancement( - session: FirSession, - javaTypeParameterStack: JavaTypeParameterStack, - boundTypeParameter: FirTypeParameter?, - isForSupertypes: Boolean = false -): ConeTypeProjection { - return when (this) { - null -> ConeStarProjection - is JavaWildcardType -> { - val bound = this.bound - val argumentVariance = if (isExtends) OUT_VARIANCE else IN_VARIANCE - val parameterVariance = boundTypeParameter?.variance ?: INVARIANT - if (bound == null || parameterVariance != INVARIANT && parameterVariance != argumentVariance) { - ConeStarProjection - } else { - val boundType = bound.toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes) - if (argumentVariance == OUT_VARIANCE) { - ConeKotlinTypeProjectionOut(boundType) - } else { - ConeKotlinTypeProjectionIn(boundType) - } - } - } - else -> toConeKotlinTypeWithoutEnhancement(session, javaTypeParameterStack, isForSupertypes = isForSupertypes) - } -} - internal fun JavaAnnotationArgument.toFirExpression( session: FirSession, javaTypeParameterStack: JavaTypeParameterStack, expectedTypeRef: FirTypeRef? ): FirExpression {