ForLoopsLowering: Add support for unsigned progressions.

This commit is contained in:
Mark Punzalan
2020-04-02 16:32:14 -07:00
committed by Alexander Udalov
parent 9fdb1ff4f8
commit 03ef3724f4
9 changed files with 313 additions and 158 deletions
@@ -64,9 +64,13 @@ open class BuiltinSymbolsBase(protected val irBuiltIns: IrBuiltIns, protected va
) )
private fun getClass(name: Name, vararg packageNameSegments: String = arrayOf("kotlin")): IrClassSymbol = private fun getClass(name: Name, vararg packageNameSegments: String = arrayOf("kotlin")): IrClassSymbol =
symbolTable.referenceClass( getClassOrNull(name, *packageNameSegments) ?: error("Class '$name' not found in package '${packageNameSegments.joinToString(".")}'")
builtInsPackage(*packageNameSegments).getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
) private fun getClassOrNull(name: Name, vararg packageNameSegments: String = arrayOf("kotlin")): IrClassSymbol? =
(builtInsPackage(*packageNameSegments).getContributedClassifier(
name,
NoLookupLocation.FROM_BACKEND
) as? ClassDescriptor)?.let { symbolTable.referenceClass(it) }
/** /**
* Use this table to reference external dependencies. * Use this table to reference external dependencies.
@@ -89,11 +93,22 @@ open class BuiltinSymbolsBase(protected val irBuiltIns: IrBuiltIns, protected va
.map { symbolTable.referenceFunction(it) } .map { symbolTable.referenceFunction(it) }
private fun progression(name: String) = getClass(Name.identifier(name), "kotlin", "ranges") private fun progression(name: String) = getClass(Name.identifier(name), "kotlin", "ranges")
private fun progressionOrNull(name: String) = getClassOrNull(Name.identifier(name), "kotlin", "ranges")
// The "...OrNull" variants are used for unsigned (and progressions) because the minimal stdlib used in tests do not include those
// those classes. It was not feasible to add them to the JS reduced runtime because all its transitive dependencies also need to be
// added, which would include a lot of the full stdlib.
open val uByte = getClassOrNull(Name.identifier("UByte"), "kotlin")
open val uShort = getClassOrNull(Name.identifier("UShort"), "kotlin")
open val uInt = getClassOrNull(Name.identifier("UInt"), "kotlin")
open val uLong = getClassOrNull(Name.identifier("ULong"), "kotlin")
val uIntProgression = progressionOrNull("UIntProgression")
val uLongProgression = progressionOrNull("ULongProgression")
val charProgression = progression("CharProgression") val charProgression = progression("CharProgression")
val intProgression = progression("IntProgression") val intProgression = progression("IntProgression")
val longProgression = progression("LongProgression") val longProgression = progression("LongProgression")
val progressionClasses = listOf(charProgression, intProgression, longProgression) val progressionClasses = listOfNotNull(charProgression, intProgression, longProgression, uIntProgression, uLongProgression)
val progressionClassesTypes = progressionClasses.map { it.descriptor.defaultType }.toSet() val progressionClassesTypes = progressionClasses.map { it.descriptor.defaultType }.toSet()
val getProgressionLastElementByReturnType = builtInsPackage("kotlin", "internal") val getProgressionLastElementByReturnType = builtInsPackage("kotlin", "internal")
@@ -105,6 +120,18 @@ open class BuiltinSymbolsBase(protected val irBuiltIns: IrBuiltIns, protected va
c to f c to f
}.toMap() }.toMap()
val toUIntByExtensionReceiver = builtInsPackage("kotlin").getContributedFunctions(
Name.identifier("toUInt"),
NoLookupLocation.FROM_BACKEND
).filter { it.containingDeclaration !is BuiltInsPackageFragment && it.extensionReceiverParameter != null }
.map { Pair(it.extensionReceiverParameter!!.type, symbolTable.referenceSimpleFunction(it)) }.toMap()
val toULongByExtensionReceiver = builtInsPackage("kotlin").getContributedFunctions(
Name.identifier("toULong"),
NoLookupLocation.FROM_BACKEND
).filter { it.containingDeclaration !is BuiltInsPackageFragment && it.extensionReceiverParameter != null }
.map { Pair(it.extensionReceiverParameter!!.type, symbolTable.referenceSimpleFunction(it)) }.toMap()
val any = symbolTable.referenceClass(builtIns.any) val any = symbolTable.referenceClass(builtIns.any)
val unit = symbolTable.referenceClass(builtIns.unit) val unit = symbolTable.referenceClass(builtIns.unit)
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.common.lower.loops
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
@@ -238,7 +237,7 @@ private class RangeLoopTransformer(
mainLoopVariable.endOffset, mainLoopVariable.endOffset,
context.irBuiltIns.unitType, context.irBuiltIns.unitType,
IrStatementOrigin.FOR_LOOP_NEXT, IrStatementOrigin.FOR_LOOP_NEXT,
loopHeader.initializeIteration(mainLoopVariable, loopVariableComponents, symbols, this) loopHeader.initializeIteration(mainLoopVariable, loopVariableComponents, this)
) )
} }
@@ -14,46 +14,77 @@ 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.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.*
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 UIntProgression, ULongProgression
/** Represents a progression type in the Kotlin stdlib. */ /** Represents a progression type in the Kotlin stdlib. */
internal enum class ProgressionType(val elementCastFunctionName: Name, val stepCastFunctionName: Name) { internal enum class ProgressionType(
private val elementCastFunctionName: Name,
private val stepCastFunctionName: Name,
val isLong: Boolean = false,
val isUnsigned: Boolean = false
) {
INT_PROGRESSION(Name.identifier("toInt"), Name.identifier("toInt")), INT_PROGRESSION(Name.identifier("toInt"), Name.identifier("toInt")),
LONG_PROGRESSION(Name.identifier("toLong"), Name.identifier("toLong")), LONG_PROGRESSION(Name.identifier("toLong"), Name.identifier("toLong"), isLong = true),
CHAR_PROGRESSION(Name.identifier("toChar"), Name.identifier("toInt")); CHAR_PROGRESSION(Name.identifier("toChar"), Name.identifier("toInt")),
UINT_PROGRESSION(Name.identifier("toUInt"), Name.identifier("toInt"), isUnsigned = true),
ULONG_PROGRESSION(Name.identifier("toULong"), Name.identifier("toLong"), isLong = true, isUnsigned = true);
/** Returns the [IrType] of the `first`/`last` properties and elements in the progression. */ /** Returns the [IrType] of the `first`/`last` properties and elements in the progression. */
fun elementType(builtIns: IrBuiltIns): IrType = when (this) { fun elementType(symbols: Symbols<CommonBackendContext>): IrType = elementClassifier(symbols).defaultType
INT_PROGRESSION -> builtIns.intType
LONG_PROGRESSION -> builtIns.longType /** Returns the [IrClassSymbol] of the `first`/`last` properties and elements in the progression. */
CHAR_PROGRESSION -> builtIns.charType fun elementClassifier(symbols: Symbols<CommonBackendContext>): IrClassSymbol = when (this) {
INT_PROGRESSION -> symbols.int
LONG_PROGRESSION -> symbols.long
CHAR_PROGRESSION -> symbols.char
UINT_PROGRESSION -> symbols.uInt!!
ULONG_PROGRESSION -> symbols.uLong!!
} }
/** Returns the [IrType] of the `step` property in the progression. */ /** Returns the [IrType] of the `step` property in the progression. */
fun stepType(builtIns: IrBuiltIns): IrType = when (this) { fun stepType(builtIns: IrBuiltIns): IrType = when (this) {
INT_PROGRESSION, CHAR_PROGRESSION -> builtIns.intType INT_PROGRESSION, CHAR_PROGRESSION, UINT_PROGRESSION -> builtIns.intType
LONG_PROGRESSION -> builtIns.longType LONG_PROGRESSION, ULONG_PROGRESSION -> builtIns.longType
} }
/** Returns the [IrClassSymbol] of the `step` property type constructor in the progression. */ /** Returns the [IrClassSymbol] of the `step` property type constructor in the progression. */
fun stepClassifier(builtIns: IrBuiltIns): IrClassSymbol = when(this) { fun stepClassifier(builtIns: IrBuiltIns): IrClassSymbol = when(this) {
INT_PROGRESSION, CHAR_PROGRESSION -> builtIns.intClass INT_PROGRESSION, CHAR_PROGRESSION , UINT_PROGRESSION-> builtIns.intClass
LONG_PROGRESSION -> builtIns.longClass LONG_PROGRESSION, ULONG_PROGRESSION -> builtIns.longClass
} }
fun castElementIfNecessary(element: IrExpression, context: CommonBackendContext) =
element.castIfNecessary(elementType(context.ir.symbols), elementCastFunctionName)
fun castStepIfNecessary(step: IrExpression, context: CommonBackendContext) =
step.castIfNecessary(stepType(context.irBuiltIns), stepCastFunctionName)
private fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name) =
// This expression's type could be Nothing from an exception throw.
if (type == targetType || type.isNothing()) {
this
} else {
val castFun = type.getClass()!!.functions.single {
it.name == numberCastFunctionName &&
it.dispatchReceiverParameter != null && it.extensionReceiverParameter == null && it.valueParameters.isEmpty()
}
IrCallImpl(startOffset, endOffset, castFun.returnType, castFun.symbol)
.apply { dispatchReceiver = this@castIfNecessary }
}
companion object { companion object {
fun fromIrType(irType: IrType, symbols: Symbols<CommonBackendContext>): ProgressionType? = when { fun fromIrType(irType: IrType, symbols: Symbols<CommonBackendContext>): ProgressionType? = when {
irType.isSubtypeOfClass(symbols.charProgression) -> CHAR_PROGRESSION irType.isSubtypeOfClass(symbols.charProgression) -> CHAR_PROGRESSION
irType.isSubtypeOfClass(symbols.intProgression) -> INT_PROGRESSION irType.isSubtypeOfClass(symbols.intProgression) -> INT_PROGRESSION
irType.isSubtypeOfClass(symbols.longProgression) -> LONG_PROGRESSION irType.isSubtypeOfClass(symbols.longProgression) -> LONG_PROGRESSION
symbols.uIntProgression != null && irType.isSubtypeOfClass(symbols.uIntProgression) -> UINT_PROGRESSION
symbols.uLongProgression != null && irType.isSubtypeOfClass(symbols.uLongProgression) -> ULONG_PROGRESSION
else -> null else -> null
} }
} }
@@ -113,12 +144,18 @@ internal class ProgressionHeaderInfo(
additionalNotEmptyCondition = additionalNotEmptyCondition additionalNotEmptyCondition = additionalNotEmptyCondition
) { ) {
@ExperimentalUnsignedTypes
val canOverflow: Boolean by lazy { val canOverflow: Boolean by lazy {
if (canOverflow != null) return@lazy canOverflow if (canOverflow != null) return@lazy canOverflow
// We can't determine the safe limit at compile-time if "step" is not const. // We can't determine the safe limit at compile-time if "step" is not const.
val stepValueAsLong = step.constLongValue ?: return@lazy true val stepValueAsLong = step.constLongValue ?: return@lazy true
if (direction == ProgressionDirection.UNKNOWN) {
// If we don't know the direction, we can't be sure which limit to use.
return@lazy true
}
// Induction variable can NOT overflow if "last" is const and is <= (MAX/MIN_VALUE - step) (depending on direction). // Induction variable can NOT overflow if "last" is const and is <= (MAX/MIN_VALUE - step) (depending on direction).
// //
// Examples that can NOT overflow: // Examples that can NOT overflow:
@@ -137,26 +174,51 @@ internal class ProgressionHeaderInfo(
// - `0..10 step someStep()` CAN overflow (we don't know the step and hence can't determine the safe limit) // - `0..10 step someStep()` CAN overflow (we don't know the step and hence can't determine the safe limit)
// - `0..someLast()` CAN overflow (we don't know the direction) // - `0..someLast()` CAN overflow (we don't know the direction)
// - `someProgression()` CAN overflow (we don't know the direction) // - `someProgression()` CAN overflow (we don't know the direction)
val lastValueAsLong = last.constLongValue ?: return@lazy true // If "last" is not a const Number or Char.
when (direction) { if (progressionType.isUnsigned) {
ProgressionDirection.UNKNOWN -> // "step" is still signed for unsigned progressions.
// If we don't know the direction, we can't be sure which limit to use. val lastValueAsULong = last.constLongValue?.toULong() ?: return@lazy true // If "last" is not a const Number or Char.
true when (direction) {
ProgressionDirection.DECREASING -> { ProgressionDirection.DECREASING -> {
val constLimitAsLong = when (progressionType) { val constLimitAsULong = when (progressionType) {
ProgressionType.INT_PROGRESSION -> Int.MIN_VALUE.toLong() ProgressionType.UINT_PROGRESSION -> UInt.MIN_VALUE.toULong()
ProgressionType.CHAR_PROGRESSION -> Char.MIN_VALUE.toLong() ProgressionType.ULONG_PROGRESSION -> ULong.MIN_VALUE
ProgressionType.LONG_PROGRESSION -> Long.MIN_VALUE else -> error("Unexpected progression type")
}
lastValueAsULong < (constLimitAsULong - stepValueAsLong.toULong())
} }
lastValueAsLong < (constLimitAsLong - stepValueAsLong) ProgressionDirection.INCREASING -> {
val constLimitAsULong = when (progressionType) {
ProgressionType.UINT_PROGRESSION -> UInt.MAX_VALUE.toULong()
ProgressionType.ULONG_PROGRESSION -> ULong.MAX_VALUE
else -> error("Unexpected progression type")
}
lastValueAsULong > (constLimitAsULong - stepValueAsLong.toULong())
}
else -> error("Unexpected progression direction")
} }
ProgressionDirection.INCREASING -> { } else {
val constLimitAsLong = when (progressionType) { val lastValueAsLong = last.constLongValue ?: return@lazy true // If "last" is not a const Number or Char.
ProgressionType.INT_PROGRESSION -> Int.MAX_VALUE.toLong() when (direction) {
ProgressionType.CHAR_PROGRESSION -> Char.MAX_VALUE.toLong() ProgressionDirection.DECREASING -> {
ProgressionType.LONG_PROGRESSION -> Long.MAX_VALUE val constLimitAsLong = when (progressionType) {
ProgressionType.INT_PROGRESSION -> Int.MIN_VALUE.toLong()
ProgressionType.CHAR_PROGRESSION -> Char.MIN_VALUE.toLong()
ProgressionType.LONG_PROGRESSION -> Long.MIN_VALUE
else -> error("Unexpected progression type")
}
lastValueAsLong < (constLimitAsLong - stepValueAsLong)
} }
lastValueAsLong > (constLimitAsLong - stepValueAsLong) ProgressionDirection.INCREASING -> {
val constLimitAsLong = when (progressionType) {
ProgressionType.INT_PROGRESSION -> Int.MAX_VALUE.toLong()
ProgressionType.CHAR_PROGRESSION -> Char.MAX_VALUE.toLong()
ProgressionType.LONG_PROGRESSION -> Long.MAX_VALUE
else -> error("Unexpected progression type")
}
lastValueAsLong > (constLimitAsLong - stepValueAsLong)
}
else -> error("Unexpected progression direction")
} }
} }
} }
@@ -268,14 +330,17 @@ internal abstract class HeaderInfoBuilder(context: CommonBackendContext, private
private val symbols = context.ir.symbols private val symbols = context.ir.symbols
// TODO: Include unsigned types private val progressionElementTypes = listOfNotNull(
private val progressionElementTypes = listOf( symbols.byte,
context.irBuiltIns.byteType, symbols.short,
context.irBuiltIns.shortType, symbols.int,
context.irBuiltIns.intType, symbols.long,
context.irBuiltIns.longType, symbols.char,
context.irBuiltIns.charType symbols.uByte,
) symbols.uShort,
symbols.uInt,
symbols.uLong
).map { it.defaultType }
private val progressionHandlers = listOf( private val progressionHandlers = listOf(
CollectionIndicesHandler(context), CollectionIndicesHandler(context),
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.backend.common.lower.loops 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.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.irIfThen import org.jetbrains.kotlin.backend.common.lower.irIfThen
@@ -50,7 +49,6 @@ internal interface ForLoopHeader {
fun initializeIteration( fun initializeIteration(
loopVariable: IrVariable?, loopVariable: IrVariable?,
loopVariableComponents: Map<Int, IrVariable>, loopVariableComponents: Map<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder
): List<IrStatement> ): List<IrStatement>
@@ -61,6 +59,7 @@ internal interface ForLoopHeader {
internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>( internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
protected val headerInfo: T, protected val headerInfo: T,
builder: DeclarationIrBuilder, builder: DeclarationIrBuilder,
context: CommonBackendContext,
protected val isLastInclusive: Boolean protected val isLastInclusive: Boolean
) : ForLoopHeader { ) : ForLoopHeader {
@@ -68,16 +67,18 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
val inductionVariable: IrVariable val inductionVariable: IrVariable
val stepVariable: IrVariable val stepVariable: IrVariable
val stepVariableForIncrement: IrVariable?
protected val lastVariableIfCanCacheLast: IrVariable? protected val lastVariableIfCanCacheLast: IrVariable?
protected val lastExpression: IrExpression protected val lastExpression: IrExpression
// Always copy `lastExpression` is it may be used in multiple conditions. // Always copy `lastExpression` is it may be used in multiple conditions.
get() = field.deepCopyWithSymbols() get() = field.deepCopyWithSymbols()
private val symbols = context.ir.symbols
private val elementType: IrType private val elementType: IrType
init { init {
with(builder) { with(builder) {
elementType = headerInfo.progressionType.elementType(context.irBuiltIns) elementType = headerInfo.progressionType.elementType(symbols)
// For this loop: // For this loop:
// //
@@ -89,10 +90,7 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
// In the above example, if first() is a Long and last() is an Int, this creates a // In the above example, if first() is a Long and last() is an Int, this creates a
// LongProgression so last() should be cast to a Long. // LongProgression so last() should be cast to a Long.
inductionVariable = scope.createTemporaryVariable( inductionVariable = scope.createTemporaryVariable(
headerInfo.first.castIfNecessary( headerInfo.progressionType.castElementIfNecessary(headerInfo.first, context),
elementType,
headerInfo.progressionType.elementCastFunctionName
),
nameHint = "inductionVariable", nameHint = "inductionVariable",
isMutable = true isMutable = true
) )
@@ -101,12 +99,7 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
// they are non-nullable (the frontend takes care about this). So we need to cast // they are non-nullable (the frontend takes care about this). So we need to cast
// them to non-nullable. // them to non-nullable.
// TODO: Confirm if casting to non-nullable is still necessary // TODO: Confirm if casting to non-nullable is still necessary
val last = ensureNotNullable( val last = ensureNotNullable(headerInfo.progressionType.castElementIfNecessary(headerInfo.last, context))
headerInfo.last.castIfNecessary(
elementType,
headerInfo.progressionType.elementCastFunctionName
)
)
lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) { lastVariableIfCanCacheLast = if (headerInfo.canCacheLast) {
scope.createTemporaryVariable( scope.createTemporaryVariable(
@@ -117,17 +110,29 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last lastExpression = if (headerInfo.canCacheLast) irGet(lastVariableIfCanCacheLast!!) else last
stepVariable = headerInfo.progressionType.stepType(context.irBuiltIns).let { stepVariable =
scope.createTemporaryVariable( scope.createTemporaryVariable(
ensureNotNullable( ensureNotNullable(headerInfo.progressionType.castStepIfNecessary(headerInfo.step, context)),
headerInfo.step.castIfNecessary(
it,
headerInfo.progressionType.stepCastFunctionName
)
),
nameHint = "step", nameHint = "step",
irType = it irType = headerInfo.progressionType.stepType(context.irBuiltIns)
) )
stepVariableForIncrement = when (headerInfo.progressionType) {
ProgressionType.UINT_PROGRESSION -> {
val castFun = context.ir.symbols.toUIntByExtensionReceiver.getValue(stepVariable.type.toKotlinType())
scope.createTemporaryVariable(
irCall(castFun).apply { extensionReceiver = irGet(stepVariable) },
nameHint = "stepForIncrement"
)
}
ProgressionType.ULONG_PROGRESSION -> {
val castFun = context.ir.symbols.toULongByExtensionReceiver.getValue(stepVariable.type.toKotlinType())
scope.createTemporaryVariable(
irCall(castFun).apply { extensionReceiver = irGet(stepVariable) },
nameHint = "stepForIncrement"
)
}
else -> null
} }
} }
} }
@@ -142,16 +147,17 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
/** Statement used to increment the induction variable. */ /** Statement used to increment the induction variable. */
protected fun incrementInductionVariable(builder: DeclarationIrBuilder): IrStatement = with(builder) { protected fun incrementInductionVariable(builder: DeclarationIrBuilder): IrStatement = with(builder) {
// inductionVariable = inductionVariable + step // inductionVariable = inductionVariable + step
val stepVariableToUse = stepVariableForIncrement ?: stepVariable
val plusFun = elementType.getClass()!!.functions.single { val plusFun = elementType.getClass()!!.functions.single {
it.name == OperatorNameConventions.PLUS && it.name == OperatorNameConventions.PLUS &&
it.valueParameters.size == 1 && it.valueParameters.size == 1 &&
it.valueParameters[0].type == stepVariable.type it.valueParameters[0].type == stepVariableToUse.type
} }
irSetVar( irSetVar(
inductionVariable.symbol, irCallOp( inductionVariable.symbol, irCallOp(
plusFun.symbol, plusFun.returnType, plusFun.symbol, plusFun.returnType,
irGet(inductionVariable), irGet(inductionVariable),
irGet(stepVariable) irGet(stepVariableToUse)
) )
) )
} }
@@ -160,25 +166,65 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
with(builder) { with(builder) {
val builtIns = context.irBuiltIns val builtIns = context.irBuiltIns
val progressionType = headerInfo.progressionType val progressionType = headerInfo.progressionType
val progressionElementType = progressionType.elementType(builtIns) val progressionElementType = progressionType.elementType(symbols)
val compFun =
if (isLastInclusive) builtIns.lessOrEqualFunByOperandType[progressionElementType.classifierOrFail]!!
else builtIns.lessFunByOperandType[progressionElementType.classifierOrFail]!!
// The default condition depends on the direction. // `compareTo` must be used for UInt/ULong; they don't have intrinsic comparison operators.
when (headerInfo.direction) { val intCompFun = if (isLastInclusive) {
ProgressionDirection.DECREASING -> builtIns.lessOrEqualFunByOperandType.getValue(builtIns.intClass)
// last <= inductionVar (use `<` if last is exclusive) } else {
irCall(compFun).apply { builtIns.lessFunByOperandType.getValue(builtIns.intClass)
}
val elementCompareToFun = progressionElementType.getClass()!!.functions.single {
it.name == OperatorNameConventions.COMPARE_TO &&
it.dispatchReceiverParameter != null && it.extensionReceiverParameter == null &&
it.valueParameters.size == 1 && it.valueParameters[0].type == progressionElementType
}
val elementCompFun =
if (isLastInclusive) {
builtIns.lessOrEqualFunByOperandType[progressionElementType.classifierOrFail]
} else {
builtIns.lessFunByOperandType[progressionElementType.classifierOrFail]
}
fun conditionForDecreasing(): IrExpression =
// last <= inductionVar (use `<` if last is exclusive)
if (progressionType.isUnsigned) {
irCall(intCompFun).apply {
putValueArgument(0, irCall(elementCompareToFun).apply {
dispatchReceiver = lastExpression
putValueArgument(0, irGet(inductionVariable))
})
putValueArgument(1, irInt(0))
}
} else {
irCall(elementCompFun!!).apply {
putValueArgument(0, lastExpression) putValueArgument(0, lastExpression)
putValueArgument(1, irGet(inductionVariable)) putValueArgument(1, irGet(inductionVariable))
} }
ProgressionDirection.INCREASING -> }
// inductionVar <= last (use `<` if last is exclusive)
irCall(compFun).apply { fun conditionForIncreasing(): IrExpression =
// inductionVar <= last (use `<` if last is exclusive)
if (progressionType.isUnsigned) {
irCall(intCompFun).apply {
putValueArgument(0, irCall(elementCompareToFun).apply {
dispatchReceiver = irGet(inductionVariable)
putValueArgument(0, lastExpression)
})
putValueArgument(1, irInt(0))
}
} else {
irCall(elementCompFun!!).apply {
putValueArgument(0, irGet(inductionVariable)) putValueArgument(0, irGet(inductionVariable))
putValueArgument(1, lastExpression) putValueArgument(1, lastExpression)
} }
}
// The default condition depends on the direction.
when (headerInfo.direction) {
ProgressionDirection.DECREASING -> conditionForDecreasing()
ProgressionDirection.INCREASING -> conditionForIncreasing()
ProgressionDirection.UNKNOWN -> { ProgressionDirection.UNKNOWN -> {
// If the direction is unknown, we check depending on the "step" value: // If the direction is unknown, we check depending on the "step" value:
// // (use `<` if last is exclusive) // // (use `<` if last is exclusive)
@@ -187,33 +233,31 @@ internal abstract class NumericForLoopHeader<T : NumericHeaderInfo>(
val isLong = progressionType == ProgressionType.LONG_PROGRESSION val isLong = progressionType == ProgressionType.LONG_PROGRESSION
context.oror( context.oror(
context.andand( context.andand(
irCall(builtIns.greaterFunByOperandType[stepTypeClassifier]!!).apply { irCall(builtIns.greaterFunByOperandType.getValue(stepTypeClassifier)).apply {
putValueArgument(0, irGet(stepVariable)) putValueArgument(0, irGet(stepVariable))
putValueArgument(1, if (isLong) irLong(0) else irInt(0)) putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
}, },
irCall(compFun).apply { conditionForIncreasing()
putValueArgument(0, irGet(inductionVariable)) ),
putValueArgument(1, lastExpression)
}),
context.andand( context.andand(
irCall(builtIns.lessFunByOperandType[stepTypeClassifier]!!).apply { irCall(builtIns.lessFunByOperandType.getValue(stepTypeClassifier)).apply {
putValueArgument(0, irGet(stepVariable)) putValueArgument(0, irGet(stepVariable))
putValueArgument(1, if (isLong) irLong(0) else irInt(0)) putValueArgument(1, if (progressionType.isLong) irLong(0) else irInt(0))
}, },
irCall(compFun).apply { conditionForDecreasing()
putValueArgument(0, lastExpression) )
putValueArgument(1, irGet(inductionVariable))
})
) )
} }
} }
} }
} }
internal class ProgressionLoopHeader( internal class ProgressionLoopHeader(
headerInfo: ProgressionHeaderInfo, headerInfo: ProgressionHeaderInfo,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder,
) : NumericForLoopHeader<ProgressionHeaderInfo>(headerInfo, builder, isLastInclusive = true) { context: CommonBackendContext
) : NumericForLoopHeader<ProgressionHeaderInfo>(headerInfo, builder, context, isLastInclusive = true) {
// For this loop: // For this loop:
// //
@@ -230,14 +274,14 @@ internal class ProgressionLoopHeader(
else else
listOfNotNull(inductionVariable, lastVariableIfCanCacheLast) listOfNotNull(inductionVariable, lastVariableIfCanCacheLast)
) + ) +
stepVariable listOfNotNull(stepVariable, stepVariableForIncrement)
private var loopVariable: IrVariable? = null private var loopVariable: IrVariable? = null
@ExperimentalUnsignedTypes
override fun initializeIteration( override fun initializeIteration(
loopVariable: IrVariable?, loopVariable: IrVariable?,
loopVariableComponents: Map<Int, IrVariable>, loopVariableComponents: Map<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder
) = ) =
with(builder) { with(builder) {
@@ -258,6 +302,7 @@ internal class ProgressionLoopHeader(
listOfNotNull(loopVariable, incrementInductionVariable(this)) listOfNotNull(loopVariable, incrementInductionVariable(this))
} }
@ExperimentalUnsignedTypes
override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?) = override fun buildLoop(builder: DeclarationIrBuilder, oldLoop: IrLoop, newBody: IrExpression?) =
with(builder) { with(builder) {
val newLoop = if (headerInfo.canOverflow) { val newLoop = if (headerInfo.canOverflow) {
@@ -322,15 +367,16 @@ private class InitializerCallReplacer(symbolRemapper: SymbolRemapper, typeRemapp
internal class IndexedGetLoopHeader( internal class IndexedGetLoopHeader(
headerInfo: IndexedGetHeaderInfo, headerInfo: IndexedGetHeaderInfo,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder,
) : NumericForLoopHeader<IndexedGetHeaderInfo>(headerInfo, builder, isLastInclusive = false) { context: CommonBackendContext
) : NumericForLoopHeader<IndexedGetHeaderInfo>(headerInfo, builder, context, isLastInclusive = false) {
override val loopInitStatements = listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable) override val loopInitStatements =
listOfNotNull(headerInfo.objectVariable, inductionVariable, lastVariableIfCanCacheLast, stepVariable, stepVariableForIncrement)
override fun initializeIteration( override fun initializeIteration(
loopVariable: IrVariable?, loopVariable: IrVariable?,
loopVariableComponents: Map<Int, IrVariable>, loopVariableComponents: Map<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder
) = ) =
with(builder) { with(builder) {
@@ -371,7 +417,8 @@ internal class IndexedGetLoopHeader(
internal class WithIndexLoopHeader( internal class WithIndexLoopHeader(
headerInfo: WithIndexHeaderInfo, headerInfo: WithIndexHeaderInfo,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder,
context: CommonBackendContext
) : ForLoopHeader { ) : ForLoopHeader {
private val nestedLoopHeader: ForLoopHeader private val nestedLoopHeader: ForLoopHeader
@@ -384,8 +431,8 @@ internal class WithIndexLoopHeader(
// To build the optimized/lowered `for` loop over a `withIndex()` call, we first need the header for the underlying iterable so // To build the optimized/lowered `for` loop over a `withIndex()` call, we first need the header for the underlying iterable so
// so that we know how to build the loop for that iterable. More info in comments in initializeIteration(). // so that we know how to build the loop for that iterable. More info in comments in initializeIteration().
nestedLoopHeader = when (val nestedInfo = headerInfo.nestedInfo) { nestedLoopHeader = when (val nestedInfo = headerInfo.nestedInfo) {
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(nestedInfo, this@with) is IndexedGetHeaderInfo -> IndexedGetLoopHeader(nestedInfo, this@with, context)
is ProgressionHeaderInfo -> ProgressionLoopHeader(nestedInfo, this@with) is ProgressionHeaderInfo -> ProgressionLoopHeader(nestedInfo, this@with, context)
is IterableHeaderInfo -> IterableLoopHeader(nestedInfo) is IterableHeaderInfo -> IterableLoopHeader(nestedInfo)
is WithIndexHeaderInfo -> throw IllegalStateException("Nested WithIndexHeaderInfo not allowed for WithIndexLoopHeader") is WithIndexHeaderInfo -> throw IllegalStateException("Nested WithIndexHeaderInfo not allowed for WithIndexLoopHeader")
} }
@@ -409,7 +456,7 @@ internal class WithIndexLoopHeader(
) )
ownsIndexVariable = true ownsIndexVariable = true
// `index++` during iteration initialization // `index++` during iteration initialization
// TODO: MUSTDO: Check for overflow for Iterable and Sequence (call to checkIndexOverflow()). // TODO: KT-34665: Check for overflow for Iterable and Sequence (call to checkIndexOverflow()).
val plusFun = indexVariable.type.getClass()!!.functions.first { val plusFun = indexVariable.type.getClass()!!.functions.first {
it.name == OperatorNameConventions.PLUS && it.name == OperatorNameConventions.PLUS &&
it.valueParameters.size == 1 && it.valueParameters.size == 1 &&
@@ -435,7 +482,6 @@ internal class WithIndexLoopHeader(
override fun initializeIteration( override fun initializeIteration(
loopVariable: IrVariable?, loopVariable: IrVariable?,
loopVariableComponents: Map<Int, IrVariable>, loopVariableComponents: Map<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder
) = ) =
with(builder) { with(builder) {
@@ -508,7 +554,6 @@ internal class WithIndexLoopHeader(
listOfNotNull(loopVariableComponents[1], incrementIndexStatement) + nestedLoopHeader.initializeIteration( listOfNotNull(loopVariableComponents[1], incrementIndexStatement) + nestedLoopHeader.initializeIteration(
loopVariableComponents[2], loopVariableComponents[2],
linkedMapOf(), linkedMapOf(),
symbols,
builder builder
) )
} }
@@ -528,7 +573,6 @@ internal class IterableLoopHeader(
override fun initializeIteration( override fun initializeIteration(
loopVariable: IrVariable?, loopVariable: IrVariable?,
loopVariableComponents: Map<Int, IrVariable>, loopVariableComponents: Map<Int, IrVariable>,
symbols: Symbols<CommonBackendContext>,
builder: DeclarationIrBuilder builder: DeclarationIrBuilder
) = ) =
with(builder) { with(builder) {
@@ -615,9 +659,9 @@ internal class HeaderProcessor(
val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset) val builder = context.createIrBuilder(scopeOwnerSymbol(), variable.startOffset, variable.endOffset)
return when (headerInfo) { return when (headerInfo) {
is IndexedGetHeaderInfo -> IndexedGetLoopHeader(headerInfo, builder) is IndexedGetHeaderInfo -> IndexedGetLoopHeader(headerInfo, builder, context)
is ProgressionHeaderInfo -> ProgressionLoopHeader(headerInfo, builder) is ProgressionHeaderInfo -> ProgressionLoopHeader(headerInfo, builder, context)
is WithIndexHeaderInfo -> WithIndexLoopHeader(headerInfo, builder) is WithIndexHeaderInfo -> WithIndexLoopHeader(headerInfo, builder, context)
is IterableHeaderInfo -> IterableLoopHeader(headerInfo) is IterableHeaderInfo -> IterableLoopHeader(headerInfo)
} }
} }
@@ -17,10 +17,12 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
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.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.asSimpleType
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@@ -73,12 +75,19 @@ internal class DownToHandler(private val context: CommonBackendContext, private
internal class UntilHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<IrType>) : internal class UntilHandler(private val context: CommonBackendContext, private val progressionElementTypes: Collection<IrType>) :
ProgressionHandler { ProgressionHandler {
private val symbols = context.ir.symbols
private val uByteType = symbols.uByte?.defaultType
private val uShortType = symbols.uShort?.defaultType
private val uIntType = symbols.uInt?.defaultType
private val uLongType = symbols.uLong?.defaultType
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 in progressionElementTypes } parameter(0) { it.type in progressionElementTypes }
} }
@ExperimentalUnsignedTypes
override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? = override fun build(expression: IrCall, data: ProgressionType, scopeOwner: IrSymbol): HeaderInfo? =
with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) { with(context.createIrBuilder(scopeOwner, expression.startOffset, expression.endOffset)) {
// `A until B` is essentially the same as `A .. (B-1)`. However, B could be MIN_VALUE and hence `(B-1)` could underflow. // `A until B` is essentially the same as `A .. (B-1)`. However, B could be MIN_VALUE and hence `(B-1)` could underflow.
@@ -121,10 +130,7 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
val untilArg = expression.getValueArgument(0)!! val untilArg = expression.getValueArgument(0)!!
// Ensure that the argument conforms to the progression type before we decrement. // Ensure that the argument conforms to the progression type before we decrement.
val untilArgCasted = untilArg.castIfNecessary( val untilArgCasted = data.castElementIfNecessary(untilArg, this@UntilHandler.context)
data.elementType(context.irBuiltIns),
data.elementCastFunctionName
)
// To reduce local variable usage, we create and use temporary variables only if necessary. // To reduce local variable usage, we create and use temporary variables only if necessary.
var receiverValueVar: IrVariable? = null var receiverValueVar: IrVariable? = null
@@ -147,6 +153,24 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
ProgressionType.INT_PROGRESSION -> Pair(Int.MIN_VALUE.toLong(), irInt(Int.MIN_VALUE)) ProgressionType.INT_PROGRESSION -> Pair(Int.MIN_VALUE.toLong(), irInt(Int.MIN_VALUE))
ProgressionType.CHAR_PROGRESSION -> Pair(Char.MIN_VALUE.toLong(), irChar(Char.MIN_VALUE)) ProgressionType.CHAR_PROGRESSION -> Pair(Char.MIN_VALUE.toLong(), irChar(Char.MIN_VALUE))
ProgressionType.LONG_PROGRESSION -> Pair(Long.MIN_VALUE, irLong(Long.MIN_VALUE)) ProgressionType.LONG_PROGRESSION -> Pair(Long.MIN_VALUE, irLong(Long.MIN_VALUE))
ProgressionType.UINT_PROGRESSION -> Pair(
UInt.MIN_VALUE.toLong(),
IrConstImpl.int(
startOffset,
endOffset,
symbols.uInt!!.defaultType,
UInt.MIN_VALUE.toInt()
)
)
ProgressionType.ULONG_PROGRESSION -> Pair(
ULong.MIN_VALUE.toLong(),
IrConstImpl.long(
startOffset,
endOffset,
symbols.uLong!!.defaultType,
ULong.MIN_VALUE.toLong()
)
)
} }
val additionalNotEmptyCondition = untilArg.constLongValue.let { val additionalNotEmptyCondition = untilArg.constLongValue.let {
when { when {
@@ -197,11 +221,14 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
// infix fun Long.until(to: Short): LongRange // infix fun Long.until(to: Short): LongRange
// infix fun Long.until(to: Int): LongRange // infix fun Long.until(to: Int): LongRange
// infix fun Long.until(to: Long): LongRange // infix fun Long.until(to: Long): LongRange
// infix fun UByte.until(to: UByte): UIntRange
// infix fun UShort.until(to: UShort): UIntRange
// infix fun UInt.until(to: UInt): UIntRange
// infix fun ULong.until(to: ULong): ULongRange
// //
// The combinations where the range element type is strictly larger than the argument type do NOT need the additional condition. // The combinations where the range element type is strictly larger than the argument type do NOT need the additional condition.
// In such combinations, there is no possibility of underflow when the argument (casted to the range element type) is decremented. // In such combinations, there is no possibility of underflow when the argument (casted to the range element type) is decremented.
// For unexpected combinations that currently don't exist (e.g., Int until Char), we assume the check is needed to be safe. // For unexpected combinations that currently don't exist (e.g., Int until Char), we assume the check is needed to be safe.
// TODO: Include unsigned types
return with(context.irBuiltIns) { return with(context.irBuiltIns) {
when (receiverType) { when (receiverType) {
charType -> true charType -> true
@@ -213,7 +240,11 @@ internal class UntilHandler(private val context: CommonBackendContext, private v
byteType, shortType, intType -> false byteType, shortType, intType -> false
else -> true else -> true
} }
else -> true uByteType -> false
uShortType -> false
uIntType -> true
uLongType -> true
else -> true // Default in case a new `until` overload is added to stdlib and this function was not updated.
} }
} }
} }
@@ -258,7 +289,7 @@ internal class StepHandler(
// We insert this check in the lowered form only if necessary. // We insert this check in the lowered form only if necessary.
val stepType = data.stepType(context.irBuiltIns) val stepType = data.stepType(context.irBuiltIns)
val stepGreaterFun = context.irBuiltIns.greaterFunByOperandType[data.stepClassifier(context.irBuiltIns)]!! val stepGreaterFun = context.irBuiltIns.greaterFunByOperandType[data.stepClassifier(context.irBuiltIns)]!!
val zeroStep = if (data == ProgressionType.LONG_PROGRESSION) irLong(0) else irInt(0) val zeroStep = if (data.isLong) irLong(0) else irInt(0)
val throwIllegalStepExceptionCall = { val throwIllegalStepExceptionCall = {
irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply { irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply {
val exceptionMessage = irConcat() val exceptionMessage = irConcat()
@@ -458,29 +489,29 @@ internal class StepHandler(
return last return last
} }
// Call `getProgressionLastElement(first, last, step)` // Call `getProgressionLastElement(first, last, step)`. The following overloads are present in the stdlib:
val stepTypeClassifier = progressionType.stepClassifier(context.irBuiltIns) // - getProgressionLastElement(Int, Int, Int): Int // Used by IntProgression and CharProgression (uses Int step)
val getProgressionLastElementFun = symbols.getProgressionLastElementByReturnType[stepTypeClassifier] // - getProgressionLastElement(Long, Long, Long): Long // Used by LongProgression
?: throw IllegalArgumentException("No `getProgressionLastElement` for step type $stepTypeClassifier") // - getProgressionLastElement(UInt, UInt, Int): UInt // Used by UIntProgression (uses Int step)
// - getProgressionLastElement(ULong, ULong, Long): ULong // Used by ULongProgression (uses Long step)
//
// We make sure to retrieve the correct symbol and use the correct argument types.
val returnTypeClassifier = if (progressionType.isUnsigned) {
progressionType.elementClassifier(symbols)
} else {
progressionType.stepClassifier(context.irBuiltIns)
}
val getProgressionLastElementFun = symbols.getProgressionLastElementByReturnType[returnTypeClassifier]
?: throw IllegalArgumentException("No `getProgressionLastElement` for return type ${returnTypeClassifier.defaultType}")
return irCall(getProgressionLastElementFun).apply { return irCall(getProgressionLastElementFun).apply {
putValueArgument( if (progressionType.isUnsigned) {
0, first.deepCopyWithSymbols().castIfNecessary( putValueArgument(0, progressionType.castElementIfNecessary(first.deepCopyWithSymbols(), this@StepHandler.context))
progressionType.stepType(context.irBuiltIns), putValueArgument(1, progressionType.castElementIfNecessary(last.deepCopyWithSymbols(), this@StepHandler.context))
progressionType.stepCastFunctionName } else {
) putValueArgument(0, progressionType.castStepIfNecessary(first.deepCopyWithSymbols(), this@StepHandler.context))
) putValueArgument(1, progressionType.castStepIfNecessary(last.deepCopyWithSymbols(), this@StepHandler.context))
putValueArgument( }
1, last.deepCopyWithSymbols().castIfNecessary( putValueArgument(2, progressionType.castStepIfNecessary(step.deepCopyWithSymbols(), this@StepHandler.context))
progressionType.stepType(context.irBuiltIns),
progressionType.stepCastFunctionName
)
)
putValueArgument(
2, step.deepCopyWithSymbols().castIfNecessary(
progressionType.stepType(context.irBuiltIns),
progressionType.stepCastFunctionName
)
)
} }
} }
} }
@@ -14,24 +14,11 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
internal fun IrExpression.castIfNecessary(targetType: IrType, numberCastFunctionName: Name): IrExpression {
// This expression's type could be Nothing from an exception throw.
return if (type == targetType || type.isNothing()) {
this
} else {
val castFun = type.getClass()!!.functions.single { it.name == numberCastFunctionName && it.valueParameters.isEmpty() }
IrCallImpl(startOffset, endOffset, castFun.returnType, castFun.symbol)
.apply { dispatchReceiver = this@castIfNecessary }
}
}
/** Return the negated value if the expression is const, otherwise call unaryMinus(). */ /** Return the negated value if the expression is const, otherwise call unaryMinus(). */
internal fun IrExpression.negate(): IrExpression { internal fun IrExpression.negate(): IrExpression {
return when (val value = (this as? IrConst<*>)?.value as? Number) { return when (val value = (this as? IrConst<*>)?.value as? Number) {
@@ -299,6 +299,7 @@ private val jvmFilePhases =
anonymousObjectSuperConstructorPhase then anonymousObjectSuperConstructorPhase then
tailrecPhase then tailrecPhase then
forLoopsPhase then
jvmInlineClassPhase then jvmInlineClassPhase then
sharedVariablesPhase then sharedVariablesPhase then
@@ -318,7 +319,6 @@ private val jvmFilePhases =
jvmDefaultConstructorPhase then jvmDefaultConstructorPhase then
forLoopsPhase then
flattenStringConcatenationPhase then flattenStringConcatenationPhase then
foldConstantLoweringPhase then foldConstantLoweringPhase then
computeStringTrimPhase then computeStringTrimPhase then
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
@@ -42,7 +43,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
val jvmInlineClassPhase = makeIrFilePhase( val jvmInlineClassPhase = makeIrFilePhase(
::JvmInlineClassLowering, ::JvmInlineClassLowering,
name = "Inline Classes", name = "Inline Classes",
description = "Lower inline classes" description = "Lower inline classes",
// forLoopsPhase may produce UInt and ULong which are inline classes.
prerequisite = setOf(forLoopsPhase)
) )
/** /**
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME
// TODO KT-36773 Use counter loop when generating 'for' loop over an unsigned range in JVM_IR
fun testUIntRangeLiteral(a: UInt, b: UInt): Int { fun testUIntRangeLiteral(a: UInt, b: UInt): Int {
var s = 0 var s = 0