diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt index 762664a8011..bf91f01aff7 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt @@ -72,6 +72,7 @@ internal sealed class HeaderInfo( val first: IrExpression, val last: IrExpression, val step: IrExpression, + val canCacheLast: Boolean, val isReversed: Boolean, val direction: ProgressionDirection, val additionalNotEmptyCondition: IrExpression? @@ -95,7 +96,13 @@ internal class ProgressionHeaderInfo( direction: ProgressionDirection, additionalNotEmptyCondition: IrExpression? = null, val additionalVariables: List = listOf() -) : HeaderInfo(progressionType, first, last, step, isReversed, direction, additionalNotEmptyCondition) { +) : HeaderInfo( + progressionType, first, last, step, + canCacheLast = true, + isReversed = isReversed, + direction = direction, + additionalNotEmptyCondition = additionalNotEmptyCondition +) { val canOverflow: Boolean by lazy { if (canOverflow != null) return@lazy canOverflow @@ -142,6 +149,7 @@ internal class IndexedGetHeaderInfo( first: IrExpression, last: IrExpression, step: IrExpression, + canCacheLast: Boolean = true, val objectVariable: IrVariable, val expressionHandler: IndexedGetIterationHandler ) : HeaderInfo( @@ -149,6 +157,7 @@ internal class IndexedGetHeaderInfo( first, last, step, + canCacheLast = canCacheLast, isReversed = false, direction = ProgressionDirection.INCREASING, additionalNotEmptyCondition = null @@ -222,9 +231,12 @@ internal class HeaderInfoBuilder(context: CommonBackendContext, private val scop private val reversedHandler = ReversedHandler(context, this) + // NOTE: StringIterationHandler MUST come before CharSequenceIterationHandler. + // String is subtype of CharSequence and therefore its handler is more specialized. private val expressionHandlers = listOf( ArrayIterationHandler(context), DefaultProgressionHandler(context), + StringIterationHandler(context), CharSequenceIterationHandler(context) ) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt index 70ba88a8864..821e2831775 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt @@ -43,17 +43,15 @@ internal data class LoopReplacement( internal sealed class ForLoopHeader( protected open val headerInfo: HeaderInfo, val inductionVariable: IrVariable, - val last: IrVariable, + val lastExpression: IrExpression, val step: IrVariable, var loopVariable: IrVariable? = null, - val isLastInclusive: Boolean + val isLastInclusive: Boolean, + val declarations: List ) { /** Expression used to initialize the loop variable at the beginning of the loop. */ abstract fun initializeLoopVariable(symbols: Symbols, builder: DeclarationIrBuilder): IrExpression - /** Declarations used in the loop condition and body (e.g., induction variable). */ - abstract val declarations: List - /** Builds a new loop from the old loop. */ abstract fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement @@ -88,14 +86,14 @@ internal sealed class ForLoopHeader( ProgressionDirection.DECREASING -> // last <= inductionVar (use `<` if last is exclusive) irCall(compFun).apply { - putValueArgument(0, irGet(last)) + putValueArgument(0, lastExpression) putValueArgument(1, irGet(inductionVariable)) } ProgressionDirection.INCREASING -> // inductionVar <= last (use `<` if last is exclusive) irCall(compFun).apply { putValueArgument(0, irGet(inductionVariable)) - putValueArgument(1, irGet(last)) + putValueArgument(1, lastExpression) } ProgressionDirection.UNKNOWN -> { // If the direction is unknown, we check depending on the "step" value: @@ -111,7 +109,7 @@ internal sealed class ForLoopHeader( }, irCall(compFun).apply { putValueArgument(0, irGet(inductionVariable)) - putValueArgument(1, irGet(last)) + putValueArgument(1, lastExpression) }), context.andand( irCall(builtIns.lessFunByOperandType[stepKotlinType]!!).apply { @@ -119,7 +117,7 @@ internal sealed class ForLoopHeader( putValueArgument(1, if (isLong) irLong(0) else irInt(0)) }, irCall(compFun).apply { - putValueArgument(0, irGet(last)) + putValueArgument(0, lastExpression) putValueArgument(1, irGet(inductionVariable)) }) ) @@ -131,29 +129,22 @@ internal sealed class ForLoopHeader( internal class ProgressionLoopHeader( override val headerInfo: ProgressionHeaderInfo, inductionVariable: IrVariable, - last: IrVariable, - step: IrVariable -) : ForLoopHeader(headerInfo, inductionVariable, last, step, isLastInclusive = true) { + lastExpression: IrExpression, + step: IrVariable, + declarations: List +) : ForLoopHeader( + headerInfo, inductionVariable, + lastExpression = lastExpression, + step = step, + isLastInclusive = true, + declarations = declarations +) { override fun initializeLoopVariable(symbols: Symbols, builder: DeclarationIrBuilder) = with(builder) { // loopVariable = inductionVariable irGet(inductionVariable) } - // For this loop: - // - // for (i in first()..last() step step()) - // - // ...the functions may have side-effects so we need to call them in the following order: first() (inductionVariable), last(), step(). - // Additional variables come first as they may be needed to the subsequent variables. - // - // In the case of a reversed range, the `inductionVariable` and `last` variables are swapped, therefore the declaration order must be - // swapped to preserve the correct evaluation order. - override val declarations: List - get() = headerInfo.additionalVariables + - (if (headerInfo.isReversed) listOf(last, inductionVariable) else listOf(inductionVariable, last)) + - step - override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?) = with(builder) { val newLoop = if (headerInfo.canOverflow) { @@ -169,7 +160,7 @@ internal class ProgressionLoopHeader( // } IrDoWhileLoopImpl(oldLoop.startOffset, oldLoop.endOffset, oldLoop.type, oldLoop.origin).apply { label = oldLoop.label - condition = irNotEquals(irGet(loopVariable!!), irGet(last)) + condition = irNotEquals(irGet(loopVariable!!), lastExpression) body = newBody } } else { @@ -203,9 +194,14 @@ internal class ProgressionLoopHeader( internal class IndexedGetLoopHeader( override val headerInfo: IndexedGetHeaderInfo, inductionVariable: IrVariable, - last: IrVariable, - step: IrVariable -) : ForLoopHeader(headerInfo, inductionVariable, last, step, isLastInclusive = false) { + lastExpression: IrExpression, + step: IrVariable, + declarations: List +) : ForLoopHeader( + headerInfo, inductionVariable, lastExpression, step, + isLastInclusive = false, + declarations = declarations +) { override fun initializeLoopVariable(symbols: Symbols, builder: DeclarationIrBuilder) = with(builder) { // inductionVar = loopVar[inductionVariable] @@ -216,16 +212,13 @@ internal class IndexedGetLoopHeader( } } - override val declarations: List - get() = listOf(headerInfo.objectVariable, inductionVariable, last, step) - override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement = with(builder) { // Loop is lowered into something like: // // var inductionVar = 0 - // var last = array.size + // var last = objectVariable.size // while (inductionVar < last) { - // val loopVar = array[inductionVar] + // val loopVar = objectVariable.get(inductionVar) // inductionVar++ // // Loop body // } @@ -278,7 +271,8 @@ internal class HeaderProcessor( if (it.valueArgumentsCount == 0 && function.isTopLevel && function.getPackageFragment()?.fqName == FqName("kotlin.text") - && function.name == OperatorNameConventions.ITERATOR) { + && function.name == OperatorNameConventions.ITERATOR + ) { extensionReceiver } else { null @@ -293,62 +287,84 @@ internal class HeaderProcessor( ?: return null // If the iterable is not supported. val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset) - with(builder) builder@{ - with(headerInfo) { - // For this loop: - // - // for (i in first()..last() step step()) - // - // We need to cast first(), last(). and step() to conform to the progression type so - // that operations on the induction variable within the loop are more efficient. - // - // In the above example, if first() is a Long and last() is an Int, this creates a - // LongProgression so last() should be cast to a Long. - val inductionVariable = scope.createTemporaryVariable( - first.castIfNecessary( - progressionType.elementType(context.irBuiltIns), - progressionType.elementCastFunctionName - ), - nameHint = "inductionVariable", - isMutable = true - ) + with(builder) { + // For this loop: + // + // for (i in first()..last() step step()) + // + // We need to cast first(), last(). and step() to conform to the progression type so + // that operations on the induction variable within the loop are more efficient. + // + // In the above example, if first() is a Long and last() is an Int, this creates a + // LongProgression so last() should be cast to a Long. + val inductionVariable = scope.createTemporaryVariable( + headerInfo.first.castIfNecessary( + headerInfo.progressionType.elementType(context.irBuiltIns), + headerInfo.progressionType.elementCastFunctionName + ), + nameHint = "inductionVariable", + isMutable = true + ) - // Due to features of PSI2IR we can obtain nullable arguments here while actually - // they are non-nullable (the frontend takes care about this). So we need to cast - // them to non-nullable. - // TODO: Confirm if casting to non-nullable is still necessary - val lastValue = scope.createTemporaryVariable( - ensureNotNullable( - last.castIfNecessary( - progressionType.elementType(context.irBuiltIns), - progressionType.elementCastFunctionName - ) - ), + // Due to features of PSI2IR we can obtain nullable arguments here while actually + // they are non-nullable (the frontend takes care about this). So we need to cast + // them to non-nullable. + // TODO: Confirm if casting to non-nullable is still necessary + val lastExpression = ensureNotNullable( + headerInfo.last.castIfNecessary( + headerInfo.progressionType.elementType(context.irBuiltIns), + headerInfo.progressionType.elementCastFunctionName + ) + ) + + val lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) { + scope.createTemporaryVariable( + lastExpression, nameHint = "last" ) + } else null - val stepValue = scope.createTemporaryVariable( - ensureNotNullable( - step.castIfNecessary( - progressionType.stepType(context.irBuiltIns), - progressionType.stepCastFunctionName - ) - ), - nameHint = "step" - ) - - return when (headerInfo) { - is IndexedGetHeaderInfo -> IndexedGetLoopHeader( - headerInfo, - inductionVariable, - lastValue, - stepValue + val stepVariable = scope.createTemporaryVariable( + ensureNotNullable( + headerInfo.step.castIfNecessary( + headerInfo.progressionType.stepType(context.irBuiltIns), + headerInfo.progressionType.stepCastFunctionName ) - is ProgressionHeaderInfo -> ProgressionLoopHeader( + ), + nameHint = "step" + ) + + return when (headerInfo) { + is IndexedGetHeaderInfo -> IndexedGetLoopHeader( + headerInfo, + inductionVariable, + if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else lastExpression, + stepVariable, + listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable) + ) + is ProgressionHeaderInfo -> { + // For this loop: + // + // for (i in first()..last() step step()) + // + // ...the functions may have side-effects so we need to call them in the following order: first() (inductionVariable), last(), step(). + // Additional variables come first as they may be needed to the subsequent variables. + // + // In the case of a reversed range, the `inductionVariable` and `last` variables are swapped, therefore the declaration order must be + // swapped to preserve the correct evaluation order. + val declarations = headerInfo.additionalVariables + ( + if (headerInfo.isReversed) + listOfNotNull(lastVariableIfCanCacheLast, inductionVariable) + else + listOfNotNull(inductionVariable, lastVariableIfCanCacheLast) + ) + + stepVariable + ProgressionLoopHeader( headerInfo, inductionVariable, - lastValue, - stepValue + if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else lastExpression, + stepVariable, + declarations ) } } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt index 836e9a284b0..d027cdd1839 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt @@ -330,7 +330,11 @@ internal class DefaultProgressionHandler(private val context: CommonBackendConte } } -internal abstract class IndexedGetIterationHandler(protected val context: CommonBackendContext) : ExpressionHandler { +internal abstract class IndexedGetIterationHandler( + protected val context: CommonBackendContext, + val canCacheLast: Boolean = true +) : + ExpressionHandler { override fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? = with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { // Consider the case like: @@ -348,19 +352,20 @@ internal abstract class IndexedGetIterationHandler(protected val context: Common // // This also ensures that the semantics of re-assignment of array variables used in the loop is consistent with the semantics // proposed in https://youtrack.jetbrains.com/issue/KT-21354. - val arrayReference = scope.createTemporaryVariable( + val objectVariable = scope.createTemporaryVariable( expression, nameHint = "indexedObject" ) val last = irCall(expression.type.sizePropertyGetter).apply { - dispatchReceiver = irGet(arrayReference) + dispatchReceiver = irGet(objectVariable) } IndexedGetHeaderInfo( first = irInt(0), last = last, step = irInt(1), - objectVariable = arrayReference, + canCacheLast = canCacheLast, + objectVariable = objectVariable, expressionHandler = this@IndexedGetIterationHandler ) } @@ -381,8 +386,24 @@ internal class ArrayIterationHandler(context: CommonBackendContext) : IndexedGet get() = getClass()!!.functions.first { it.name.asString() == "get" } } -/** Builds a [HeaderInfo] for iteration over characters in a `CharacterSequence`. */ -internal class CharSequenceIterationHandler(context: CommonBackendContext) : IndexedGetIterationHandler(context) { +/** Builds a [HeaderInfo] for iteration over characters in a [String]. */ +internal class StringIterationHandler(context: CommonBackendContext) : IndexedGetIterationHandler(context) { + override fun match(expression: IrExpression) = expression.type.isString() + + override val IrType.sizePropertyGetter + get() = getClass()!!.properties.first { it.name.asString() == "length" }.getter!! + + override val IrType.getFunction + get() = getClass()!!.functions.first { it.name.asString() == "get" } +} + +/** + * Builds a [HeaderInfo] for iteration over characters in a [CharSequence]. + * + * Note: The value for "last" can NOT be cached (i.e., stored in a variable) because the size/length can change within the loop. This means + * that "last" is re-evaluated with each iteration of the loop. + */ +internal class CharSequenceIterationHandler(context: CommonBackendContext) : IndexedGetIterationHandler(context, canCacheLast = false) { override fun match(expression: IrExpression) = expression.type.isSubtypeOfClass(context.ir.symbols.charSequence) // The lowering operates on subtypes of CharSequence. Therefore, the IrType could be diff --git a/compiler/testData/codegen/box/ranges/forInCharSequenceLengthDecreasedInLoopBody.kt b/compiler/testData/codegen/box/ranges/forInCharSequenceLengthDecreasedInLoopBody.kt new file mode 100644 index 00000000000..a1c065de731 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInCharSequenceLengthDecreasedInLoopBody.kt @@ -0,0 +1,16 @@ +// KJS_WITH_FULL_RUNTIME +// WITH_RUNTIME +import kotlin.test.* + +fun box(): String { + val sb = StringBuilder("1234") + val result = StringBuilder() + for (c in sb) { + sb.clear() + result.append(c) + } + assertEquals("", sb.toString()) + assertEquals("1", result.toString()) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInCharSequenceLengthIncreasedInLoopBody.kt b/compiler/testData/codegen/box/ranges/forInCharSequenceLengthIncreasedInLoopBody.kt new file mode 100644 index 00000000000..827a056e7c5 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInCharSequenceLengthIncreasedInLoopBody.kt @@ -0,0 +1,19 @@ +// KJS_WITH_FULL_RUNTIME +// WITH_RUNTIME +import kotlin.test.* + +fun box(): String { + val sb = StringBuilder("1234") + val result = StringBuilder() + var ctr = 0 + for (c in sb) { + if (ctr % 2 == 0) + sb.append('x') + ctr++ + result.append(c) + } + assertEquals("1234xxxx", sb.toString()) + assertEquals("1234xxxx", result.toString()) + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 07ca4190041..36f9d27e045 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -18819,6 +18819,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/ranges/forByteProgressionWithIntIncrement.kt"); } + @TestMetadata("forInCharSequenceLengthDecreasedInLoopBody.kt") + public void testForInCharSequenceLengthDecreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthDecreasedInLoopBody.kt"); + } + + @TestMetadata("forInCharSequenceLengthIncreasedInLoopBody.kt") + public void testForInCharSequenceLengthIncreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthIncreasedInLoopBody.kt"); + } + @TestMetadata("forInRangeLiteralWithMixedTypeBounds.kt") public void testForInRangeLiteralWithMixedTypeBounds() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInRangeLiteralWithMixedTypeBounds.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 420563b0a48..b3a0210539a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -18819,6 +18819,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/ranges/forByteProgressionWithIntIncrement.kt"); } + @TestMetadata("forInCharSequenceLengthDecreasedInLoopBody.kt") + public void testForInCharSequenceLengthDecreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthDecreasedInLoopBody.kt"); + } + + @TestMetadata("forInCharSequenceLengthIncreasedInLoopBody.kt") + public void testForInCharSequenceLengthIncreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthIncreasedInLoopBody.kt"); + } + @TestMetadata("forInRangeLiteralWithMixedTypeBounds.kt") public void testForInRangeLiteralWithMixedTypeBounds() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInRangeLiteralWithMixedTypeBounds.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 691ad26b42c..eee9159348f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -17704,6 +17704,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/ranges/forByteProgressionWithIntIncrement.kt"); } + @TestMetadata("forInCharSequenceLengthDecreasedInLoopBody.kt") + public void testForInCharSequenceLengthDecreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthDecreasedInLoopBody.kt"); + } + + @TestMetadata("forInCharSequenceLengthIncreasedInLoopBody.kt") + public void testForInCharSequenceLengthIncreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthIncreasedInLoopBody.kt"); + } + @TestMetadata("forInRangeLiteralWithMixedTypeBounds.kt") public void testForInRangeLiteralWithMixedTypeBounds() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInRangeLiteralWithMixedTypeBounds.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 712db018ff9..0596903f921 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -14639,6 +14639,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/ranges/forByteProgressionWithIntIncrement.kt"); } + @TestMetadata("forInCharSequenceLengthDecreasedInLoopBody.kt") + public void testForInCharSequenceLengthDecreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthDecreasedInLoopBody.kt"); + } + + @TestMetadata("forInCharSequenceLengthIncreasedInLoopBody.kt") + public void testForInCharSequenceLengthIncreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthIncreasedInLoopBody.kt"); + } + @TestMetadata("forInRangeLiteralWithMixedTypeBounds.kt") public void testForInRangeLiteralWithMixedTypeBounds() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInRangeLiteralWithMixedTypeBounds.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 19c461e4303..339f20ce81f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -15794,6 +15794,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/ranges/forByteProgressionWithIntIncrement.kt"); } + @TestMetadata("forInCharSequenceLengthDecreasedInLoopBody.kt") + public void testForInCharSequenceLengthDecreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthDecreasedInLoopBody.kt"); + } + + @TestMetadata("forInCharSequenceLengthIncreasedInLoopBody.kt") + public void testForInCharSequenceLengthIncreasedInLoopBody() throws Exception { + runTest("compiler/testData/codegen/box/ranges/forInCharSequenceLengthIncreasedInLoopBody.kt"); + } + @TestMetadata("forInRangeLiteralWithMixedTypeBounds.kt") public void testForInRangeLiteralWithMixedTypeBounds() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInRangeLiteralWithMixedTypeBounds.kt");