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.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.ir.IrStatement 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.IrFile
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl 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.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -117,24 +118,53 @@ private class RangeLoopTransformer(
) : IrElementTransformerVoidWithContext() { ) : IrElementTransformerVoidWithContext() {
private val symbols = context.ir.symbols private val symbols = context.ir.symbols
private val iteratorToLoopHeader = mutableMapOf<IrVariableSymbol, ForLoopHeader>()
private val headerInfoBuilder = HeaderInfoBuilder(context, this::getScopeOwnerSymbol) private val headerInfoBuilder = HeaderInfoBuilder(context, this::getScopeOwnerSymbol)
private val headerProcessor = HeaderProcessor(context, headerInfoBuilder, this::getScopeOwnerSymbol) private val headerProcessor = HeaderProcessor(context, headerInfoBuilder, this::getScopeOwnerSymbol)
fun getScopeOwnerSymbol() = currentScope!!.scope.scopeOwnerSymbol fun getScopeOwnerSymbol() = currentScope!!.scope.scopeOwnerSymbol
override fun visitVariable(declaration: IrVariable): IrStatement { override fun visitBlock(expression: IrBlock): IrExpression {
val initializer = declaration.initializer // LoopExpressionGenerator in psi2ir lowers `for (loopVar in <someIterable>) { // Loop body }` into an IrBlock with origin FOR_LOOP.
if (initializer == null || initializer !is IrCall) { // This block has 2 statements:
return super.visitVariable(declaration) //
// // #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 -> with(expression.statements) {
processHeader(declaration) assert(size == 2) { "Expected 2 statements in for-loop block, was:\n${expression.dump()}" }
IrStatementOrigin.FOR_LOOP_NEXT -> val iteratorVariable = get(0) as IrVariable
processNext(declaration) assert(iteratorVariable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR) { "Expected FOR_LOOP_ITERATOR origin for iterator variable, was:\n${iteratorVariable.dump()}" }
else -> null val loopHeader = headerProcessor.extractHeader(iteratorVariable)
} ?: super.visitVariable(declaration) ?: 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. * Returns null if the for-loop cannot be lowered.
*/ */
private fun processHeader(variable: IrVariable): IrStatement? { private fun lowerHeader(variable: IrVariable, loopHeader: ForLoopHeader): IrStatement {
assert(variable.symbol !in iteratorToLoopHeader)
val forLoopInfo = headerProcessor.processHeader(variable)
?: return null // If the for-loop cannot be lowered.
iteratorToLoopHeader[variable.symbol] = forLoopInfo
// Lower into a composite with additional statements (e.g., induction variable) used in the loop condition and body. // Lower into a composite with additional statements (e.g., induction variable) used in the loop condition and body.
return IrCompositeImpl( return IrCompositeImpl(
variable.startOffset, variable.startOffset,
variable.endOffset, variable.endOffset,
context.irBuiltIns.unitType, context.irBuiltIns.unitType,
null, null,
forLoopInfo.loopInitStatements loopHeader.loopInitStatements
) )
} }
override fun visitWhileLoop(loop: IrWhileLoop): IrExpression { private fun lowerWhileLoop(loop: IrWhileLoop, loopHeader: ForLoopHeader): LoopReplacement? {
if (loop.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) { val loopBodyStatements = (loop.body as? IrContainerExpression)?.statements ?: return null
return super.visitWhileLoop(loop) val (mainLoopVariable, mainLoopVariableIndex, loopVariableComponents, loopVariableComponentIndices) = gatherLoopVariableInfo(
} loopBodyStatements
)
with(context.createIrBuilder(getScopeOwnerSymbol(), loop.startOffset, loop.endOffset)) { if (loopHeader.consumesLoopVariableComponents && mainLoopVariable.origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE) {
// Visit the loop body to process the "next" statement and lower nested loops. // We determine if there is a destructuring declaration by checking if the main loop variable is temporary.
// Processing the "next" statement is necessary for building loops that need to // This is somewhat brittle and depends on the implementation of LoopExpressionGenerator in psi2ir.
// reference the loop variable in the loop condition. //
val newBody = loop.body?.transform(this@RangeLoopTransformer, null)?.let { // 1. If the loop is `for ((i, v) in arr.withIndex() {}`), the loop body looks like this:
if (it is IrContainerExpression && !it.isTransparentScope) { //
IrCompositeImpl(startOffset, endOffset, it.type, it.origin, it.statements) // val tmp_loopParameter = it.next() // origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE
} else { // val i = tmp_loopParameter.component1()
it // 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 loopHeader = getLoopHeader(loop.condition) // val iv = it.next() // origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE
?: return super.visitWhileLoop(loop) // If the for-loop cannot be lowered. // val i = iv.component1()
val (newLoop, replacementExpression) = loopHeader.buildLoop(this, loop, newBody) // val v = iv.component2()
//
// Update mapping from old to new loop so we can later update references in break/continue. // 3. If the loop is `for ((_, _) in arr.withIndex() {}`), the loop body looks like this:
oldLoopToNewLoop[loop] = newLoop //
// val tmp_loopParameter = it.next() // origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE
return replacementExpression // // No component variables
} //
} // 4. If the loop is `for (iv in arr.withIndex() {}`), the loop body looks like this:
//
private fun getLoopHeader(expression: IrExpression): ForLoopHeader? { // val iv = it.next() // origin != IrDeclarationOrigin.IR_TEMPORARY_VARIABLE
if (expression !is IrCall // // No component variables
|| (expression.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT //
&& expression.origin != IrStatementOrigin.FOR_LOOP_NEXT) // 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 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): // 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 // val i = inductionVariable // For progressions, or `array[inductionVariable]` for arrays
// inductionVariable = inductionVariable + step // 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( IrCompositeImpl(
variable.startOffset, mainLoopVariable.startOffset,
variable.endOffset, mainLoopVariable.endOffset,
context.irBuiltIns.unitType, context.irBuiltIns.unitType,
IrStatementOrigin.FOR_LOOP_NEXT, 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 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. */ /** Matches an iterable expression and builds a [HeaderInfo] from the expression. */
internal interface HeaderInfoHandler<E : IrExpression, D> { internal interface HeaderInfoHandler<E : IrExpression, D> {
/** Returns true if the handler can build a [HeaderInfo] from the iterable expression. */ /** 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) 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. // NOTE: StringIterationHandler MUST come before CharSequenceIterationHandler.
// String is subtype of CharSequence and therefore its handler is more specialized. // 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`. */ /** Builds a [HeaderInfo] for iterable expressions that are calls (e.g., `.reversed()`, `.indices`. */
override fun visitCall(iterable: IrCall, iteratorCall: IrCall?): HeaderInfo? { override fun visitCall(iterable: IrCall, iteratorCall: IrCall?): HeaderInfo? {
// Return the HeaderInfo from the first successful match. First, try to match a `reversed()` call. // Return the HeaderInfo from the first successful match.
val reversedHeaderInfo = reversedHandler.handle(iterable, iteratorCall, null, scopeOwnerSymbol()) // First, try to match a `reversed()` or `withIndex()` call.
if (reversedHeaderInfo != null) val callHeaderInfo = callHandlers.firstNotNullResult { it.handle(iterable, iteratorCall, null, scopeOwnerSymbol()) }
return reversedHeaderInfo if (callHeaderInfo != null)
return callHeaderInfo
// Try to match a call to build a progression (e.g., `.indices`, `downTo`). // Try to match a call to build a progression (e.g., `.indices`, `downTo`).
val progressionType = ProgressionType.fromIrType(iterable.type, symbols) 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.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.IrCall 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.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.expressions.impl.IrDoWhileLoopImpl 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). */ /** Statements used to initialize the entire loop (e.g., declare induction variable). */
val loopInitStatements: List<IrStatement> 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). */ /** Statements used to initialize an iteration of the loop (e.g., assign loop variable). */
fun initializeIteration( fun initializeIteration(
loopVariable: IrVariable, loopVariable: IrVariable?,
loopVariableComponents: Map<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>, symbols: Symbols<CommonBackendContext>,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder
): List<IrStatement> ): List<IrStatement>
@@ -52,15 +60,17 @@ internal interface ForLoopHeader {
fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement
} }
internal abstract class NumericForLoopHeader( internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
headerInfo: NumericHeaderInfo, protected val headerInfo: T,
builder: DeclarationIrBuilder, builder: DeclarationIrBuilder,
protected val isLastInclusive: Boolean protected val isLastInclusive: Boolean
) : ForLoopHeader { ) : ForLoopHeader {
protected val inductionVariable: IrVariable override val consumesLoopVariableComponents = false
val inductionVariable: IrVariable
val stepVariable: IrVariable
protected val lastVariableIfCanCacheLast: IrVariable? protected val lastVariableIfCanCacheLast: IrVariable?
protected val stepVariable: IrVariable
protected val lastExpression: IrExpression protected val lastExpression: IrExpression
// Always copy `lastExpression` is it may be used in multiple conditions. // Always copy `lastExpression` is it may be used in multiple conditions.
get() = field.deepCopyWithSymbols() 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) = private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) =
if (expression.type is IrSimpleType && expression.type.isNullable()) { if (expression.type is IrSimpleType && expression.type.isNullable()) {
irImplicitCast(expression, expression.type.makeNotNull()) irImplicitCast(expression, expression.type.makeNotNull())
@@ -204,9 +209,9 @@ internal abstract class NumericForLoopHeader(
} }
internal class ProgressionLoopHeader( internal class ProgressionLoopHeader(
override val headerInfo: ProgressionHeaderInfo, headerInfo: ProgressionHeaderInfo,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder
) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = true) { ) : NumericForLoopHeader<ProgressionHeaderInfo>(headerInfo, builder, isLastInclusive = true) {
// For this loop: // For this loop:
// //
@@ -227,13 +232,28 @@ internal class ProgressionLoopHeader(
private var loopVariable: IrVariable? = null 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) { 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 // loopVariable = inductionVariable
// inductionVariable = inductionVariable + step // inductionVariable = inductionVariable + step
loopVariable.initializer = irGet(inductionVariable) listOfNotNull(loopVariable, incrementInductionVariable(this))
listOf(loopVariable, incrementInductionVariable(this))
} }
override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?) = override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?) =
@@ -259,9 +279,9 @@ internal class ProgressionLoopHeader(
// //
// if (inductionVar <= last) { // if (inductionVar <= last) {
// do { // do {
// val loopVar = inductionVar // val loopVar = inductionVar
// inductionVar += step // inductionVar += step
// // Loop body // // Loop body
// } while (inductionVar <= last) // } while (inductionVar <= last)
// } // }
// //
@@ -283,21 +303,29 @@ internal class ProgressionLoopHeader(
} }
internal class IndexedGetLoopHeader( internal class IndexedGetLoopHeader(
override val headerInfo: IndexedGetHeaderInfo, headerInfo: IndexedGetHeaderInfo,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder
) : NumericForLoopHeader(headerInfo, builder, isLastInclusive = false) { ) : NumericForLoopHeader<IndexedGetHeaderInfo>(headerInfo, builder, isLastInclusive = false) {
override val loopInitStatements = listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable) 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) { with(builder) {
// loopVariable = objectVariable[inductionVariable] // loopVariable = objectVariable[inductionVariable]
val indexedGetFun = with(headerInfo.expressionHandler) { headerInfo.objectVariable.type.getFunction } val indexedGetFun = with(headerInfo.expressionHandler) { headerInfo.objectVariable.type.getFunction }
loopVariable.initializer = irCall(indexedGetFun).apply { val get = irCall(indexedGetFun).apply {
dispatchReceiver = irGet(headerInfo.objectVariable) dispatchReceiver = irGet(headerInfo.objectVariable)
putValueArgument(0, irGet(inductionVariable)) 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) { 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 * Given the for-loop iterator variable, extract information about the iterable subject
* and create a [ForLoopHeader] from it. * and create a [ForLoopHeader] from it.
@@ -338,7 +501,7 @@ internal class HeaderProcessor(
* *
* Returns null if the for-loop cannot be lowered. * 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<*>. // Verify the variable type is a subtype of Iterator<*>.
assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR) assert(variable.origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR)
if (!variable.type.isSubtypeOfClass(symbols.iterator)) { if (!variable.type.isSubtypeOfClass(symbols.iterator)) {
@@ -365,6 +528,7 @@ internal class HeaderProcessor(
return when (headerInfo) { return when (headerInfo) {
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(headerInfo, builder) is IndexedGetHeaderInfo -> IndexedGetLoopHeader(headerInfo, builder)
is ProgressionHeaderInfo -> ProgressionLoopHeader(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) { internal class StringIterationHandler(context: CommonBackendContext) : CharSequenceIterationHandler(context, canCacheLast = true) {
override fun matchIterable(expression: IrExpression) = expression.type.isString() 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") val arr = arrayOf("a", "b", "c", "d")
fun box(): String { fun box(): String {
@@ -19,3 +18,6 @@ fun box(): String {
// 0 component1 // 0 component1
// 0 component2 // 0 component2
// 1 ARRAYLENGTH // 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") val arr = arrayOf("a", "b", "c", "d")
fun box(): String { fun box(): String {
@@ -19,3 +18,6 @@ fun box(): String {
// 0 component1 // 0 component1
// 0 component2 // 0 component2
// 1 ARRAYLENGTH // 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() val arr = intArrayOf()
fun box(): String { fun box(): String {
@@ -16,3 +15,6 @@ fun box(): String {
// 0 component1 // 0 component1
// 0 component2 // 0 component2
// 1 ARRAYLENGTH // 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) val arr = intArrayOf(10, 20, 30, 40)
fun box(): String { fun box(): String {
@@ -17,3 +16,6 @@ fun box(): String {
// 0 component1 // 0 component1
// 0 component2 // 0 component2
// 1 ARRAYLENGTH // 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") val arr = arrayOf("a", "b", "c", "d")
fun box(): String { fun box(): String {
@@ -19,3 +18,6 @@ fun box(): String {
// 0 component1 // 0 component1
// 0 component2 // 0 component2
// 1 ARRAYLENGTH // 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" val cs: CharSequence = "abcd"
fun box(): String { fun box(): String {
@@ -20,3 +19,6 @@ fun box(): String {
// 0 component2 // 0 component2
// 1 length // 1 length
// 1 charAt // 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 { fun box(): String {
for ((index, x) in "".withIndex()) { for ((index, x) in "".withIndex()) {
return "Loop over empty String should not be executed" return "Loop over empty String should not be executed"
@@ -14,3 +13,6 @@ fun box(): String {
// 0 component2 // 0 component2
// 1 length // 1 length
// 1 charAt // 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 { fun box(): String {
val s = StringBuilder() val s = StringBuilder()
@@ -18,3 +17,6 @@ fun box(): String {
// 0 component2 // 0 component2
// 1 length // 1 length
// 1 charAt // 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" val xs = "abcd"
fun box(): String { fun box(): String {
@@ -20,3 +19,6 @@ fun box(): String {
// 0 component2 // 0 component2
// 1 length // 1 length
// 1 charAt // 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" val xs = "abcd"
fun box(): String { fun box(): String {
@@ -20,3 +19,6 @@ fun box(): String {
// 0 component2 // 0 component2
// 1 length // 1 length
// 1 charAt // 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" val xs = "abcd"
fun useAny(x: Any) {} fun useAny(x: Any) {}
@@ -23,3 +22,6 @@ fun box(): String {
// 0 component2 // 0 component2
// 1 length // 1 length
// 1 charAt // 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"); 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") @TestMetadata("forInArrayWithIndexNoIndexVar.kt")
public void testForInArrayWithIndexNoIndexVar() throws Exception { public void testForInArrayWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); 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") @TestMetadata("forInStringWithIndexNoIndexVar.kt")
public void testForInStringWithIndexNoIndexVar() throws Exception { public void testForInStringWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); 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") @TestMetadata("forInArrayWithIndexNoIndexVar.kt")
public void testForInArrayWithIndexNoIndexVar() throws Exception { public void testForInArrayWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); 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") @TestMetadata("forInStringWithIndexNoIndexVar.kt")
public void testForInStringWithIndexNoIndexVar() throws Exception { public void testForInStringWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexContinuesAsUnmodified.kt");
} }
@TestMetadata("forInArrrayWithIndexNoElementVar.kt") @TestMetadata("forInArrayWithIndexNoElementVar.kt")
public void testForInArrrayWithIndexNoElementVar() throws Exception { public void testForInArrayWithIndexNoElementVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrrayWithIndexNoElementVar.kt"); runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoElementVar.kt");
} }
@TestMetadata("forInArrrayWithIndexNoIndexVar.kt") @TestMetadata("forInArrayWithIndexNoIndexOrElementVar.kt")
public void testForInArrrayWithIndexNoIndexVar() throws Exception { public void testForInArrayWithIndexNoIndexOrElementVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrrayWithIndexNoIndexVar.kt"); runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexOrElementVar.kt");
} }
@TestMetadata("forInArrrayWithIndexWithExplicitlyTypedIndexVariable.kt") @TestMetadata("forInArrayWithIndexNoIndexVar.kt")
public void testForInArrrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInArrayWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrrayWithIndexWithExplicitlyTypedIndexVariable.kt"); 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") @TestMetadata("forInByteArrayWithIndexWithSmartCast.kt")
@@ -5286,14 +5301,9 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInByteArrayWithIndexWithSmartCast.kt"); runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInByteArrayWithIndexWithSmartCast.kt");
} }
@TestMetadata("forInByteArrrayWithIndex.kt") @TestMetadata("forInEmptyArrayWithIndex.kt")
public void testForInByteArrrayWithIndex() throws Exception { public void testForInEmptyArrayWithIndex() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInByteArrrayWithIndex.kt"); runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInEmptyArrayWithIndex.kt");
}
@TestMetadata("forInEmptyArrrayWithIndex.kt")
public void testForInEmptyArrrayWithIndex() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInEmptyArrrayWithIndex.kt");
} }
@TestMetadata("forInGenericArrayOfIntsWithIndex.kt") @TestMetadata("forInGenericArrayOfIntsWithIndex.kt")
@@ -5311,30 +5321,30 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInGenericArrayWithIndex.kt"); 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") @TestMetadata("forInIntArrayWithIndexWithSmartCast.kt")
public void testForInIntArrayWithIndexWithSmartCast() throws Exception { public void testForInIntArrayWithIndexWithSmartCast() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInIntArrayWithIndexWithSmartCast.kt"); runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInIntArrayWithIndexWithSmartCast.kt");
} }
@TestMetadata("forInIntArrrayWithIndex.kt") @TestMetadata("forInObjectArrayWithIndex.kt")
public void testForInIntArrrayWithIndex() throws Exception { public void testForInObjectArrayWithIndex() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInIntArrrayWithIndex.kt"); runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInObjectArrayWithIndex.kt");
} }
@TestMetadata("forInObjectArrrayWithIndex.kt") @TestMetadata("forInShortArrayWithIndex.kt")
public void testForInObjectArrrayWithIndex() throws Exception { public void testForInShortArrayWithIndex() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInObjectArrrayWithIndex.kt"); runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInShortArrayWithIndex.kt");
} }
@TestMetadata("forInShortArrayWithIndexWithSmartCast.kt") @TestMetadata("forInShortArrayWithIndexWithSmartCast.kt")
public void testForInShortArrayWithIndexWithSmartCast() throws Exception { public void testForInShortArrayWithIndexWithSmartCast() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInShortArrayWithIndexWithSmartCast.kt"); 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") @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"); 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") @TestMetadata("forInStringWithIndexNoIndexVar.kt")
public void testForInStringWithIndexNoIndexVar() throws Exception { public void testForInStringWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); 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") @TestMetadata("forInArrayWithIndexNoIndexVar.kt")
public void testForInArrayWithIndexNoIndexVar() throws Exception { public void testForInArrayWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); 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") @TestMetadata("forInStringWithIndexNoIndexVar.kt")
public void testForInStringWithIndexNoIndexVar() throws Exception { public void testForInStringWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); 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") @TestMetadata("forInArrayWithIndexNoIndexVar.kt")
public void testForInArrayWithIndexNoIndexVar() throws Exception { public void testForInArrayWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); 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") @TestMetadata("forInStringWithIndexNoIndexVar.kt")
public void testForInStringWithIndexNoIndexVar() throws Exception { public void testForInStringWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); 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") @TestMetadata("forInArrayWithIndexNoIndexVar.kt")
public void testForInArrayWithIndexNoIndexVar() throws Exception { public void testForInArrayWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInArrayWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexWithExplicitlyTypedIndexVariable.kt"); 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"); 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") @TestMetadata("forInStringWithIndexNoIndexVar.kt")
public void testForInStringWithIndexNoIndexVar() throws Exception { public void testForInStringWithIndexNoIndexVar() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNoIndexVar.kt"); 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") @TestMetadata("forInStringWithIndexWithExplicitlyTypedIndexVariable.kt")
public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception { public void testForInStringWithIndexWithExplicitlyTypedIndexVariable() throws Exception {
runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt"); runTest("compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexWithExplicitlyTypedIndexVariable.kt");