diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt index d73aa482d91..0a3f961688c 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt @@ -11,11 +11,12 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl -import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol +import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -117,24 +118,53 @@ private class RangeLoopTransformer( ) : IrElementTransformerVoidWithContext() { private val symbols = context.ir.symbols - private val iteratorToLoopHeader = mutableMapOf() private val headerInfoBuilder = HeaderInfoBuilder(context, this::getScopeOwnerSymbol) private val headerProcessor = HeaderProcessor(context, headerInfoBuilder, this::getScopeOwnerSymbol) fun getScopeOwnerSymbol() = currentScope!!.scope.scopeOwnerSymbol - override fun visitVariable(declaration: IrVariable): IrStatement { - val initializer = declaration.initializer - if (initializer == null || initializer !is IrCall) { - return super.visitVariable(declaration) + override fun visitBlock(expression: IrBlock): IrExpression { + // LoopExpressionGenerator in psi2ir lowers `for (loopVar in ) { // Loop body }` into an IrBlock with origin FOR_LOOP. + // This block has 2 statements: + // + // // #1: The "header" + // val it = .iterator() + // + // // #2: The inner while loop + // while (it.hasNext()) { + // val loopVar = it.next() + // // Loop body + // } + // + // We primarily need to determine HOW to optimize the for loop from the iterable expression in the header (e.g., if it's a + // `withIndex()` call, a progression such as `10 downTo 1`). However in some cases (e.g., for `withIndex()`), we also need to + // examine the while loop to determine if we CAN optimize the loop. + if (expression.origin != IrStatementOrigin.FOR_LOOP) { + return super.visitBlock(expression) // Not a for-loop block. } - return when (initializer.origin) { - IrStatementOrigin.FOR_LOOP_ITERATOR -> - processHeader(declaration) - IrStatementOrigin.FOR_LOOP_NEXT -> - processNext(declaration) - else -> null - } ?: super.visitVariable(declaration) + + with(expression.statements) { + assert(size == 2) { "Expected 2 statements in for-loop block, was:\n${expression.dump()}" } + val iteratorVariable = get(0) as IrVariable + assert(iteratorVariable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR) { "Expected FOR_LOOP_ITERATOR origin for iterator variable, was:\n${iteratorVariable.dump()}" } + val loopHeader = headerProcessor.extractHeader(iteratorVariable) + ?: return super.visitBlock(expression) // The iterable in the header is not supported. + val loweredHeader = lowerHeader(iteratorVariable, loopHeader) + + val oldLoop = get(1) as IrWhileLoop + assert(oldLoop.origin == IrStatementOrigin.FOR_LOOP_INNER_WHILE) { "Expected FOR_LOOP_INNER_WHILE origin for while loop, was:\n${oldLoop.dump()}" } + val (newLoop, loopReplacementExpression) = lowerWhileLoop(oldLoop, loopHeader) + ?: return super.visitBlock(expression) // Cannot lower the loop. + + // We can lower both the header and while loop. + // Update mapping from old to new loop so we can later update references in break/continue. + oldLoopToNewLoop[oldLoop] = newLoop + + set(0, loweredHeader) + set(1, loopReplacementExpression) + } + + return super.visitBlock(expression) } /** @@ -144,74 +174,53 @@ private class RangeLoopTransformer( * * Returns null if the for-loop cannot be lowered. */ - private fun processHeader(variable: IrVariable): IrStatement? { - assert(variable.symbol !in iteratorToLoopHeader) - val forLoopInfo = headerProcessor.processHeader(variable) - ?: return null // If the for-loop cannot be lowered. - iteratorToLoopHeader[variable.symbol] = forLoopInfo - + private fun lowerHeader(variable: IrVariable, loopHeader: ForLoopHeader): IrStatement { // Lower into a composite with additional statements (e.g., induction variable) used in the loop condition and body. return IrCompositeImpl( variable.startOffset, variable.endOffset, context.irBuiltIns.unitType, null, - forLoopInfo.loopInitStatements + loopHeader.loopInitStatements ) } - override fun visitWhileLoop(loop: IrWhileLoop): IrExpression { - if (loop.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) { - return super.visitWhileLoop(loop) - } + private fun lowerWhileLoop(loop: IrWhileLoop, loopHeader: ForLoopHeader): LoopReplacement? { + val loopBodyStatements = (loop.body as? IrContainerExpression)?.statements ?: return null + val (mainLoopVariable, mainLoopVariableIndex, loopVariableComponents, loopVariableComponentIndices) = gatherLoopVariableInfo( + loopBodyStatements + ) - with(context.createIrBuilder(getScopeOwnerSymbol(), loop.startOffset, loop.endOffset)) { - // Visit the loop body to process the "next" statement and lower nested loops. - // Processing the "next" statement is necessary for building loops that need to - // reference the loop variable in the loop condition. - val newBody = loop.body?.transform(this@RangeLoopTransformer, null)?.let { - if (it is IrContainerExpression && !it.isTransparentScope) { - IrCompositeImpl(startOffset, endOffset, it.type, it.origin, it.statements) - } else { - it - } - } - - val loopHeader = getLoopHeader(loop.condition) - ?: return super.visitWhileLoop(loop) // If the for-loop cannot be lowered. - val (newLoop, replacementExpression) = loopHeader.buildLoop(this, loop, newBody) - - // Update mapping from old to new loop so we can later update references in break/continue. - oldLoopToNewLoop[loop] = newLoop - - return replacementExpression - } - } - - private fun getLoopHeader(expression: IrExpression): ForLoopHeader? { - if (expression !is IrCall - || (expression.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT - && expression.origin != IrStatementOrigin.FOR_LOOP_NEXT) - ) { + if (loopHeader.consumesLoopVariableComponents && mainLoopVariable.origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE) { + // We determine if there is a destructuring declaration by checking if the main loop variable is temporary. + // This is somewhat brittle and depends on the implementation of LoopExpressionGenerator in psi2ir. + // + // 1. If the loop is `for ((i, v) in arr.withIndex() {}`), the loop body looks like this: + // + // val tmp_loopParameter = it.next() // origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE + // val i = tmp_loopParameter.component1() + // val v = tmp_loopParameter.component2() + // + // 2. If the loop is `for (iv in arr.withIndex() { val (i, v) = iv }`), the loop body looks like this: + // + // val iv = it.next() // origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE + // val i = iv.component1() + // val v = iv.component2() + // + // 3. If the loop is `for ((_, _) in arr.withIndex() {}`), the loop body looks like this: + // + // val tmp_loopParameter = it.next() // origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE + // // No component variables + // + // 4. If the loop is `for (iv in arr.withIndex() {}`), the loop body looks like this: + // + // val iv = it.next() // origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE + // // No component variables + // + // The only way to distinguish between #1 and #2, and between #3 and #4 is to check the origin of the main loop variable. + // We need to distinguish between these because we intend to only optimize #1 and #3. return null } - val iterator = expression.dispatchReceiver as IrGetValue - - // Return null if we didn't lower the corresponding header. - return iteratorToLoopHeader[iterator.symbol] - } - - /** - * Lowers the "next" statement that stores the next element in the iterable into the - * loop variable, e.g., `val i = it.next()`. - * - * Returns null if there was no stored [ForLoopHeader] corresponding to the given "next" - * statement. - */ - private fun processNext(variable: IrVariable): IrExpression? { - val initializer = variable.initializer as IrCall - val forLoopInfo = getLoopHeader(initializer) - ?: return null // If the for-loop cannot be lowered. // The "next" statement (at the top of the loop): // @@ -221,14 +230,84 @@ private class RangeLoopTransformer( // // val i = inductionVariable // For progressions, or `array[inductionVariable]` for arrays // inductionVariable = inductionVariable + step - return with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) { + val initializer = mainLoopVariable.initializer as IrCall + val replacement = with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) { IrCompositeImpl( - variable.startOffset, - variable.endOffset, + mainLoopVariable.startOffset, + mainLoopVariable.endOffset, context.irBuiltIns.unitType, IrStatementOrigin.FOR_LOOP_NEXT, - forLoopInfo.initializeIteration(variable, symbols, this) + loopHeader.initializeIteration(mainLoopVariable, loopVariableComponents, symbols, this) ) } + + // Remove the main loop variable components if they are consumed in initializing the iteration. + if (loopHeader.consumesLoopVariableComponents) { + for (index in loopVariableComponentIndices.asReversed()) { + assert(index > mainLoopVariableIndex) + loopBodyStatements.removeAt(index) + } + } + loopBodyStatements[mainLoopVariableIndex] = replacement + + // Variables in the loop body may be used in the loop condition, so ensure the body scope is transparent (i.e., an IrComposite). + val newBody = loop.body?.let { + if (it is IrContainerExpression && !it.isTransparentScope) { + IrCompositeImpl(loop.startOffset, loop.endOffset, it.type, it.origin, it.statements) + } else { + it + } + } + + return loopHeader.buildLoop(context.createIrBuilder(getScopeOwnerSymbol(), loop.startOffset, loop.endOffset), loop, newBody) + } + + private data class LoopVariableInfo( + val mainLoopVariable: IrVariable, + val mainLoopVariableIndex: Int, + val loopVariableComponents: Map, + val loopVariableComponentIndices: List + ) + + private fun gatherLoopVariableInfo(statements: MutableList): LoopVariableInfo { + // The "next" statement (at the top of the loop) looks something like: + // + // val i = it.next() + // + // In the case of loops with a destructuring declaration (e.g., `for ((i, v) in arr.withIndex()`), the "next" statement includes + // component variables: + // + // val tmp_loopParameter = it.next() + // val i = tmp_loopParameter.component1() + // val v = tmp_loopParameter.component2() + // + // We find the main loop variable and all the component variables that are used to initialize the iteration. + var mainLoopVariable: IrVariable? = null + var mainLoopVariableIndex = -1 + val loopVariableComponents = mutableMapOf() + val loopVariableComponentIndices = mutableListOf() + for ((i, stmt) in statements.withIndex()) { + if (stmt !is IrVariable) continue + val initializer = stmt.initializer as? IrCall + when (val origin = initializer?.origin) { + IrStatementOrigin.FOR_LOOP_NEXT -> { + mainLoopVariable = stmt + mainLoopVariableIndex = i + } + is IrStatementOrigin.COMPONENT_N -> { + if (mainLoopVariable != null && + (initializer.dispatchReceiver as? IrGetValue)?.symbol == mainLoopVariable.symbol + ) { + loopVariableComponents[origin.index] = stmt + loopVariableComponentIndices.add(i) + } + } + } + } + + checkNotNull(mainLoopVariable) { "No 'next' statement in for-loop" } + assert(mainLoopVariableIndex >= 0) + + return LoopVariableInfo(mainLoopVariable, mainLoopVariableIndex, loopVariableComponents, loopVariableComponentIndices) } } \ No newline at end of file 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 194de40cbea..d3642f61733 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 @@ -203,6 +203,15 @@ internal class IndexedGetHeaderInfo( override fun asReversed(): HeaderInfo? = null } +/** + * Information about a for-loop over an iterable returned by `withIndex()`. + */ +internal class WithIndexHeaderInfo(val nestedInfo: HeaderInfo) : HeaderInfo() { + // We cannot easily reverse `withIndex()` so we do not attempt to handle it. We would have to start from the last value of the index, + // easily calculable (or even impossible) in most cases. + override fun asReversed(): HeaderInfo? = null +} + /** Matches an iterable expression and builds a [HeaderInfo] from the expression. */ internal interface HeaderInfoHandler { /** Returns true if the handler can build a [HeaderInfo] from the iterable expression. */ @@ -263,7 +272,10 @@ internal class HeaderInfoBuilder(context: CommonBackendContext, private val scop StepHandler(context, this) ) - private val reversedHandler = ReversedHandler(context, this) + private val callHandlers = listOf( + ReversedHandler(context, this), + WithIndexHandler(context, this) + ) // NOTE: StringIterationHandler MUST come before CharSequenceIterationHandler. // String is subtype of CharSequence and therefore its handler is more specialized. @@ -278,10 +290,11 @@ internal class HeaderInfoBuilder(context: CommonBackendContext, private val scop /** Builds a [HeaderInfo] for iterable expressions that are calls (e.g., `.reversed()`, `.indices`. */ override fun visitCall(iterable: IrCall, iteratorCall: IrCall?): HeaderInfo? { - // Return the HeaderInfo from the first successful match. First, try to match a `reversed()` call. - val reversedHeaderInfo = reversedHandler.handle(iterable, iteratorCall, null, scopeOwnerSymbol()) - if (reversedHeaderInfo != null) - return reversedHeaderInfo + // Return the HeaderInfo from the first successful match. + // First, try to match a `reversed()` or `withIndex()` call. + val callHeaderInfo = callHandlers.firstNotNullResult { it.handle(iterable, iteratorCall, null, scopeOwnerSymbol()) } + if (callHeaderInfo != null) + return callHeaderInfo // Try to match a call to build a progression (e.g., `.indices`, `downTo`). val progressionType = ProgressionType.fromIrType(iterable.type, symbols) 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 a596e1eda4b..e3a4d59334d 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 @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrContainerExpression import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.expressions.impl.IrDoWhileLoopImpl @@ -41,9 +42,16 @@ internal interface ForLoopHeader { /** Statements used to initialize the entire loop (e.g., declare induction variable). */ val loopInitStatements: List + /** + * Whether or not [initializeIteration] consumes the loop variable components assigned to it. + * If true, the component variables should be removed from the un-lowered loop. + */ + val consumesLoopVariableComponents: Boolean + /** Statements used to initialize an iteration of the loop (e.g., assign loop variable). */ fun initializeIteration( - loopVariable: IrVariable, + loopVariable: IrVariable?, + loopVariableComponents: Map, symbols: Symbols, builder: DeclarationIrBuilder ): List @@ -52,15 +60,17 @@ internal interface ForLoopHeader { fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement } -internal abstract class NumericForLoopHeader( - headerInfo: NumericHeaderInfo, +internal abstract class NumericForLoopHeader( + protected val headerInfo: T, builder: DeclarationIrBuilder, protected val isLastInclusive: Boolean ) : ForLoopHeader { - protected val inductionVariable: IrVariable + override val consumesLoopVariableComponents = false + + val inductionVariable: IrVariable + val stepVariable: IrVariable protected val lastVariableIfCanCacheLast: IrVariable? - protected val stepVariable: IrVariable protected val lastExpression: IrExpression // Always copy `lastExpression` is it may be used in multiple conditions. get() = field.deepCopyWithSymbols() @@ -120,11 +130,6 @@ internal abstract class NumericForLoopHeader( } } - // This cannot be declared and initialized directly in the constructor. At the time of the base class (ForLoopHeader) constructor - // execution, the `headerInfo` property overridden in the derived class (e.g., NumericForLoopHeader) is not yet initialized. - // Therefore, we must use the `headerInfo` constructor parameter in the "init" block above instead of using the property. - protected open val headerInfo: NumericHeaderInfo = headerInfo - private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) = if (expression.type is IrSimpleType && expression.type.isNullable()) { irImplicitCast(expression, expression.type.makeNotNull()) @@ -204,9 +209,9 @@ internal abstract class NumericForLoopHeader( } internal class ProgressionLoopHeader( - override val headerInfo: ProgressionHeaderInfo, + headerInfo: ProgressionHeaderInfo, builder: DeclarationIrBuilder -) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = true) { +) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = true) { // For this loop: // @@ -227,13 +232,28 @@ internal class ProgressionLoopHeader( private var loopVariable: IrVariable? = null - override fun initializeIteration(loopVariable: IrVariable, symbols: Symbols, builder: DeclarationIrBuilder) = + override fun initializeIteration( + loopVariable: IrVariable?, + loopVariableComponents: Map, + symbols: Symbols, + builder: DeclarationIrBuilder + ) = with(builder) { - this@ProgressionLoopHeader.loopVariable = loopVariable + // loopVariable is used in the loop condition if it can overflow. If no loopVariable was provided, create one. + this@ProgressionLoopHeader.loopVariable = if (headerInfo.canOverflow && loopVariable == null) { + scope.createTemporaryVariable( + irGet(inductionVariable), + nameHint = "loopVariable", + isMutable = true + ) + } else { + loopVariable?.initializer = irGet(inductionVariable) + loopVariable + } + // loopVariable = inductionVariable // inductionVariable = inductionVariable + step - loopVariable.initializer = irGet(inductionVariable) - listOf(loopVariable, incrementInductionVariable(this)) + listOfNotNull(loopVariable, incrementInductionVariable(this)) } override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?) = @@ -259,9 +279,9 @@ internal class ProgressionLoopHeader( // // if (inductionVar <= last) { // do { - // val loopVar = inductionVar - // inductionVar += step - // // Loop body + // val loopVar = inductionVar + // inductionVar += step + // // Loop body // } while (inductionVar <= last) // } // @@ -283,21 +303,29 @@ internal class ProgressionLoopHeader( } internal class IndexedGetLoopHeader( - override val headerInfo: IndexedGetHeaderInfo, + headerInfo: IndexedGetHeaderInfo, builder: DeclarationIrBuilder -) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = false) { +) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = false) { override val loopInitStatements = listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable) - override fun initializeIteration(loopVariable: IrVariable, symbols: Symbols, builder: DeclarationIrBuilder) = + override fun initializeIteration( + loopVariable: IrVariable?, + loopVariableComponents: Map, + symbols: Symbols, + builder: DeclarationIrBuilder + ) = with(builder) { // loopVariable = objectVariable[inductionVariable] val indexedGetFun = with(headerInfo.expressionHandler) { headerInfo.objectVariable.type.getFunction } - loopVariable.initializer = irCall(indexedGetFun).apply { + val get = irCall(indexedGetFun).apply { dispatchReceiver = irGet(headerInfo.objectVariable) putValueArgument(0, irGet(inductionVariable)) } - listOf(loopVariable, incrementInductionVariable(this)) + loopVariable?.initializer = get + // Even if there is no loop variable, we always want to call `get()` as it may have side-effects. + // The un-lowered loop always calls `get()` on each iteration. + listOf(loopVariable ?: get) + incrementInductionVariable(this) } override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement = with(builder) { @@ -319,6 +347,141 @@ internal class IndexedGetLoopHeader( } } +internal class WithIndexLoopHeader( + private val headerInfo: WithIndexHeaderInfo, + builder: DeclarationIrBuilder +) : ForLoopHeader { + + private val nestedLoopHeader: ForLoopHeader + private val indexVariable: IrVariable + private val ownsIndexVariable: Boolean + + init { + with(builder) { + // To build the optimized/lowered `for` loop over a `withIndex()` call, we first need the header for the underlying iterable so + // so that we know how to build the loop for that iterable. More info in comments in initializeIteration(). + nestedLoopHeader = when (val nestedInfo = headerInfo.nestedInfo) { + is IndexedGetHeaderInfo -> IndexedGetLoopHeader(nestedInfo, builder) + is ProgressionHeaderInfo -> ProgressionLoopHeader(nestedInfo, builder) + is WithIndexHeaderInfo -> throw IllegalStateException("Nested WithIndexHeaderInfo not allowed for WithIndexLoopHeader") + } + + // Do not build own indexVariable if the nested loop header has an inductionVariable == 0 and step == 1. + // This is the case when the underlying iterable is an array, CharSequence, or a progression from 0 with step 1. + // We can use the induction variable from the underlying iterable as the index variable, since it progresses in the same way. + if (nestedLoopHeader is NumericForLoopHeader<*> && + nestedLoopHeader.inductionVariable.type.isInt() && + nestedLoopHeader.inductionVariable.initializer?.constLongValue == 0L && + nestedLoopHeader.stepVariable.initializer?.constLongValue == 1L + ) { + indexVariable = nestedLoopHeader.inductionVariable + ownsIndexVariable = false + } else { + indexVariable = scope.createTemporaryVariable( + irInt(0), + nameHint = "index", + isMutable = true + ) + ownsIndexVariable = true + } + } + } + + // Add the index variable to the statements from the nested loop header. + override val loopInitStatements = nestedLoopHeader.loopInitStatements.let { if (ownsIndexVariable) it + indexVariable else it } + + override val consumesLoopVariableComponents = true + + override fun initializeIteration( + loopVariable: IrVariable?, + loopVariableComponents: Map, + symbols: Symbols, + builder: DeclarationIrBuilder + ) = + with(builder) { + // The `withIndex()` extension function returns a lazy Iterable that wraps each element of the underlying iterable (e.g., array, + // progression, Iterable, Sequence, CharSequence) into an IndexedValue containing the index of that element and the element + // itself. The iterator for this lazy Iterable looks like this: + // + // internal class IndexingIterator(private val iterator: Iterator) : Iterator> { + // private var index = 0 + // override fun hasNext() = iterator.hasNext() + // override fun next() = IndexedValue(checkIndexOverflow(index++), iterator.next()) + // } + // + // IndexedValue looks like this: + // + // data class IndexedValue(val index: Int, val value: T) + // + // For example, if the `for` loop is: + // + // for ((i, v) in (1..10 step 2).withIndex()) { /* Loop body */ } + // + // ...the optimized loop for the underlying progression looks something like this: + // + // var inductionVar = 1 + // val last = 10 + // val step = 2 + // if (inductionVar <= last) { + // do { + // val v = inductionVar + // inductionVar += step + // // Loop body + // } while (inductionVar <= last) + // } + // + // ...and the optimized loop with `withIndex()` looks something like this (see "// ADDED" statements): + // + // var inductionVar = 1 + // val last = 10 + // val step = 2 + // var index = 0 // ADDED + // if (inductionVar <= last) { + // do { + // val i = index // ADDED + // val v = inductionVar + // inductionVar += step + // // Loop body + // checkIndexOverflow(index++) // ADDED + // } while (inductionVar <= last) + // } + // + // We "wire" the 1st destructured component to index, and the 2nd to the loop variable value from the underlying iterable. + loopVariableComponents[1]?.initializer = irGet(indexVariable) + listOfNotNull(loopVariableComponents[1]) + nestedLoopHeader.initializeIteration( + loopVariableComponents[2], + linkedMapOf(), + symbols, + builder + ) + } + + // Use the nested loop header to build the loop. More info in comments in initializeIteration(). + override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?) = + nestedLoopHeader.buildLoop(builder, oldLoop, newBody).apply { + if (ownsIndexVariable) { + with(builder) { + // Add `index++` to end of the loop. + // TODO: MUSTDO: Check for overflow for Iterable and Sequence (call to checkIndexOverflow()). + val plusFun = indexVariable.type.getClass()!!.functions.first { + it.name == OperatorNameConventions.PLUS && + it.valueParameters.size == 1 && + it.valueParameters[0].type.isInt() + } + (newLoop.body as IrContainerExpression).statements.add( + irSetVar( + indexVariable.symbol, irCallOp( + plusFun.symbol, plusFun.returnType, + irGet(indexVariable), + irInt(1) + ) + ) + ) + } + } + } +} + /** * Given the for-loop iterator variable, extract information about the iterable subject * and create a [ForLoopHeader] from it. @@ -338,7 +501,7 @@ internal class HeaderProcessor( * * Returns null if the for-loop cannot be lowered. */ - fun processHeader(variable: IrVariable): ForLoopHeader? { + fun extractHeader(variable: IrVariable): ForLoopHeader? { // Verify the variable type is a subtype of Iterator<*>. assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR) if (!variable.type.isSubtypeOfClass(symbols.iterator)) { @@ -365,6 +528,7 @@ internal class HeaderProcessor( return when (headerInfo) { is IndexedGetHeaderInfo -> IndexedGetLoopHeader(headerInfo, builder) is ProgressionHeaderInfo -> ProgressionLoopHeader(headerInfo, builder) + is WithIndexHeaderInfo -> WithIndexLoopHeader(headerInfo, builder) } } } 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 846591d9277..917e7462315 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 @@ -695,3 +695,34 @@ internal open class CharSequenceIterationHandler(context: CommonBackendContext, internal class StringIterationHandler(context: CommonBackendContext) : CharSequenceIterationHandler(context, canCacheLast = true) { override fun matchIterable(expression: IrExpression) = expression.type.isString() } + +/** Builds a [HeaderInfo] for calls to `withIndex()`. */ +internal class WithIndexHandler(context: CommonBackendContext, private val visitor: HeaderInfoBuilder) : + HeaderInfoFromCallHandler { + + // Use Quantifier.ANY so we can handle all `withIndex()` calls in the same manner. + override val matcher = createIrCallMatcher(Quantifier.ANY) { + callee { + fqName { it == FqName("kotlin.collections.withIndex") } + extensionReceiver { it != null && it.type.run { isArray() || isPrimitiveArray() } } + parameterCount { it == 0 } + } + callee { + fqName { it == FqName("kotlin.text.withIndex") } + extensionReceiver { it != null && it.type.isSubtypeOfClass(context.ir.symbols.charSequence) } + parameterCount { it == 0 } + } + + // TODO: Handle Iterable.withIndex(), Sequence.withIndex() + } + + override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol): HeaderInfo? { + // WithIndexHeaderInfo is a composite that contains the HeaderInfo for the underlying iterable (if any). + val nestedInfo = expression.extensionReceiver!!.accept(visitor, null) ?: return null + + // We cannot lower `iterable.withIndex().withIndex()`. + if (nestedInfo is WithIndexHeaderInfo) return null + + return WithIndexHeaderInfo(nestedInfo) + } +} diff --git a/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt b/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt new file mode 100644 index 00000000000..3a9ba63199f --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt @@ -0,0 +1,15 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// KJS_WITH_FULL_RUNTIME +// WITH_RUNTIME + +val arr = arrayOf("a", "b", "c", "d") + +fun box(): String { + var count = 0 + + for ((_, _) in arr.withIndex()) { + count++ + } + + return if (count == 4) "OK" else "fail: '$count'" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNotDestructured.kt b/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNotDestructured.kt new file mode 100644 index 00000000000..aa7400e504a --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNotDestructured.kt @@ -0,0 +1,17 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// KJS_WITH_FULL_RUNTIME +// WITH_RUNTIME + +val arr = arrayOf("a", "b", "c", "d") + +fun box(): String { + val s = StringBuilder() + + for (iv in arr.withIndex()) { + val (i, x) = iv + s.append("$i:$x;") + } + + val ss = s.toString() + return if (ss == "0:a;1:b;2:c;3:d;") "OK" else "fail: '$ss'" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexOrElementVar.kt b/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexOrElementVar.kt new file mode 100644 index 00000000000..6e5355e4bcc --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexOrElementVar.kt @@ -0,0 +1,15 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// KJS_WITH_FULL_RUNTIME +// WITH_RUNTIME + +val xs = "abcd" + +fun box(): String { + var count = 0 + + for ((_, _) in xs.withIndex()) { + count++ + } + + return if (count == 4) "OK" else "fail: '$count'" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNotDestructured.kt b/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNotDestructured.kt new file mode 100644 index 00000000000..99942f3619a --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNotDestructured.kt @@ -0,0 +1,15 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// KJS_WITH_FULL_RUNTIME +// WITH_RUNTIME + +fun box(): String { + val s = StringBuilder() + + for (iv in "abcd".withIndex()) { + val (index, x) = iv + s.append("$index:$x;") + } + + val ss = s.toString() + return if (ss == "0:a;1:b;2:c;3:d;") "OK" else "fail: '$ss'" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt index 9da89b3103f..cce6d207706 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val arr = arrayOf("a", "b", "c", "d") fun box(): String { @@ -19,3 +18,6 @@ fun box(): String { // 0 component1 // 0 component2 // 1 ARRAYLENGTH + +// The 1st ICONST_0 is for initializing the array. 2nd is for initializing the index in the lowered for-loop. +// 2 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt index 86dfe9f9169..5284ae1f151 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val arr = arrayOf("a", "b", "c", "d") fun box(): String { @@ -19,3 +18,6 @@ fun box(): String { // 0 component1 // 0 component2 // 1 ARRAYLENGTH + +// The 1st ICONST_0 is for initializing the array. 2nd is for initializing the index in the lowered for-loop. +// 2 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInEmptyArrayWithIndex.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInEmptyArrayWithIndex.kt index 1a9c142ee9d..a7d55ca1f55 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInEmptyArrayWithIndex.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInEmptyArrayWithIndex.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val arr = intArrayOf() fun box(): String { @@ -16,3 +15,6 @@ fun box(): String { // 0 component1 // 0 component2 // 1 ARRAYLENGTH + +// The 1st ICONST_0 is for initializing the array. 2nd is for initializing the index in the lowered for-loop. +// 2 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInIntArrayWithIndex.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInIntArrayWithIndex.kt index 3fdfdec2110..96071761471 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInIntArrayWithIndex.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInIntArrayWithIndex.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val arr = intArrayOf(10, 20, 30, 40) fun box(): String { @@ -17,3 +16,6 @@ fun box(): String { // 0 component1 // 0 component2 // 1 ARRAYLENGTH + +// The 1st ICONST_0 is for initializing the array. 2nd is for initializing the index in the lowered for-loop. +// 2 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInObjectArrayWithIndex.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInObjectArrayWithIndex.kt index e4392ae5f0d..1d3b1d4b461 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInObjectArrayWithIndex.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInArrayWithIndex/forInObjectArrayWithIndex.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val arr = arrayOf("a", "b", "c", "d") fun box(): String { @@ -19,3 +18,6 @@ fun box(): String { // 0 component1 // 0 component2 // 1 ARRAYLENGTH + +// The 1st ICONST_0 is for initializing the array. 2nd is for initializing the index in the lowered for-loop. +// 2 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInCharSequenceWithIndex.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInCharSequenceWithIndex.kt index 308f81abc59..bf48c2d3b46 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInCharSequenceWithIndex.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInCharSequenceWithIndex.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val cs: CharSequence = "abcd" fun box(): String { @@ -20,3 +19,6 @@ fun box(): String { // 0 component2 // 1 length // 1 charAt + +// The ICONST_0 is for initializing the index in the lowered for-loop. +// 1 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInEmptyStringWithIndex.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInEmptyStringWithIndex.kt index a66b2a42aae..e3752888b3e 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInEmptyStringWithIndex.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInEmptyStringWithIndex.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun box(): String { for ((index, x) in "".withIndex()) { return "Loop over empty String should not be executed" @@ -14,3 +13,6 @@ fun box(): String { // 0 component2 // 1 length // 1 charAt + +// The ICONST_0 is for initializing the index in the lowered for-loop. +// 1 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndex.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndex.kt index 2777e316a82..f3d3f15aeeb 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndex.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndex.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun box(): String { val s = StringBuilder() @@ -18,3 +17,6 @@ fun box(): String { // 0 component2 // 1 length // 1 charAt + +// The ICONST_0 is for initializing the index in the lowered for-loop. +// 1 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt index 790747ca657..e3068baaa07 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val xs = "abcd" fun box(): String { @@ -20,3 +19,6 @@ fun box(): String { // 0 component2 // 1 length // 1 charAt + +// The ICONST_0 is for initializing the index in the lowered for-loop. +// 1 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt index f2d1fd59e5c..95e2ec7e04e 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val xs = "abcd" fun box(): String { @@ -20,3 +19,6 @@ fun box(): String { // 0 component2 // 1 length // 1 charAt + +// The ICONST_0 is for initializing the index in the lowered for-loop. +// 1 ICONST_0 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt index c37e5ab0aa7..94f7335754d 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR val xs = "abcd" fun useAny(x: Any) {} @@ -23,3 +22,6 @@ fun box(): String { // 0 component2 // 1 length // 1 charAt + +// The ICONST_0 is for initializing the index in the lowered for-loop. +// 1 ICONST_0 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index a5414b9a9e4..04246915cce 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -5301,11 +5301,21 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt"); } + @TestMetadata("forInArrayWithIndexNoIndexOrElementVar.kt") + public void testForInArrayWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInArrayWithIndexNoIndexVar.kt") public void testForInArrayWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); } + @TestMetadata("forInArrayWithIndexNotDestructured.kt") + public void testForInArrayWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNotDestructured.kt"); + } + @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); @@ -5419,11 +5429,21 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt"); } + @TestMetadata("forInStringWithIndexNoIndexOrElementVar.kt") + public void testForInStringWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInStringWithIndexNoIndexVar.kt") public void testForInStringWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); } + @TestMetadata("forInStringWithIndexNotDestructured.kt") + public void testForInStringWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNotDestructured.kt"); + } + @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 680baa5fda3..57a4219df36 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -5301,11 +5301,21 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt"); } + @TestMetadata("forInArrayWithIndexNoIndexOrElementVar.kt") + public void testForInArrayWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInArrayWithIndexNoIndexVar.kt") public void testForInArrayWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); } + @TestMetadata("forInArrayWithIndexNotDestructured.kt") + public void testForInArrayWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNotDestructured.kt"); + } + @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); @@ -5419,11 +5429,21 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt"); } + @TestMetadata("forInStringWithIndexNoIndexOrElementVar.kt") + public void testForInStringWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInStringWithIndexNoIndexVar.kt") public void testForInStringWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); } + @TestMetadata("forInStringWithIndexNotDestructured.kt") + public void testForInStringWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNotDestructured.kt"); + } + @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 67331472b2e..9daa941f491 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -5266,19 +5266,34 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexContinuesAsUnmodified.kt"); } - @TestMetadata("forInArrrayWithIndexNoElementVar.kt") - public void testForInArrrayWithIndexNoElementVar() throws Exception { - runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrrayWithIndexNoElementVar.kt"); + @TestMetadata("forInArrayWithIndexNoElementVar.kt") + public void testForInArrayWithIndexNoElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt"); } - @TestMetadata("forInArrrayWithIndexNoIndexVar.kt") - public void testForInArrrayWithIndexNoIndexVar() throws Exception { - runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrrayWithIndexNoIndexVar.kt"); + @TestMetadata("forInArrayWithIndexNoIndexOrElementVar.kt") + public void testForInArrayWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt"); } - @TestMetadata("forInArrrayWithIndexWithExplicitlyTypedIndexVariable.kt") - public void testForInArrrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { - runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrrayWithIndexWithExplicitlyTypedIndexVariable.kt"); + @TestMetadata("forInArrayWithIndexNoIndexVar.kt") + public void testForInArrayWithIndexNoIndexVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); + } + + @TestMetadata("forInArrayWithIndexNotDestructured.kt") + public void testForInArrayWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNotDestructured.kt"); + } + + @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt") + public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); + } + + @TestMetadata("forInByteArrayWithIndex.kt") + public void testForInByteArrayWithIndex() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInByteArrayWithIndex.kt"); } @TestMetadata("forInByteArrayWithIndexWithSmartCast.kt") @@ -5286,14 +5301,9 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInByteArrayWithIndexWithSmartCast.kt"); } - @TestMetadata("forInByteArrrayWithIndex.kt") - public void testForInByteArrrayWithIndex() throws Exception { - runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInByteArrrayWithIndex.kt"); - } - - @TestMetadata("forInEmptyArrrayWithIndex.kt") - public void testForInEmptyArrrayWithIndex() throws Exception { - runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInEmptyArrrayWithIndex.kt"); + @TestMetadata("forInEmptyArrayWithIndex.kt") + public void testForInEmptyArrayWithIndex() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInEmptyArrayWithIndex.kt"); } @TestMetadata("forInGenericArrayOfIntsWithIndex.kt") @@ -5311,30 +5321,30 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInGenericArrayWithIndex.kt"); } + @TestMetadata("forInIntArrayWithIndex.kt") + public void testForInIntArrayWithIndex() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInIntArrayWithIndex.kt"); + } + @TestMetadata("forInIntArrayWithIndexWithSmartCast.kt") public void testForInIntArrayWithIndexWithSmartCast() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInIntArrayWithIndexWithSmartCast.kt"); } - @TestMetadata("forInIntArrrayWithIndex.kt") - public void testForInIntArrrayWithIndex() throws Exception { - runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInIntArrrayWithIndex.kt"); + @TestMetadata("forInObjectArrayWithIndex.kt") + public void testForInObjectArrayWithIndex() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInObjectArrayWithIndex.kt"); } - @TestMetadata("forInObjectArrrayWithIndex.kt") - public void testForInObjectArrrayWithIndex() throws Exception { - runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInObjectArrrayWithIndex.kt"); + @TestMetadata("forInShortArrayWithIndex.kt") + public void testForInShortArrayWithIndex() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInShortArrayWithIndex.kt"); } @TestMetadata("forInShortArrayWithIndexWithSmartCast.kt") public void testForInShortArrayWithIndexWithSmartCast() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInShortArrayWithIndexWithSmartCast.kt"); } - - @TestMetadata("forInShortArrrayWithIndex.kt") - public void testForInShortArrrayWithIndex() throws Exception { - runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInShortArrrayWithIndex.kt"); - } } @TestMetadata("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex") @@ -5389,11 +5399,21 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt"); } + @TestMetadata("forInStringWithIndexNoIndexOrElementVar.kt") + public void testForInStringWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInStringWithIndexNoIndexVar.kt") public void testForInStringWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); } + @TestMetadata("forInStringWithIndexNotDestructured.kt") + public void testForInStringWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNotDestructured.kt"); + } + @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index d81fe15bcce..cf8f8923075 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -5271,11 +5271,21 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt"); } + @TestMetadata("forInArrayWithIndexNoIndexOrElementVar.kt") + public void testForInArrayWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInArrayWithIndexNoIndexVar.kt") public void testForInArrayWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); } + @TestMetadata("forInArrayWithIndexNotDestructured.kt") + public void testForInArrayWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNotDestructured.kt"); + } + @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); @@ -5389,11 +5399,21 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt"); } + @TestMetadata("forInStringWithIndexNoIndexOrElementVar.kt") + public void testForInStringWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInStringWithIndexNoIndexVar.kt") public void testForInStringWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); } + @TestMetadata("forInStringWithIndexNotDestructured.kt") + public void testForInStringWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNotDestructured.kt"); + } + @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.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 e778ac4a838..72aaa57d417 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 @@ -4401,11 +4401,21 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt"); } + @TestMetadata("forInArrayWithIndexNoIndexOrElementVar.kt") + public void testForInArrayWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInArrayWithIndexNoIndexVar.kt") public void testForInArrayWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); } + @TestMetadata("forInArrayWithIndexNotDestructured.kt") + public void testForInArrayWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNotDestructured.kt"); + } + @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); @@ -4519,11 +4529,21 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt"); } + @TestMetadata("forInStringWithIndexNoIndexOrElementVar.kt") + public void testForInStringWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInStringWithIndexNoIndexVar.kt") public void testForInStringWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); } + @TestMetadata("forInStringWithIndexNotDestructured.kt") + public void testForInStringWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNotDestructured.kt"); + } + @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.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 d8e5bd3acf0..47245c9a160 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 @@ -4411,11 +4411,21 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt"); } + @TestMetadata("forInArrayWithIndexNoIndexOrElementVar.kt") + public void testForInArrayWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInArrayWithIndexNoIndexVar.kt") public void testForInArrayWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); } + @TestMetadata("forInArrayWithIndexNotDestructured.kt") + public void testForInArrayWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNotDestructured.kt"); + } + @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); @@ -4529,11 +4539,21 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoElementVar.kt"); } + @TestMetadata("forInStringWithIndexNoIndexOrElementVar.kt") + public void testForInStringWithIndexNoIndexOrElementVar() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexOrElementVar.kt"); + } + @TestMetadata("forInStringWithIndexNoIndexVar.kt") public void testForInStringWithIndexNoIndexVar() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); } + @TestMetadata("forInStringWithIndexNotDestructured.kt") + public void testForInStringWithIndexNotDestructured() throws Exception { + runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNotDestructured.kt"); + } + @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt") public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt");