[FE] Use indexed loop to prevent CME in incorporation

#KT-60225 Fixed
This commit is contained in:
Kirill Rakhman
2023-12-14 12:59:54 +01:00
committed by Space Team
parent 3e41faf91f
commit 3bfcf3090c
8 changed files with 61 additions and 3 deletions
@@ -27,7 +27,7 @@ class ConstraintIncorporator(
// if such type variable is fixed then it is error
fun getTypeVariable(typeConstructor: TypeConstructorMarker): TypeVariableMarker?
fun getConstraintsForVariable(typeVariable: TypeVariableMarker): Collection<Constraint>
fun getConstraintsForVariable(typeVariable: TypeVariableMarker): List<Constraint>
fun addNewIncorporatedConstraint(
lowerType: KotlinTypeMarker,
@@ -67,7 +67,7 @@ class ConstraintIncorporator(
// \alpha <: constraint.type
if (constraint.kind != ConstraintKind.LOWER) {
getConstraintsForVariable(typeVariable).forEach {
forEachConstraint(typeVariable) {
if (it.kind != ConstraintKind.UPPER) {
addNewIncorporatedConstraint(it.type, constraint.type, shouldBeTypeVariableFlexible, it.isNullabilityConstraint)
}
@@ -76,7 +76,7 @@ class ConstraintIncorporator(
// constraint.type <: \alpha
if (constraint.kind != ConstraintKind.UPPER) {
getConstraintsForVariable(typeVariable).forEach {
forEachConstraint(typeVariable) {
if (it.kind != ConstraintKind.LOWER) {
val isFromDeclaredUpperBound =
it.position.from is DeclaredUpperBoundConstraintPosition<*> && !it.type.typeConstructor().isTypeVariable()
@@ -92,6 +92,16 @@ class ConstraintIncorporator(
}
}
private inline fun Context.forEachConstraint(typeVariable: TypeVariableMarker, action: (Constraint) -> Unit) {
// We use an indexed loop because the collection might be modified during the iteration.
// However, the only modification is appending, so we should be fine.
val constraints = getConstraintsForVariable(typeVariable)
var i = 0
while (i < constraints.size) {
action(constraints[i++])
}
}
// \alpha <: Number, \beta <: Inv<\alpha> => \beta <: Inv<out Number>
private fun Context.insideOtherConstraint(
typeVariable: TypeVariableMarker,
@@ -46,6 +46,14 @@ class MutableVariableWithConstraints private constructor(
private val mutableConstraints = if (constraints == null) SmartList() else SmartList(constraints)
/**
* The contract for mutating this list is that the only allowed mutation is appending items.
* In any other case, it must be set to `null`, so that it will be recomputed when [constraints] is called.
*
* The reason is that the list might be mutated while it's being iterated.
* For this reason, we use an index loop in
* [org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintIncorporator.forEachConstraint].
*/
private var simplifiedConstraints: SmartList<Constraint>? = mutableConstraints
val rawConstraintsCount get() = mutableConstraints.size