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:
committed by
max-kammerer
parent
3d1b6fb83c
commit
1b703448d3
+6
-28
@@ -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,
|
||||||
|
|||||||
+112
-40
@@ -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) {
|
/** Builds a [HeaderInfo] for iterable expressions not handled in [visitCall]. */
|
||||||
private val progressionHeaderInfoBuilder = ProgressionHeaderInfoBuilder(context)
|
override fun visitExpression(expression: IrExpression, data: Nothing?): HeaderInfo? {
|
||||||
private val arrayIterationHandler = ArrayIterationHandler(context)
|
return expressionHandlers.firstNotNullResult { it.handle(expression, null, scopeOwnerSymbol()) }
|
||||||
private val defaultProgressionHandler = DefaultProgressionHandler(context)
|
?: 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+118
-78
@@ -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(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) =
|
internal fun ForLoopHeader.buildIncrementInductionVariableExpression(builder: DeclarationIrBuilder): IrExpression = with(builder) {
|
||||||
if (expression.type is IrSimpleType && expression.type.isNullable()) {
|
// inductionVariable = inductionVariable + step
|
||||||
irImplicitCast(expression, expression.type.makeNotNull())
|
val plusFun = inductionVariable.type.getClass()!!.functions.first {
|
||||||
} else {
|
it.name.asString() == "plus" &&
|
||||||
expression
|
it.valueParameters.size == 1 &&
|
||||||
}
|
it.valueParameters[0].type.toKotlinType() == step.type.toKotlinType()
|
||||||
|
|
||||||
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 }
|
|
||||||
}
|
}
|
||||||
|
irSetVar(
|
||||||
|
inductionVariable.symbol, irCallOp(
|
||||||
|
plusFun.symbol, plusFun.returnType,
|
||||||
|
irGet(inductionVariable),
|
||||||
|
irGet(step)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
+58
-38
@@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+12
-5
@@ -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)
|
||||||
Vendored
+18
@@ -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"
|
||||||
|
}
|
||||||
+18
@@ -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"
|
||||||
|
}
|
||||||
+18
@@ -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"
|
||||||
|
}
|
||||||
Vendored
+18
@@ -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"
|
||||||
|
}
|
||||||
+14
@@ -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"
|
||||||
|
}
|
||||||
+24
@@ -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"
|
||||||
|
}
|
||||||
+24
@@ -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"
|
||||||
|
}
|
||||||
Vendored
+31
@@ -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"
|
||||||
|
}
|
||||||
+5
-1
@@ -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
|
||||||
|
|||||||
+8
-1
@@ -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
|
||||||
Vendored
+9
@@ -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
|
||||||
Vendored
+9
@@ -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
|
||||||
+10
-1
@@ -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
|
||||||
Vendored
+1
-1
@@ -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
|
||||||
|
|||||||
+5
-1
@@ -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
|
||||||
|
|||||||
+5
-5
@@ -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
|
||||||
Vendored
+20
@@ -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
|
||||||
Vendored
+35
@@ -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
|
||||||
Vendored
+5
-1
@@ -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
|
||||||
|
|||||||
+32
@@ -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
|
||||||
+10
-1
@@ -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
|
||||||
+53
@@ -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");
|
||||||
|
|||||||
+53
@@ -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");
|
||||||
|
|||||||
+53
@@ -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");
|
||||||
|
|||||||
+15
@@ -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");
|
||||||
|
|||||||
Generated
+53
@@ -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");
|
||||||
|
|||||||
+53
@@ -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");
|
||||||
|
|||||||
js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsLegacyPrimitiveArraysBoxTestGenerated.java
Generated
+33
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user