[NI] Fix StackOverflow in TypeApproximator
For cases like Enum<Captured(*)> where Captured(*) has supertype Enum<Captured(*)> we get SO in TypeApproximator. To prevent this argument depth was introduced.
This commit is contained in:
committed by
Mikhail Zarechenskiy
parent
9fa3bce73a
commit
c0c94910e2
-14
@@ -85,20 +85,6 @@ class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator, val
|
||||
c.maxTypeDepthFromInitialConstraints = Math.max(c.maxTypeDepthFromInitialConstraints, initialType.typeDepth())
|
||||
}
|
||||
|
||||
private fun UnwrappedType.typeDepth() =
|
||||
when (this) {
|
||||
is SimpleType -> typeDepth()
|
||||
is FlexibleType -> Math.max(lowerBound.typeDepth(), upperBound.typeDepth())
|
||||
}
|
||||
|
||||
private fun SimpleType.typeDepth(): Int {
|
||||
val maxInArguments = arguments.asSequence().map {
|
||||
if (it.isStarProjection) 1 else it.type.unwrap().typeDepth()
|
||||
}.max() ?: 0
|
||||
|
||||
return maxInArguments + 1
|
||||
}
|
||||
|
||||
private fun Context.shouldWeSkipConstraint(typeVariable: NewTypeVariable, constraint: Constraint): Boolean {
|
||||
assert(constraint.kind != ConstraintKind.EQUALITY)
|
||||
|
||||
|
||||
@@ -77,8 +77,8 @@ open class TypeApproximatorConfiguration {
|
||||
}
|
||||
|
||||
class TypeApproximator {
|
||||
private val referenceApproximateToSuperType = this::approximateToSuperType
|
||||
private val referenceApproximateToSubType = this::approximateToSubType
|
||||
private val referenceApproximateToSuperType = this::approximateSimpleToSuperType
|
||||
private val referenceApproximateToSubType = this::approximateSimpleToSubType
|
||||
|
||||
fun approximateDeclarationType(baseType: KotlinType, local: Boolean): UnwrappedType {
|
||||
if (!USE_NEW_INFERENCE) return baseType.unwrap()
|
||||
@@ -89,15 +89,23 @@ class TypeApproximator {
|
||||
|
||||
// null means that this input type is the result, i.e. input type not contains not-allowed kind of types
|
||||
// type <: resultType
|
||||
fun approximateToSuperType(type: UnwrappedType, conf: TypeApproximatorConfiguration): UnwrappedType? {
|
||||
if (type is TypeUtils.SpecialType) return null
|
||||
return approximateTo(NewKotlinTypeChecker.transformToNewType(type), conf, FlexibleType::upperBound, referenceApproximateToSuperType)
|
||||
}
|
||||
fun approximateToSuperType(type: UnwrappedType, conf: TypeApproximatorConfiguration): UnwrappedType? =
|
||||
approximateToSuperType(type, conf, - type.typeDepth())
|
||||
|
||||
// resultType <: type
|
||||
fun approximateToSubType(type: UnwrappedType, conf: TypeApproximatorConfiguration): UnwrappedType? {
|
||||
fun approximateToSubType(type: UnwrappedType, conf: TypeApproximatorConfiguration): UnwrappedType? =
|
||||
approximateToSubType(type, conf, - type.typeDepth())
|
||||
|
||||
private fun approximateToSuperType(type: UnwrappedType, conf: TypeApproximatorConfiguration, depth: Int): UnwrappedType? {
|
||||
if (type is TypeUtils.SpecialType) return null
|
||||
return approximateTo(NewKotlinTypeChecker.transformToNewType(type), conf, FlexibleType::lowerBound, referenceApproximateToSubType)
|
||||
return approximateTo(NewKotlinTypeChecker.transformToNewType(type), conf, FlexibleType::upperBound,
|
||||
referenceApproximateToSuperType, depth)
|
||||
}
|
||||
|
||||
private fun approximateToSubType(type: UnwrappedType, conf: TypeApproximatorConfiguration, depth: Int): UnwrappedType? {
|
||||
if (type is TypeUtils.SpecialType) return null
|
||||
return approximateTo(NewKotlinTypeChecker.transformToNewType(type), conf, FlexibleType::lowerBound,
|
||||
referenceApproximateToSubType, depth)
|
||||
}
|
||||
|
||||
// comments for case bound = upperBound, approximateTo = toSuperType
|
||||
@@ -105,10 +113,11 @@ class TypeApproximator {
|
||||
type: UnwrappedType,
|
||||
conf: TypeApproximatorConfiguration,
|
||||
bound: FlexibleType.() -> SimpleType,
|
||||
approximateTo: (SimpleType, TypeApproximatorConfiguration) -> UnwrappedType?
|
||||
approximateTo: (SimpleType, TypeApproximatorConfiguration, depth: Int) -> UnwrappedType?,
|
||||
depth: Int
|
||||
): UnwrappedType? {
|
||||
when (type) {
|
||||
is SimpleType -> return approximateTo(type, conf)
|
||||
is SimpleType -> return approximateTo(type, conf, depth)
|
||||
is FlexibleType -> {
|
||||
if (type is DynamicType) {
|
||||
return if (conf.dynamic) null else type.bound()
|
||||
@@ -132,8 +141,8 @@ class TypeApproximator {
|
||||
* I.e. for every type B such as L_2 <: B, L_1 <: B. For example B = L_2.
|
||||
*/
|
||||
|
||||
val lowerResult = approximateTo(type.lowerBound, conf)
|
||||
val upperResult = approximateTo(type.upperBound, conf)
|
||||
val lowerResult = approximateTo(type.lowerBound, conf, depth)
|
||||
val upperResult = approximateTo(type.upperBound, conf, depth)
|
||||
if (lowerResult == null && upperResult == null) return null
|
||||
|
||||
/**
|
||||
@@ -147,13 +156,13 @@ class TypeApproximator {
|
||||
upperResult?.upperIfFlexible() ?: type.upperBound)
|
||||
}
|
||||
else {
|
||||
return type.bound().let { approximateTo(it, conf) ?: it }
|
||||
return type.bound().let { approximateTo(it, conf, depth) ?: it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun approximateIntersectionType(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean): UnwrappedType? {
|
||||
private fun approximateIntersectionType(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean, depth: Int): UnwrappedType? {
|
||||
val typeConstructor = type.constructor
|
||||
assert(typeConstructor is IntersectionTypeConstructor) {
|
||||
"Should be intersection type: $type, typeConstructor class: ${typeConstructor::class.java.canonicalName}"
|
||||
@@ -164,7 +173,7 @@ class TypeApproximator {
|
||||
|
||||
var thereIsApproximation = false
|
||||
val newTypes = typeConstructor.supertypes.map {
|
||||
val newType = if (toSuper) approximateToSuperType(it.unwrap(), conf) else approximateToSubType(it.unwrap(), conf)
|
||||
val newType = if (toSuper) approximateToSuperType(it.unwrap(), conf, depth) else approximateToSubType(it.unwrap(), conf, depth)
|
||||
if (newType != null) {
|
||||
thereIsApproximation = true
|
||||
newType
|
||||
@@ -187,7 +196,7 @@ class TypeApproximator {
|
||||
return if (type.isMarkedNullable) baseResult.makeNullableAsSpecified(true) else baseResult
|
||||
}
|
||||
|
||||
private fun approximateCapturedType(type: NewCapturedType, conf: TypeApproximatorConfiguration, toSuper: Boolean): UnwrappedType? {
|
||||
private fun approximateCapturedType(type: NewCapturedType, conf: TypeApproximatorConfiguration, toSuper: Boolean, depth: Int): UnwrappedType? {
|
||||
val supertypes = type.constructor.supertypes
|
||||
val baseSuperType = when (supertypes.size) {
|
||||
0 -> type.builtIns.nullableAnyType // Let C = in Int, then superType for C and C? is Any?
|
||||
@@ -205,28 +214,32 @@ class TypeApproximator {
|
||||
*
|
||||
* todo handle flexible types
|
||||
*/
|
||||
if (approximateToSuperType(baseSuperType, conf) == null && approximateToSubType(baseSubType, conf) == null) {
|
||||
if (approximateToSuperType(baseSuperType, conf, depth) == null && approximateToSubType(baseSubType, conf, depth) == null) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
val baseResult = if (toSuper) approximateToSuperType(baseSuperType, conf) ?: baseSuperType else approximateToSubType(baseSubType, conf) ?: baseSubType
|
||||
val baseResult = if (toSuper) approximateToSuperType(baseSuperType, conf, depth) ?: baseSuperType else approximateToSubType(baseSubType, conf, depth) ?: baseSubType
|
||||
|
||||
// C = in Int, Int <: C => Int? <: C?
|
||||
// C = out Number, C <: Number => C? <: Number?
|
||||
return if (type.isMarkedNullable) baseResult.makeNullableAsSpecified(true) else baseResult
|
||||
}
|
||||
|
||||
private fun approximateToSuperType(type: SimpleType, conf: TypeApproximatorConfiguration) = approximateTo(type, conf, toSuper = true)
|
||||
private fun approximateToSubType(type: SimpleType, conf: TypeApproximatorConfiguration) = approximateTo(type, conf, toSuper = false)
|
||||
private fun approximateSimpleToSuperType(type: SimpleType, conf: TypeApproximatorConfiguration, depth: Int) =
|
||||
approximateTo(type, conf, toSuper = true, depth = depth)
|
||||
private fun approximateSimpleToSubType(type: SimpleType, conf: TypeApproximatorConfiguration, depth: Int) =
|
||||
approximateTo(type, conf, toSuper = false, depth = depth)
|
||||
|
||||
private fun approximateTo(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean): UnwrappedType? {
|
||||
private fun approximateTo(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean, depth: Int): UnwrappedType? {
|
||||
if (type.isError) {
|
||||
// todo -- fix builtIns. Now builtIns here is DefaultBuiltIns
|
||||
return if (conf.errorType) null else type.defaultResult(toSuper)
|
||||
}
|
||||
|
||||
if (depth > 3) return type.defaultResult(toSuper)
|
||||
|
||||
if (type.arguments.isNotEmpty()) {
|
||||
return approximateParametrizedType(type, conf, toSuper)
|
||||
return approximateParametrizedType(type, conf, toSuper, depth + 1)
|
||||
}
|
||||
|
||||
val typeConstructor = type.constructor
|
||||
@@ -236,11 +249,11 @@ class TypeApproximator {
|
||||
"Type is inconsistent -- somewhere we create type with typeConstructor = $typeConstructor " +
|
||||
"and class: ${type::class.java.canonicalName}. type.toString() = $type"
|
||||
}
|
||||
return approximateCapturedType(type as NewCapturedType, conf, toSuper)
|
||||
return approximateCapturedType(type as NewCapturedType, conf, toSuper, depth)
|
||||
}
|
||||
|
||||
if (typeConstructor is IntersectionTypeConstructor) {
|
||||
return approximateIntersectionType(type, conf, toSuper)
|
||||
return approximateIntersectionType(type, conf, toSuper, depth)
|
||||
}
|
||||
|
||||
if (typeConstructor is TypeVariableTypeConstructor) {
|
||||
@@ -257,7 +270,7 @@ class TypeApproximator {
|
||||
Variance.INVARIANT -> throw AssertionError("Incorrect variance $effectiveVariance")
|
||||
}
|
||||
|
||||
private fun approximateParametrizedType(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean): SimpleType? {
|
||||
private fun approximateParametrizedType(type: SimpleType, conf: TypeApproximatorConfiguration, toSuper: Boolean, depth: Int): SimpleType? {
|
||||
val parameters = type.constructor.parameters
|
||||
val arguments = type.arguments
|
||||
if (parameters.size != arguments.size) {
|
||||
@@ -293,7 +306,12 @@ class TypeApproximator {
|
||||
* Inv<in Foo> <: Inv<in subType(Foo)>
|
||||
*/
|
||||
val approximatedArgument = argumentType.let {
|
||||
if (isApproximateDirectionToSuper(effectiveVariance, toSuper)) approximateToSuperType(it, conf) else approximateToSubType(it, conf)
|
||||
if (isApproximateDirectionToSuper(effectiveVariance, toSuper)) {
|
||||
approximateToSuperType(it, conf, depth)
|
||||
}
|
||||
else {
|
||||
approximateToSubType(it, conf, depth)
|
||||
}
|
||||
} ?: continue@loop
|
||||
|
||||
if (parameter.variance == Variance.INVARIANT) {
|
||||
@@ -305,7 +323,7 @@ class TypeApproximator {
|
||||
Variance.INVARIANT -> {
|
||||
if (!toSuper) {
|
||||
// Inv<Foo> cannot be approximated to subType
|
||||
val toSubType = approximateToSubType(argumentType, conf) ?: continue@loop
|
||||
val toSubType = approximateToSubType(argumentType, conf, depth) ?: continue@loop
|
||||
|
||||
// Inv<Foo!> is supertype for Inv<Foo?>
|
||||
if (!NewKotlinTypeChecker.equalTypes(argumentType, toSubType)) return type.defaultResult(toSuper)
|
||||
@@ -326,16 +344,16 @@ class TypeApproximator {
|
||||
* May be we should do the same for deeper types, but not now.
|
||||
*/
|
||||
if (argumentType is NewCapturedType) {
|
||||
val subType = approximateToSubType(argumentType, conf) ?: continue@loop
|
||||
val subType = approximateToSubType(argumentType, conf, depth) ?: continue@loop
|
||||
if (!subType.isTrivialSub()) {
|
||||
newArguments[index] = TypeProjectionImpl(Variance.IN_VARIANCE, subType)
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
|
||||
val approximatedSuperType = approximateToSuperType(argumentType, conf) ?: continue@loop // null means that this type we can leave as is
|
||||
val approximatedSuperType = approximateToSuperType(argumentType, conf, depth) ?: continue@loop // null means that this type we can leave as is
|
||||
if (approximatedSuperType.isTrivialSuper()) {
|
||||
val approximatedSubType = approximateToSubType(argumentType, conf) ?: continue@loop // seems like this is never null
|
||||
val approximatedSubType = approximateToSubType(argumentType, conf, depth) ?: continue@loop // seems like this is never null
|
||||
if (!approximatedSubType.isTrivialSub()) {
|
||||
newArguments[index] = TypeProjectionImpl(Variance.IN_VARIANCE, approximatedSubType)
|
||||
continue@loop
|
||||
@@ -362,4 +380,20 @@ class TypeApproximator {
|
||||
|
||||
// Nothing or Nothing!
|
||||
private fun UnwrappedType.isTrivialSub() = lowerIfFlexible().isNothing()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun UnwrappedType.typeDepth() =
|
||||
when (this) {
|
||||
is SimpleType -> typeDepth()
|
||||
is FlexibleType -> Math.max(lowerBound.typeDepth(), upperBound.typeDepth())
|
||||
}
|
||||
|
||||
internal fun SimpleType.typeDepth(): Int {
|
||||
if (this is TypeUtils.SpecialType) return 0
|
||||
|
||||
val maxInArguments = arguments.asSequence().map {
|
||||
if (it.isStarProjection) 1 else it.type.unwrap().typeDepth()
|
||||
}.max() ?: 0
|
||||
|
||||
return maxInArguments + 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user