FE: in NullMarked scope, enhance T! to at least T

#KT-44436 Fixed
This commit is contained in:
pyos
2021-09-07 11:30:02 +02:00
committed by Victor Petukhov
parent f793afbfea
commit afa1d18cc2
9 changed files with 23 additions and 44 deletions
@@ -34,8 +34,8 @@ public open class BK : B {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
} }
@org.jspecify.nullness.NullMarked public open class C</*0*/ E : C<E!>> : SelfType<E> { @org.jspecify.nullness.NullMarked public open class C</*0*/ E : C<E>> : SelfType<E> {
public constructor C</*0*/ E : C<E!>>() public constructor C</*0*/ E : C<E>>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun foo(/*0*/ t: E): kotlin.Unit public open override /*1*/ /*fake_override*/ fun foo(/*0*/ t: E): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@@ -58,8 +58,8 @@ public open class CKN : C<@org.jspecify.nullness.Nullable CK?> {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
} }
@org.jspecify.nullness.NullMarked public open class SelfType</*0*/ T : SelfType<T!>> { @org.jspecify.nullness.NullMarked public open class SelfType</*0*/ T : SelfType<T>> {
public constructor SelfType</*0*/ T : SelfType<T!>>() public constructor SelfType</*0*/ T : SelfType<T>>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open fun foo(/*0*/ t: T): kotlin.Unit public open fun foo(/*0*/ t: T): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@@ -82,5 +82,6 @@ public open class UtilGenericNullnessUnspecifiedBound {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members // Static members
public open fun </*0*/ K : @org.jspecify.nullness.NullnessUnspecified kotlin.Any!> getFooOfK(): Foo<K!> public open fun </*0*/ K : @org.jspecify.nullness.NullnessUnspecified kotlin.Any!> getFooOfK(): Foo<K>
} }
@@ -1,18 +0,0 @@
// JSPECIFY_STATE: strict
// !LANGUAGE: +TypeEnhancementImprovementsInStrictMode
// MUTE_FOR_PSI_CLASS_FILES_READING
// FILE: Foo.java
import org.jspecify.nullness.*;
@NullMarked
public class Foo<T extends @Nullable Object> {
static <T extends Comparable> Foo<T> create() {
return new Foo<>();
}
}
// FILE: main.kt
fun test(): Foo<String> {
return <!DEBUG_INFO_EXPRESSION_TYPE("Foo<kotlin.String>")!>Foo.create()<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// JSPECIFY_STATE: strict // JSPECIFY_STATE: strict
// !LANGUAGE: +TypeEnhancementImprovementsInStrictMode // !LANGUAGE: +TypeEnhancementImprovementsInStrictMode
// MUTE_FOR_PSI_CLASS_FILES_READING // MUTE_FOR_PSI_CLASS_FILES_READING
@@ -14,5 +15,5 @@ public class Foo<T extends @Nullable Object> {
// FILE: main.kt // FILE: main.kt
fun test(): Foo<String> { fun test(): Foo<String> {
return <!DEBUG_INFO_EXPRESSION_TYPE("Foo<(kotlin.String..kotlin.String?)>")!>Foo.create()<!> return <!DEBUG_INFO_EXPRESSION_TYPE("Foo<kotlin.String>")!>Foo.create()<!>
} }
@@ -9,5 +9,6 @@ public fun test(): Foo<kotlin.String>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members // Static members
public/*package*/ open fun </*0*/ T : (kotlin.Comparable<(raw) kotlin.Any?>..kotlin.Comparable<kotlin.Nothing>?)> create(): Foo<T!> public/*package*/ open fun </*0*/ T : (kotlin.Comparable<(raw) kotlin.Any?>..kotlin.Comparable<kotlin.Nothing>?)> create(): Foo<T>
} }
@@ -34,7 +34,7 @@ public open class BK : B {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
} }
@org.jspecify.nullness.NullMarked public open class C</*0*/ E : C<E!>!> : SelfType<E!> { @org.jspecify.nullness.NullMarked public open class C</*0*/ E : C<E!>!> : SelfType<E> {
public constructor C</*0*/ E : C<E!>!>() public constructor C</*0*/ E : C<E!>!>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun foo(/*0*/ t: E!): kotlin.Unit public open override /*1*/ /*fake_override*/ fun foo(/*0*/ t: E!): kotlin.Unit
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.name.FqName
data class JavaDefaultQualifiers( data class JavaDefaultQualifiers(
val nullabilityQualifier: NullabilityQualifierWithMigrationStatus, val nullabilityQualifier: NullabilityQualifierWithMigrationStatus,
val qualifierApplicabilityTypes: Collection<AnnotationQualifierApplicabilityType>, val qualifierApplicabilityTypes: Collection<AnnotationQualifierApplicabilityType>,
val affectsTypeParameterBasedTypes: Boolean = nullabilityQualifier.qualifier == NullabilityQualifier.NOT_NULL val definitelyNotNull: Boolean = nullabilityQualifier.qualifier == NullabilityQualifier.NOT_NULL
) )
val TYPE_QUALIFIER_NICKNAME_FQNAME = FqName("javax.annotation.meta.TypeQualifierNickname") val TYPE_QUALIFIER_NICKNAME_FQNAME = FqName("javax.annotation.meta.TypeQualifierNickname")
@@ -33,7 +33,7 @@ val JSPECIFY_DEFAULT_ANNOTATIONS = mapOf(
JSPECIFY_NULL_MARKED to JavaDefaultQualifiers( JSPECIFY_NULL_MARKED to JavaDefaultQualifiers(
NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL), NullabilityQualifierWithMigrationStatus(NullabilityQualifier.NOT_NULL),
DEFAULT_JSPECIFY_APPLICABILITY, DEFAULT_JSPECIFY_APPLICABILITY,
affectsTypeParameterBasedTypes = false definitelyNotNull = false
) )
) )
@@ -104,7 +104,6 @@ abstract class AbstractSignatureParts<TAnnotation : Any> {
else -> AnnotationQualifierApplicabilityType.TYPE_USE else -> AnnotationQualifierApplicabilityType.TYPE_USE
} }
val defaultTypeQualifier = defaultQualifiers?.get(applicabilityType) val defaultTypeQualifier = defaultQualifiers?.get(applicabilityType)
?.takeIf { (it.affectsTypeParameterBasedTypes || typeParameterUse == null) }
val referencedParameterBoundsNullability = typeParameterUse?.boundsNullability val referencedParameterBoundsNullability = typeParameterUse?.boundsNullability
// For type parameter uses, we have *three* options: // For type parameter uses, we have *three* options:
@@ -112,7 +111,7 @@ abstract class AbstractSignatureParts<TAnnotation : Any> {
// happens if T is bounded by @NotNull (technically !! is redundant) or context says unannotated // happens if T is bounded by @NotNull (technically !! is redundant) or context says unannotated
// type parameters are non-null; // type parameters are non-null;
// T - NOT_NULL, isNotNullTypeParameter = false // T - NOT_NULL, isNotNullTypeParameter = false
// happens if T is bounded by @Nullable or context says unannotated types in general are non-null; // happens if T is bounded by @Nullable (should it?) or context says unannotated types in general are non-null;
// T? - NULLABLE, isNotNullTypeParameter = false // T? - NULLABLE, isNotNullTypeParameter = false
// happens if context says unannotated types in general are nullable. // happens if context says unannotated types in general are nullable.
// For other types, this is more straightforward (just take nullability from the context). // For other types, this is more straightforward (just take nullability from the context).
@@ -122,27 +121,22 @@ abstract class AbstractSignatureParts<TAnnotation : Any> {
?: defaultTypeQualifier?.nullabilityQualifier ?: defaultTypeQualifier?.nullabilityQualifier
val definitelyNotNull = val definitelyNotNull =
referencedParameterBoundsNullability?.qualifier == NullabilityQualifier.NOT_NULL || referencedParameterBoundsNullability?.qualifier == NullabilityQualifier.NOT_NULL ||
(typeParameterUse != null && defaultTypeQualifier?.nullabilityQualifier?.qualifier == NullabilityQualifier.NOT_NULL) (typeParameterUse != null && defaultTypeQualifier?.definitelyNotNull == true)
// We should also enhance this type to satisfy the bound of the type parameter it is instantiating: // We should also enhance this type to satisfy the bound of the type parameter it is instantiating:
// for C<T extends @NotNull V>, C<X!> becomes C<X!!> regardless of the above. // for C<T extends @NotNull V>, C<X!> becomes C<X!!> regardless of the above.
val substitutedParameterBoundsNullability = typeParameterForArgument?.boundsNullability val substitutedParameterBoundsNullability = typeParameterForArgument?.boundsNullability
val result = when { ?.let { if (it.qualifier == NullabilityQualifier.NULLABLE) it.copy(qualifier = NullabilityQualifier.FORCE_FLEXIBILITY) else it }
substitutedParameterBoundsNullability == null -> defaultNullability val result = mostSpecific(substitutedParameterBoundsNullability, 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, definitelyNotNull, result?.isForWarningOnly == true) return JavaTypeQualifiers(result?.qualifier, annotationsMutability, definitelyNotNull, result?.isForWarningOnly == true)
} }
private fun mostSpecific( private fun mostSpecific(
a: NullabilityQualifierWithMigrationStatus, a: NullabilityQualifierWithMigrationStatus?,
b: NullabilityQualifierWithMigrationStatus b: NullabilityQualifierWithMigrationStatus?
): NullabilityQualifierWithMigrationStatus { ): NullabilityQualifierWithMigrationStatus? {
if (a == null) return b
if (b == null) return a
// TODO: this probably behaves really weirdly when some of those are warnings. // TODO: this probably behaves really weirdly when some of those are warnings.
if (a.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return b if (a.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return b
if (b.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return a if (b.qualifier == NullabilityQualifier.FORCE_FLEXIBILITY) return a
@@ -16,7 +16,7 @@ enum class MutabilityQualifier {
MUTABLE MUTABLE
} }
class JavaTypeQualifiers constructor( class JavaTypeQualifiers(
val nullability: NullabilityQualifier?, val nullability: NullabilityQualifier?,
val mutability: MutabilityQualifier?, val mutability: MutabilityQualifier?,
val definitelyNotNull: Boolean, val definitelyNotNull: Boolean,