From 90d1a689db2289887defd285e846635d99a34875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Thu, 24 Sep 2020 21:14:16 +0200 Subject: [PATCH] gen: fix str gen for pointers in structs (#6462) --- vlib/v/gen/auto_str_methods.v | 82 ++++++++++++++++++++++++--------- vlib/v/gen/cgen.v | 12 +++-- vlib/v/tests/str_gen_test.v | 57 +++++++++++++++++++++++ vlib/v/tests/sumtype_str_test.v | 10 ++-- 4 files changed, 129 insertions(+), 32 deletions(-) diff --git a/vlib/v/gen/auto_str_methods.v b/vlib/v/gen/auto_str_methods.v index 12e90490a3..2bb0138701 100644 --- a/vlib/v/gen/auto_str_methods.v +++ b/vlib/v/gen/auto_str_methods.v @@ -291,7 +291,11 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { for field in info.fields { sym := g.table.get_type_symbol(field.typ) if !sym.has_method('str') { - field_styp := g.typ(field.typ) + mut typ := field.typ + if typ.is_ptr() { + typ = typ.deref() + } + field_styp := g.typ(typ) field_fn_name := g.gen_str_for_type_with_styp(field.typ, field_styp) fnames2strfunc[field_styp] = field_fn_name } @@ -318,10 +322,13 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { g.auto_str_funcs.writeln('\t}') g.auto_str_funcs.writeln('\treturn _STR("$clean_struct_v_type_name {\\n"') for field in info.fields { - fmt := g.type_to_fmt(field.typ) - g.auto_str_funcs.writeln('\t\t"%.*s\\000 ' + '$field.name: $fmt\\n"') + mut fmt := g.type_to_fmt(field.typ) + if field.typ.is_ptr() { + fmt = '&$fmt' + } + g.auto_str_funcs.writeln('\t\t"%.*s\\000 $field.name: $fmt\\n"') } - g.auto_str_funcs.write('\t\t"%.*s\\000}", ${2*(info.fields.len+1)}') + g.auto_str_funcs.write('\t\t"%.*s\\000}", ${2 * (info.fields.len + 1)}') if info.fields.len > 0 { g.auto_str_funcs.write(',\n\t\t') for i, field in info.fields { @@ -332,27 +339,19 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { field_styp = field_styp.replace('*', '') } field_styp_fn_name := if has_custom_str { '${field_styp}_str' } else { fnames2strfunc[field_styp] } - if sym.kind == .enum_ { - g.auto_str_funcs.write('indents, ') - g.auto_str_funcs.write('${field_styp_fn_name}( it->${c_name(field.name)} ) ') - } else if sym.kind == .struct_ { - g.auto_str_funcs.write('indents, ') - if has_custom_str { - g.auto_str_funcs.write('${field_styp_fn_name}( it->${c_name(field.name)} ) ') - } else { - g.auto_str_funcs.write('indent_${field_styp_fn_name}( it->${c_name(field.name)}, indent_count + 1 ) ') - } - } 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 { - g.auto_str_funcs.write(' ? _SLIT("true") : _SLIT("false")') + + g.auto_str_funcs.write('indents, ') + func := struct_auto_str_func(sym, field.typ, field_styp_fn_name, field.name) + if field.typ.is_ptr() { + g.auto_str_funcs.write('isnil(it->${c_name(field.name)})') + g.auto_str_funcs.write(' ? tos_lit("nil") : ') + // struct, floats and ints have a special case through the _str function + if sym.kind != .struct_ && !field.typ.is_int() && !field.typ.is_float() { + g.auto_str_funcs.write('*') } } + g.auto_str_funcs.write(func) + if i < info.fields.len - 1 { g.auto_str_funcs.write(',\n\t\t') } @@ -363,6 +362,43 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { g.auto_str_funcs.writeln('}') } +fn struct_auto_str_func(sym table.TypeSymbol, field_type table.Type, fn_name, field_name string) string { + has_custom_str := sym.has_method('str') + if sym.kind == .enum_ { + return '${fn_name}(it->${c_name(field_name)})' + } else if sym.kind == .struct_ { + mut obj := 'it->${c_name(field_name)}' + if field_type.is_ptr() { + obj = '*$obj' + } + if has_custom_str { + return '${fn_name}($obj)' + } else { + return 'indent_${fn_name}($obj, indent_count + 1)' + } + } else if sym.kind in [.array, .array_fixed, .map] { + return '${fn_name}(it->${c_name(field_name)})' + } else if sym.kind == .sum_type { + return 'indent_${fn_name}(it->${c_name(field_name)}, indent_count + 1)' + } else { + mut method_str := 'it->${c_name(field_name)}' + if sym.kind == .bool { + method_str += ' ? _SLIT("true") : _SLIT("false")' + } else if (field_type.is_int() || field_type.is_float()) && field_type.is_ptr() { + // ptr int can be "nil", so this needs to be castet to a string + fmt := if sym.kind in [.f32, .f64] { + '%g\\000' + } else if sym.kind == .u64 { + '%lld\\000' + } else { + '%d\\000' + } + method_str = '_STR("$fmt", 2, *$method_str)' + } + return method_str + } +} + fn (mut g Gen) gen_str_for_enum(info table.Enum, styp, str_fn_name string) { s := util.no_dots(styp) g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto') diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index b0665b8537..e7b0a01d47 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -5062,17 +5062,19 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) { fn (g &Gen) type_to_fmt(typ table.Type) string { sym := g.table.get_type_symbol(typ) - if sym.kind in [.struct_, .array, .array_fixed, .map] { + if (typ.is_int() || typ.is_float()) && typ.is_ptr() { return '%.*s\\000' - } else if typ == table.string_type { + } else if sym.kind in [.struct_, .array, .array_fixed, .map] { + return '%.*s\\000' + } else if sym.kind == .string { return "\'%.*s\\000\'" - } else if typ == table.bool_type { + } else if sym.kind == .bool { return '%.*s\\000' } else if sym.kind == .enum_ { return '%.*s\\000' - } else if typ in [table.f32_type, table.f64_type] { + } else if sym.kind in [.f32, .f64] { return '%g\\000' // g removes trailing zeros unlike %f - } else if typ == table.u64_type { + } else if sym.kind == .u64 { return '%lld\\000' } else if sym.kind == .sum_type { return '%.*s\\000' diff --git a/vlib/v/tests/str_gen_test.v b/vlib/v/tests/str_gen_test.v index b30fa9486e..506f700b0b 100644 --- a/vlib/v/tests/str_gen_test.v +++ b/vlib/v/tests/str_gen_test.v @@ -139,3 +139,60 @@ fn test_fixed_array_of_strings() { assert aa.str() == "['aa', 'bb', 'cc']" assert '$aa' == "['aa', 'bb', 'cc']" } + +struct Wrapper { + foo &string +} +fn test_struct_with_string_pointer() { + s := 'test' + w := Wrapper{&s} + assert '$w' == 'Wrapper {\n foo: &\'test\'\n}' + assert w.str() == 'Wrapper {\n foo: &\'test\'\n}' +} + +struct Wrapper2 { + foo &int +} +fn test_struct_with_int_pointer() { + i := 5 + w := Wrapper2{&i} + assert '$w' == 'Wrapper2 {\n foo: &5\n}' + assert w.str() == 'Wrapper2 {\n foo: &5\n}' +} + +struct Wrapper3 { + foo &bool +} +fn test_struct_with_bool_pointer() { + b := true + w := Wrapper3{&b} + assert '$w' == 'Wrapper3 {\n foo: &true\n}' + assert w.str() == 'Wrapper3 {\n foo: &true\n}' +} + +struct Foo {} +struct Wrapper4 { + foo &Foo +} +fn test_struct_with_struct_pointer() { + b := Foo{} + w := Wrapper4{&b} + assert '$w' == 'Wrapper4 {\n foo: &Foo {\n }\n}' + assert w.str() == 'Wrapper4 {\n foo: &Foo {\n }\n}' +} + +fn test_struct_with_nil() { + w := Wrapper4{} + assert '$w' == 'Wrapper4 {\n foo: &nil\n}' + assert w.str() == 'Wrapper4 {\n foo: &nil\n}' +} + +struct Wrapper5 { + foo &f32 +} +fn test_struct_with_f32_pointer() { + i := f32(5.1) + w := Wrapper5{&i} + assert '$w' == 'Wrapper5 {\n foo: &5.1\n}' + assert w.str() == 'Wrapper5 {\n foo: &5.1\n}' +} diff --git a/vlib/v/tests/sumtype_str_test.v b/vlib/v/tests/sumtype_str_test.v index a371ca9bf4..4e68b09223 100644 --- a/vlib/v/tests/sumtype_str_test.v +++ b/vlib/v/tests/sumtype_str_test.v @@ -9,25 +9,24 @@ type ST = int | string | bool | Abc fn test_int_st_str() { a := ST(0) assert '$a' == 'ST(0)' + assert a.str() == 'ST(0)' } fn test_string_st_str() { a := ST('test') assert '$a' == 'ST(\'test\')' + assert a.str() == 'ST(\'test\')' } fn test_struct_st_str() { a := ST(Abc{}) assert '$a' == 'ST(Abc {\n foo: 0\n bar: false\n str: \'\'\n})' + assert a.str() == '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)' } @@ -38,17 +37,20 @@ struct Container { fn test_in_struct() { c := Container{ST(0)} assert '$c' == 'Container {\n st: ST(0)\n}' + assert c.str() == 'Container {\n st: ST(0)\n}' } fn test_unknown_value() { c := Container{} assert '$c' == 'Container {\n st: unknown sum type value\n}' + assert c.str() == '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}' + assert c.str() == 'Container {\n st: ST(Abc {\n foo: 0\n bar: false\n str: \'\'\n })\n}' } fn test_pointer() {