From 0832a68bd714695d292aef2ca9b08d16b9a86516 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 23 Apr 2026 19:05:46 +0300 Subject: [PATCH] all: fix add checker error when non-explicitly downcasting integers internally (fixes #23695) --- vlib/builtin/int.v | 12 ++-- vlib/compress/zstd/zstd.c.v | 4 +- vlib/dlmalloc/dlmalloc.v | 4 +- vlib/math/bits/bits.v | 8 +-- vlib/math/factorial.v | 4 +- vlib/rand/rand.c.v | 6 +- vlib/strconv/number_to_base.c.v | 2 +- vlib/v/checker/check_types.v | 26 ++++---- vlib/v/checker/checker.v | 60 +++++++++++++++---- .../tests/generics_undefined_operation_2.out | 4 +- .../tests/generics_undefined_operation_2.vv | 2 +- .../global_array_indexed_by_global_fn.vv | 2 +- .../index_expr_implicit_int_downcast_err.out | 20 +++++++ .../index_expr_implicit_int_downcast_err.vv | 13 ++++ vlib/v/eval/expr.c.v | 4 +- vlib/v/tests/generic_calls/faker.v | 2 +- 16 files changed, 126 insertions(+), 47 deletions(-) create mode 100644 vlib/v/checker/tests/index_expr_implicit_int_downcast_err.out create mode 100644 vlib/v/checker/tests/index_expr_implicit_int_downcast_err.vv diff --git a/vlib/builtin/int.v b/vlib/builtin/int.v index c3ad36f45..5387ac9df 100644 --- a/vlib/builtin/int.v +++ b/vlib/builtin/int.v @@ -177,10 +177,10 @@ pub fn (nn u32) str() string { n1 := n / u32(100) d = ((n - (n1 * u32(100))) << u32(1)) n = n1 - buf[index] = digit_pairs[d] + buf[index] = digit_pairs[int(d)] index-- d++ - buf[index] = digit_pairs[d] + buf[index] = digit_pairs[int(d)] index-- } index++ @@ -231,10 +231,10 @@ fn impl_i64_to_string(nn i64) string { n1 := n / i64(100) d = (u32(n - (n1 * i64(100))) << i64(1)) n = n1 - buf[index] = digit_pairs[d] + buf[index] = digit_pairs[int(d)] index-- d++ - buf[index] = digit_pairs[d] + buf[index] = digit_pairs[int(d)] index-- } index++ @@ -272,10 +272,10 @@ pub fn (nn u64) str() string { n1 := n / 100 d = ((n - (n1 * 100)) << 1) n = n1 - buf[index] = digit_pairs[d] + buf[index] = digit_pairs[int(d)] index-- d++ - buf[index] = digit_pairs[d] + buf[index] = digit_pairs[int(d)] index-- } index++ diff --git a/vlib/compress/zstd/zstd.c.v b/vlib/compress/zstd/zstd.c.v index 86c713be1..256bea50f 100644 --- a/vlib/compress/zstd/zstd.c.v +++ b/vlib/compress/zstd/zstd.c.v @@ -426,7 +426,7 @@ pub fn compress(data []u8, params CompressParams) ![]u8 { } size := C.ZSTD_compress2(cctx.ctx, dst.data, dst.len, data.data, data.len) check_error(size)! - return dst[..size] + return dst[..int(size)] } @[params] @@ -450,7 +450,7 @@ pub fn decompress(data []u8, params DecompressParams) ![]u8 { mut dst := []u8{len: int(dst_capacity)} decompressed_size := C.ZSTD_decompress(dst.data, dst.len, data.data, data.len) check_error(decompressed_size)! - return dst[..decompressed_size] + return dst[..int(decompressed_size)] } pub struct CCtx { diff --git a/vlib/dlmalloc/dlmalloc.v b/vlib/dlmalloc/dlmalloc.v index 833950095..0a6f3f8a4 100644 --- a/vlib/dlmalloc/dlmalloc.v +++ b/vlib/dlmalloc/dlmalloc.v @@ -857,7 +857,7 @@ fn (mut dl Dlmalloc) insert_large_chunk(chunk_ &TreeChunk, size usize) { mut k := size << leftshift_for_tree_index(idx) for { if t.chunk().size() != size { - c_ := &t.child[(k >> sizeof(usize) * 8 - 1) & 1] + c_ := &t.child[int((k >> sizeof(usize) * 8 - 1) & 1)] mut c := &&TreeChunk(c_) k <<= 1 if !isnil(c) { @@ -1201,7 +1201,7 @@ fn (mut dl Dlmalloc) tmalloc_large(size usize) voidptr { } rt := t.child[1] - t = t.child[(sizebits >> (sizeof(usize) * 8 - 1)) & 1] + t = t.child[int((sizebits >> (sizeof(usize) * 8 - 1)) & 1)] if !isnil(rt) && voidptr(rt) != voidptr(t) { rst = rt } diff --git a/vlib/math/bits/bits.v b/vlib/math/bits/bits.v index 9aab95c0e..41fbfc584 100644 --- a/vlib/math/bits/bits.v +++ b/vlib/math/bits/bits.v @@ -131,7 +131,7 @@ fn trailing_zeros_64_default(x u64) int { // find by how many bits it was shifted by looking at which six bit // substring ended up at the top of the word. // (Knuth, volume 4, section 7.3.1) - return int(de_bruijn64tab[(x & -x) * de_bruijn64 >> (64 - 6)]) + return int(de_bruijn64tab[int((x & -x) * de_bruijn64 >> (64 - 6))]) } // --- OnesCount --- @@ -325,7 +325,7 @@ pub fn len_16(x u16) int { y >>= 8 n = 8 } - return n + int(len_8_tab[y]) + return n + int(len_8_tab[int(y)]) } // len_32 returns the minimum number of bits required to represent x; the result is 0 for x == 0. @@ -341,7 +341,7 @@ pub fn len_32(x u32) int { y >>= 8 n += 8 } - return n + int(len_8_tab[y]) + return n + int(len_8_tab[int(y)]) } // len_64 returns the minimum number of bits required to represent x; the result is 0 for x == 0. @@ -361,7 +361,7 @@ pub fn len_64(x u64) int { y >>= 8 n += 8 } - return n + int(len_8_tab[y]) + return n + int(len_8_tab[int(y)]) } // --- Add with carry --- diff --git a/vlib/math/factorial.v b/vlib/math/factorial.v index 8d76a69ae..adcd1c408 100644 --- a/vlib/math/factorial.v +++ b/vlib/math/factorial.v @@ -8,7 +8,7 @@ pub fn factorial(n f64) f64 { } // Otherwise return n!. if n == f64(i64(n)) && n >= 0.0 { - return factorials_table[i64(n)] + return factorials_table[int(n)] } return gamma(n + 1.0) } @@ -23,7 +23,7 @@ pub fn log_factorial(n f64) f64 { if n != f64(i64(n)) { return log_gamma(n + 1) } else if n < log_factorials_table.len { - return log_factorials_table[i64(n)] + return log_factorials_table[int(n)] } // Otherwise return asymptotic expansion of ln(n!). return log_factorial_asymptotic_expansion(int(n)) diff --git a/vlib/rand/rand.c.v b/vlib/rand/rand.c.v index 1af6f5409..a705c81ce 100644 --- a/vlib/rand/rand.c.v +++ b/vlib/rand/rand.c.v @@ -105,7 +105,7 @@ fn internal_ulid_at_millisecond(mut rng PRNG, unix_time_milli u64) string { mut i := 9 for i >= 0 { unsafe { - buf[i] = ulid_encoding[t & 0x1F] + buf[i] = ulid_encoding[int(t & 0x1F)] } t = t >> 5 i-- @@ -115,7 +115,7 @@ fn internal_ulid_at_millisecond(mut rng PRNG, unix_time_milli u64) string { i = 10 for i < 19 { unsafe { - buf[i] = ulid_encoding[x & 0x1F] + buf[i] = ulid_encoding[int(x & 0x1F)] } x = x >> 5 i++ @@ -124,7 +124,7 @@ fn internal_ulid_at_millisecond(mut rng PRNG, unix_time_milli u64) string { x = rng.u64() for i < 26 { unsafe { - buf[i] = ulid_encoding[x & 0x1F] + buf[i] = ulid_encoding[int(x & 0x1F)] } x = x >> 5 i++ diff --git a/vlib/strconv/number_to_base.c.v b/vlib/strconv/number_to_base.c.v index dbdd7e2b3..b4b438795 100644 --- a/vlib/strconv/number_to_base.c.v +++ b/vlib/strconv/number_to_base.c.v @@ -55,7 +55,7 @@ pub fn format_uint(n u64, radix int) string { uradix := u64(radix) for n_copy != 0 { tmp_0 := res - tmp_1 := base_digits[n_copy % uradix].ascii_str() + tmp_1 := base_digits[int(n_copy % uradix)].ascii_str() res = tmp_1 + res tmp_0.free() tmp_1.free() diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 84c7c53d0..3b978b723 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -454,30 +454,30 @@ fn (mut c Checker) can_convert_array_elem_to_interface_array(got ast.Type, expec return c.table.does_type_implement_interface(got_type, expected_type) } -fn (mut c Checker) warn_if_integer_literal_overflow_for_known_type(expected ast.Type, expr ast.Expr, pos token.Pos) { +fn (c &Checker) integer_literal_outside_type_range(expected ast.Type, expr ast.Expr) bool { if !expected.is_int() { - return + return false } if expr !is ast.IntegerLiteral { - return + return false } int_lit := expr as ast.IntegerLiteral mut lit := int_lit.val.replace('_', '') if lit.len == 0 { - return + return false } is_negative := lit.starts_with('-') if is_negative || lit.starts_with('+') { lit = lit[1..] } if lit.len == 0 { - return + return false } literal_value := lit.u64() bits, _ := c.table.type_size(expected.idx_type()) bit_size := bits * 8 if bit_size == 0 { - return + return false } mut outside_type_range := false if expected.is_signed() { @@ -498,11 +498,17 @@ fn (mut c Checker) warn_if_integer_literal_overflow_for_known_type(expected ast. outside_type_range = literal_value > max_unsigned } } - if outside_type_range { - expected_type_str := c.table.type_to_str(expected.clear_flag(.variadic)) - c.warn('value `${int_lit.val}` is outside the range of `${expected_type_str}` in argument, this will be considered hard error soon', - pos) + return outside_type_range +} + +fn (mut c Checker) warn_if_integer_literal_overflow_for_known_type(expected ast.Type, expr ast.Expr, pos token.Pos) { + if !c.integer_literal_outside_type_range(expected, expr) { + return } + int_lit := expr as ast.IntegerLiteral + expected_type_str := c.table.type_to_str(expected.clear_flag(.variadic)) + c.warn('value `${int_lit.val}` is outside the range of `${expected_type_str}` in argument, this will be considered hard error soon', + pos) } fn (c &Checker) get_string_names_of(got ast.Type, expected ast.Type) (string, string) { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 8491a6cdf..825450a76 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -7698,20 +7698,68 @@ fn (mut c Checker) type_error_for_operator(op_label string, types_label string, pos) } +fn (c &Checker) internal_index_type(index_type ast.Type) ast.Type { + mut internal_index_type := c.table.unaliased_type(index_type.clear_flag(.variadic)) + internal_index_sym := c.table.final_sym(internal_index_type) + if internal_index_sym.kind == .enum { + internal_index_type = internal_index_sym.enum_info().typ + } + return internal_index_type +} + +fn (mut c Checker) check_internal_index_type(index ast.Expr, index_type ast.Type, typ_sym &ast.TypeSymbol) bool { + if c.pref.translated || c.file.is_translated { + return true + } + internal_index_type := c.internal_index_type(index_type) + if internal_index_type == ast.int_literal_type { + if c.integer_literal_outside_type_range(ast.int_type, index) { + c.error('overflow in implicit type `int`, use explicit type casting instead', + index.pos()) + return false + } + return true + } + int_size, _ := c.table.type_size(ast.int_type_idx) + internal_index_size, _ := c.table.type_size(internal_index_type.idx_type()) + if internal_index_size > int_size { + index_type_str := if typ_sym.kind == .string { 'string index' } else { 'index' } + got_type_str := c.table.type_to_str(index_type) + c.error('cannot use `${got_type_str}` as ${index_type_str} type `int`, use an explicit cast like `int(expr)`', + index.pos()) + return false + } + return true +} + fn (mut c Checker) check_index(typ_sym &ast.TypeSymbol, index ast.Expr, index_type ast.Type, range_index bool, is_gated bool) { if typ_sym.kind in [.array, .array_fixed, .string] { index_type_sym := c.table.sym(index_type) - if !(index_type.is_int() || index_type_sym.kind == .enum + is_integer_index := index_type.is_int() || index_type_sym.kind == .enum || (index_type_sym.kind == .alias && (index_type_sym.info as ast.Alias).parent_type.is_int()) - || (c.pref.translated && index_type.is_any_kind_of_pointer())) { + || (c.pref.translated && index_type.is_any_kind_of_pointer()) + if !is_integer_index { type_str := if typ_sym.kind == .string { 'non-integer string index `${c.table.type_to_str(index_type)}`' } else { 'non-integer index `${c.table.type_to_str(index_type)}` (array type `${typ_sym.name}`)' } c.error('${type_str}', index.pos()) + return + } + if index_type.has_option_or_result() { + type_str := if typ_sym.kind == .string { + '(type `${typ_sym.name}`)' + } else { + '(array type `${typ_sym.name}`)' + } + c.error('cannot use Option or Result as index ${type_str}', index.pos()) + return + } + if !c.check_internal_index_type(index, index_type, typ_sym) { + return } if index is ast.IntegerLiteral && !is_gated { if index.val[0] == `-` { @@ -7724,14 +7772,6 @@ fn (mut c Checker) check_index(typ_sym &ast.TypeSymbol, index ast.Expr, index_ty } } } - if index_type.has_option_or_result() { - type_str := if typ_sym.kind == .string { - '(type `${typ_sym.name}`)' - } else { - '(array type `${typ_sym.name}`)' - } - c.error('cannot use Option or Result as index ${type_str}', index.pos()) - } } } diff --git a/vlib/v/checker/tests/generics_undefined_operation_2.out b/vlib/v/checker/tests/generics_undefined_operation_2.out index 452ab0271..0067920cd 100644 --- a/vlib/v/checker/tests/generics_undefined_operation_2.out +++ b/vlib/v/checker/tests/generics_undefined_operation_2.out @@ -1,7 +1,7 @@ vlib/v/checker/tests/generics_undefined_operation_2.vv:23:18: error: mismatched types `INode` and `&INode` 21 | fn (mut h IHeap[T]) percolateup(hl u64, mut element T) { 22 | mut hole := hl - 23 | for hole > 1 && element < h.array[hole / 2] { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 23 | for hole > 1 && element < h.array[int(hole / 2)] { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 | // ^ Notice that '<' was not defined 25 | println("didnt define '<' for the INode") diff --git a/vlib/v/checker/tests/generics_undefined_operation_2.vv b/vlib/v/checker/tests/generics_undefined_operation_2.vv index 96b1af1ca..ec2df830c 100644 --- a/vlib/v/checker/tests/generics_undefined_operation_2.vv +++ b/vlib/v/checker/tests/generics_undefined_operation_2.vv @@ -20,7 +20,7 @@ pub fn create_iheap[T](capacity int) &IHeap[T] { fn (mut h IHeap[T]) percolateup(hl u64, mut element T) { mut hole := hl - for hole > 1 && element < h.array[hole / 2] { + for hole > 1 && element < h.array[int(hole / 2)] { // ^ Notice that '<' was not defined println("didnt define '<' for the INode") hole /= 2 diff --git a/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv b/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv index 272df4c7b..12078c213 100644 --- a/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv +++ b/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv @@ -20,7 +20,7 @@ fn abc1() u64 { } pub fn current() &Local { - return cpu_locals[cpu_get_id()] + return cpu_locals[int(cpu_get_id())] } fn main() { diff --git a/vlib/v/checker/tests/index_expr_implicit_int_downcast_err.out b/vlib/v/checker/tests/index_expr_implicit_int_downcast_err.out new file mode 100644 index 000000000..e4983f488 --- /dev/null +++ b/vlib/v/checker/tests/index_expr_implicit_int_downcast_err.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/index_expr_implicit_int_downcast_err.vv:10:12: error: cannot use `u64` as string index type `int`, use an explicit cast like `int(expr)` + 8 | _ = s[int(idx_u64)] + 9 | _ = a[idx_u32] + 10 | println(s[idx_u64]) + | ~~~~~~~ + 11 | println(a[idx_i64]) + 12 | println(a[idx_usize..]) +vlib/v/checker/tests/index_expr_implicit_int_downcast_err.vv:11:12: error: cannot use `i64` as index type `int`, use an explicit cast like `int(expr)` + 9 | _ = a[idx_u32] + 10 | println(s[idx_u64]) + 11 | println(a[idx_i64]) + | ~~~~~~~ + 12 | println(a[idx_usize..]) + 13 | } +vlib/v/checker/tests/index_expr_implicit_int_downcast_err.vv:12:12: error: cannot use `usize` as index type `int`, use an explicit cast like `int(expr)` + 10 | println(s[idx_u64]) + 11 | println(a[idx_i64]) + 12 | println(a[idx_usize..]) + | ~~~~~~~~~ + 13 | } diff --git a/vlib/v/checker/tests/index_expr_implicit_int_downcast_err.vv b/vlib/v/checker/tests/index_expr_implicit_int_downcast_err.vv new file mode 100644 index 000000000..2708d9b79 --- /dev/null +++ b/vlib/v/checker/tests/index_expr_implicit_int_downcast_err.vv @@ -0,0 +1,13 @@ +fn main() { + s := 'abc' + a := [1, 2, 3] + idx_u64 := u64(1) + idx_u32 := u32(1) + idx_i64 := i64(1) + idx_usize := usize(1) + _ = s[int(idx_u64)] + _ = a[idx_u32] + println(s[idx_u64]) + println(a[idx_i64]) + println(a[idx_usize..]) +} diff --git a/vlib/v/eval/expr.c.v b/vlib/v/eval/expr.c.v index e44615113..515c30d2d 100644 --- a/vlib/v/eval/expr.c.v +++ b/vlib/v/eval/expr.c.v @@ -582,14 +582,14 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object { if expr.is_farray { if left is FixedArray { if index is i64 { - return left.val[index] + return left.val[int(index)] } } } if expr.is_array { if left is Array { if index is i64 { - return left.val[index] + return left.val[int(index)] } } } diff --git a/vlib/v/tests/generic_calls/faker.v b/vlib/v/tests/generic_calls/faker.v index 8a8b4654d..b483472e5 100644 --- a/vlib/v/tests/generic_calls/faker.v +++ b/vlib/v/tests/generic_calls/faker.v @@ -17,7 +17,7 @@ pub fn (mut this Faker) random_element[T](elements []T) T { number_of_elements := u64(elements.len) random_index := this.randomizer.u64() % number_of_elements - return elements[random_index] or { + return elements[int(random_index)] or { panic('Failed to get random element at index ${random_index} with length ${number_of_elements}.') } } -- 2.39.5