backend: Don't create a Progression objects in 'for' loops
This patch optimizes the following pattern:
for (i in first..last step st) { ... }
In this case we need to create a Progression object and then call its
iterator() method causing at least 2 allocation per loop. This change
replaces such loops with the following constuction:
var inductionVar = first
checkProgressionStep(step) // check if step > 0
last = getProgressionLastElement(first, last, step)
if (first <= last) {
do {
i = inductionVar
inductionVar += step
...
} while(i != last)
}
This commit is contained in:
+47
-2
@@ -1,7 +1,6 @@
|
|||||||
package org.jetbrains.kotlin.backend.common.ir
|
package org.jetbrains.kotlin.backend.common.ir
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||||
import org.jetbrains.kotlin.backend.common.descriptors.replace
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
@@ -9,11 +8,13 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
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.ir.util.SymbolTable
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.SimpleType
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
// This is what Context collects about IR.
|
// This is what Context collects about IR.
|
||||||
@@ -61,17 +62,42 @@ open class Symbols<out T: CommonBackendContext>(val context: T, private val symb
|
|||||||
) as ClassDescriptor
|
) as ClassDescriptor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val iterator = symbolTable.referenceClass(
|
||||||
|
builtInsPackage("kotlin", "collections").getContributedClassifier(
|
||||||
|
Name.identifier("Iterator"), NoLookupLocation.FROM_BACKEND
|
||||||
|
) as ClassDescriptor)
|
||||||
|
|
||||||
|
private fun progression(name: String) = symbolTable.referenceClass(
|
||||||
|
builtInsPackage("kotlin", "ranges").getContributedClassifier(
|
||||||
|
Name.identifier(name), NoLookupLocation.FROM_BACKEND
|
||||||
|
) as ClassDescriptor
|
||||||
|
)
|
||||||
|
|
||||||
|
val charProgression = progression("CharProgression")
|
||||||
|
val intProgression = progression("IntProgression")
|
||||||
|
val longProgression = progression("LongProgression")
|
||||||
|
val progressionClasses = listOf(charProgression, intProgression, longProgression)
|
||||||
|
val progressionClassesTypes = progressionClasses.map { it.descriptor.defaultType }.toSet()
|
||||||
|
|
||||||
|
val checkProgressionStep = context.getInternalFunctions("checkProgressionStep")
|
||||||
|
.map { Pair(it.returnType, symbolTable.referenceSimpleFunction(it)) }.toMap()
|
||||||
|
val getProgressionBound = context.getInternalFunctions("getProgressionBound")
|
||||||
|
.map { Pair(it.returnType, symbolTable.referenceSimpleFunction(it)) }.toMap()
|
||||||
|
|
||||||
val defaultConstructorMarker = symbolTable.referenceClass(context.getInternalClass("DefaultConstructorMarker"))
|
val defaultConstructorMarker = symbolTable.referenceClass(context.getInternalClass("DefaultConstructorMarker"))
|
||||||
|
|
||||||
val any = symbolTable.referenceClass(builtIns.any)
|
val any = symbolTable.referenceClass(builtIns.any)
|
||||||
val unit = symbolTable.referenceClass(builtIns.unit)
|
val unit = symbolTable.referenceClass(builtIns.unit)
|
||||||
|
|
||||||
|
val char = symbolTable.referenceClass(builtIns.char)
|
||||||
|
|
||||||
val byte = symbolTable.referenceClass(builtIns.byte)
|
val byte = symbolTable.referenceClass(builtIns.byte)
|
||||||
val short = symbolTable.referenceClass(builtIns.short)
|
val short = symbolTable.referenceClass(builtIns.short)
|
||||||
val int = symbolTable.referenceClass(builtIns.int)
|
val int = symbolTable.referenceClass(builtIns.int)
|
||||||
val long = symbolTable.referenceClass(builtIns.long)
|
val long = symbolTable.referenceClass(builtIns.long)
|
||||||
|
|
||||||
val integerClasses = listOf(byte, short, int, long)
|
val integerClasses = listOf(byte, short, int, long)
|
||||||
|
val integerClassesTypes = integerClasses.map { it.descriptor.defaultType }
|
||||||
|
|
||||||
val arrayOf = symbolTable.referenceSimpleFunction(
|
val arrayOf = symbolTable.referenceSimpleFunction(
|
||||||
builtInsPackage("kotlin").getContributedFunctions(
|
builtInsPackage("kotlin").getContributedFunctions(
|
||||||
@@ -143,6 +169,8 @@ open class Symbols<out T: CommonBackendContext>(val context: T, private val symb
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val valuesForEnum = symbolTable.referenceSimpleFunction(
|
val valuesForEnum = symbolTable.referenceSimpleFunction(
|
||||||
context.getInternalFunctions("valuesForEnum").single())
|
context.getInternalFunctions("valuesForEnum").single())
|
||||||
|
|
||||||
@@ -171,4 +199,21 @@ open class Symbols<out T: CommonBackendContext>(val context: T, private val symb
|
|||||||
val kLocalDelegatedPropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedPropertyImpl)
|
val kLocalDelegatedPropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedPropertyImpl)
|
||||||
val kLocalDelegatedMutablePropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedMutablePropertyImpl)
|
val kLocalDelegatedMutablePropertyImpl = symbolTable.referenceClass(context.reflectionTypes.kLocalDelegatedMutablePropertyImpl)
|
||||||
|
|
||||||
|
fun getFunction(name: Name, receiverType: KotlinType, vararg argTypes: KotlinType) =
|
||||||
|
symbolTable.referenceFunction(receiverType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
|
||||||
|
.single {
|
||||||
|
var i = 0
|
||||||
|
it.valueParameters.size == argTypes.size && it.valueParameters.all { type -> type == argTypes[i++] }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getBinaryOperator(name: Name, lhsType: KotlinType, rhsType: KotlinType) =
|
||||||
|
symbolTable.referenceFunction(lhsType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
|
||||||
|
.single { it.valueParameters.size == 1 && it.valueParameters[0].type == rhsType }
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getUnaryOperator(name: Name, receiverType: KotlinType) =
|
||||||
|
symbolTable.referenceFunction(receiverType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND)
|
||||||
|
.single { it.valueParameters.isEmpty() }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
+3
@@ -66,6 +66,9 @@ internal class KonanLower(val context: Context) {
|
|||||||
phaser.phase(KonanPhase.LOWER_DATA_CLASSES) {
|
phaser.phase(KonanPhase.LOWER_DATA_CLASSES) {
|
||||||
DataClassOperatorsLowering(context).runOnFilePostfix(irFile)
|
DataClassOperatorsLowering(context).runOnFilePostfix(irFile)
|
||||||
}
|
}
|
||||||
|
phaser.phase(KonanPhase.LOWER_FOR_LOOPS) {
|
||||||
|
ForLoopsLowering(context).lower(irFile)
|
||||||
|
}
|
||||||
phaser.phase(KonanPhase.LOWER_ENUMS) {
|
phaser.phase(KonanPhase.LOWER_ENUMS) {
|
||||||
EnumClassLowering(context).run(irFile)
|
EnumClassLowering(context).run(irFile)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -32,6 +32,7 @@ enum class KonanPhase(val description: String,
|
|||||||
/* ... ... */ LOWER_INLINE("Functions inlining", LOWER_INLINE_CONSTRUCTORS),
|
/* ... ... */ LOWER_INLINE("Functions inlining", LOWER_INLINE_CONSTRUCTORS),
|
||||||
/* ... ... ... */ DESERIALIZER("Deserialize inline bodies"),
|
/* ... ... ... */ DESERIALIZER("Deserialize inline bodies"),
|
||||||
/* ... ... */ LOWER_INTEROP_PART1("Interop lowering, part 1", LOWER_INLINE),
|
/* ... ... */ LOWER_INTEROP_PART1("Interop lowering, part 1", LOWER_INLINE),
|
||||||
|
/* ... ... */ LOWER_FOR_LOOPS("For loops lowering"),
|
||||||
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
|
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
|
||||||
/* ... ... */ LOWER_DELEGATION("Delegation lowering"),
|
/* ... ... */ LOWER_DELEGATION("Delegation lowering"),
|
||||||
/* ... ... */ LOWER_INITIALIZERS("Initializers lowering", LOWER_ENUMS),
|
/* ... ... */ LOWER_INITIALIZERS("Initializers lowering", LOWER_ENUMS),
|
||||||
|
|||||||
+414
@@ -0,0 +1,414 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||||
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.SimpleType
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isBoolean
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
|
/** This lowering pass optimizes range-based for loops. */
|
||||||
|
internal class ForLoopsLowering(val context: Context) : FileLoweringPass {
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
val transformer = ForLoopsTransformer(context)
|
||||||
|
// Lower loops
|
||||||
|
irFile.transformChildrenVoid(transformer)
|
||||||
|
|
||||||
|
// Update references in break/continue.
|
||||||
|
irFile.transformChildrenVoid(object: IrElementTransformerVoid() {
|
||||||
|
override fun visitBreakContinue(jump: IrBreakContinue): IrExpression {
|
||||||
|
transformer.oldLoopToNewLoop[jump.loop]?.let { jump.loop = it }
|
||||||
|
return jump
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ForLoopsTransformer(val context: Context) : IrElementTransformerVoidWithContext() {
|
||||||
|
|
||||||
|
private val symbols = context.ir.symbols
|
||||||
|
private val iteratorToLoopInfo = mutableMapOf<IrVariableSymbol, ForLoopInfo>()
|
||||||
|
internal val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
|
||||||
|
|
||||||
|
private val iteratorType = symbols.iterator.descriptor.defaultType.replaceArgumentsWithStarProjections()
|
||||||
|
|
||||||
|
private val scopeOwnerSymbol
|
||||||
|
get() = currentScope!!.scope.scopeOwnerSymbol
|
||||||
|
|
||||||
|
private fun irConstOne(startOffset: Int, endOffset: Int) =
|
||||||
|
IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, 1)
|
||||||
|
|
||||||
|
private fun irConstMinusOne(startOffset: Int, endOffset: Int) =
|
||||||
|
IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, -1)
|
||||||
|
|
||||||
|
private val progressionElementClasses: Set<IrClassSymbol> = mutableSetOf(symbols.char).apply {
|
||||||
|
addAll(symbols.integerClasses)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val progressionElementClassesTypes: Set<SimpleType> = mutableSetOf<SimpleType>().apply {
|
||||||
|
progressionElementClasses.mapTo(this) { it.descriptor.defaultType }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Symbols for progression building functions ======================================================================
|
||||||
|
private fun getProgressionBuildingMethods(name: String): Set<IrFunctionSymbol> =
|
||||||
|
getMethodsForProgressionElements(name) {
|
||||||
|
it.valueParameters.size == 1 &&
|
||||||
|
it.valueParameters[0].type in progressionElementClassesTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getProgressionBuildingExtensions(name: String, pkg: FqName): Set<IrFunctionSymbol> =
|
||||||
|
getExtensionsForProgressionElements(name, pkg) {
|
||||||
|
it.extensionReceiverParameter?.type in progressionElementClassesTypes &&
|
||||||
|
it.valueParameters.size == 1 &&
|
||||||
|
it.valueParameters[0].type in progressionElementClassesTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMethodsForProgressionElements(name: String,
|
||||||
|
filter: (SimpleFunctionDescriptor) -> Boolean): Set<IrFunctionSymbol> =
|
||||||
|
mutableSetOf<IrFunctionSymbol>().apply {
|
||||||
|
progressionElementClasses.flatMapTo(this) { receiver ->
|
||||||
|
receiver.descriptor.unsubstitutedMemberScope
|
||||||
|
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
||||||
|
.filter(filter).map { symbols.symbolTable.referenceFunction(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExtensionsForProgressionElements(name: String,
|
||||||
|
pkg: FqName,
|
||||||
|
filter: (SimpleFunctionDescriptor) -> Boolean): Set<IrFunctionSymbol> =
|
||||||
|
mutableSetOf<IrFunctionSymbol>().apply {
|
||||||
|
progressionElementClasses.flatMapTo(this) { receiver ->
|
||||||
|
context.builtIns.builtInsModule.getPackage(pkg).memberScope
|
||||||
|
.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND)
|
||||||
|
.filter(filter).map { symbols.symbolTable.referenceFunction(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val rangeToSymbols by lazy { getProgressionBuildingMethods("rangeTo") }
|
||||||
|
private val untilSymbols by lazy { getProgressionBuildingExtensions("until", FqName("kotlin.ranges")) }
|
||||||
|
private val downToSymbols by lazy { getProgressionBuildingExtensions("downTo", FqName("kotlin.ranges")) }
|
||||||
|
private val stepSymbols by lazy {
|
||||||
|
getExtensionsForProgressionElements("step", FqName("kotlin.ranges")) {
|
||||||
|
it.extensionReceiverParameter?.type in symbols.progressionClassesTypes &&
|
||||||
|
it.valueParameters.size == 1 &&
|
||||||
|
(KotlinBuiltIns.isLong(it.valueParameters[0].type) || KotlinBuiltIns.isInt(it.valueParameters[0].type))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//region Util methods ==============================================================================================
|
||||||
|
private fun IrExpression.castIfNecessary(progressionType: ProgressionType, castToChar: Boolean = true): IrExpression {
|
||||||
|
assert(type in progressionElementClassesTypes)
|
||||||
|
if (type == progressionType.elementType || (!castToChar && KotlinBuiltIns.isChar(progressionType.elementType))) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
return IrCallImpl(startOffset, endOffset, symbols.getFunction(progressionType.numberCastFunctionName, type))
|
||||||
|
.apply { dispatchReceiver = this@castIfNecessary }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrExpression.unaryMinus(): IrExpression =
|
||||||
|
IrCallImpl(startOffset, endOffset, symbols.getUnaryOperator(OperatorNameConventions.UNARY_MINUS, type)).apply {
|
||||||
|
dispatchReceiver = this@unaryMinus
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun irCheckProgressionStep(progressionType: ProgressionType,
|
||||||
|
step: IrExpression): IrExpression {
|
||||||
|
if (step is IrConst<*> &&
|
||||||
|
((step.kind == IrConstKind.Long && step.value as Long > 0) ||
|
||||||
|
(step.kind == IrConstKind.Int && step.value as Int > 0))) {
|
||||||
|
return step
|
||||||
|
}
|
||||||
|
val castedStep = step.castIfNecessary(progressionType, false)
|
||||||
|
val symbol = symbols.checkProgressionStep[castedStep.type]
|
||||||
|
?: throw IllegalArgumentException("Unknown progression element type: ${step.type}")
|
||||||
|
return IrCallImpl(step.startOffset, step.endOffset, symbol).apply {
|
||||||
|
putValueArgument(0, castedStep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun irGetProgressionBound(progressionType: ProgressionType,
|
||||||
|
first: IrVariableSymbol,
|
||||||
|
lastExpression: IrExpression,
|
||||||
|
step: IrVariableSymbol): IrExpression {
|
||||||
|
val symbol = symbols.getProgressionBound[progressionType.elementType]
|
||||||
|
?: throw IllegalArgumentException("Unknown progression element type: ${lastExpression.type}")
|
||||||
|
val startOffset = lastExpression.startOffset
|
||||||
|
val endOffset = lastExpression.endOffset
|
||||||
|
return IrCallImpl(startOffset, lastExpression.endOffset, symbol).apply {
|
||||||
|
putValueArgument(0, IrGetValueImpl(startOffset, endOffset, first))
|
||||||
|
putValueArgument(1, lastExpression.castIfNecessary(progressionType))
|
||||||
|
putValueArgument(2, IrGetValueImpl(startOffset, endOffset, step))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region Util classes ====================================================================================================
|
||||||
|
// TODO: Replace with a cast when such support is added in the boxing lowering.
|
||||||
|
private data class ProgressionType(val elementType: KotlinType,
|
||||||
|
val numberCastFunctionName: Name)
|
||||||
|
|
||||||
|
private data class ProgressionInfo(
|
||||||
|
val progressionType: ProgressionType,
|
||||||
|
val first: IrExpression,
|
||||||
|
val last: IrExpression,
|
||||||
|
val step: IrExpression? = null,
|
||||||
|
val increasing: Boolean = true)
|
||||||
|
|
||||||
|
/** Contains information about variables used in the loop. */
|
||||||
|
private data class ForLoopInfo(
|
||||||
|
val progressionInfo: ProgressionInfo,
|
||||||
|
val inductionVariable: IrVariableSymbol,
|
||||||
|
val bound: IrVariableSymbol,
|
||||||
|
val step: IrVariableSymbol,
|
||||||
|
var loopVariable: IrVariableSymbol? = null)
|
||||||
|
|
||||||
|
private inner class ProgressionInfoBuilder : IrElementVisitor<ProgressionInfo?, Nothing?> {
|
||||||
|
|
||||||
|
val INT_PROGRESSION = ProgressionType(context.builtIns.intType, Name.identifier("toInt"))
|
||||||
|
val LONG_PROGRESSION = ProgressionType(context.builtIns.longType, Name.identifier("toLong"))
|
||||||
|
val CHAR_PROGRESSION = ProgressionType(context.builtIns.charType, Name.identifier("toChar"))
|
||||||
|
|
||||||
|
private fun buildRangeTo(expression: IrCall, progressionType: ProgressionType) =
|
||||||
|
ProgressionInfo(progressionType, expression.dispatchReceiver!!, expression.getValueArgument(0)!!)
|
||||||
|
|
||||||
|
private fun buildUntil(expression: IrCall, progressionType: ProgressionType): ProgressionInfo {
|
||||||
|
val firstExpression = expression.extensionReceiver!!
|
||||||
|
val lastExpression = expression.getValueArgument(0)!!
|
||||||
|
val decrementSymbol = symbols.getUnaryOperator(OperatorNameConventions.DEC, lastExpression.type)
|
||||||
|
val decrementCall = IrCallImpl(expression.startOffset, expression.endOffset, decrementSymbol).apply {
|
||||||
|
dispatchReceiver = lastExpression
|
||||||
|
}
|
||||||
|
return ProgressionInfo(progressionType, firstExpression, decrementCall)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildDownTo(expression: IrCall, progressionType: ProgressionType) =
|
||||||
|
ProgressionInfo(progressionType, expression.extensionReceiver!!, expression.getValueArgument(0)!!, increasing = false)
|
||||||
|
|
||||||
|
private fun buildStep(expression: IrCall, progressionType: ProgressionType) =
|
||||||
|
expression.extensionReceiver!!.accept(this, null)?.let {
|
||||||
|
val newStep = expression.getValueArgument(0)!!
|
||||||
|
val step = when {
|
||||||
|
it.step == null -> irCheckProgressionStep(progressionType, newStep)
|
||||||
|
// There were step calls before. Just add our check in the container or create a new one.
|
||||||
|
it.step is IrStatementContainer -> {
|
||||||
|
it.step.statements.add(irCheckProgressionStep(progressionType, newStep)); it.step
|
||||||
|
}
|
||||||
|
else -> IrCompositeImpl(expression.startOffset, expression.endOffset, newStep.type).apply {
|
||||||
|
statements.add(it.step)
|
||||||
|
statements.add(irCheckProgressionStep(progressionType, newStep))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProgressionInfo(progressionType, it.first, it.last, step, it.increasing)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitElement(element: IrElement, data: Nothing?): ProgressionInfo? = null
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall, data: Nothing?): ProgressionInfo? {
|
||||||
|
val type = expression.type
|
||||||
|
val progressionType = when {
|
||||||
|
type.isSubtypeOf(symbols.charProgression.descriptor.defaultType) -> CHAR_PROGRESSION
|
||||||
|
type.isSubtypeOf(symbols.intProgression.descriptor.defaultType) -> INT_PROGRESSION
|
||||||
|
type.isSubtypeOf(symbols.longProgression.descriptor.defaultType) -> LONG_PROGRESSION
|
||||||
|
else -> return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Process constructors and other factory functions.
|
||||||
|
return when (expression.symbol) {
|
||||||
|
in rangeToSymbols -> buildRangeTo(expression, progressionType)
|
||||||
|
in untilSymbols -> buildUntil(expression, progressionType)
|
||||||
|
in downToSymbols -> buildDownTo(expression, progressionType)
|
||||||
|
in stepSymbols -> buildStep(expression, progressionType)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
// Lowering ========================================================================================================
|
||||||
|
// Lower a loop header.
|
||||||
|
private fun processHeader(variable: IrVariable, initializer: IrCall): IrStatement? {
|
||||||
|
val symbol = variable.symbol
|
||||||
|
if (!variable.descriptor.type.isSubtypeOf(iteratorType)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
assert(symbol !in iteratorToLoopInfo)
|
||||||
|
|
||||||
|
val builder = context.createIrBuilder(scopeOwnerSymbol, variable.startOffset, variable.endOffset)
|
||||||
|
// Collect loop info and form the loop header block.
|
||||||
|
val progressionInfo = initializer.dispatchReceiver?.accept(ProgressionInfoBuilder(), null) ?: return null
|
||||||
|
return builder.irBlock {
|
||||||
|
with(progressionInfo) {
|
||||||
|
val inductionVariable = irTemporaryVar(first.castIfNecessary(progressionType), "inductionVariable")
|
||||||
|
val stepExpression = if (increasing) { // TODO: Remove the casts.
|
||||||
|
step ?: irConstOne(startOffset, endOffset).castIfNecessary(progressionType, false)
|
||||||
|
} else {
|
||||||
|
step?.unaryMinus() ?: irConstMinusOne(startOffset, endOffset).castIfNecessary(progressionType, false)
|
||||||
|
}
|
||||||
|
val stepValue = irTemporary(stepExpression, "step")
|
||||||
|
// TODO: Don't call the check it step is 1 or -1
|
||||||
|
val boundExpression = irGetProgressionBound(progressionType, inductionVariable.symbol, last, stepValue.symbol)
|
||||||
|
val boundValue = irTemporary(boundExpression, "bound")
|
||||||
|
iteratorToLoopInfo[symbol] = ForLoopInfo(progressionInfo,
|
||||||
|
inductionVariable.symbol,
|
||||||
|
boundValue.symbol,
|
||||||
|
stepValue.symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Lower getting a next induction variable value.
|
||||||
|
private fun processNext(variable: IrVariable, initializer: IrCall): IrExpression? {
|
||||||
|
val irIteratorAccess = initializer.dispatchReceiver as? IrGetValue ?: throw AssertionError()
|
||||||
|
val forLoopInfo = iteratorToLoopInfo[irIteratorAccess.symbol] ?: return null // If we didn't lower a corresponding header.
|
||||||
|
val builder = context.createIrBuilder(scopeOwnerSymbol, initializer.startOffset, initializer.endOffset)
|
||||||
|
|
||||||
|
// TODO: Cache it.
|
||||||
|
val plusOperator = symbols.getBinaryOperator(
|
||||||
|
OperatorNameConventions.PLUS,
|
||||||
|
forLoopInfo.inductionVariable.descriptor.type,
|
||||||
|
forLoopInfo.step.descriptor.type
|
||||||
|
)
|
||||||
|
forLoopInfo.loopVariable = variable.symbol
|
||||||
|
|
||||||
|
with(builder) {
|
||||||
|
variable.initializer = irGet(forLoopInfo.inductionVariable)
|
||||||
|
val increment = irSetVar(forLoopInfo.inductionVariable,
|
||||||
|
irCallOp(plusOperator, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.step)))
|
||||||
|
return IrCompositeImpl(variable.startOffset,
|
||||||
|
variable.endOffset,
|
||||||
|
context.irBuiltIns.unit,
|
||||||
|
IrStatementOrigin.FOR_LOOP_NEXT,
|
||||||
|
listOf(variable, increment))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DeclarationIrBuilder.buildEmptyCheck(loop: IrLoop, forLoopInfo: ForLoopInfo): IrExpression {
|
||||||
|
val increasing = forLoopInfo.progressionInfo.increasing
|
||||||
|
val comparingBuiltIn = if (increasing) context.irBuiltIns.lteq0Symbol else context.irBuiltIns.gteq0Symbol
|
||||||
|
|
||||||
|
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
|
||||||
|
forLoopInfo.inductionVariable.descriptor.type,
|
||||||
|
forLoopInfo.bound.descriptor.type)
|
||||||
|
|
||||||
|
val check = irCall(comparingBuiltIn).apply {
|
||||||
|
putValueArgument(0, irCallOp(compareTo, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.bound)))
|
||||||
|
}
|
||||||
|
return irIfThen(check, loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DeclarationIrBuilder.buildNewCondition(oldCondition: IrExpression): Pair<IrExpression, ForLoopInfo>? {
|
||||||
|
if (oldCondition !is IrCall || oldCondition.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val irIteratorAccess = oldCondition.dispatchReceiver as? IrGetValue ?: throw AssertionError()
|
||||||
|
// Return null if we didn't lower a corresponding header.
|
||||||
|
val forLoopInfo = iteratorToLoopInfo[irIteratorAccess.symbol] ?: return null
|
||||||
|
val loopVariable = forLoopInfo.loopVariable!! // TODO: Check!
|
||||||
|
|
||||||
|
return irCall(context.irBuiltIns.booleanNotSymbol).apply {
|
||||||
|
val eqeqCall = irCall(context.irBuiltIns.eqeqSymbol).apply {
|
||||||
|
putValueArgument(0, irGet(loopVariable))
|
||||||
|
putValueArgument(1, irGet(forLoopInfo.bound))
|
||||||
|
}
|
||||||
|
putValueArgument(0, eqeqCall)
|
||||||
|
} to forLoopInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This loop
|
||||||
|
*
|
||||||
|
* for (i in first..last step foo) { ... }
|
||||||
|
*
|
||||||
|
* is represented in IR in such a manner:
|
||||||
|
*
|
||||||
|
* val it = (first..last step foo).iterator()
|
||||||
|
* while (it.hasNext()) {
|
||||||
|
* val i = it.next()
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* We transform it into the following loop:
|
||||||
|
*
|
||||||
|
* var it = first
|
||||||
|
* if (it <= last) { // (it >= last if the progression is decreasing)
|
||||||
|
* do {
|
||||||
|
* val i = it++
|
||||||
|
* ...
|
||||||
|
* } while (i != last)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
override fun visitWhileLoop(loop: IrWhileLoop): IrExpression {
|
||||||
|
if (loop.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) {
|
||||||
|
return super.visitWhileLoop(loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
with(context.createIrBuilder(scopeOwnerSymbol)) {
|
||||||
|
// Transform accesses to the old iterator (see visitVariable method). Store loopVariable in loopInfo.
|
||||||
|
val newBody = loop.body?.transform(this@ForLoopsTransformer, null)
|
||||||
|
val (newCondition, forLoopInfo) = buildNewCondition(loop.condition) ?: return super.visitWhileLoop(loop)
|
||||||
|
assert(forLoopInfo.loopVariable != null)
|
||||||
|
|
||||||
|
val newLoop = IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
|
||||||
|
label = loop.label
|
||||||
|
condition = newCondition
|
||||||
|
body = newBody
|
||||||
|
}
|
||||||
|
oldLoopToNewLoop[loop] = newLoop
|
||||||
|
// Build a check for an empty progression before the loop.
|
||||||
|
return buildEmptyCheck(newLoop, forLoopInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||||
|
val initializer = declaration.initializer
|
||||||
|
if (initializer == null || initializer !is IrCall) {
|
||||||
|
return super.visitVariable(declaration)
|
||||||
|
}
|
||||||
|
val result = when (initializer.origin) {
|
||||||
|
IrStatementOrigin.FOR_LOOP_ITERATOR -> processHeader(declaration, initializer)
|
||||||
|
IrStatementOrigin.FOR_LOOP_NEXT -> processNext(declaration, initializer)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
return result ?: super.visitVariable(declaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -307,6 +307,29 @@ task sum_3const(type: RunKonanTest) {
|
|||||||
source = "codegen/function/sum_3const.kt"
|
source = "codegen/function/sum_3const.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task codegen_basics_for_loops(type: RunKonanTest) {
|
||||||
|
source = "codegen/basics/for_loops.kt"
|
||||||
|
goldValue = "01234\n0123\n43210\n\n024\n02\n420\n\n036\n03\n630\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
task codegen_basics_for_loops_types(type: RunKonanTest) {
|
||||||
|
source = "codegen/basics/for_loops_types.kt"
|
||||||
|
goldValue = "01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n01234\n" +
|
||||||
|
"01234\n01234\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n0123\n" +
|
||||||
|
"0123\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n43210\n" +
|
||||||
|
"43210\n43210\nabcd\nabc\ndcba\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
task codegen_basics_for_loops_overflow(type: RunKonanTest) {
|
||||||
|
source = "codegen/basics/for_loops_overflow.kt"
|
||||||
|
goldValue = "2147483646 2147483647 \n2147483646 \n-2147483647 -2147483648 \n1073741827 \n"
|
||||||
|
}
|
||||||
|
|
||||||
|
task codegen_basics_for_loops_errors(type: RunKonanTest) {
|
||||||
|
source = "codegen/basics/for_loops_errors.kt"
|
||||||
|
goldValue = "OK\n"
|
||||||
|
}
|
||||||
|
|
||||||
task local_variable(type: RunKonanTest) {
|
task local_variable(type: RunKonanTest) {
|
||||||
source = "codegen/basics/local_variable.kt"
|
source = "codegen/basics/local_variable.kt"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
|
||||||
|
// Simple loops
|
||||||
|
for (i in 0..4) {
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
|
||||||
|
for (i in 0 until 4) {
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
|
||||||
|
for (i in 4 downTo 0) {
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
println()
|
||||||
|
|
||||||
|
// Steps
|
||||||
|
for (i in 0..4 step 2) {
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
|
||||||
|
for (i in 0 until 4 step 2) {
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
|
||||||
|
for (i in 4 downTo 0 step 2) {
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
println()
|
||||||
|
|
||||||
|
|
||||||
|
// Two steps
|
||||||
|
for (i in 0..6 step 2 step 3) {
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
|
||||||
|
for (i in 0 until 6 step 2 step 3) {
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
|
||||||
|
for (i in 6 downTo 0 step 2 step 3) {
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
// Negative step.
|
||||||
|
try {
|
||||||
|
for (i in 0 .. 4 step -2) print(i); println()
|
||||||
|
throw AssertionError()
|
||||||
|
} catch (e: IllegalArgumentException) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (i in 0 until 4 step -2) print(i); println()
|
||||||
|
throw AssertionError()
|
||||||
|
} catch (e: IllegalArgumentException) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (i in 4 downTo 0 step -2) print(i); println()
|
||||||
|
throw AssertionError()
|
||||||
|
} catch (e: IllegalArgumentException) {}
|
||||||
|
|
||||||
|
// Zero step.
|
||||||
|
try {
|
||||||
|
for (i in 0 .. 4 step 0) print(i); println()
|
||||||
|
throw AssertionError()
|
||||||
|
} catch (e: IllegalArgumentException) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (i in 0 until 4 step 0) print(i); println()
|
||||||
|
throw AssertionError()
|
||||||
|
} catch (e: IllegalArgumentException) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (i in 4 downTo 0 step 0) print(i); println()
|
||||||
|
throw AssertionError()
|
||||||
|
} catch (e: IllegalArgumentException) {}
|
||||||
|
|
||||||
|
// Two steps, one is negative.
|
||||||
|
try {
|
||||||
|
for (i in 0 .. 4 step -2 step 3) print(i); println()
|
||||||
|
throw AssertionError()
|
||||||
|
} catch (e: IllegalArgumentException) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (i in 0 until 4 step -2 step 3) print(i); println()
|
||||||
|
throw AssertionError()
|
||||||
|
} catch (e: IllegalArgumentException) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (i in 4 downTo 0 step -2 step 3) print(i); println()
|
||||||
|
throw AssertionError()
|
||||||
|
} catch (e: IllegalArgumentException) {}
|
||||||
|
|
||||||
|
println("OK")
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
for (i in Int.MAX_VALUE - 1 .. Int.MAX_VALUE) { print(i); print(' ') }; println()
|
||||||
|
for (i in Int.MAX_VALUE - 1 until Int.MAX_VALUE) { print(i); print(' ') }; println()
|
||||||
|
for (i in Int.MIN_VALUE + 1 downTo Int.MIN_VALUE) { print(i); print(' ') }; println()
|
||||||
|
|
||||||
|
val M = Int.MAX_VALUE / 2
|
||||||
|
for (i in M + 4..M + 10 step M) { print(i); print(' ') }; println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
fun main(args: Array<String>) {
|
||||||
|
for (i in 0.toByte() .. 4.toByte()) print(i); println()
|
||||||
|
for (i in 0.toByte() .. 4.toShort()) print(i); println()
|
||||||
|
for (i in 0.toByte() .. 4.toInt()) print(i); println()
|
||||||
|
for (i in 0.toByte() .. 4.toLong()) print(i); println()
|
||||||
|
for (i in 0.toShort() .. 4.toByte()) print(i); println()
|
||||||
|
for (i in 0.toShort() .. 4.toShort()) print(i); println()
|
||||||
|
for (i in 0.toShort() .. 4.toInt()) print(i); println()
|
||||||
|
for (i in 0.toShort() .. 4.toLong()) print(i); println()
|
||||||
|
for (i in 0.toInt() .. 4.toByte()) print(i); println()
|
||||||
|
for (i in 0.toInt() .. 4.toShort()) print(i); println()
|
||||||
|
for (i in 0.toInt() .. 4.toInt()) print(i); println()
|
||||||
|
for (i in 0.toInt() .. 4.toLong()) print(i); println()
|
||||||
|
for (i in 0.toLong() .. 4.toByte()) print(i); println()
|
||||||
|
for (i in 0.toLong() .. 4.toShort()) print(i); println()
|
||||||
|
for (i in 0.toLong() .. 4.toInt()) print(i); println()
|
||||||
|
for (i in 0.toLong() .. 4.toLong()) print(i); println()
|
||||||
|
for (i in 0.toByte() until 4.toByte()) print(i); println()
|
||||||
|
for (i in 0.toByte() until 4.toShort()) print(i); println()
|
||||||
|
for (i in 0.toByte() until 4.toInt()) print(i); println()
|
||||||
|
for (i in 0.toByte() until 4.toLong()) print(i); println()
|
||||||
|
for (i in 0.toShort() until 4.toByte()) print(i); println()
|
||||||
|
for (i in 0.toShort() until 4.toShort()) print(i); println()
|
||||||
|
for (i in 0.toShort() until 4.toInt()) print(i); println()
|
||||||
|
for (i in 0.toShort() until 4.toLong()) print(i); println()
|
||||||
|
for (i in 0.toInt() until 4.toByte()) print(i); println()
|
||||||
|
for (i in 0.toInt() until 4.toShort()) print(i); println()
|
||||||
|
for (i in 0.toInt() until 4.toInt()) print(i); println()
|
||||||
|
for (i in 0.toInt() until 4.toLong()) print(i); println()
|
||||||
|
for (i in 0.toLong() until 4.toByte()) print(i); println()
|
||||||
|
for (i in 0.toLong() until 4.toShort()) print(i); println()
|
||||||
|
for (i in 0.toLong() until 4.toInt()) print(i); println()
|
||||||
|
for (i in 0.toLong() until 4.toLong()) print(i); println()
|
||||||
|
for (i in 4.toByte() downTo 0.toByte()) print(i); println()
|
||||||
|
for (i in 4.toByte() downTo 0.toShort()) print(i); println()
|
||||||
|
for (i in 4.toByte() downTo 0.toInt()) print(i); println()
|
||||||
|
for (i in 4.toByte() downTo 0.toLong()) print(i); println()
|
||||||
|
for (i in 4.toShort() downTo 0.toByte()) print(i); println()
|
||||||
|
for (i in 4.toShort() downTo 0.toShort()) print(i); println()
|
||||||
|
for (i in 4.toShort() downTo 0.toInt()) print(i); println()
|
||||||
|
for (i in 4.toShort() downTo 0.toLong()) print(i); println()
|
||||||
|
for (i in 4.toInt() downTo 0.toByte()) print(i); println()
|
||||||
|
for (i in 4.toInt() downTo 0.toShort()) print(i); println()
|
||||||
|
for (i in 4.toInt() downTo 0.toInt()) print(i); println()
|
||||||
|
for (i in 4.toInt() downTo 0.toLong()) print(i); println()
|
||||||
|
for (i in 4.toLong() downTo 0.toByte()) print(i); println()
|
||||||
|
for (i in 4.toLong() downTo 0.toShort()) print(i); println()
|
||||||
|
for (i in 4.toLong() downTo 0.toInt()) print(i); println()
|
||||||
|
for (i in 4.toLong() downTo 0.toLong()) print(i); println()
|
||||||
|
for (i in 'a' .. 'd') print(i); println()
|
||||||
|
for (i in 'a' until 'd') print(i); println()
|
||||||
|
for (i in 'd' downTo 'a') print(i); println()
|
||||||
|
|
||||||
|
// TODO: Add step tests.
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package konan.internal
|
package konan.internal
|
||||||
|
|
||||||
|
import kotlin.internal.getProgressionLastElement
|
||||||
|
|
||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
fun ThrowNullPointerException(): Nothing {
|
fun ThrowNullPointerException(): Nothing {
|
||||||
throw NullPointerException()
|
throw NullPointerException()
|
||||||
@@ -84,4 +86,13 @@ fun <T: Enum<T>> valuesForEnum(values: Array<T>): Array<T>
|
|||||||
result[value.ordinal] = value
|
result[value.ordinal] = value
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return result as Array<T>
|
return result as Array<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkProgressionStep(step: Int) = if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")
|
||||||
|
fun checkProgressionStep(step: Long) = if (step > 0) step else throw IllegalArgumentException("Step must be positive, was: $step.")
|
||||||
|
|
||||||
|
fun getProgressionBound(start: Char, end: Char, step: Int): Char =
|
||||||
|
getProgressionBound(start.toInt(), end.toInt(), step).toChar()
|
||||||
|
|
||||||
|
fun getProgressionBound(start: Int, end: Int, step: Int): Int = getProgressionLastElement(start, end, step)
|
||||||
|
fun getProgressionBound(start: Long, end: Long, step: Long): Long = getProgressionLastElement(start, end, step)
|
||||||
Reference in New Issue
Block a user