[NI] Do not run "or" branches for constraint subtyping.

We should never do that, because otherwise we can get unexpected result.
  Example for input constraint: C(in String) <: T.
  If we run usual subtyping algorithm, then we get 2 constraints:
  C(in String) <: T and T <: String.
  Of course such system has contradiction.
This commit is contained in:
Stanislav Erokhin
2017-04-06 23:55:37 +03:00
parent 0fadf0bf70
commit 657c332a1f
3 changed files with 34 additions and 2 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.*
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.types.typeUtil.contains
abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorTypeEqualsToAnything = true, allowedTypeVariable = false) {
@@ -28,6 +29,11 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT
abstract fun addUpperConstraint(typeVariable: TypeConstructor, superType: UnwrappedType)
abstract fun addLowerConstraint(typeVariable: TypeConstructor, subType: UnwrappedType)
override fun allowSubtypeViaLowerTypeForCapturedType(subType: SimpleType, superType: NewCapturedType) =
!subType.contains { it.anyBound(this::isMyTypeVariable) }
override val sameConstructorPolicy get() = SeveralSupertypesWithSameConstructorPolicy.TAKE_FIRST_FOR_SUBTYPING
override final fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType): Boolean? {
assertInputTypes(subType, superType)
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.CapturedType
import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructor
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.TypeCheckerContext.SeveralSupertypesWithSameConstructorPolicy.*
import org.jetbrains.kotlin.types.checker.TypeCheckerContext.SupertypesPolicy
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.makeNullable
@@ -156,7 +157,13 @@ object NewKotlinTypeChecker : KotlinTypeChecker {
return StrictEqualityTypeChecker.strictEqualTypes(subType.makeNullableAsSpecified(false), superType.makeNullableAsSpecified(false))
}
if (superType is NewCapturedType && superType.lowerType != null && isSubtypeOf(subType, superType.lowerType)) return true
if (superType is NewCapturedType &&
superType.lowerType != null &&
allowSubtypeViaLowerTypeForCapturedType(subType, superType) &&
isSubtypeOf(subType, superType.lowerType))
{
return true
}
(superType.constructor as? IntersectionTypeConstructor)?.let {
assert(!superType.isMarkedNullable) { "Intersection type should not be marked nullable!: $superType" }
@@ -193,7 +200,16 @@ object NewKotlinTypeChecker : KotlinTypeChecker {
1 -> return isSubtypeForSameConstructor(supertypesWithSameConstructor.first().arguments, superType)
else -> { // at least 2 supertypes with same constructors. Such case is rare
if (supertypesWithSameConstructor.any { isSubtypeForSameConstructor(it.arguments, superType) }) return true
when (sameConstructorPolicy) {
FORCE_NOT_SUBTYPE -> return false
TAKE_FIRST_FOR_SUBTYPING -> return isSubtypeForSameConstructor(supertypesWithSameConstructor.first().arguments, superType)
CHECK_ANY_OF_THEM,
INTERSECT_ARGUMENTS_AND_CHECK_AGAIN ->
if (supertypesWithSameConstructor.any { isSubtypeForSameConstructor(it.arguments, superType) }) return true
}
if (sameConstructorPolicy != INTERSECT_ARGUMENTS_AND_CHECK_AGAIN) return false
val newArguments = superConstructor.parameters.mapIndexed { index, _ ->
val allProjections = supertypesWithSameConstructor.map {
@@ -33,6 +33,9 @@ open class TypeCheckerContext(val errorTypeEqualsToAnything: Boolean, val allowe
return a == b
}
open fun allowSubtypeViaLowerTypeForCapturedType(subType: SimpleType, superType: NewCapturedType): Boolean = true
open val sameConstructorPolicy get() = SeveralSupertypesWithSameConstructorPolicy.INTERSECT_ARGUMENTS_AND_CHECK_AGAIN
internal inline fun <T> runWithArgumentsSettings(subArgument: UnwrappedType, f: TypeCheckerContext.() -> T): T {
if (argumentsDepth > 100) {
error("Arguments depth is too high. Some related argument: $subArgument")
@@ -117,5 +120,12 @@ open class TypeCheckerContext(val errorTypeEqualsToAnything: Boolean, val allowe
}
}
enum class SeveralSupertypesWithSameConstructorPolicy {
TAKE_FIRST_FOR_SUBTYPING,
FORCE_NOT_SUBTYPE,
CHECK_ANY_OF_THEM,
INTERSECT_ARGUMENTS_AND_CHECK_AGAIN
}
val UnwrappedType.isAllowedTypeVariable: Boolean get() = allowedTypeVariable && constructor is NewTypeVariableConstructor
}