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:
Denis Zharkov
2016-01-25 14:39:26 +03:00
parent 08dd675fcd
commit 11a96ee8c8
26 changed files with 451 additions and 45 deletions
@@ -0,0 +1,28 @@
// FILE: A.java
import org.jetbrains.annotations.NotNull;
public interface A<T> {
void foo(@NotNull T x);
}
// FILE: B.java
public class B<E> {
public void foo(E x) {}
}
// FILE: C.java
public class C<F> extends B<F> implements A<F> {
public static C<String> create() { return null; }
public void foo(F x) {}
}
// FILE: main.kt
fun test() {
C.create().foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)
C.create().foo("")
C<String>().foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)
C<String?>().foo(<!NULL_FOR_NONNULL_TYPE!>null<!>)
C<String?>().foo("")
}
@@ -0,0 +1,30 @@
package
public /*synthesized*/ fun </*0*/ T : kotlin.Any!> A(/*0*/ function: (T!) -> kotlin.Unit): A<T>
public fun test(): kotlin.Unit
public interface A</*0*/ T : kotlin.Any!> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(/*0*/ @org.jetbrains.annotations.NotNull() x: T): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class B</*0*/ E : kotlin.Any!> {
public constructor B</*0*/ E : kotlin.Any!>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open fun foo(/*0*/ x: E!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class C</*0*/ F : kotlin.Any!> : B<F!>, A<F!> {
public constructor C</*0*/ F : kotlin.Any!>()
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*2*/ fun foo(/*0*/ x: F): kotlin.Unit
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun create(): C<kotlin.String!>!
}
@@ -0,0 +1,28 @@
// FILE: A.kt
import org.jetbrains.annotations.NotNull;
public interface A<T> {
fun foo(x: T)
}
// FILE: B.java
public class B<E> {
public void foo(E x) {}
}
// FILE: C.java
public class C<F> extends B<F> implements A<F> {
public static C<String> create() { return null; }
public void foo(F x) {}
}
// FILE: main.kt
fun test() {
C.create().foo(null)
C.create().foo("")
C<String>().foo(null)
C<String?>().foo(null)
C<String?>().foo("")
}
@@ -0,0 +1,29 @@
package
public fun test(): kotlin.Unit
public interface A</*0*/ T> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract fun foo(/*0*/ x: T): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class B</*0*/ E : kotlin.Any!> {
public constructor B</*0*/ E : kotlin.Any!>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open fun foo(/*0*/ x: E!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class C</*0*/ F : kotlin.Any!> : B<F!>, A<F!> {
public constructor C</*0*/ F : kotlin.Any!>()
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*2*/ fun foo(/*0*/ x: F!): kotlin.Unit
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun create(): C<kotlin.String!>!
}
@@ -0,0 +1,18 @@
// FILE: A.java
import org.jetbrains.annotations.NotNull;
public class A {
public static <T> void bar(@NotNull T x, T y) { }
public static String platformString() { return null; }
}
// FILE: k.kt
fun test() {
A.<!TYPE_INFERENCE_INCORPORATION_ERROR!>bar<!>(<!NULL_FOR_NONNULL_TYPE!>null<!>, "")
A.bar<String>(<!NULL_FOR_NONNULL_TYPE!>null<!>, "")
A.bar<String?>(<!NULL_FOR_NONNULL_TYPE!>null<!>, "")
A.<!TYPE_INFERENCE_INCORPORATION_ERROR!>bar<!>(<!NULL_FOR_NONNULL_TYPE!>null<!>, A.platformString())
}
@@ -0,0 +1,14 @@
package
public fun test(): kotlin.Unit
public open class A {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun </*0*/ T : kotlin.Any!> bar(/*0*/ @org.jetbrains.annotations.NotNull() x: T, /*1*/ y: T!): kotlin.Unit
public open fun platformString(): kotlin.String!
}
@@ -0,0 +1,23 @@
// FILE: A.java
import java.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class A<T> {
public static A<String> create() {
return null;
}
@NotNull
public T bar() {
}
}
// FILE: k.kt
fun test() {
A.create().bar()<!UNNECESSARY_SAFE_CALL!>?.<!>length
A<String?>().bar()<!UNNECESSARY_SAFE_CALL!>?.<!>length
}
@@ -0,0 +1,14 @@
package
public fun test(): kotlin.Unit
public open class A</*0*/ T : kotlin.Any!> {
public constructor A</*0*/ T : kotlin.Any!>()
@org.jetbrains.annotations.NotNull() public open fun bar(): T
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun create(): A<kotlin.String!>!
}
@@ -0,0 +1,26 @@
// FILE: A.java
import java.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class A<T> {
public static A<String> create() {
return null;
}
public void bar(@NotNull T x) {
}
}
// FILE: k.kt
fun test() {
A.create().bar(<!NULL_FOR_NONNULL_TYPE!>null<!>)
A.create().bar("")
A<String>().bar(<!NULL_FOR_NONNULL_TYPE!>null<!>)
A<String?>().bar(<!NULL_FOR_NONNULL_TYPE!>null<!>)
A<String?>().bar("")
}
@@ -0,0 +1,14 @@
package
public fun test(): kotlin.Unit
public open class A</*0*/ T : kotlin.Any!> {
public constructor A</*0*/ T : kotlin.Any!>()
public open fun bar(/*0*/ @org.jetbrains.annotations.NotNull() x: T): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun create(): A<kotlin.String!>!
}
@@ -0,0 +1,41 @@
// FILE: A.java
import java.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class A<T> {
public void bar(@NotNull T x) {
}
}
// FILE: B1.java
public class B1 extends A<String> {
// real override
public void bar(String x) {
}
}
// FILE: B2.java
public class B2 extends A<String> {
// fake override bar
}
// FILE: k.kt
class C1 : A<String?>() {
override fun bar(x: String) {}
}
class C2 : A<String?>() {
<!NOTHING_TO_OVERRIDE!>override<!> fun bar(x: String?) {}
}
fun test() {
B1().bar(<!NULL_FOR_NONNULL_TYPE!>null<!>)
B2().bar(<!NULL_FOR_NONNULL_TYPE!>null<!>)
C1().bar(<!NULL_FOR_NONNULL_TYPE!>null<!>)
}
@@ -0,0 +1,44 @@
package
public fun test(): kotlin.Unit
public open class A</*0*/ T : kotlin.Any!> {
public constructor A</*0*/ T : kotlin.Any!>()
public open fun bar(/*0*/ @org.jetbrains.annotations.NotNull() x: T): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class B1 : A<kotlin.String!> {
public constructor B1()
public open override /*1*/ fun bar(/*0*/ x: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public open class B2 : A<kotlin.String!> {
public constructor B2()
public open override /*1*/ /*fake_override*/ fun bar(/*0*/ @org.jetbrains.annotations.NotNull() x: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class C1 : A<kotlin.String?> {
public constructor C1()
public open override /*1*/ fun bar(/*0*/ x: kotlin.String): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class C2 : A<kotlin.String?> {
public constructor C2()
public open override /*1*/ /*fake_override*/ fun bar(/*0*/ @org.jetbrains.annotations.NotNull() x: kotlin.String): kotlin.Unit
public open fun bar(/*0*/ x: kotlin.String?): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -22,6 +22,6 @@ val doubleList: List<Double?> = null!!
fun main() {
Test.rawField.foo("", doubleList)
Test.rawField.foo(<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>null<!>, doubleList)
Test.rawField.foo(<!NULL_FOR_NONNULL_TYPE!>null<!>, doubleList)
Test.DerivedRawA().foo(<!NULL_FOR_NONNULL_TYPE!>null<!>, doubleList)
}
@@ -24,12 +24,8 @@ public interface B2 extends A<String> {
import org.jetbrains.annotations.*;
public interface B3 extends A<String> {
// inconsistent override, second parameter type is platform
// TODO: first one should be platform too, but it's not because when substituting T -> String!,
// value parameter type becomes platform and it can be overridden with @NotNull String.
// Conceptual problem: we can use type parameter as T?, but there is no way to specify same semantics as in Java `@NotNull T`.
// override /*1*/ fun foo(/*0*/ org.jetbrains.annotations.Nullable() x: kotlin.String?, /*1*/ org.jetbrains.annotations.NotNull() y: kotlin.String!): kotlin.Unit
// inconsistent override, both parameters are platform
// override /*1*/ fun foo(/*0*/ org.jetbrains.annotations.Nullable() x: kotlin.String!, /*1*/ org.jetbrains.annotations.NotNull() y: kotlin.String!): kotlin.Unit
void foo(@Nullable String x, @NotNull String y);
}
@@ -1,7 +1,7 @@
package
public /*synthesized*/ fun </*0*/ T : kotlin.Any!> A(/*0*/ function: (T!, T!) -> kotlin.Unit): A<T>
public /*synthesized*/ fun B1(/*0*/ function: (kotlin.String!, kotlin.String?) -> kotlin.Unit): B1
public /*synthesized*/ fun B1(/*0*/ function: (kotlin.String, kotlin.String?) -> kotlin.Unit): B1
public /*synthesized*/ fun B2(/*0*/ function: (kotlin.String!, kotlin.String!) -> kotlin.Unit): B2
public /*synthesized*/ fun B3(/*0*/ function: (kotlin.String!, kotlin.String!) -> kotlin.Unit): B3
@@ -28,7 +28,7 @@ public interface B2 : A<kotlin.String!> {
public interface B3 : A<kotlin.String!> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ fun foo(/*0*/ @org.jetbrains.annotations.Nullable() x: kotlin.String?, /*1*/ @org.jetbrains.annotations.NotNull() y: kotlin.String!): kotlin.Unit
public abstract override /*1*/ fun foo(/*0*/ @org.jetbrains.annotations.Nullable() x: kotlin.String!, /*1*/ @org.jetbrains.annotations.NotNull() y: kotlin.String!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -42,7 +42,7 @@ public interface C1 : A<kotlin.String> {
public interface C2 : A<kotlin.String?> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public abstract override /*1*/ /*fake_override*/ fun foo(/*0*/ @org.jetbrains.annotations.NotNull() x: kotlin.String?, /*1*/ @org.jetbrains.annotations.Nullable() y: kotlin.String?): kotlin.Unit
public abstract override /*1*/ /*fake_override*/ fun foo(/*0*/ @org.jetbrains.annotations.NotNull() x: kotlin.String, /*1*/ @org.jetbrains.annotations.Nullable() y: kotlin.String?): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
+1 -1
View File
@@ -38,6 +38,6 @@ fun main(a: A<String>, a1: A<String?>) {
a.baz(<!NULL_FOR_NONNULL_TYPE!>null<!>)<!UNSAFE_CALL!>.<!>length
a1.baz("")!!.length
a1.baz(null)!!.length
a1.baz(<!NULL_FOR_NONNULL_TYPE!>null<!>)!!.length
}
@@ -38,5 +38,5 @@ fun main(a: A<String>, a1: A<String?>) {
a.baz(<!NULL_FOR_NONNULL_TYPE!>null<!>)<!UNSAFE_CALL!>.<!>length
a1.baz("")!!.length
a1.baz(null)!!.length
a1.baz(<!NULL_FOR_NONNULL_TYPE!>null<!>)!!.length
}
+1 -1
View File
@@ -38,5 +38,5 @@ fun main(a: A<String>, a1: A<String?>) {
a.baz(<!NULL_FOR_NONNULL_TYPE!>null<!>)<!UNSAFE_CALL!>.<!>length
a1.baz("")!!.length
a1.baz(null)!!.length
a1.baz(<!NULL_FOR_NONNULL_TYPE!>null<!>)!!.length
}
@@ -4,7 +4,7 @@ public interface TypeParamOfClass {
public interface Sub</*0*/ T : kotlin.Any!> : test.TypeParamOfClass.Super<T!> {
public abstract override /*1*/ /*fake_override*/ fun dummy(): kotlin.Unit
public abstract override /*1*/ fun foo(): T!
public abstract override /*1*/ fun foo(): T
}
public interface Super</*0*/ T : kotlin.Any!> {
@@ -4,7 +4,7 @@ public interface TypeParamOfClassSubstituted {
public interface Sub : test.TypeParamOfClassSubstituted.Super<kotlin.String!> {
public abstract override /*1*/ /*fake_override*/ fun dummy(): kotlin.Unit
public abstract override /*1*/ fun foo(): kotlin.String!
public abstract override /*1*/ fun foo(): kotlin.String
}
public interface Super</*0*/ T : kotlin.Any!> {
@@ -12435,6 +12435,51 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
}
}
@TestMetadata("compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class NotNullTypeParameter extends AbstractDiagnosticsTest {
public void testAllFilesPresentInNotNullTypeParameter() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("enhancementFromAnnotation.kt")
public void testEnhancementFromAnnotation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/enhancementFromAnnotation.kt");
doTest(fileName);
}
@TestMetadata("enhancementFromKotlin.kt")
public void testEnhancementFromKotlin() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/enhancementFromKotlin.kt");
doTest(fileName);
}
@TestMetadata("methodTypeParameter.kt")
public void testMethodTypeParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/methodTypeParameter.kt");
doTest(fileName);
}
@TestMetadata("noInheritanceReturnType.kt")
public void testNoInheritanceReturnType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceReturnType.kt");
doTest(fileName);
}
@TestMetadata("noInheritanceValueParameter.kt")
public void testNoInheritanceValueParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/noInheritanceValueParameter.kt");
doTest(fileName);
}
@TestMetadata("substitutionInSuperType.kt")
public void testSubstitutionInSuperType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/substitutionInSuperType.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -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))
}
}
@@ -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)
)
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.types
import org.jetbrains.kotlin.descriptors.PossiblyInnerType
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
interface TypeCapability
@@ -41,6 +40,14 @@ class SingletonTypeCapabilities(private val clazz: Class<*>, private val typeCap
}
}
fun <T : TypeCapability> TypeCapabilities.addCapability(clazz: Class<T>, typeCapability: T): TypeCapabilities {
if (getCapability(clazz) === typeCapability) return this
val newCapabilities = SingletonTypeCapabilities(clazz, typeCapability)
if (this === TypeCapabilities.NONE) return newCapabilities
return CompositeTypeCapabilities(this, newCapabilities)
}
inline fun <reified T : TypeCapability> KotlinType.getCapability(): T? = getCapability(T::class.java)
interface Specificity : TypeCapability {
@@ -117,11 +117,13 @@ fun KotlinType.computeNewSubstitution(
return composedSubstitution
}
@JvmOverloads
fun KotlinType.replace(
newArguments: List<TypeProjection>,
newAnnotations: Annotations = annotations
newArguments: List<TypeProjection> = arguments,
newAnnotations: Annotations = annotations,
newCapabilities: TypeCapabilities = capabilities
): KotlinType {
if (newArguments.isEmpty() && newAnnotations === annotations) return this
if (newArguments.isEmpty() && newAnnotations === annotations && newCapabilities === capabilities) return this
if (newArguments.isEmpty()) {
return KotlinTypeImpl.create(
@@ -131,7 +133,7 @@ fun KotlinType.replace(
arguments,
substitution,
memberScope,
capabilities
newCapabilities
)
}
@@ -150,7 +152,7 @@ fun KotlinType.replace(
newArguments,
newSubstitution,
newScope,
capabilities
newCapabilities
)
}
@@ -57,6 +57,8 @@ fun KotlinType.isNullableAny(): Boolean = KotlinBuiltIns.isNullableAny(this)
fun KotlinType.isBoolean(): Boolean = KotlinBuiltIns.isBoolean(this)
fun KotlinType.isBooleanOrNullableBoolean(): Boolean = KotlinBuiltIns.isBooleanOrNullableBoolean(this)
fun KotlinType.isTypeParameter(): Boolean = TypeUtils.isTypeParameter(this)
fun KotlinType?.isArrayOfNothing(): Boolean {
if (this == null || !KotlinBuiltIns.isArray(this)) return false