[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:
Stanislav Erokhin
2017-06-07 15:44:39 +03:00
committed by Mikhail Zarechenskiy
parent 9fa3bce73a
commit c0c94910e2
2 changed files with 65 additions and 45 deletions
@@ -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
}