From 3877522ee3a8a40a99c040a10630b8976ed8a63c Mon Sep 17 00:00:00 2001 From: yuyi Date: Tue, 27 Apr 2021 01:05:10 +0800 Subject: [PATCH] table,checker,cgen: fix generics with recursive generics struct (#9862) --- vlib/v/ast/table.v | 41 +++++++++++++++---- vlib/v/checker/checker.v | 12 +++--- vlib/v/gen/c/fn.v | 4 +- ...rics_with_recursive_generics_struct_test.v | 29 +++++++++++++ 4 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 vlib/v/tests/generics_with_recursive_generics_struct_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 6acb88292e..6cbd3e3c2a 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -993,7 +993,7 @@ pub fn (mut t Table) bitsize_to_type(bit_size int) Type { // resolve_generic_to_concrete resolves generics to real types T => int. // Even map[string]map[string]T can be resolved. // This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl. -pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_names []string, concrete_types []Type) ?Type { +pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_names []string, concrete_types []Type, is_inst bool) ?Type { mut sym := t.get_type_symbol(generic_type) if sym.name in generic_names { index := generic_names.index(sym.name) @@ -1009,13 +1009,17 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name elem_sym = t.get_type_symbol(elem_type) dims++ } - if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types) { + if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types, + is_inst) + { idx := t.find_or_register_array_with_dims(typ, dims) return new_type(idx).derive(generic_type).clear_flag(.generic) } } else if sym.kind == .chan { info := sym.info as Chan - if typ := t.resolve_generic_to_concrete(info.elem_type, generic_names, concrete_types) { + if typ := t.resolve_generic_to_concrete(info.elem_type, generic_names, concrete_types, + is_inst) + { idx := t.find_or_register_chan(typ, typ.nr_muls() > 0) return new_type(idx).derive(generic_type).clear_flag(.generic) } @@ -1023,7 +1027,9 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name mut types := []Type{} mut type_changed := false for ret_type in sym.info.types { - if typ := t.resolve_generic_to_concrete(ret_type, generic_names, concrete_types) { + if typ := t.resolve_generic_to_concrete(ret_type, generic_names, concrete_types, + is_inst) + { types << typ type_changed = true } else { @@ -1038,11 +1044,15 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name mut type_changed := false mut unwrapped_key_type := sym.info.key_type mut unwrapped_value_type := sym.info.value_type - if typ := t.resolve_generic_to_concrete(sym.info.key_type, generic_names, concrete_types) { + if typ := t.resolve_generic_to_concrete(sym.info.key_type, generic_names, concrete_types, + is_inst) + { unwrapped_key_type = typ type_changed = true } - if typ := t.resolve_generic_to_concrete(sym.info.value_type, generic_names, concrete_types) { + if typ := t.resolve_generic_to_concrete(sym.info.value_type, generic_names, concrete_types, + is_inst) + { unwrapped_value_type = typ type_changed = true } @@ -1050,6 +1060,23 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name idx := t.find_or_register_map(unwrapped_key_type, unwrapped_value_type) return new_type(idx).derive(generic_type).clear_flag(.generic) } + } else if mut sym.info is Struct { + if sym.info.is_generic && is_inst { + mut nrt := '$sym.name<' + for i in 0 .. concrete_types.len { + gts := t.get_type_symbol(concrete_types[i]) + nrt += gts.name + if i != concrete_types.len - 1 { + nrt += ',' + } + } + nrt += '>' + mut idx := t.type_idxs[nrt] + if idx == 0 { + idx = t.add_placeholder_type(nrt, .v) + } + return new_type(idx).derive(generic_type).clear_flag(.generic) + } } return none } @@ -1070,7 +1097,7 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() { 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) + info.concrete_types, true) { fields[i].typ = t_typ } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index eb8abcd1de..7260340fc8 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1464,7 +1464,7 @@ fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_e generic_names := rts.info.generic_types.map(c.table.get_type_symbol(it).name) for i, _ in fields { if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, - generic_names, concrete_types) + generic_names, concrete_types, false) { fields[i].typ = t_typ } @@ -1764,7 +1764,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { } if call_expr.concrete_types.len > 0 && method.return_type != 0 { if typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names, - call_expr.concrete_types) + call_expr.concrete_types, false) { call_expr.return_type = typ return typ @@ -2280,7 +2280,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type { if param.typ.has_flag(.generic) && func.generic_names.len == call_expr.concrete_types.len { if unwrap_typ := c.table.resolve_generic_to_concrete(param.typ, func.generic_names, - call_expr.concrete_types) + call_expr.concrete_types, false) { c.check_expected_call_arg(typ, unwrap_typ, call_expr.language) or { c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos) @@ -2297,7 +2297,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type { } if call_expr.concrete_types.len > 0 && func.return_type != 0 { if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names, - call_expr.concrete_types) + call_expr.concrete_types, false) { call_expr.return_type = typ return typ @@ -4158,7 +4158,9 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) { pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type { if typ.has_flag(.generic) { - if t_typ := c.table.resolve_generic_to_concrete(typ, c.cur_fn.generic_names, c.cur_fn.cur_generic_types) { + if t_typ := c.table.resolve_generic_to_concrete(typ, c.cur_fn.generic_names, c.cur_fn.cur_generic_types, + false) + { return t_typ } } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 29e9bdd4ae..9b3b2dd03b 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -479,7 +479,9 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { pub fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { if typ.has_flag(.generic) { - if t_typ := g.table.resolve_generic_to_concrete(typ, g.cur_fn.generic_names, g.cur_concrete_types) { + if t_typ := g.table.resolve_generic_to_concrete(typ, g.cur_fn.generic_names, g.cur_concrete_types, + false) + { return t_typ } } diff --git a/vlib/v/tests/generics_with_recursive_generics_struct_test.v b/vlib/v/tests/generics_with_recursive_generics_struct_test.v new file mode 100644 index 0000000000..9216faf5df --- /dev/null +++ b/vlib/v/tests/generics_with_recursive_generics_struct_test.v @@ -0,0 +1,29 @@ +pub struct Node { + value T + points_to []&Node +} + +fn test_generics_with_recursive_generics_struct() { + mid := &Node{ + value: 'Middle' + } + finish := &Node{ + value: 'Finish' + } + + graph := &Node{ + value: 'Start' + points_to: [ + &Node{ + value: 'TopLeft' + points_to: [ + finish, + mid, + ] + }, + ] + } + + println(graph.points_to[0].value) + assert graph.points_to[0].value == 'TopLeft' +}