Handle reversed() in ForLoopsLowering.

We get the info for the underlying progression and invert it. For
progressions whose last bound was open (e.g., `until` loop), the
reversed version will have an open first bound and so the induction
variable must be incremented first.

Also unified the way of extracting HeaderInfo out of changed calls
(e.g., `indices.reversed()`), and fixed declaration parents in
ForLoopsLowering.
This commit is contained in:
Mark Punzalan
2019-04-10 14:33:51 -07:00
committed by max-kammerer
parent 3d1b6fb83c
commit 1b703448d3
34 changed files with 953 additions and 201 deletions
@@ -9,21 +9,13 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext 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.lower.irIfThen
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.builders.irCallOp
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irSetVar
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.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
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
@@ -99,11 +91,9 @@ val forLoopsPhase = makeIrFilePhase(
*/ */
internal class ForLoopsLowering(val context: CommonBackendContext) : FileLoweringPass { internal class ForLoopsLowering(val context: CommonBackendContext) : FileLoweringPass {
private val headerInfoBuilder = HeaderInfoBuilder(context)
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>() val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
val transformer = RangeLoopTransformer(context, oldLoopToNewLoop, headerInfoBuilder) val transformer = RangeLoopTransformer(context, oldLoopToNewLoop)
irFile.transformChildrenVoid(transformer) irFile.transformChildrenVoid(transformer)
// Update references in break/continue. // Update references in break/continue.
@@ -118,12 +108,12 @@ internal class ForLoopsLowering(val context: CommonBackendContext) : FileLowerin
private class RangeLoopTransformer( private class RangeLoopTransformer(
val context: CommonBackendContext, val context: CommonBackendContext,
val oldLoopToNewLoop: MutableMap<IrLoop, IrLoop>, val oldLoopToNewLoop: MutableMap<IrLoop, IrLoop>
headerInfoBuilder: HeaderInfoBuilder
) : IrElementTransformerVoidWithContext() { ) : IrElementTransformerVoidWithContext() {
private val symbols = context.ir.symbols private val symbols = context.ir.symbols
private val iteratorToLoopHeader = mutableMapOf<IrVariableSymbol, ForLoopHeader>() private val iteratorToLoopHeader = mutableMapOf<IrVariableSymbol, ForLoopHeader>()
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
@@ -185,13 +175,12 @@ private class RangeLoopTransformer(
val loopHeader = getLoopHeader(loop.condition) val loopHeader = getLoopHeader(loop.condition)
?: return super.visitWhileLoop(loop) // If the for-loop cannot be lowered. ?: return super.visitWhileLoop(loop) // If the for-loop cannot be lowered.
val newLoop = loopHeader.buildInnerLoop(this, loop, newBody) val (newLoop, replacementExpression) = loopHeader.buildLoop(this, loop, newBody)
// Update mapping from old to new loop so we can later update references in break/continue. // Update mapping from old to new loop so we can later update references in break/continue.
oldLoopToNewLoop[loop] = newLoop oldLoopToNewLoop[loop] = newLoop
// Surround the new loop with a check for an empty loop, if necessary. return replacementExpression
return loopHeader.buildNotEmptyConditionIfNecessary(this@with)?.let { irIfThen(it, newLoop) } ?: newLoop
} }
} }
@@ -232,18 +221,7 @@ private class RangeLoopTransformer(
// inductionVariable = inductionVariable + step // inductionVariable = inductionVariable + step
return with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) { return with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) {
variable.initializer = forLoopInfo.initializeLoopVariable(symbols, this) variable.initializer = forLoopInfo.initializeLoopVariable(symbols, this)
val plusFun = forLoopInfo.inductionVariable.type.getClass()!!.functions.first { val increment = forLoopInfo.buildIncrementInductionVariableExpression(this)
it.name.asString() == "plus" &&
it.valueParameters.size == 1 &&
it.valueParameters[0].type.toKotlinType() == forLoopInfo.step.type.toKotlinType()
}
val increment = irSetVar(
forLoopInfo.inductionVariable.symbol, irCallOp(
plusFun.symbol, plusFun.returnType,
irGet(forLoopInfo.inductionVariable),
irGet(forLoopInfo.step)
)
)
IrCompositeImpl( IrCompositeImpl(
variable.startOffset, variable.startOffset,
variable.endOffset, variable.endOffset,
@@ -13,15 +13,20 @@ import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
// TODO: Handle withIndex() // TODO: Handle withIndex()
// TODO: Handle reversed()
// TODO: Handle Strings/CharSequences // TODO: Handle Strings/CharSequences
// TODO: Handle UIntProgression, ULongProgression // TODO: Handle UIntProgression, ULongProgression
@@ -62,7 +67,9 @@ internal sealed class HeaderInfo(
val first: IrExpression, val first: IrExpression,
val last: IrExpression, val last: IrExpression,
val step: IrExpression, val step: IrExpression,
val isLastInclusive: Boolean val isFirstInclusive: Boolean,
val isLastInclusive: Boolean,
val isReversed: Boolean
) { ) {
val direction: ProgressionDirection by lazy { val direction: ProgressionDirection by lazy {
// If step is a constant (either Int or Long), then we can determine the direction. // If step is a constant (either Int or Long), then we can determine the direction.
@@ -75,6 +82,13 @@ internal sealed class HeaderInfo(
else -> ProgressionDirection.UNKNOWN else -> ProgressionDirection.UNKNOWN
} }
} }
/**
* Returns a copy of this [HeaderInfo] with the values reversed.
* I.e., first and last (and their inclusiveness) are swapped, step is negated.
* Returns null if the iterable cannot be iterated in reverse.
*/
abstract fun asReversed(): HeaderInfo?
} }
/** Information about a for-loop over a progression. */ /** Information about a for-loop over a progression. */
@@ -83,14 +97,15 @@ internal class ProgressionHeaderInfo(
first: IrExpression, first: IrExpression,
last: IrExpression, last: IrExpression,
step: IrExpression, step: IrExpression,
isFirstInclusive: Boolean = true,
isLastInclusive: Boolean = true, isLastInclusive: Boolean = true,
canOverflow: Boolean? = null, isReversed: Boolean = false,
val additionalVariables: List<IrVariable> = listOf() val additionalVariables: List<IrVariable> = listOf()
) : HeaderInfo(progressionType, first, last, step, isLastInclusive) { ) : HeaderInfo(progressionType, first, last, step, isFirstInclusive, isLastInclusive, isReversed) {
private val _canOverflow: Boolean? = canOverflow
val canOverflow: Boolean by lazy { val canOverflow: Boolean by lazy {
if (_canOverflow != null) return@lazy _canOverflow // Last-exclusive progressions can never overflow.
if (!isLastInclusive) return@lazy false
// Induction variable can overflow if it is not a const, or is MAX/MIN_VALUE (depending on direction). // Induction variable can overflow if it is not a const, or is MAX/MIN_VALUE (depending on direction).
val lastValue = (last as? IrConst<*>)?.value val lastValue = (last as? IrConst<*>)?.value
@@ -118,6 +133,17 @@ internal class ProgressionHeaderInfo(
} }
constLimitAsLong == lastValueAsLong constLimitAsLong == lastValueAsLong
} }
override fun asReversed() = ProgressionHeaderInfo(
progressionType = progressionType,
first = last,
last = first,
step = step.negate(),
isFirstInclusive = isLastInclusive,
isLastInclusive = isFirstInclusive,
isReversed = !isReversed,
additionalVariables = additionalVariables
)
} }
/** Information about a for-loop over an array. The internal induction variable used is an Int. */ /** Information about a for-loop over an array. The internal induction variable used is an Int. */
@@ -131,28 +157,72 @@ internal class ArrayHeaderInfo(
first, first,
last, last,
step, step,
isLastInclusive = false isFirstInclusive = true,
) isLastInclusive = false,
isReversed = false
) {
// Technically one can easily iterate over an array in reverse by swapping first/last and
// negating the step. However, Array.reversed() and Array.reversedArray() return a collection
// with a copy of the array elements, which means that the original array can be modified with
// no effect on the iteration over the reversed array. That is:
//
// val arr = intArrayOf(1, 2, 3, 4)
// for (i in arr.reversed()) {
// arr[0] = 0 // Does not affect iteration over reversed array
// print(i) // Should print "4321"
// }
//
// If we simply iterated over `arr` in reverse, then we would get "4320" which is not the right
// output. Hence we return null to indicate that we cannot loop over arrays in reverse.
override fun asReversed(): HeaderInfo? = null
}
/** Matches a call to `iterator()` and builds a [HeaderInfo] out of the call's context. */ /** Return the negated value if the expression is const, otherwise call unaryMinus(). */
internal interface HeaderInfoHandler<T> { private fun IrExpression.negate(): IrExpression {
val matcher: IrCallMatcher val stepValue = (this as? IrConst<*>)?.value as? Number
return when (stepValue) {
is Int -> IrConstImpl(startOffset, endOffset, type, IrConstKind.Int, -stepValue)
is Long -> IrConstImpl(startOffset, endOffset, type, IrConstKind.Long, -stepValue)
else -> {
val unaryMinusFun = type.getClass()!!.functions.first { it.name.asString() == "unaryMinus" }
IrCallImpl(startOffset, endOffset, type, unaryMinusFun.symbol, unaryMinusFun.descriptor).apply {
dispatchReceiver = this@negate
}
}
}
}
fun build(call: IrCall, data: T): HeaderInfo? /** 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 expression. */
fun match(expression: E): Boolean
fun handle(irCall: IrCall, data: T) = if (matcher(irCall)) { /** Builds a [HeaderInfo] from the expression. */
build(irCall, data) fun build(expression: E, data: D, scopeOwner: IrSymbol): HeaderInfo?
fun handle(expression: E, data: D, scopeOwner: IrSymbol) = if (match(expression)) {
build(expression, data, scopeOwner)
} else { } else {
null null
} }
} }
internal typealias ProgressionHandler = HeaderInfoHandler<ProgressionType>
/** internal interface ExpressionHandler : HeaderInfoHandler<IrExpression, Nothing?> {
* Handles a call to `iterator()` on more specialized forms of progressions, built using extension fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo?
* and member functions/properties in the stdlib (e.g., `.indices`, `downTo`). override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol) = build(expression, scopeOwner)
*/ }
private class ProgressionHeaderInfoBuilder(val context: CommonBackendContext) : IrElementVisitor<HeaderInfo?, Nothing?> {
/** Matches a call to build an iterable and builds a [HeaderInfo] from the call's context. */
internal interface HeaderInfoFromCallHandler<D> : HeaderInfoHandler<IrCall, D> {
val matcher: IrCallMatcher
override fun match(expression: IrCall) = matcher(expression)
}
internal typealias ProgressionHandler = HeaderInfoFromCallHandler<ProgressionType>
internal class HeaderInfoBuilder(context: CommonBackendContext, private val scopeOwnerSymbol: () -> IrSymbol) :
IrElementVisitor<HeaderInfo?, Nothing?> {
private val symbols = context.ir.symbols private val symbols = context.ir.symbols
@@ -166,31 +236,33 @@ private class ProgressionHeaderInfoBuilder(val context: CommonBackendContext) :
RangeToHandler(context, progressionElementTypes) RangeToHandler(context, progressionElementTypes)
) )
private val reversedHandler = ReversedHandler(context, this)
private val expressionHandlers = listOf(
ArrayIterationHandler(context),
DefaultProgressionHandler(context)
)
override fun visitElement(element: IrElement, data: Nothing?): HeaderInfo? = null override fun visitElement(element: IrElement, data: Nothing?): HeaderInfo? = null
/** Builds a [HeaderInfo] for iterable expressions that are calls (e.g., `.reversed()`, `.indices`. */
override fun visitCall(expression: IrCall, data: Nothing?): HeaderInfo? { override fun visitCall(expression: IrCall, data: Nothing?): HeaderInfo? {
// Return the HeaderInfo from the first successful match. // Return the HeaderInfo from the first successful match. First, try to match a `reversed()` call.
val reversedHeaderInfo = reversedHandler.handle(expression, null, scopeOwnerSymbol())
if (reversedHeaderInfo != null)
return reversedHeaderInfo
// Try to match a call to build a progression (e.g., `.indices`, `downTo`).
val progressionType = ProgressionType.fromIrType(expression.type, symbols) val progressionType = ProgressionType.fromIrType(expression.type, symbols)
?: return null val progressionHeaderInfo =
return progressionHandlers.firstNotNullResult { it.handle(expression, progressionType) } progressionType?.run { progressionHandlers.firstNotNullResult { it.handle(expression, this, scopeOwnerSymbol()) } }
}
} return progressionHeaderInfo ?: super.visitCall(expression, data)
}
internal class HeaderInfoBuilder(context: CommonBackendContext) {
private val progressionHeaderInfoBuilder = ProgressionHeaderInfoBuilder(context) /** Builds a [HeaderInfo] for iterable expressions not handled in [visitCall]. */
private val arrayIterationHandler = ArrayIterationHandler(context) override fun visitExpression(expression: IrExpression, data: Nothing?): HeaderInfo? {
private val defaultProgressionHandler = DefaultProgressionHandler(context) return expressionHandlers.firstNotNullResult { it.handle(expression, null, scopeOwnerSymbol()) }
?: super.visitExpression(expression, data)
fun build(variable: IrVariable): HeaderInfo? {
// TODO: Merge DefaultProgressionHandler into ProgressionHeaderInfoBuilder. Not
// straightforward because ProgressionHeaderInfoBuilder works only on calls and not just
// any progression expression (i.e., progression may not be a call result).
// DefaultProgressionHandler must come AFTER ProgressionHeaderInfoBuilder, which handles
// more specialized forms of progressions.
val initializer = variable.initializer as IrCall
return arrayIterationHandler.handle(initializer, null)
?: initializer.dispatchReceiver?.accept(progressionHeaderInfoBuilder, null)
?: defaultProgressionHandler.handle(initializer, null)
} }
} }
@@ -9,10 +9,13 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irComposite
import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.* 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.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.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
@@ -24,6 +27,18 @@ import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.isNullable import org.jetbrains.kotlin.ir.util.isNullable
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
/**
* Contains the loop and expression to replace the old loop.
*
* @param newLoop The new loop.
* @param replacementExpression The expression to use in place of the old loop. It is either `newLoop`, or a container
* that contains `newLoop`.
*/
internal data class LoopReplacement(
val newLoop: IrLoop,
val replacementExpression: IrExpression
)
/** Contains information about variables used in the loop. */ /** Contains information about variables used in the loop. */
internal sealed class ForLoopHeader( internal sealed class ForLoopHeader(
protected open val headerInfo: HeaderInfo, protected open val headerInfo: HeaderInfo,
@@ -39,15 +54,7 @@ internal sealed class ForLoopHeader(
abstract val declarations: List<IrStatement> abstract val declarations: List<IrStatement>
/** Builds a new loop from the old loop. */ /** Builds a new loop from the old loop. */
abstract fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop abstract fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement
/**
* A condition used in a check surrounding the loop checking for an empty loop. The expression
* should evaluate to true if the loop is NOT empty.
*
* Returns null if no check is needed for the for-loop.
*/
abstract fun buildNotEmptyConditionIfNecessary(builder: DeclarationIrBuilder): IrExpression?
protected fun buildLoopCondition(builder: DeclarationIrBuilder): IrExpression = protected fun buildLoopCondition(builder: DeclarationIrBuilder): IrExpression =
with(builder) { with(builder) {
@@ -121,58 +128,67 @@ internal class ProgressionLoopHeader(
// //
// ...the functions may have side-effects so we need to call them in the following order: first() (inductionVariable), last(), step(). // ...the functions may have side-effects so we need to call them in the following order: first() (inductionVariable), last(), step().
// Additional variables come first as they may be needed to the subsequent variables. // Additional variables come first as they may be needed to the subsequent variables.
//
// In the case of a reversed range, the `inductionVariable` and `last` variables are swapped, therefore the declaration order must be
// swapped to preserve the correct evaluation order.
override val declarations: List<IrStatement> override val declarations: List<IrStatement>
get() = headerInfo.additionalVariables + listOf(inductionVariable, last, step) get() = headerInfo.additionalVariables +
(if (headerInfo.isReversed) listOf(last, inductionVariable) else listOf(inductionVariable, last)) +
step
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) { override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement {
if (headerInfo.canOverflow) { with(builder) {
// If the induction variable CAN overflow, we cannot use it in the loop condition. Loop is lowered into something like: var (newLoop, replacementExpression) = if (headerInfo.canOverflow) {
// // If the induction variable CAN overflow, we cannot use it in the loop condition. Loop is lowered into something like:
// var inductionVar = A //
// var last = B // if (inductionVar <= last) {
// if (inductionVar <= last) { // // Loop is not empty
// // Loop is not empty // do {
// do { // val loopVar = inductionVar
// val loopVar = inductionVar // inductionVar += step
// inductionVar++ // // Loop body
// // Loop body // } while (loopVar != last)
// } while (loopVar != last) // }
// } assert(loopVariable != null)
// val booleanNotFun = context.irBuiltIns.booleanClass.functions.first { it.owner.name.asString() == "not" }
// The `if (inductionVar <= last)` "not empty" check is added in buildNotEmptyConditionIfNecessary(). val newCondition = irCallOp(booleanNotFun, booleanNotFun.owner.returnType, irCall(context.irBuiltIns.eqeqSymbol).apply {
assert(loopVariable != null) putValueArgument(0, irGet(loopVariable!!))
val booleanNotFun = context.irBuiltIns.booleanClass.functions.first { it.owner.name.asString() == "not" } putValueArgument(1, irGet(last))
val newCondition = irCallOp(booleanNotFun, booleanNotFun.owner.returnType, irCall(context.irBuiltIns.eqeqSymbol).apply { })
putValueArgument(0, irGet(loopVariable!!)) val newLoop = IrDoWhileLoopImpl(oldLoop.startOffset, oldLoop.endOffset, oldLoop.type, oldLoop.origin).apply {
putValueArgument(1, irGet(last)) label = oldLoop.label
}) condition = newCondition
IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply { body = newBody
label = loop.label }
condition = newCondition val notEmptyCheck = irIfThen(buildLoopCondition(builder), newLoop)
body = newBody LoopReplacement(newLoop, notEmptyCheck)
} else {
// If the induction variable can NOT overflow, use a simple while loop. Loop is lowered into something like:
//
// while (inductionVar <= last) {
// val loopVar = inductionVar
// inductionVar += step
// // Loop body
// }
val newLoop = IrWhileLoopImpl(oldLoop.startOffset, oldLoop.endOffset, oldLoop.type, oldLoop.origin).apply {
label = oldLoop.label
condition = buildLoopCondition(this@with)
body = newBody
}
LoopReplacement(newLoop, newLoop)
} }
} else {
// If the induction variable can NOT overflow, use a simple while loop. Loop is lowered into something like: if (!headerInfo.isFirstInclusive) {
// // Pre-increment the induction variable.
// var inductionVar = A replacementExpression = irComposite(replacementExpression) {
// var last = B +buildIncrementInductionVariableExpression(this@with)
// while (inductionVar <= last) { +replacementExpression
// val loopVar = inductionVar }
// inductionVar++
// // Loop body
// }
IrWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
label = loop.label
condition = buildLoopCondition(this@with)
body = newBody
} }
return LoopReplacement(newLoop, replacementExpression)
} }
} }
// If the induction variable can NOT overflow, we do NOT need the enclosing "not empty" check because the for-loop is lowered into a
// simple while loop (see buildInnerLoop()); the enclosing check would be redundant.
override fun buildNotEmptyConditionIfNecessary(builder: DeclarationIrBuilder): IrExpression? =
if (headerInfo.canOverflow) buildLoopCondition(builder) else null
} }
internal class ArrayLoopHeader( internal class ArrayLoopHeader(
@@ -194,25 +210,30 @@ internal class ArrayLoopHeader(
override val declarations: List<IrStatement> override val declarations: List<IrStatement>
get() = listOf(headerInfo.arrayVariable, inductionVariable, last, step) get() = listOf(headerInfo.arrayVariable, inductionVariable, last, step)
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) { override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement = with(builder) {
// Loop is lowered into something like: // Loop is lowered into something like:
// //
// var inductionVar = 0 // var inductionVar = 0
// var last = array.size // var last = array.size
// while (inductionVar < last) { // while (inductionVar < last) {
// val loopVar = array[inductionVar++] // val loopVar = array[inductionVar]
// inductionVar++ // inductionVar++
// // Loop body // // Loop body
// } // }
IrWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply { val newLoop = IrWhileLoopImpl(oldLoop.startOffset, oldLoop.endOffset, oldLoop.type, oldLoop.origin).apply {
label = loop.label label = oldLoop.label
condition = buildLoopCondition(this@with) condition = buildLoopCondition(this@with)
body = newBody body = newBody
} }
val replacementExpression = if (!headerInfo.isFirstInclusive) {
// Pre-increment the induction variable.
irComposite(newLoop) {
+buildIncrementInductionVariableExpression(this@with)
+newLoop
}
} else newLoop
LoopReplacement(newLoop, replacementExpression)
} }
// No surrounding emptiness check is needed with a while loop.
override fun buildNotEmptyConditionIfNecessary(builder: DeclarationIrBuilder): IrExpression? = null
} }
/** /**
@@ -241,8 +262,11 @@ internal class HeaderProcessor(
return null return null
} }
// Collect loop information. // Get the iterable expression, e.g., `someIterable` in the following loop variable declaration:
val headerInfo = headerInfoBuilder.build(variable) // val it = someIterable.iterator()
val iterable = (variable.initializer as? IrCall)?.dispatchReceiver
// Collect loop information from the iterable expression.
val headerInfo = iterable?.accept(headerInfoBuilder, null)
?: return null // If the iterable is not supported. ?: return null // If the iterable is not supported.
val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset) val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset)
@@ -310,21 +334,37 @@ internal class HeaderProcessor(
} }
} }
} }
}
internal 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())
} else { } else {
expression expression
} }
internal fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name): IrExpression { private fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name): IrExpression {
return if (type.toKotlinType() == targetType.toKotlinType()) { return if (type.toKotlinType() == targetType.toKotlinType()) {
this this
} else { } else {
val function = type.getClass()!!.functions.first { it.name == numberCastFunctionName } val function = type.getClass()!!.functions.first { it.name == numberCastFunctionName }
IrCallImpl(startOffset, endOffset, function.returnType, function.symbol) IrCallImpl(startOffset, endOffset, function.returnType, function.symbol)
.apply { dispatchReceiver = this@castIfNecessary } .apply { dispatchReceiver = this@castIfNecessary }
}
} }
} }
internal fun ForLoopHeader.buildIncrementInductionVariableExpression(builder: DeclarationIrBuilder): IrExpression = with(builder) {
// inductionVariable = inductionVariable + step
val plusFun = inductionVariable.type.getClass()!!.functions.first {
it.name.asString() == "plus" &&
it.valueParameters.size == 1 &&
it.valueParameters[0].type.toKotlinType() == step.type.toKotlinType()
}
irSetVar(
inductionVariable.symbol, irCallOp(
plusFun.symbol, plusFun.returnType,
irGet(inductionVariable),
irGet(step)
)
)
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.common.lower.loops
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.matchers.Quantifier
import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher import org.jetbrains.kotlin.backend.common.lower.matchers.SimpleCalleeMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher import org.jetbrains.kotlin.backend.common.lower.matchers.createIrCallMatcher
import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension import org.jetbrains.kotlin.backend.common.lower.matchers.singleArgumentExtension
@@ -16,10 +17,12 @@ import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irInt import org.jetbrains.kotlin.ir.builders.irInt
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.properties import org.jetbrains.kotlin.ir.util.properties
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.SimpleType
@@ -27,6 +30,7 @@ import org.jetbrains.kotlin.types.SimpleType
/** Builds a [HeaderInfo] for progressions built using the `rangeTo` function. */ /** Builds a [HeaderInfo] for progressions built using the `rangeTo` function. */
internal class RangeToHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<SimpleType>) : internal class RangeToHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<SimpleType>) :
ProgressionHandler { ProgressionHandler {
override val matcher = SimpleCalleeMatcher { override val matcher = SimpleCalleeMatcher {
dispatchReceiver { it != null && it.type.toKotlinType() in progressionElementTypes } dispatchReceiver { it != null && it.type.toKotlinType() in progressionElementTypes }
fqName { it.pathSegments().last() == Name.identifier("rangeTo") } fqName { it.pathSegments().last() == Name.identifier("rangeTo") }
@@ -34,12 +38,12 @@ internal class RangeToHandler(private val context: CommonBackendContext, private
parameter(0) { it.type.toKotlinType() in progressionElementTypes } parameter(0) { it.type.toKotlinType() in progressionElementTypes }
} }
override fun build(call: IrCall, data: ProgressionType) = override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol) =
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
ProgressionHeaderInfo( ProgressionHeaderInfo(
data, data,
first = call.dispatchReceiver!!, first = expression.dispatchReceiver!!,
last = call.getValueArgument(0)!!, last = expression.getValueArgument(0)!!,
step = irInt(1) step = irInt(1)
) )
} }
@@ -48,18 +52,19 @@ internal class RangeToHandler(private val context: CommonBackendContext, private
/** Builds a [HeaderInfo] for progressions built using the `downTo` extension function. */ /** Builds a [HeaderInfo] for progressions built using the `downTo` extension function. */
internal class DownToHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<SimpleType>) : internal class DownToHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<SimpleType>) :
ProgressionHandler { ProgressionHandler {
override val matcher = SimpleCalleeMatcher { override val matcher = SimpleCalleeMatcher {
singleArgumentExtension(FqName("kotlin.ranges.downTo"), progressionElementTypes) singleArgumentExtension(FqName("kotlin.ranges.downTo"), progressionElementTypes)
parameterCount { it == 1 } parameterCount { it == 1 }
parameter(0) { it.type.toKotlinType() in progressionElementTypes } parameter(0) { it.type.toKotlinType() in progressionElementTypes }
} }
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? = override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
ProgressionHeaderInfo( ProgressionHeaderInfo(
data, data,
first = call.extensionReceiver!!, first = expression.extensionReceiver!!,
last = call.getValueArgument(0)!!, last = expression.getValueArgument(0)!!,
step = irInt(-1) step = irInt(-1)
) )
} }
@@ -68,21 +73,21 @@ internal class DownToHandler(private val context: CommonBackendContext, private
/** Builds a [HeaderInfo] for progressions built using the `until` extension function. */ /** Builds a [HeaderInfo] for progressions built using the `until` extension function. */
internal class UntilHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<SimpleType>) : internal class UntilHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<SimpleType>) :
ProgressionHandler { ProgressionHandler {
override val matcher = SimpleCalleeMatcher { override val matcher = SimpleCalleeMatcher {
singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementTypes) singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementTypes)
parameterCount { it == 1 } parameterCount { it == 1 }
parameter(0) { it.type.toKotlinType() in progressionElementTypes } parameter(0) { it.type.toKotlinType() in progressionElementTypes }
} }
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? = override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
ProgressionHeaderInfo( ProgressionHeaderInfo(
data, data,
first = call.extensionReceiver!!, first = expression.extensionReceiver!!,
last = call.getValueArgument(0)!!, last = expression.getValueArgument(0)!!,
step = irInt(1), step = irInt(1),
isLastInclusive = false, isLastInclusive = false
canOverflow = false
) )
} }
} }
@@ -98,12 +103,12 @@ internal class IndicesHandler(val context: CommonBackendContext) : ProgressionHa
parameterCount { it == 0 } parameterCount { it == 0 }
} }
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? = override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
// `last = array.size` for the loop `for (i in array.indices)`. // `last = array.size` for the loop `for (i in array.indices)`.
val arraySizeProperty = call.extensionReceiver!!.type.getClass()!!.properties.first { it.name.asString() == "size" } val arraySizeProperty = expression.extensionReceiver!!.type.getClass()!!.properties.first { it.name.asString() == "size" }
val last = irCall(arraySizeProperty.getter!!).apply { val last = irCall(arraySizeProperty.getter!!).apply {
dispatchReceiver = call.extensionReceiver dispatchReceiver = expression.extensionReceiver
} }
ProgressionHeaderInfo( ProgressionHeaderInfo(
@@ -111,26 +116,45 @@ internal class IndicesHandler(val context: CommonBackendContext) : ProgressionHa
first = irInt(0), first = irInt(0),
last = last, last = last,
step = irInt(1), step = irInt(1),
isLastInclusive = false, isLastInclusive = false
canOverflow = false // Cannot overflow because `last` is at most MAX_VALUE - 1
) )
} }
} }
/** Builds a [HeaderInfo] for progressions not handled by more specialized handlers. */ /** Builds a [HeaderInfo] for calls to reverse an iterable. */
internal class DefaultProgressionHandler(private val context: CommonBackendContext) : HeaderInfoHandler<Nothing?> { internal class ReversedHandler(context: CommonBackendContext, val visitor: IrElementVisitor<HeaderInfo?, Nothing?>) :
HeaderInfoFromCallHandler<Nothing?> {
private val symbols = context.ir.symbols private val symbols = context.ir.symbols
override val matcher = createIrCallMatcher { // Use Quantifier.ANY so we can handle all reversed iterables in the same manner.
origin { it == IrStatementOrigin.FOR_LOOP_ITERATOR } override val matcher = createIrCallMatcher(Quantifier.ANY) {
dispatchReceiver { it != null && ProgressionType.fromIrType(it.type, symbols) != null } // Matcher for reversed progression.
callee {
fqName { it == FqName("kotlin.ranges.reversed") }
extensionReceiver { it != null && it.type.toKotlinType() in symbols.progressionClassesTypes }
parameterCount { it == 0 }
}
// TODO: Handle reversed String, Progression.withIndex(), etc.
} }
override fun build(call: IrCall, data: Nothing?): HeaderInfo? = // Reverse the HeaderInfo from the underlying progression or array (if any).
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { override fun build(expression: IrCall, data: Nothing?, scopeOwner: IrSymbol) =
expression.extensionReceiver!!.accept(visitor, null)?.asReversed()
}
/** Builds a [HeaderInfo] for progressions not handled by more specialized handlers. */
internal class DefaultProgressionHandler(private val context: CommonBackendContext) : ExpressionHandler {
private val symbols = context.ir.symbols
override fun match(expression: IrExpression) = ProgressionType.fromIrType(expression.type, symbols) != null
override fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
// Directly use the `first/last/step` properties of the progression. // Directly use the `first/last/step` properties of the progression.
val progression = scope.createTemporaryVariable(call.dispatchReceiver!!) val progression = scope.createTemporaryVariable(expression)
val progressionClass = progression.type.getClass()!! val progressionClass = progression.type.getClass()!!
val firstProperty = progressionClass.properties.first { it.name.asString() == "first" } val firstProperty = progressionClass.properties.first { it.name.asString() == "first" }
val first = irCall(firstProperty.getter!!).apply { val first = irCall(firstProperty.getter!!).apply {
@@ -156,16 +180,12 @@ internal class DefaultProgressionHandler(private val context: CommonBackendConte
} }
/** Builds a [HeaderInfo] for arrays. */ /** Builds a [HeaderInfo] for arrays. */
internal class ArrayIterationHandler(private val context: CommonBackendContext) : HeaderInfoHandler<Nothing?> { internal class ArrayIterationHandler(private val context: CommonBackendContext) : ExpressionHandler {
override val matcher = createIrCallMatcher { override fun match(expression: IrExpression) = KotlinBuiltIns.isArrayOrPrimitiveArray(expression.type.toKotlinType())
origin { it == IrStatementOrigin.FOR_LOOP_ITERATOR }
// TODO: Support rare cases like `T : IntArray`
dispatchReceiver { it != null && KotlinBuiltIns.isArrayOrPrimitiveArray(it.type.toKotlinType()) }
}
override fun build(call: IrCall, data: Nothing?): HeaderInfo? = override fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
// Consider the case like: // Consider the case like:
// //
// for (elem in A) { f(elem) }` // for (elem in A) { f(elem) }`
@@ -182,7 +202,7 @@ internal class ArrayIterationHandler(private val context: CommonBackendContext)
// This also ensures that the semantics of re-assignment of array variables used in the loop is consistent with the semantics // This also ensures that the semantics of re-assignment of array variables used in the loop is consistent with the semantics
// proposed in https://youtrack.jetbrains.com/issue/KT-21354. // proposed in https://youtrack.jetbrains.com/issue/KT-21354.
val arrayReference = scope.createTemporaryVariable( val arrayReference = scope.createTemporaryVariable(
call.dispatchReceiver!!, nameHint = "array", expression, nameHint = "array",
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE
) )
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
internal interface IrCallMatcher : (IrCall) -> Boolean internal interface IrCallMatcher : (IrCall) -> Boolean
/** /**
@@ -42,7 +41,9 @@ internal class IrCallOriginMatcher(
override fun invoke(call: IrCall) = restriction(call.origin) override fun invoke(call: IrCall) = restriction(call.origin)
} }
internal open class IrCallMatcherContainer : IrCallMatcher { internal enum class Quantifier { ALL, ANY }
internal open class IrCallMatcherContainer(private val quantifier: Quantifier) : IrCallMatcher {
private val matchers = mutableListOf<IrCallMatcher>() private val matchers = mutableListOf<IrCallMatcher>()
@@ -63,8 +64,14 @@ internal open class IrCallMatcherContainer : IrCallMatcher {
fun dispatchReceiver(restriction: (IrExpression?) -> Boolean) = fun dispatchReceiver(restriction: (IrExpression?) -> Boolean) =
add(IrCallDispatchReceiverMatcher(restriction)) add(IrCallDispatchReceiverMatcher(restriction))
override fun invoke(call: IrCall) = matchers.all { it(call) } override fun invoke(call: IrCall) = when (quantifier) {
Quantifier.ALL -> matchers.all { it(call) }
Quantifier.ANY -> matchers.any { it(call) }
}
} }
internal fun createIrCallMatcher(restrictions: IrCallMatcherContainer.() -> Unit) = internal fun createIrCallMatcher(
IrCallMatcherContainer().apply(restrictions) quantifier: Quantifier = Quantifier.ALL,
restrictions: IrCallMatcherContainer.() -> Unit
) =
IrCallMatcherContainer(quantifier).apply(restrictions)
@@ -0,0 +1,18 @@
// KJS_WITH_FULL_RUNTIME
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
val arr = intArrayOf(1, 2, 3, 4)
var sum = 0
var index = 0
for (i in arr.reversedArray()) {
// reversedArray() returns a new Array with elements in reversed order.
// Modifying the original array should have no effect on the iteration subject.
arr[index++] = 0
sum = sum * 10 + i
}
assertEquals(4321, sum)
return "OK"
}
@@ -0,0 +1,18 @@
// KJS_WITH_FULL_RUNTIME
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
val arr = intArrayOf(1, 2, 3, 4)
var sum = 0
var index = arr.size - 1
for (i in arr.reversedArray().reversedArray()) {
// reversedArray() returns a new Array with elements in reversed order.
// Modifying the original array should have no effect on the iteration subject.
arr[index--] = 0
sum = sum * 10 + i
}
assertEquals(1234, sum)
return "OK"
}
@@ -0,0 +1,18 @@
// KJS_WITH_FULL_RUNTIME
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
val arr = intArrayOf(1, 2, 3, 4)
var sum = 0
var index = 0
for (i in arr.reversed()) {
// reversed() returns a new List with elements in reversed order.
// Modifying the original array should have no effect on the iteration subject.
arr[index++] = 0
sum = sum * 10 + i
}
assertEquals(4321, sum)
return "OK"
}
@@ -0,0 +1,18 @@
// KJS_WITH_FULL_RUNTIME
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
val arr = intArrayOf(1, 2, 3, 4)
var sum = 0
var index = arr.size - 1
for (i in arr.reversed().reversed()) {
// reversed() returns a new List with elements in reversed order.
// Modifying the original array should have no effect on the iteration subject.
arr[index--] = 0
sum = sum * 10 + i
}
assertEquals(1234, sum)
return "OK"
}
@@ -0,0 +1,14 @@
// KJS_WITH_FULL_RUNTIME
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
val arr = intArrayOf(1, 1, 1, 1)
var sum = 0
for (i in arr.indices.reversed().reversed()) {
sum = sum * 10 + i + arr[i]
}
assertEquals(1234, sum)
return "OK"
}
@@ -0,0 +1,24 @@
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
var sum = 0
for (i in (4 downTo 1).reversed().reversed()) {
sum = sum * 10 + i
}
assertEquals(4321, sum)
var sumL = 0L
for (i in (4L downTo 1L).reversed().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(4321, sumL)
var sumC = 0
for (i in ('4' downTo '1').reversed().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(4321, sumC)
return "OK"
}
@@ -0,0 +1,24 @@
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
var sum = 0
for (i in (1 until 5).reversed().reversed()) {
sum = sum * 10 + i
}
assertEquals(1234, sum)
var sumL = 0L
for (i in (1L until 5L).reversed().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(1234, sumL)
var sumC = 0
for (i in ('1' until '5').reversed().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(1234, sumC)
return "OK"
}
@@ -0,0 +1,31 @@
// WITH_RUNTIME
import kotlin.test.*
fun intLow() = 1
fun intHigh() = 5
fun longLow() = 1L
fun longHigh() = 5L
fun charLow() = '1'
fun charHigh() = '5'
fun box(): String {
var sum = 0
for (i in (intLow() until intHigh()).reversed().reversed()) {
sum = sum * 10 + i
}
assertEquals(1234, sum)
var sumL = 0L
for (i in (longLow() until longHigh()).reversed().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(1234, sumL)
var sumC = 0
for (i in (charLow() until charHigh()).reversed().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(1234, sumC)
return "OK"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
import kotlin.test.* import kotlin.test.*
fun intRange() = 1 .. 4 fun intRange() = 1 .. 4
@@ -28,3 +27,8 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 3 getFirst
// 3 getLast
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
import kotlin.test.* import kotlin.test.*
fun box(): String { fun box(): String {
@@ -13,3 +12,11 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IF(_ICMPG|L)T
// 1 IF
@@ -1,4 +1,5 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// NOTE: Enable once CharSequence.indices is handled
import kotlin.test.* import kotlin.test.*
fun box(): String { fun box(): String {
@@ -13,3 +14,11 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IF(_ICMPG|L)T
// 1 IF
@@ -1,4 +1,5 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// NOTE: Enable once Collection.indices is handled
import kotlin.test.* import kotlin.test.*
fun box(): String { fun box(): String {
@@ -13,3 +14,11 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 1 IF(_ICMPG|L)T
// 1 IF
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
import kotlin.test.* import kotlin.test.*
fun box(): String { fun box(): String {
@@ -24,3 +23,13 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 2 IF_ICMP[LG]T
// 1 IF[LG]T
// 3 IF
// 1 LCMP
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
fun box(): String { fun box(): String {
for (i in (4 .. 1).reversed()) { for (i in (4 .. 1).reversed()) {
throw AssertionError("Loop should not be executed") throw AssertionError("Loop should not be executed")
@@ -13,6 +12,7 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart // 0 getStart
// 0 getEnd // 0 getEnd
// 0 getFirst // 0 getFirst
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
import kotlin.test.* import kotlin.test.*
fun intRange() = 1 .. 4 fun intRange() = 1 .. 4
@@ -28,3 +27,8 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 3 getFirst
// 3 getLast
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
import kotlin.test.* import kotlin.test.*
fun box(): String { fun box(): String {
@@ -24,12 +23,13 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart // 0 getStart
// 0 getEnd // 0 getEnd
// 0 getFirst // 0 getFirst
// 0 getLast // 0 getLast
// 0 getStep // 0 getStep
// 0 IFEQ // 2 IF_ICMP[LG]T
// 0 IF_ICMPEQ // 1 IF[LG]T
// 2 IF_ICMPLT // 3 IF
// 1 LCMP // 1 LCMP
@@ -0,0 +1,20 @@
import kotlin.test.*
fun box(): String {
val arr = intArrayOf(1, 1, 1, 1)
var sum = 0
for (i in arr.indices.reversed().reversed()) {
sum = sum * 10 + i + arr[i]
}
assertEquals(4321, sum)
return "OK"
}
// 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
@@ -0,0 +1,35 @@
import kotlin.test.*
fun box(): String {
var sum = 0
for (i in (4 downTo 1).reversed().reversed()) {
sum = sum * 10 + i
}
assertEquals(1234, sum)
var sumL = 0L
for (i in (4L downTo 1L).reversed().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(1234L, sumL)
var sumC = 0
for (i in ('4' downTo '1').reversed().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(1234, sumC)
return "OK"
}
// 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 2 IF_ICMP[LG]T
// 1 IF[LG]T
// 3 IF
// 1 LCMP
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
import kotlin.test.* import kotlin.test.*
fun intRange() = 1 .. 4 fun intRange() = 1 .. 4
@@ -28,3 +27,8 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 3 getFirst
// 3 getLast
@@ -0,0 +1,32 @@
import kotlin.test.*
fun box(): String {
var sum = 0
for (i in (1 until 5).reversed().reversed()) {
sum = sum * 10 + i
}
var sumL = 0L
for (i in (1L until 5L).reversed().reversed()) {
sumL = sumL * 10 + i
}
var sumC = 0
for (i in ('1' until '5').reversed().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
return "OK"
}
// 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 2 IF_ICMP[LG]E
// 1 IF[LG]E
// 3 IF
// 1 LCMP
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
import kotlin.test.* import kotlin.test.*
fun box(): String { fun box(): String {
@@ -21,3 +20,13 @@ fun box(): String {
} }
// 0 reversed // 0 reversed
// 0 iterator
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 2 IF_ICMP[LG]T
// 1 IF[LG]T
// 3 IF
// 1 LCMP
@@ -648,6 +648,39 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
} }
} }
@TestMetadata("compiler/testData/codegen/box/arrays/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractBlackBoxCodegenTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("reversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedOriginalUpdatedInLoopBody.kt")
public void testReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedReversedOriginalUpdatedInLoopBody.kt")
public void testReversedReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedReversedOriginalUpdatedInLoopBody.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/arrays/multiDecl") @TestMetadata("compiler/testData/codegen/box/arrays/multiDecl")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -18992,6 +19025,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
} }
@TestMetadata("forInReversedReversedArrayIndices.kt")
public void testForInReversedReversedArrayIndices() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedArrayIndices.kt");
}
@TestMetadata("forInReversedReversedDownTo.kt")
public void testForInReversedReversedDownTo() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedDownTo.kt");
}
@TestMetadata("ForInReversedReversedRange.kt") @TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception { public void testForInReversedReversedRange() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt");
@@ -19002,6 +19045,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt");
} }
@TestMetadata("forInReversedReversedUntil.kt")
public void testForInReversedReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntil.kt");
}
@TestMetadata("forInReversedReversedUntilWithNonConstBounds.kt")
public void testForInReversedReversedUntilWithNonConstBounds() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntilWithNonConstBounds.kt");
}
@TestMetadata("forInReversedUntil.kt") @TestMetadata("forInReversedUntil.kt")
public void testForInReversedUntil() throws Exception { public void testForInReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt");
@@ -1971,6 +1971,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt"); runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt");
} }
@TestMetadata("forInReversedReversedArrayIndices.kt")
public void testForInReversedReversedArrayIndices() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedArrayIndices.kt");
}
@TestMetadata("forInReversedReversedDownTo.kt")
public void testForInReversedReversedDownTo() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedDownTo.kt");
}
@TestMetadata("ForInReversedReversedRange.kt") @TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception { public void testForInReversedReversedRange() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/ForInReversedReversedRange.kt"); runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/ForInReversedReversedRange.kt");
@@ -1981,6 +1991,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedReversedRange.kt"); runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedReversedRange.kt");
} }
@TestMetadata("forInReversedReversedUntil.kt")
public void testForInReversedReversedUntil() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedUntil.kt");
}
@TestMetadata("forInReversedUntil.kt") @TestMetadata("forInReversedUntil.kt")
public void testForInReversedUntil() throws Exception { public void testForInReversedUntil() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt"); runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt");
@@ -648,6 +648,39 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
} }
} }
@TestMetadata("compiler/testData/codegen/box/arrays/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractLightAnalysisModeTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("reversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedOriginalUpdatedInLoopBody.kt")
public void testReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedReversedOriginalUpdatedInLoopBody.kt")
public void testReversedReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedReversedOriginalUpdatedInLoopBody.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/arrays/multiDecl") @TestMetadata("compiler/testData/codegen/box/arrays/multiDecl")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -18992,6 +19025,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
} }
@TestMetadata("forInReversedReversedArrayIndices.kt")
public void testForInReversedReversedArrayIndices() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedArrayIndices.kt");
}
@TestMetadata("forInReversedReversedDownTo.kt")
public void testForInReversedReversedDownTo() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedDownTo.kt");
}
@TestMetadata("ForInReversedReversedRange.kt") @TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception { public void testForInReversedReversedRange() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt");
@@ -19002,6 +19045,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt");
} }
@TestMetadata("forInReversedReversedUntil.kt")
public void testForInReversedReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntil.kt");
}
@TestMetadata("forInReversedReversedUntilWithNonConstBounds.kt")
public void testForInReversedReversedUntilWithNonConstBounds() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntilWithNonConstBounds.kt");
}
@TestMetadata("forInReversedUntil.kt") @TestMetadata("forInReversedUntil.kt")
public void testForInReversedUntil() throws Exception { public void testForInReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt");
@@ -648,6 +648,39 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
} }
} }
@TestMetadata("compiler/testData/codegen/box/arrays/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractIrBlackBoxCodegenTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
}
@TestMetadata("reversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedOriginalUpdatedInLoopBody.kt")
public void testReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedReversedOriginalUpdatedInLoopBody.kt")
public void testReversedReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedReversedOriginalUpdatedInLoopBody.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/arrays/multiDecl") @TestMetadata("compiler/testData/codegen/box/arrays/multiDecl")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -18997,6 +19030,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
} }
@TestMetadata("forInReversedReversedArrayIndices.kt")
public void testForInReversedReversedArrayIndices() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedArrayIndices.kt");
}
@TestMetadata("forInReversedReversedDownTo.kt")
public void testForInReversedReversedDownTo() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedDownTo.kt");
}
@TestMetadata("ForInReversedReversedRange.kt") @TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception { public void testForInReversedReversedRange() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt");
@@ -19007,6 +19050,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt");
} }
@TestMetadata("forInReversedReversedUntil.kt")
public void testForInReversedReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntil.kt");
}
@TestMetadata("forInReversedReversedUntilWithNonConstBounds.kt")
public void testForInReversedReversedUntilWithNonConstBounds() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntilWithNonConstBounds.kt");
}
@TestMetadata("forInReversedUntil.kt") @TestMetadata("forInReversedUntil.kt")
public void testForInReversedUntil() throws Exception { public void testForInReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt");
@@ -1981,6 +1981,16 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt"); runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt");
} }
@TestMetadata("forInReversedReversedArrayIndices.kt")
public void testForInReversedReversedArrayIndices() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedArrayIndices.kt");
}
@TestMetadata("forInReversedReversedDownTo.kt")
public void testForInReversedReversedDownTo() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedDownTo.kt");
}
@TestMetadata("ForInReversedReversedRange.kt") @TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception { public void testForInReversedReversedRange() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/ForInReversedReversedRange.kt"); runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/ForInReversedReversedRange.kt");
@@ -1991,6 +2001,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedReversedRange.kt"); runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedReversedRange.kt");
} }
@TestMetadata("forInReversedReversedUntil.kt")
public void testForInReversedReversedUntil() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedUntil.kt");
}
@TestMetadata("forInReversedUntil.kt") @TestMetadata("forInReversedUntil.kt")
public void testForInReversedUntil() throws Exception { public void testForInReversedUntil() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt"); runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt");
@@ -488,6 +488,39 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
} }
} }
@TestMetadata("compiler/testData/codegen/box/arrays/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractIrJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true);
}
@TestMetadata("reversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedOriginalUpdatedInLoopBody.kt")
public void testReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedReversedOriginalUpdatedInLoopBody.kt")
public void testReversedReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedReversedOriginalUpdatedInLoopBody.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/arrays/multiDecl") @TestMetadata("compiler/testData/codegen/box/arrays/multiDecl")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -15022,6 +15055,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
} }
@TestMetadata("forInReversedReversedArrayIndices.kt")
public void testForInReversedReversedArrayIndices() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedArrayIndices.kt");
}
@TestMetadata("forInReversedReversedDownTo.kt")
public void testForInReversedReversedDownTo() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedDownTo.kt");
}
@TestMetadata("ForInReversedReversedRange.kt") @TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception { public void testForInReversedReversedRange() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt");
@@ -15032,6 +15075,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt");
} }
@TestMetadata("forInReversedReversedUntil.kt")
public void testForInReversedReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntil.kt");
}
@TestMetadata("forInReversedReversedUntilWithNonConstBounds.kt")
public void testForInReversedReversedUntilWithNonConstBounds() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntilWithNonConstBounds.kt");
}
@TestMetadata("forInReversedUntil.kt") @TestMetadata("forInReversedUntil.kt")
public void testForInReversedUntil() throws Exception { public void testForInReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt");
@@ -488,6 +488,39 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
} }
} }
@TestMetadata("compiler/testData/codegen/box/arrays/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("reversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedOriginalUpdatedInLoopBody.kt")
public void testReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedReversedOriginalUpdatedInLoopBody.kt")
public void testReversedReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedReversedOriginalUpdatedInLoopBody.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/arrays/multiDecl") @TestMetadata("compiler/testData/codegen/box/arrays/multiDecl")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -16167,6 +16200,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
} }
@TestMetadata("forInReversedReversedArrayIndices.kt")
public void testForInReversedReversedArrayIndices() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedArrayIndices.kt");
}
@TestMetadata("forInReversedReversedDownTo.kt")
public void testForInReversedReversedDownTo() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedDownTo.kt");
}
@TestMetadata("ForInReversedReversedRange.kt") @TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception { public void testForInReversedReversedRange() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt");
@@ -16177,6 +16220,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt");
} }
@TestMetadata("forInReversedReversedUntil.kt")
public void testForInReversedReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntil.kt");
}
@TestMetadata("forInReversedReversedUntilWithNonConstBounds.kt")
public void testForInReversedReversedUntilWithNonConstBounds() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntilWithNonConstBounds.kt");
}
@TestMetadata("forInReversedUntil.kt") @TestMetadata("forInReversedUntil.kt")
public void testForInReversedUntil() throws Exception { public void testForInReversedUntil() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt");
@@ -352,6 +352,39 @@ public class JsLegacyPrimitiveArraysBoxTestGenerated extends AbstractJsLegacyPri
} }
} }
@TestMetadata("compiler/testData/codegen/box/arrays/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractJsLegacyPrimitiveArraysBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("reversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt")
public void testReversedArrayReversedArrayOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedOriginalUpdatedInLoopBody.kt")
public void testReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedOriginalUpdatedInLoopBody.kt");
}
@TestMetadata("reversedReversedOriginalUpdatedInLoopBody.kt")
public void testReversedReversedOriginalUpdatedInLoopBody() throws Exception {
runTest("compiler/testData/codegen/box/arrays/forInReversed/reversedReversedOriginalUpdatedInLoopBody.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/arrays/multiDecl") @TestMetadata("compiler/testData/codegen/box/arrays/multiDecl")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)