From 64343168c6f2d8380bd8bdb0960f90959af8cd33 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sat, 13 Sep 2025 02:49:09 -0300 Subject: [PATCH] cgen: fix option variadic arg passing (fix #25261) (#25273) --- cmd/tools/vast/vast.v | 1 + vlib/v/ast/ast.v | 1 + vlib/v/ast/types.v | 2 +- vlib/v/checker/fn.v | 1 + vlib/v/gen/c/fn.v | 17 +++++++++++++-- vlib/v/markused/walker.v | 3 +++ .../v/tests/options/option_fn_variadic_test.v | 21 +++++++++++++++++++ 7 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 vlib/v/tests/options/option_fn_variadic_test.v diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 67394cf1f..f74e32529 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -1614,6 +1614,7 @@ fn (t Tree) call_expr(node ast.CallExpr) &Node { obj.add_terse('is_ctor_new', t.bool_node(node.is_ctor_new)) obj.add_terse('is_return_used', t.bool_node(node.is_return_used)) obj.add_terse('is_static_method', t.bool_node(node.is_static_method)) + obj.add_terse('is_variadic', t.bool_node(node.is_variadic)) obj.add('should_be_skipped', t.bool_node(node.should_be_skipped)) obj.add_terse('free_receiver', t.bool_node(node.free_receiver)) obj.add('scope', t.number_node(int(node.scope))) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index b21534929..5c948082a 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -830,6 +830,7 @@ pub mut: is_ctor_new bool // if JS ctor calls requires `new` before call, marked as `[use_new]` in V is_file_translated bool // true, when the file it resides in is `@[translated]` is_static_method bool // it is a static method call + is_variadic bool args []CallArg expected_arg_types []Type comptime_ret_val bool diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 31dd3896a..0e30e7d59 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -1617,7 +1617,7 @@ pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string] if nr_muls > 0 && !typ.has_flag(.variadic) { res = strings.repeat(`&`, nr_muls) + res } - if typ.has_flag(.option) { + if typ.has_flag(.option) && res[0] != `?` { res = '?${res}' } if typ.has_flag(.result) { diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 0c2b338fa..1c6ebf8f7 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -2907,6 +2907,7 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) ! min_required_params-- } if f.is_variadic { + node.is_variadic = f.is_variadic min_required_params-- c.markused_array_method(!c.is_builtin_mod, '') } else { diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index c937d3de1..77ddecb61 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -2398,8 +2398,8 @@ fn (mut g Gen) call_args(node ast.CallExpr) { } } // only v variadic, C variadic args will be appended like normal args - is_variadic := expected_types.len > 0 && expected_types.last().has_flag(.variadic) - && node.language == .v + is_variadic := node.language == .v && node.is_variadic && expected_types.len > 0 + && expected_types.last().has_flag(.variadic) mut already_decomposed := false for i, arg in args { if is_variadic && i == expected_types.len - 1 { @@ -2568,6 +2568,14 @@ fn (mut g Gen) call_args(node ast.CallExpr) { false) } else { noscan := g.check_noscan(arr_info.elem_type) + is_option := arr_info.elem_type.has_flag(.option) + tmp_var := if is_option { g.new_tmp_var() } else { '' } + base_type := g.base_type(varg_type) + tmp := if is_option { g.go_before_last_stmt() } else { '' } + if is_option { + g.writeln('${g.styp(varg_type)} ${tmp_var};') + g.write('builtin___option_ok((${base_type}[]) {') + } g.write('builtin__new_array_from_c_array${noscan}(${variadic_count}, ${variadic_count}, sizeof(${elem_type}), _MOV((${elem_type}[${variadic_count}]){') for j in arg_nr .. args.len { g.ref_or_deref_arg(args[j], arr_info.elem_type, node.language, @@ -2577,6 +2585,11 @@ fn (mut g Gen) call_args(node ast.CallExpr) { } } g.write('}))') + if is_option { + g.writeln(' }, (${option_name}*)&${tmp_var}, sizeof(${base_type}));') + g.write(tmp) + g.write(tmp_var) + } } } } else { diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 4dfc9b69b..57bef1683 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -945,6 +945,9 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { for arg in node.args { w.expr(arg.expr) } + if node.is_variadic && node.expected_arg_types.last().has_flag(.option) { + w.used_option++ + } for concrete_type in node.concrete_types { w.mark_by_type(concrete_type) } diff --git a/vlib/v/tests/options/option_fn_variadic_test.v b/vlib/v/tests/options/option_fn_variadic_test.v new file mode 100644 index 000000000..0fb1b4b04 --- /dev/null +++ b/vlib/v/tests/options/option_fn_variadic_test.v @@ -0,0 +1,21 @@ +struct Empty { +} + +type Elem = int | Empty + +fn new_elems(elems ...?int) []Elem { + mut out := []Elem{} + for elem in elems { + if elem == none { + out << Empty{} + } else { + out << elem + } + } + return out +} + +fn test_main() { + elems := new_elems(0, none, 2) + assert '${elems}' == '[Elem(0), Elem(Empty{}), Elem(2)]' +} -- 2.39.5