diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt index 017bda71673..1f975902180 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt @@ -9,21 +9,13 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext 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.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.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.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.transformChildrenVoid @@ -99,11 +91,9 @@ val forLoopsPhase = makeIrFilePhase( */ internal class ForLoopsLowering(val context: CommonBackendContext) : FileLoweringPass { - private val headerInfoBuilder = HeaderInfoBuilder(context) - override fun lower(irFile: IrFile) { val oldLoopToNewLoop = mutableMapOf() - val transformer = RangeLoopTransformer(context, oldLoopToNewLoop, headerInfoBuilder) + val transformer = RangeLoopTransformer(context, oldLoopToNewLoop) irFile.transformChildrenVoid(transformer) // Update references in break/continue. @@ -118,12 +108,12 @@ internal class ForLoopsLowering(val context: CommonBackendContext) : FileLowerin private class RangeLoopTransformer( val context: CommonBackendContext, - val oldLoopToNewLoop: MutableMap, - headerInfoBuilder: HeaderInfoBuilder + val oldLoopToNewLoop: MutableMap ) : IrElementTransformerVoidWithContext() { private val symbols = context.ir.symbols private val iteratorToLoopHeader = mutableMapOf() + private val headerInfoBuilder = HeaderInfoBuilder(context, this::getScopeOwnerSymbol) private val headerProcessor = HeaderProcessor(context, headerInfoBuilder, this::getScopeOwnerSymbol) fun getScopeOwnerSymbol() = currentScope!!.scope.scopeOwnerSymbol @@ -185,13 +175,12 @@ private class RangeLoopTransformer( val loopHeader = getLoopHeader(loop.condition) ?: 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. oldLoopToNewLoop[loop] = newLoop - // Surround the new loop with a check for an empty loop, if necessary. - return loopHeader.buildNotEmptyConditionIfNecessary(this@with)?.let { irIfThen(it, newLoop) } ?: newLoop + return replacementExpression } } @@ -232,18 +221,7 @@ private class RangeLoopTransformer( // inductionVariable = inductionVariable + step return with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) { variable.initializer = forLoopInfo.initializeLoopVariable(symbols, this) - val plusFun = forLoopInfo.inductionVariable.type.getClass()!!.functions.first { - 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) - ) - ) + val increment = forLoopInfo.buildIncrementInductionVariableExpression(this) IrCompositeImpl( variable.startOffset, variable.endOffset, diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt index aa5e03dcf99..21bcc46048c 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt @@ -13,15 +13,20 @@ import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.IrCall 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.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.getClass 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.name.Name import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult // TODO: Handle withIndex() -// TODO: Handle reversed() // TODO: Handle Strings/CharSequences // TODO: Handle UIntProgression, ULongProgression @@ -62,7 +67,9 @@ internal sealed class HeaderInfo( val first: IrExpression, val last: IrExpression, val step: IrExpression, - val isLastInclusive: Boolean + val isFirstInclusive: Boolean, + val isLastInclusive: Boolean, + val isReversed: Boolean ) { val direction: ProgressionDirection by lazy { // 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 } } + + /** + * 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. */ @@ -83,14 +97,15 @@ internal class ProgressionHeaderInfo( first: IrExpression, last: IrExpression, step: IrExpression, + isFirstInclusive: Boolean = true, isLastInclusive: Boolean = true, - canOverflow: Boolean? = null, + isReversed: Boolean = false, val additionalVariables: List = 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 { - 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). val lastValue = (last as? IrConst<*>)?.value @@ -118,6 +133,17 @@ internal class ProgressionHeaderInfo( } 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. */ @@ -131,28 +157,72 @@ internal class ArrayHeaderInfo( first, last, 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. */ -internal interface HeaderInfoHandler { - val matcher: IrCallMatcher +/** Return the negated value if the expression is const, otherwise call unaryMinus(). */ +private fun IrExpression.negate(): IrExpression { + 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 { + /** 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)) { - build(irCall, data) + /** Builds a [HeaderInfo] from the expression. */ + 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 { null } } -internal typealias ProgressionHandler = HeaderInfoHandler -/** - * Handles a call to `iterator()` on more specialized forms of progressions, built using extension - * and member functions/properties in the stdlib (e.g., `.indices`, `downTo`). - */ -private class ProgressionHeaderInfoBuilder(val context: CommonBackendContext) : IrElementVisitor { +internal interface ExpressionHandler : HeaderInfoHandler { + fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? + override fun build(expression: IrExpression, data: Nothing?, scopeOwner: IrSymbol) = build(expression, scopeOwner) +} + +/** Matches a call to build an iterable and builds a [HeaderInfo] from the call's context. */ +internal interface HeaderInfoFromCallHandler : HeaderInfoHandler { + val matcher: IrCallMatcher + + override fun match(expression: IrCall) = matcher(expression) +} + +internal typealias ProgressionHandler = HeaderInfoFromCallHandler + +internal class HeaderInfoBuilder(context: CommonBackendContext, private val scopeOwnerSymbol: () -> IrSymbol) : + IrElementVisitor { private val symbols = context.ir.symbols @@ -166,31 +236,33 @@ private class ProgressionHeaderInfoBuilder(val context: CommonBackendContext) : 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 + /** Builds a [HeaderInfo] for iterable expressions that are calls (e.g., `.reversed()`, `.indices`. */ 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) - ?: return null - return progressionHandlers.firstNotNullResult { it.handle(expression, progressionType) } + val progressionHeaderInfo = + 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) - private val arrayIterationHandler = ArrayIterationHandler(context) - private val defaultProgressionHandler = DefaultProgressionHandler(context) - - 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) + /** Builds a [HeaderInfo] for iterable expressions not handled in [visitCall]. */ + override fun visitExpression(expression: IrExpression, data: Nothing?): HeaderInfo? { + return expressionHandlers.firstNotNullResult { it.handle(expression, null, scopeOwnerSymbol()) } + ?: super.visitExpression(expression, data) } } \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt index 9040fa63d7d..977cf1bce72 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt @@ -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.lower.DeclarationIrBuilder 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.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.IrExpression import org.jetbrains.kotlin.ir.expressions.IrLoop 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.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. */ internal sealed class ForLoopHeader( protected open val headerInfo: HeaderInfo, @@ -39,15 +54,7 @@ internal sealed class ForLoopHeader( abstract val declarations: List /** Builds a new loop from the old loop. */ - abstract fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop - - /** - * 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? + abstract fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement protected fun buildLoopCondition(builder: DeclarationIrBuilder): IrExpression = 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(). // 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 - 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) { - 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) { - // // Loop is not empty - // do { - // val loopVar = inductionVar - // inductionVar++ - // // Loop body - // } while (loopVar != last) - // } - // - // The `if (inductionVar <= last)` "not empty" check is added in buildNotEmptyConditionIfNecessary(). - assert(loopVariable != null) - val booleanNotFun = context.irBuiltIns.booleanClass.functions.first { it.owner.name.asString() == "not" } - val newCondition = irCallOp(booleanNotFun, booleanNotFun.owner.returnType, irCall(context.irBuiltIns.eqeqSymbol).apply { - putValueArgument(0, irGet(loopVariable!!)) - putValueArgument(1, irGet(last)) - }) - IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply { - label = loop.label - condition = newCondition - body = newBody + override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?): LoopReplacement { + with(builder) { + 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: + // + // if (inductionVar <= last) { + // // Loop is not empty + // do { + // val loopVar = inductionVar + // inductionVar += step + // // Loop body + // } while (loopVar != last) + // } + assert(loopVariable != null) + val booleanNotFun = context.irBuiltIns.booleanClass.functions.first { it.owner.name.asString() == "not" } + val newCondition = irCallOp(booleanNotFun, booleanNotFun.owner.returnType, irCall(context.irBuiltIns.eqeqSymbol).apply { + putValueArgument(0, irGet(loopVariable!!)) + putValueArgument(1, irGet(last)) + }) + val newLoop = IrDoWhileLoopImpl(oldLoop.startOffset, oldLoop.endOffset, oldLoop.type, oldLoop.origin).apply { + label = oldLoop.label + condition = newCondition + body = newBody + } + val notEmptyCheck = irIfThen(buildLoopCondition(builder), newLoop) + 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: - // - // var inductionVar = A - // var last = B - // while (inductionVar <= last) { - // 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 + + if (!headerInfo.isFirstInclusive) { + // Pre-increment the induction variable. + replacementExpression = irComposite(replacementExpression) { + +buildIncrementInductionVariableExpression(this@with) + +replacementExpression + } } + + 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( @@ -194,25 +210,30 @@ internal class ArrayLoopHeader( override val declarations: List 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: // // var inductionVar = 0 // var last = array.size // while (inductionVar < last) { - // val loopVar = array[inductionVar++] + // val loopVar = array[inductionVar] // inductionVar++ // // Loop body // } - IrWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply { - label = loop.label + val newLoop = IrWhileLoopImpl(oldLoop.startOffset, oldLoop.endOffset, oldLoop.type, oldLoop.origin).apply { + label = oldLoop.label condition = buildLoopCondition(this@with) 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 } - // Collect loop information. - val headerInfo = headerInfoBuilder.build(variable) + // Get the iterable expression, e.g., `someIterable` in the following loop variable declaration: + // 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. val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset) @@ -310,21 +334,37 @@ internal class HeaderProcessor( } } } + + private fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) = + if (expression.type is IrSimpleType && expression.type.isNullable()) { + irImplicitCast(expression, expression.type.makeNotNull()) + } else { + expression + } + + private fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name): IrExpression { + return if (type.toKotlinType() == targetType.toKotlinType()) { + this + } else { + val function = type.getClass()!!.functions.first { it.name == numberCastFunctionName } + IrCallImpl(startOffset, endOffset, function.returnType, function.symbol) + .apply { dispatchReceiver = this@castIfNecessary } + } + } } -internal fun DeclarationIrBuilder.ensureNotNullable(expression: IrExpression) = - if (expression.type is IrSimpleType && expression.type.isNullable()) { - irImplicitCast(expression, expression.type.makeNotNull()) - } else { - expression - } - -internal fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name): IrExpression { - return if (type.toKotlinType() == targetType.toKotlinType()) { - this - } else { - val function = type.getClass()!!.functions.first { it.name == numberCastFunctionName } - IrCallImpl(startOffset, endOffset, function.returnType, function.symbol) - .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) + ) + ) } \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt index ad988bfba96..06df3bce0a8 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt @@ -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.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.createIrCallMatcher 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.declarations.IrDeclarationOrigin 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.toKotlinType 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.Name 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. */ internal class RangeToHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection) : ProgressionHandler { + override val matcher = SimpleCalleeMatcher { dispatchReceiver { it != null && it.type.toKotlinType() in progressionElementTypes } 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 } } - override fun build(call: IrCall, data: ProgressionType) = - with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { + override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol) = + with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { ProgressionHeaderInfo( data, - first = call.dispatchReceiver!!, - last = call.getValueArgument(0)!!, + first = expression.dispatchReceiver!!, + last = expression.getValueArgument(0)!!, 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. */ internal class DownToHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection) : ProgressionHandler { + override val matcher = SimpleCalleeMatcher { singleArgumentExtension(FqName("kotlin.ranges.downTo"), progressionElementTypes) parameterCount { it == 1 } parameter(0) { it.type.toKotlinType() in progressionElementTypes } } - override fun build(call: IrCall, data: ProgressionType): HeaderInfo? = - with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { + override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? = + with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { ProgressionHeaderInfo( data, - first = call.extensionReceiver!!, - last = call.getValueArgument(0)!!, + first = expression.extensionReceiver!!, + last = expression.getValueArgument(0)!!, 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. */ internal class UntilHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection) : ProgressionHandler { + override val matcher = SimpleCalleeMatcher { singleArgumentExtension(FqName("kotlin.ranges.until"), progressionElementTypes) parameterCount { it == 1 } parameter(0) { it.type.toKotlinType() in progressionElementTypes } } - override fun build(call: IrCall, data: ProgressionType): HeaderInfo? = - with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { + override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? = + with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { ProgressionHeaderInfo( data, - first = call.extensionReceiver!!, - last = call.getValueArgument(0)!!, + first = expression.extensionReceiver!!, + last = expression.getValueArgument(0)!!, step = irInt(1), - isLastInclusive = false, - canOverflow = false + isLastInclusive = false ) } } @@ -98,12 +103,12 @@ internal class IndicesHandler(val context: CommonBackendContext) : ProgressionHa parameterCount { it == 0 } } - override fun build(call: IrCall, data: ProgressionType): HeaderInfo? = - with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { + override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? = + with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { // `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 { - dispatchReceiver = call.extensionReceiver + dispatchReceiver = expression.extensionReceiver } ProgressionHeaderInfo( @@ -111,26 +116,45 @@ internal class IndicesHandler(val context: CommonBackendContext) : ProgressionHa first = irInt(0), last = last, step = irInt(1), - isLastInclusive = false, - canOverflow = false // Cannot overflow because `last` is at most MAX_VALUE - 1 + isLastInclusive = false ) } } -/** Builds a [HeaderInfo] for progressions not handled by more specialized handlers. */ -internal class DefaultProgressionHandler(private val context: CommonBackendContext) : HeaderInfoHandler { +/** Builds a [HeaderInfo] for calls to reverse an iterable. */ +internal class ReversedHandler(context: CommonBackendContext, val visitor: IrElementVisitor) : + HeaderInfoFromCallHandler { private val symbols = context.ir.symbols - override val matcher = createIrCallMatcher { - origin { it == IrStatementOrigin.FOR_LOOP_ITERATOR } - dispatchReceiver { it != null && ProgressionType.fromIrType(it.type, symbols) != null } + // Use Quantifier.ANY so we can handle all reversed iterables in the same manner. + override val matcher = createIrCallMatcher(Quantifier.ANY) { + // 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? = - with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { + // Reverse the HeaderInfo from the underlying progression or array (if any). + 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. - val progression = scope.createTemporaryVariable(call.dispatchReceiver!!) + val progression = scope.createTemporaryVariable(expression) val progressionClass = progression.type.getClass()!! val firstProperty = progressionClass.properties.first { it.name.asString() == "first" } val first = irCall(firstProperty.getter!!).apply { @@ -156,16 +180,12 @@ internal class DefaultProgressionHandler(private val context: CommonBackendConte } /** Builds a [HeaderInfo] for arrays. */ -internal class ArrayIterationHandler(private val context: CommonBackendContext) : HeaderInfoHandler { +internal class ArrayIterationHandler(private val context: CommonBackendContext) : ExpressionHandler { - override val matcher = createIrCallMatcher { - origin { it == IrStatementOrigin.FOR_LOOP_ITERATOR } - // TODO: Support rare cases like `T : IntArray` - dispatchReceiver { it != null && KotlinBuiltIns.isArrayOrPrimitiveArray(it.type.toKotlinType()) } - } + override fun match(expression: IrExpression) = KotlinBuiltIns.isArrayOrPrimitiveArray(expression.type.toKotlinType()) - override fun build(call: IrCall, data: Nothing?): HeaderInfo? = - with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { + override fun build(expression: IrExpression, scopeOwner: IrSymbol): HeaderInfo? = + with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { // Consider the case like: // // 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 // proposed in https://youtrack.jetbrains.com/issue/KT-21354. val arrayReference = scope.createTemporaryVariable( - call.dispatchReceiver!!, nameHint = "array", + expression, nameHint = "array", origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE ) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/matchers/IrCallMatcher.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/matchers/IrCallMatcher.kt index 15c0f898290..4a6852e548b 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/matchers/IrCallMatcher.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/matchers/IrCallMatcher.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin - internal interface IrCallMatcher : (IrCall) -> Boolean /** @@ -42,7 +41,9 @@ internal class IrCallOriginMatcher( 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() @@ -63,8 +64,14 @@ internal open class IrCallMatcherContainer : IrCallMatcher { fun dispatchReceiver(restriction: (IrExpression?) -> Boolean) = 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) = - IrCallMatcherContainer().apply(restrictions) \ No newline at end of file +internal fun createIrCallMatcher( + quantifier: Quantifier = Quantifier.ALL, + restrictions: IrCallMatcherContainer.() -> Unit +) = + IrCallMatcherContainer(quantifier).apply(restrictions) \ No newline at end of file diff --git a/compiler/testData/codegen/box/arrays/forInReversed/reversedArrayOriginalUpdatedInLoopBody.kt b/compiler/testData/codegen/box/arrays/forInReversed/reversedArrayOriginalUpdatedInLoopBody.kt new file mode 100644 index 00000000000..faee25e6092 --- /dev/null +++ b/compiler/testData/codegen/box/arrays/forInReversed/reversedArrayOriginalUpdatedInLoopBody.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/arrays/forInReversed/reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt b/compiler/testData/codegen/box/arrays/forInReversed/reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt new file mode 100644 index 00000000000..76f4abbeed1 --- /dev/null +++ b/compiler/testData/codegen/box/arrays/forInReversed/reversedArrayReversedArrayOriginalUpdatedInLoopBody.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/arrays/forInReversed/reversedOriginalUpdatedInLoopBody.kt b/compiler/testData/codegen/box/arrays/forInReversed/reversedOriginalUpdatedInLoopBody.kt new file mode 100644 index 00000000000..e51e49904fc --- /dev/null +++ b/compiler/testData/codegen/box/arrays/forInReversed/reversedOriginalUpdatedInLoopBody.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/arrays/forInReversed/reversedReversedOriginalUpdatedInLoopBody.kt b/compiler/testData/codegen/box/arrays/forInReversed/reversedReversedOriginalUpdatedInLoopBody.kt new file mode 100644 index 00000000000..44a2264c2f1 --- /dev/null +++ b/compiler/testData/codegen/box/arrays/forInReversed/reversedReversedOriginalUpdatedInLoopBody.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedArrayIndices.kt b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedArrayIndices.kt new file mode 100644 index 00000000000..87ef2e80d99 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedArrayIndices.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedDownTo.kt b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedDownTo.kt new file mode 100644 index 00000000000..430f2f2f5fe --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedDownTo.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntil.kt b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntil.kt new file mode 100644 index 00000000000..673e20b6142 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntil.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntilWithNonConstBounds.kt b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntilWithNonConstBounds.kt new file mode 100644 index 00000000000..09b1cd793a7 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedUntilWithNonConstBounds.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/ForInReversedReversedRange.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/ForInReversedReversedRange.kt index 0e753b2a9b7..f9d548e31ef 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/ForInReversedReversedRange.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/ForInReversedReversedRange.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR import kotlin.test.* fun intRange() = 1 .. 4 @@ -28,3 +27,8 @@ fun box(): String { } // 0 reversed +// 0 iterator +// 0 getStart +// 0 getEnd +// 3 getFirst +// 3 getLast diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedArrayIndices.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedArrayIndices.kt index 9cddddb6854..9efd068097c 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedArrayIndices.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedArrayIndices.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR import kotlin.test.* fun box(): String { @@ -13,3 +12,11 @@ fun box(): String { } // 0 reversed +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 IF(_ICMPG|L)T +// 1 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCharSequenceIndices.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCharSequenceIndices.kt index 3b5817916ab..a9ef956c592 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCharSequenceIndices.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCharSequenceIndices.kt @@ -1,4 +1,5 @@ // IGNORE_BACKEND: JVM_IR +// NOTE: Enable once CharSequence.indices is handled import kotlin.test.* fun box(): String { @@ -13,3 +14,11 @@ fun box(): String { } // 0 reversed +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 IF(_ICMPG|L)T +// 1 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCollectionIndices.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCollectionIndices.kt index efc228d3ce6..6657046c488 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCollectionIndices.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedCollectionIndices.kt @@ -1,4 +1,5 @@ // IGNORE_BACKEND: JVM_IR +// NOTE: Enable once Collection.indices is handled import kotlin.test.* fun box(): String { @@ -13,3 +14,11 @@ fun box(): String { } // 0 reversed +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 IF(_ICMPG|L)T +// 1 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedDownTo.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedDownTo.kt index 59e75777136..34530ee0a13 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedDownTo.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedDownTo.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR import kotlin.test.* fun box(): String { @@ -24,3 +23,13 @@ fun box(): String { } // 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 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt index a7e5d0e7618..6067efd7841 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun box(): String { for (i in (4 .. 1).reversed()) { throw AssertionError("Loop should not be executed") @@ -13,6 +12,7 @@ fun box(): String { } // 0 reversed +// 0 iterator // 0 getStart // 0 getEnd // 0 getFirst diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRange.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRange.kt index b35ab109db2..da203dbaaba 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRange.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRange.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR import kotlin.test.* fun intRange() = 1 .. 4 @@ -28,3 +27,8 @@ fun box(): String { } // 0 reversed +// 0 iterator +// 0 getStart +// 0 getEnd +// 3 getFirst +// 3 getLast diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt index 7becf5b4a3d..665f0d1820e 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR import kotlin.test.* fun box(): String { @@ -24,12 +23,13 @@ fun box(): String { } // 0 reversed +// 0 iterator // 0 getStart // 0 getEnd // 0 getFirst // 0 getLast // 0 getStep -// 0 IFEQ -// 0 IF_ICMPEQ -// 2 IF_ICMPLT -// 1 LCMP +// 2 IF_ICMP[LG]T +// 1 IF[LG]T +// 3 IF +// 1 LCMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedArrayIndices.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedArrayIndices.kt new file mode 100644 index 00000000000..4fc487e27d1 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedArrayIndices.kt @@ -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 diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedDownTo.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedDownTo.kt new file mode 100644 index 00000000000..b313b5ea990 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedDownTo.kt @@ -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 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedReversedRange.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedReversedRange.kt index b00f569767c..0225f709aee 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedReversedRange.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedReversedRange.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR import kotlin.test.* fun intRange() = 1 .. 4 @@ -28,3 +27,8 @@ fun box(): String { } // 0 reversed +// 0 iterator +// 0 getStart +// 0 getEnd +// 3 getFirst +// 3 getLast diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedUntil.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedUntil.kt new file mode 100644 index 00000000000..b0984bb5706 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedUntil.kt @@ -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 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt index c8980d8b633..e10f91cab87 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR import kotlin.test.* fun box(): String { @@ -21,3 +20,13 @@ fun box(): String { } // 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 \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 3daf89f97e0..2ac664cfc58 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -18992,6 +19025,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { 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") public void testForInReversedReversedRange() throws Exception { 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"); } + @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") public void testForInReversedUntil() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 907e584f0e0..8c43a02b7fa 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1971,6 +1971,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { 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") public void testForInReversedReversedRange() throws Exception { 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"); } + @TestMetadata("forInReversedReversedUntil.kt") + public void testForInReversedReversedUntil() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedUntil.kt"); + } + @TestMetadata("forInReversedUntil.kt") public void testForInReversedUntil() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 45e1208ac2c..40d9bb869c0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -18992,6 +19025,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes 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") public void testForInReversedReversedRange() throws Exception { 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"); } + @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") public void testForInReversedUntil() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 28bb7da6186..877ef904e3c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -18997,6 +19030,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes 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") public void testForInReversedReversedRange() throws Exception { 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"); } + @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") public void testForInReversedUntil() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index 8ffe6476b17..5b43136197a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -1981,6 +1981,16 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { 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") public void testForInReversedReversedRange() throws Exception { 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"); } + @TestMetadata("forInReversedReversedUntil.kt") + public void testForInReversedReversedUntil() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedUntil.kt"); + } + @TestMetadata("forInReversedUntil.kt") public void testForInReversedUntil() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedUntil.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 8b010279af2..04eabc48792 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -15022,6 +15055,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { 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") public void testForInReversedReversedRange() throws Exception { 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"); } + @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") public void testForInReversedUntil() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 26c0b33aa9c..386170485f3 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -16167,6 +16200,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { 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") public void testForInReversedReversedRange() throws Exception { 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"); } + @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") public void testForInReversedUntil() throws Exception { runTest("compiler/testData/codegen/box/ranges/forInReversed/forInReversedUntil.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsLegacyPrimitiveArraysBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsLegacyPrimitiveArraysBoxTestGenerated.java index 91edf06097b..4fd50a26cbe 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsLegacyPrimitiveArraysBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsLegacyPrimitiveArraysBoxTestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)