Handle withIndex() on arrays and CharSequences in ForLoopsLowering.

This commit is contained in:
Mark Punzalan
2019-11-04 16:13:56 -08:00
committed by max-kammerer
parent 735535dd5a
commit a54d9482dd
25 changed files with 633 additions and 142 deletions
@@ -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<IrVariableSymbol, ForLoopHeader>()
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 <someIterable>) { // Loop body }` into an IrBlock with origin FOR_LOOP.
// This block has 2 statements:
//
// // #1: The "header"
// val it = <someIterable>.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<Int, IrVariable>,
val loopVariableComponentIndices: List<Int>
)
private fun gatherLoopVariableInfo(statements: MutableList<IrStatement>): 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<Int, IrVariable>()
val loopVariableComponentIndices = mutableListOf<Int>()
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)
}
}
@@ -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<E : IrExpression, D> {
/** 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)
@@ -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<IrStatement>
/**
* 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<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>,
builder: DeclarationIrBuilder
): List<IrStatement>
@@ -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<T : NumericHeaderInfo>(
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<ProgressionHeaderInfo>(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<CommonBackendContext>, builder: DeclarationIrBuilder) =
override fun initializeIteration(
loopVariable: IrVariable?,
loopVariableComponents: Map<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>,
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<IndexedGetHeaderInfo>(headerInfo, builder, isLastInclusive = false) {
override val loopInitStatements = listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable)
override fun initializeIteration(loopVariable: IrVariable, symbols: Symbols<CommonBackendContext>, builder: DeclarationIrBuilder) =
override fun initializeIteration(
loopVariable: IrVariable?,
loopVariableComponents: Map<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>,
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<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>,
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<out T>(private val iterator: Iterator<T>) : Iterator<IndexedValue<T>> {
// private var index = 0
// override fun hasNext() = iterator.hasNext()
// override fun next() = IndexedValue(checkIndexOverflow(index++), iterator.next())
// }
//
// IndexedValue looks like this:
//
// data class IndexedValue<out T>(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)
}
}
}
@@ -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<Nothing?> {
// 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)
}
}
@@ -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'"
}
@@ -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'"
}
@@ -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'"
}
@@ -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'"
}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");