diff --git a/vlib/v/gen/auto_str_methods.v b/vlib/v/gen/auto_str_methods.v index c97ca5d1d8..e16c4f3114 100644 --- a/vlib/v/gen/auto_str_methods.v +++ b/vlib/v/gen/auto_str_methods.v @@ -600,11 +600,13 @@ fn (mut g Gen) gen_str_for_union_sum_type(info table.SumType, styp string, str_f g.gen_str_for_type(typ) } sym := g.table.get_type_symbol(typ) - if sym.kind == .struct_ { + sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() + deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' } + if sym.kind == .struct_ && !sym_has_str_method { func_name = 'indent_$func_name' } - g.auto_str_funcs.write('\t\tcase $typ: return _STR("${clean_sum_type_v_type_name}($value_fmt)", 2, ${func_name}(*($typ_str*)x._$sym.cname') - if sym.kind == .struct_ { + g.auto_str_funcs.write('\t\tcase $typ: return _STR("${clean_sum_type_v_type_name}($value_fmt)", 2, ${func_name}(${deref}($typ_str*)x._$sym.cname') + if sym.kind == .struct_ && !sym_has_str_method { g.auto_str_funcs.write(', indent_count') } g.auto_str_funcs.writeln('));') diff --git a/vlib/v/tests/sumtype_str_for_subtypes_with_str_test.v b/vlib/v/tests/sumtype_str_for_subtypes_with_str_test.v new file mode 100644 index 0000000000..a9b34f03b5 --- /dev/null +++ b/vlib/v/tests/sumtype_str_for_subtypes_with_str_test.v @@ -0,0 +1,43 @@ +// Test whether a sumtype that has multiple subtypes, +// some with custom .str() methods, can be converted to a string, +// i.e. whether its own autogenerated .str() method will work. +// NB: Dictionary.str() calls $v.str() in the string interpolation, +// which in turn can call Dictionary.str(), i.e. they are mutually +// recursive. +type Object = Dictionary | Stream | bool | f32 | int | string + +struct Dictionary { + items map[string]Object +} + +struct Stream { +mut: + content string +} + +fn (dict Dictionary) str() string { + mut temp := []string{} + for k, v in dict.items { + temp << ' << "$k": ' + v.str().replace('\n', ' ') + } + return '\n' + temp.join('\n') + '\n' +} + +fn test_str_of_sumtype_works() { + o := Object(Dictionary{ + items: { + 'abc': Object(Stream{ + content: 'xyz' + }) + 'aaa': Object(int(321)) + 'bbb': Object(f32(3.14)) + } + }) + so := o.str() + println(so) + assert so.starts_with('Object(') + assert so.contains('<< "abc": Object(Stream{') + assert so.contains('<< "aaa": Object(321)') + assert so.contains('<< "bbb": Object(3.14') + assert so.ends_with(')') +}