Add equality constraints without subtyping

^KT-42195 Fixed
^KT-41741 Fixed
This commit is contained in:
Victor Petukhov
2020-10-16 14:27:08 +03:00
parent b1b87becc8
commit 84129098cb
20 changed files with 325 additions and 46 deletions
@@ -34,6 +34,8 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck
isFromNullabilityConstraint: Boolean = false
)
abstract fun addEqualityConstraint(typeVariable: TypeConstructorMarker, type: KotlinTypeMarker)
override fun getLowerCapturedTypePolicy(subType: SimpleTypeMarker, superType: CapturedTypeMarker): LowerCapturedTypePolicy {
return when {
isMyTypeVariable(subType) -> {
@@ -40,15 +40,19 @@ class ConstraintIncorporator(
fun addNewIncorporatedConstraint(typeVariable: TypeVariableMarker, type: KotlinTypeMarker, constraintContext: ConstraintContext)
}
fun incorporateIntoOtherConstraints(c: Context, typeVariable: TypeVariableMarker, constraint: Constraint) {
fun incorporateEqualityConstraint(c: Context, typeVariable: TypeVariableMarker, constraint: Constraint) = with(c) {
// we shouldn't incorporate recursive constraint -- It is too dangerous
if (c.areThereRecursiveConstraints(typeVariable, constraint)) return
c.directWithVariable(typeVariable, constraint)
if (constraint.type.contains { it is TypeVariableTypeConstructorMarker }) {
c.otherInsideMyConstraint(typeVariable, constraint)
}
c.insideOtherConstraint(typeVariable, constraint)
}
// \alpha is typeVariable, \beta -- other type variable registered in ConstraintStorage
fun incorporate(c: Context, typeVariable: TypeVariableMarker, constraint: Constraint) {
fun incorporateSubtypeConstraint(c: Context, typeVariable: TypeVariableMarker, constraint: Constraint) {
// we shouldn't incorporate recursive constraint -- It is too dangerous
if (c.areThereRecursiveConstraints(typeVariable, constraint)) return
@@ -39,28 +39,42 @@ class ConstraintInjector(
fun addInitialSubtypeConstraint(c: Context, lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, position: ConstraintPosition) {
val initialConstraint = InitialConstraint(lowerType, upperType, UPPER, position).also { c.addInitialConstraint(it) }
val typeCheckerContext = TypeCheckerContext(c, IncorporationConstraintPosition(position, initialConstraint))
updateAllowedTypeDepth(c, lowerType)
updateAllowedTypeDepth(c, upperType)
addSubTypeConstraintAndIncorporateIt(
c,
lowerType,
upperType,
TypeCheckerContext(c, IncorporationConstraintPosition(position, initialConstraint))
)
addSubTypeConstraintAndIncorporateIt(c, lowerType, upperType, typeCheckerContext)
}
fun addInitialEqualityConstraint(c: Context, a: KotlinTypeMarker, b: KotlinTypeMarker, position: ConstraintPosition) {
val initialConstraint = InitialConstraint(a, b, EQUALITY, position).also { c.addInitialConstraint(it) }
updateAllowedTypeDepth(c, a)
updateAllowedTypeDepth(c, b)
private fun Context.addInitialEqualityConstraintThroughSubtyping(
a: KotlinTypeMarker,
b: KotlinTypeMarker,
typeCheckerContext: TypeCheckerContext
) {
updateAllowedTypeDepth(this, a)
updateAllowedTypeDepth(this, b)
addSubTypeConstraintAndIncorporateIt(this, a, b, typeCheckerContext)
addSubTypeConstraintAndIncorporateIt(this, b, a, typeCheckerContext)
}
fun addInitialEqualityConstraint(c: Context, a: KotlinTypeMarker, b: KotlinTypeMarker, position: ConstraintPosition) = with(c) {
val (typeVariable, equalType) = when {
a.typeConstructor(c) is TypeVariableTypeConstructorMarker -> a to b
b.typeConstructor(c) is TypeVariableTypeConstructorMarker -> b to a
else -> return
}
val initialConstraint = InitialConstraint(typeVariable, equalType, EQUALITY, position).also { c.addInitialConstraint(it) }
val typeCheckerContext = TypeCheckerContext(c, IncorporationConstraintPosition(position, initialConstraint))
addSubTypeConstraintAndIncorporateIt(c, a, b, typeCheckerContext)
addSubTypeConstraintAndIncorporateIt(c, b, a, typeCheckerContext)
// We add constraints like `T? == Foo!` in the old way
if (!typeVariable.isSimpleType() || typeVariable.isMarkedNullable()) {
addInitialEqualityConstraintThroughSubtyping(typeVariable, equalType, typeCheckerContext)
return
}
updateAllowedTypeDepth(c, equalType)
addEqualityConstraintAndIncorporateIt(c, typeVariable, equalType, typeCheckerContext)
}
private fun addSubTypeConstraintAndIncorporateIt(
@@ -72,6 +86,26 @@ class ConstraintInjector(
typeCheckerContext.setConstrainingTypesToPrintDebugInfo(lowerType, upperType)
typeCheckerContext.runIsSubtypeOf(lowerType, upperType)
processConstraints(c, typeCheckerContext, constraintIncorporator::incorporateSubtypeConstraint)
}
private fun addEqualityConstraintAndIncorporateIt(
c: Context,
typeVariable: KotlinTypeMarker,
equalType: KotlinTypeMarker,
typeCheckerContext: TypeCheckerContext
) {
typeCheckerContext.setConstrainingTypesToPrintDebugInfo(typeVariable, equalType)
typeCheckerContext.addEqualityConstraint(typeVariable.typeConstructor(c), equalType)
processConstraints(c, typeCheckerContext, constraintIncorporator::incorporateEqualityConstraint)
}
private fun processConstraints(
c: Context,
typeCheckerContext: TypeCheckerContext,
incorporate: (c: TypeCheckerContext, typeVariable: TypeVariableMarker, constraint: Constraint) -> Unit
) {
while (typeCheckerContext.hasConstraintsToProcess()) {
for ((typeVariable, constraint) in typeCheckerContext.extractAllConstraints()!!) {
if (c.shouldWeSkipConstraint(typeVariable, constraint)) continue
@@ -82,7 +116,7 @@ class ConstraintInjector(
// it is important, that we add constraint here(not inside TypeCheckerContext), because inside incorporation we read constraints
constraints.addConstraint(constraint)?.let {
if (!constraint.isNullabilityConstraint) {
constraintIncorporator.incorporate(typeCheckerContext, typeVariable, it)
incorporate(typeCheckerContext, typeVariable, it)
}
}
}
@@ -105,7 +139,8 @@ class ConstraintInjector(
}
private fun Context.shouldWeSkipConstraint(typeVariable: TypeVariableMarker, constraint: Constraint): Boolean {
assert(constraint.kind != EQUALITY)
if (constraint.kind == EQUALITY)
return false
val constraintType = constraint.type
@@ -221,6 +256,10 @@ class ConstraintInjector(
isFromNullabilityConstraint: Boolean
) = addConstraint(typeVariable, subType, LOWER, isFromNullabilityConstraint)
override fun addEqualityConstraint(typeVariable: TypeConstructorMarker, type: KotlinTypeMarker) {
addConstraint(typeVariable, type, EQUALITY, false)
}
private fun isCapturedTypeFromSubtyping(type: KotlinTypeMarker) =
when ((type as? CapturedTypeMarker)?.captureStatus()) {
null, CaptureStatus.FROM_EXPRESSION -> false