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:
Victor Petukhov
2019-12-16 11:46:10 +03:00
parent 68576da494
commit 437a26684d
41 changed files with 985 additions and 85 deletions
@@ -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
@@ -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