Handle withIndex() on arrays and CharSequences in ForLoopsLowering.
This commit is contained in:
committed by
max-kammerer
parent
735535dd5a
commit
a54d9482dd
+152
-73
@@ -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)
|
||||
}
|
||||
}
|
||||
+18
-5
@@ -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)
|
||||
|
||||
+189
-25
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -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'"
|
||||
}
|
||||
+17
@@ -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'"
|
||||
}
|
||||
+15
@@ -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'"
|
||||
}
|
||||
+15
@@ -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'"
|
||||
}
|
||||
+3
-1
@@ -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
|
||||
|
||||
Vendored
+3
-1
@@ -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
|
||||
|
||||
Vendored
+3
-1
@@ -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
|
||||
|
||||
Vendored
+3
-1
@@ -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
|
||||
|
||||
Vendored
+3
-1
@@ -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
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
Vendored
+3
-1
@@ -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
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+20
@@ -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");
|
||||
|
||||
+20
@@ -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");
|
||||
|
||||
+48
-28
@@ -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");
|
||||
|
||||
+20
@@ -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");
|
||||
|
||||
Generated
+20
@@ -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");
|
||||
|
||||
+20
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user