[NI] Check type compatibility before fixation.

If for type variable we have upper and lower bounds, then sometimes
our approximation before fixation give us incorrect result for type
variable and we should chose other bound as result.

Example: Int & Byte <: T <: Byte. If we run approximation for lower
bound we get Int as result and it isn't subtype of Byte.
This commit is contained in:
Stanislav Erokhin
2017-06-07 11:05:01 +03:00
committed by Mikhail Zarechenskiy
parent d1263c5dc3
commit 5eb56dca60
2 changed files with 79 additions and 39 deletions
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalc
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.resolve.calls.inference.model.checkConstraint
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.intersectTypes import org.jetbrains.kotlin.types.checker.intersectTypes
@@ -37,9 +38,49 @@ class ResultTypeResolver(
fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): UnwrappedType? { fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): UnwrappedType? {
findResultIfThereIsEqualsConstraint(c, variableWithConstraints, allowedFixToNotProperType = false)?.let { return it } findResultIfThereIsEqualsConstraint(c, variableWithConstraints, allowedFixToNotProperType = false)?.let { return it }
val builtIns = variableWithConstraints.typeVariable.freshTypeConstructor.builtIns val subType = findSubType(c, variableWithConstraints)
val superType = findSuperType(c, variableWithConstraints)
val result = if (direction == ResolveDirection.TO_SUBTYPE || direction == ResolveDirection.UNKNOWN) {
c.resultType(subType, superType, variableWithConstraints)
}
else {
c.resultType(superType, subType, variableWithConstraints)
}
if (direction == ResolveDirection.TO_SUBTYPE || direction == ResolveDirection.UNKNOWN) { if (result != null) return result
// no proper constraints
return variableWithConstraints.typeVariable.freshTypeConstructor.builtIns.run {
if (direction == ResolveDirection.TO_SUBTYPE) nothingType else anyType
}
}
private fun Context.resultType(
firstCandidate: UnwrappedType?,
secondCandidate: UnwrappedType?,
variableWithConstraints: VariableWithConstraints
): UnwrappedType? {
if (firstCandidate == null || secondCandidate == null) return firstCandidate ?: secondCandidate
if (isSuitableType(firstCandidate, variableWithConstraints)) return firstCandidate
if (isSuitableType(secondCandidate, variableWithConstraints)) {
return secondCandidate
}
else {
return firstCandidate
}
}
private fun Context.isSuitableType(resultType: UnwrappedType, variableWithConstraints: VariableWithConstraints): Boolean {
for (constraint in variableWithConstraints.constraints) {
if (!isProperType(constraint.type)) continue
if (!checkConstraint(constraint.type, constraint.kind, resultType)) return false
}
return true
}
private fun findSubType(c: Context, variableWithConstraints: VariableWithConstraints): UnwrappedType? {
val lowerConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.LOWER && c.isProperType(it.type) } val lowerConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.LOWER && c.isProperType(it.type) }
if (lowerConstraints.isNotEmpty()) { if (lowerConstraints.isNotEmpty()) {
val commonSupertype = NewCommonSuperTypeCalculator.commonSuperType(convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints)) val commonSupertype = NewCommonSuperTypeCalculator.commonSuperType(convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints))
@@ -63,23 +104,18 @@ class ResultTypeResolver(
return typeApproximator.approximateToSuperType(commonSupertype, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: commonSupertype return typeApproximator.approximateToSuperType(commonSupertype, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: commonSupertype
} }
return null
} }
// direction == TO_SUPER or there is no LOWER bounds private fun findSuperType(c: Context, variableWithConstraints: VariableWithConstraints): UnwrappedType? {
val upperConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && c.isProperType(it.type) } val upperConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && c.isProperType(it.type) }
if (upperConstraints.isNotEmpty()) { if (upperConstraints.isNotEmpty()) {
val upperType = intersectTypes(upperConstraints.map { it.type }) val upperType = intersectTypes(upperConstraints.map { it.type })
return typeApproximator.approximateToSubType(upperType, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: upperType return typeApproximator.approximateToSubType(upperType, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: upperType
} }
return null
// no proper constraints
if (direction == ResolveDirection.TO_SUBTYPE) {
return builtIns.nothingType
}
else {
return builtIns.anyType
}
} }
fun findResultIfThereIsEqualsConstraint( fun findResultIfThereIsEqualsConstraint(
@@ -128,10 +128,14 @@ class InitialConstraint(
fun InitialConstraint.checkConstraint(substitutor: TypeSubstitutor): Boolean { fun InitialConstraint.checkConstraint(substitutor: TypeSubstitutor): Boolean {
val newA = substitutor.substitute(a) val newA = substitutor.substitute(a)
val newB = substitutor.substitute(a) val newB = substitutor.substitute(a)
return checkConstraint(newB, constraintKind, newA)
}
fun checkConstraint(constraintType: UnwrappedType, constraintKind: ConstraintKind, resultType: UnwrappedType): Boolean {
val typeChecker = KotlinTypeChecker.DEFAULT val typeChecker = KotlinTypeChecker.DEFAULT
return when (constraintKind) { return when (constraintKind) {
ConstraintKind.EQUALITY -> typeChecker.equalTypes(newA, newB) ConstraintKind.EQUALITY -> typeChecker.equalTypes(constraintType, resultType)
ConstraintKind.UPPER -> typeChecker.isSubtypeOf(newA, newB) ConstraintKind.LOWER -> typeChecker.isSubtypeOf(constraintType, resultType)
ConstraintKind.LOWER -> typeChecker.isSubtypeOf(newB, newA) ConstraintKind.UPPER -> typeChecker.isSubtypeOf(resultType, constraintType)
} }
} }