From e83a8416d53c2c1f280877b60846041c8030b139 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 18 Dec 2022 00:17:43 +0800 Subject: [PATCH] ast, parser: fix generic struct init with inconsistent generic types (#16697) --- vlib/v/ast/table.v | 42 +++++++++++++++---- vlib/v/ast/types.v | 23 +++++----- vlib/v/parser/parse_type.v | 32 +++++++++++++- ...t_with_inconsistent_generic_types_3_test.v | 34 +++++++++++++++ 4 files changed, 111 insertions(+), 20 deletions(-) create mode 100644 vlib/v/tests/generics_struct_init_with_inconsistent_generic_types_3_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 4c7823793a..2ef862e65c 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1704,18 +1704,30 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name if sym.info.is_generic { mut nrt := '${sym.name}[' mut t_generic_names := generic_names.clone() - if generic_names.len == sym.info.generic_types.len { + mut t_concrete_types := concrete_types.clone() + if sym.generic_types.len > 0 && sym.generic_types.len == sym.info.generic_types.len + && sym.generic_types != sym.info.generic_types { t_generic_names = sym.info.generic_types.map(t.sym(it).name) + t_concrete_types = [] + for t_typ in sym.generic_types { + tname := t.sym(t_typ).name + index := generic_names.index(tname) + if index >= 0 && index < concrete_types.len { + t_concrete_types << concrete_types[index] + } + } } for i in 0 .. sym.info.generic_types.len { if ct := t.resolve_generic_to_concrete(sym.info.generic_types[i], - t_generic_names, concrete_types) + t_generic_names, t_concrete_types) { gts := t.sym(ct) nrt += gts.name if i != sym.info.generic_types.len - 1 { nrt += ', ' } + } else { + return none } } nrt += ']' @@ -1839,14 +1851,24 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr return typ } mut t_generic_names := generic_names.clone() - if generic_names.len == ts.info.generic_types.len { + mut t_concrete_types := concrete_types.clone() + if ts.generic_types.len > 0 && ts.generic_types.len == ts.info.generic_types.len + && ts.generic_types != ts.info.generic_types { t_generic_names = ts.info.generic_types.map(t.sym(it).name) + t_concrete_types = [] + for t_typ in ts.generic_types { + tname := t.sym(t_typ).name + index := generic_names.index(tname) + if index >= 0 && index < concrete_types.len { + t_concrete_types << concrete_types[index] + } + } } nrt = '${ts.name}[' c_nrt = '${ts.cname}_T_' for i in 0 .. ts.info.generic_types.len { if ct := t.resolve_generic_to_concrete(ts.info.generic_types[i], t_generic_names, - concrete_types) + t_concrete_types) { gts := t.sym(ct) nrt += gts.name @@ -1855,6 +1877,8 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr nrt += ', ' c_nrt += '_' } + } else { + return typ } } nrt += ']' @@ -1869,10 +1893,10 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr sym := t.sym(fields[i].typ) if sym.kind == .struct_ && fields[i].typ.idx() != typ.idx() { fields[i].typ = t.unwrap_generic_type(fields[i].typ, t_generic_names, - concrete_types) + t_concrete_types) } else { if t_typ := t.resolve_generic_to_concrete(fields[i].typ, t_generic_names, - concrete_types) + t_concrete_types) { fields[i].typ = t_typ } @@ -1882,7 +1906,7 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr // update concrete types for i in 0 .. ts.info.generic_types.len { if t_typ := t.resolve_generic_to_concrete(ts.info.generic_types[i], - t_generic_names, concrete_types) + t_generic_names, t_concrete_types) { final_concrete_types << t_typ } @@ -1903,7 +1927,9 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr } } } - t.register_fn_concrete_types(method.fkey(), final_concrete_types) + if final_concrete_types.len == method.generic_names.len { + t.register_fn_concrete_types(method.fkey(), final_concrete_types) + } } } } diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 50e209d69d..0cf8541d15 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -89,17 +89,18 @@ pub struct TypeSymbol { pub: parent_idx int pub mut: - info TypeInfo - kind Kind - name string // the internal & source name of the type, i.e. `[5]int`. - cname string // the name with no dots for use in the generated C code - methods []Fn - mod string - is_pub bool - language Language - idx int - size int = -1 - align int = -1 + info TypeInfo + kind Kind + name string // the internal & source name of the type, i.e. `[5]int`. + cname string // the name with no dots for use in the generated C code + methods []Fn + generic_types []Type + mod string + is_pub bool + language Language + idx int + size int = -1 + align int = -1 } // max of 8 diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index beb6213e89..3914ef58ff 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -647,7 +647,37 @@ pub fn (mut p Parser) find_type_or_add_placeholder(name string, language ast.Lan // struct / enum / placeholder mut idx := p.table.find_type_idx(name) if idx > 0 { - return ast.new_type(idx) + mut typ := ast.new_type(idx) + sym := p.table.sym(typ) + match sym.info { + ast.Struct, ast.Interface, ast.SumType { + if p.struct_init_generic_types.len > 0 && sym.info.generic_types.len > 0 + && p.struct_init_generic_types != sym.info.generic_types { + generic_names := p.struct_init_generic_types.map(p.table.sym(it).name) + mut sym_name := sym.name + '[' + for i, gt in generic_names { + sym_name += gt + if i != generic_names.len - 1 { + sym_name += ',' + } + } + sym_name += ']' + existing_idx := p.table.type_idxs[sym_name] + if existing_idx > 0 { + idx = existing_idx + } else { + idx = p.table.register_sym(ast.TypeSymbol{ + ...sym + name: sym_name + generic_types: p.struct_init_generic_types.clone() + }) + } + typ = ast.new_type(idx) + } + } + else {} + } + return typ } // not found - add placeholder idx = p.table.add_placeholder_type(name, language) diff --git a/vlib/v/tests/generics_struct_init_with_inconsistent_generic_types_3_test.v b/vlib/v/tests/generics_struct_init_with_inconsistent_generic_types_3_test.v new file mode 100644 index 0000000000..0f2bc20408 --- /dev/null +++ b/vlib/v/tests/generics_struct_init_with_inconsistent_generic_types_3_test.v @@ -0,0 +1,34 @@ +module main + +pub struct Maybe[T] { + present bool + val T +} + +pub fn some[T](t T) Maybe[T] { + return Maybe[T]{true, t} +} + +pub fn (me Maybe[T]) map[T, U](f fn (T) U) Maybe[U] { + if me.present { + return Maybe[U]{true, f(me.val)} + } + return Maybe[U]{} +} + +pub fn (o Maybe[T]) get() ?T { + if o.present { + return o.val + } else { + return none + } +} + +fn test_generics_struct_init_with_inconsistent_generic_types() { + m := some[int](12) + ret := m.map[int, string](fn (i int) string { + return i.str() + }) + println(ret) + assert ret.val == '12' +}