KT-32044 Fix unsigned integer coercion in loop intrinsics
This commit is contained in:
+2
-2
@@ -33,8 +33,8 @@ class DownToProgressionRangeValue(rangeCall: ResolvedCall<out CallableDescriptor
|
||||
|
||||
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
||||
BoundedValue(
|
||||
lowBound = codegen.generateCallSingleArgument(rangeCall),
|
||||
highBound = codegen.generateCallReceiver(rangeCall)
|
||||
lowBound = codegen.generateCallSingleArgument(rangeCall).coerceToRangeElementTypeIfRequired(),
|
||||
highBound = codegen.generateCallReceiver(rangeCall).coerceToRangeElementTypeIfRequired()
|
||||
)
|
||||
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
|
||||
+82
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.range
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.UnsignedType
|
||||
import org.jetbrains.kotlin.builtins.UnsignedTypes
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForKotlinType
|
||||
@@ -32,6 +35,7 @@ import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
@@ -77,6 +81,84 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(
|
||||
|
||||
protected abstract fun getBoundedValue(codegen: ExpressionCodegen): BoundedValue
|
||||
|
||||
protected fun StackValue.coerceToRangeElementTypeIfRequired(): StackValue {
|
||||
val rangeKotlinType = rangeCall.resultingDescriptor.returnType!!
|
||||
val rangeElementKotlinType = getRangeOrProgressionElementType(rangeKotlinType)!!
|
||||
return when {
|
||||
KotlinBuiltIns.isUInt(rangeElementKotlinType) ->
|
||||
coerceUnsignedToUInt(this, rangeElementKotlinType)
|
||||
KotlinBuiltIns.isULong(rangeElementKotlinType) ->
|
||||
coerceUnsignedToULong(this, rangeElementKotlinType)
|
||||
else ->
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
private val StackValue.unsignedType: UnsignedType?
|
||||
get() = kotlinType?.let { UnsignedTypes.toUnsignedType(it) }
|
||||
|
||||
private fun coerceUnsignedToUInt(stackValue: StackValue, uIntKotlinType: KotlinType): StackValue {
|
||||
val valueKotlinType = stackValue.kotlinType
|
||||
val valueUnsignedType = stackValue.unsignedType
|
||||
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
|
||||
|
||||
if (valueUnsignedType == UnsignedType.UINT) return stackValue
|
||||
|
||||
return StackValue.operation(Type.INT_TYPE, uIntKotlinType) { v ->
|
||||
stackValue.put(stackValue.type, valueKotlinType, v)
|
||||
when (valueUnsignedType) {
|
||||
UnsignedType.UBYTE -> {
|
||||
v.iconst(0xFF)
|
||||
v.and(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.USHORT -> {
|
||||
v.iconst(0xFFFF)
|
||||
v.and(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.ULONG -> {
|
||||
v.cast(Type.LONG_TYPE, Type.INT_TYPE)
|
||||
}
|
||||
|
||||
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun coerceUnsignedToULong(stackValue: StackValue, uLongKotlinType: KotlinType): StackValue {
|
||||
val valueKotlinType = stackValue.kotlinType
|
||||
val valueUnsignedType = stackValue.unsignedType
|
||||
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
|
||||
|
||||
if (valueUnsignedType == UnsignedType.ULONG) return stackValue
|
||||
|
||||
return StackValue.operation(Type.LONG_TYPE, uLongKotlinType) { v ->
|
||||
stackValue.put(stackValue.type, valueKotlinType, v)
|
||||
when (valueUnsignedType) {
|
||||
UnsignedType.UBYTE -> {
|
||||
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
|
||||
v.lconst(0xFF)
|
||||
v.and(Type.LONG_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.USHORT -> {
|
||||
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
|
||||
v.lconst(0xFFFF)
|
||||
v.and(Type.LONG_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.UINT -> {
|
||||
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
|
||||
v.lconst(0xFFFF_FFFFL)
|
||||
v.and(Type.LONG_TYPE)
|
||||
}
|
||||
|
||||
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun createConstBoundedForLoopGeneratorOrNull(
|
||||
codegen: ExpressionCodegen,
|
||||
forExpression: KtForExpression,
|
||||
|
||||
+11
-5
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.generateCallReceiver
|
||||
import org.jetbrains.kotlin.codegen.generateCallSingleArgument
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForKotlinType
|
||||
@@ -37,15 +38,20 @@ class PrimitiveNumberRangeLiteralRangeValue(
|
||||
ReversableRangeValue {
|
||||
|
||||
override fun getBoundedValue(codegen: ExpressionCodegen): BoundedValue {
|
||||
val instanceType = codegen.asmType(rangeCall.resultingDescriptor.returnType!!)
|
||||
val lowBound = codegen.generateCallReceiver(rangeCall)
|
||||
|
||||
if (codegen.canBeSpecializedByExcludingHighBound(rangeCall)) {
|
||||
val highBound = (rangeCall.getFirstArgumentExpression() as KtBinaryExpression).left
|
||||
return BoundedValue(lowBound, true, codegen.gen(highBound), false)
|
||||
return BoundedValue(
|
||||
lowBound = lowBound,
|
||||
isLowInclusive = true,
|
||||
highBound = codegen.gen((rangeCall.getFirstArgumentExpression() as KtBinaryExpression).left),
|
||||
isHighInclusive = false
|
||||
)
|
||||
}
|
||||
|
||||
return BoundedValue(
|
||||
lowBound = lowBound,
|
||||
highBound = codegen.generateCallSingleArgument(rangeCall)
|
||||
lowBound = lowBound.coerceToRangeElementTypeIfRequired(),
|
||||
highBound = codegen.generateCallSingleArgument(rangeCall).coerceToRangeElementTypeIfRequired()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -32,10 +32,10 @@ class PrimitiveNumberUntilRangeValue(rangeCall: ResolvedCall<out CallableDescrip
|
||||
|
||||
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
||||
BoundedValue(
|
||||
codegen.generateCallReceiver(rangeCall),
|
||||
true,
|
||||
codegen.generateCallSingleArgument(rangeCall),
|
||||
false
|
||||
lowBound = codegen.generateCallReceiver(rangeCall).coerceToRangeElementTypeIfRequired(),
|
||||
isLowInclusive = true,
|
||||
highBound = codegen.generateCallSingleArgument(rangeCall).coerceToRangeElementTypeIfRequired(),
|
||||
isHighInclusive = false
|
||||
)
|
||||
|
||||
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
|
||||
|
||||
@@ -32,11 +32,15 @@ fun isPrimitiveProgression(rangeType: KotlinType) =
|
||||
fun isUnsignedProgression(rangeType: KotlinType) =
|
||||
isClassTypeWithFqn(rangeType, UNSIGNED_PROGRESSION_FQNS)
|
||||
|
||||
private fun isClassTypeWithFqn(kotlinType: KotlinType, fqns: Set<String>): Boolean {
|
||||
val declarationDescriptor = kotlinType.constructor.declarationDescriptor as? ClassDescriptor ?: return false
|
||||
val fqName = DescriptorUtils.getFqName(declarationDescriptor).takeIf { it.isSafe } ?: return false
|
||||
return fqName.asString() in fqns
|
||||
}
|
||||
private val KotlinType.classFqnString: String?
|
||||
get() {
|
||||
val declarationDescriptor = constructor.declarationDescriptor as? ClassDescriptor ?: return null
|
||||
val fqn = DescriptorUtils.getFqName(declarationDescriptor)
|
||||
return if (fqn.isSafe) fqn.asString() else null
|
||||
}
|
||||
|
||||
private fun isClassTypeWithFqn(kotlinType: KotlinType, fqns: Set<String>): Boolean =
|
||||
kotlinType.classFqnString in fqns
|
||||
|
||||
internal const val CHAR_RANGE_FQN = "kotlin.ranges.CharRange"
|
||||
internal const val INT_RANGE_FQN = "kotlin.ranges.IntRange"
|
||||
|
||||
Reference in New Issue
Block a user