diff --git a/vlib/v/gen/auto_str_methods.v b/vlib/v/gen/auto_str_methods.v index c46ce4bc34..f23d227edf 100644 --- a/vlib/v/gen/auto_str_methods.v +++ b/vlib/v/gen/auto_str_methods.v @@ -160,25 +160,29 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name stri g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(a.len * 10);') g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, _SLIT("["));') g.auto_str_funcs.writeln('\tfor (int i = 0; i < a.len; ++i) {') - g.auto_str_funcs.writeln('\t\t$field_styp it = (*($field_styp*)array_get(a, i));') - if sym.kind == .struct_ && !sym_has_str_method { - if is_elem_ptr { - g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(*it, indent_count);') - } else { - g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(it, indent_count);') - } - } else if sym.kind in [.f32, .f64] { - g.auto_str_funcs.writeln('\t\tstring x = _STR("%g", 1, it);') + if sym.kind == .function { + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();') } else { - // There is a custom .str() method, so use it. - // NB: we need to take account of whether the user has defined - // `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly - if (str_method_expects_ptr && is_elem_ptr) || (!str_method_expects_ptr && !is_elem_ptr) { - g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(it);') - } else if str_method_expects_ptr && !is_elem_ptr { - g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(&it);') - } else if !str_method_expects_ptr && is_elem_ptr { - g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(*it);') + g.auto_str_funcs.writeln('\t\t$field_styp it = (*($field_styp*)array_get(a, i));') + if sym.kind == .struct_ && !sym_has_str_method { + if is_elem_ptr { + g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(*it, indent_count);') + } else { + g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(it, indent_count);') + } + } else if sym.kind in [.f32, .f64] { + g.auto_str_funcs.writeln('\t\tstring x = _STR("%g", 1, it);') + } else { + // There is a custom .str() method, so use it. + // NB: we need to take account of whether the user has defined + // `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly + if (str_method_expects_ptr && is_elem_ptr) || (!str_method_expects_ptr && !is_elem_ptr) { + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(it);') + } else if str_method_expects_ptr && !is_elem_ptr { + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(&it);') + } else if !str_method_expects_ptr && is_elem_ptr { + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(*it);') + } } } g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, x);') @@ -224,20 +228,24 @@ fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp string, str_f g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp a, int indent_count) {') g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder($info.size * 10);') g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, _SLIT("["));') - g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; ++i) {') - if sym.kind == .struct_ && !sym_has_str_method { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(a[i], indent_count));') - } else if sym.kind in [.f32, .f64] { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("%g", 1, a[i]));') - } else if sym.kind == .string { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a[i]));') + if sym.kind == .function { + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();') } else { - if (str_method_expects_ptr && is_elem_ptr) || (!str_method_expects_ptr && !is_elem_ptr) { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(a[i]));') - } else if str_method_expects_ptr && !is_elem_ptr { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(&a[i]));') - } else if !str_method_expects_ptr && is_elem_ptr { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(*a[i]));') + g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; ++i) {') + if sym.kind == .struct_ && !sym_has_str_method { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(a[i], indent_count));') + } else if sym.kind in [.f32, .f64] { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("%g", 1, a[i]));') + } else if sym.kind == .string { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a[i]));') + } else { + if (str_method_expects_ptr && is_elem_ptr) || (!str_method_expects_ptr && !is_elem_ptr) { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(a[i]));') + } else if str_method_expects_ptr && !is_elem_ptr { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(&a[i]));') + } else if !str_method_expects_ptr && is_elem_ptr { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(*a[i]));') + } } } g.auto_str_funcs.writeln('\t\tif (i < ${info.size - 1}) {') @@ -271,15 +279,19 @@ fn (mut g Gen) gen_str_for_map(info table.Map, styp string, str_fn_name string) g.auto_str_funcs.writeln('\t\tstring key = *(string*)DenseArray_key(&m.key_values, i);') g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, key));') g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _SLIT(": "));') - g.auto_str_funcs.writeln('\t\t$val_styp it = *($val_styp*)DenseArray_value(&m.key_values, i);') - if val_sym.kind == .string { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, it));') - } else if val_sym.kind == .struct_ && !val_sym.has_method('str') { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, indent_${elem_str_fn_name}(it, indent_count));') - } else if val_sym.kind in [.f32, .f64] { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("%g", 1, it));') + if val_sym.kind == .function { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}());') } else { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(it));') + g.auto_str_funcs.writeln('\t\t$val_styp it = *($val_styp*)DenseArray_value(&m.key_values, i);') + if val_sym.kind == .string { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, it));') + } else if val_sym.kind == .struct_ && !val_sym.has_method('str') { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, indent_${elem_str_fn_name}(it, indent_count));') + } else if val_sym.kind in [.f32, .f64] { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("%g", 1, it));') + } else { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(it));') + } } g.auto_str_funcs.writeln('\t\tif (i != m.key_values.len-1) {') g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, _SLIT(", "));') @@ -335,6 +347,8 @@ fn (mut g Gen) gen_str_for_multi_return(info table.MultiReturn, styp string, str g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, _STR("%g", 1, a.arg$i));') } else if sym.kind == .string { g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a.arg$i));') + } else if sym.kind == .function { + g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, ${str_fn_name}());') } else { if (str_method_expects_ptr && is_arg_ptr) || (!str_method_expects_ptr && !is_arg_ptr) { g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, ${arg_str_fn_name}(a.arg$i));') @@ -447,6 +461,8 @@ fn struct_auto_str_func(sym table.TypeSymbol, field_type table.Type, fn_name str return '${fn_name}(it.${c_name(field_name)})' } return 'indent_${fn_name}(it.${c_name(field_name)}, indent_count + 1)' + } else if sym.kind == .function { + return '${fn_name}()' } else { mut method_str := 'it.${c_name(field_name)}' if sym.kind == .bool { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index acd5add5eb..512ce84942 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -5659,7 +5659,8 @@ fn (g &Gen) type_to_fmt(typ table.Type) string { sym := g.table.get_type_symbol(typ) if typ.is_ptr() && (typ.is_int() || typ.is_float()) { return '%.*s\\000' - } else if sym.kind in [.struct_, .array, .array_fixed, .map, .bool, .enum_, .sum_type] { + } else if sym.kind in + [.struct_, .array, .array_fixed, .map, .bool, .enum_, .sum_type, .function] { return '%.*s\\000' } else if sym.kind == .string { return "\'%.*s\\000\'" diff --git a/vlib/v/gen/str.v b/vlib/v/gen/str.v index 513d078022..80b882aeff 100644 --- a/vlib/v/gen/str.v +++ b/vlib/v/gen/str.v @@ -177,7 +177,10 @@ fn (mut g Gen) string_inter_literal_sb_optimized(call_expr ast.CallExpr) { typ := node.expr_types[i] g.write(g.typ(typ)) g.write('_str(') - g.expr(node.exprs[i]) + sym := g.table.get_type_symbol(typ) + if sym.kind != .function { + g.expr(node.exprs[i]) + } g.writeln('));') } g.writeln('') @@ -366,7 +369,9 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) { } else { str_fn_name := g.gen_str_for_type(typ) g.write('${str_fn_name}(') - g.expr(expr) + if sym.kind != .function { + g.expr(expr) + } g.write(')') } } diff --git a/vlib/v/tests/string_interpolation_function_test.v b/vlib/v/tests/string_interpolation_function_test.v index b563fdd60e..c21fadd3bf 100644 --- a/vlib/v/tests/string_interpolation_function_test.v +++ b/vlib/v/tests/string_interpolation_function_test.v @@ -10,3 +10,30 @@ fn test_function_interpolation() { println(show) assert '$show' == 'fn (string) string' } + +struct Info { + aa fn()string + bb int +} + +fn test_function_interpolation_in_struct() { + a := Info{ + aa: fn()string {return 'aaa'} + bb: 22 + } + println(a) + assert '$a'.contains(': fn () string') +} + +fn test_function_interpolation_in_array() { + f := [fn()string{return 'aaa'}, fn()string{return 'bbb'}] + println(f) + assert '$f' == '[fn () string, fn () string]' +} + +fn test_function_interpolation_in_map() { + m := {'aaa': fn()string{return 'aaa'}, 'bbb': fn()string{return 'bbb'}} + println(m) + assert '$m'.contains(': fn () string') +} +