diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index c3a03bf7a80..d0dda40e6c1 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -14111,6 +14111,16 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/j+k/types/arrayList.kt"); } + @TestMetadata("notNullTypeParameterWithKotlinNullable.kt") + public void testNotNullTypeParameterWithKotlinNullable() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.kt"); + } + + @TestMetadata("notNullTypeParameterWithKotlinNullableWarnings.kt") + public void testNotNullTypeParameterWithKotlinNullableWarnings() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.kt"); + } + @TestMetadata("returnCollection.kt") public void testReturnCollection() throws Exception { runTest("compiler/testData/diagnostics/tests/j+k/types/returnCollection.kt"); diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt index 13b5d813fd9..b2e8bb52aa0 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/frontend/java/di/injection.kt @@ -41,8 +41,10 @@ import org.jetbrains.kotlin.load.kotlin.DeserializationComponentsForJava import org.jetbrains.kotlin.load.kotlin.PackagePartProvider import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory import org.jetbrains.kotlin.platform.TargetPlatform -import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.TargetEnvironment import org.jetbrains.kotlin.resolve.calls.tower.ImplicitsExtensionsResolutionFilter +import org.jetbrains.kotlin.resolve.createContainer import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.JvmDiagnosticComponents import org.jetbrains.kotlin.resolve.jvm.multiplatform.OptionalAnnotationPackageFragmentProvider @@ -129,7 +131,10 @@ fun StorageComponentContainer.configureJavaSpecificComponents( useInstance(javaClassTracker ?: JavaClassesTracker.Default) useInstance( - JavaResolverSettings.create(isReleaseCoroutines = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)) + JavaResolverSettings.create( + isReleaseCoroutines = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines), + correctNullabilityForNotNullTypeParameter = languageVersionSettings.supportsFeature(LanguageFeature.ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated), + ) ) useImpl() diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt index 491d1468edb..a175101638b 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JavaNullabilityChecker.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.resolve.jvm.checkers import org.jetbrains.kotlin.cfg.WhenChecker +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens @@ -35,15 +36,30 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerContext import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker +import org.jetbrains.kotlin.types.model.KotlinTypeMarker class JavaNullabilityChecker : AdditionalTypeChecker { - override fun checkType(expression: KtExpression, expressionType: KotlinType, expressionTypeWithSmartCast: KotlinType, c: ResolutionContext<*>) { + override fun checkType( + expression: KtExpression, + expressionType: KotlinType, + expressionTypeWithSmartCast: KotlinType, + c: ResolutionContext<*> + ) { + val dataFlowValue by lazy(LazyThreadSafetyMode.NONE) { + c.dataFlowValueFactory.createDataFlowValue(expression, expressionType, c) + } + + if (isWrongTypeParameterNullabilityForSubtyping(expressionType, c) { dataFlowValue }) { + c.trace.report(ErrorsJvm.NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER.on(expression, c.expectedType, expressionType)) + } + doCheckType( expressionType, c.expectedType, - { c.dataFlowValueFactory.createDataFlowValue(expression, expressionType, c) }, + { dataFlowValue }, c.dataFlowInfo ) { expectedType, actualType -> c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, expectedType, actualType)) @@ -103,6 +119,45 @@ class JavaNullabilityChecker : AdditionalTypeChecker { } } + private fun isWrongTypeParameterNullabilityForSubtyping( + expressionType: KotlinType, + c: ResolutionContext<*>, + dataFlowValueForWholeExpression: () -> DataFlowValue + ): Boolean { + if (c.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated)) return false + if (TypeUtils.noExpectedType(c.expectedType)) return false + + var metWrongNullabilityInsideArguments = false + + val typeContext: AbstractTypeCheckerContext = object : ClassicTypeCheckerContext(errorTypeEqualsToAnything = true) { + private var expectsTypeArgument = false + override fun customIsSubtypeOf(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean { + + if (isNullableTypeAgainstNotNullTypeParameter(subType as KotlinType, superType as KotlinType)) { + // data flow value is only checked for top-level types + if (expectsTypeArgument || c.dataFlowInfo.getStableNullability(dataFlowValueForWholeExpression()) != Nullability.NOT_NULL) { + metWrongNullabilityInsideArguments = true + return false + } + } + + if (!expectsTypeArgument) { + expectsTypeArgument = true + } + return true + } + } + + AbstractTypeChecker.isSubtypeOf(typeContext, expressionType, c.expectedType) + + return metWrongNullabilityInsideArguments + } + + private fun isNullableTypeAgainstNotNullTypeParameter( + subType: KotlinType, + superType: KotlinType + ) = superType is NotNullTypeVariable && subType.isNullable() + override fun checkReceiver( receiverParameter: ReceiverParameterDescriptor, receiverArgument: ReceiverValue, diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index 2608142f60c..31bade49248 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -82,6 +82,10 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC, "Using non-JVM static members protected in the superclass companion is unsupported yet"); MAP.put(NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, "Type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE); + MAP.put(NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER, + "Type mismatch: type parameter with nullable bounds is used {1} is used where {0} was expected. This warning will become an error soon", + RENDER_TYPE, RENDER_TYPE + ); MAP.put(RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, "Unsafe use of a nullable receiver of type {0}", RENDER_TYPE); diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 6ebcdd8c0f1..a279f9b75d0 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -121,6 +121,9 @@ public interface ErrorsJvm { DiagnosticFactory2 NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS = DiagnosticFactory2.create(WARNING); + DiagnosticFactory2 NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER + = DiagnosticFactory2.create(WARNING); + DiagnosticFactory1 RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS = DiagnosticFactory1.create(WARNING); diff --git a/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.fir.kt b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.fir.kt new file mode 100644 index 00000000000..2279c091050 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.fir.kt @@ -0,0 +1,28 @@ +// FILE: SLRUMap.java +// !LANGUAGE: +ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated + +import org.jetbrains.annotations.NotNull; +import java.util.List; + +public interface SLRUMap { + void takeV(@NotNull V value); + void takeE(@NotNull E value); + + void takeVList(@NotNull List<@NotNull V> value); + void takeEList(@NotNull List<@NotNull E> value); +} + +// FILE: main.kt + +fun SLRUMap.getOrPut(value: V, l: List) { + takeV(value) + takeVList(l) + + takeE(value) + takeEList(l) + + if (value != null) { + takeV(value) + takeE(value) + } +} diff --git a/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.kt b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.kt new file mode 100644 index 00000000000..c09043b7615 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.kt @@ -0,0 +1,28 @@ +// FILE: SLRUMap.java +// !LANGUAGE: +ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated + +import org.jetbrains.annotations.NotNull; +import java.util.List; + +public interface SLRUMap { + void takeV(@NotNull V value); + void takeE(@NotNull E value); + + void takeVList(@NotNull List<@NotNull V> value); + void takeEList(@NotNull List<@NotNull E> value); +} + +// FILE: main.kt + +fun SLRUMap.getOrPut(value: V, l: List) { + takeV(value) + takeVList(l) + + takeE(value) + takeEList(l) + + if (value != null) { + takeV(value) + takeE(value) + } +} diff --git a/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.txt b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.txt new file mode 100644 index 00000000000..2957bc8922a --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.txt @@ -0,0 +1,13 @@ +package + +public fun SLRUMap.getOrPut(/*0*/ value: V, /*1*/ l: kotlin.collections.List): kotlin.Unit + +public interface SLRUMap { + 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 abstract fun takeE(/*0*/ @org.jetbrains.annotations.NotNull value: E!!): kotlin.Unit + public abstract fun takeEList(/*0*/ @org.jetbrains.annotations.NotNull value: kotlin.collections.(Mutable)List<@org.jetbrains.annotations.NotNull E!!>): kotlin.Unit + public abstract fun takeV(/*0*/ @org.jetbrains.annotations.NotNull value: V!!): kotlin.Unit + public abstract fun takeVList(/*0*/ @org.jetbrains.annotations.NotNull value: kotlin.collections.(Mutable)List<@org.jetbrains.annotations.NotNull V!!>): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.fir.kt b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.fir.kt new file mode 100644 index 00000000000..70694844ab7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.fir.kt @@ -0,0 +1,28 @@ +// FILE: SLRUMap.java +// !LANGUAGE: -ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated + +import org.jetbrains.annotations.NotNull; +import java.util.List; + +public interface SLRUMap { + void takeV(@NotNull V value); + void takeE(@NotNull E value); + + void takeVList(@NotNull List<@NotNull V> value); + void takeEList(@NotNull List<@NotNull E> value); +} + +// FILE: main.kt + +fun SLRUMap.getOrPut(value: V, l: List) { + takeV(value) + takeVList(l) + + takeE(value) + takeEList(l) + + if (value != null) { + takeV(value) + takeE(value) + } +} diff --git a/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.kt b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.kt new file mode 100644 index 00000000000..9496564d17f --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.kt @@ -0,0 +1,28 @@ +// FILE: SLRUMap.java +// !LANGUAGE: -ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated + +import org.jetbrains.annotations.NotNull; +import java.util.List; + +public interface SLRUMap { + void takeV(@NotNull V value); + void takeE(@NotNull E value); + + void takeVList(@NotNull List<@NotNull V> value); + void takeEList(@NotNull List<@NotNull E> value); +} + +// FILE: main.kt + +fun SLRUMap.getOrPut(value: V, l: List) { + takeV(value) + takeVList(l) + + takeE(value) + takeEList(l) + + if (value != null) { + takeV(value) + takeE(value) + } +} diff --git a/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.txt b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.txt new file mode 100644 index 00000000000..385cc65e60b --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.txt @@ -0,0 +1,13 @@ +package + +public fun SLRUMap.getOrPut(/*0*/ value: V, /*1*/ l: kotlin.collections.List): kotlin.Unit + +public interface SLRUMap { + 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 abstract fun takeE(/*0*/ @org.jetbrains.annotations.NotNull value: E): kotlin.Unit + public abstract fun takeEList(/*0*/ @org.jetbrains.annotations.NotNull value: kotlin.collections.(Mutable)List<@org.jetbrains.annotations.NotNull E>): kotlin.Unit + public abstract fun takeV(/*0*/ @org.jetbrains.annotations.NotNull value: V): kotlin.Unit + public abstract fun takeVList(/*0*/ @org.jetbrains.annotations.NotNull value: kotlin.collections.(Mutable)List<@org.jetbrains.annotations.NotNull V>): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt index 8d588891567..2c1cd1514b7 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt @@ -22,5 +22,5 @@ interface TypePredicate : (KotlinType) -> Boolean { fun TypePredicate.expectedTypeFor(keys: Iterable): Map = keys.fold(SmartFMap.emptyMap()) { map, key -> - map.plus(key, this) + map.plus(key, this) } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 7e5a914ad9e..2fa7a868e33 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -14118,6 +14118,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/j+k/types/arrayList.kt"); } + @TestMetadata("notNullTypeParameterWithKotlinNullable.kt") + public void testNotNullTypeParameterWithKotlinNullable() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.kt"); + } + + @TestMetadata("notNullTypeParameterWithKotlinNullableWarnings.kt") + public void testNotNullTypeParameterWithKotlinNullableWarnings() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.kt"); + } + @TestMetadata("returnCollection.kt") public void testReturnCollection() throws Exception { runTest("compiler/testData/diagnostics/tests/j+k/types/returnCollection.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 57586c64594..9cc48fc0a44 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -14113,6 +14113,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/j+k/types/arrayList.kt"); } + @TestMetadata("notNullTypeParameterWithKotlinNullable.kt") + public void testNotNullTypeParameterWithKotlinNullable() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullable.kt"); + } + + @TestMetadata("notNullTypeParameterWithKotlinNullableWarnings.kt") + public void testNotNullTypeParameterWithKotlinNullableWarnings() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/types/notNullTypeParameterWithKotlinNullableWarnings.kt"); + } + @TestMetadata("returnCollection.kt") public void testReturnCollection() throws Exception { runTest("compiler/testData/diagnostics/tests/j+k/types/returnCollection.kt"); diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index ff530cf4f5e..84f8d7ca0bc 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -134,6 +134,7 @@ enum class LanguageFeature( ProperArrayConventionSetterWithDefaultCalls(KOTLIN_1_5, kind = OTHER), DisableCompatibilityModeForNewInference(KOTLIN_1_5, defaultState = LanguageFeature.State.DISABLED), AdaptedCallableReferenceAgainstReflectiveType(KOTLIN_1_5, defaultState = LanguageFeature.State.DISABLED), + ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated(KOTLIN_1_5, kind = BUG_FIX), // Temporarily disabled, see KT-27084/KT-22379 SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX), diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt index 19dc82a80a7..ca51f2bd81d 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/context.kt @@ -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 } } } diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt index fbfd9cb173a..b6d40f1d971 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt @@ -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.compositeAnnotationsOrSingle() = when (size) { diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/RuntimeModuleData.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/RuntimeModuleData.kt index 1af25bbf58c..798a67004b9 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/RuntimeModuleData.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/RuntimeModuleData.kt @@ -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 ) -} \ No newline at end of file +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt index 7720071e26a..9026ca8d7f7 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/SpecialTypes.kt @@ -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) diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index eac39c75ca6..7228e8eb4df 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -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" } diff --git a/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt b/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt index ade2be673ef..b63079e1e9c 100644 --- a/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt +++ b/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt @@ -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) }