From ac52d8ad822519455c5d30f05b2695a0b65691b9 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Mon, 25 May 2026 16:33:40 +0300 Subject: [PATCH] cgen: use c_fn_name for fn-variable signatures and closure captures (fix #27109) (#27259) --- vlib/v/gen/c/fn.v | 20 +++++++++++----- vlib/v/gen/c/utils.v | 4 ++-- .../fns/fn_var_name_using_reserved_test.v | 24 +++++++++++++++++++ 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 5fd151729..0478c65c2 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1811,6 +1811,14 @@ fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) { mut has_inherited := false mut is_ptr := false var_name := c_name(var.name) + // `ref_name` is how the captured variable is referenced in the enclosing + // scope. For fn-typed local variables `fn_var_signature` emits `c_fn_name`, + // so the RHS reference must match. The struct field name (LHS) stays as + // `c_name(var.name)` to match `closure_field_cname` reads inside the body. + mut ref_name := var_name + if parent_obj := node.decl.scope.parent.find_var(var.name) { + ref_name = g.var_cname(parent_obj) + } if g.is_mut_closure_fixed_array(var) { if obj := node.decl.scope.find_var(var.name) { if obj.has_inherited { @@ -1819,7 +1827,7 @@ fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) { } } if !has_inherited { - g.writeln('.${var_name} = ${var_name},') + g.writeln('.${var_name} = ${ref_name},') } continue } @@ -1852,13 +1860,13 @@ fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) { if var_sym.info is ast.ArrayFixed { g.write('.${var_name} = {') for i in 0 .. var_sym.info.size { - g.write('${var_name}[${i}],') + g.write('${ref_name}[${i}],') } g.writeln('},') } else if g.is_autofree && !var.is_mut && var_sym.info is ast.Array { - g.writeln('.${var_name} = builtin__array_clone(&${var_name}),') + g.writeln('.${var_name} = builtin__array_clone(&${ref_name}),') } else if g.is_autofree && !var.is_mut && var_sym.kind == .string { - g.writeln('.${var_name} = builtin__string_clone(${var_name}),') + g.writeln('.${var_name} = builtin__string_clone(${ref_name}),') } else { mut is_auto_heap := false mut is_auto_deref_capture := false @@ -1877,9 +1885,9 @@ fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) { } } if (is_auto_heap && !is_ptr) || is_auto_deref_capture || field_name != '' { - g.writeln('.${var_name} = *${var_name}${field_name},') + g.writeln('.${var_name} = *${ref_name}${field_name},') } else { - g.writeln('.${var_name} = ${var_name},') + g.writeln('.${var_name} = ${ref_name},') } } } diff --git a/vlib/v/gen/c/utils.v b/vlib/v/gen/c/utils.v index 0f0e5a5a0..289ba2a6e 100644 --- a/vlib/v/gen/c/utils.v +++ b/vlib/v/gen/c/utils.v @@ -1760,11 +1760,11 @@ fn (mut g Gen) unwrap(typ ast.Type) Type { // generate function variable definition, e.g. `void (*var_name) (int, string)` fn (mut g Gen) fn_var_signature(var_type ast.Type, return_type ast.Type, arg_types []ast.Type, var_name string) string { if var_type.has_flag(.option) || var_type.has_flag(.result) { - return '${g.styp(var_type)} ${c_name(var_name)}' + return '${g.styp(var_type)} ${c_fn_name(var_name)}' } ret_styp := g.styp(return_type) nr_muls := var_type.nr_muls() - mut sig := '${ret_styp} (${'*'.repeat(nr_muls + 1)}${c_name(var_name)}) (' + mut sig := '${ret_styp} (${'*'.repeat(nr_muls + 1)}${c_fn_name(var_name)}) (' for j, arg_typ in arg_types { arg_sym := g.table.sym(arg_typ) if arg_sym.info is ast.FnType { diff --git a/vlib/v/tests/fns/fn_var_name_using_reserved_test.v b/vlib/v/tests/fns/fn_var_name_using_reserved_test.v index a66682873..81d26c37b 100644 --- a/vlib/v/tests/fns/fn_var_name_using_reserved_test.v +++ b/vlib/v/tests/fns/fn_var_name_using_reserved_test.v @@ -9,3 +9,27 @@ fn test_fn_var_name_using_reserved() { println(new(42)) assert new(42) == 42 } + +fn call_fn(do fn () int) int { + return do() +} + +// https://github.com/vlang/v/issues/27109 +fn test_reserved_name_as_fn_param_and_call_arg() { + do := fn () int { + return -1 + } + assert call_fn(do) == -1 +} + +fn call_result_fn(do fn () !int) !int { + return do()! +} + +fn test_reserved_name_as_result_fn_param_and_call_arg() { + do := fn () !int { + return -1 + } + r := call_result_fn(do)! + assert r == -1 +} -- 2.39.5