Fix incorrect handling of @NotNull type parameters

^KT-36770 In progress
^KT-40114 Relates
^KT-28785 Relates
This commit is contained in:
Denis Zharkov
2020-07-07 18:27:09 +03:00
parent f1c68a9080
commit 037ff2fa52
21 changed files with 311 additions and 30 deletions
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.load.java.JavaClassFinder
import org.jetbrains.kotlin.load.java.JavaClassesTracker
import org.jetbrains.kotlin.load.java.components.JavaPropertyInitializerEvaluator
import org.jetbrains.kotlin.load.java.components.JavaResolverCache
import org.jetbrains.kotlin.resolve.sam.SamConversionResolver
import org.jetbrains.kotlin.load.java.components.SignaturePropagator
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
import org.jetbrains.kotlin.load.java.sources.JavaSourceElementFactory
@@ -40,6 +39,7 @@ import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
import org.jetbrains.kotlin.resolve.sam.SamConversionResolver
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker
@@ -82,16 +82,24 @@ class JavaResolverComponents(
interface JavaResolverSettings {
val isReleaseCoroutines: Boolean
val correctNullabilityForNotNullTypeParameter: Boolean
object Default : JavaResolverSettings {
override val isReleaseCoroutines: Boolean
get() = false
override val correctNullabilityForNotNullTypeParameter: Boolean
get() = false
}
companion object {
fun create(isReleaseCoroutines: Boolean): JavaResolverSettings =
fun create(
isReleaseCoroutines: Boolean,
correctNullabilityForNotNullTypeParameter: Boolean
): JavaResolverSettings =
object : JavaResolverSettings {
override val isReleaseCoroutines get() = isReleaseCoroutines
override val correctNullabilityForNotNullTypeParameter get() = correctNullabilityForNotNullTypeParameter
}
}
}
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.lazy.JavaResolverSettings
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeImpl
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.MUTABLE
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.READ_ONLY
@@ -51,7 +52,7 @@ enum class TypeComponentPosition {
INFLEXIBLE
}
class JavaTypeEnhancement {
class JavaTypeEnhancement(private val javaResolverSettings: JavaResolverSettings) {
private open class Result(open val type: KotlinType, val subtreeSize: Int, val wereChanges: Boolean) {
val typeIfChanged: KotlinType? get() = type.takeIf { wereChanges }
@@ -144,12 +145,18 @@ class JavaTypeEnhancement {
enhancedNullability
)
val enhancement = if (effectiveQualifiers.isNotNullTypeParameter) NotNullTypeParameter(enhancedType) else enhancedType
val enhancement = if (effectiveQualifiers.isNotNullTypeParameter) notNullTypeParameter(enhancedType) else enhancedType
val nullabilityForWarning = enhancedNullabilityAnnotations != null && effectiveQualifiers.isNullabilityQualifierForWarning
val result = if (nullabilityForWarning) wrapEnhancement(enhancement) else enhancement
return SimpleResult(result as SimpleType, subtreeSize, wereChanges = true)
}
private fun notNullTypeParameter(enhancedType: SimpleType) =
if (javaResolverSettings.correctNullabilityForNotNullTypeParameter)
enhancedType.makeSimpleTypeDefinitelyNotNullOrNotNull(useCorrectedNullabilityForTypeParameters = true)
else
NotNullTypeParameter(enhancedType)
}
private fun List<Annotations>.compositeAnnotationsOrSingle() = when (size) {
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.load.java.components.JavaPropertyInitializerEvaluato
import org.jetbrains.kotlin.load.java.components.JavaResolverCache
import org.jetbrains.kotlin.load.java.components.SignaturePropagator
import org.jetbrains.kotlin.load.java.lazy.*
import org.jetbrains.kotlin.load.java.typeEnhancement.JavaTypeEnhancement
import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement
import org.jetbrains.kotlin.load.kotlin.*
import org.jetbrains.kotlin.name.Name
@@ -116,7 +117,7 @@ fun makeLazyJavaPackageFragmentFromClassLoaderProvider(
JavaPropertyInitializerEvaluator.DoNothing, SamConversionResolverImpl(storageManager, emptyList()), RuntimeSourceElementFactory,
singleModuleClassResolver, packagePartProvider, SupertypeLoopChecker.EMPTY, LookupTracker.DO_NOTHING, module,
ReflectionTypes(module, notFoundClasses), annotationTypeQualifierResolver,
SignatureEnhancement(annotationTypeQualifierResolver, Jsr305State.DISABLED),
SignatureEnhancement(annotationTypeQualifierResolver, Jsr305State.DISABLED, JavaTypeEnhancement(JavaResolverSettings.Default)),
JavaClassesTracker.Default, JavaResolverSettings.Default, NewKotlinTypeChecker.Default
)
@@ -140,4 +141,4 @@ fun makeDeserializationComponentsForJava(
binaryClassAnnotationAndConstantLoader, lazyJavaPackageFragmentProvider, notFoundClasses,
RuntimeErrorReporter, LookupTracker.DO_NOTHING, ContractDeserializer.DEFAULT, NewKotlinTypeChecker.Default
)
}
}
@@ -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" }
@@ -28,6 +28,8 @@ abstract class AbstractTypeCheckerContext : TypeSystemContext {
return type
}
open fun customIsSubtypeOf(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean = true
abstract val isErrorTypeEqualsToAnything: Boolean
abstract val isStubTypeEqualsToAnything: Boolean
@@ -175,6 +177,8 @@ object AbstractTypeChecker {
): Boolean {
if (subType === superType) return true
if (!context.customIsSubtypeOf(subType, superType)) return false
return with(context) {
completeIsSubTypeOf(prepareType(refineType(subType)), prepareType(refineType(superType)), isFromNullabilityConstraint)
}