From ffd6da8ac9c8314c9dd2d5ea6ed878e6402883eb Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Fri, 24 Apr 2026 17:55:24 +0300 Subject: [PATCH] cgen: int index fix for slices --- vlib/builtin/builtin.c.v | 16 +++++++++++++ vlib/v/checker/checker.v | 16 +++++++++++-- ...ndex_expr_implicit_int_downcast_err.js.out | 20 ++++++++++++++++ .../index_expr_implicit_int_downcast_err.vv | 13 +++++++++++ vlib/v/compiler_errors_test.v | 3 ++- vlib/v/gen/c/index.v | 23 +++++++++++++++++-- vlib/v/markused/walker.v | 5 ++++ 7 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 vlib/v/checker/tests/index_expr_implicit_int_downcast_err.js.out create mode 100644 vlib/v/checker/tests/index_expr_implicit_int_downcast_err.vv diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index b8471d48b..c91e115c7 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -106,6 +106,22 @@ fn v_fixed_index_ni(i int, len int) int { return v_fixed_index(v_ni_index(i, len), len) } +@[inline; markused] +fn v_slice_index_i64(i i64) int { + if i < i64(min_int) || i > i64(max_int) { + panic('slice index out of range for int: ' + i.str()) + } + return int(i) +} + +@[inline; markused] +fn v_slice_index_u64(i u64) int { + if i > u64(max_int) { + panic('slice index out of range for int: ' + i.str()) + } + return int(i) +} + // arguments returns the command line arguments, used for starting the current program as a V array of strings. // The first string in the array (index 0), is the name of the program, used for invoking the program. // The second string in the array (index 1), if it exists, is the first argument to the program, etc. diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index a0200043a..bf103641a 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -7708,7 +7708,7 @@ fn (c &Checker) internal_index_type(index_type ast.Type) ast.Type { return internal_index_type } -fn (mut c Checker) check_internal_index_type(index ast.Expr, index_type ast.Type) bool { +fn (mut c Checker) check_internal_index_type(index ast.Expr, index_type ast.Type, typ_sym &ast.TypeSymbol, is_gated bool) bool { if c.pref.translated || c.file.is_translated { return true } @@ -7721,6 +7721,18 @@ fn (mut c Checker) check_internal_index_type(index ast.Expr, index_type ast.Type } return true } + if c.pref.backend == .c && !is_gated { + 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 } @@ -7750,7 +7762,7 @@ fn (mut c Checker) check_index(typ_sym &ast.TypeSymbol, index ast.Expr, index_ty c.error('cannot use Option or Result as index ${type_str}', index.pos()) return } - if !c.check_internal_index_type(index, index_type) { + if !c.check_internal_index_type(index, index_type, typ_sym, is_gated) { return } if index is ast.IntegerLiteral && !is_gated { diff --git a/vlib/v/checker/tests/index_expr_implicit_int_downcast_err.js.out b/vlib/v/checker/tests/index_expr_implicit_int_downcast_err.js.out new file mode 100644 index 000000000..e4983f488 --- /dev/null +++ b/vlib/v/checker/tests/index_expr_implicit_int_downcast_err.js.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/compiler_errors_test.v b/vlib/v/compiler_errors_test.v index 69b17ee73..29312cd3f 100644 --- a/vlib/v/compiler_errors_test.v +++ b/vlib/v/compiler_errors_test.v @@ -94,7 +94,8 @@ fn test_all() { run_dir := '${checker_dir}/run' su_dir := 'vlib/v/tests/skip_unused' no_closures_dir := 'vlib/v/tests/no_closures' - js_checker_tests := ['js_number_requires_explicit_cast.vv'] + js_checker_tests := ['index_expr_implicit_int_downcast_err.vv', + 'js_number_requires_explicit_cast.vv'] disable_explicit_mutability_tests := ['disable_explicit_mutability.vv'] checker_tests := get_tests_in_dir(checker_dir, false).filter(!it.contains('with_check_option') diff --git a/vlib/v/gen/c/index.v b/vlib/v/gen/c/index.v index 4f657535e..a94be910a 100644 --- a/vlib/v/gen/c/index.v +++ b/vlib/v/gen/c/index.v @@ -38,6 +38,25 @@ fn (mut g Gen) c_wide_index_kind(index_type ast.Type) CWideIndexKind { return if internal_index_size < int_size { .plain } else { .unsigned_64 } } +fn (mut g Gen) write_c_range_bound(expr ast.Expr) { + bound_type := g.resolved_expr_type(expr, 0) + match g.c_wide_index_kind(bound_type) { + .signed_64 { + g.write('builtin__v_slice_index_i64(') + g.expr(expr) + g.write(')') + } + .unsigned_64 { + g.write('builtin__v_slice_index_u64(') + g.expr(expr) + g.write(')') + } + else { + g.expr(expr) + } + } +} + fn (mut g Gen) resolved_index_operator_receiver_type(receiver ast.Expr, receiver_type ast.Type) ast.Type { mut resolved_type := g.recheck_concrete_type(g.resolved_expr_type(receiver, receiver_type)) if resolved_type == 0 { @@ -298,13 +317,13 @@ fn (mut g Gen) index_range_expr(node ast.IndexExpr, range ast.RangeExpr) { } g.write(', ') if range.has_low { - g.expr(range.low) + g.write_c_range_bound(range.low) } else { g.write('0') } g.write(', ') if range.has_high { - g.expr(range.high) + g.write_c_range_bound(range.high) } else if sym.info is ast.ArrayFixed { g.write('${sym.info.size}') } else { diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 6186eba27..10b057f51 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -3437,6 +3437,11 @@ fn (mut w Walker) mark_resource_dependencies() { w.fn_by_name('v_fixed_index_i64') w.fn_by_name('v_fixed_index_u64') } + if w.pref.backend == .c + && (w.uses_arr_range_index || w.uses_str_range_index || w.uses_range_index_check) { + w.fn_by_name('v_slice_index_i64') + w.fn_by_name('v_slice_index_u64') + } if w.uses_str_range_index { w.fn_by_name(string_idx_str + '.substr') } -- 2.39.5