From 5716ea7682c2af84a94472770e5b14dc3b1d585d Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 15 Apr 2026 02:15:20 +0300 Subject: [PATCH] checker: fix .str() method inconsistent behavior (fixes #25825) --- vlib/v/ast/table.v | 19 +++++++++++++++---- vlib/v/checker/checker.v | 11 ++++++++--- .../tests/interface_implicit_str_char_err.out | 6 ++++++ .../tests/interface_implicit_str_char_err.vv | 11 +++++++++++ .../interface_implicit_str_method_test.v | 9 +++++++++ 5 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 vlib/v/checker/tests/interface_implicit_str_char_err.out create mode 100644 vlib/v/checker/tests/interface_implicit_str_char_err.vv diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index d5fc49cb9..7f79bd80d 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -319,19 +319,27 @@ pub fn (t &Table) type_has_implicit_str_method(typ Type, method &Fn) bool { if typ.has_option_or_result() { return false } - if typ.is_any_kind_of_pointer() { - return false - } sym := t.sym(typ.clear_flag(.variadic)) if sym.has_method_with_generic_parent('str') { return false } + if sym.kind == .char && typ.nr_muls() == 0 { + return false + } + if sym.kind == .function { + return false + } + if typ.is_any_kind_of_pointer() || typ in voidptr_types || typ in byteptr_types + || typ in charptr_types || typ == nil_type { + return true + } match sym.info { Alias, Array, ArrayFixed, Enum, FnType, Struct, Map, MultiReturn, SumType, Chan, Thread { return sym.name != 'nil' } else { - return false + return sym.kind in [.i8, .i16, .i32, .int, .i64, .isize, .u8, .u16, .u32, .u64, .usize, + .f32, .f64, .rune, .bool, .string, .generic_inst] } } } @@ -2259,6 +2267,9 @@ pub fn (t &Table) does_type_implement_interface(typ Type, inter_typ Type) bool { } // verify methods for imethod in inter_sym.info.methods { + if t.is_compatible_auto_str_method(imethod) && typ.nr_muls() == 0 && sym.kind == .char { + return false + } if method := t.find_method_with_embeds(sym, imethod.name) { msg := t.is_same_method(imethod, method) if msg.len > 0 { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e30477f8d..f814bd19c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2213,11 +2213,16 @@ fn (mut c Checker) type_implements_with_mut_receiver(typ ast.Type, interface_typ // Verify methods for imethod in imethods { + if c.table.is_compatible_auto_str_method(imethod) && utyp.nr_muls() == 0 + && typ_sym.kind == .char { + c.error("`${styp}` doesn't implement method `${imethod.name}` of interface `${inter_sym.name}`", + pos) + are_methods_implemented = false + continue + } mut method := typ_sym.find_method_with_generic_parent(imethod.name) or { c.table.find_method_with_embeds(typ_sym, imethod.name) or { - // `str() string` is auto-generated for all types during cgen - if imethod.name == 'str' && imethod.params.len == 1 - && imethod.return_type == ast.string_type { + if c.table.type_has_implicit_str_method(utyp, imethod) { are_methods_implemented = true continue } diff --git a/vlib/v/checker/tests/interface_implicit_str_char_err.out b/vlib/v/checker/tests/interface_implicit_str_char_err.out new file mode 100644 index 000000000..326db99ca --- /dev/null +++ b/vlib/v/checker/tests/interface_implicit_str_char_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/interface_implicit_str_char_err.vv:10:7: error: `char` doesn't implement method `str` of interface `Stringy` + 8 | + 9 | fn main() { + 10 | take(char(91)) + | ~~~~~~~~ + 11 | } diff --git a/vlib/v/checker/tests/interface_implicit_str_char_err.vv b/vlib/v/checker/tests/interface_implicit_str_char_err.vv new file mode 100644 index 000000000..433a46b1b --- /dev/null +++ b/vlib/v/checker/tests/interface_implicit_str_char_err.vv @@ -0,0 +1,11 @@ +interface Stringy { + str() string +} + +fn take(v Stringy) { + _ = v +} + +fn main() { + take(char(91)) +} diff --git a/vlib/v/tests/interfaces/interface_implicit_str_method_test.v b/vlib/v/tests/interfaces/interface_implicit_str_method_test.v index 3e595c7c6..d6dcff111 100644 --- a/vlib/v/tests/interfaces/interface_implicit_str_method_test.v +++ b/vlib/v/tests/interfaces/interface_implicit_str_method_test.v @@ -12,6 +12,10 @@ fn join(values ...Stringy) string { return values.map(it.str()).join(' ') } +fn collect(values []Stringy) string { + return values.map(it.str()).join(' ') +} + fn test_interface_implicit_str_method() { some_struct := SomeStruct{} assert stringify('hi there') == 'hi there' @@ -20,3 +24,8 @@ fn test_interface_implicit_str_method() { assert stringify(some_struct) == 'SomeStruct{}' assert join('hi', 42, false, some_struct) == 'hi 42 false SomeStruct{}' } + +fn test_interface_implicit_str_method_for_pointer_arrays() { + values := [&SomeStruct{}] + assert collect(values) == 'SomeStruct{}' +} -- 2.39.5