From c141d34592abb9f3c296d94fdc7107e11f7c0b1d Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Fri, 19 Dec 2025 09:52:24 -0300 Subject: [PATCH] cgen: fix fnptr indirections declaration (fix #25940) (#25987) --- vlib/v/checker/fn.v | 5 ++++- vlib/v/checker/tests/fn_ptr_undereferenced_err.out | 6 ++++++ vlib/v/checker/tests/fn_ptr_undereferenced_err.vv | 11 +++++++++++ vlib/v/gen/c/assign.v | 2 +- vlib/v/gen/c/cgen.v | 3 +++ vlib/v/gen/c/fn.v | 11 ++++++++--- vlib/v/gen/c/for.v | 2 +- vlib/v/gen/c/if.v | 9 +++++++-- vlib/v/gen/c/testdata/assign_fn_addr.c.must_have | 4 ++-- vlib/v/tests/fns/anon_with_prefix_test.v | 4 ++-- vlib/v/tests/fns/fn_ptr_deref_test.v | 14 ++++++++++++++ vlib/v/tests/fns/iter_alias_fn_test.v | 3 ++- vlib/v/tests/options/option_fn_guard_test.v | 10 ++++++---- 13 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 vlib/v/checker/tests/fn_ptr_undereferenced_err.out create mode 100644 vlib/v/checker/tests/fn_ptr_undereferenced_err.vv create mode 100644 vlib/v/tests/fns/fn_ptr_deref_test.v diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 63cddd97b..8025e8d1d 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1276,7 +1276,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } // check for arg (var) of fn type if !found { - mut typ := 0 + mut typ := ast.no_type if mut obj := node.scope.find(node.name) { match mut obj { ast.GlobalField { @@ -1300,6 +1300,9 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } node.is_fn_var = true node.fn_var_type = typ + if typ.nr_muls() > 0 { + c.error('function pointer must be undereferenced first', node.pos) + } } else {} } diff --git a/vlib/v/checker/tests/fn_ptr_undereferenced_err.out b/vlib/v/checker/tests/fn_ptr_undereferenced_err.out new file mode 100644 index 000000000..0e390b95f --- /dev/null +++ b/vlib/v/checker/tests/fn_ptr_undereferenced_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/fn_ptr_undereferenced_err.vv:10:2: error: function pointer must be undereferenced first + 8 | ref := &f + 9 | deref := ref + 10 | deref() + | ~~~~~~~ + 11 | } diff --git a/vlib/v/checker/tests/fn_ptr_undereferenced_err.vv b/vlib/v/checker/tests/fn_ptr_undereferenced_err.vv new file mode 100644 index 000000000..c24f12956 --- /dev/null +++ b/vlib/v/checker/tests/fn_ptr_undereferenced_err.vv @@ -0,0 +1,11 @@ +module main + +fn f_a() { +} + +fn main() { + f := f_a + ref := &f + deref := ref + deref() +} diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 9e42d56b4..4ae99c533 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -799,7 +799,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { ret_styp := g.styp(g.unwrap_generic(val_type)) g.write('${ret_styp} ${fn_name}') } else { - g.write_fntype_decl(fn_name, right_sym.info) + g.write_fntype_decl(fn_name, right_sym.info, var_type.nr_muls()) } } else { if is_decl { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index b4c27027b..75d34d1b7 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5747,6 +5747,8 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) { g.expr(node.expr) g.write('), sizeof(${expr_styp})),._typ=${u32(expr_typ)}})') } else { + old_inside_assign_fn_var := g.inside_assign_fn_var + g.inside_assign_fn_var = g.table.final_sym(expr_type).kind == .function g.write('(') if node.expr is ast.Ident { if !node.typ.is_ptr() && node.expr_type.is_ptr() && node.expr.obj is ast.Var @@ -5766,6 +5768,7 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) { g.write('(${expr_styp})') } g.expr(node.expr) + g.inside_assign_fn_var = old_inside_assign_fn_var g.write('))') } } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 2c6a13556..200369f70 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1080,8 +1080,13 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { unwrapped_styp = unwrapped_styp[3..] } if node.is_return_used { + is_fn := g.table.final_sym(unwrapped_typ).kind == .function // return value is used, so we need to write the unwrapped temporary var - g.write('\n ${cur_line}(*(${unwrapped_styp}*)${tmp_opt}.data)') + if is_fn && unwrapped_typ.nr_muls() > 0 { + g.write('\n ${cur_line}((${unwrapped_styp}*)${tmp_opt}.data)') + } else { + g.write('\n ${cur_line}(*(${unwrapped_styp}*)${tmp_opt}.data)') + } } else { g.write('\n ${cur_line}') } @@ -3051,7 +3056,7 @@ fn call_convention_attribute(cconvention string, is_cc_msvc bool) string { return if is_cc_msvc { '__${cconvention} ' } else { '__attribute__((${cconvention})) ' } } -fn (mut g Gen) write_fntype_decl(fn_name string, info ast.FnType) { +fn (mut g Gen) write_fntype_decl(fn_name string, info ast.FnType, nr_muls int) { ret_styp := g.styp(info.func.return_type) mut call_conv := '' mut msvc_call_conv := '' @@ -3072,7 +3077,7 @@ fn (mut g Gen) write_fntype_decl(fn_name string, info ast.FnType) { } else { '' } - g.write('${ret_styp} (${msvc_call_conv}*${fn_name}) (') + g.write('${ret_styp} (${msvc_call_conv}${'*'.repeat(nr_muls + 1)}${fn_name}) (') def_pos := g.definitions.len g.fn_decl_params(info.func.params, unsafe { nil }, false, false) g.definitions.go_back(g.definitions.len - def_pos) diff --git a/vlib/v/gen/c/for.v b/vlib/v/gen/c/for.v index 59a2a9a30..e216527c3 100644 --- a/vlib/v/gen/c/for.v +++ b/vlib/v/gen/c/for.v @@ -562,7 +562,7 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) { g.write('\tmemcpy(${val}, ${t_var}.data, sizeof(${val_styp}));') } else { if ret_sym.info is ast.FnType { - g.write_fntype_decl(val, ret_sym.info) + g.write_fntype_decl(val, ret_sym.info, 0) g.writeln(' = **(${val_styp}**)&${t_var}.data;') } else { g.writeln('\t${val_styp} ${val} = *(${val_styp}*)${t_var}.data;') diff --git a/vlib/v/gen/c/if.v b/vlib/v/gen/c/if.v index 3eb7ed23b..90f94c9ea 100644 --- a/vlib/v/gen/c/if.v +++ b/vlib/v/gen/c/if.v @@ -402,11 +402,16 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { } expr_sym := g.table.sym(branch.cond.expr_type) if expr_sym.info is ast.FnType { - g.write_fntype_decl(left_var_name, expr_sym.info) + g.write_fntype_decl(left_var_name, expr_sym.info, branch.cond.expr_type.nr_muls()) + if branch.cond.expr_type.nr_muls() == 0 { + g.writeln(' = *(${base_type}*)${var_name}${dot_or_ptr}data;') + } else { + g.writeln(' = (${base_type}*)${var_name}${dot_or_ptr}data;') + } } else { g.write('\t${base_type} ${left_var_name}') + g.writeln(' = *(${base_type}*)${var_name}${dot_or_ptr}data;') } - g.writeln(' = *(${base_type}*)${var_name}${dot_or_ptr}data;') } } else if branch.cond.vars.len > 1 { sym := g.table.sym(branch.cond.expr_type) diff --git a/vlib/v/gen/c/testdata/assign_fn_addr.c.must_have b/vlib/v/gen/c/testdata/assign_fn_addr.c.must_have index 4433816d0..6fa13df4c 100644 --- a/vlib/v/gen/c/testdata/assign_fn_addr.c.must_have +++ b/vlib/v/gen/c/testdata/assign_fn_addr.c.must_have @@ -1,2 +1,2 @@ -string (*cb2) (void) = &cb1; -string (*cb3) (void) = &cb2; \ No newline at end of file +string (**cb2) (void) = &cb1; +string (***cb3) (void) = &cb2; \ No newline at end of file diff --git a/vlib/v/tests/fns/anon_with_prefix_test.v b/vlib/v/tests/fns/anon_with_prefix_test.v index e7070ef7e..386588839 100644 --- a/vlib/v/tests/fns/anon_with_prefix_test.v +++ b/vlib/v/tests/fns/anon_with_prefix_test.v @@ -5,13 +5,13 @@ struct TestA { } fn test_main() { - func := &fn () int { + func := fn () int { arr := []TestA{} return arr.len } assert func() == 0 - func2 := &fn () int { + func2 := fn () int { arr := []TestA{} return arr.len } diff --git a/vlib/v/tests/fns/fn_ptr_deref_test.v b/vlib/v/tests/fns/fn_ptr_deref_test.v new file mode 100644 index 000000000..a715bbef9 --- /dev/null +++ b/vlib/v/tests/fns/fn_ptr_deref_test.v @@ -0,0 +1,14 @@ +module main + +fn f_a() { +} + +fn test_main() { + f := f_a + f() + assert voidptr(f) == voidptr(f_a) + ref := &f + deref := *ref + assert voidptr(deref) == voidptr(f_a) + deref() +} diff --git a/vlib/v/tests/fns/iter_alias_fn_test.v b/vlib/v/tests/fns/iter_alias_fn_test.v index 4cab67ff2..84d6ef88e 100644 --- a/vlib/v/tests/fns/iter_alias_fn_test.v +++ b/vlib/v/tests/fns/iter_alias_fn_test.v @@ -18,7 +18,8 @@ fn test_main() { mut c := 0 for p in Iter{} { println(p) - p() + w := *p + w() c += 1 } assert c == 1 diff --git a/vlib/v/tests/options/option_fn_guard_test.v b/vlib/v/tests/options/option_fn_guard_test.v index 89c52db1c..3b227744e 100644 --- a/vlib/v/tests/options/option_fn_guard_test.v +++ b/vlib/v/tests/options/option_fn_guard_test.v @@ -10,12 +10,14 @@ fn get_ptr() ?&MyFn { fn test_main() { if p := get_ptr() { - assert voidptr(p) == voidptr(hello) + assert voidptr(*p) == voidptr(hello) dump(p) - p() + w := *p + w() } p2 := get_ptr() or { return } - p2() - assert voidptr(p2) == voidptr(hello) + w2 := *p2 + w2() + assert voidptr(*p2) == voidptr(hello) } -- 2.39.5