[NI] Fix number type adjustment for greatest lower bound calculation

Compute common supertype (regardless of whether the given set of
lower bounds contains number types, intersection types, or whatever).

If the result type S is a possibly nullable intersection X1 & ... & Xn,
N = {Xi | Xi is a primitive number type},
R = {Xi | Xi is not a primitive number type},
M = default primitive number type for {Nj},
then adjusted type T* = M & R1 & ... Rm with the same nullability as S.

NB: IntegerValueType(_) = Int & Byte & Short & Long
This commit is contained in:
Dmitry Petrov
2017-06-07 17:43:59 +03:00
committed by Mikhail Zarechenskiy
parent 1ae4278cfd
commit fbad16567b
@@ -16,17 +16,15 @@
package org.jetbrains.kotlin.resolve.calls.inference.components package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator.ResolveDirection import org.jetbrains.kotlin.resolve.calls.inference.components.FixationOrderCalculator.ResolveDirection
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.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
import java.util.* import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
class ResultTypeResolver( class ResultTypeResolver(
val typeApproximator: TypeApproximator val typeApproximator: TypeApproximator
@@ -83,7 +81,8 @@ class ResultTypeResolver(
private fun findSubType(c: Context, variableWithConstraints: VariableWithConstraints): UnwrappedType? { 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(lowerConstraints.map { it.type })
val adjustedCommonSuperType = adjustCommonSupertypeWithKnowledgeOfNumberTypes(commonSuperType)
/** /**
* *
* fun <T> Array<out T>.intersect(other: Iterable<T>) { * fun <T> Array<out T>.intersect(other: Iterable<T>) {
@@ -102,12 +101,40 @@ class ResultTypeResolver(
* *
*/ */
return typeApproximator.approximateToSuperType(commonSupertype, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: commonSupertype return typeApproximator.approximateToSuperType(adjustedCommonSuperType, TypeApproximatorConfiguration.CapturedTypesApproximation)
?: adjustedCommonSuperType
} }
return null return null
} }
private fun adjustCommonSupertypeWithKnowledgeOfNumberTypes(commonSuperType: UnwrappedType): UnwrappedType {
val constructor = commonSuperType.constructor
return when (constructor) {
is IntegerValueTypeConstructor,
is IntersectionTypeConstructor -> {
val newSupertypes = arrayListOf<UnwrappedType>()
val numberSupertypes = arrayListOf<KotlinType>()
for (supertype in constructor.supertypes.map { it.unwrap() }) {
if (supertype.isPrimitiveNumberType())
numberSupertypes.add(supertype)
else
newSupertypes.add(supertype)
}
TypeUtils.getDefaultPrimitiveNumberType(numberSupertypes)?.let {
newSupertypes.add(it.unwrap())
}
intersectTypes(newSupertypes).makeNullableAsSpecified(commonSuperType.isMarkedNullable)
}
else ->
commonSuperType
}
}
private fun findSuperType(c: Context, variableWithConstraints: VariableWithConstraints): UnwrappedType? { 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()) {
@@ -138,45 +165,4 @@ class ResultTypeResolver(
// may be we should just firstOrNull // may be we should just firstOrNull
return notProperEqualsConstraint.singleOrNull()?.type return notProperEqualsConstraint.singleOrNull()?.type
} }
private fun convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints: Collection<Constraint>): List<UnwrappedType> {
if (lowerConstraints.isEmpty()) return emptyList()
val (numberLowerBounds, generalLowerBounds) = lowerConstraints.map { it.type }.partition { it.isNumberValueType() }
val numberType = commonSupertypeForNumberTypes(numberLowerBounds) ?: return generalLowerBounds
return generalLowerBounds + numberType
}
private fun KotlinType.isNumberValueType() =
constructor is IntegerValueTypeConstructor ||
(constructor is IntersectionTypeConstructor && constructor.supertypes.all { it.isPrimitiveIntegerType() } )
private fun KotlinType.isPrimitiveIntegerType() =
KotlinBuiltIns.isByte(this) ||
KotlinBuiltIns.isShort(this) ||
KotlinBuiltIns.isInt(this) ||
KotlinBuiltIns.isLong(this)
private fun commonSupertypeForNumberTypes(numberLowerBounds: List<UnwrappedType>): UnwrappedType? {
if (numberLowerBounds.isEmpty()) return null
val intersectionOfSupertypes = getIntersectionOfSupertypes(numberLowerBounds)
return TypeUtils.getDefaultPrimitiveNumberType(intersectionOfSupertypes)?.unwrap() ?:
NewCommonSuperTypeCalculator.commonSuperType(numberLowerBounds)
}
private fun getIntersectionOfSupertypes(types: Collection<UnwrappedType>): Set<UnwrappedType> {
val upperBounds = HashSet<UnwrappedType>()
for (type in types) {
val supertypes = type.constructor.supertypes.map { it.unwrap() }
if (upperBounds.isEmpty()) {
upperBounds.addAll(supertypes)
}
else {
upperBounds.retainAll(supertypes)
}
}
return upperBounds
}
} }