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
@@ -14111,6 +14111,16 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
runTest("compiler/testData/diagnostics/tests/j+k/types/arrayList.kt"); 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") @TestMetadata("returnCollection.kt")
public void testReturnCollection() throws Exception { public void testReturnCollection() throws Exception {
runTest("compiler/testData/diagnostics/tests/j+k/types/returnCollection.kt"); runTest("compiler/testData/diagnostics/tests/j+k/types/returnCollection.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.PackagePartProvider
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
import org.jetbrains.kotlin.platform.TargetPlatform 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.calls.tower.ImplicitsExtensionsResolutionFilter
import org.jetbrains.kotlin.resolve.createContainer
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import org.jetbrains.kotlin.resolve.jvm.JvmDiagnosticComponents import org.jetbrains.kotlin.resolve.jvm.JvmDiagnosticComponents
import org.jetbrains.kotlin.resolve.jvm.multiplatform.OptionalAnnotationPackageFragmentProvider import org.jetbrains.kotlin.resolve.jvm.multiplatform.OptionalAnnotationPackageFragmentProvider
@@ -129,7 +131,10 @@ fun StorageComponentContainer.configureJavaSpecificComponents(
useInstance(javaClassTracker ?: JavaClassesTracker.Default) useInstance(javaClassTracker ?: JavaClassesTracker.Default)
useInstance( useInstance(
JavaResolverSettings.create(isReleaseCoroutines = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)) JavaResolverSettings.create(
isReleaseCoroutines = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
correctNullabilityForNotNullTypeParameter = languageVersionSettings.supportsFeature(LanguageFeature.ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated),
)
) )
useImpl<FilesByFacadeFqNameIndexer>() useImpl<FilesByFacadeFqNameIndexer>()
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.resolve.jvm.checkers package org.jetbrains.kotlin.resolve.jvm.checkers
import org.jetbrains.kotlin.cfg.WhenChecker import org.jetbrains.kotlin.cfg.WhenChecker
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens 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.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerContext
import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
class JavaNullabilityChecker : AdditionalTypeChecker { 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( doCheckType(
expressionType, expressionType,
c.expectedType, c.expectedType,
{ c.dataFlowValueFactory.createDataFlowValue(expression, expressionType, c) }, { dataFlowValue },
c.dataFlowInfo c.dataFlowInfo
) { expectedType, actualType -> ) { expectedType, actualType ->
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, 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( override fun checkReceiver(
receiverParameter: ReceiverParameterDescriptor, receiverParameter: ReceiverParameterDescriptor,
receiverArgument: ReceiverValue, receiverArgument: ReceiverValue,
@@ -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(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(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, MAP.put(RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS,
"Unsafe use of a nullable receiver of type {0}", RENDER_TYPE); "Unsafe use of a nullable receiver of type {0}", RENDER_TYPE);
@@ -121,6 +121,9 @@ public interface ErrorsJvm {
DiagnosticFactory2<KtElement, KotlinType, KotlinType> NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS DiagnosticFactory2<KtElement, KotlinType, KotlinType> NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS
= DiagnosticFactory2.create(WARNING); = DiagnosticFactory2.create(WARNING);
DiagnosticFactory2<KtElement, KotlinType, KotlinType> NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER
= DiagnosticFactory2.create(WARNING);
DiagnosticFactory1<KtElement, KotlinType> RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS DiagnosticFactory1<KtElement, KotlinType> RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS
= DiagnosticFactory1.create(WARNING); = DiagnosticFactory1.create(WARNING);
@@ -0,0 +1,28 @@
// FILE: SLRUMap.java
// !LANGUAGE: +ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated
import org.jetbrains.annotations.NotNull;
import java.util.List;
public interface SLRUMap<V> {
void takeV(@NotNull V value);
<E> void takeE(@NotNull E value);
void takeVList(@NotNull List<@NotNull V> value);
<E> void takeEList(@NotNull List<@NotNull E> value);
}
// FILE: main.kt
fun <V> SLRUMap<V>.getOrPut(value: V, l: List<V>) {
<!INAPPLICABLE_CANDIDATE!>takeV<!>(value)
takeVList(l)
takeE(value)
takeEList(l)
if (value != null) {
takeV(value)
takeE(value)
}
}
@@ -0,0 +1,28 @@
// FILE: SLRUMap.java
// !LANGUAGE: +ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated
import org.jetbrains.annotations.NotNull;
import java.util.List;
public interface SLRUMap<V> {
void takeV(@NotNull V value);
<E> void takeE(@NotNull E value);
void takeVList(@NotNull List<@NotNull V> value);
<E> void takeEList(@NotNull List<@NotNull E> value);
}
// FILE: main.kt
fun <V> SLRUMap<V>.getOrPut(value: V, l: List<V>) {
takeV(<!TYPE_MISMATCH!>value<!>)
takeVList(<!TYPE_MISMATCH!>l<!>)
takeE(<!TYPE_MISMATCH!>value<!>)
takeEList(<!TYPE_MISMATCH!>l<!>)
if (value != null) {
takeV(<!DEBUG_INFO_SMARTCAST!>value<!>)
takeE(<!DEBUG_INFO_SMARTCAST!>value<!>)
}
}
@@ -0,0 +1,13 @@
package
public fun </*0*/ V> SLRUMap<V>.getOrPut(/*0*/ value: V, /*1*/ l: kotlin.collections.List<V>): kotlin.Unit
public interface SLRUMap</*0*/ V : kotlin.Any!> {
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 </*0*/ E : kotlin.Any!> takeE(/*0*/ @org.jetbrains.annotations.NotNull value: E!!): kotlin.Unit
public abstract fun </*0*/ E : kotlin.Any!> 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
}
@@ -0,0 +1,28 @@
// FILE: SLRUMap.java
// !LANGUAGE: -ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated
import org.jetbrains.annotations.NotNull;
import java.util.List;
public interface SLRUMap<V> {
void takeV(@NotNull V value);
<E> void takeE(@NotNull E value);
void takeVList(@NotNull List<@NotNull V> value);
<E> void takeEList(@NotNull List<@NotNull E> value);
}
// FILE: main.kt
fun <V> SLRUMap<V>.getOrPut(value: V, l: List<V>) {
<!INAPPLICABLE_CANDIDATE!>takeV<!>(value)
takeVList(l)
takeE(value)
takeEList(l)
if (value != null) {
takeV(value)
takeE(value)
}
}
@@ -0,0 +1,28 @@
// FILE: SLRUMap.java
// !LANGUAGE: -ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated
import org.jetbrains.annotations.NotNull;
import java.util.List;
public interface SLRUMap<V> {
void takeV(@NotNull V value);
<E> void takeE(@NotNull E value);
void takeVList(@NotNull List<@NotNull V> value);
<E> void takeEList(@NotNull List<@NotNull E> value);
}
// FILE: main.kt
fun <V> SLRUMap<V>.getOrPut(value: V, l: List<V>) {
takeV(<!NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER!>value<!>)
takeVList(<!NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER!>l<!>)
takeE(<!NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER!>value<!>)
takeEList(<!NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER!>l<!>)
if (value != null) {
takeV(value)
takeE(value)
}
}
@@ -0,0 +1,13 @@
package
public fun </*0*/ V> SLRUMap<V>.getOrPut(/*0*/ value: V, /*1*/ l: kotlin.collections.List<V>): kotlin.Unit
public interface SLRUMap</*0*/ V : kotlin.Any!> {
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 </*0*/ E : kotlin.Any!> takeE(/*0*/ @org.jetbrains.annotations.NotNull value: E): kotlin.Unit
public abstract fun </*0*/ E : kotlin.Any!> 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
}
@@ -22,5 +22,5 @@ interface TypePredicate : (KotlinType) -> Boolean {
fun <T : Any?> TypePredicate.expectedTypeFor(keys: Iterable<T>): Map<T, TypePredicate> = fun <T : Any?> TypePredicate.expectedTypeFor(keys: Iterable<T>): Map<T, TypePredicate> =
keys.fold(SmartFMap.emptyMap<T, TypePredicate>()) { map, key -> keys.fold(SmartFMap.emptyMap<T, TypePredicate>()) { map, key ->
map.plus(key, this) map.plus(<!NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER!>key<!>, this)
} }
@@ -14118,6 +14118,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali
runTest("compiler/testData/diagnostics/tests/j+k/types/arrayList.kt"); 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") @TestMetadata("returnCollection.kt")
public void testReturnCollection() throws Exception { public void testReturnCollection() throws Exception {
runTest("compiler/testData/diagnostics/tests/j+k/types/returnCollection.kt"); runTest("compiler/testData/diagnostics/tests/j+k/types/returnCollection.kt");
@@ -14113,6 +14113,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/j+k/types/arrayList.kt"); 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") @TestMetadata("returnCollection.kt")
public void testReturnCollection() throws Exception { public void testReturnCollection() throws Exception {
runTest("compiler/testData/diagnostics/tests/j+k/types/returnCollection.kt"); runTest("compiler/testData/diagnostics/tests/j+k/types/returnCollection.kt");
@@ -134,6 +134,7 @@ enum class LanguageFeature(
ProperArrayConventionSetterWithDefaultCalls(KOTLIN_1_5, kind = OTHER), ProperArrayConventionSetterWithDefaultCalls(KOTLIN_1_5, kind = OTHER),
DisableCompatibilityModeForNewInference(KOTLIN_1_5, defaultState = LanguageFeature.State.DISABLED), DisableCompatibilityModeForNewInference(KOTLIN_1_5, defaultState = LanguageFeature.State.DISABLED),
AdaptedCallableReferenceAgainstReflectiveType(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 // Temporarily disabled, see KT-27084/KT-22379
SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX), SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.load.java.JavaClassFinder
import org.jetbrains.kotlin.load.java.JavaClassesTracker import org.jetbrains.kotlin.load.java.JavaClassesTracker
import org.jetbrains.kotlin.load.java.components.JavaPropertyInitializerEvaluator import org.jetbrains.kotlin.load.java.components.JavaPropertyInitializerEvaluator
import org.jetbrains.kotlin.load.java.components.JavaResolverCache 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.components.SignaturePropagator
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
import org.jetbrains.kotlin.load.java.sources.JavaSourceElementFactory 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.DeserializedDescriptorResolver
import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider 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.serialization.deserialization.ErrorReporter
import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker
@@ -82,16 +82,24 @@ class JavaResolverComponents(
interface JavaResolverSettings { interface JavaResolverSettings {
val isReleaseCoroutines: Boolean val isReleaseCoroutines: Boolean
val correctNullabilityForNotNullTypeParameter: Boolean
object Default : JavaResolverSettings { object Default : JavaResolverSettings {
override val isReleaseCoroutines: Boolean override val isReleaseCoroutines: Boolean
get() = false get() = false
override val correctNullabilityForNotNullTypeParameter: Boolean
get() = false
} }
companion object { companion object {
fun create(isReleaseCoroutines: Boolean): JavaResolverSettings = fun create(
isReleaseCoroutines: Boolean,
correctNullabilityForNotNullTypeParameter: Boolean
): JavaResolverSettings =
object : JavaResolverSettings { object : JavaResolverSettings {
override val isReleaseCoroutines get() = isReleaseCoroutines 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.Annotations
import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations
import org.jetbrains.kotlin.load.java.JvmAnnotationNames 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.lazy.types.RawTypeImpl
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.MUTABLE import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.MUTABLE
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.READ_ONLY import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.READ_ONLY
@@ -51,7 +52,7 @@ enum class TypeComponentPosition {
INFLEXIBLE INFLEXIBLE
} }
class JavaTypeEnhancement { class JavaTypeEnhancement(private val javaResolverSettings: JavaResolverSettings) {
private open class Result(open val type: KotlinType, val subtreeSize: Int, val wereChanges: Boolean) { private open class Result(open val type: KotlinType, val subtreeSize: Int, val wereChanges: Boolean) {
val typeIfChanged: KotlinType? get() = type.takeIf { wereChanges } val typeIfChanged: KotlinType? get() = type.takeIf { wereChanges }
@@ -144,12 +145,18 @@ class JavaTypeEnhancement {
enhancedNullability 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 nullabilityForWarning = enhancedNullabilityAnnotations != null && effectiveQualifiers.isNullabilityQualifierForWarning
val result = if (nullabilityForWarning) wrapEnhancement(enhancement) else enhancement val result = if (nullabilityForWarning) wrapEnhancement(enhancement) else enhancement
return SimpleResult(result as SimpleType, subtreeSize, wereChanges = true) 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) { 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.JavaResolverCache
import org.jetbrains.kotlin.load.java.components.SignaturePropagator import org.jetbrains.kotlin.load.java.components.SignaturePropagator
import org.jetbrains.kotlin.load.java.lazy.* 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.java.typeEnhancement.SignatureEnhancement
import org.jetbrains.kotlin.load.kotlin.* import org.jetbrains.kotlin.load.kotlin.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -116,7 +117,7 @@ fun makeLazyJavaPackageFragmentFromClassLoaderProvider(
JavaPropertyInitializerEvaluator.DoNothing, SamConversionResolverImpl(storageManager, emptyList()), RuntimeSourceElementFactory, JavaPropertyInitializerEvaluator.DoNothing, SamConversionResolverImpl(storageManager, emptyList()), RuntimeSourceElementFactory,
singleModuleClassResolver, packagePartProvider, SupertypeLoopChecker.EMPTY, LookupTracker.DO_NOTHING, module, singleModuleClassResolver, packagePartProvider, SupertypeLoopChecker.EMPTY, LookupTracker.DO_NOTHING, module,
ReflectionTypes(module, notFoundClasses), annotationTypeQualifierResolver, ReflectionTypes(module, notFoundClasses), annotationTypeQualifierResolver,
SignatureEnhancement(annotationTypeQualifierResolver, Jsr305State.DISABLED), SignatureEnhancement(annotationTypeQualifierResolver, Jsr305State.DISABLED, JavaTypeEnhancement(JavaResolverSettings.Default)),
JavaClassesTracker.Default, JavaResolverSettings.Default, NewKotlinTypeChecker.Default JavaClassesTracker.Default, JavaResolverSettings.Default, NewKotlinTypeChecker.Default
) )
@@ -140,4 +141,4 @@ fun makeDeserializationComponentsForJava(
binaryClassAnnotationAndConstantLoader, lazyJavaPackageFragmentProvider, notFoundClasses, binaryClassAnnotationAndConstantLoader, lazyJavaPackageFragmentProvider, notFoundClasses,
RuntimeErrorReporter, LookupTracker.DO_NOTHING, ContractDeserializer.DEFAULT, NewKotlinTypeChecker.Default 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.checker.NullabilityChecker
import org.jetbrains.kotlin.types.model.DefinitelyNotNullTypeMarker import org.jetbrains.kotlin.types.model.DefinitelyNotNullTypeMarker
import org.jetbrains.kotlin.types.refinement.TypeRefinement import org.jetbrains.kotlin.types.refinement.TypeRefinement
import org.jetbrains.kotlin.types.typeUtil.canHaveUndefinedNullability
abstract class DelegatingSimpleType : SimpleType() { abstract class DelegatingSimpleType : SimpleType() {
protected abstract val delegate: 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 { DefinitelyNotNullTypeMarker {
companion object { companion object {
internal fun makeDefinitelyNotNull(type: UnwrappedType): DefinitelyNotNullType? { internal fun makeDefinitelyNotNull(
type: UnwrappedType,
useCorrectedNullabilityForTypeParameters: Boolean = false
): DefinitelyNotNullType? {
return when { return when {
type is DefinitelyNotNullType -> type type is DefinitelyNotNullType -> type
makesSenseToBeDefinitelyNotNull(type) -> { makesSenseToBeDefinitelyNotNull(type, useCorrectedNullabilityForTypeParameters) -> {
if (type is FlexibleType) { if (type is FlexibleType) {
assert(type.lowerBound.constructor == type.upperBound.constructor) { 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" "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 else -> null
} }
} }
private fun makesSenseToBeDefinitelyNotNull(type: UnwrappedType): Boolean = private fun makesSenseToBeDefinitelyNotNull(
type.canHaveUndefinedNullability() && !NullabilityChecker.isSubtypeOfAny(type) 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 override val delegate: SimpleType
@@ -129,10 +159,10 @@ class DefinitelyNotNullType private constructor(val original: SimpleType) : Dele
delegate.constructor.declarationDescriptor is TypeParameterDescriptor delegate.constructor.declarationDescriptor is TypeParameterDescriptor
override fun substitutionResult(replacement: KotlinType): KotlinType = override fun substitutionResult(replacement: KotlinType): KotlinType =
replacement.unwrap().makeDefinitelyNotNullOrNotNull() replacement.unwrap().makeDefinitelyNotNullOrNotNull(useCorrectedNullabilityForTypeParameters)
override fun replaceAnnotations(newAnnotations: Annotations): DefinitelyNotNullType = override fun replaceAnnotations(newAnnotations: Annotations): DefinitelyNotNullType =
DefinitelyNotNullType(delegate.replaceAnnotations(newAnnotations)) DefinitelyNotNullType(delegate.replaceAnnotations(newAnnotations), useCorrectedNullabilityForTypeParameters)
override fun makeNullableAsSpecified(newNullability: Boolean): SimpleType = override fun makeNullableAsSpecified(newNullability: Boolean): SimpleType =
if (newNullability) delegate.makeNullableAsSpecified(newNullability) else this if (newNullability) delegate.makeNullableAsSpecified(newNullability) else this
@@ -140,22 +170,22 @@ class DefinitelyNotNullType private constructor(val original: SimpleType) : Dele
override fun toString(): String = "$delegate!!" override fun toString(): String = "$delegate!!"
@TypeRefinement @TypeRefinement
override fun replaceDelegate(delegate: SimpleType) = DefinitelyNotNullType(delegate) override fun replaceDelegate(delegate: SimpleType) = DefinitelyNotNullType(delegate, useCorrectedNullabilityForTypeParameters)
} }
val KotlinType.isDefinitelyNotNullType: Boolean val KotlinType.isDefinitelyNotNullType: Boolean
get() = unwrap() is DefinitelyNotNullType get() = unwrap() is DefinitelyNotNullType
fun SimpleType.makeSimpleTypeDefinitelyNotNullOrNotNull(): SimpleType = fun SimpleType.makeSimpleTypeDefinitelyNotNullOrNotNull(useCorrectedNullabilityForTypeParameters: Boolean = false): SimpleType =
DefinitelyNotNullType.makeDefinitelyNotNull(this) DefinitelyNotNullType.makeDefinitelyNotNull(this, useCorrectedNullabilityForTypeParameters)
?: makeIntersectionTypeDefinitelyNotNullOrNotNull() ?: makeIntersectionTypeDefinitelyNotNullOrNotNull()
?: makeNullableAsSpecified(false) ?: makeNullableAsSpecified(false)
fun NewCapturedType.withNotNullProjection() = fun NewCapturedType.withNotNullProjection() =
NewCapturedType(captureStatus, constructor, lowerType, annotations, isMarkedNullable, isProjectionNotNull = true) NewCapturedType(captureStatus, constructor, lowerType, annotations, isMarkedNullable, isProjectionNotNull = true)
fun UnwrappedType.makeDefinitelyNotNullOrNotNull(): UnwrappedType = fun UnwrappedType.makeDefinitelyNotNullOrNotNull(useCorrectedNullabilityForTypeParameters: Boolean = false): UnwrappedType =
DefinitelyNotNullType.makeDefinitelyNotNull(this) DefinitelyNotNullType.makeDefinitelyNotNull(this, useCorrectedNullabilityForTypeParameters)
?: makeIntersectionTypeDefinitelyNotNullOrNotNull() ?: makeIntersectionTypeDefinitelyNotNullOrNotNull()
?: makeNullableAsSpecified(false) ?: makeNullableAsSpecified(false)
@@ -227,11 +227,6 @@ fun KotlinType.containsTypeProjectionsInTopLevelArguments(): Boolean {
return possiblyInnerType.arguments.any { it.isStarProjection || it.projectionKind != Variance.INVARIANT } 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 val TypeParameterDescriptor.representativeUpperBound: KotlinType
get() { get() {
assert(upperBounds.isNotEmpty()) { "Upper bounds should not be empty: $this" } assert(upperBounds.isNotEmpty()) { "Upper bounds should not be empty: $this" }
@@ -28,6 +28,8 @@ abstract class AbstractTypeCheckerContext : TypeSystemContext {
return type return type
} }
open fun customIsSubtypeOf(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean = true
abstract val isErrorTypeEqualsToAnything: Boolean abstract val isErrorTypeEqualsToAnything: Boolean
abstract val isStubTypeEqualsToAnything: Boolean abstract val isStubTypeEqualsToAnything: Boolean
@@ -175,6 +177,8 @@ object AbstractTypeChecker {
): Boolean { ): Boolean {
if (subType === superType) return true if (subType === superType) return true
if (!context.customIsSubtypeOf(subType, superType)) return false
return with(context) { return with(context) {
completeIsSubTypeOf(prepareType(refineType(subType)), prepareType(refineType(superType)), isFromNullabilityConstraint) completeIsSubTypeOf(prepareType(refineType(subType)), prepareType(refineType(superType)), isFromNullabilityConstraint)
} }