From fe5b069996d657afb0672d24c2ad1dcc72dc52da Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Fri, 10 Oct 2025 14:13:37 +0300 Subject: [PATCH] parser,cgen: support a `@[ignore_overflow]` fn tag, cleanup cgen (#25470) --- vlib/v/ast/ast.v | 3 ++- vlib/v/gen/c/assign.v | 11 ++++------- vlib/v/gen/c/cgen.v | 5 +++-- vlib/v/gen/c/fn.v | 8 ++++++++ vlib/v/gen/c/for.v | 14 ++++++-------- vlib/v/gen/c/infix.v | 2 +- .../c/testdata/ignore_overflow_tag_check.out | 7 +++++++ .../gen/c/testdata/ignore_overflow_tag_check.vv | 17 +++++++++++++++++ vlib/v/parser/fn.v | 5 +++++ 9 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 vlib/v/gen/c/testdata/ignore_overflow_tag_check.out create mode 100644 vlib/v/gen/c/testdata/ignore_overflow_tag_check.vv diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index ca22b999c..dd72cfbc1 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -600,6 +600,7 @@ pub: is_unsafe bool // true, when @[unsafe] is used on a fn is_must_use bool // true, when @[must_use] is used on a fn. Calls to such functions, that ignore the return value, will cause warnings. is_markused bool // true, when an explicit `@[markused]` tag was put on a fn; `-skip-unused` will not remove that fn + is_ignore_overflow bool // true, when an explicit `@[ignore_overflow]` tag was put on a fn. `-check-overflow` will not generate checks for arithmetic done in that fn. is_file_translated bool // true, when the file it resides in is `@[translated]` is_closure bool // true, for actual closures like `fn [inherited] () {}` . It is false for normal anonymous functions, and for named functions/methods too. receiver StructField // TODO: this is not a struct field @@ -621,7 +622,7 @@ pub: body_pos token.Pos // function bodys position file string generic_names []string - is_direct_arr bool // direct array access + is_direct_arr bool // @[direct_array_access] was used; a[i] inside such a fn, will *not* do array index bounds checks. attrs []Attr ctdefine_idx int = -1 // the index in fn.attrs of `[if xyz]`, when such attribute exists pub mut: diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index f30d34c6b..959c44a36 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -307,13 +307,10 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { scope: unsafe { nil } } mut cur_indexexpr := -1 - is_safe_add_assign := node.op == .plus_assign && g.pref.is_check_overflow - && g.unwrap_generic(var_type).is_int() && !g.is_builtin_overflow_mod - is_safe_sub_assign := node.op == .minus_assign && g.pref.is_check_overflow - && g.unwrap_generic(var_type).is_int() && !g.is_builtin_overflow_mod - is_safe_mul_assign := node.op == .mult_assign && g.pref.is_check_overflow - && g.unwrap_generic(var_type).is_int() && !g.is_builtin_overflow_mod - + consider_int_overflow := g.do_int_overflow_checks && g.unwrap_generic(var_type).is_int() + is_safe_add_assign := node.op == .plus_assign && consider_int_overflow + is_safe_sub_assign := node.op == .minus_assign && consider_int_overflow + is_safe_mul_assign := node.op == .mult_assign && consider_int_overflow left_sym := g.table.sym(g.unwrap_generic(var_type)) is_va_list = left_sym.language == .c && left_sym.name == 'C.va_list' if mut left is ast.Ident { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index b9fdd5ee4..4c9cbb8ed 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -282,6 +282,7 @@ mut: postinclude_nodes []&ast.HashStmtNode // allows hash stmts to go after all the rest of the code generation curr_comptime_node &ast.Expr = unsafe { nil } // current `$if` expr is_builtin_overflow_mod bool + do_int_overflow_checks bool // outside a `@[ignore_overflow] fn abc() {}` or a function in `builtin.overflow` } @[heap] @@ -3916,8 +3917,8 @@ fn (mut g Gen) expr(node_ ast.Expr) { if node.auto_locked != '' { g.writeln('sync__RwMutex_lock(&${node.auto_locked}->mtx);') } - is_safe_inc := node.op == .inc && g.pref.is_check_overflow && !g.is_builtin_overflow_mod - is_safe_dec := node.op == .dec && g.pref.is_check_overflow && !g.is_builtin_overflow_mod + is_safe_inc := g.do_int_overflow_checks && node.op == .inc + is_safe_dec := g.do_int_overflow_checks && node.op == .dec g.inside_map_postfix = true if node.is_c2v_prefix { g.write(node.op.str()) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index f6fb66c4a..ed3834134 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -83,6 +83,14 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) { g.is_direct_array_access = prev_is_direct_array_access } + // handle `@[ignore_overflow] fn abc() {}` and -check-overflow : + prev_do_int_overflow_checks := g.do_int_overflow_checks + g.do_int_overflow_checks = g.pref.is_check_overflow && !g.is_builtin_overflow_mod + && !node.is_ignore_overflow + defer { + g.do_int_overflow_checks = prev_do_int_overflow_checks + } + // handle `@[c_extern] fn C.some_name() int` declarations: old_inside_c_extern := g.inside_c_extern defer { diff --git a/vlib/v/gen/c/for.v b/vlib/v/gen/c/for.v index 3bc0aff9b..a98690ffe 100644 --- a/vlib/v/gen/c/for.v +++ b/vlib/v/gen/c/for.v @@ -140,8 +140,6 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { mut node := unsafe { node_ } mut is_comptime := false - is_safe_op := g.pref.is_check_overflow && !g.is_builtin_overflow_mod - if (node.cond is ast.Ident && node.cond.ct_expr) || node.cond is ast.ComptimeSelector { mut unwrapped_typ := g.unwrap_generic(node.cond_type) ctyp := g.type_resolver.get_type(node.cond) @@ -211,7 +209,7 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { if node.is_range { // `for x in 1..10 {` i := if node.val_var == '_' { g.new_tmp_var() } else { c_name(node.val_var) } - plus_plus_i := if is_safe_op { + plus_plus_i := if g.do_int_overflow_checks { $if new_int ? && x64 { '${i}=builtin__overflow__add_i64(${i},1)' } $else { @@ -250,7 +248,7 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { g.writeln(';') } i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var } - plus_plus_i := if is_safe_op { + plus_plus_i := if g.do_int_overflow_checks { $if new_int ? && x64 { '${i}=builtin__overflow__add_i64(${i},1)' } $else { @@ -326,7 +324,7 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { cond_var = g.expr_string(node.cond) } idx := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var } - plus_plus_idx := if is_safe_op { + plus_plus_idx := if g.do_int_overflow_checks { $if new_int ? && x64 { '${idx}=builtin__overflow__add_i64(${idx},1)' } $else { @@ -384,7 +382,7 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { } dot_or_ptr := g.dot_or_ptr(node.cond_type) idx := g.new_tmp_var() - plus_plus_idx := if is_safe_op { + plus_plus_idx := if g.do_int_overflow_checks { $if new_int ? && x64 { '${idx}=builtin__overflow__add_i64(${idx},1)' } $else { @@ -451,7 +449,7 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { } field_accessor := if node.cond_type.is_ptr() { '->' } else { '.' } i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var } - plus_plus_i := if is_safe_op { + plus_plus_i := if g.do_int_overflow_checks { $if new_int ? && x64 { '${i}=builtin__overflow__add_i64(${i},1)' } $else { @@ -491,7 +489,7 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { g.expr(node.cond) g.writeln(';') i := node.key_var - plus_plus_i := if is_safe_op { + plus_plus_i := if g.do_int_overflow_checks { $if new_int ? && x64 { '${i}=builtin__overflow__add_i64(${i},1)' } $else { diff --git a/vlib/v/gen/c/infix.v b/vlib/v/gen/c/infix.v index 62f61b76e..7af143523 100644 --- a/vlib/v/gen/c/infix.v +++ b/vlib/v/gen/c/infix.v @@ -1256,7 +1256,7 @@ fn (mut g Gen) gen_plain_infix_expr(node ast.InfixExpr) { } // do not use promoted_type for overflow detect left_type := g.unwrap_generic(node.left_type) - checkoverflow_op := g.pref.is_check_overflow && !g.is_builtin_overflow_mod && left_type.is_int() + checkoverflow_op := g.do_int_overflow_checks && left_type.is_int() is_safe_add := checkoverflow_op && node.op == .plus is_safe_sub := checkoverflow_op && node.op == .minus is_safe_mul := checkoverflow_op && node.op == .mul diff --git a/vlib/v/gen/c/testdata/ignore_overflow_tag_check.out b/vlib/v/gen/c/testdata/ignore_overflow_tag_check.out new file mode 100644 index 000000000..b079518f0 --- /dev/null +++ b/vlib/v/gen/c/testdata/ignore_overflow_tag_check.out @@ -0,0 +1,7 @@ +-2 +0 +1 +2 +0 +1 +hi from main diff --git a/vlib/v/gen/c/testdata/ignore_overflow_tag_check.vv b/vlib/v/gen/c/testdata/ignore_overflow_tag_check.vv new file mode 100644 index 000000000..fa287f0bc --- /dev/null +++ b/vlib/v/gen/c/testdata/ignore_overflow_tag_check.vv @@ -0,0 +1,17 @@ +// vtest vflags: -check-overflow + +@[ignore_overflow] +fn arithmetic_i32(x i32, y i32) { + a := x + y + b := x - y + c := x * y + println(a) + println(b) + println(c) +} + +fn main() { + arithmetic_i32(2147483647, 2147483647) + arithmetic_i32(-2147483647, -2147483647) + println('hi from main') +} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 96403cdf5..ac7b32fdc 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -213,6 +213,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { mut is_c2v_variadic := false mut is_c_extern := false mut is_markused := false + mut is_ignore_overflow := false mut is_weak := false mut is_expand_simple_interpolation := false mut comments := []ast.Comment{} @@ -264,6 +265,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl { 'markused' { is_markused = true } + 'ignore_overflow' { + is_ignore_overflow = true + } 'c_extern' { is_c_extern = true } @@ -710,6 +714,7 @@ run them via `v file.v` instead', is_unsafe: is_unsafe is_must_use: is_must_use is_markused: is_markused + is_ignore_overflow: is_ignore_overflow is_weak: is_weak is_file_translated: p.is_translated // -- 2.39.5