Fix incorrect handling of @NotNull type parameters
^KT-36770 In progress ^KT-40114 Relates ^KT-28785 Relates
This commit is contained in:
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.types.checker.NewTypeVariableConstructor
|
||||
import org.jetbrains.kotlin.types.checker.NullabilityChecker
|
||||
import org.jetbrains.kotlin.types.model.DefinitelyNotNullTypeMarker
|
||||
import org.jetbrains.kotlin.types.refinement.TypeRefinement
|
||||
import org.jetbrains.kotlin.types.typeUtil.canHaveUndefinedNullability
|
||||
|
||||
abstract class DelegatingSimpleType : SimpleType() {
|
||||
protected abstract val delegate: SimpleType
|
||||
@@ -91,15 +90,21 @@ class LazyWrappedType(
|
||||
}
|
||||
}
|
||||
|
||||
class DefinitelyNotNullType private constructor(val original: SimpleType) : DelegatingSimpleType(), CustomTypeVariable,
|
||||
class DefinitelyNotNullType private constructor(
|
||||
val original: SimpleType,
|
||||
private val useCorrectedNullabilityForTypeParameters: Boolean
|
||||
) : DelegatingSimpleType(), CustomTypeVariable,
|
||||
DefinitelyNotNullTypeMarker {
|
||||
|
||||
companion object {
|
||||
internal fun makeDefinitelyNotNull(type: UnwrappedType): DefinitelyNotNullType? {
|
||||
internal fun makeDefinitelyNotNull(
|
||||
type: UnwrappedType,
|
||||
useCorrectedNullabilityForTypeParameters: Boolean = false
|
||||
): DefinitelyNotNullType? {
|
||||
return when {
|
||||
type is DefinitelyNotNullType -> type
|
||||
|
||||
makesSenseToBeDefinitelyNotNull(type) -> {
|
||||
makesSenseToBeDefinitelyNotNull(type, useCorrectedNullabilityForTypeParameters) -> {
|
||||
if (type is FlexibleType) {
|
||||
assert(type.lowerBound.constructor == type.upperBound.constructor) {
|
||||
"DefinitelyNotNullType for flexible type ($type) can be created only from type variable with the same constructor for bounds"
|
||||
@@ -107,15 +112,40 @@ class DefinitelyNotNullType private constructor(val original: SimpleType) : Dele
|
||||
}
|
||||
|
||||
|
||||
DefinitelyNotNullType(type.lowerIfFlexible())
|
||||
DefinitelyNotNullType(type.lowerIfFlexible(), useCorrectedNullabilityForTypeParameters)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun makesSenseToBeDefinitelyNotNull(type: UnwrappedType): Boolean =
|
||||
type.canHaveUndefinedNullability() && !NullabilityChecker.isSubtypeOfAny(type)
|
||||
private fun makesSenseToBeDefinitelyNotNull(
|
||||
type: UnwrappedType,
|
||||
useCorrectedNullabilityForFlexibleTypeParameters: Boolean
|
||||
): Boolean {
|
||||
if (!type.canHaveUndefinedNullability()) return false
|
||||
|
||||
// Replacing `useCorrectedNullabilityForFlexibleTypeParameters` with true for all call-sites seems to be correct
|
||||
// But it seems that it should be a new feature: KT-28785 would be automatically fixed then
|
||||
// (see the tests org.jetbrains.kotlin.spec.checkers.DiagnosticsTestSpecGenerated.NotLinked.Dfa.Pos.test12/13)
|
||||
// So it should be a language feature, but it's hard correctly identify language version settings for all call sites
|
||||
// Thus, we have non-trivial value at org.jetbrains.kotlin.load.java.typeEnhancement.JavaTypeEnhancement.notNullTypeParameter
|
||||
// that run under related language-feature only
|
||||
if (useCorrectedNullabilityForFlexibleTypeParameters && type.constructor.declarationDescriptor is TypeParameterDescriptor) {
|
||||
// Effectively checks if the type is flexible or has nullable bound
|
||||
return TypeUtils.isNullableType(type)
|
||||
}
|
||||
|
||||
// Actually, this code should work for type parameters as well, but it breaks some cases
|
||||
// See KT-40114
|
||||
return !NullabilityChecker.isSubtypeOfAny(type)
|
||||
}
|
||||
|
||||
private fun UnwrappedType.canHaveUndefinedNullability(): Boolean =
|
||||
constructor is NewTypeVariableConstructor ||
|
||||
constructor.declarationDescriptor is TypeParameterDescriptor ||
|
||||
this is NewCapturedType
|
||||
|
||||
}
|
||||
|
||||
override val delegate: SimpleType
|
||||
@@ -129,10 +159,10 @@ class DefinitelyNotNullType private constructor(val original: SimpleType) : Dele
|
||||
delegate.constructor.declarationDescriptor is TypeParameterDescriptor
|
||||
|
||||
override fun substitutionResult(replacement: KotlinType): KotlinType =
|
||||
replacement.unwrap().makeDefinitelyNotNullOrNotNull()
|
||||
replacement.unwrap().makeDefinitelyNotNullOrNotNull(useCorrectedNullabilityForTypeParameters)
|
||||
|
||||
override fun replaceAnnotations(newAnnotations: Annotations): DefinitelyNotNullType =
|
||||
DefinitelyNotNullType(delegate.replaceAnnotations(newAnnotations))
|
||||
DefinitelyNotNullType(delegate.replaceAnnotations(newAnnotations), useCorrectedNullabilityForTypeParameters)
|
||||
|
||||
override fun makeNullableAsSpecified(newNullability: Boolean): SimpleType =
|
||||
if (newNullability) delegate.makeNullableAsSpecified(newNullability) else this
|
||||
@@ -140,22 +170,22 @@ class DefinitelyNotNullType private constructor(val original: SimpleType) : Dele
|
||||
override fun toString(): String = "$delegate!!"
|
||||
|
||||
@TypeRefinement
|
||||
override fun replaceDelegate(delegate: SimpleType) = DefinitelyNotNullType(delegate)
|
||||
override fun replaceDelegate(delegate: SimpleType) = DefinitelyNotNullType(delegate, useCorrectedNullabilityForTypeParameters)
|
||||
}
|
||||
|
||||
val KotlinType.isDefinitelyNotNullType: Boolean
|
||||
get() = unwrap() is DefinitelyNotNullType
|
||||
|
||||
fun SimpleType.makeSimpleTypeDefinitelyNotNullOrNotNull(): SimpleType =
|
||||
DefinitelyNotNullType.makeDefinitelyNotNull(this)
|
||||
fun SimpleType.makeSimpleTypeDefinitelyNotNullOrNotNull(useCorrectedNullabilityForTypeParameters: Boolean = false): SimpleType =
|
||||
DefinitelyNotNullType.makeDefinitelyNotNull(this, useCorrectedNullabilityForTypeParameters)
|
||||
?: makeIntersectionTypeDefinitelyNotNullOrNotNull()
|
||||
?: makeNullableAsSpecified(false)
|
||||
|
||||
fun NewCapturedType.withNotNullProjection() =
|
||||
NewCapturedType(captureStatus, constructor, lowerType, annotations, isMarkedNullable, isProjectionNotNull = true)
|
||||
|
||||
fun UnwrappedType.makeDefinitelyNotNullOrNotNull(): UnwrappedType =
|
||||
DefinitelyNotNullType.makeDefinitelyNotNull(this)
|
||||
fun UnwrappedType.makeDefinitelyNotNullOrNotNull(useCorrectedNullabilityForTypeParameters: Boolean = false): UnwrappedType =
|
||||
DefinitelyNotNullType.makeDefinitelyNotNull(this, useCorrectedNullabilityForTypeParameters)
|
||||
?: makeIntersectionTypeDefinitelyNotNullOrNotNull()
|
||||
?: makeNullableAsSpecified(false)
|
||||
|
||||
|
||||
@@ -227,11 +227,6 @@ fun KotlinType.containsTypeProjectionsInTopLevelArguments(): Boolean {
|
||||
return possiblyInnerType.arguments.any { it.isStarProjection || it.projectionKind != Variance.INVARIANT }
|
||||
}
|
||||
|
||||
fun UnwrappedType.canHaveUndefinedNullability(): Boolean =
|
||||
constructor is NewTypeVariableConstructor ||
|
||||
constructor.declarationDescriptor is TypeParameterDescriptor ||
|
||||
this is NewCapturedType
|
||||
|
||||
val TypeParameterDescriptor.representativeUpperBound: KotlinType
|
||||
get() {
|
||||
assert(upperBounds.isNotEmpty()) { "Upper bounds should not be empty: $this" }
|
||||
|
||||
Reference in New Issue
Block a user