diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index 253e9f26c96..dabe49b4ad8 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -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.ConstraintKind 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.types.* import org.jetbrains.kotlin.types.checker.intersectTypes @@ -37,49 +38,84 @@ class ResultTypeResolver( fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): UnwrappedType? { findResultIfThereIsEqualsConstraint(c, variableWithConstraints, allowedFixToNotProperType = false)?.let { return it } - val builtIns = variableWithConstraints.typeVariable.freshTypeConstructor.builtIns - - if (direction == ResolveDirection.TO_SUBTYPE || direction == ResolveDirection.UNKNOWN) { - val lowerConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.LOWER && c.isProperType(it.type) } - if (lowerConstraints.isNotEmpty()) { - val commonSupertype = NewCommonSuperTypeCalculator.commonSuperType(convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints)) - /** - * - * fun Array.intersect(other: Iterable) { - * val set = toMutableSet() - * set.retainAll(other) - * } - * fun Array.toMutableSet(): MutableSet = ... - * fun MutableCollection.retainAll(elements: Iterable) {} - * - * Here, when we solve type system for `toMutableSet` we have the following constrains: - * Array <: Array => C(out X) <: T. - * If we fix it to T = C(out X) then return type of `toMutableSet()` will be `MutableSet` - * and type of variable `set` will be `MutableSet` and the following line will have contradiction. - * - * To fix this problem when we fix variable, we will approximate captured types before fixation. - * - */ - - return typeApproximator.approximateToSuperType(commonSupertype, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: commonSupertype - } + 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) } - // direction == TO_SUPER or there is no LOWER bounds + 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) } + if (lowerConstraints.isNotEmpty()) { + val commonSupertype = NewCommonSuperTypeCalculator.commonSuperType(convertLowerTypesWithKnowledgeOfNumberTypes(lowerConstraints)) + /** + * + * fun Array.intersect(other: Iterable) { + * val set = toMutableSet() + * set.retainAll(other) + * } + * fun Array.toMutableSet(): MutableSet = ... + * fun MutableCollection.retainAll(elements: Iterable) {} + * + * Here, when we solve type system for `toMutableSet` we have the following constrains: + * Array <: Array => C(out X) <: T. + * If we fix it to T = C(out X) then return type of `toMutableSet()` will be `MutableSet` + * and type of variable `set` will be `MutableSet` and the following line will have contradiction. + * + * To fix this problem when we fix variable, we will approximate captured types before fixation. + * + */ + + return typeApproximator.approximateToSuperType(commonSupertype, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: commonSupertype + } + + return null + } + + private fun findSuperType(c: Context, variableWithConstraints: VariableWithConstraints): UnwrappedType? { val upperConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && c.isProperType(it.type) } if (upperConstraints.isNotEmpty()) { val upperType = intersectTypes(upperConstraints.map { it.type }) return typeApproximator.approximateToSubType(upperType, TypeApproximatorConfiguration.CapturedTypesApproximation) ?: upperType } - - // no proper constraints - if (direction == ResolveDirection.TO_SUBTYPE) { - return builtIns.nothingType - } - else { - return builtIns.anyType - } + return null } fun findResultIfThereIsEqualsConstraint( diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt index 6c43f263976..82f443059ac 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintStorage.kt @@ -128,10 +128,14 @@ class InitialConstraint( fun InitialConstraint.checkConstraint(substitutor: TypeSubstitutor): Boolean { val newA = 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 return when (constraintKind) { - ConstraintKind.EQUALITY -> typeChecker.equalTypes(newA, newB) - ConstraintKind.UPPER -> typeChecker.isSubtypeOf(newA, newB) - ConstraintKind.LOWER -> typeChecker.isSubtypeOf(newB, newA) + ConstraintKind.EQUALITY -> typeChecker.equalTypes(constraintType, resultType) + ConstraintKind.LOWER -> typeChecker.isSubtypeOf(constraintType, resultType) + ConstraintKind.UPPER -> typeChecker.isSubtypeOf(resultType, constraintType) } -} +} \ No newline at end of file