K2: Fix processing inference lower bound NullableType <: T & Any

Previously, it was led to plainly adding NullableType <: T constraint
which silently led to successful call completion.
What is suggested is just marking such initial constraint
as unsuccessful.

In K1, the error was reported just via additional type checking
mechanism being run after call completion.

^KT-58665 Fixed
This commit is contained in:
Denis.Zharkov
2023-06-06 13:54:49 +02:00
committed by Space Team
parent 7bc04e2c33
commit 0d070f8ba9
19 changed files with 141 additions and 76 deletions
@@ -205,6 +205,13 @@ abstract class TypeCheckerStateForConstraintSystem(
* (Foo & Any .. Bar) <: T -- (Foo!! .. Bar) <: T
*
* => (Foo..Bar) <: T! -- (Foo!! .. Bar) <: T
*
* T & Any
*
* Foo? <: T & Any => ERROR (for K2 only)
*
* Foo..Bar? <: T & Any => Foo..Bar? <: T
* Foo <: T & Any => Foo <: T
*/
private fun simplifyLowerConstraint(
typeVariable: KotlinTypeMarker,
@@ -213,35 +220,41 @@ abstract class TypeCheckerStateForConstraintSystem(
): Boolean = with(extensionTypeContext) {
val lowerConstraint = when (typeVariable) {
is SimpleTypeMarker ->
/*
* Foo <: T -- Foo <: T
* Foo <: T? (T is contained in invariant or contravariant positions of a return type) -- Foo <: T
* Example:
* fun <T> foo(x: T?): Inv<T> {}
* fun <K> main(z: K) { val x = foo(z) }
* Foo <: T? (T isn't contained there) -- Foo!! <: T
* Example:
* fun <T> foo(x: T?) {}
* fun <K> main(z: K) { foo(z) }
*/
if (typeVariable.isMarkedNullable()) {
val typeVariableTypeConstructor = typeVariable.typeConstructor()
val subTypeConstructor = subType.typeConstructor()
val needToMakeDefNotNull = subTypeConstructor.isTypeVariable() ||
typeVariableTypeConstructor !is TypeVariableTypeConstructorMarker ||
!typeVariableTypeConstructor.isContainedInInvariantOrContravariantPositions()
when {
// Foo? (any type which cannot be used as dispatch receiver because of nullability) <: T & Any => ERROR (for K2 only)
isK2 && typeVariable.isDefinitelyNotNullType()
&& !AbstractNullabilityChecker.isSubtypeOfAny(extensionTypeContext, subType) -> return false
/*
* Foo <: T? (T is contained in invariant or contravariant positions of a return type) -- Foo <: T
* Example:
* fun <T> foo(x: T?): Inv<T> {}
* fun <K> main(z: K) { val x = foo(z) }
* Foo <: T? (T isn't contained there) -- Foo!! <: T
* Example:
* fun <T> foo(x: T?) {}
* fun <K> main(z: K) { foo(z) }
*/
typeVariable.isMarkedNullable() -> {
val typeVariableTypeConstructor = typeVariable.typeConstructor()
val subTypeConstructor = subType.typeConstructor()
val needToMakeDefNotNull = subTypeConstructor.isTypeVariable() ||
typeVariableTypeConstructor !is TypeVariableTypeConstructorMarker ||
!typeVariableTypeConstructor.isContainedInInvariantOrContravariantPositions()
val resultType = if (needToMakeDefNotNull) {
subType.makeDefinitelyNotNullOrNotNull()
} else {
if (!isInferenceCompatibilityEnabled && subType is CapturedTypeMarker) {
subType.withNotNullProjection()
val resultType = if (needToMakeDefNotNull) {
subType.makeDefinitelyNotNullOrNotNull()
} else {
subType.withNullability(false)
if (!isInferenceCompatibilityEnabled && subType is CapturedTypeMarker) {
subType.withNotNullProjection()
} else {
subType.withNullability(false)
}
}
if (isInferenceCompatibilityEnabled && resultType is CapturedTypeMarker) resultType.withNotNullProjection() else resultType
}
if (isInferenceCompatibilityEnabled && resultType is CapturedTypeMarker) resultType.withNotNullProjection() else resultType
} else subType
// Foo <: T => Foo <: T
else -> subType
}
is FlexibleTypeMarker -> {
assertFlexibleTypeVariable(typeVariable)
@@ -298,6 +311,7 @@ abstract class TypeCheckerStateForConstraintSystem(
* T! <: Foo <=> T <: Foo & Any..Foo?
* T? <: Foo <=> T <: Foo && Nothing? <: Foo
* T <: Foo -- leave as is
* T & Any <: Foo <=> T <: Foo?
*/
private fun simplifyUpperConstraint(typeVariable: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean = with(extensionTypeContext) {
val typeVariableLowerBound = typeVariable.lowerBoundIfFlexible()