From 665279938e853db88118c2196da570d30bceabc9 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 4 Jul 2021 23:37:31 +0800 Subject: [PATCH] checker: fix nested generic struct init (fix #10652) (#10659) --- vlib/v/ast/table.v | 33 ----------- vlib/v/builder/builder.v | 2 +- vlib/v/checker/checker.v | 57 +++++++++++++++++-- ...ics_with_nested_generic_struct_init_test.v | 24 ++++++++ 4 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 vlib/v/tests/generics_with_nested_generic_struct_init_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 011a2b31b3..20ec6b1515 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1298,36 +1298,3 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name } return none } - -// generic struct instantiations to concrete types -pub fn (mut t Table) generic_struct_insts_to_concrete() { - for mut typ in t.type_symbols { - if typ.kind == .generic_struct_inst { - info := typ.info as GenericStructInst - parent := t.type_symbols[info.parent_idx] - if parent.kind == .placeholder { - typ.kind = .placeholder - continue - } - mut parent_info := parent.info as Struct - mut fields := parent_info.fields.clone() - if parent_info.generic_types.len == info.concrete_types.len { - generic_names := parent_info.generic_types.map(t.get_type_symbol(it).name) - for i in 0 .. fields.len { - if t_typ := t.resolve_generic_to_concrete(fields[i].typ, generic_names, - info.concrete_types) - { - fields[i].typ = t_typ - } - } - parent_info.is_generic = false - parent_info.concrete_types = info.concrete_types.clone() - parent_info.fields = fields - parent_info.parent_type = new_type(info.parent_idx).set_flag(.generic) - typ.is_public = true - typ.kind = .struct_ - typ.info = parent_info - } - } - } -} diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index 53a4871d6a..096db1a0fd 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -80,7 +80,7 @@ pub fn (mut b Builder) front_stages(v_files []string) ? { pub fn (mut b Builder) middle_stages() ? { util.timing_start('CHECK') - b.table.generic_struct_insts_to_concrete() + b.checker.generic_struct_insts_to_concrete() b.checker.check_files(b.parsed_files) util.timing_measure('CHECK') b.print_warnings_and_errors() diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c6f3c35045..d0eaa3e0f8 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -664,10 +664,18 @@ fn (mut c Checker) unwrap_generic_struct(struct_type ast.Type, generic_names []s // fields type translate to concrete type mut fields := ts.info.fields.clone() for i in 0 .. fields.len { - if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, generic_names, - concrete_types) - { - fields[i].typ = t_typ + if fields[i].typ.has_flag(.generic) { + sym := c.table.get_type_symbol(fields[i].typ) + if sym.kind == .struct_ && fields[i].typ.idx() != struct_type.idx() { + fields[i].typ = c.unwrap_generic_struct(fields[i].typ, generic_names, + concrete_types) + } else { + if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, + generic_names, concrete_types) + { + fields[i].typ = t_typ + } + } } } // update concrete types @@ -698,6 +706,47 @@ fn (mut c Checker) unwrap_generic_struct(struct_type ast.Type, generic_names []s return struct_type } +// generic struct instantiations to concrete types +pub fn (mut c Checker) generic_struct_insts_to_concrete() { + for mut typ in c.table.type_symbols { + if typ.kind == .generic_struct_inst { + info := typ.info as ast.GenericStructInst + parent := c.table.type_symbols[info.parent_idx] + if parent.kind == .placeholder { + typ.kind = .placeholder + continue + } + mut parent_info := parent.info as ast.Struct + mut fields := parent_info.fields.clone() + if parent_info.generic_types.len == info.concrete_types.len { + generic_names := parent_info.generic_types.map(c.table.get_type_symbol(it).name) + for i in 0 .. fields.len { + if fields[i].typ.has_flag(.generic) { + sym := c.table.get_type_symbol(fields[i].typ) + if sym.kind == .struct_ && fields[i].typ.idx() != info.parent_idx { + fields[i].typ = c.unwrap_generic_struct(fields[i].typ, generic_names, + info.concrete_types) + } else { + if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, + generic_names, info.concrete_types) + { + fields[i].typ = t_typ + } + } + } + } + parent_info.is_generic = false + parent_info.concrete_types = info.concrete_types.clone() + parent_info.fields = fields + parent_info.parent_type = ast.new_type(info.parent_idx).set_flag(.generic) + typ.is_public = true + typ.kind = .struct_ + typ.info = parent_info + } + } + } +} + pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { if node.typ == ast.void_type { // Short syntax `({foo: bar})` diff --git a/vlib/v/tests/generics_with_nested_generic_struct_init_test.v b/vlib/v/tests/generics_with_nested_generic_struct_init_test.v new file mode 100644 index 0000000000..b218a05b45 --- /dev/null +++ b/vlib/v/tests/generics_with_nested_generic_struct_init_test.v @@ -0,0 +1,24 @@ +fn test_nested_generic_struct_init() { + mut list1 := &List{} + println(list1) + assert '$list1'.contains('head: &nil') + + mut list2 := list_new() + println(list2) + assert '$list2'.contains('head: &nil') +} + +struct List { +pub mut: + head &ListNode = 0 +} + +struct ListNode { +pub mut: + value T + next &ListNode = 0 +} + +pub fn list_new() &List { + return &List{} +}