checker: fix nested generic struct init (fix #10652) (#10659)

pull/10668/head
yuyi 2021-07-04 23:37:31 +08:00 committed by GitHub
parent 8c43d2450f
commit 665279938e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 38 deletions

View File

@ -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
}
}
}
}

View File

@ -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()

View File

@ -664,12 +664,20 @@ 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,
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
mut info_concrete_types := []ast.Type{}
for i in 0 .. ts.info.generic_types.len {
@ -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})`

View File

@ -0,0 +1,24 @@
fn test_nested_generic_struct_init() {
mut list1 := &List<int>{}
println(list1)
assert '$list1'.contains('head: &nil')
mut list2 := list_new<int>()
println(list2)
assert '$list2'.contains('head: &nil')
}
struct List<T> {
pub mut:
head &ListNode<T> = 0
}
struct ListNode<T> {
pub mut:
value T
next &ListNode<T> = 0
}
pub fn list_new<T>() &List<T> {
return &List<T>{}
}