Introduce not null type parameter capability
Java nullability annotations may generate types that currently are not denotable in Kotlin:
class Java {
void <F> foo(@NotNull F f) {}
}
Type of given value parameter should be not nullable under any substitution:
String/String?/String! because of annotation contract.
NB: Currently there is no full analogues for such types in pure kotlin
This commit is contained in:
+35
-1
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.createProjection
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.utils.toReadOnlyList
|
||||
|
||||
// The index in the lambda is the position of the type component:
|
||||
@@ -104,6 +106,12 @@ private fun KotlinType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers
|
||||
typeConstructor, enhancedArguments
|
||||
)
|
||||
|
||||
val newCapabilities =
|
||||
if (effectiveQualifiers.isNotNullTypeParameter)
|
||||
capabilities.addCapability(CustomTypeVariable::class.java, NotNullTypeParameterTypeCapability)
|
||||
else
|
||||
capabilities
|
||||
|
||||
val enhancedType = KotlinTypeImpl.create(
|
||||
newAnnotations,
|
||||
typeConstructor,
|
||||
@@ -113,7 +121,7 @@ private fun KotlinType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers
|
||||
if (enhancedClassifier is ClassDescriptor)
|
||||
enhancedClassifier.getMemberScope(newSubstitution)
|
||||
else enhancedClassifier.getDefaultType().getMemberScope(),
|
||||
capabilities
|
||||
newCapabilities
|
||||
)
|
||||
return Result(enhancedType, globalArgIndex - index)
|
||||
}
|
||||
@@ -192,3 +200,29 @@ private object EnhancedTypeAnnotationDescriptor : AnnotationDescriptor {
|
||||
override fun getSource() = throwError()
|
||||
override fun toString() = "[EnhancedType]"
|
||||
}
|
||||
|
||||
internal object NotNullTypeParameterTypeCapability : CustomTypeVariable {
|
||||
override val isTypeVariable: Boolean
|
||||
get() = true
|
||||
|
||||
override fun substitutionResult(replacement: KotlinType): KotlinType {
|
||||
if (!TypeUtils.isNullableType(replacement) && !replacement.isTypeParameter()) return replacement
|
||||
|
||||
if (replacement.isFlexible()) {
|
||||
with(replacement.flexibility()) {
|
||||
return DelegatingFlexibleType.create(lowerBound.prepareReplacement(), upperBound.prepareReplacement(), extraCapabilities)
|
||||
}
|
||||
}
|
||||
|
||||
return replacement.prepareReplacement()
|
||||
}
|
||||
|
||||
private fun KotlinType.prepareReplacement(): KotlinType {
|
||||
val result = makeNotNullable()
|
||||
if (!this.isTypeParameter()) return result
|
||||
|
||||
return result.replace(
|
||||
newCapabilities = capabilities.addCapability(
|
||||
CustomTypeVariable::class.java, NotNullTypeParameterTypeCapability))
|
||||
}
|
||||
}
|
||||
|
||||
+34
-23
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.load.java.typeEnhancement
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.load.java.*
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.MUTABLE
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.READ_ONLY
|
||||
@@ -25,11 +24,10 @@ import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier.NOT_N
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier.NULLABLE
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.flexibility
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import java.util.*
|
||||
|
||||
enum class NullabilityQualifier {
|
||||
NULLABLE,
|
||||
@@ -41,12 +39,13 @@ enum class MutabilityQualifier {
|
||||
MUTABLE
|
||||
}
|
||||
|
||||
class JavaTypeQualifiers(
|
||||
class JavaTypeQualifiers internal constructor(
|
||||
val nullability: NullabilityQualifier?,
|
||||
val mutability: MutabilityQualifier?
|
||||
val mutability: MutabilityQualifier?,
|
||||
internal val isNotNullTypeParameter: Boolean
|
||||
) {
|
||||
companion object {
|
||||
val NONE = JavaTypeQualifiers(null, null)
|
||||
val NONE = JavaTypeQualifiers(null, null, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,13 +57,13 @@ private fun KotlinType.extractQualifiers(): JavaTypeQualifiers {
|
||||
|
||||
val mapping = JavaToKotlinClassMap.INSTANCE
|
||||
return JavaTypeQualifiers(
|
||||
if (lower.isMarkedNullable()) NULLABLE else if (!upper.isMarkedNullable()) NOT_NULL else null,
|
||||
if (mapping.isReadOnly(lower)) READ_ONLY else if (mapping.isMutable(upper)) MUTABLE else null
|
||||
)
|
||||
if (lower.isMarkedNullable) NULLABLE else if (!upper.isMarkedNullable) NOT_NULL else null,
|
||||
if (mapping.isReadOnly(lower)) READ_ONLY else if (mapping.isMutable(upper)) MUTABLE else null,
|
||||
isNotNullTypeParameter = getCapability<CustomTypeVariable>() is NotNullTypeParameterTypeCapability)
|
||||
}
|
||||
|
||||
private fun Annotations.extractQualifiers(): JavaTypeQualifiers {
|
||||
fun <T: Any> List<FqName>.ifPresent(qualifier: T) = if (any { findAnnotation(it) != null}) qualifier else null
|
||||
private fun KotlinType.extractQualifiersFromAnnotations(): JavaTypeQualifiers {
|
||||
fun <T: Any> List<FqName>.ifPresent(qualifier: T) = if (any { annotations.findAnnotation(it) != null}) qualifier else null
|
||||
|
||||
// These two overloads are just for sake of optimization as in most cases last parameter in second overload is null
|
||||
fun <T: Any> uniqueNotNull(x: T?, y: T?) = if (x == null || y == null || x == y) x ?: y else null
|
||||
@@ -76,7 +75,7 @@ private fun Annotations.extractQualifiers(): JavaTypeQualifiers {
|
||||
|
||||
// Javax/FundBugs NonNull annotation has parameter `when` that determines actual nullability
|
||||
fun FqName.extractQualifierFromAnnotationWithWhen(): NullabilityQualifier? {
|
||||
val annotationDescriptor = findAnnotation(this) ?: return null
|
||||
val annotationDescriptor = annotations.findAnnotation(this) ?: return null
|
||||
return annotationDescriptor.allValueArguments.values.singleOrNull()?.value?.let {
|
||||
enumEntryDescriptor ->
|
||||
if (enumEntryDescriptor !is ClassDescriptor) return@let null
|
||||
@@ -84,13 +83,15 @@ private fun Annotations.extractQualifiers(): JavaTypeQualifiers {
|
||||
} ?: NOT_NULL
|
||||
}
|
||||
|
||||
val nullability = uniqueNotNull(
|
||||
NULLABLE_ANNOTATIONS.ifPresent(NULLABLE),
|
||||
NOT_NULL_ANNOTATIONS.ifPresent(NOT_NULL),
|
||||
JAVAX_NONNULL_ANNOTATION.extractQualifierFromAnnotationWithWhen()
|
||||
)
|
||||
return JavaTypeQualifiers(
|
||||
uniqueNotNull(
|
||||
NULLABLE_ANNOTATIONS.ifPresent(NULLABLE),
|
||||
NOT_NULL_ANNOTATIONS.ifPresent(NOT_NULL),
|
||||
JAVAX_NONNULL_ANNOTATION.extractQualifierFromAnnotationWithWhen()
|
||||
),
|
||||
uniqueNotNull(READ_ONLY_ANNOTATIONS.ifPresent(READ_ONLY), MUTABLE_ANNOTATIONS.ifPresent(MUTABLE))
|
||||
nullability,
|
||||
uniqueNotNull(READ_ONLY_ANNOTATIONS.ifPresent(READ_ONLY), MUTABLE_ANNOTATIONS.ifPresent(MUTABLE)),
|
||||
isNotNullTypeParameter = nullability == NOT_NULL && isTypeParameter()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -143,14 +144,24 @@ fun KotlinType.computeIndexedQualifiersForOverride(fromSupertypes: Collection<Ko
|
||||
private fun KotlinType.computeQualifiersForOverride(fromSupertypes: Collection<KotlinType>, isCovariant: Boolean): JavaTypeQualifiers {
|
||||
val nullabilityFromSupertypes = fromSupertypes.map { it.extractQualifiers().nullability }.filterNotNull().toSet()
|
||||
val mutabilityFromSupertypes = fromSupertypes.map { it.extractQualifiers().mutability }.filterNotNull().toSet()
|
||||
val own = getAnnotations().extractQualifiers()
|
||||
|
||||
val own = extractQualifiersFromAnnotations()
|
||||
|
||||
val isAnyNonNullTypeParameter = own.isNotNullTypeParameter || fromSupertypes.any { it.extractQualifiers().isNotNullTypeParameter }
|
||||
|
||||
fun createJavaTypeQualifiers(nullability: NullabilityQualifier?, mutability: MutabilityQualifier?): JavaTypeQualifiers {
|
||||
if (!isAnyNonNullTypeParameter || nullability != NOT_NULL) return JavaTypeQualifiers(nullability, mutability, false)
|
||||
return JavaTypeQualifiers(
|
||||
nullability, mutability,
|
||||
isNotNullTypeParameter = true)
|
||||
}
|
||||
|
||||
if (isCovariant) {
|
||||
fun <T : Any> Set<T>.selectCovariantly(low: T, high: T, own: T?): T? {
|
||||
val supertypeQualifier = if (low in this) low else if (high in this) high else null
|
||||
return if (supertypeQualifier == low && own == high) null else own ?: supertypeQualifier
|
||||
}
|
||||
return JavaTypeQualifiers(
|
||||
return createJavaTypeQualifiers(
|
||||
nullabilityFromSupertypes.selectCovariantly(NOT_NULL, NULLABLE, own.nullability),
|
||||
mutabilityFromSupertypes.selectCovariantly(MUTABLE, READ_ONLY, own.mutability)
|
||||
)
|
||||
@@ -163,7 +174,7 @@ private fun KotlinType.computeQualifiersForOverride(fromSupertypes: Collection<K
|
||||
// and all qualifiers are discarded
|
||||
return effectiveSet.singleOrNull()
|
||||
}
|
||||
return JavaTypeQualifiers(
|
||||
return createJavaTypeQualifiers(
|
||||
nullabilityFromSupertypes.selectInvariantly(own.nullability),
|
||||
mutabilityFromSupertypes.selectInvariantly(own.mutability)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user