Allow unsigned integers overflow values of corresponding signed numbers

This commit is contained in:
Mikhail Zarechenskiy
2018-05-29 03:00:39 +03:00
parent 0da3ae328e
commit 5b5d9dd5a0
11 changed files with 138 additions and 35 deletions
@@ -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
}
}
@@ -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"
}
@@ -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<!>
@@ -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
@@ -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");
@@ -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");
@@ -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");
@@ -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
@@ -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()
@@ -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");