From 9d5be12517e6fde00b60d30500e1622f35ae034e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Tue, 3 Nov 2020 14:13:35 +0100 Subject: [PATCH] gen: add prefix when printing type aliases (#6701) --- vlib/v/gen/auto_str_methods.v | 100 ++++++++++++++++++------ vlib/v/gen/cgen.v | 45 ++++++----- vlib/v/gen/fn.v | 140 ++++++++++++++++++---------------- vlib/v/gen/str.v | 11 +-- vlib/v/parser/parser.v | 1 + vlib/v/table/atypes.v | 1 + vlib/v/tests/str_gen_test.v | 40 +++++++++- 7 files changed, 224 insertions(+), 114 deletions(-) diff --git a/vlib/v/gen/auto_str_methods.v b/vlib/v/gen/auto_str_methods.v index 9bf68fd97c..948e33ceb8 100644 --- a/vlib/v/gen/auto_str_methods.v +++ b/vlib/v/gen/auto_str_methods.v @@ -10,9 +10,11 @@ import v.util fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string { mut sym := g.table.get_type_symbol(g.unwrap_generic(typ)) mut str_fn_name := styp_to_str_fn_name(styp) - if sym.info is table.Alias { - sym = g.table.get_type_symbol((sym.info as table.Alias).parent_type) - str_fn_name = styp_to_str_fn_name(sym.name.replace('.', '__')) + if sym.info is table.Alias as sym_info { + if sym_info.is_import { + sym = g.table.get_type_symbol((sym.info as table.Alias).parent_type) + str_fn_name = styp_to_str_fn_name(sym.name.replace('.', '__')) + } } sym_has_str_method, str_method_expects_ptr, str_nr_args := sym.str_method_info() // generate for type @@ -45,16 +47,38 @@ fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string { eprintln('> gen_str_for_type_with_styp: |typ: ${typ:5}, ${sym.name:20}|has_str: ${sym_has_str_method:5}|expects_ptr: ${str_method_expects_ptr:5}|nr_args: ${str_nr_args:1}|fn_name: ${str_fn_name:20}') } g.str_types << already_generated_key - match sym.info { - table.Alias { g.gen_str_default(sym, styp, str_fn_name) } - table.Array { g.gen_str_for_array(it, styp, str_fn_name) } - table.ArrayFixed { g.gen_str_for_array_fixed(it, styp, str_fn_name) } - table.Enum { g.gen_str_for_enum(it, styp, str_fn_name) } - 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 { g.gen_str_for_sum_type(it, styp, str_fn_name) } - else { verror("could not generate string method $str_fn_name for type \'$styp\'") } + match sym.info as sym_info { + table.Alias { + if sym_info.is_import { + g.gen_str_default(sym, styp, str_fn_name) + } else { + g.gen_str_for_alias(sym_info, styp, str_fn_name) + } + } + table.Array { + g.gen_str_for_array(it, styp, str_fn_name) + } + table.ArrayFixed { + g.gen_str_for_array_fixed(it, styp, str_fn_name) + } + table.Enum { + g.gen_str_for_enum(it, styp, str_fn_name) + } + 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 { + g.gen_str_for_sum_type(it, styp, str_fn_name) + } + else { + verror("could not generate string method $str_fn_name for type \'$styp\'") + } } } // if varg, generate str for varg @@ -69,10 +93,36 @@ fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string { return str_fn_name } +fn (mut g Gen) gen_str_for_alias(info table.Alias, styp string, str_fn_name string) { + sym := g.table.get_type_symbol(info.parent_type) + sym_has_str_method, _, _ := sym.str_method_info() + mut parent_str_fn_name := styp_to_str_fn_name(sym.name.replace('.', '__')) + if !sym_has_str_method { + parent_styp := g.typ(info.parent_type) + parent_str_fn_name = g.gen_str_for_type_with_styp(info.parent_type, parent_styp) + } + mut clean_type_v_type_name := util.strip_main_name(styp.replace('__', '.')) + g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto') + g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }') + g.type_definitions.writeln('string indent_${str_fn_name}($styp it, int indent_count); // auto') + g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp it, int indent_count) {') + g.auto_str_funcs.writeln('\tstring indents = tos_lit("");') + g.auto_str_funcs.writeln('\tfor (int i = 0; i < indent_count; ++i) {') + g.auto_str_funcs.writeln('\t\tindents = string_add(indents, tos_lit(" "));') + g.auto_str_funcs.writeln('\t}') + g.auto_str_funcs.writeln('\treturn _STR("%.*s\\000${clean_type_v_type_name}(%.*s\\000)", 3, indents, ${parent_str_fn_name}(it));') + g.auto_str_funcs.writeln('}') +} + fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name string) { - sym := g.table.get_type_symbol(info.elem_type) - field_styp := g.typ(info.elem_type) - is_elem_ptr := info.elem_type.is_ptr() + mut typ := info.elem_type + mut sym := g.table.get_type_symbol(info.elem_type) + if sym.info is table.Alias as alias_info { + typ = alias_info.parent_type + sym = g.table.get_type_symbol(alias_info.parent_type) + } + field_styp := g.typ(typ) + is_elem_ptr := typ.is_ptr() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() mut elem_str_fn_name := '' if sym_has_str_method { @@ -85,8 +135,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name stri elem_str_fn_name = styp_to_str_fn_name(field_styp) } if !sym_has_str_method { - // eprintln('> sym.name: does not have method `str`') - g.gen_str_for_type_with_styp(info.elem_type, field_styp) + g.gen_str_for_type_with_styp(typ, field_styp) } g.type_definitions.writeln('string ${str_fn_name}($styp a); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}') @@ -117,7 +166,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name stri } } g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, x);') - if g.pref.autofree && info.elem_type != table.bool_type { + if g.pref.autofree && typ != table.bool_type { // no need to free "true"/"false" literals g.auto_str_funcs.writeln('\t\tstring_free(&x);') } @@ -134,9 +183,14 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name stri } fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp string, str_fn_name string) { - sym := g.table.get_type_symbol(info.elem_type) - field_styp := g.typ(info.elem_type) - is_elem_ptr := info.elem_type.is_ptr() + mut typ := info.elem_type + mut sym := g.table.get_type_symbol(info.elem_type) + if sym.info is table.Alias as alias_info { + typ = alias_info.parent_type + sym = g.table.get_type_symbol(alias_info.parent_type) + } + field_styp := g.typ(typ) + is_elem_ptr := typ.is_ptr() sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() mut elem_str_fn_name := '' if sym_has_str_method { @@ -146,7 +200,7 @@ fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp string, str_f elem_str_fn_name = styp_to_str_fn_name(field_styp) } if !sym.has_method('str') { - g.gen_str_for_type_with_styp(info.elem_type, field_styp) + elem_str_fn_name = g.gen_str_for_type_with_styp(typ, field_styp) } g.type_definitions.writeln('string ${str_fn_name}($styp a); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}') diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index cb85cdf33d..8b8a0d8b8d 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2090,7 +2090,11 @@ fn (mut g Gen) expr(node ast.Expr) { g.write('))') } else { styp := g.typ(node.typ) - g.write('(($styp)(') + mut cast_label := '' + if sym.kind != .alias || (sym.info as table.Alias).parent_type != node.expr_type { + cast_label = '($styp)' + } + g.write('(${cast_label}(') g.expr(node.expr) if node.expr is ast.IntegerLiteral && node.typ in [table.u64_type, table.u32_type, table.u16_type] { @@ -4402,18 +4406,22 @@ fn (g &Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol { } fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool { - mut sym := g.table.get_type_symbol(etype) - if sym.info is table.Alias { - sym = g.table.get_type_symbol((sym.info as table.Alias).parent_type) + mut typ := etype + mut sym := g.table.get_type_symbol(typ) + if sym.info is table.Alias as alias_info { + parent_sym := g.table.get_type_symbol(alias_info.parent_type) + if parent_sym.has_method('str') { + sym = parent_sym + typ = alias_info.parent_type + } } sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() - if etype.has_flag(.variadic) { - str_fn_name := g.gen_str_for_type(etype) + if typ.has_flag(.variadic) { + str_fn_name := g.gen_str_for_type(typ) g.write('${str_fn_name}(') g.expr(expr) g.write(')') - } else if sym.kind == .alias && (sym.info as table.Alias).parent_type == table.string_type { - // handle string aliases + } else if typ == table.string_type { g.expr(expr) return true } else if sym.kind == .enum_ { @@ -4422,7 +4430,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool { else { false } } if is_var { - str_fn_name := g.gen_str_for_type(etype) + str_fn_name := g.gen_str_for_type(typ) g.write('${str_fn_name}(') g.enum_expr(expr) g.write(')') @@ -4433,20 +4441,20 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool { } } 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 } + is_p := typ.is_ptr() + val_type := if is_p { typ.deref() } else { typ } str_fn_name := g.gen_str_for_type(val_type) if is_p && str_method_expects_ptr { - g.write('string_add(_SLIT("&"), ${str_fn_name}( (') + g.write('string_add(_SLIT("&"), ${str_fn_name}((') } if is_p && !str_method_expects_ptr { - g.write('string_add(_SLIT("&"), ${str_fn_name}( *(') + g.write('string_add(_SLIT("&"), ${str_fn_name}(*(') } if !is_p && !str_method_expects_ptr { - g.write('${str_fn_name}( ') + g.write('${str_fn_name}(') } if !is_p && str_method_expects_ptr { - g.write('${str_fn_name}( &') + g.write('${str_fn_name}(&') } if expr is ast.ArrayInit { if expr.is_fixed { @@ -4473,13 +4481,16 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool { g.write(')') } } - } else if g.typ(etype).starts_with('Option') { + } else if g.typ(typ).starts_with('Option') { str_fn_name := 'OptionBase_str' g.write('${str_fn_name}(*(OptionBase*)&') g.expr(expr) g.write(')') } else { - return error('cannot convert to string') + str_fn_name := g.gen_str_for_type(typ) + g.write('${str_fn_name}(') + g.expr(expr) + g.write(')') } return true } diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index bebed796a4..853e96f54c 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -548,82 +548,94 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { // cgen shouldn't modify ast nodes, this should be moved // g.generate_tmp_autofree_arg_vars(node, name) // Handle `print(x)` + mut print_auto_str := false if is_print && node.args[0].typ != table.string_type { // && !free_tmp_arg_vars { - typ := node.args[0].typ + mut typ := node.args[0].typ if typ == 0 { g.checker_bug('print arg.typ is 0', node.pos) } - mut styp := g.typ(typ) - sym := g.table.get_type_symbol(typ) - if typ.is_ptr() { - styp = styp.replace('*', '') + mut sym := g.table.get_type_symbol(typ) + if sym.info is table.Alias as alias_info { + typ = alias_info.parent_type + sym = g.table.get_type_symbol(alias_info.parent_type) } - mut str_fn_name := g.gen_str_for_type_with_styp(typ, styp) - if g.autofree && !typ.has_flag(.optional) { - // Create a temporary variable so that the value can be freed - tmp := g.new_tmp_var() - // tmps << tmp - g.write('string $tmp = ${str_fn_name}(') - g.expr(node.args[0].expr) - g.writeln('); ${print_method}($tmp); string_free(&$tmp); //MEM2 $styp') - } else { - expr := node.args[0].expr - is_var := match expr { - ast.SelectorExpr { true } - ast.Ident { true } - else { false } + // check if alias parent also not a string + if typ != table.string_type { + mut styp := g.typ(typ) + if typ.is_ptr() { + styp = styp.replace('*', '') } - if typ.is_ptr() && sym.kind != .struct_ { - // ptr_str() for pointers - styp = 'ptr' - str_fn_name = 'ptr_str' - } - if sym.kind == .enum_ { - if is_var { - g.write('${print_method}(${str_fn_name}(') - } else { - // when no var, print string directly - g.write('${print_method}(tos3("') - } - if typ.is_ptr() { - // dereference - g.write('*') - } - g.enum_expr(expr) - if !is_var { - // end of string - g.write('"') - } + mut str_fn_name := g.gen_str_for_type_with_styp(typ, styp) + if g.autofree && !typ.has_flag(.optional) { + // Create a temporary variable so that the value can be freed + tmp := g.new_tmp_var() + // tmps << tmp + g.write('string $tmp = ${str_fn_name}(') + g.expr(node.args[0].expr) + g.writeln('); ${print_method}($tmp); string_free(&$tmp); //MEM2 $styp') } else { - g.write('${print_method}(${str_fn_name}(') - if typ.is_ptr() && sym.kind == .struct_ { - // dereference - g.write('*') + expr := node.args[0].expr + is_var := match expr { + ast.SelectorExpr { true } + ast.Ident { true } + else { false } } - g.expr(expr) + if typ.is_ptr() && sym.kind != .struct_ { + // ptr_str() for pointers + styp = 'ptr' + str_fn_name = 'ptr_str' + } + if sym.kind == .enum_ { + if is_var { + g.write('${print_method}(${str_fn_name}(') + } else { + // when no var, print string directly + g.write('${print_method}(tos3("') + } + if typ.is_ptr() { + // dereference + g.write('*') + } + g.enum_expr(expr) + if !is_var { + // end of string + g.write('"') + } + } else { + g.write('${print_method}(${str_fn_name}(') + if typ.is_ptr() && sym.kind == .struct_ { + // dereference + g.write('*') + } + g.expr(expr) + } + g.write('))') } - g.write('))') + print_auto_str = true } - } else if g.pref.is_debug && node.name == 'panic' { - paline, pafile, pamod, pafn := g.panic_debug_info(node.pos) - g.write('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ') - // g.call_args(node.args, node.expected_arg_types) // , []) - g.call_args(node) - g.write(')') - } else { - // Simple function call - // if free_tmp_arg_vars { - // g.writeln(';') - // g.write(cur_line + ' /* <== af cur line*/') - // } - g.write('${g.get_ternary_name(name)}(') - if g.is_json_fn { - g.write(json_obj) - } else { - // g.call_args(node.args, node.expected_arg_types) // , tmp_arg_vars_to_free) + } + if !print_auto_str { + if g.pref.is_debug && node.name == 'panic' { + paline, pafile, pamod, pafn := g.panic_debug_info(node.pos) + g.write('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ') + // g.call_args(node.args, node.expected_arg_types) // , []) g.call_args(node) + g.write(')') + } else { + // Simple function call + // if free_tmp_arg_vars { + // g.writeln(';') + // g.write(cur_line + ' /* <== af cur line*/') + // } + g.write('${g.get_ternary_name(name)}(') + if g.is_json_fn { + g.write(json_obj) + } else { + // g.call_args(node.args, node.expected_arg_types) // , tmp_arg_vars_to_free) + g.call_args(node) + } + g.write(')') } - g.write(')') } g.is_c_call = false g.is_json_fn = false diff --git a/vlib/v/gen/str.v b/vlib/v/gen/str.v index 5fd954402a..34d81abcde 100644 --- a/vlib/v/gen/str.v +++ b/vlib/v/gen/str.v @@ -266,15 +266,8 @@ fn (mut g Gen) string_inter_literal_sb_optimized(call_expr ast.CallExpr) { g.expr(call_expr.left) g.write(', ') typ := node.expr_types[i] - sym := g.table.get_type_symbol(typ) - // if typ.is_number() { - if sym.kind == .alias && (sym.info as table.Alias).parent_type.is_number() { - // Handle number aliases TODO this must be more generic, handled by g.typ()? - g.write('int_str(') - } else { - g.write(g.typ(typ)) - g.write('_str(') - } + g.write(g.typ(typ)) + g.write('_str(') g.expr(node.exprs[i]) g.writeln('));') } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index acee12ac0b..bf1762f225 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1571,6 +1571,7 @@ fn (mut p Parser) import_syms(mut parent ast.Import) { info: table.Alias{ parent_type: typ language: table.Language.v + is_import: true } is_public: false }) diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index b401c7cb3a..d2d62194d0 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -732,6 +732,7 @@ pub struct Alias { pub: parent_type Type language Language + is_import bool } pub struct Aggregate { diff --git a/vlib/v/tests/str_gen_test.v b/vlib/v/tests/str_gen_test.v index 7254c2a8ee..4a7d960f00 100644 --- a/vlib/v/tests/str_gen_test.v +++ b/vlib/v/tests/str_gen_test.v @@ -182,7 +182,7 @@ fn test_struct_with_struct_pointer() { } fn test_struct_with_nil() { - w := Wrapper4{} + w := Wrapper4{0} assert '$w' == 'Wrapper4{\n foo: &nil\n}' assert w.str() == 'Wrapper4{\n foo: &nil\n}' } @@ -223,11 +223,49 @@ struct ForGeneric {} fn generic_fn_interpolation(p T) string { return '$p' } + fn generic_fn_str(p T) string { return p.str() } + fn test_generic_auto_str() { s := ForGeneric{} assert generic_fn_interpolation(s) == 'ForGeneric{}' assert generic_fn_str(s) == 'ForGeneric{}' } + +type Alias1 = int +fn test_alias_in_array() { + t := [Alias1(1)] + assert t.str() == '[1]' + assert '$t' == '[1]' +} + +type Alias2 = int +fn test_alias_in_fixed_array() { + t := [Alias1(1)]!! + assert t.str() == '[1]' + assert '$t' == '[1]' +} + +fn test_alias_int() { + a := Alias1(1) + assert a.str() == '1' + assert '$a' == '1' +} + +type Alias3 = string +fn test_alias_string() { + s := 'test' + a := Alias3(s) + assert a.str() == s + assert '$a' == s +} + +type TestAlias = TestStruct +fn test_alias_struct() { + ts := TestStruct{} + t := TestAlias(ts) + assert t.str() == 'TestAlias($ts)' + assert '$t' == 'TestAlias(TestStruct{\n x: 0\n})' +}