Allow unsigned integers overflow values of corresponding signed numbers
This commit is contained in:
+22
-11
@@ -941,11 +941,17 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
if (integerValue != null) {
|
||||
return integerValue.wrap(parameters)
|
||||
}
|
||||
return when (value) {
|
||||
value.toInt().toLong() ->
|
||||
if (parameters.isUnsigned) UIntValue(value.toInt()) else IntValue(value.toInt())
|
||||
else ->
|
||||
if (parameters.isUnsigned) ULongValue(value) else LongValue(value)
|
||||
|
||||
return if (parameters.isUnsigned) {
|
||||
when (value) {
|
||||
value.toInt().fromUIntToLong() -> UIntValue(value.toInt())
|
||||
else -> ULongValue(value)
|
||||
}
|
||||
} else {
|
||||
when (value) {
|
||||
value.toInt().toLong() -> IntValue(value.toInt())
|
||||
else -> LongValue(value)
|
||||
}
|
||||
}.wrap(parameters)
|
||||
}
|
||||
|
||||
@@ -975,14 +981,19 @@ private fun parseNumericLiteral(text: String, type: IElementType): Any? {
|
||||
}
|
||||
|
||||
private fun parseLong(text: String): Long? {
|
||||
try {
|
||||
fun substringSuffix(s: String) = if (hasLongSuffix(text) || hasUnsignedSuffix(text)) s.substring(0, s.length - 1) else s
|
||||
fun parseLong(text: String, radix: Int) = java.lang.Long.parseLong(substringSuffix(text), radix)
|
||||
return try {
|
||||
val hasUnsignedSuffix = hasUnsignedSuffix(text)
|
||||
val hasLongSuffix = hasLongSuffix(text)
|
||||
val textWithoutSuffix = if (hasUnsignedSuffix || hasLongSuffix) text.substring(0, text.length - 1) else text
|
||||
val (number, radix) = extractRadix(textWithoutSuffix)
|
||||
|
||||
val (number, radix) = extractRadix(text)
|
||||
return parseLong(number, radix)
|
||||
if (hasUnsignedSuffix) {
|
||||
java.lang.Long.parseUnsignedLong(number, radix)
|
||||
} else {
|
||||
java.lang.Long.parseLong(number, radix)
|
||||
}
|
||||
} catch (e: NumberFormatException) {
|
||||
return null
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// !SKIP_METADATA_VERSION_CHECK
|
||||
// WITH_UNSIGNED
|
||||
|
||||
fun box(): String {
|
||||
val u1: UByte = 255u
|
||||
if (u1.toByte().toInt() != -1) return "fail"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// !SKIP_METADATA_VERSION_CHECK
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
const val u1: UByte = 0xFFu
|
||||
const val u2: UShort = 0xFFFFu
|
||||
const val u3: UInt = 0xFFFF_FFFFu
|
||||
const val u4: ULong = 0xFFFF_FFFF_FFFF_FFFFu
|
||||
const val u5: ULong = 18446744073709551615u
|
||||
|
||||
const val u6 = 0xFFFF_FFFF_FFFF_FFFFu
|
||||
const val u7 = 18446744073709551615u
|
||||
|
||||
val u8: Comparable<*> = 0xFFFF_FFFF_FFFF_FFFFu
|
||||
|
||||
fun takeUByte(ubyte: UByte) {}
|
||||
|
||||
fun test() {
|
||||
takeUByte(200u)
|
||||
takeUByte(255u)
|
||||
takeUByte(0xFFu)
|
||||
}
|
||||
|
||||
val s1: UByte = <!CONSTANT_EXPECTED_TYPE_MISMATCH!>256u<!>
|
||||
val s2 = <!INT_LITERAL_OUT_OF_RANGE!>18446744073709551616u<!>
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
package
|
||||
|
||||
public val s1: kotlin.UByte = 256.toUInt()
|
||||
public val s2: kotlin.Int
|
||||
public const val u1: kotlin.UByte = -1.toUByte()
|
||||
public const val u2: kotlin.UShort = -1.toUShort()
|
||||
public const val u3: kotlin.UInt = -1.toUInt()
|
||||
public const val u4: kotlin.ULong = -1.toULong()
|
||||
public const val u5: kotlin.ULong = -1.toULong()
|
||||
public const val u6: kotlin.ULong = -1.toULong()
|
||||
public const val u7: kotlin.ULong = -1.toULong()
|
||||
public val u8: kotlin.Comparable<*>
|
||||
public fun takeUByte(/*0*/ ubyte: kotlin.UByte): kotlin.Unit
|
||||
public fun test(): kotlin.Unit
|
||||
Generated
+5
@@ -11304,6 +11304,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/UIntArraySortExample.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unsignedLiteralsWithSignedOverflow.kt")
|
||||
public void testUnsignedLiteralsWithSignedOverflow() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/unsignedLiteralsWithSignedOverflow.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("useInlineClassesInsideElvisOperator.kt")
|
||||
public void testUseInlineClassesInsideElvisOperator() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/useInlineClassesInsideElvisOperator.kt");
|
||||
|
||||
+5
@@ -34,6 +34,11 @@ public class DiagnosticsWithUnsignedTypesGenerated extends AbstractDiagnosticsWi
|
||||
runTest("compiler/testData/diagnostics/testsWithUnsignedTypes/unsignedLiteralsInsideConstVals.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unsignedLiteralsOverflowSignedBorder.kt")
|
||||
public void testUnsignedLiteralsOverflowSignedBorder() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithUnsignedTypes/unsignedLiteralsOverflowSignedBorder.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unsignedLiteralsTypeCheck.kt")
|
||||
public void testUnsignedLiteralsTypeCheck() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithUnsignedTypes/unsignedLiteralsTypeCheck.kt");
|
||||
|
||||
+5
@@ -11304,6 +11304,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/UIntArraySortExample.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unsignedLiteralsWithSignedOverflow.kt")
|
||||
public void testUnsignedLiteralsWithSignedOverflow() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/unsignedLiteralsWithSignedOverflow.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("useInlineClassesInsideElvisOperator.kt")
|
||||
public void testUseInlineClassesInsideElvisOperator() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/useInlineClassesInsideElvisOperator.kt");
|
||||
|
||||
+5
@@ -11304,6 +11304,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/UIntArraySortExample.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unsignedLiteralsWithSignedOverflow.kt")
|
||||
public void testUnsignedLiteralsWithSignedOverflow() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/unsignedLiteralsWithSignedOverflow.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("useInlineClassesInsideElvisOperator.kt")
|
||||
public void testUseInlineClassesInsideElvisOperator() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/useInlineClassesInsideElvisOperator.kt");
|
||||
|
||||
@@ -71,9 +71,9 @@ object ConstantValueFactory {
|
||||
val notNullExpected = TypeUtils.makeNotNullable(expectedType)
|
||||
return if (isUnsigned) {
|
||||
when {
|
||||
KotlinBuiltIns.isUByte(notNullExpected) && value == value.toByte().toLong() -> UByteValue(value.toByte())
|
||||
KotlinBuiltIns.isUShort(notNullExpected) && value == value.toShort().toLong() -> UShortValue(value.toShort())
|
||||
KotlinBuiltIns.isUInt(notNullExpected) && value == value.toInt().toLong() -> UIntValue(value.toInt())
|
||||
KotlinBuiltIns.isUByte(notNullExpected) && value == value.toByte().fromUByteToLong() -> UByteValue(value.toByte())
|
||||
KotlinBuiltIns.isUShort(notNullExpected) && value == value.toShort().fromUShortToLong() -> UShortValue(value.toShort())
|
||||
KotlinBuiltIns.isUInt(notNullExpected) && value == value.toInt().fromUIntToLong() -> UIntValue(value.toInt())
|
||||
KotlinBuiltIns.isULong(notNullExpected) -> ULongValue(value)
|
||||
else -> null
|
||||
}
|
||||
@@ -89,3 +89,7 @@ object ConstantValueFactory {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Byte.fromUByteToLong(): Long = this.toLong() and 0xFF
|
||||
fun Short.fromUShortToLong(): Long = this.toLong() and 0xFFFF
|
||||
fun Int.fromUIntToLong(): Long = this.toLong() and 0xFFFF_FFFF
|
||||
|
||||
+35
-21
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.resolve.constants
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.UnsignedTypes
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
|
||||
@@ -45,30 +46,14 @@ class IntegerValueTypeConstructor(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix value ranges for unsigned types
|
||||
checkBoundsAndAddSuperType(
|
||||
value,
|
||||
Integer.MIN_VALUE.toLong(),
|
||||
Integer.MAX_VALUE.toLong(),
|
||||
if (isUnsigned) unsignedType(KotlinBuiltIns.FQ_NAMES.uInt) else builtIns.intType
|
||||
)
|
||||
checkBoundsAndAddSuperType(
|
||||
value,
|
||||
java.lang.Byte.MIN_VALUE.toLong(),
|
||||
java.lang.Byte.MAX_VALUE.toLong(),
|
||||
if (isUnsigned) unsignedType(KotlinBuiltIns.FQ_NAMES.uByte) else builtIns.byteType
|
||||
)
|
||||
checkBoundsAndAddSuperType(
|
||||
value,
|
||||
java.lang.Short.MIN_VALUE.toLong(),
|
||||
java.lang.Short.MAX_VALUE.toLong(),
|
||||
if (isUnsigned) unsignedType(KotlinBuiltIns.FQ_NAMES.uShort) else builtIns.shortType
|
||||
)
|
||||
checkBoundsAndAddSuperType(value, if (isUnsigned) unsignedType(KotlinBuiltIns.FQ_NAMES.uInt) else builtIns.intType)
|
||||
checkBoundsAndAddSuperType(value, if (isUnsigned) unsignedType(KotlinBuiltIns.FQ_NAMES.uByte) else builtIns.byteType)
|
||||
checkBoundsAndAddSuperType(value, if (isUnsigned) unsignedType(KotlinBuiltIns.FQ_NAMES.uShort) else builtIns.shortType)
|
||||
supertypes.add(if (isUnsigned) unsignedType(KotlinBuiltIns.FQ_NAMES.uLong) else builtIns.longType)
|
||||
}
|
||||
|
||||
private fun checkBoundsAndAddSuperType(value: Long, minValue: Long, maxValue: Long, kotlinType: KotlinType) {
|
||||
if (value >= minValue && value <= maxValue) {
|
||||
private fun checkBoundsAndAddSuperType(value: Long, kotlinType: KotlinType) {
|
||||
if (value in kotlinType.minValue()..kotlinType.maxValue()) {
|
||||
supertypes.add(kotlinType)
|
||||
}
|
||||
}
|
||||
@@ -93,3 +78,32 @@ class IntegerValueTypeConstructor(
|
||||
|
||||
override fun toString() = "IntegerValueType($value)"
|
||||
}
|
||||
|
||||
private fun KotlinType.minValue(): Long {
|
||||
if (UnsignedTypes.isUnsignedType(this)) return 0
|
||||
return when {
|
||||
KotlinBuiltIns.isByte(this) -> Byte.MIN_VALUE.toLong()
|
||||
KotlinBuiltIns.isShort(this) -> Short.MIN_VALUE.toLong()
|
||||
KotlinBuiltIns.isInt(this) -> Int.MIN_VALUE.toLong()
|
||||
else -> error("Can't get min value for type: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.maxValue(): Long {
|
||||
return when {
|
||||
KotlinBuiltIns.isByte(this) -> Byte.MAX_VALUE.toLong()
|
||||
KotlinBuiltIns.isShort(this) -> Short.MAX_VALUE.toLong()
|
||||
KotlinBuiltIns.isInt(this) -> Int.MAX_VALUE.toLong()
|
||||
|
||||
KotlinBuiltIns.isUByte(this) -> UBYTE_MAX_VALUE
|
||||
KotlinBuiltIns.isUShort(this) -> USHORT_MAX_VALUE
|
||||
KotlinBuiltIns.isUInt(this) -> UINT_MAX_VALUE
|
||||
|
||||
else -> error("Can't get max value for type: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private val UBYTE_MAX_VALUE = (-1).toByte().fromUByteToLong()
|
||||
private val USHORT_MAX_VALUE = (-1).toShort().fromUShortToLong()
|
||||
private val UINT_MAX_VALUE = (-1).fromUIntToLong()
|
||||
|
||||
|
||||
+5
@@ -9840,6 +9840,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/UIntArraySortExample.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unsignedLiteralsWithSignedOverflow.kt")
|
||||
public void testUnsignedLiteralsWithSignedOverflow() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/unsignedLiteralsWithSignedOverflow.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("useInlineClassesInsideElvisOperator.kt")
|
||||
public void testUseInlineClassesInsideElvisOperator() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/inlineClasses/useInlineClassesInsideElvisOperator.kt");
|
||||
|
||||
Reference in New Issue
Block a user