Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Justify signed integer literal overflow error when most significant bit occupies signed bit #23919

Merged
merged 6 commits into from
Mar 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion vlib/builtin/int_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn test_str_methods() {
assert int(1).str() == '1'
assert int(-1).str() == '-1'
assert int(2147483647).str() == '2147483647'
assert int(2147483648).str() == '-2147483648'
assert int(u32(2147483648)).str() == '-2147483648'
assert int(-2147483648).str() == '-2147483648'
assert i64(1).str() == '1'
assert i64(-1).str() == '-1'
Expand Down
4 changes: 2 additions & 2 deletions vlib/builtin/string_int_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,8 @@ fn test_parse() {
}

fn test_interpolate_binary_literals() {
assert ' 1 ${i64(0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000)}' == ' 1 -9223372036854775808'
assert ' 2 ${i64(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)}' == ' 2 -1'
assert ' 1 ${i64(u64(0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000))}' == ' 1 -9223372036854775808'
assert ' 2 ${i64(u64(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111))}' == ' 2 -1'
assert ' 3 ${i64(0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)}' == ' 3 9223372036854775807'
assert ' 4 ${u64(0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000)}' == ' 4 9223372036854775808'
assert ' 5 ${u64(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)}' == ' 5 18446744073709551615'
Expand Down
4 changes: 2 additions & 2 deletions vlib/rand/mt19937/mt19937_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ fn test_mt19937_u64_in_range() {

fn test_mt19937_int31() {
max_u31 := int(0x7FFFFFFF)
sign_mask := int(0x80000000)
sign_mask := int(u32(0x80000000))
for seed in seeds {
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
rng.seed(seed)
Expand All @@ -182,7 +182,7 @@ fn test_mt19937_int31() {

fn test_mt19937_int63() {
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
sign_mask := i64(0x8000000000000000)
sign_mask := i64(u64(0x8000000000000000))
for seed in seeds {
mut rng := &rand.PRNG(&mt19937.MT19937RNG{})
rng.seed(seed)
Expand Down
4 changes: 2 additions & 2 deletions vlib/rand/musl/musl_rng_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ fn test_musl_u64_in_range() {

fn test_musl_int31() {
max_u31 := int(0x7FFFFFFF)
sign_mask := int(0x80000000)
sign_mask := int(u32(0x80000000))
for seed in seeds {
mut rng := &rand.PRNG(&musl.MuslRNG{})
rng.seed(seed)
Expand All @@ -171,7 +171,7 @@ fn test_musl_int31() {

fn test_musl_int63() {
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
sign_mask := i64(0x8000000000000000)
sign_mask := i64(u64(0x8000000000000000))
for seed in seeds {
mut rng := &rand.PRNG(&musl.MuslRNG{})
rng.seed(seed)
Expand Down
4 changes: 2 additions & 2 deletions vlib/rand/pcg32/pcg32_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ fn test_pcg32_u64_in_range() {

fn test_pcg32_int31() {
max_u31 := int(0x7FFFFFFF)
sign_mask := int(0x80000000)
sign_mask := int(u32(0x80000000))
for seed in seeds {
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
rng.seed(seed)
Expand All @@ -174,7 +174,7 @@ fn test_pcg32_int31() {

fn test_pcg32_int63() {
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
sign_mask := i64(0x8000000000000000)
sign_mask := i64(u64(0x8000000000000000))
for seed in seeds {
mut rng := &rand.PRNG(&pcg32.PCG32RNG{})
rng.seed(seed)
Expand Down
4 changes: 2 additions & 2 deletions vlib/rand/random_numbers_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ fn test_rand_i64_in_range() {

fn test_rand_int31() {
max_u31 := int(0x7FFFFFFF)
sign_mask := int(0x80000000)
sign_mask := int(u32(0x80000000))
for _ in 0 .. rnd_count {
value := rand.int31()
assert value >= 0
Expand All @@ -133,7 +133,7 @@ fn test_rand_int31() {

fn test_rand_int63() {
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
sign_mask := i64(0x8000000000000000)
sign_mask := i64(u64(0x8000000000000000))
for _ in 0 .. rnd_count {
value := rand.int63()
assert value >= 0
Expand Down
4 changes: 2 additions & 2 deletions vlib/rand/splitmix64/splitmix64_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ fn test_splitmix64_u64_in_range() {

fn test_splitmix64_int31() {
max_u31 := int(0x7FFFFFFF)
sign_mask := int(0x80000000)
sign_mask := int(u32(0x80000000))
for seed in seeds {
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
rng.seed(seed)
Expand All @@ -171,7 +171,7 @@ fn test_splitmix64_int31() {

fn test_splitmix64_int63() {
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
sign_mask := i64(0x8000000000000000)
sign_mask := i64(u64(0x8000000000000000))
for seed in seeds {
mut rng := &rand.PRNG(&splitmix64.SplitMix64RNG{})
rng.seed(seed)
Expand Down
4 changes: 2 additions & 2 deletions vlib/rand/sys/system_rng_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ fn test_sys_rng_i64_in_range() {

fn test_sys_rng_int31() {
max_u31 := int(0x7FFFFFFF)
sign_mask := int(0x80000000)
sign_mask := int(u32(0x80000000))
for seed in seeds {
seed_data := [seed]
mut rng := &rand.PRNG(&sys.SysRNG{})
Expand All @@ -270,7 +270,7 @@ fn test_sys_rng_int31() {

fn test_sys_rng_int63() {
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
sign_mask := i64(0x8000000000000000)
sign_mask := i64(u64(0x8000000000000000))
for seed in seeds {
seed_data := [seed]
mut rng := &rand.PRNG(&sys.SysRNG{})
Expand Down
4 changes: 2 additions & 2 deletions vlib/rand/wyrand/wyrand_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ fn test_wyrand_u64_in_range() {

fn test_wyrand_int31() {
max_u31 := int(0x7FFFFFFF)
sign_mask := int(0x80000000)
sign_mask := int(u32(0x80000000))
for seed in seeds {
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
rng.seed(seed)
Expand All @@ -171,7 +171,7 @@ fn test_wyrand_int31() {

fn test_wyrand_int63() {
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
sign_mask := i64(0x8000000000000000)
sign_mask := i64(u64(0x8000000000000000))
for seed in seeds {
mut rng := &rand.PRNG(&wyrand.WyRandRNG{})
rng.seed(seed)
Expand Down
4 changes: 2 additions & 2 deletions vlib/rand/xoroshiro128pp/xoros128pp_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ fn test_xoroshiro128pp_u64_in_range() {

fn test_xoroshiro128pp_int31() {
max_u31 := int(0x7FFFFFFF)
sign_mask := int(0x80000000)
sign_mask := int(u32(0x80000000))
for seed in seeds {
mut rng := &rand.PRNG(&XOROS128PPRNG{})
rng.seed(seed)
Expand All @@ -175,7 +175,7 @@ fn test_xoroshiro128pp_int31() {

fn test_xoroshiro128pp_int63() {
max_u63 := i64(0x7FFFFFFFFFFFFFFF)
sign_mask := i64(0x8000000000000000)
sign_mask := i64(u64(0x8000000000000000))
for seed in seeds {
mut rng := &rand.PRNG(&XOROS128PPRNG{})
rng.seed(seed)
Expand Down
2 changes: 1 addition & 1 deletion vlib/strconv/format.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ hexadecimal
import strconv

a1 := u8(0xff)
b1 := i16(0xffff)
b1 := i16(u16(0xffff))
c1 := u32(0xffffffff)
d1 := u64(-1)
sc3 := '%hhx %hx %x %lx'
Expand Down
2 changes: 1 addition & 1 deletion vlib/strconv/format_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn test_format() {
assert tmp_str == temp_s

a1 := u8(0xff)
b1 := i16(0xffff)
b1 := i16(u16(0xffff))
c1 := u32(0xffff_ffff)
d1 := u64(-1)
sc2 := '%hhu %hu %u %lu'
Expand Down
50 changes: 49 additions & 1 deletion vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -3651,6 +3651,7 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
tt := c.table.type_to_str(to_type)
tsize, _ := c.table.type_size(to_type.idx_type())
bit_size := tsize * 8
signed := node.expr.val[0] == `-`
value_string := match node.expr.val[0] {
`-`, `+` {
node.expr.val[1..]
Expand All @@ -3659,16 +3660,63 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
node.expr.val
}
}
_, e := strconv.common_parse_uint2(value_string, 0, bit_size)
mut is_overflowed := false
v, e := strconv.common_parse_uint2(value_string, 0, bit_size)
match e {
0 {}
-3 {
// FIXME: Once integer literal is considered as hard error, remove this warn and migrate to later error
c.error('value `${node.expr.val}` overflows `${tt}`', node.pos)
is_overflowed = true
}
else {
c.error('cannot cast value `${node.expr.val}` to `${tt}`', node.pos)
}
}

// checks if integer literal's most significant bit
// alters sign bit when casting to signed integer
if !is_overflowed && to_type.is_signed() {
signed_one := match to_type.idx() {
ast.char_type_idx, ast.i8_type_idx {
u64(0xff)
}
ast.i16_type_idx {
u64(0xffff)
}
ast.int_type_idx, ast.i32_type_idx {
u64(0xffffffff)
}
ast.i64_type_idx {
u64(0xffffffffffffffff)
}
ast.isize_type_idx {
$if x64 {
u64(0xffffffffffffffff)
} $else {
u64(0xffffffff)
}
}
else {
c.error('ICE: Not a valid signed type', node.pos)
0
}
}
max_signed := (u64(1) << (bit_size - 1)) - 1

is_overflowed = v == signed_one || (signed && v - 2 == max_signed)
|| (!signed && v - 1 == max_signed)
// FIXME: Once integer literal is considered as hard error, remove this warn and migrate to later error
if is_overflowed {
c.warn('value `${node.expr.val}` overflows `${tt}`, this will be considered hard error soon',
node.pos)
}
}

// FIXME: Once integer literal is considered as hard error, uncomment this
// if is_overflowed {
// c.error('value `${node.expr.val}` overflows `${tt}`', node.pos)
// }
} else if to_type.is_float() && mut node.expr is ast.FloatLiteral {
tt := c.table.type_to_str(to_type)
strconv.atof64(node.expr.val) or {
Expand Down
138 changes: 138 additions & 0 deletions vlib/v/checker/tests/overflow_int_signed_err.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
vlib/v/checker/tests/overflow_int_signed_err.vv:2:2: warning: value `0xff` overflows `i8`, this will be considered hard error soon
1 | const _i8s = [
2 | i8(0xff), // converted to -1
| ~~~~~~~~
3 | i8(128), // converted to -128
4 | i8(-129), // converted to +127
vlib/v/checker/tests/overflow_int_signed_err.vv:3:2: warning: value `128` overflows `i8`, this will be considered hard error soon
1 | const _i8s = [
2 | i8(0xff), // converted to -1
3 | i8(128), // converted to -128
| ~~~~~~~
4 | i8(-129), // converted to +127
5 | i8(-0xff), // converted to +1
vlib/v/checker/tests/overflow_int_signed_err.vv:4:2: warning: value `-129` overflows `i8`, this will be considered hard error soon
2 | i8(0xff), // converted to -1
3 | i8(128), // converted to -128
4 | i8(-129), // converted to +127
| ~~~~~~~~
5 | i8(-0xff), // converted to +1
6 | ]
vlib/v/checker/tests/overflow_int_signed_err.vv:5:2: warning: value `-0xff` overflows `i8`, this will be considered hard error soon
3 | i8(128), // converted to -128
4 | i8(-129), // converted to +127
5 | i8(-0xff), // converted to +1
| ~~~~~~~~~
6 | ]
7 |
vlib/v/checker/tests/overflow_int_signed_err.vv:9:2: warning: value `0xffff` overflows `i16`, this will be considered hard error soon
7 |
8 | const _i16s = [
9 | i16(0xffff), // converted to -1
| ~~~~~~~~~~~
10 | i16(32768), // converted to -32768
11 | i16(-32769), // converted to +32767
vlib/v/checker/tests/overflow_int_signed_err.vv:10:2: warning: value `32768` overflows `i16`, this will be considered hard error soon
8 | const _i16s = [
9 | i16(0xffff), // converted to -1
10 | i16(32768), // converted to -32768
| ~~~~~~~~~~
11 | i16(-32769), // converted to +32767
12 | i16(-0xffff), // converted to +1
vlib/v/checker/tests/overflow_int_signed_err.vv:11:2: warning: value `-32769` overflows `i16`, this will be considered hard error soon
9 | i16(0xffff), // converted to -1
10 | i16(32768), // converted to -32768
11 | i16(-32769), // converted to +32767
| ~~~~~~~~~~~
12 | i16(-0xffff), // converted to +1
13 | ]
vlib/v/checker/tests/overflow_int_signed_err.vv:12:2: warning: value `-0xffff` overflows `i16`, this will be considered hard error soon
10 | i16(32768), // converted to -32768
11 | i16(-32769), // converted to +32767
12 | i16(-0xffff), // converted to +1
| ~~~~~~~~~~~~
13 | ]
14 |
vlib/v/checker/tests/overflow_int_signed_err.vv:16:2: warning: value `0xffffffff` overflows `int`, this will be considered hard error soon
14 |
15 | const _ints = [
16 | int(0xffffffff), // converted to -1
| ~~~~~~~~~~~~~~~
17 | int(2147483648), // converted to -2147483648 (overflow in 32-bit int)
18 | int(-2147483649), // converted to +2147483647 (overflow)
vlib/v/checker/tests/overflow_int_signed_err.vv:17:2: warning: value `2147483648` overflows `int`, this will be considered hard error soon
15 | const _ints = [
16 | int(0xffffffff), // converted to -1
17 | int(2147483648), // converted to -2147483648 (overflow in 32-bit int)
| ~~~~~~~~~~~~~~~
18 | int(-2147483649), // converted to +2147483647 (overflow)
19 | int(-0xffffffff), // converted to +1
vlib/v/checker/tests/overflow_int_signed_err.vv:18:2: warning: value `-2147483649` overflows `int`, this will be considered hard error soon
16 | int(0xffffffff), // converted to -1
17 | int(2147483648), // converted to -2147483648 (overflow in 32-bit int)
18 | int(-2147483649), // converted to +2147483647 (overflow)
| ~~~~~~~~~~~~~~~~
19 | int(-0xffffffff), // converted to +1
20 | ]
vlib/v/checker/tests/overflow_int_signed_err.vv:19:2: warning: value `-0xffffffff` overflows `int`, this will be considered hard error soon
17 | int(2147483648), // converted to -2147483648 (overflow in 32-bit int)
18 | int(-2147483649), // converted to +2147483647 (overflow)
19 | int(-0xffffffff), // converted to +1
| ~~~~~~~~~~~~~~~~
20 | ]
21 |
vlib/v/checker/tests/overflow_int_signed_err.vv:23:2: warning: value `0xffffffff` overflows `i32`, this will be considered hard error soon
21 |
22 | const _i32s = [
23 | i32(0xffffffff), // converted to -1
| ~~~~~~~~~~~~~~~
24 | i32(2147483648), // converted to -2147483648
25 | i32(-2147483649), // converted to +2147483647
vlib/v/checker/tests/overflow_int_signed_err.vv:24:2: warning: value `2147483648` overflows `i32`, this will be considered hard error soon
22 | const _i32s = [
23 | i32(0xffffffff), // converted to -1
24 | i32(2147483648), // converted to -2147483648
| ~~~~~~~~~~~~~~~
25 | i32(-2147483649), // converted to +2147483647
26 | i32(-0xffffffff), // converted to +1
vlib/v/checker/tests/overflow_int_signed_err.vv:25:2: warning: value `-2147483649` overflows `i32`, this will be considered hard error soon
23 | i32(0xffffffff), // converted to -1
24 | i32(2147483648), // converted to -2147483648
25 | i32(-2147483649), // converted to +2147483647
| ~~~~~~~~~~~~~~~~
26 | i32(-0xffffffff), // converted to +1
27 | ]
vlib/v/checker/tests/overflow_int_signed_err.vv:26:2: warning: value `-0xffffffff` overflows `i32`, this will be considered hard error soon
24 | i32(2147483648), // converted to -2147483648
25 | i32(-2147483649), // converted to +2147483647
26 | i32(-0xffffffff), // converted to +1
| ~~~~~~~~~~~~~~~~
27 | ]
28 |
vlib/v/checker/tests/overflow_int_signed_err.vv:30:2: warning: value `0xffffffffffffffff` overflows `i64`, this will be considered hard error soon
28 |
29 | const _i64s = [
30 | i64(0xffffffffffffffff), // converted to -1
| ~~~~~~~~~~~~~~~~~~~~~~~
31 | i64(9223372036854775808), // converted to -9223372036854775808
32 | i64(-9223372036854775809), // converted to +9223372036854775807
vlib/v/checker/tests/overflow_int_signed_err.vv:31:2: warning: value `9223372036854775808` overflows `i64`, this will be considered hard error soon
29 | const _i64s = [
30 | i64(0xffffffffffffffff), // converted to -1
31 | i64(9223372036854775808), // converted to -9223372036854775808
| ~~~~~~~~~~~~~~~~~~~~~~~~
32 | i64(-9223372036854775809), // converted to +9223372036854775807
33 | i64(-0xffffffffffffffff), // converted to +1
vlib/v/checker/tests/overflow_int_signed_err.vv:32:2: warning: value `-9223372036854775809` overflows `i64`, this will be considered hard error soon
30 | i64(0xffffffffffffffff), // converted to -1
31 | i64(9223372036854775808), // converted to -9223372036854775808
32 | i64(-9223372036854775809), // converted to +9223372036854775807
| ~~~~~~~~~~~~~~~~~~~~~~~~~
33 | i64(-0xffffffffffffffff), // converted to +1
34 | ]
vlib/v/checker/tests/overflow_int_signed_err.vv:33:2: warning: value `-0xffffffffffffffff` overflows `i64`, this will be considered hard error soon
31 | i64(9223372036854775808), // converted to -9223372036854775808
32 | i64(-9223372036854775809), // converted to +9223372036854775807
33 | i64(-0xffffffffffffffff), // converted to +1
| ~~~~~~~~~~~~~~~~~~~~~~~~
34 | ]
Loading
Loading