mirror of
https://github.com/vlang/v.git
synced 2025-09-08 06:41:58 -04:00
cgen: force C struct types which does not implement str() to be passed as ptr (#21054)
This commit is contained in:
parent
0b573c5943
commit
24d157205b
@ -30,7 +30,11 @@ pub fn (a Character) == (b Character) bool {
|
||||
// to_rune creates a V rune, given a Character
|
||||
@[inline]
|
||||
pub fn (c Character) to_rune() rune {
|
||||
return unsafe { *(&rune(&c)) }
|
||||
$if windows {
|
||||
return unsafe { *(&rune(&c)) } & 0xFFFF
|
||||
} $else {
|
||||
return unsafe { *(&rune(&c)) }
|
||||
}
|
||||
}
|
||||
|
||||
// from_rune creates a Character, given a V rune
|
||||
|
@ -934,6 +934,15 @@ pub fn (t &TypeSymbol) is_array_fixed() bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) is_c_struct() bool {
|
||||
if t.info is Struct {
|
||||
return t.language == .c
|
||||
} else if t.info is Alias {
|
||||
return global_table.final_sym(t.info.parent_type).is_c_struct()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) is_array_fixed_ret() bool {
|
||||
if t.info is ArrayFixed {
|
||||
return t.info.is_fn_ret
|
||||
@ -1707,6 +1716,9 @@ pub fn (t &TypeSymbol) str_method_info() (bool, bool, int) {
|
||||
if nr_args > 0 {
|
||||
expects_ptr = sym_str_method.params[0].typ.is_ptr()
|
||||
}
|
||||
} else {
|
||||
// C Struct which does not implement str() are passed as pointer to handle incomplete type
|
||||
expects_ptr = t.is_c_struct()
|
||||
}
|
||||
return has_str_method, expects_ptr, nr_args
|
||||
}
|
||||
|
@ -248,19 +248,25 @@ fn (mut g Gen) gen_str_for_result(typ ast.Type, styp string, str_fn_name string)
|
||||
|
||||
fn (mut g Gen) gen_str_for_alias(info ast.Alias, styp string, str_fn_name string) {
|
||||
parent_str_fn_name := g.get_str_fn(info.parent_type)
|
||||
_, str_method_expects_ptr, _ := g.table.sym(info.parent_type).str_method_info()
|
||||
parent_sym := g.table.sym(info.parent_type)
|
||||
_, str_method_expects_ptr, _ := parent_sym.str_method_info()
|
||||
|
||||
$if trace_autostr ? {
|
||||
eprintln('> gen_str_for_alias: ${parent_str_fn_name} | ${styp} | ${str_fn_name}')
|
||||
}
|
||||
mut clean_type_v_type_name := util.strip_main_name(styp.replace('__', '.'))
|
||||
g.definitions.writeln('static string ${str_fn_name}(${styp} it); // auto')
|
||||
g.auto_str_funcs.writeln('static string ${str_fn_name}(${styp} it) { return indent_${str_fn_name}(it, 0); }')
|
||||
g.definitions.writeln('static string indent_${str_fn_name}(${styp} it, int indent_count); // auto')
|
||||
g.auto_str_funcs.writeln('static string indent_${str_fn_name}(${styp} it, int indent_count) {')
|
||||
|
||||
is_c_struct := parent_sym.is_c_struct() && str_method_expects_ptr
|
||||
arg_def := if is_c_struct { '${styp}* it' } else { '${styp} it' }
|
||||
|
||||
g.definitions.writeln('static string ${str_fn_name}(${arg_def}); // auto')
|
||||
g.auto_str_funcs.writeln('static string ${str_fn_name}(${arg_def}) { return indent_${str_fn_name}(it, 0); }')
|
||||
g.definitions.writeln('static string indent_${str_fn_name}(${arg_def}, int indent_count); // auto')
|
||||
g.auto_str_funcs.writeln('static string indent_${str_fn_name}(${arg_def}, int indent_count) {')
|
||||
g.auto_str_funcs.writeln('\tstring indents = string_repeat(_SLIT(" "), indent_count);')
|
||||
if str_method_expects_ptr {
|
||||
g.auto_str_funcs.writeln('\tstring tmp_ds = ${parent_str_fn_name}(&it);')
|
||||
it_arg := if is_c_struct { 'it' } else { '&it' }
|
||||
g.auto_str_funcs.writeln('\tstring tmp_ds = ${parent_str_fn_name}(${it_arg});')
|
||||
} else {
|
||||
deref, _ := deref_kind(str_method_expects_ptr, info.parent_type.is_ptr(), info.parent_type)
|
||||
g.auto_str_funcs.writeln('\tstring tmp_ds = ${parent_str_fn_name}(${deref}it);')
|
||||
@ -882,14 +888,17 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp strin
|
||||
eprintln('> gen_str_for_struct: ${info.parent_type.debug()} | ${styp} | ${str_fn_name}')
|
||||
}
|
||||
// _str() functions should have a single argument, the indenting ones take 2:
|
||||
g.definitions.writeln('static string ${str_fn_name}(${styp} it); // auto')
|
||||
g.auto_str_funcs.writeln('static string ${str_fn_name}(${styp} it) { return indent_${str_fn_name}(it, 0);}')
|
||||
g.definitions.writeln('static string indent_${str_fn_name}(${styp} it, int indent_count); // auto')
|
||||
is_c_struct := lang == .c
|
||||
arg_def := if is_c_struct { '${styp}* it' } else { '${styp} it' }
|
||||
g.definitions.writeln('static string ${str_fn_name}(${arg_def}); // auto')
|
||||
g.auto_str_funcs.writeln('static string ${str_fn_name}(${arg_def}) { return indent_${str_fn_name}(it, 0);}')
|
||||
g.definitions.writeln('static string indent_${str_fn_name}(${arg_def}, int indent_count); // auto')
|
||||
mut fn_builder := strings.new_builder(512)
|
||||
defer {
|
||||
g.auto_fn_definitions << fn_builder.str()
|
||||
}
|
||||
fn_builder.writeln('static string indent_${str_fn_name}(${styp} it, int indent_count) {')
|
||||
fn_builder.writeln('static string indent_${str_fn_name}(${arg_def}, int indent_count) {')
|
||||
|
||||
clean_struct_v_type_name := if info.is_anon { 'struct ' } else { util.strip_main_name(typ_str) }
|
||||
// generate ident / indent length = 4 spaces
|
||||
if info.fields.len == 0 {
|
||||
@ -997,18 +1006,20 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp strin
|
||||
field_styp_fn_name, field.name, sym_has_str_method, str_method_expects_ptr)
|
||||
ftyp_nr_muls := field.typ.nr_muls()
|
||||
field_name := if lang == .c { field.name } else { c_name(field.name) }
|
||||
op := if is_c_struct { '->' } else { '.' }
|
||||
it_field_name := 'it${op}${field_name}'
|
||||
if ftyp_nr_muls > 1 || field.typ in ast.cptr_types {
|
||||
if is_opt_field {
|
||||
} else {
|
||||
func = '(voidptr) it.${field.name}'
|
||||
func = '(voidptr) ${it_field_name}'
|
||||
caller_should_free = false
|
||||
}
|
||||
} else if ftyp_noshared.is_ptr() {
|
||||
// reference types can be "nil"
|
||||
if ftyp_noshared.has_flag(.option) {
|
||||
funcprefix += 'isnil(&it.${field_name}) || isnil(&it.${field_name}.data)'
|
||||
funcprefix += 'isnil(&${it_field_name}) || isnil(&${it_field_name}.data)'
|
||||
} else {
|
||||
funcprefix += 'isnil(it.${field_name})'
|
||||
funcprefix += 'isnil(${it_field_name})'
|
||||
}
|
||||
funcprefix += ' ? _SLIT("nil") : '
|
||||
// struct, floats and ints have a special case through the _str function
|
||||
@ -1031,9 +1042,9 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp strin
|
||||
if is_field_array {
|
||||
if is_opt_field {
|
||||
arr_styp := g.base_type(field.typ)
|
||||
fn_body.write_string('it.${field_name}.state != 2 && (*(${arr_styp}*)it.${field_name}.data).len > 0 ? ${funcprefix}_SLIT("[<circular>]") : ${funcprefix}_SLIT("[]")')
|
||||
fn_body.write_string('${it_field_name}.state != 2 && (*(${arr_styp}*)${it_field_name}.data).len > 0 ? ${funcprefix}_SLIT("[<circular>]") : ${funcprefix}_SLIT("[]")')
|
||||
} else {
|
||||
fn_body.write_string('it.${field_name}.len > 0 ? ${funcprefix}_SLIT("[<circular>]") : ${funcprefix}_SLIT("[]")')
|
||||
fn_body.write_string('${it_field_name}.len > 0 ? ${funcprefix}_SLIT("[<circular>]") : ${funcprefix}_SLIT("[]")')
|
||||
}
|
||||
} else {
|
||||
fn_body.write_string('${funcprefix}_SLIT("<circular>")')
|
||||
@ -1065,6 +1076,25 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp strin
|
||||
fn_body.writeln('\t}));')
|
||||
}
|
||||
|
||||
// c_struct_ptr handles the C struct argument for .str() method
|
||||
@[inline]
|
||||
pub fn c_struct_ptr(sym &ast.TypeSymbol, typ ast.Type, expects_ptr bool) string {
|
||||
if sym.is_c_struct() {
|
||||
if typ.has_flag(.option) {
|
||||
return ''
|
||||
}
|
||||
if typ.nr_muls() >= 1 {
|
||||
if expects_ptr {
|
||||
return '*'.repeat(typ.nr_muls() - 1)
|
||||
} else {
|
||||
return '*'.repeat(typ.nr_muls())
|
||||
}
|
||||
}
|
||||
return if expects_ptr { '&' } else { '' }
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn struct_auto_str_func(sym &ast.TypeSymbol, lang ast.Language, _field_type ast.Type, fn_name string, field_name string, has_custom_str bool, expects_ptr bool) (string, bool) {
|
||||
$if trace_autostr ? {
|
||||
eprintln('> struct_auto_str_func: ${sym.name} | field_type.debug() | ${fn_name} | ${field_name} | ${has_custom_str} | ${expects_ptr}')
|
||||
@ -1073,13 +1103,15 @@ fn struct_auto_str_func(sym &ast.TypeSymbol, lang ast.Language, _field_type ast.
|
||||
sufix := if field_type.has_flag(.shared_f) { '->val' } else { '' }
|
||||
deref, _ := deref_kind(expects_ptr, field_type.is_ptr(), field_type)
|
||||
final_field_name := if lang == .c { field_name } else { c_name(field_name) }
|
||||
op := if lang == .c { '->' } else { '.' }
|
||||
prefix := if sym.is_c_struct() { c_struct_ptr(sym, _field_type, expects_ptr) } else { deref }
|
||||
if sym.kind == .enum_ {
|
||||
return '${fn_name}(${deref}(it.${final_field_name}))', true
|
||||
return '${fn_name}(${deref}(it${op}${final_field_name}))', true
|
||||
} else if _field_type.has_flag(.option) || should_use_indent_func(sym.kind) {
|
||||
obj := '${deref}it.${final_field_name}${sufix}'
|
||||
obj := '${prefix}it${op}${final_field_name}${sufix}'
|
||||
if has_custom_str {
|
||||
if sym.kind == .interface_ && (sym.info as ast.Interface).defines_method('str') {
|
||||
iface_obj := 'it.${final_field_name}${sufix}'
|
||||
iface_obj := '${prefix}it${op}${final_field_name}${sufix}'
|
||||
dot := if field_type.is_ptr() { '->' } else { '.' }
|
||||
return '${fn_name.trim_string_right('_str')}_name_table[${iface_obj}${dot}_typ]._method_str(${iface_obj}${dot}_object)', true
|
||||
}
|
||||
@ -1087,24 +1119,24 @@ fn struct_auto_str_func(sym &ast.TypeSymbol, lang ast.Language, _field_type ast.
|
||||
}
|
||||
return 'indent_${fn_name}(${obj}, indent_count + 1)', true
|
||||
} else if sym.kind in [.array, .array_fixed, .map, .sum_type] {
|
||||
obj := '${deref}it.${final_field_name}${sufix}'
|
||||
obj := '${prefix}it${op}${final_field_name}${sufix}'
|
||||
if has_custom_str {
|
||||
return '${fn_name}(${obj})', true
|
||||
}
|
||||
return 'indent_${fn_name}(${obj}, indent_count + 1)', true
|
||||
} else if sym.kind == .function {
|
||||
obj := '${deref}it.${final_field_name}${sufix}'
|
||||
obj := '${deref}it${op}${final_field_name}${sufix}'
|
||||
return '${fn_name}(${obj})', true
|
||||
} else if sym.kind == .chan {
|
||||
return '${fn_name}(${deref}it.${final_field_name}${sufix})', true
|
||||
return '${fn_name}(${deref}it${op}${final_field_name}${sufix})', true
|
||||
} else if sym.kind == .thread {
|
||||
return '${fn_name}(${deref}it.${final_field_name}${sufix})', false
|
||||
return '${fn_name}(${deref}it${op}${final_field_name}${sufix})', false
|
||||
} else {
|
||||
mut method_str := ''
|
||||
if !field_type.is_ptr() && field_type.has_option_or_result() {
|
||||
method_str = '(*(${sym.name}*)it.${final_field_name}.data)'
|
||||
method_str = '(*(${sym.name}*)it${op}${final_field_name}.data)'
|
||||
} else {
|
||||
method_str = 'it.${final_field_name}'
|
||||
method_str = 'it${op}${final_field_name}'
|
||||
}
|
||||
if sym.kind == .bool {
|
||||
return '${method_str} ? _SLIT("true") : _SLIT("false")', false
|
||||
|
@ -2458,7 +2458,6 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
|
||||
lock g.referenced_fns {
|
||||
g.referenced_fns[fname] = true
|
||||
}
|
||||
fname = '/*${exp_sym}*/${fname}'
|
||||
if exp_sym.info.is_generic {
|
||||
fname = g.generic_fn_name(exp_sym.info.concrete_types, fname)
|
||||
}
|
||||
|
@ -181,11 +181,21 @@ fn (mut g Gen) dump_expr_definitions() {
|
||||
surrounder.add('\tstring value = isnil(&dump_arg.data) ? _SLIT("nil") : ${to_string_fn_name}(${deref}dump_arg);',
|
||||
'\tstring_free(&value);')
|
||||
} else {
|
||||
surrounder.add('\tstring value = (dump_arg == NULL) ? _SLIT("nil") : ${to_string_fn_name}(${deref}dump_arg);',
|
||||
prefix := if dump_sym.is_c_struct() {
|
||||
c_struct_ptr(dump_sym, dump_type, str_method_expects_ptr)
|
||||
} else {
|
||||
deref
|
||||
}
|
||||
surrounder.add('\tstring value = (dump_arg == NULL) ? _SLIT("nil") : ${to_string_fn_name}(${prefix}dump_arg);',
|
||||
'\tstring_free(&value);')
|
||||
}
|
||||
} else {
|
||||
surrounder.add('\tstring value = ${to_string_fn_name}(${deref}dump_arg);',
|
||||
prefix := if dump_sym.is_c_struct() {
|
||||
c_struct_ptr(dump_sym, dump_type, str_method_expects_ptr)
|
||||
} else {
|
||||
deref
|
||||
}
|
||||
surrounder.add('\tstring value = ${to_string_fn_name}(${prefix}dump_arg);',
|
||||
'\tstring_free(&value);')
|
||||
}
|
||||
surrounder.add('
|
||||
|
@ -151,7 +151,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
||||
}
|
||||
g.write('${str_fn_name}(')
|
||||
if str_method_expects_ptr && !is_ptr {
|
||||
if is_dump_expr {
|
||||
if is_dump_expr || (g.pref.ccompiler_type != .tinyc && expr is ast.CallExpr) {
|
||||
g.write('ADDR(${g.typ(typ)}, ')
|
||||
defer {
|
||||
g.write(')')
|
||||
@ -162,7 +162,13 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
||||
} else if is_ptr && typ.has_flag(.option) {
|
||||
g.write('*(${g.typ(typ)}*)&')
|
||||
} else if !str_method_expects_ptr && !is_shared && (is_ptr || is_var_mut) {
|
||||
g.write('*'.repeat(typ.nr_muls()))
|
||||
if sym.is_c_struct() {
|
||||
g.write(c_struct_ptr(sym, typ, str_method_expects_ptr))
|
||||
} else {
|
||||
g.write('*'.repeat(typ.nr_muls()))
|
||||
}
|
||||
} else if sym.is_c_struct() {
|
||||
g.write(c_struct_ptr(sym, typ, str_method_expects_ptr))
|
||||
}
|
||||
if expr is ast.ArrayInit {
|
||||
if expr.is_fixed {
|
||||
@ -199,6 +205,10 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
||||
g.write('&')
|
||||
} else if (!str_method_expects_ptr && is_ptr && !is_shared) || is_var_mut {
|
||||
g.write('*')
|
||||
} else {
|
||||
if sym.is_c_struct() {
|
||||
g.write(c_struct_ptr(sym, typ, str_method_expects_ptr))
|
||||
}
|
||||
}
|
||||
g.expr_with_cast(expr, typ, typ)
|
||||
} else if typ.has_flag(.option) {
|
||||
|
@ -61,7 +61,6 @@ fn (mut g Gen) str_format(node ast.StringInterLiteral, i int, fmts []u8) (u64, s
|
||||
mut remove_tail_zeros := false
|
||||
fspec := fmts[i]
|
||||
mut fmt_type := StrIntpType.si_no_str
|
||||
g.write('/*${fspec} ${sym}*/')
|
||||
// upper cases
|
||||
if (fspec - `A`) <= (`Z` - `A`) {
|
||||
upper_case = true
|
||||
|
@ -2,3 +2,11 @@
|
||||
struct Abc {
|
||||
int field;
|
||||
};
|
||||
|
||||
typedef struct Test2 {
|
||||
int a;
|
||||
} Test2;
|
||||
|
||||
typedef struct Test1 {
|
||||
Test2 a;
|
||||
} Test1;
|
50
vlib/v/tests/c_structs/cstruct_str_test.v
Normal file
50
vlib/v/tests/c_structs/cstruct_str_test.v
Normal file
@ -0,0 +1,50 @@
|
||||
#include "@VMODROOT/cstruct.h"
|
||||
|
||||
@[typedef]
|
||||
struct C.Test2 {}
|
||||
|
||||
@[typedef]
|
||||
struct C.Test1 {
|
||||
a C.Test2
|
||||
}
|
||||
|
||||
fn (s C.Test2) str() string {
|
||||
return 'test2'
|
||||
}
|
||||
|
||||
fn (s C.Test1) get() C.Test1 {
|
||||
return s
|
||||
}
|
||||
|
||||
fn (s C.Test1) s() string {
|
||||
return '.${s.get()}.'
|
||||
}
|
||||
|
||||
type TestAlias = C.Test2
|
||||
|
||||
fn (s TestAlias) str() string {
|
||||
return 'test_alias'
|
||||
}
|
||||
|
||||
fn (s TestAlias) get() TestAlias {
|
||||
return s
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
x := unsafe { &C.Test1(malloc(1024)) }
|
||||
println(x)
|
||||
assert dump('${x}') == '&C.Test1{
|
||||
a: test2
|
||||
}'
|
||||
println('.${x.get()}.${x.s()}')
|
||||
|
||||
y := unsafe { &TestAlias(malloc(1024)) }
|
||||
println(y)
|
||||
assert dump('${y}') == '&test_alias'
|
||||
println('.${y.get()}.')
|
||||
|
||||
w := TestAlias(*y)
|
||||
assert dump(w.str()) == 'test_alias'
|
||||
|
||||
assert true
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user