diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index dde23e40bd..841c17c40d 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -609,6 +609,56 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) { } } +fn (mut c Checker) unwrap_generics_struct_init(struct_type ast.Type) ast.Type { + ts := c.table.get_type_symbol(struct_type) + if ts.info is ast.Struct { + if ts.info.is_generic { + mut nrt := '$ts.name<' + mut c_nrt := '${ts.name}_T_' + for i in 0 .. ts.info.generic_types.len { + gts := c.table.get_type_symbol(c.unwrap_generic(ts.info.generic_types[i])) + nrt += gts.name + c_nrt += gts.name + if i != ts.info.generic_types.len - 1 { + nrt += ',' + c_nrt += '_' + } + } + nrt += '>' + idx := c.table.type_idxs[nrt] + if idx != 0 && c.table.type_symbols[idx].kind != .placeholder { + return ast.new_type(idx).derive(struct_type).clear_flag(.generic) + } else { + mut fields := ts.info.fields.clone() + if ts.info.generic_types.len == c.cur_concrete_types.len { + generic_names := ts.info.generic_types.map(c.table.get_type_symbol(it).name) + for i in 0 .. fields.len { + if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, + generic_names, c.cur_concrete_types, true) + { + fields[i].typ = t_typ + } + } + mut info := ts.info + info.is_generic = false + info.concrete_types = c.cur_concrete_types.clone() + info.parent_type = struct_type + info.fields = fields + stru_idx := c.table.register_type_symbol(ast.TypeSymbol{ + kind: .struct_ + name: nrt + cname: util.no_dots(c_nrt) + mod: c.mod + info: info + }) + return ast.new_type(stru_idx).derive(struct_type).clear_flag(.generic) + } + } + } + } + return struct_type +} + pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { // typ := c.table.find_type(struct_init.typ.typ.name) or { // c.error('unknown struct: $struct_init.typ.typ.name', struct_init.pos) @@ -627,7 +677,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { struct_init.typ = c.expected_type } } - utyp := c.unwrap_generic(struct_init.typ) + utyp := c.unwrap_generics_struct_init(struct_init.typ) c.ensure_type_exists(utyp, struct_init.pos) or {} type_sym := c.table.get_type_symbol(utyp) if !c.inside_unsafe && type_sym.kind == .sum_type { @@ -757,7 +807,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { if is_embed { expected_type = embed_type c.expected_type = expected_type - expr_type = c.expr(field.expr) + expr_type = c.unwrap_generic(c.expr(field.expr)) expr_type_sym := c.table.get_type_symbol(expr_type) if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { c.check_expected(expr_type, embed_type) or { @@ -772,7 +822,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { field_type_sym := c.table.get_type_symbol(info_field.typ) expected_type = info_field.typ c.expected_type = expected_type - expr_type = c.expr(field.expr) + expr_type = c.unwrap_generic(c.expr(field.expr)) if !info_field.typ.has_flag(.optional) { expr_type = c.check_expr_opt_call(field.expr, expr_type) } @@ -882,7 +932,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { c.error('expression is not an lvalue', struct_init.update_expr.position()) } } - return struct_init.typ + return utyp } fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) { @@ -2930,7 +2980,14 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) ast.Typ // TODO: non deferred pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { c.expected_type = c.table.cur_fn.return_type - expected_type := c.unwrap_generic(c.expected_type) + mut expected_type := c.unwrap_generic(c.expected_type) + if expected_type.has_flag(.generic) && c.table.get_type_symbol(expected_type).kind == .struct_ { + if t_typ := c.table.resolve_generic_to_concrete(expected_type, c.table.cur_fn.generic_names, + c.cur_concrete_types, true) + { + expected_type = t_typ + } + } expected_type_sym := c.table.get_type_symbol(expected_type) if return_stmt.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type { c.error('unexpected argument, current function does not return anything', return_stmt.exprs[0].position()) diff --git a/vlib/v/checker/tests/check_generic_int_init.out b/vlib/v/checker/tests/check_generic_int_init.out deleted file mode 100644 index 8542862d5d..0000000000 --- a/vlib/v/checker/tests/check_generic_int_init.out +++ /dev/null @@ -1,6 +0,0 @@ -vlib/v/checker/tests/check_generic_int_init.vv:2:9: error: type `int` is private - 1 | fn test() T { - 2 | return T{} - | ~~~ - 3 | } - 4 | diff --git a/vlib/v/checker/tests/check_generic_int_init.vv b/vlib/v/checker/tests/check_generic_int_init.vv deleted file mode 100644 index f1518c799b..0000000000 --- a/vlib/v/checker/tests/check_generic_int_init.vv +++ /dev/null @@ -1,7 +0,0 @@ -fn test() T { - return T{} -} - -fn main() { - _ := test() -} diff --git a/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out b/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out index 502f2b816a..d2c1933c8b 100644 --- a/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out +++ b/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out @@ -12,3 +12,10 @@ vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:32:1: error: g | ~~~~~~~~~~~~~~ 33 | println("hi") 34 | } +vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:21:14: error: cannot use `Generic` as `Generic` in argument 1 to `g_worker` + 19 | } + 20 | + 21 | go g_worker(g) + | ^ + 22 | + 23 | return g diff --git a/vlib/v/checker/tests/generics_fn_return_generic_struct_err.out b/vlib/v/checker/tests/generics_fn_return_generic_struct_err.out index 12a579c300..b6efe4555b 100644 --- a/vlib/v/checker/tests/generics_fn_return_generic_struct_err.out +++ b/vlib/v/checker/tests/generics_fn_return_generic_struct_err.out @@ -5,3 +5,9 @@ vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv:13:32: error: retu | ~~~~~~~~~~~~~~~~~~~~ 14 | d := GenericChannelStruct{ 15 | ch: chan T{} +vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv:17:9: error: cannot use `GenericChannelStruct` as type `GenericChannelStruct` in return argument + 15 | ch: chan T{} + 16 | } + 17 | return d + | ^ + 18 | } diff --git a/vlib/v/tests/generics_with_generics_struct_init_test.v b/vlib/v/tests/generics_with_generics_struct_init_test.v new file mode 100644 index 0000000000..039fd4d971 --- /dev/null +++ b/vlib/v/tests/generics_with_generics_struct_init_test.v @@ -0,0 +1,32 @@ +struct List { +mut: + count u32 + first &ListNode + last &ListNode +} + +struct ListNode { +mut: + val T + next &ListNode = 0 +} + +fn create(arr []T) &List { + assert arr.len > 0 + mut n := &ListNode{ + val: arr[0] + next: 0 + } + mut l := &List{ + first: n + last: n + count: 1 + } + return l +} + +fn test_generics_with_generic_structs_init() { + n := create([1, 2, 3]) + println(n) + assert n.count == 1 +}