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:
parent
6cc420880f
commit
377c2e25ff
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
5
vlib/v/checker/tests/invalid_recursive_struct_err.out
Normal file
5
vlib/v/checker/tests/invalid_recursive_struct_err.out
Normal 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
|
4
vlib/v/checker/tests/invalid_recursive_struct_err.vv
Normal file
4
vlib/v/checker/tests/invalid_recursive_struct_err.vv
Normal file
@ -0,0 +1,4 @@
|
||||
struct Node {
|
||||
mut:
|
||||
next ?Node
|
||||
}
|
@ -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);')
|
||||
|
@ -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
|
||||
|
15
vlib/v/tests/option_nested_struct_test.v
Normal file
15
vlib/v/tests/option_nested_struct_test.v
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user