mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
cgen: fix auto .str() generation for []&T, and fn (t &T) str() string{}
This commit is contained in:
parent
8866773f97
commit
fec7f0f0b9
@ -833,6 +833,19 @@ pub fn (mut c Checker) call_fn(call_expr mut ast.CallExpr) table.Type {
|
||||
if fn_name == 'println' || fn_name == 'print' {
|
||||
c.expected_type = table.string_type
|
||||
call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
|
||||
/*
|
||||
// TODO: optimize `struct T{} fn (t &T) str() string {return 'abc'} mut a := []&T{} a << &T{} println(a[0])`
|
||||
// It currently generates:
|
||||
// `println(T_str_no_ptr(*(*(T**)array_get(a, 0))));`
|
||||
// ... which works, but could be just:
|
||||
// `println(T_str(*(T**)array_get(a, 0)));`
|
||||
prexpr := call_expr.args[0].expr
|
||||
prtyp := call_expr.args[0].typ
|
||||
prtyp_sym := c.table.get_type_symbol(prtyp)
|
||||
prtyp_is_ptr := prtyp.is_ptr()
|
||||
prhas_str, prexpects_ptr, prnr_args := prtyp_sym.str_method_info()
|
||||
eprintln('>>> println hack typ: ${prtyp} | sym.name: ${prtyp_sym.name} | is_ptr: $prtyp_is_ptr | has_str: $prhas_str | expects_ptr: $prexpects_ptr | nr_args: $prnr_args | expr: ${prexpr.str()} ')
|
||||
*/
|
||||
return f.return_type
|
||||
}
|
||||
// TODO: typ optimize.. this node can get processed more than once
|
||||
|
@ -2578,6 +2578,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||
}
|
||||
} else if specs[i] == `s` {
|
||||
sym := g.table.get_type_symbol(node.expr_types[i])
|
||||
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
|
||||
if node.expr_types[i].flag_is(.variadic) {
|
||||
str_fn_name := g.gen_str_for_type(node.expr_types[i])
|
||||
g.write('${str_fn_name}(')
|
||||
@ -2599,17 +2600,24 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||
g.enum_expr(expr)
|
||||
g.write('")')
|
||||
}
|
||||
} else if sym.has_method('str') || sym.kind in [.array, .array_fixed, .map, .struct_] {
|
||||
} else if sym_has_str_method || sym.kind in [.array, .array_fixed, .map, .struct_] {
|
||||
is_p := node.expr_types[i].is_ptr()
|
||||
val_type := if is_p { node.expr_types[i].deref() } else { node.expr_types[i] }
|
||||
str_fn_name := g.gen_str_for_type(val_type)
|
||||
if is_p {
|
||||
g.write('string_add(_SLIT("&"), ${str_fn_name}(*(')
|
||||
} else {
|
||||
g.write('${str_fn_name}(')
|
||||
if is_p && str_method_expects_ptr {
|
||||
g.write('string_add(_SLIT("&"), ${str_fn_name}( (')
|
||||
}
|
||||
if is_p && !str_method_expects_ptr {
|
||||
g.write('string_add(_SLIT("&"), ${str_fn_name}( *(')
|
||||
}
|
||||
if !is_p && !str_method_expects_ptr {
|
||||
g.write('${str_fn_name}( ')
|
||||
}
|
||||
if !is_p && str_method_expects_ptr {
|
||||
g.write('${str_fn_name}( &')
|
||||
}
|
||||
g.expr(expr)
|
||||
if sym.kind == .struct_ && !sym.has_method('str') {
|
||||
if sym.kind == .struct_ && !sym_has_str_method {
|
||||
if is_p {
|
||||
g.write('),0))')
|
||||
} else {
|
||||
@ -3179,7 +3187,7 @@ fn (mut g Gen) as_cast(node ast.AsCast) {
|
||||
expr_type_sym := g.table.get_type_symbol(node.expr_type)
|
||||
if expr_type_sym.kind == .sum_type {
|
||||
/*
|
||||
g.write('/* as */ *($styp*)')
|
||||
g.write('*($styp*)')
|
||||
g.expr(node.expr)
|
||||
g.write('.obj')
|
||||
*/
|
||||
@ -3220,9 +3228,33 @@ fn (mut g Gen) gen_str_for_type(typ table.Type) string {
|
||||
fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string {
|
||||
sym := g.table.get_type_symbol(typ)
|
||||
str_fn_name := styp_to_str_fn_name(styp)
|
||||
already_generated_key := '${styp}:${str_fn_name}'
|
||||
sym_has_str_method, str_method_expects_ptr, str_nr_args := sym.str_method_info()
|
||||
// generate for type
|
||||
if !sym.has_method('str') && already_generated_key !in g.str_types {
|
||||
if sym_has_str_method && str_method_expects_ptr && str_nr_args == 1 {
|
||||
// TODO: optimize out this.
|
||||
// It is needed, so that println() can be called with &T and T has `fn (t &T).str() string`
|
||||
/*
|
||||
eprintln('>> gsftws: typ: $typ | typ_is_ptr $typ_is_ptr | styp: $styp ' +
|
||||
'| $str_fn_name | sym.name: $sym.name has_str: $sym_has_str_method ' +
|
||||
'| expects_ptr: $str_method_expects_ptr')
|
||||
*/
|
||||
str_fn_name_no_ptr := '${str_fn_name}_no_ptr'
|
||||
already_generated_key_no_ptr := '${styp}:${str_fn_name_no_ptr}'
|
||||
if already_generated_key_no_ptr !in g.str_types {
|
||||
g.str_types << already_generated_key_no_ptr
|
||||
g.definitions.writeln('string ${str_fn_name_no_ptr}(${styp} it); // auto no_ptr version')
|
||||
g.auto_str_funcs.writeln('string ${str_fn_name_no_ptr}(${styp} it){ return ${str_fn_name}(&it); }')
|
||||
}
|
||||
/*
|
||||
typ_is_ptr := typ.is_ptr()
|
||||
ret_type := if typ_is_ptr { str_fn_name } else { str_fn_name_no_ptr }
|
||||
eprintln(' ret_type: $ret_type')
|
||||
return ret_type
|
||||
*/
|
||||
return str_fn_name_no_ptr
|
||||
}
|
||||
already_generated_key := '${styp}:${str_fn_name}'
|
||||
if !sym_has_str_method && already_generated_key !in g.str_types {
|
||||
g.str_types << already_generated_key
|
||||
match sym.info {
|
||||
table.Alias { g.gen_str_default(sym, styp, str_fn_name) }
|
||||
@ -3362,7 +3394,16 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
|
||||
fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) {
|
||||
sym := g.table.get_type_symbol(info.elem_type)
|
||||
field_styp := g.typ(info.elem_type)
|
||||
if !sym.has_method('str') {
|
||||
is_elem_ptr := info.elem_type.is_ptr()
|
||||
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
|
||||
mut elem_str_fn_name := ''
|
||||
if sym_has_str_method {
|
||||
elem_str_fn_name = if is_elem_ptr { field_styp.replace('*', '') +'_str' } else { field_styp + '_str' }
|
||||
} else {
|
||||
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.definitions.writeln('string ${str_fn_name}($styp a); // auto')
|
||||
@ -3371,12 +3412,25 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) {
|
||||
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos3("["));')
|
||||
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_method('str') {
|
||||
g.auto_str_funcs.writeln('\t\tstring x = ${field_styp}_str(it,0);')
|
||||
if sym.kind == .struct_ && !sym_has_str_method {
|
||||
if is_elem_ptr {
|
||||
g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(*it,0);')
|
||||
}else{
|
||||
g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}(it,0);')
|
||||
}
|
||||
} else if sym.kind in [.f32, .f64] {
|
||||
g.auto_str_funcs.writeln('\t\tstring x = _STR("%g", 1, it);')
|
||||
} else {
|
||||
g.auto_str_funcs.writeln('\t\tstring x = ${field_styp}_str(it);')
|
||||
// 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);')
|
||||
if g.pref.autofree && info.elem_type != table.bool_type {
|
||||
@ -3398,6 +3452,14 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) {
|
||||
fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp, 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()
|
||||
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
|
||||
mut elem_str_fn_name := ''
|
||||
if sym_has_str_method {
|
||||
elem_str_fn_name = if is_elem_ptr { field_styp.replace('*', '') +'_str' } else { field_styp + '_str' }
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
@ -3406,14 +3468,20 @@ fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp, str_fn_name
|
||||
g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder($info.size * 10);')
|
||||
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos3("["));')
|
||||
g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; i++) {')
|
||||
if sym.kind == .struct_ && !sym.has_method('str') {
|
||||
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${field_styp}_str(a[i],0));')
|
||||
if sym.kind == .struct_ && !sym_has_str_method {
|
||||
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(a[i],0));')
|
||||
} 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 {
|
||||
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${field_styp}_str(a[i]));')
|
||||
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}) {')
|
||||
g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, tos3(", "));')
|
||||
@ -3432,6 +3500,7 @@ fn (mut g Gen) gen_str_for_map(info table.Map, styp, str_fn_name string) {
|
||||
}
|
||||
val_sym := g.table.get_type_symbol(info.value_type)
|
||||
val_styp := g.typ(info.value_type)
|
||||
elem_str_fn_name := val_styp.replace('*', '') + '_str'
|
||||
if !val_sym.has_method('str') {
|
||||
g.gen_str_for_type_with_styp(info.value_type, val_styp)
|
||||
}
|
||||
@ -3451,11 +3520,11 @@ fn (mut g Gen) gen_str_for_map(info table.Map, styp, str_fn_name string) {
|
||||
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, ${val_styp}_str(it,0));')
|
||||
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${elem_str_fn_name}(it,0));')
|
||||
} 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, ${val_styp}_str(it));')
|
||||
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.size-1) {')
|
||||
g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, tos3(", "));')
|
||||
|
@ -677,6 +677,20 @@ pub fn (t &TypeSymbol) find_method(name string) ?Fn {
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) str_method_info() (bool, bool, int) {
|
||||
mut has_str_method := false
|
||||
mut expects_ptr := false
|
||||
mut nr_args := 0
|
||||
if sym_str_method := t.find_method('str') {
|
||||
has_str_method = true
|
||||
nr_args = sym_str_method.args.len
|
||||
if nr_args > 0 {
|
||||
expects_ptr = sym_str_method.args[0].typ.is_ptr()
|
||||
}
|
||||
}
|
||||
return has_str_method, expects_ptr, nr_args
|
||||
}
|
||||
|
||||
pub fn (s Struct) find_field(name string) ?Field {
|
||||
for field in s.fields {
|
||||
if field.name == name {
|
||||
|
91
vlib/v/tests/string_interpolation_of_array_of_structs_test.v
Normal file
91
vlib/v/tests/string_interpolation_of_array_of_structs_test.v
Normal file
@ -0,0 +1,91 @@
|
||||
module main
|
||||
|
||||
struct Anything {
|
||||
mut:
|
||||
name string = ""
|
||||
keepo int = 0
|
||||
}
|
||||
|
||||
fn (a Anything) str() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
fn test_array_of_ptrs_to_structs_can_be_printed() {
|
||||
mut testing := []&Anything{}
|
||||
testing << &Anything{name: "Hehe"}
|
||||
testing << &Anything{name: "other"}
|
||||
testing << &Anything{name: "test"}
|
||||
for test in testing {
|
||||
println(test)
|
||||
assert true
|
||||
}
|
||||
println('testing: $testing')
|
||||
println( testing )
|
||||
assert true
|
||||
}
|
||||
|
||||
// At the same time, this should also work:
|
||||
// (note the str method defined on (a &T), instead on (a T))
|
||||
|
||||
struct PstrAnything {
|
||||
mut:
|
||||
name string = ""
|
||||
keepo int = 0
|
||||
}
|
||||
fn (a &PstrAnything) str() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
fn test_array_of_ptrs_to_structs_can_be_printed_when_structs_have_str_with_ptr() {
|
||||
mut testing := []&PstrAnything{}
|
||||
testing << &PstrAnything{name: "abc"}
|
||||
testing << &PstrAnything{name: "def"}
|
||||
testing << &PstrAnything{name: "ghi"}
|
||||
for test in testing {
|
||||
println(test)
|
||||
assert true
|
||||
}
|
||||
println('testing: $testing')
|
||||
println( testing )
|
||||
assert true
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fn test_stack_array_of_structs_can_be_printed_when_structs_have_ordinary_str() {
|
||||
mut t := [3]Anything
|
||||
t[0] = Anything{name: "012"}
|
||||
t[1] = Anything{name: "345"}
|
||||
t[2] = Anything{name: "678"}
|
||||
for test in t {
|
||||
println(test)
|
||||
assert true
|
||||
}
|
||||
println('t: $t')
|
||||
println( t )
|
||||
println( 't[0] := ${t[0]}')
|
||||
assert true
|
||||
}
|
||||
|
||||
fn test_stack_array_of_structs_can_be_printed_when_structs_have_str_with_ptr() {
|
||||
// this generates a C error
|
||||
mut pt := [3]PstrAnything
|
||||
pt[0] = PstrAnything{name: "P012"}
|
||||
pt[1] = PstrAnything{name: "P345"}
|
||||
pt[2] = PstrAnything{name: "P678"}
|
||||
for test in pt {
|
||||
println(test)
|
||||
assert true
|
||||
}
|
||||
println('pt: $pt')
|
||||
println( pt )
|
||||
print( 'pt[0] := ')
|
||||
print( pt[0] )
|
||||
println('')
|
||||
assert true
|
||||
|
||||
$if debug_buggy_println ? {
|
||||
//TODO: fix string interpolation for structs with `fn (t &T) str() string` too:
|
||||
println( 'pt[0] := ${pt[0]}')
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user