diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 3605810568..366273a120 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -315,7 +315,7 @@ pub fn (c &Checker) get_default_fmt(ftyp, typ table.Type) byte { } } if ftyp in [table.string_type, table.bool_type] || - sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return] || ftyp.has_flag(.optional) || + sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type] || ftyp.has_flag(.optional) || sym.has_method('str') { return `s` } else { diff --git a/vlib/v/gen/auto_str_methods.v b/vlib/v/gen/auto_str_methods.v index f8c2215f9a..12e90490a3 100644 --- a/vlib/v/gen/auto_str_methods.v +++ b/vlib/v/gen/auto_str_methods.v @@ -53,7 +53,7 @@ fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string { table.Struct { g.gen_str_for_struct(it, styp, str_fn_name) } table.Map { g.gen_str_for_map(it, styp, str_fn_name) } table.MultiReturn { g.gen_str_for_multi_return(it, styp, str_fn_name) } - table.SumType {} + table.SumType { g.gen_str_for_sum_type(it, styp, str_fn_name) } else { verror("could not generate string method $str_fn_name for type \'$styp\'") } } } @@ -298,8 +298,7 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { } // _str() functions should have a single argument, the indenting ones take 2: g.type_definitions.writeln('string ${str_fn_name}($styp x); // auto') - g.auto_str_funcs.writeln('string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x,0);}') - // + g.auto_str_funcs.writeln('string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x, 0);}') g.type_definitions.writeln('string indent_${str_fn_name}($styp x, int indent_count); // auto') g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp x, int indent_count) {') mut clean_struct_v_type_name := styp.replace('__', '.') @@ -346,6 +345,8 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { } else if sym.kind in [.array, .array_fixed, .map] { g.auto_str_funcs.write('indents, ') g.auto_str_funcs.write('${field_styp_fn_name}( it->${c_name(field.name)}) ') + } else if sym.kind == .sum_type { + g.auto_str_funcs.write('indents, indent_${field_styp_fn_name}(it->${c_name(field.name)}, indent_count + 1)') } else { g.auto_str_funcs.write('indents, it->${c_name(field.name)}') if field.typ == table.bool_type { @@ -381,3 +382,50 @@ fn (mut g Gen) gen_str_for_enum(info table.Enum, styp, str_fn_name string) { g.auto_str_funcs.writeln('\t}') g.auto_str_funcs.writeln('}') } + +fn (mut g Gen) gen_str_for_sum_type(info table.SumType, styp, str_fn_name string) { + mut gen_fn_names := map[string]string + for typ in info.variants { + sym := g.table.get_type_symbol(typ) + if !sym.has_method('str') { + field_styp := g.typ(typ) + field_fn_name := g.gen_str_for_type_with_styp(typ, field_styp) + gen_fn_names[field_styp] = field_fn_name + } + } + // _str() functions should have a single argument, the indenting ones take 2: + g.type_definitions.writeln('string ${str_fn_name}($styp x); // auto') + g.auto_str_funcs.writeln('string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x, 0); }') + g.type_definitions.writeln('string indent_${str_fn_name}($styp x, int indent_count); // auto') + g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp x, int indent_count) {') + mut clean_sum_type_v_type_name := styp.replace('__', '.') + if styp.ends_with('*') { + clean_sum_type_v_type_name = '&' + clean_sum_type_v_type_name.replace('*', '') + } + clean_sum_type_v_type_name = util.strip_main_name(clean_sum_type_v_type_name) + g.auto_str_funcs.writeln('\tswitch(x.typ) {') + for typ in info.variants { + mut value_fmt := '%.*s\\000' + if typ == table.string_type { + value_fmt = '\'$value_fmt\'' + } + typ_str := g.typ(typ) + mut func_name := if typ_str in gen_fn_names { + gen_fn_names[typ_str] + } else { + g.gen_str_for_type_with_styp(typ, typ_str) + } + sym := g.table.get_type_symbol(typ) + if sym.kind == .struct_ { + 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._object') + if sym.kind == .struct_ { + g.auto_str_funcs.write(', indent_count') + } + g.auto_str_funcs.writeln('));') + } + g.auto_str_funcs.writeln('\t\tdefault: return tos_lit("unknown sum type value");') + g.auto_str_funcs.writeln('\t}') + g.auto_str_funcs.writeln('}') +} diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 1c53bf5c7b..6e5086b27d 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -4159,7 +4159,8 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool { g.enum_expr(expr) g.write('")') } - } else if sym_has_str_method || sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return] { + } else if sym_has_str_method || sym.kind in + [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type] { is_p := etype.is_ptr() val_type := if is_p { etype.deref() } else { etype } str_fn_name := g.gen_str_for_type(val_type) @@ -5073,6 +5074,8 @@ fn (g &Gen) type_to_fmt(typ table.Type) string { return '%g\\000' // g removes trailing zeros unlike %f } else if typ == table.u64_type { return '%lld\\000' + } else if sym.kind == .sum_type { + return '%.*s\\000' } return '%d\\000' } diff --git a/vlib/v/tests/sumtype_str_test.v b/vlib/v/tests/sumtype_str_test.v new file mode 100644 index 0000000000..a371ca9bf4 --- /dev/null +++ b/vlib/v/tests/sumtype_str_test.v @@ -0,0 +1,57 @@ +struct Abc { + foo int + bar bool + str string +} + +type ST = int | string | bool | Abc + +fn test_int_st_str() { + a := ST(0) + assert '$a' == 'ST(0)' +} + +fn test_string_st_str() { + a := ST('test') + assert '$a' == 'ST(\'test\')' +} + +fn test_struct_st_str() { + a := ST(Abc{}) + assert '$a' == 'ST(Abc {\n foo: 0\n bar: false\n str: \'\'\n})' +} + +fn test_bool_st_str() { + a := ST(false) + assert '$a' == 'ST(false)' +} + +fn test_str() { + a := ST(false) + assert a.str() == 'ST(false)' +} + +struct Container { + st ST +} + +fn test_in_struct() { + c := Container{ST(0)} + assert '$c' == 'Container {\n st: ST(0)\n}' +} + +fn test_unknown_value() { + c := Container{} + assert '$c' == 'Container {\n st: unknown sum type value\n}' +} + +fn test_nested_in_struct() { + abc := Abc{} + c := Container{ST(abc)} + assert '$c' == 'Container {\n st: ST(Abc {\n foo: 0\n bar: false\n str: \'\'\n })\n}' +} + +fn test_pointer() { + st := ST(0) + assert '${&st}' == '&ST(0)' +}