From 249d835b7b9bde1c0180d14bc1b262f0f82ebd55 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 16:42:24 +0300 Subject: [PATCH] cgen: fix wrong shift (fixes #26378) --- vlib/v/gen/c/assign.v | 15 ++++++++- vlib/v/gen/c/cgen.v | 1 + vlib/v/gen/c/cheaders.v | 43 ++++++++++++++++++++++++ vlib/v/gen/c/infix.v | 35 +++++++++++++++++-- vlib/v/tests/shift_test.v | 21 ++++++++++++ vlib/v/tests/unsigned_right_shift_test.v | 12 +++++++ 6 files changed, 124 insertions(+), 3 deletions(-) diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index fa2a106f9..e53ea4acc 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -1397,6 +1397,13 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { final_left_sym := g.table.final_sym(g.unwrap_generic(var_type)) final_right_sym := g.table.final_sym(unwrapped_val_type) mut aligned := 0 + is_safe_shift_assign := !g.pref.translated && !g.file.is_translated + && node.op in [.left_shift_assign, .right_shift_assign] && final_left_sym.is_int() + safe_shift_fn_name := if is_safe_shift_assign { + g.safe_shift_fn_name(var_type, token.assign_op_to_infix_op(node.op)) + } else { + '' + } if final_left_sym.info is ast.Struct { if attr := final_left_sym.info.attrs.find_first('aligned') { aligned = if attr.arg == '' { 0 } else { attr.arg.int() } @@ -1537,11 +1544,15 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } } else if !var_type.has_flag(.option_mut_param_t) && cur_indexexpr == -1 && !str_add && !op_overloaded && !is_safe_add_assign && !is_safe_sub_assign - && !is_safe_mul_assign { + && !is_safe_mul_assign && !is_safe_shift_assign { g.write(' ${op} ') } else if (str_add || op_overloaded) && !is_safe_add_assign && !is_safe_sub_assign && !is_safe_mul_assign { g.write(', ') + } else if is_safe_shift_assign { + g.write(' = ${safe_shift_fn_name}(') + g.expr(left) + g.write(', (u64)') } else if is_safe_add_assign || is_safe_sub_assign || is_safe_mul_assign { overflow_styp := g.styp(get_overflow_fn_type(var_type)) vsafe_fn_name := match true { @@ -1823,6 +1834,8 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { if str_add || op_overloaded || is_safe_add_assign || is_safe_sub_assign || is_safe_mul_assign { g.write(')') + } else if is_safe_shift_assign { + g.write(')') } if node_.op == .assign && var_type.has_flag(.option_mut_param_t) { g.write('.data, sizeof(${g.base_type(val_type)}))') diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 190b8bdd5..6fb6442b7 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1059,6 +1059,7 @@ pub fn (mut g Gen) init() { g.cheaders.writeln('#define VNOFLOAT 1') } g.cheaders.writeln(c_builtin_types) + g.cheaders.writeln(c_shift_helpers) if !g.pref.skip_unused || g.table.used_features.used_maps > 0 { g.cheaders.writeln(c_mapfn_callback_types) } diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index b690151c6..ed413943c 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -572,6 +572,49 @@ typedef struct sync__Channel* chan; #endif ' +const c_shift_helpers = ' +#define V_SAFE_SHIFT_BITS(type) ((u64)(sizeof(type) * 8)) +#define V_SAFE_LSHIFT_UNSIGNED(name, type) static inline type name(type x, u64 y) { return y >= V_SAFE_SHIFT_BITS(type) ? (type)0 : (type)(x << y); } +#define V_SAFE_LSHIFT_SIGNED(name, type, unsigned_type) static inline type name(type x, u64 y) { return y >= V_SAFE_SHIFT_BITS(type) ? (type)0 : (type)(((unsigned_type)x) << y); } +#define V_SAFE_RSHIFT_UNSIGNED(name, type) static inline type name(type x, u64 y) { return y >= V_SAFE_SHIFT_BITS(type) ? (type)0 : (type)(x >> y); } +#define V_SAFE_RSHIFT_SIGNED(name, type) static inline type name(type x, u64 y) { return y >= V_SAFE_SHIFT_BITS(type) ? (type)(x < 0 ? -1 : 0) : (type)(x >> y); } +V_SAFE_LSHIFT_SIGNED(v__lshift_char, char, u8) +V_SAFE_RSHIFT_SIGNED(v__rshift_char, char) +V_SAFE_LSHIFT_SIGNED(v__lshift_i8, i8, u8) +V_SAFE_RSHIFT_SIGNED(v__rshift_i8, i8) +V_SAFE_LSHIFT_SIGNED(v__lshift_i16, i16, u16) +V_SAFE_RSHIFT_SIGNED(v__rshift_i16, i16) +V_SAFE_LSHIFT_SIGNED(v__lshift_i32, i32, u32) +V_SAFE_RSHIFT_SIGNED(v__rshift_i32, i32) +V_SAFE_LSHIFT_SIGNED(v__lshift_int, int, unsigned int) +V_SAFE_RSHIFT_SIGNED(v__rshift_int, int) +V_SAFE_LSHIFT_SIGNED(v__lshift_vint_t, vint_t, u64) +V_SAFE_RSHIFT_SIGNED(v__rshift_vint_t, vint_t) +V_SAFE_LSHIFT_SIGNED(v__lshift_i64, i64, u64) +V_SAFE_RSHIFT_SIGNED(v__rshift_i64, i64) +V_SAFE_LSHIFT_SIGNED(v__lshift_isize, isize, usize) +V_SAFE_RSHIFT_SIGNED(v__rshift_isize, isize) +V_SAFE_LSHIFT_UNSIGNED(v__lshift_u8, u8) +V_SAFE_RSHIFT_UNSIGNED(v__rshift_u8, u8) +V_SAFE_LSHIFT_UNSIGNED(v__lshift_u16, u16) +V_SAFE_RSHIFT_UNSIGNED(v__rshift_u16, u16) +V_SAFE_LSHIFT_UNSIGNED(v__lshift_u32, u32) +V_SAFE_RSHIFT_UNSIGNED(v__rshift_u32, u32) +V_SAFE_LSHIFT_UNSIGNED(v__lshift_u64, u64) +V_SAFE_RSHIFT_UNSIGNED(v__rshift_u64, u64) +V_SAFE_LSHIFT_UNSIGNED(v__lshift_usize, usize) +V_SAFE_RSHIFT_UNSIGNED(v__rshift_usize, usize) +V_SAFE_LSHIFT_UNSIGNED(v__lshift_rune, rune) +V_SAFE_RSHIFT_UNSIGNED(v__rshift_rune, rune) +V_SAFE_LSHIFT_SIGNED(v__lshift_int_literal, int_literal, u64) +V_SAFE_RSHIFT_SIGNED(v__rshift_int_literal, int_literal) +#undef V_SAFE_RSHIFT_SIGNED +#undef V_SAFE_RSHIFT_UNSIGNED +#undef V_SAFE_LSHIFT_SIGNED +#undef V_SAFE_LSHIFT_UNSIGNED +#undef V_SAFE_SHIFT_BITS +' + const c_mapfn_callback_types = ' typedef u64 (*MapHashFn)(voidptr); typedef bool (*MapEqFn)(voidptr, voidptr); diff --git a/vlib/v/gen/c/infix.v b/vlib/v/gen/c/infix.v index e8b0478c3..3c1160962 100644 --- a/vlib/v/gen/c/infix.v +++ b/vlib/v/gen/c/infix.v @@ -7,6 +7,29 @@ import v.ast import v.token import v.util +fn (mut g Gen) safe_shift_fn_name(left_type ast.Type, op token.Kind) string { + shift_type := g.table.unalias_num_type(g.unwrap_generic(left_type)) + prefix := if op == .left_shift { 'v__lshift_' } else { 'v__rshift_' } + return prefix + util.no_dots(g.base_type(shift_type)) +} + +fn (mut g Gen) gen_safe_shift_expr(node ast.InfixExpr) { + left_type := match node.left { + ast.CastExpr { + node.left.typ + } + else { + g.type_resolver.get_type_or_default(node.left, node.left_type) + } + } + g.write(g.safe_shift_fn_name(left_type, node.op)) + g.write('(') + g.expr(node.left) + g.write(', (u64)') + g.expr(node.right) + g.write(')') +} + fn (mut g Gen) infix_expr(node ast.InfixExpr) { g.expected_fixed_arr = true defer { @@ -43,7 +66,11 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { } .right_shift { g.write('(') - g.gen_plain_infix_expr(node) + if g.pref.translated || g.file.is_translated { + g.gen_plain_infix_expr(node) + } else { + g.gen_safe_shift_expr(node) + } g.write(')') } .and, .logical_or { @@ -1492,7 +1519,11 @@ fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) { } } else { g.write('(') - g.gen_plain_infix_expr(node) + if g.pref.translated || g.file.is_translated { + g.gen_plain_infix_expr(node) + } else { + g.gen_safe_shift_expr(node) + } g.write(')') } } diff --git a/vlib/v/tests/shift_test.v b/vlib/v/tests/shift_test.v index 29193a510..22e08e627 100644 --- a/vlib/v/tests/shift_test.v +++ b/vlib/v/tests/shift_test.v @@ -85,3 +85,24 @@ fn test_shift_operators() { x := MyInt(2) assert x << 2 == 8 } + +fn oversized_shift_count() u64 { + return u64(64) +} + +fn test_runtime_oversized_shift_expr() { + bits := u64(9221120237041090561) + shift := oversized_shift_count() + assert bits >> shift == u64(0) + assert bits << shift == u64(0) +} + +fn test_runtime_oversized_shift_assign() { + shift := oversized_shift_count() + mut left := u64(1) + left <<= shift + assert left == u64(0) + mut right := u64(1) + right >>= shift + assert right == u64(0) +} diff --git a/vlib/v/tests/unsigned_right_shift_test.v b/vlib/v/tests/unsigned_right_shift_test.v index a55aac5a7..230fd58b5 100644 --- a/vlib/v/tests/unsigned_right_shift_test.v +++ b/vlib/v/tests/unsigned_right_shift_test.v @@ -41,3 +41,15 @@ fn test_unsigned_right_shift_assignment() { assert z == answer_u32 } } + +fn oversized_unsigned_shift_count() u64 { + return u64(64) +} + +fn test_unsigned_right_shift_oversized_count() { + shift := oversized_unsigned_shift_count() + assert u64(1) >>> shift == u64(0) + mut x := u64(1) + x >>>= shift + assert x == u64(0) +} -- 2.39.5