From a41a13ac62cef64c319e42b075ad7305ed567cbc Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Fri, 19 Jun 2026 23:19:15 +0300 Subject: [PATCH] fmt: don't prefix current-module types inside `fn` typed struct fields (#27475) (#27506) --- vlib/v/ast/types.v | 14 ++++++++++++++ vlib/v/fmt/fmt.v | 5 +++++ .../struct_fn_field_current_module_type_keep.vv | 14 ++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 vlib/v/fmt/tests/struct_fn_field_current_module_type_keep.vv diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 62d5ad5a6..156ef7050 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -1642,6 +1642,20 @@ pub fn (t &Table) delete_cached_type_to_str(typ Type, import_aliases_len int) { } } +// invalidate_type_to_str_cache clears every cached `type_to_str` result. +// vfmt calls this whenever it enters a new module (see Fmt.set_current_module_name), +// because strings that were memoized before the current module was known (e.g. while +// parsing, when `cmod_prefix` was still empty) keep stale `mod.` prefixes on types +// that actually belong to the current module. Those stale entries are otherwise reused +// when the signature of a `fn` typed struct field is rebuilt, producing output like +// `fn (s main.Struct) bool` instead of `fn (s Struct) bool` (issue #27475). +pub fn (t &Table) invalidate_type_to_str_cache() { + mut mt := unsafe { &Table(t) } + lock mt.cached_type_to_str { + mt.cached_type_to_str.clear() + } +} + // import_aliases is a map of imported symbol aliases 'module.Type' => 'Type' pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]string) string { cache_key := (u64(import_aliases.len) << 32) | u64(typ) diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index a347dd617..e68bd614d 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -268,6 +268,11 @@ fn (mut f Fmt) write_generic_types(gtypes []ast.Type) { pub fn (mut f Fmt) set_current_module_name(cmodname string) { f.cur_mod = cmodname f.table.cmod_prefix = cmodname + '.' + // Drop type_to_str strings that were memoized before the module context was + // known (during parsing `cmod_prefix` is still empty), so that types of the + // current module are not left with a stale `mod.` prefix when a `fn` typed + // field signature is rebuilt later on (issue #27475). + f.table.invalidate_type_to_str_cache() } fn (f &Fmt) get_modname_prefix(mname string) (string, string) { diff --git a/vlib/v/fmt/tests/struct_fn_field_current_module_type_keep.vv b/vlib/v/fmt/tests/struct_fn_field_current_module_type_keep.vv new file mode 100644 index 000000000..b801f37d8 --- /dev/null +++ b/vlib/v/fmt/tests/struct_fn_field_current_module_type_keep.vv @@ -0,0 +1,14 @@ +struct Struct1 {} + +struct Struct2 {} + +// Regression test for https://github.com/vlang/v/issues/27475 : +// types of the current module must not gain a `main.` prefix inside the +// signature of a `fn` typed struct field (the bug only showed up when the +// file had no imports, i.e. an empty alias map). +struct Holder { + field1 fn (s1 Struct1, s2 Struct2) bool + field2 Struct1 + field3 fn (s Struct1) Struct2 + field4 fn (int, Struct1) (Struct2, bool) +} -- 2.39.5