[NI] Don't loose platform types on simplifying lower constraints
This commit is contained in:
+91
-9
@@ -87,24 +87,106 @@ abstract class TypeCheckerContextForConstraintSystem : TypeCheckerContext(errorT
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Foo <: T! <=> Foo <: T? <=> Foo & Any <: T
|
|
||||||
* Foo <: T? <=> Foo & Any <: T
|
|
||||||
* Foo <: T -- leave as is
|
* Foo <: T -- leave as is
|
||||||
|
*
|
||||||
|
* T?
|
||||||
|
*
|
||||||
|
* Foo <: T? -- Foo & Any <: T
|
||||||
|
* Foo? <: T? -- Foo? & Any <: T
|
||||||
|
* (Foo..Bar) <: T? -- (Foo..Bar) & Any <: T
|
||||||
|
*
|
||||||
|
* T!
|
||||||
|
*
|
||||||
|
* Foo <: T! --
|
||||||
|
* assert T! == (T..T?)
|
||||||
|
* Foo <: T
|
||||||
|
* Foo <: T?
|
||||||
|
* =>
|
||||||
|
* Foo <: T
|
||||||
|
* Foo & Any <: T
|
||||||
|
* =>
|
||||||
|
* (Foo & Any .. Foo) <: T
|
||||||
|
*
|
||||||
|
* => Foo <: T! -- (Foo & Any .. Foo) <: T
|
||||||
|
*
|
||||||
|
* Foo? <: T! -- (Foo? & Any .. Foo?) <: T
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* (Foo..Bar) <: T! --
|
||||||
|
* assert T! == (T..T?)
|
||||||
|
* (Foo..Bar) <: (T..T?)
|
||||||
|
* =>
|
||||||
|
* Foo <: T?
|
||||||
|
* Bar <: T
|
||||||
|
* =>
|
||||||
|
* (Foo & Any .. Bar) <: T
|
||||||
|
*
|
||||||
|
* => (Foo..Bar) <: T! -- (Foo & Any .. Bar) <: T
|
||||||
*/
|
*/
|
||||||
private fun simplifyLowerConstraint(typeVariable: UnwrappedType, subType: UnwrappedType): Boolean {
|
private fun simplifyLowerConstraint(typeVariable: UnwrappedType, subType: UnwrappedType): Boolean {
|
||||||
@Suppress("NAME_SHADOWING")
|
when (typeVariable) {
|
||||||
val typeVariable = typeVariable.upperIfFlexible()
|
is SimpleType ->
|
||||||
|
addLowerConstraintForSimpleType(typeVariable, subType)
|
||||||
|
|
||||||
if (typeVariable.isMarkedNullable) {
|
is FlexibleType ->
|
||||||
addLowerConstraint(typeVariable.constructor, intersectTypes(listOf(subType, subType.builtIns.anyType)))
|
when (subType) {
|
||||||
}
|
is SimpleType -> addLowerConstraintWithSimpleSubtype(typeVariable, subType)
|
||||||
else {
|
is FlexibleType -> addLowerConstraintWithFlexibleSubtype(typeVariable, subType)
|
||||||
addLowerConstraint(typeVariable.constructor, subType)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Foo <: T! -- (Foo & Any .. Foo) <: T
|
||||||
|
// T <: T! -- (T..T?) <: T
|
||||||
|
// we can't use constraint (T & Any .. T) in the latter, because non-platform type with type variables is incorrect
|
||||||
|
private fun addLowerConstraintWithSimpleSubtype(typeVariable: FlexibleType, subType: SimpleType) {
|
||||||
|
assertFlexibleTypeVariable(typeVariable)
|
||||||
|
|
||||||
|
val constraintType = if (isMyTypeVariable(subType))
|
||||||
|
KotlinTypeFactory.flexibleType(subType.makeNullableAsSpecified(false), subType.makeNullableAsSpecified(true))
|
||||||
|
else
|
||||||
|
KotlinTypeFactory.flexibleType(subType.definitelyNotNull(), subType)
|
||||||
|
|
||||||
|
addLowerConstraint(typeVariable.constructor, constraintType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Foo..Bar) <: T! -- (Foo & Any .. Bar) <: T
|
||||||
|
private fun addLowerConstraintWithFlexibleSubtype(typeVariable: FlexibleType, subType: FlexibleType) {
|
||||||
|
assertFlexibleTypeVariable(typeVariable)
|
||||||
|
|
||||||
|
val lowerBound = subType.lowerBound
|
||||||
|
val upperBound = subType.upperBound
|
||||||
|
|
||||||
|
if (isMyTypeVariable(lowerBound) || isMyTypeVariable(upperBound)) {
|
||||||
|
assertFlexibleTypeVariable(subType)
|
||||||
|
// This means that we are trying to add constraint like T! <: A!
|
||||||
|
addLowerConstraint(typeVariable.constructor, subType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
addLowerConstraint(typeVariable.constructor, KotlinTypeFactory.flexibleType(lowerBound.definitelyNotNull(), upperBound))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foo <: T or
|
||||||
|
// Foo <: T? -- Foo & Any <: T
|
||||||
|
private fun addLowerConstraintForSimpleType(typeVariable: SimpleType, subType: UnwrappedType) {
|
||||||
|
if (typeVariable.isMarkedNullable)
|
||||||
|
addLowerConstraint(typeVariable.constructor, subType.definitelyNotNull())
|
||||||
|
else
|
||||||
|
addLowerConstraint(typeVariable.constructor, subType)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun UnwrappedType.definitelyNotNull(): UnwrappedType = intersectTypes(listOf(this, this.builtIns.anyType))
|
||||||
|
private fun SimpleType.definitelyNotNull(): SimpleType = intersectTypes(listOf(this, this.builtIns.anyType))
|
||||||
|
|
||||||
|
private fun assertFlexibleTypeVariable(typeVariable: FlexibleType) {
|
||||||
|
assert(typeVariable.lowerBound.constructor == typeVariable.upperBound.constructor) {
|
||||||
|
"Flexible type variable ($typeVariable) should have bounds with the same type constructor, i.e. (T..T?)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* T! <: Foo <=> T <: Foo
|
* T! <: Foo <=> T <: Foo
|
||||||
* T? <: Foo <=> T <: Foo && Nothing? <: Foo
|
* T? <: Foo <=> T <: Foo && Nothing? <: Foo
|
||||||
|
|||||||
Reference in New Issue
Block a user