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_volatile bool
|
||||||
is_deprecated bool
|
is_deprecated bool
|
||||||
pub mut:
|
pub mut:
|
||||||
|
is_recursive bool
|
||||||
default_expr Expr
|
default_expr Expr
|
||||||
default_expr_typ Type
|
default_expr_typ Type
|
||||||
name string
|
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 {
|
if ts.info is Struct {
|
||||||
for field in ts.info.fields {
|
for field in ts.info.fields {
|
||||||
sym := t.sym(field.typ)
|
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
|
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')
|
util.timing_start('Checker.struct setting default_expr_typ')
|
||||||
old_expected_type := c.expected_type
|
old_expected_type := c.expected_type
|
||||||
for mut field in node.fields {
|
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 {
|
if field.has_default_expr {
|
||||||
c.expected_type = field.typ
|
c.expected_type = field.typ
|
||||||
field.default_expr_typ = c.expr(field.default_expr)
|
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 {
|
} else {
|
||||||
if field_info.typ.is_ptr() && !expr_type.is_real_pointer()
|
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',
|
c.error('reference field must be initialized with reference',
|
||||||
field.pos)
|
field.pos)
|
||||||
}
|
}
|
||||||
@ -598,8 +612,9 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr
|
if field.typ.is_ptr() && !field.typ.has_flag(.shared_f)
|
||||||
&& !c.pref.translated && !c.file.is_translated {
|
&& !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',
|
c.warn('reference field `${type_sym.name}.${field.name}` must be initialized',
|
||||||
node.pos)
|
node.pos)
|
||||||
continue
|
continue
|
||||||
@ -721,7 +736,8 @@ fn (mut c Checker) check_ref_fields_initialized(struct_sym &ast.TypeSymbol, mut
|
|||||||
// an embedded struct field
|
// an embedded struct field
|
||||||
continue
|
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}`)',
|
c.warn('reference field `${linked_name}.${field.name}` must be initialized (part of struct `${struct_sym.name}`)',
|
||||||
node.pos)
|
node.pos)
|
||||||
continue
|
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()
|
is_ptr := typ.is_ptr()
|
||||||
deref, _ := deref_kind(str_method_expects_ptr, is_ptr, dump_type)
|
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))
|
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 := ''
|
mut str_dumparg_type := ''
|
||||||
if dump_sym.kind == .none_ {
|
if dump_sym.kind == .none_ {
|
||||||
str_dumparg_type = 'IError' + ptr_asterisk
|
str_dumparg_type = 'IError' + ptr_asterisk
|
||||||
} else {
|
} else {
|
||||||
if typ.has_flag(.option) {
|
if typ.has_flag(.option) {
|
||||||
str_dumparg_type += '_option_'
|
str_dumparg_type += '_option_'
|
||||||
|
ptr_asterisk = ptr_asterisk.replace('*', '_ptr')
|
||||||
}
|
}
|
||||||
str_dumparg_type += g.cc_type(dump_type, true) + ptr_asterisk
|
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_ {
|
} else if dump_sym.kind == .none_ {
|
||||||
surrounder.add('\tstring value = _SLIT("none");', '\tstring_free(&value);')
|
surrounder.add('\tstring value = _SLIT("none");', '\tstring_free(&value);')
|
||||||
} else if is_ptr {
|
} else if is_ptr {
|
||||||
surrounder.add('\tstring value = (dump_arg == NULL) ? _SLIT("nil") : ${to_string_fn_name}(${deref}dump_arg);',
|
if typ.has_flag(.option) {
|
||||||
'\tstring_free(&value);')
|
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 {
|
} else {
|
||||||
surrounder.add('\tstring value = ${to_string_fn_name}(${deref}dump_arg);',
|
surrounder.add('\tstring value = ${to_string_fn_name}(${deref}dump_arg);',
|
||||||
'\tstring_free(&value);')
|
'\tstring_free(&value);')
|
||||||
|
@ -324,8 +324,12 @@ fn (mut g Gen) zero_struct_field(field ast.StructField) bool {
|
|||||||
}
|
}
|
||||||
g.write('.${field_name} = ')
|
g.write('.${field_name} = ')
|
||||||
if field.typ.has_flag(.option) {
|
if field.typ.has_flag(.option) {
|
||||||
tmp_var := g.new_tmp_var()
|
if field.is_recursive {
|
||||||
g.expr_with_tmp_var(default_init, field.typ, field.typ, tmp_var)
|
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 {
|
} else {
|
||||||
g.struct_init(default_init)
|
g.struct_init(default_init)
|
||||||
}
|
}
|
||||||
@ -421,6 +425,7 @@ fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) {
|
|||||||
} else {
|
} else {
|
||||||
g.type_definitions.writeln('struct ${name} {')
|
g.type_definitions.writeln('struct ${name} {')
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.fields.len > 0 || s.embeds.len > 0 {
|
if s.fields.len > 0 || s.embeds.len > 0 {
|
||||||
for field in s.fields {
|
for field in s.fields {
|
||||||
// Some of these structs may want to contain
|
// 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…
x
Reference in New Issue
Block a user