1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

all: allow recursive struct with option ptr (?&Node) (#17682)

This commit is contained in:
Felipe Pena 2023-04-18 08:07:21 -03:00 committed by GitHub
parent 6cc420880f
commit 377c2e25ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 63 additions and 10 deletions

View File

@ -314,6 +314,7 @@ pub:
is_volatile bool
is_deprecated bool
pub mut:
is_recursive bool
default_expr Expr
default_expr_typ Type
name string

View File

@ -1313,7 +1313,8 @@ pub fn (t &Table) has_deep_child_no_ref(ts &TypeSymbol, name string) bool {
if ts.info is Struct {
for field in ts.info.fields {
sym := t.sym(field.typ)
if !field.typ.is_ptr() && (sym.name == name || t.has_deep_child_no_ref(sym, name)) {
if !field.typ.is_ptr() && ((sym.name == name && !field.typ.has_flag(.option))
|| t.has_deep_child_no_ref(sym, name)) {
return true
}
}

View File

@ -56,6 +56,20 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
util.timing_start('Checker.struct setting default_expr_typ')
old_expected_type := c.expected_type
for mut field in node.fields {
// when the field has the same type that the struct itself (recursive)
if field.typ.clear_flag(.option).set_nr_muls(0) == struct_typ_idx {
for mut symfield in struct_sym.info.fields {
if symfield.name == field.name {
// only ?&Struct is allowed to be recursive
if field.typ.is_ptr() {
symfield.is_recursive = true
} else {
c.error('recursive struct is only possible with optional pointer (e.g. ?&${c.table.type_to_str(field.typ.clear_flag(.option))})',
node.pos)
}
}
}
}
if field.has_default_expr {
c.expected_type = field.typ
field.default_expr_typ = c.expr(field.default_expr)
@ -522,7 +536,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
}
} else {
if field_info.typ.is_ptr() && !expr_type.is_real_pointer()
&& field.expr.str() != '0' {
&& field.expr.str() != '0' && !field_info.typ.has_flag(.option) {
c.error('reference field must be initialized with reference',
field.pos)
}
@ -598,8 +612,9 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
}
continue
}
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr
&& !c.pref.translated && !c.file.is_translated {
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f)
&& !field.typ.has_flag(.option) && !node.has_update_expr && !c.pref.translated
&& !c.file.is_translated {
c.warn('reference field `${type_sym.name}.${field.name}` must be initialized',
node.pos)
continue
@ -721,7 +736,8 @@ fn (mut c Checker) check_ref_fields_initialized(struct_sym &ast.TypeSymbol, mut
// an embedded struct field
continue
}
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !field.has_default_expr {
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !field.typ.has_flag(.option)
&& !field.has_default_expr {
c.warn('reference field `${linked_name}.${field.name}` must be initialized (part of struct `${struct_sym.name}`)',
node.pos)
continue

View File

@ -0,0 +1,5 @@
vlib/v/checker/tests/invalid_recursive_struct_err.vv:1:1: error: recursive struct is only possible with optional pointer (e.g. ?&Node)
1 | struct Node {
| ~~~~~~~~~~~
2 | mut:
3 | next ?Node

View File

@ -0,0 +1,4 @@
struct Node {
mut:
next ?Node
}

View File

@ -84,13 +84,14 @@ fn (mut g Gen) dump_expr_definitions() {
is_ptr := typ.is_ptr()
deref, _ := deref_kind(str_method_expects_ptr, is_ptr, dump_type)
to_string_fn_name := g.get_str_fn(typ.clear_flags(.shared_f, .result))
ptr_asterisk := if is_ptr { '*'.repeat(typ.nr_muls()) } else { '' }
mut ptr_asterisk := if is_ptr { '*'.repeat(typ.nr_muls()) } else { '' }
mut str_dumparg_type := ''
if dump_sym.kind == .none_ {
str_dumparg_type = 'IError' + ptr_asterisk
} else {
if typ.has_flag(.option) {
str_dumparg_type += '_option_'
ptr_asterisk = ptr_asterisk.replace('*', '_ptr')
}
str_dumparg_type += g.cc_type(dump_type, true) + ptr_asterisk
}
@ -118,8 +119,13 @@ fn (mut g Gen) dump_expr_definitions() {
} else if dump_sym.kind == .none_ {
surrounder.add('\tstring value = _SLIT("none");', '\tstring_free(&value);')
} else if is_ptr {
surrounder.add('\tstring value = (dump_arg == NULL) ? _SLIT("nil") : ${to_string_fn_name}(${deref}dump_arg);',
'\tstring_free(&value);')
if typ.has_flag(.option) {
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);',
'\tstring_free(&value);')
}
} else {
surrounder.add('\tstring value = ${to_string_fn_name}(${deref}dump_arg);',
'\tstring_free(&value);')

View File

@ -324,8 +324,12 @@ fn (mut g Gen) zero_struct_field(field ast.StructField) bool {
}
g.write('.${field_name} = ')
if field.typ.has_flag(.option) {
tmp_var := g.new_tmp_var()
g.expr_with_tmp_var(default_init, field.typ, field.typ, tmp_var)
if field.is_recursive {
g.expr_with_opt(ast.None{}, ast.none_type, field.typ)
} else {
tmp_var := g.new_tmp_var()
g.expr_with_tmp_var(default_init, field.typ, field.typ, tmp_var)
}
} else {
g.struct_init(default_init)
}
@ -421,6 +425,7 @@ fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) {
} else {
g.type_definitions.writeln('struct ${name} {')
}
if s.fields.len > 0 || s.embeds.len > 0 {
for field in s.fields {
// Some of these structs may want to contain

View File

@ -0,0 +1,15 @@
struct Node {
mut:
next ?&Node
}
fn test_struct_nested_option() {
mut b := Node{}
mut a := Node{
next: &b
}
dump(a)
dump(a.next)
dump(a.next?.next)
assert a.next?.next == none
}