From 478bb9ce8eaf3576d6c1d73ecedd098b36f0c394 Mon Sep 17 00:00:00 2001 From: yuyi Date: Fri, 26 Mar 2021 14:32:11 +0800 Subject: [PATCH] parser, checker: fix generic fn that returns a generic struct (#9469) --- vlib/v/checker/checker.v | 34 +++++++++++++++++-- vlib/v/parser/parse_type.v | 5 +++ .../generics_return_generics_struct_test.v | 31 +++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 vlib/v/tests/generics_return_generics_struct_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index af7de08ced..5cbcd36d56 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1944,9 +1944,39 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { if rts.info.generic_types.len > 0 { gts := c.table.get_type_symbol(call_expr.generic_types[0]) nrt := '$rts.name<$gts.name>' + c_nrt := '${rts.name}_T_$gts.name' idx := c.table.type_idxs[nrt] - c.ensure_type_exists(idx, call_expr.pos) or {} - call_expr.return_type = table.new_type(idx).derive(f.return_type) + if idx != 0 { + c.ensure_type_exists(idx, call_expr.pos) or {} + call_expr.return_type = table.new_type(idx).derive(f.return_type) + } else { + mut fields := rts.info.fields.clone() + if rts.info.generic_types.len == generic_types.len { + for i, _ in fields { + mut field := fields[i] + if field.typ.has_flag(.generic) { + for j, gp in rts.info.generic_types { + if gp == field.typ { + field.typ = generic_types[j].derive(field.typ).clear_flag(.generic) + break + } + } + } + fields[i] = field + } + mut info := rts.info + info.generic_types = [] + info.fields = fields + stru_idx := c.table.register_type_symbol(table.TypeSymbol{ + kind: .struct_ + name: nrt + cname: util.no_dots(c_nrt) + mod: c.mod + info: info + }) + call_expr.return_type = table.new_type(stru_idx) + } + } } } } else { diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 51eb4f1cdd..d5c67d3f42 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -502,6 +502,11 @@ pub fn (mut p Parser) parse_generic_struct_inst_type(name string) table.Type { } }) return table.new_type(idx) + } else { + idx := p.table.find_type_idx(name) + if idx != 0 { + return table.new_type(idx).set_flag(.generic) + } } return p.parse_enum_or_struct_type(name, .v) } diff --git a/vlib/v/tests/generics_return_generics_struct_test.v b/vlib/v/tests/generics_return_generics_struct_test.v new file mode 100644 index 0000000000..82958f2c03 --- /dev/null +++ b/vlib/v/tests/generics_return_generics_struct_test.v @@ -0,0 +1,31 @@ +pub struct Optional { +mut: + value T + some bool +} + +pub fn new_some(value T) Optional { + return {value: value, some: true} +} + +pub fn some(opt Optional) bool { + return opt.some +} + +pub fn get(opt Optional) T { + return opt.value +} + +pub fn set(mut opt Optional, value T) { + opt.value = value + opt.some = true +} + +fn test_generics_return_generics_struct() { + mut o := new_some(23) + println(some(o)) + assert some(o) == true + set(mut o, 42) + println(get(o)) + assert get(o) == 42 +}