NI: Prefer nullable lower bound to flexible one when substitution of type variable is performed and remember flexibility of type parameters based on flexibility of its upper bounds
^KT-32435 Fixed
This commit is contained in:
+18
-6
@@ -21,11 +21,10 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.InfixCallNoInfixModifier
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.InvokeConventionCallNoOperatorModifier
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.VisibilityError
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.contains
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
internal object CheckInstantiationOfAbstractClass : ResolutionPart() {
|
||||
@@ -151,7 +150,7 @@ internal object CreateFreshVariablesSubstitutor : ResolutionPart() {
|
||||
if (knownTypeArgument != null) {
|
||||
csBuilder.addEqualityConstraint(
|
||||
freshVariable.defaultType,
|
||||
knownTypeArgument.unwrap(),
|
||||
getTypePreservingFlexibilityWrtTypeVariable(knownTypeArgument.unwrap(), freshVariable),
|
||||
KnownTypeParameterConstraintPosition(knownTypeArgument)
|
||||
)
|
||||
continue
|
||||
@@ -162,7 +161,7 @@ internal object CreateFreshVariablesSubstitutor : ResolutionPart() {
|
||||
if (typeArgument is SimpleTypeArgument) {
|
||||
csBuilder.addEqualityConstraint(
|
||||
freshVariable.defaultType,
|
||||
typeArgument.type,
|
||||
getTypePreservingFlexibilityWrtTypeVariable(typeArgument.type, freshVariable),
|
||||
ExplicitTypeParameterConstraintPosition(typeArgument)
|
||||
)
|
||||
} else {
|
||||
@@ -173,6 +172,19 @@ internal object CreateFreshVariablesSubstitutor : ResolutionPart() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun TypeParameterDescriptor.shouldBeFlexible(): Boolean {
|
||||
return upperBounds.any {
|
||||
it.isFlexible() || ((it.constructor.declarationDescriptor as? TypeParameterDescriptor)?.run { shouldBeFlexible() } ?: false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTypePreservingFlexibilityWrtTypeVariable(
|
||||
type: KotlinType,
|
||||
typeVariable: TypeVariableFromCallableDescriptor
|
||||
) = if (typeVariable.originalTypeParameter.shouldBeFlexible()) {
|
||||
KotlinTypeFactory.flexibleType(type.makeNotNullable().lowerIfFlexible(), type.makeNullable().upperIfFlexible())
|
||||
} else type
|
||||
|
||||
fun createToFreshVariableSubstitutorAndAddInitialConstraints(
|
||||
candidateDescriptor: CallableDescriptor,
|
||||
csBuilder: ConstraintSystemOperation
|
||||
|
||||
+17
-12
@@ -146,7 +146,7 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck
|
||||
*
|
||||
* => Foo <: T! -- (Foo!! .. Foo) <: T
|
||||
*
|
||||
* Foo? <: T! -- (Foo!! .. Foo?) <: T
|
||||
* Foo? <: T! -- Foo? <: T
|
||||
*
|
||||
*
|
||||
* (Foo..Bar) <: T! --
|
||||
@@ -173,7 +173,11 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck
|
||||
when (subType) {
|
||||
is SimpleTypeMarker ->
|
||||
// Foo <: T! -- (Foo!! .. Foo) <: T
|
||||
createFlexibleType(subType.makeSimpleTypeDefinitelyNotNullOrNotNull(), subType)
|
||||
if (subType.isMarkedNullable()) {
|
||||
subType // prefer nullable type to flexible one: `Foo? <: (T..T?)` => lowerConstraint = `Foo?`
|
||||
} else {
|
||||
createFlexibleType(subType, subType.withNullability(true))
|
||||
}
|
||||
|
||||
is FlexibleTypeMarker ->
|
||||
// (Foo..Bar) <: T! -- (Foo!! .. Bar) <: T
|
||||
@@ -197,23 +201,24 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck
|
||||
}
|
||||
|
||||
/**
|
||||
* T! <: Foo <=> T <: Foo
|
||||
* T! <: Foo <=> T <: Foo..Foo?
|
||||
* T? <: Foo <=> T <: Foo && Nothing? <: Foo
|
||||
* T <: Foo -- leave as is
|
||||
*/
|
||||
private fun simplifyUpperConstraint(typeVariable: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val typeVariable = typeVariable.lowerBoundIfFlexible()
|
||||
val typeVariableLowerBound = typeVariable.lowerBoundIfFlexible()
|
||||
val simplifiedSuperType = if (typeVariableLowerBound.isDefinitelyNotNullType()) {
|
||||
superType.withNullability(true)
|
||||
} else if (typeVariable.isFlexible() && superType is SimpleTypeMarker) {
|
||||
createFlexibleType(superType, superType.withNullability(true))
|
||||
} else superType
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val superType = if (typeVariable.isDefinitelyNotNullType()) superType.withNullability(true) else superType
|
||||
addUpperConstraint(typeVariableLowerBound.typeConstructor(), simplifiedSuperType)
|
||||
|
||||
addUpperConstraint(typeVariable.typeConstructor(), superType)
|
||||
|
||||
if (typeVariable.isMarkedNullable()) {
|
||||
if (typeVariableLowerBound.isMarkedNullable()) {
|
||||
// here is important that superType is singleClassifierType
|
||||
return superType.anyBound(this::isMyTypeVariable) ||
|
||||
isSubtypeOfByTypeChecker(nullableNothingType(), superType)
|
||||
return simplifiedSuperType.anyBound(this::isMyTypeVariable) ||
|
||||
isSubtypeOfByTypeChecker(nullableNothingType(), simplifiedSuperType)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user