From c0c94910e26dd852190dc0ecec34a63814b443ed Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Wed, 7 Jun 2017 15:44:39 +0300 Subject: [PATCH] [NI] Fix StackOverflow in TypeApproximator For cases like Enum where Captured(*) has supertype Enum we get SO in TypeApproximator. To prevent this argument depth was introduced. --- .../components/ConstraintInjector.kt | 14 --- .../kotlin/types/TypeApproximator.kt | 96 +++++++++++++------ 2 files changed, 65 insertions(+), 45 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt index 18a3c9723e8..cc931fb460f 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt @@ -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) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt index 6635d420c1b..0160716f6e2 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt @@ -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 <: Inv */ 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 cannot be approximated to subType - val toSubType = approximateToSubType(argumentType, conf) ?: continue@loop + val toSubType = approximateToSubType(argumentType, conf, depth) ?: continue@loop // Inv is supertype for Inv 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() -} \ No newline at end of file +} + +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 +}