diff --git a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/AbstractSignatureParts.kt b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/AbstractSignatureParts.kt new file mode 100644 index 00000000000..4885b114d02 --- /dev/null +++ b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/AbstractSignatureParts.kt @@ -0,0 +1,230 @@ +/* + * 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.load.java.typeEnhancement + +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.load.java.AbstractAnnotationTypeQualifierResolver +import org.jetbrains.kotlin.load.java.AnnotationQualifierApplicabilityType +import org.jetbrains.kotlin.load.java.JavaTypeQualifiersByElementType +import org.jetbrains.kotlin.name.FqNameUnsafe +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.types.model.TypeVariance + +abstract class AbstractSignatureParts { + // TODO: some of this might be better off as parameters + abstract val annotationTypeQualifierResolver: AbstractAnnotationTypeQualifierResolver + abstract val enableImprovementsInStrictMode: Boolean + abstract val containerAnnotations: Iterable + abstract val containerApplicabilityType: AnnotationQualifierApplicabilityType + abstract val containerDefaultTypeQualifiers: JavaTypeQualifiersByElementType? + abstract val containerIsVarargParameter: Boolean + abstract val isCovariant: Boolean + abstract val skipRawTypeArguments: Boolean + abstract val typeSystem: TypeSystemContext + + abstract val Annotation.forceWarning: Boolean + + abstract val KotlinTypeMarker.annotations: Iterable + abstract val KotlinTypeMarker.enhancedForWarnings: KotlinTypeMarker? + abstract val KotlinTypeMarker.fqNameUnsafe: FqNameUnsafe? + abstract fun KotlinTypeMarker.isEqual(other: KotlinTypeMarker): Boolean + + abstract val TypeParameterMarker.starProjectedType: KotlinTypeMarker? + abstract val TypeParameterMarker.isFromJava: Boolean + + open val KotlinTypeMarker.isNotNullTypeParameterCompat: Boolean + get() = false + + private val KotlinTypeMarker.nullabilityQualifier: NullabilityQualifier? + get() = with(typeSystem) { + when { + lowerBoundIfFlexible().isMarkedNullable() -> NullabilityQualifier.NULLABLE + !upperBoundIfFlexible().isMarkedNullable() -> NullabilityQualifier.NOT_NULL + else -> null + } + } + + private fun KotlinTypeMarker.extractQualifiers(): JavaTypeQualifiers { + val forErrors = nullabilityQualifier + val forErrorsOrWarnings = forErrors ?: enhancedForWarnings?.nullabilityQualifier + val mutability = with(typeSystem) { + when { + JavaToKotlinClassMap.isReadOnly(lowerBoundIfFlexible().fqNameUnsafe) -> MutabilityQualifier.READ_ONLY + JavaToKotlinClassMap.isMutable(upperBoundIfFlexible().fqNameUnsafe) -> MutabilityQualifier.MUTABLE + else -> null + } + } + val isNotNullTypeParameter = with(typeSystem) { isDefinitelyNotNullType() } || isNotNullTypeParameterCompat + return JavaTypeQualifiers(forErrorsOrWarnings, mutability, isNotNullTypeParameter, forErrorsOrWarnings != forErrors) + } + + private fun TypeAndDefaultQualifiers.extractQualifiersFromAnnotations(): JavaTypeQualifiers { + if (type == null && with(typeSystem) { typeParameterForArgument?.getVariance() } == TypeVariance.IN) { + // Star projections can only be enhanced in one way: `?` -> `? extends `. Given a Kotlin type `C + // (declaration-site variance), this is not a valid enhancement due to conflicting variances. + return JavaTypeQualifiers.NONE + } + + val isHeadTypeConstructor = typeParameterForArgument == null + val typeOrBound = type ?: typeParameterForArgument?.starProjectedType ?: return JavaTypeQualifiers.NONE + val typeParameterUse = with(typeSystem) { typeOrBound.typeConstructor().getTypeParameterClassifier() } + val typeParameterBounds = containerApplicabilityType == AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS + val composedAnnotation = when { + !isHeadTypeConstructor -> typeOrBound.annotations + !typeParameterBounds && enableImprovementsInStrictMode -> + // We don't apply container type use annotations to avoid double applying them like with arrays: + // @NotNull Integer [] f15(); + // Otherwise, in the example above we would apply `@NotNull` to `Integer` (i.e. array element; as TYPE_USE annotation) + // and to entire array (as METHOD annotation). + // In other words, we prefer TYPE_USE target of an annotation, and apply the annotation only according to it, if it's present. + // See KT-24392 for more details. + containerAnnotations.filter { !annotationTypeQualifierResolver.isTypeUseAnnotation(it) } + typeOrBound.annotations + else -> containerAnnotations + typeOrBound.annotations + } + + val annotationsMutability = annotationTypeQualifierResolver.extractMutability(composedAnnotation) + val annotationsNullability = annotationTypeQualifierResolver.extractNullability(composedAnnotation) { forceWarning } + + if (type != null && annotationsNullability != null) { + return JavaTypeQualifiers( + annotationsNullability.qualifier, annotationsMutability, + annotationsNullability.qualifier == NullabilityQualifier.NOT_NULL && typeParameterUse != null, + annotationsNullability.isForWarningOnly + ) + } + + // TODO: check whether the code below works properly for star projections (when typeOrBound != type) + val applicabilityType = when { + isHeadTypeConstructor || typeParameterBounds -> containerApplicabilityType + else -> AnnotationQualifierApplicabilityType.TYPE_USE + } + val defaultTypeQualifier = defaultQualifiers?.get(applicabilityType) + ?.takeIf { (it.affectsTypeParameterBasedTypes || typeParameterUse == null) && (it.affectsStarProjection || type != null) } + + val referencedParameterBoundsNullability = typeParameterUse?.boundsNullability + // For type parameter uses, we have *three* options: + // T!! - NOT_NULL, isNotNullTypeParameter = true + // happens if T is bounded by @NotNull (technically !! is redundant) or context says unannotated + // type parameters are non-null; + // T - NOT_NULL, isNotNullTypeParameter = false + // happens if T is bounded by @Nullable or context says unannotated types in general are non-null; + // T? - NULLABLE, isNotNullTypeParameter = false + // happens if context says unannotated types in general are nullable. + // For other types, this is more straightforward (just take nullability from the context). + // TODO: clean up the representation of those cases in JavaTypeQualifiers + val defaultNullability = + referencedParameterBoundsNullability?.copy(qualifier = NullabilityQualifier.NOT_NULL) + ?: defaultTypeQualifier?.nullabilityQualifier + val isNotNullTypeParameter = + referencedParameterBoundsNullability?.qualifier == NullabilityQualifier.NOT_NULL || + (typeParameterUse != null && defaultTypeQualifier?.nullabilityQualifier?.qualifier == NullabilityQualifier.NOT_NULL) + + // We should also enhance this type to satisfy the bound of the type parameter it is instantiating: + // for C, C becomes C regardless of the above. + val substitutedParameterBoundsNullability = typeParameterForArgument?.boundsNullability + val result = when { + substitutedParameterBoundsNullability == null -> defaultNullability + defaultNullability == null -> + if (substitutedParameterBoundsNullability.qualifier == NullabilityQualifier.NULLABLE) + substitutedParameterBoundsNullability.copy(qualifier = NullabilityQualifier.FORCE_FLEXIBILITY) + else + substitutedParameterBoundsNullability + type == null -> substitutedParameterBoundsNullability + else -> mostSpecific(substitutedParameterBoundsNullability, defaultNullability) + } + return JavaTypeQualifiers(result?.qualifier, annotationsMutability, isNotNullTypeParameter, result?.isForWarningOnly == true) + } + + private fun mostSpecific( + a: NullabilityQualifierWithMigrationStatus, + b: NullabilityQualifierWithMigrationStatus + ): NullabilityQualifierWithMigrationStatus { + // TODO: this probably behaves really weirdly when some of those are warnings. + if (a.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return b + if (b.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return a + if (a.qualifier == NullabilityQualifier.NULLABLE) return b + if (b.qualifier == NullabilityQualifier.NULLABLE) return a + assert(a.qualifier == b.qualifier && a.qualifier == NullabilityQualifier.NOT_NULL) { + "Expected everything is NOT_NULL, but $a and $b are found" + } + + return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL) + } + + private val TypeParameterMarker.boundsNullability: NullabilityQualifierWithMigrationStatus? + get() = with(typeSystem) { + if (!isFromJava) return null + val bounds = getUpperBounds() + val enhancedBounds = when { + bounds.all { it.isError() } -> return null + // TODO: what if e.g. one bound is nullable and another is not null for warnings? + bounds.any { it.nullabilityQualifier != null } -> bounds + bounds.any { it.enhancedForWarnings != null } -> bounds.mapNotNull { it.enhancedForWarnings } + else -> return null + } + val qualifier = if (enhancedBounds.all { it.isNullableType() }) NullabilityQualifier.NULLABLE else NullabilityQualifier.NOT_NULL + return NullabilityQualifierWithMigrationStatus(qualifier, isForWarningOnly = enhancedBounds !== bounds) + } + + fun KotlinTypeMarker.computeIndexedQualifiers( + overrides: Iterable, predefined: TypeEnhancementInfo? + ): IndexedJavaTypeQualifiers { + val indexedThisType = toIndexed() + val indexedFromSupertypes = overrides.map { it.toIndexed() } + + // The covariant case may be hard, e.g. in the superclass the return may be Super, but in the subclass it may be Derived, which + // is declared to extend Super, 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! in the subclass and { List, (Mutable)List! } from superclasses + // Note that `this` is flexible here, so it's equal to it's bounds + val onlyHeadTypeConstructor = isCovariant && overrides.any { !this@computeIndexedQualifiers.isEqual(it) } + + val treeSize = if (onlyHeadTypeConstructor) 1 else indexedThisType.size + val computedResult = Array(treeSize) { index -> + val qualifiers = indexedThisType[index].extractQualifiersFromAnnotations() + val superQualifiers = indexedFromSupertypes.mapNotNull { it.getOrNull(index)?.type?.extractQualifiers() } + qualifiers.computeQualifiersForOverride(superQualifiers, index == 0 && isCovariant, index == 0 && containerIsVarargParameter) + } + return { index -> predefined?.map?.get(index) ?: computedResult.getOrElse(index) { JavaTypeQualifiers.NONE } } + } + + private fun T.flattenTree(result: MutableList, children: (T) -> Iterable?) { + result.add(this) + children(this)?.forEach { it.flattenTree(result, children) } + } + + private fun T.flattenTree(children: (T) -> Iterable?): List = + ArrayList(1).also { flattenTree(it, children) } + + private fun KotlinTypeMarker.extractAndMergeDefaultQualifiers(oldQualifiers: JavaTypeQualifiersByElementType?) = + annotationTypeQualifierResolver.extractAndMergeDefaultQualifiers(oldQualifiers, annotations) + + private fun KotlinTypeMarker.toIndexed(): List = with(typeSystem) { + TypeAndDefaultQualifiers(this@toIndexed, extractAndMergeDefaultQualifiers(containerDefaultTypeQualifiers), null).flattenTree { + // Enhancement of raw type arguments may enter a loop in FE1.0. + if (skipRawTypeArguments && it.type?.asFlexibleType()?.asRawType() != null) return@flattenTree null + + it.type?.typeConstructor()?.getParameters()?.zip(it.type.getArguments()) { parameter, arg -> + if (arg.isStarProjection()) { + TypeAndDefaultQualifiers(null, it.defaultQualifiers, parameter) + } else { + val type = arg.getType() + TypeAndDefaultQualifiers(type, type.extractAndMergeDefaultQualifiers(it.defaultQualifiers), parameter) + } + } + } + } + + private class TypeAndDefaultQualifiers( + val type: KotlinTypeMarker?, + val defaultQualifiers: JavaTypeQualifiersByElementType?, + val typeParameterForArgument: TypeParameterMarker? + ) +} + +typealias IndexedJavaTypeQualifiers = (Int) -> JavaTypeQualifiers diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt index c16c5e654f3..76898993d01 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt @@ -19,8 +19,8 @@ package org.jetbrains.kotlin.load.java.typeEnhancement import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.annotations.composeAnnotations import org.jetbrains.kotlin.load.java.* import org.jetbrains.kotlin.load.java.descriptors.* import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext @@ -37,9 +37,11 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.deprecation.DEPRECATED_FUNCTION_KEY import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext +import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.types.model.TypeParameterMarker +import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext import org.jetbrains.kotlin.types.typeUtil.contains -import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.utils.addToStdlib.safeAs class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) { @@ -81,12 +83,12 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) { val receiverTypeEnhancement = if (extensionReceiverParameter != null) - partsForValueParameter( + enhanceValueParameter( parameterDescriptor = annotationOwnerForMember.safeAs() ?.getUserData(JavaMethodDescriptor.ORIGINAL_VALUE_PARAMETER_FOR_EXTENSION_RECEIVER), - methodContext = memberContext - ) { it.extensionReceiverParameter!!.type }.enhance() + methodContext = memberContext, predefined = null + ) { it.extensionReceiverParameter!!.type } else null val predefinedEnhancementInfo = @@ -102,20 +104,21 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) { } val valueParameterEnhancements = annotationOwnerForMember.valueParameters.map { p -> - partsForValueParameter(p, memberContext) { it.valueParameters[p.index].type } - .enhance(predefinedEnhancementInfo?.parametersInfo?.getOrNull(p.index)) + val predefined = predefinedEnhancementInfo?.parametersInfo?.getOrNull(p.index) + enhanceValueParameter(p, memberContext, predefined) { it.valueParameters[p.index].type } } val returnTypeEnhancement = - parts( + enhance( typeContainer = annotationOwnerForMember, isCovariant = true, containerContext = memberContext, containerApplicabilityType = if (this.safeAs()?.isJavaField == true) AnnotationQualifierApplicabilityType.FIELD else - AnnotationQualifierApplicabilityType.METHOD_RETURN_TYPE - ) { it.returnType!! }.enhance(predefinedEnhancementInfo?.returnTypeInfo) + AnnotationQualifierApplicabilityType.METHOD_RETURN_TYPE, + predefinedEnhancementInfo?.returnTypeInfo + ) { it.returnType!! } val containsFunctionN = returnType!!.containsFunctionN() || extensionReceiverParameter?.type?.containsFunctionN() ?: false || @@ -151,11 +154,8 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) { // TODO: would not enhancing raw type arguments be sufficient? if (bound.contains { it is RawType }) return@map bound - SignatureParts( - typeEnhancement, typeParameter, bound, emptyList(), false, context, - AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS, - typeParameterBounds = true - ).enhance() ?: bound + SignatureParts(typeParameter, false, context, AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS) + .enhance(bound, emptyList()) ?: bound } } @@ -165,9 +165,9 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) { */ fun enhanceSuperType(type: KotlinType, context: LazyJavaResolverContext) = SignatureParts( - typeEnhancement, typeContainer = null, type, emptyList(), isCovariant = false, - context, AnnotationQualifierApplicabilityType.TYPE_USE, isSuperTypesEnhancement = true - ).enhance() ?: type + typeContainer = null, isCovariant = false, + context, AnnotationQualifierApplicabilityType.TYPE_USE, skipRawTypeArguments = true + ).enhance(type, emptyList()) ?: type private fun KotlinType.containsFunctionN(): Boolean = TypeUtils.contains(this) { @@ -176,255 +176,84 @@ class SignatureEnhancement(private val typeEnhancement: JavaTypeEnhancement) { classifier.fqNameOrNull() == JavaToKotlinClassMap.FUNCTION_N_FQ_NAME } + private fun CallableMemberDescriptor.enhanceValueParameter( + // TODO: investigate if it's really can be a null (check properties' with extension overrides in Java) + parameterDescriptor: ValueParameterDescriptor?, + methodContext: LazyJavaResolverContext, + predefined: TypeEnhancementInfo?, + collector: (CallableMemberDescriptor) -> KotlinType + ) = enhance( + parameterDescriptor, false, + parameterDescriptor?.let { methodContext.copyWithNewDefaultTypeQualifiers(it.annotations) } ?: methodContext, + AnnotationQualifierApplicabilityType.VALUE_PARAMETER, + predefined, collector + ) - private fun CallableMemberDescriptor.parts( + private fun CallableMemberDescriptor.enhance( typeContainer: Annotated?, isCovariant: Boolean, containerContext: LazyJavaResolverContext, containerApplicabilityType: AnnotationQualifierApplicabilityType, + predefined: TypeEnhancementInfo?, collector: (CallableMemberDescriptor) -> KotlinType - ): SignatureParts { - return SignatureParts( - typeEnhancement, - typeContainer, - collector(this), - this.overriddenDescriptors.map { - collector(it) - }, - isCovariant, - // recompute default type qualifiers using type annotations - containerContext.copyWithNewDefaultTypeQualifiers(collector(this).annotations), - containerApplicabilityType - ) + ): KotlinType? { + return SignatureParts(typeContainer, isCovariant, containerContext, containerApplicabilityType) + .enhance(collector(this), overriddenDescriptors.map { collector(it) }, predefined) } + + private fun SignatureParts.enhance(type: KotlinType, overrides: List, predefined: TypeEnhancementInfo? = null) = + with(typeEnhancement) { type.enhance(type.computeIndexedQualifiers(overrides, predefined), skipRawTypeArguments) } } private class SignatureParts( - private val typeEnhancement: JavaTypeEnhancement, private val typeContainer: Annotated?, - private val fromOverride: KotlinType, - private val fromOverridden: Collection, - private val isCovariant: Boolean, + override val isCovariant: Boolean, private val containerContext: LazyJavaResolverContext, - private val containerApplicabilityType: AnnotationQualifierApplicabilityType, - private val typeParameterBounds: Boolean = false, - private val isSuperTypesEnhancement: Boolean = false -) { - private val isForVarargParameter get() = typeContainer.safeAs()?.varargElementType != null + override val containerApplicabilityType: AnnotationQualifierApplicabilityType, + override val skipRawTypeArguments: Boolean = false +) : AbstractSignatureParts() { + override val annotationTypeQualifierResolver: AnnotationTypeQualifierResolver + get() = containerContext.components.annotationTypeQualifierResolver - fun enhance(predefined: TypeEnhancementInfo? = null): KotlinType? = - with(typeEnhancement) { - fromOverride.enhance(computeIndexedQualifiersForOverride(predefined), isSuperTypesEnhancement) - } + override val enableImprovementsInStrictMode: Boolean + get() = containerContext.components.settings.typeEnhancementImprovementsInStrictMode - private val KotlinType.isNotNullTypeParameter: Boolean - get() = unwrap().let { it is NotNullTypeParameter || it is DefinitelyNotNullType } + override val containerAnnotations: Iterable + get() = typeContainer?.annotations ?: emptyList() - private val KotlinType.fqNameUnsafe: FqNameUnsafe? - get() = TypeUtils.getClassDescriptor(this)?.let { DescriptorUtils.getFqName(it) } + override val containerDefaultTypeQualifiers: JavaTypeQualifiersByElementType? + get() = containerContext.defaultTypeQualifiers - private val KotlinType.nullabilityQualifier: NullabilityQualifier? - get() = when { - lowerIfFlexible().isMarkedNullable -> NullabilityQualifier.NULLABLE - !upperIfFlexible().isMarkedNullable -> NullabilityQualifier.NOT_NULL - else -> null - } + override val containerIsVarargParameter: Boolean + get() = typeContainer is ValueParameterDescriptor && typeContainer.varargElementType != null - private val KotlinType.mutabilityQualifier: MutabilityQualifier? - get() = when { - JavaToKotlinClassMap.isReadOnly(lowerIfFlexible().fqNameUnsafe) -> MutabilityQualifier.READ_ONLY - JavaToKotlinClassMap.isMutable(upperIfFlexible().fqNameUnsafe) -> MutabilityQualifier.MUTABLE - else -> null - } + override val typeSystem: TypeSystemInferenceExtensionContext + get() = SimpleClassicTypeSystemContext - private fun KotlinType.extractQualifiers(): JavaTypeQualifiers { - val forErrors = nullabilityQualifier - val forErrorsOrWarnings = forErrors ?: unwrapEnhancement().nullabilityQualifier - return JavaTypeQualifiers(forErrorsOrWarnings, mutabilityQualifier, isNotNullTypeParameter, forErrorsOrWarnings != forErrors) - } + override val AnnotationDescriptor.forceWarning: Boolean + get() = (this is PossiblyExternalAnnotationDescriptor && isIdeExternalAnnotation) || + (this is LazyJavaAnnotationDescriptor && !enableImprovementsInStrictMode && + (isFreshlySupportedTypeUseAnnotation || + containerApplicabilityType == AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS)) - private fun TypeAndDefaultQualifiers.extractQualifiersFromAnnotations(): JavaTypeQualifiers { - if (type == null && typeParameterForArgument?.variance == Variance.IN_VARIANCE) { - // Star projections can only be enhanced in one way: `?` -> `? extends `. Given a Kotlin type `C - // (declaration-site variance), this is not a valid enhancement due to conflicting variances. - return JavaTypeQualifiers.NONE - } + override val KotlinTypeMarker.annotations: Iterable + get() = (this as KotlinType).annotations - val annotationTypeQualifierResolver = containerContext.components.annotationTypeQualifierResolver - val areImprovementsInStrictMode = containerContext.components.settings.typeEnhancementImprovementsInStrictMode + override val KotlinTypeMarker.enhancedForWarnings: KotlinType? + get() = (this as KotlinType).getEnhancement() - val isHeadTypeConstructor = typeParameterForArgument == null - val typeOrBound = type ?: typeParameterForArgument!!.starProjectionType() - val composedAnnotation = - if (isHeadTypeConstructor && typeContainer != null && !typeParameterBounds && areImprovementsInStrictMode) { - val filteredContainerAnnotations = typeContainer.annotations.filter { - /* - * We don't apply container type use annotations to avoid double applying them like with arrays: - * @NotNull Integer [] f15(); - * Otherwise, in the example above we would apply `@NotNull` to `Integer` (i.e. array element; as TYPE_USE annotation) - * and to entire array (as METHOD annotation). - * In other words, we prefer TYPE_USE target of an annotation, and apply the annotation only according to it, if it's present. - * See KT-24392 for more details. - */ - !annotationTypeQualifierResolver.isTypeUseAnnotation(it) - } - composeAnnotations(Annotations.create(filteredContainerAnnotations), typeOrBound.annotations) - } else if (isHeadTypeConstructor && typeContainer != null) { - composeAnnotations(typeContainer.annotations, typeOrBound.annotations) - } else typeOrBound.annotations + override val KotlinTypeMarker.fqNameUnsafe: FqNameUnsafe? + get() = TypeUtils.getClassDescriptor(this as KotlinType)?.let { DescriptorUtils.getFqName(it) } - val annotationsMutability = annotationTypeQualifierResolver.extractMutability(composedAnnotation) - val annotationsNullability = annotationTypeQualifierResolver.extractNullability(composedAnnotation) { - (this is LazyJavaAnnotationDescriptor && (isFreshlySupportedTypeUseAnnotation || typeParameterBounds) && !areImprovementsInStrictMode) || - (this is PossiblyExternalAnnotationDescriptor && isIdeExternalAnnotation) - } + override val KotlinTypeMarker.isNotNullTypeParameterCompat: Boolean + get() = (this as KotlinType).unwrap() is NotNullTypeParameter - if (type != null && annotationsNullability != null) { - return JavaTypeQualifiers( - annotationsNullability.qualifier, annotationsMutability, - annotationsNullability.qualifier == NullabilityQualifier.NOT_NULL && type.isTypeParameter(), - annotationsNullability.isForWarningOnly - ) - } + override fun KotlinTypeMarker.isEqual(other: KotlinTypeMarker): Boolean = + containerContext.components.kotlinTypeChecker.equalTypes(this as KotlinType, other as KotlinType) - // TODO: check whether the code below works properly for star projections (when typeOrBound != type) - val defaultTypeQualifier = ( - if (isHeadTypeConstructor) - containerContext.defaultTypeQualifiers?.get(containerApplicabilityType) - else - defaultQualifiers?.get( - if (typeParameterBounds) - AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS - else - AnnotationQualifierApplicabilityType.TYPE_USE - ) - )?.takeIf { (it.affectsTypeParameterBasedTypes || !typeOrBound.isTypeParameter()) && (it.affectsStarProjection || type != null) } + override val TypeParameterMarker.starProjectedType: KotlinType + get() = (this as TypeParameterDescriptor).starProjectionType() - val referencedParameterBoundsNullability = - (typeOrBound.constructor.declarationDescriptor as? TypeParameterDescriptor)?.boundsNullability() - - // For type parameter uses, we have *three* options: - // T!! - NOT_NULL, isNotNullTypeParameter = true - // happens if T is bounded by @NotNull (technically !! is redundant) or context says unannotated - // type parameters are non-null; - // T - NOT_NULL, isNotNullTypeParameter = false - // happens if T is bounded by @Nullable or context says unannotated types in general are non-null; - // T? - NULLABLE, isNotNullTypeParameter = false - // happens if context says unannotated types in general are nullable. - // For other types, this is more straightforward (just take nullability from the context). - // TODO: clean up the representation of those cases in JavaTypeQualifiers - val defaultNullability = - referencedParameterBoundsNullability?.copy(qualifier = NullabilityQualifier.NOT_NULL) - ?: defaultTypeQualifier?.nullabilityQualifier - val isNotNullTypeParameter = - referencedParameterBoundsNullability?.qualifier == NullabilityQualifier.NOT_NULL || - (typeOrBound.isTypeParameter() && defaultTypeQualifier?.nullabilityQualifier?.qualifier == NullabilityQualifier.NOT_NULL) - - // We should also enhance this type to satisfy the bound of the type parameter it is instantiating: - // for C, C becomes C regardless of the above. - val substitutedParameterBoundsNullability = typeParameterForArgument?.boundsNullability() - val result = when { - substitutedParameterBoundsNullability == null -> defaultNullability - defaultNullability == null -> - if (substitutedParameterBoundsNullability.qualifier == NullabilityQualifier.NULLABLE) - substitutedParameterBoundsNullability.copy(qualifier = NullabilityQualifier.FORCE_FLEXIBILITY) - else - substitutedParameterBoundsNullability - else -> mostSpecific(substitutedParameterBoundsNullability, defaultNullability) - } - return JavaTypeQualifiers(result?.qualifier, annotationsMutability, isNotNullTypeParameter, result?.isForWarningOnly == true) - } - - private fun mostSpecific( - a: NullabilityQualifierWithMigrationStatus, - b: NullabilityQualifierWithMigrationStatus - ): NullabilityQualifierWithMigrationStatus { - // TODO: this probably behaves really weirdly when some of those are warnings. - if (a.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return b - if (b.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return a - if (a.qualifier == NullabilityQualifier.NULLABLE) return b - if (b.qualifier == NullabilityQualifier.NULLABLE) return a - assert(a.qualifier == b.qualifier && a.qualifier == NullabilityQualifier.NOT_NULL) { - "Expected everything is NOT_NULL, but $a and $b are found" - } - - return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL) - } - - private fun TypeParameterDescriptor.boundsNullability(): NullabilityQualifierWithMigrationStatus? { - // Do not use bounds from Kotlin-defined type parameters - if (this !is LazyJavaTypeParameterDescriptor || upperBounds.all(KotlinType::isError)) return null - - if (upperBounds.all(KotlinType::isNullabilityFlexible)) { - if (upperBounds.any { it is FlexibleTypeWithEnhancement && !it.enhancement.isNullable() }) { - return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL, isForWarningOnly = true) - } - - if (upperBounds.any { it is FlexibleTypeWithEnhancement && it.enhancement.isNullable() }) { - return NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NULLABLE, isForWarningOnly = true) - } - - return null - } - - val resultingQualifier = - if (upperBounds.any { !it.isNullable() }) NullabilityQualifier.NOT_NULL else NullabilityQualifier.NULLABLE - - return NullabilityQualifierWithMigrationStatus(resultingQualifier) - } - - private fun computeIndexedQualifiersForOverride(predefined: TypeEnhancementInfo?): (Int) -> JavaTypeQualifiers { - val indexedFromSupertypes = fromOverridden.map { it.toIndexed() } - val indexedThisType = fromOverride.toIndexed() - - // The covariant case may be hard, e.g. in the superclass the return may be Super, but in the subclass it may be Derived, which - // is declared to extend Super, 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! in the subclass and { List, (Mutable)List! } from superclasses - // Note that `this` is flexible here, so it's equal to it's bounds - val onlyHeadTypeConstructor = isCovariant && fromOverridden.any { !KotlinTypeChecker.DEFAULT.equalTypes(it, fromOverride) } - - val treeSize = if (onlyHeadTypeConstructor) 1 else indexedThisType.size - val computedResult = Array(treeSize) { index -> - val qualifiers = indexedThisType[index].extractQualifiersFromAnnotations() - 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.getOrElse(index) { JavaTypeQualifiers.NONE } } - } - - private fun T.flattenTree(result: MutableList, children: (T) -> Iterable?) { - result.add(this) - children(this)?.forEach { it.flattenTree(result, children) } - } - - private fun T.flattenTree(children: (T) -> Iterable?): List = - ArrayList(1).also { flattenTree(it, children) } - - private fun KotlinType.extractAndMergeDefaultQualifiers(oldQualifiers: JavaTypeQualifiersByElementType?) = - containerContext.components.annotationTypeQualifierResolver.extractAndMergeDefaultQualifiers(oldQualifiers, annotations) - - private fun KotlinType.toIndexed(): List = - TypeAndDefaultQualifiers(this, extractAndMergeDefaultQualifiers(containerContext.defaultTypeQualifiers), null).flattenTree { - // Enhancement of raw type arguments may enter a loop. - if (isSuperTypesEnhancement && it.type is RawType) return@flattenTree null - - it.type?.arguments?.zip(it.type.constructor.parameters) { arg, parameter -> - if (arg.isStarProjection) - TypeAndDefaultQualifiers(null, it.defaultQualifiers, parameter) - else - TypeAndDefaultQualifiers(arg.type, arg.type.extractAndMergeDefaultQualifiers(it.defaultQualifiers), parameter) - } - } -} - -private data class TypeAndDefaultQualifiers( - val type: KotlinType?, - val defaultQualifiers: JavaTypeQualifiersByElementType?, - val typeParameterForArgument: TypeParameterDescriptor? -) - -private fun KotlinType.isNullabilityFlexible(): Boolean { - val flexibility = unwrap() as? FlexibleType ?: return false - return flexibility.lowerBound.isMarkedNullable != flexibility.upperBound.isMarkedNullable + override val TypeParameterMarker.isFromJava: Boolean + get() = this is LazyJavaTypeParameterDescriptor }