Skip to content

Commit 3877522

Browse files
authored
table,checker,cgen: fix generics with recursive generics struct (#9862)
1 parent 21d1f86 commit 3877522

File tree

4 files changed

+73
-13
lines changed

4 files changed

+73
-13
lines changed

vlib/v/ast/table.v

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,7 @@ pub fn (mut t Table) bitsize_to_type(bit_size int) Type {
993993
// resolve_generic_to_concrete resolves generics to real types T => int.
994994
// Even map[string]map[string]T can be resolved.
995995
// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl.
996-
pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_names []string, concrete_types []Type) ?Type {
996+
pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_names []string, concrete_types []Type, is_inst bool) ?Type {
997997
mut sym := t.get_type_symbol(generic_type)
998998
if sym.name in generic_names {
999999
index := generic_names.index(sym.name)
@@ -1009,21 +1009,27 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
10091009
elem_sym = t.get_type_symbol(elem_type)
10101010
dims++
10111011
}
1012-
if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types) {
1012+
if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types,
1013+
is_inst)
1014+
{
10131015
idx := t.find_or_register_array_with_dims(typ, dims)
10141016
return new_type(idx).derive(generic_type).clear_flag(.generic)
10151017
}
10161018
} else if sym.kind == .chan {
10171019
info := sym.info as Chan
1018-
if typ := t.resolve_generic_to_concrete(info.elem_type, generic_names, concrete_types) {
1020+
if typ := t.resolve_generic_to_concrete(info.elem_type, generic_names, concrete_types,
1021+
is_inst)
1022+
{
10191023
idx := t.find_or_register_chan(typ, typ.nr_muls() > 0)
10201024
return new_type(idx).derive(generic_type).clear_flag(.generic)
10211025
}
10221026
} else if mut sym.info is MultiReturn {
10231027
mut types := []Type{}
10241028
mut type_changed := false
10251029
for ret_type in sym.info.types {
1026-
if typ := t.resolve_generic_to_concrete(ret_type, generic_names, concrete_types) {
1030+
if typ := t.resolve_generic_to_concrete(ret_type, generic_names, concrete_types,
1031+
is_inst)
1032+
{
10271033
types << typ
10281034
type_changed = true
10291035
} else {
@@ -1038,18 +1044,39 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
10381044
mut type_changed := false
10391045
mut unwrapped_key_type := sym.info.key_type
10401046
mut unwrapped_value_type := sym.info.value_type
1041-
if typ := t.resolve_generic_to_concrete(sym.info.key_type, generic_names, concrete_types) {
1047+
if typ := t.resolve_generic_to_concrete(sym.info.key_type, generic_names, concrete_types,
1048+
is_inst)
1049+
{
10421050
unwrapped_key_type = typ
10431051
type_changed = true
10441052
}
1045-
if typ := t.resolve_generic_to_concrete(sym.info.value_type, generic_names, concrete_types) {
1053+
if typ := t.resolve_generic_to_concrete(sym.info.value_type, generic_names, concrete_types,
1054+
is_inst)
1055+
{
10461056
unwrapped_value_type = typ
10471057
type_changed = true
10481058
}
10491059
if type_changed {
10501060
idx := t.find_or_register_map(unwrapped_key_type, unwrapped_value_type)
10511061
return new_type(idx).derive(generic_type).clear_flag(.generic)
10521062
}
1063+
} else if mut sym.info is Struct {
1064+
if sym.info.is_generic && is_inst {
1065+
mut nrt := '$sym.name<'
1066+
for i in 0 .. concrete_types.len {
1067+
gts := t.get_type_symbol(concrete_types[i])
1068+
nrt += gts.name
1069+
if i != concrete_types.len - 1 {
1070+
nrt += ','
1071+
}
1072+
}
1073+
nrt += '>'
1074+
mut idx := t.type_idxs[nrt]
1075+
if idx == 0 {
1076+
idx = t.add_placeholder_type(nrt, .v)
1077+
}
1078+
return new_type(idx).derive(generic_type).clear_flag(.generic)
1079+
}
10531080
}
10541081
return none
10551082
}
@@ -1070,7 +1097,7 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() {
10701097
generic_names := parent_info.generic_types.map(t.get_type_symbol(it).name)
10711098
for i in 0 .. fields.len {
10721099
if t_typ := t.resolve_generic_to_concrete(fields[i].typ, generic_names,
1073-
info.concrete_types)
1100+
info.concrete_types, true)
10741101
{
10751102
fields[i].typ = t_typ
10761103
}

vlib/v/checker/checker.v

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,7 +1464,7 @@ fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_e
14641464
generic_names := rts.info.generic_types.map(c.table.get_type_symbol(it).name)
14651465
for i, _ in fields {
14661466
if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ,
1467-
generic_names, concrete_types)
1467+
generic_names, concrete_types, false)
14681468
{
14691469
fields[i].typ = t_typ
14701470
}
@@ -1764,7 +1764,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
17641764
}
17651765
if call_expr.concrete_types.len > 0 && method.return_type != 0 {
17661766
if typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names,
1767-
call_expr.concrete_types)
1767+
call_expr.concrete_types, false)
17681768
{
17691769
call_expr.return_type = typ
17701770
return typ
@@ -2280,7 +2280,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
22802280
if param.typ.has_flag(.generic)
22812281
&& func.generic_names.len == call_expr.concrete_types.len {
22822282
if unwrap_typ := c.table.resolve_generic_to_concrete(param.typ, func.generic_names,
2283-
call_expr.concrete_types)
2283+
call_expr.concrete_types, false)
22842284
{
22852285
c.check_expected_call_arg(typ, unwrap_typ, call_expr.language) or {
22862286
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 {
22972297
}
22982298
if call_expr.concrete_types.len > 0 && func.return_type != 0 {
22992299
if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
2300-
call_expr.concrete_types)
2300+
call_expr.concrete_types, false)
23012301
{
23022302
call_expr.return_type = typ
23032303
return typ
@@ -4158,7 +4158,9 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
41584158

41594159
pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
41604160
if typ.has_flag(.generic) {
4161-
if t_typ := c.table.resolve_generic_to_concrete(typ, c.cur_fn.generic_names, c.cur_fn.cur_generic_types) {
4161+
if t_typ := c.table.resolve_generic_to_concrete(typ, c.cur_fn.generic_names, c.cur_fn.cur_generic_types,
4162+
false)
4163+
{
41624164
return t_typ
41634165
}
41644166
}

vlib/v/gen/c/fn.v

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,9 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
479479

480480
pub fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
481481
if typ.has_flag(.generic) {
482-
if t_typ := g.table.resolve_generic_to_concrete(typ, g.cur_fn.generic_names, g.cur_concrete_types) {
482+
if t_typ := g.table.resolve_generic_to_concrete(typ, g.cur_fn.generic_names, g.cur_concrete_types,
483+
false)
484+
{
483485
return t_typ
484486
}
485487
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
pub struct Node<T> {
2+
value T
3+
points_to []&Node<T>
4+
}
5+
6+
fn test_generics_with_recursive_generics_struct() {
7+
mid := &Node<string>{
8+
value: 'Middle'
9+
}
10+
finish := &Node<string>{
11+
value: 'Finish'
12+
}
13+
14+
graph := &Node<string>{
15+
value: 'Start'
16+
points_to: [
17+
&Node<string>{
18+
value: 'TopLeft'
19+
points_to: [
20+
finish,
21+
mid,
22+
]
23+
},
24+
]
25+
}
26+
27+
println(graph.points_to[0].value)
28+
assert graph.points_to[0].value == 'TopLeft'
29+
}

0 commit comments

Comments
 (0)