From b023a470b579004c3baaa1278ee13cfd69f51836 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 6 May 2026 18:26:10 +0300 Subject: [PATCH] Reapply "cgen: preserve option fn concrete type names (#27085)" This reverts commit 1253f96a3bd0a7efa63f1ea6313e9a407783f643. --- vlib/v/ast/table.v | 7 +++ vlib/v/checker/comptime.v | 22 +++++++- vlib/v/gen/c/cgen.v | 19 +++++-- vlib/v/parser/checks.v | 31 +++++++++++ .../comptime_if_fn_type_signature_test.v | 51 +++++++++++++++++++ .../generics_option_fn_concrete_type_test.v | 18 +++++++ 6 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 vlib/v/tests/comptime/comptime_if_fn_type_signature_test.v create mode 100644 vlib/v/tests/generics/generics_option_fn_concrete_type_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index e57c5b790..cbdb72aea 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -2557,6 +2557,13 @@ pub fn (mut t Table) convert_generic_type(generic_type Type, generic_names []str rtyp = rtyp.set_flag(.option_mut_param_t) } } + resolved_typ_sym := t.sym(t.fully_unaliased_type(typ)) + if resolved_typ_sym.info is FnType && typ.has_flag(.shared_f) { + rtyp = rtyp.set_flag(.shared_f) + } + if resolved_typ_sym.info is FnType && typ.has_flag(.atomic_f) { + rtyp = rtyp.set_flag(.atomic_f) + } return rtyp } match mut sym.info { diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index d665cb5fe..325237e70 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -1631,7 +1631,9 @@ fn (mut c Checker) check_compatible_types(left_type ast.Type, left_name string, return resolved_left_type.has_flag(.option) == right_type.has_flag(.option) && c.table.does_type_implement_interface(resolved_left_type, right_type) } - if right_sym.info is ast.FnType && c.comptime.comptime_for_method_var == left_name { + if right_sym.info is ast.FnType && c.comptime.comptime_for_method_var != '' + && c.comptime.comptime_for_method != unsafe { nil } + && c.comptime.comptime_for_method_var == left_name { right_fn_type := right_sym.info as ast.FnType return c.table.fn_signature(right_fn_type.func, skip_receiver: true @@ -1640,6 +1642,24 @@ fn (mut c Checker) check_compatible_types(left_type ast.Type, left_name string, skip_receiver: true type_only: true ) + } + left_unaliased_type := c.table.fully_unaliased_type(resolved_left_type) + right_unaliased_type := c.table.fully_unaliased_type(right_type) + left_unaliased_sym := c.table.sym(left_unaliased_type) + right_unaliased_sym := c.table.sym(right_unaliased_type) + if left_unaliased_sym.info is ast.FnType && right_unaliased_sym.info is ast.FnType { + same_flags := left_unaliased_type.nr_muls() == right_unaliased_type.nr_muls() + && left_unaliased_type.has_flag(.option) == right_unaliased_type.has_flag(.option) + && left_unaliased_type.has_flag(.result) == right_unaliased_type.has_flag(.result) + && left_unaliased_type.has_flag(.shared_f) == right_unaliased_type.has_flag(.shared_f) + && left_unaliased_type.has_flag(.atomic_f) == right_unaliased_type.has_flag(.atomic_f) + return same_flags && c.table.fn_signature(left_unaliased_sym.info.func, + skip_receiver: true + type_only: true + ) == c.table.fn_signature(right_unaliased_sym.info.func, + skip_receiver: true + type_only: true + ) } else { return resolved_left_type == right_type } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index e62db7d0c..b12e7e61b 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1587,7 +1587,17 @@ fn (mut g Gen) generic_fn_name(types []ast.Type, before string) string { // E.g. `neg` (a specific fn) should produce the same name as `fn(int) int`. sym := g.table.sym(normalized_typ) if sym.info is ast.FnType && sym.kind == .function { - anon_cname := 'anon_fn_${g.table.fn_type_signature(sym.info.func)}' + mut anon_cname := 'anon_fn_${g.table.fn_type_signature(sym.info.func)}' + if normalized_typ.has_flag(.option) { + anon_cname = '${option_name}_${anon_cname}' + } else if normalized_typ.has_flag(.result) { + anon_cname = '${result_name}_${anon_cname}' + } + if normalized_typ.has_flag(.shared_f) { + anon_cname = '__shared__${anon_cname}' + } else if normalized_typ.has_flag(.atomic_f) { + anon_cname = 'atomic_${anon_cname}' + } if anon_cname != typ_name { typ_name = anon_cname } @@ -6314,7 +6324,9 @@ fn (mut g Gen) type_name(raw_type ast.Type) { sym := g.table.sym(typ) mut s := '' if sym.kind == .function { - if typ.is_ptr() { + if typ.has_option_or_result() { + s = g.table.type_to_str(g.unwrap_generic(typ)) + } else if typ.is_ptr() { s = '&' + g.fn_decl_str(sym.info as ast.FnType) } else { s = g.fn_decl_str(sym.info as ast.FnType) @@ -6410,8 +6422,7 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) { typ_name := g.table.get_type_name(fixed_info.elem_type) g.write('_S("[${fixed_info.size}]${util.strip_main_name(typ_name)}")') } else if sym.kind == .function { - info := sym.info as ast.FnType - g.write('_S("${g.fn_decl_str(info)}")') + g.write('_S("${util.strip_main_name(g.table.type_to_str(g.unwrap_generic(typ)))}")') } else if typ.has_flag(.variadic) { varg_elem_type_sym := g.table.sym(g.table.value_type(typ)) g.write('_S("...${util.strip_main_name(varg_elem_type_sym.name)}")') diff --git a/vlib/v/parser/checks.v b/vlib/v/parser/checks.v index 3e8bc1bc6..2a3072938 100644 --- a/vlib/v/parser/checks.v +++ b/vlib/v/parser/checks.v @@ -222,6 +222,16 @@ fn (p &Parser) is_generic_call() bool { tok5 = p.peek_token(6) kind5 = tok5.kind } + if (kind2 == .question || kind2 in [.key_shared, .key_atomic]) && kind3 == .key_fn { + tok2 = tok3 + kind2 = kind3 + tok3 = tok4 + kind3 = kind4 + tok4 = tok5 + kind4 = kind5 + tok5 = p.peek_token(6) + kind5 = tok5.kind + } if kind2 == .lsbr { // case 1 (array or fixed array type) @@ -230,6 +240,27 @@ fn (p &Parser) is_generic_call() bool { if kind2 == .key_struct { return p.is_anon_struct_generic_arg(.lpar) } + if kind2 == .key_fn { + mut i := 3 + mut nested_sbr_count := 0 + for { + cur_tok := p.peek_token(i) + if cur_tok.kind == .eof + || cur_tok.kind !in [.amp, .dot, .comma, .ellipsis, .name, .number, .lpar, .rpar, .lsbr, .rsbr, .question, .not, .key_fn, .key_mut, .key_shared, .key_atomic] { + break + } + if cur_tok.kind == .lsbr { + nested_sbr_count++ + } else if cur_tok.kind == .rsbr { + if nested_sbr_count > 0 { + nested_sbr_count-- + } else { + return p.peek_token(i + 1).kind == .lpar + } + } + i++ + } + } if kind2 == .name { if kind3 == .lsbr && tok2.lit == 'map' { diff --git a/vlib/v/tests/comptime/comptime_if_fn_type_signature_test.v b/vlib/v/tests/comptime/comptime_if_fn_type_signature_test.v new file mode 100644 index 000000000..d37dc3d36 --- /dev/null +++ b/vlib/v/tests/comptime/comptime_if_fn_type_signature_test.v @@ -0,0 +1,51 @@ +type IntMapper = fn (int) int + +type StringMapper = fn (string) string + +fn is_int_mapper[T]() bool { + $if T is fn (int) int { + return true + } $else { + return false + } +} + +fn is_ref_int_mapper[T]() bool { + $if T is &fn (int) int { + return true + } $else { + return false + } +} + +fn forwarded_is_int_mapper[T]() bool { + return is_int_mapper[T]() +} + +fn accepts_fixed_array_param_mapper[T]() bool { + return true +} + +fn test_comptime_if_fn_type_signature() { + assert is_int_mapper[IntMapper]() + assert !is_int_mapper[StringMapper]() + assert is_int_mapper[fn (int) int]() + assert !is_int_mapper[&fn (int) int]() + assert !is_int_mapper[fn (string) string]() +} + +fn test_comptime_if_fn_type_signature_with_shared_modifier() { + assert is_ref_int_mapper[&fn (int) int]() + assert !is_ref_int_mapper[shared fn (int) int]() + assert !is_int_mapper[atomic fn (int) int]() +} + +fn test_comptime_if_fn_type_signature_with_forwarded_modifier() { + assert forwarded_is_int_mapper[fn (int) int]() + assert !forwarded_is_int_mapper[shared fn (int) int]() + assert !forwarded_is_int_mapper[atomic fn (int) int]() +} + +fn test_fn_type_generic_call_detection_with_fixed_array_param() { + assert accepts_fixed_array_param_mapper[fn ([4]int) bool]() +} diff --git a/vlib/v/tests/generics/generics_option_fn_concrete_type_test.v b/vlib/v/tests/generics/generics_option_fn_concrete_type_test.v new file mode 100644 index 000000000..0a245551b --- /dev/null +++ b/vlib/v/tests/generics/generics_option_fn_concrete_type_test.v @@ -0,0 +1,18 @@ +fn option_fn_type_name[T]() string { + return typeof[T]().name +} + +fn is_option_fn[T]() bool { + $if T is ?fn (int) int { + return true + } $else { + return false + } +} + +fn test_option_fn_concrete_type() { + assert option_fn_type_name[fn (int) int]() == 'fn (int) int' + assert option_fn_type_name[?fn (int) int]() == '?fn (int) int' + assert is_option_fn[?fn (int) int]() + assert !is_option_fn[fn (int) int]() +} -- 2.39.5