Skip to content

Commit cc7e600

Browse files
authored
checker: fix generic fn with short generic struct init syntax (#16504)
1 parent 00383ed commit cc7e600

File tree

5 files changed

+169
-36
lines changed

5 files changed

+169
-36
lines changed

vlib/v/checker/check_types.v

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,122 @@ pub fn (mut c Checker) symmetric_check(left ast.Type, right ast.Type) bool {
643643
return c.check_basic(left, right)
644644
}
645645

646+
fn (mut c Checker) infer_generic_struct_init_concrete_types(typ ast.Type, node ast.StructInit) []ast.Type {
647+
mut concrete_types := []ast.Type{}
648+
sym := c.table.sym(typ)
649+
if sym.info is ast.Struct {
650+
generic_names := sym.info.generic_types.map(c.table.sym(it).name)
651+
gname: for gt_name in generic_names {
652+
for ft in sym.info.fields {
653+
field_sym := c.table.sym(ft.typ)
654+
if field_sym.name == gt_name {
655+
for t in node.fields {
656+
if ft.name == t.name && t.typ != 0 {
657+
concrete_types << t.typ
658+
continue gname
659+
}
660+
}
661+
}
662+
if field_sym.kind == .array {
663+
for t in node.fields {
664+
if ft.name == t.name {
665+
init_sym := c.table.sym(t.typ)
666+
if init_sym.kind == .array {
667+
mut init_elem_info := init_sym.info as ast.Array
668+
mut field_elem_info := field_sym.info as ast.Array
669+
mut init_elem_sym := c.table.sym(init_elem_info.elem_type)
670+
mut field_elem_sym := c.table.sym(field_elem_info.elem_type)
671+
for {
672+
if init_elem_sym.kind == .array && field_elem_sym.kind == .array {
673+
init_elem_info = init_elem_sym.info as ast.Array
674+
init_elem_sym = c.table.sym(init_elem_info.elem_type)
675+
field_elem_info = field_elem_sym.info as ast.Array
676+
field_elem_sym = c.table.sym(field_elem_info.elem_type)
677+
} else {
678+
if field_elem_sym.name == gt_name {
679+
mut elem_typ := init_elem_info.elem_type
680+
if field_elem_info.elem_type.nr_muls() > 0
681+
&& elem_typ.nr_muls() > 0 {
682+
elem_typ = elem_typ.set_nr_muls(0)
683+
}
684+
concrete_types << elem_typ
685+
continue gname
686+
}
687+
break
688+
}
689+
}
690+
}
691+
}
692+
}
693+
} else if field_sym.kind == .array_fixed {
694+
for t in node.fields {
695+
if ft.name == t.name {
696+
init_sym := c.table.sym(t.typ)
697+
if init_sym.kind == .array_fixed {
698+
mut init_elem_info := init_sym.info as ast.ArrayFixed
699+
mut field_elem_info := field_sym.info as ast.ArrayFixed
700+
mut init_elem_sym := c.table.sym(init_elem_info.elem_type)
701+
mut field_elem_sym := c.table.sym(field_elem_info.elem_type)
702+
for {
703+
if init_elem_sym.kind == .array_fixed
704+
&& field_elem_sym.kind == .array_fixed {
705+
init_elem_info = init_elem_sym.info as ast.ArrayFixed
706+
init_elem_sym = c.table.sym(init_elem_info.elem_type)
707+
field_elem_info = field_elem_sym.info as ast.ArrayFixed
708+
field_elem_sym = c.table.sym(field_elem_info.elem_type)
709+
} else {
710+
if field_elem_sym.name == gt_name {
711+
mut elem_typ := init_elem_info.elem_type
712+
if field_elem_info.elem_type.nr_muls() > 0
713+
&& elem_typ.nr_muls() > 0 {
714+
elem_typ = elem_typ.set_nr_muls(0)
715+
}
716+
concrete_types << elem_typ
717+
continue gname
718+
}
719+
break
720+
}
721+
}
722+
}
723+
}
724+
}
725+
} else if field_sym.kind == .map {
726+
for t in node.fields {
727+
if ft.name == t.name {
728+
init_sym := c.table.sym(t.typ)
729+
if init_sym.kind == .map {
730+
init_map_info := init_sym.info as ast.Map
731+
field_map_info := field_sym.info as ast.Map
732+
if field_map_info.key_type.has_flag(.generic)
733+
&& c.table.sym(field_map_info.key_type).name == gt_name {
734+
mut key_typ := init_map_info.key_type
735+
if field_map_info.key_type.nr_muls() > 0
736+
&& key_typ.nr_muls() > 0 {
737+
key_typ = key_typ.set_nr_muls(0)
738+
}
739+
concrete_types << key_typ
740+
continue gname
741+
}
742+
if field_map_info.value_type.has_flag(.generic)
743+
&& c.table.sym(field_map_info.value_type).name == gt_name {
744+
mut val_typ := init_map_info.value_type
745+
if field_map_info.value_type.nr_muls() > 0
746+
&& val_typ.nr_muls() > 0 {
747+
val_typ = val_typ.set_nr_muls(0)
748+
}
749+
concrete_types << val_typ
750+
continue gname
751+
}
752+
}
753+
}
754+
}
755+
}
756+
}
757+
}
758+
}
759+
return concrete_types
760+
}
761+
646762
pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr) {
647763
mut inferred_types := []ast.Type{}
648764
for gi, gt_name in func.generic_names {

vlib/v/checker/fn.v

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,10 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
10021002
continue
10031003
}
10041004

1005-
arg_typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr))
1005+
mut arg_typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr))
1006+
if call_arg.expr is ast.StructInit {
1007+
arg_typ = c.expr(call_arg.expr)
1008+
}
10061009
node.args[i].typ = arg_typ
10071010
if c.inside_comptime_for_field {
10081011
if mut call_arg.expr is ast.Ident {

vlib/v/checker/struct.v

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -254,26 +254,35 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
254254
c.error('unknown type `${ct_sym.name}`', node.pos)
255255
}
256256
}
257-
if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0
258-
&& !node.is_short_syntax {
259-
if c.table.cur_concrete_types.len == 0 {
260-
c.error('generic struct init must specify type parameter, e.g. Foo<int>',
261-
node.pos)
262-
} else if node.generic_types.len == 0 {
263-
c.error('generic struct init must specify type parameter, e.g. Foo<T>',
264-
node.pos)
265-
} else if node.generic_types.len > 0
266-
&& node.generic_types.len != struct_sym.info.generic_types.len {
267-
c.error('generic struct init expects ${struct_sym.info.generic_types.len} generic parameter, but got ${node.generic_types.len}',
268-
node.pos)
269-
} else if node.generic_types.len > 0 && c.table.cur_fn != unsafe { nil } {
270-
for gtyp in node.generic_types {
271-
gtyp_name := c.table.sym(gtyp).name
272-
if gtyp_name !in c.table.cur_fn.generic_names {
273-
cur_generic_names := '(' + c.table.cur_fn.generic_names.join(',') + ')'
274-
c.error('generic struct init type parameter `${gtyp_name}` must be within the parameters `${cur_generic_names}` of the current generic function',
275-
node.pos)
276-
break
257+
if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0 {
258+
if node.is_short_syntax {
259+
concrete_types := c.infer_generic_struct_init_concrete_types(node.typ,
260+
node)
261+
if concrete_types.len > 0 {
262+
generic_names := struct_sym.info.generic_types.map(c.table.sym(it).name)
263+
node.typ = c.table.unwrap_generic_type(node.typ, generic_names, concrete_types)
264+
return node.typ
265+
}
266+
} else {
267+
if c.table.cur_concrete_types.len == 0 {
268+
c.error('generic struct init must specify type parameter, e.g. Foo<int>',
269+
node.pos)
270+
} else if node.generic_types.len == 0 {
271+
c.error('generic struct init must specify type parameter, e.g. Foo<T>',
272+
node.pos)
273+
} else if node.generic_types.len > 0
274+
&& node.generic_types.len != struct_sym.info.generic_types.len {
275+
c.error('generic struct init expects ${struct_sym.info.generic_types.len} generic parameter, but got ${node.generic_types.len}',
276+
node.pos)
277+
} else if node.generic_types.len > 0 && c.table.cur_fn != unsafe { nil } {
278+
for gtyp in node.generic_types {
279+
gtyp_name := c.table.sym(gtyp).name
280+
if gtyp_name !in c.table.cur_fn.generic_names {
281+
cur_generic_names := '(' + c.table.cur_fn.generic_names.join(',') + ')'
282+
c.error('generic struct init type parameter `${gtyp_name}` must be within the parameters `${cur_generic_names}` of the current generic function',
283+
node.pos)
284+
break
285+
}
277286
}
278287
}
279288
}
@@ -447,7 +456,8 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
447456
c.mark_as_referenced(mut &field.expr, true)
448457
}
449458
}
450-
} else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
459+
} else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder
460+
&& !field_info.typ.has_flag(.generic) {
451461
c.check_expected(c.unwrap_generic(expr_type), c.unwrap_generic(field_info.typ)) or {
452462
c.error('cannot assign to field `${field_info.name}`: ${err.msg()}',
453463
field.pos)

vlib/v/checker/tests/generics_struct_init_type_parameter_err.out

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,10 @@ vlib/v/checker/tests/generics_struct_init_type_parameter_err.vv:6:9: error: gene
55
| ~~~~~~~~~~~~
66
7 | result: res
77
8 | }
8-
vlib/v/checker/tests/generics_struct_init_type_parameter_err.vv:7:3: error: cannot assign to field `result`: expected `U`, not `int`
9-
5 | fn send_1<A, B>(res A, b B) string {
10-
6 | msg := Response<U>{
11-
7 | result: res
12-
| ~~~~~~~~~~~
13-
8 | }
14-
9 | println(b)
158
vlib/v/checker/tests/generics_struct_init_type_parameter_err.vv:14:9: error: generic struct init expects 1 generic parameter, but got 2
169
12 |
1710
13 | fn send_2<A, B>(res A, b B) string {
1811
14 | msg := Response<A, B>{
1912
| ~~~~~~~~~~~~~~~
2013
15 | result: res
2114
16 | }
22-
vlib/v/checker/tests/generics_struct_init_type_parameter_err.vv:15:3: error: cannot assign to field `result`: expected `U`, not `int`
23-
13 | fn send_2<A, B>(res A, b B) string {
24-
14 | msg := Response<A, B>{
25-
15 | result: res
26-
| ~~~~~~~~~~~
27-
16 | }
28-
17 | println(b)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
pub struct EncodeOptions<T> {
2+
payload T
3+
key string
4+
algorithm string = 'HS256'
5+
}
6+
7+
pub fn encode<T>(options EncodeOptions<T>) !string {
8+
return 'test'
9+
}
10+
11+
fn test_generic_fn_with_generic_struct_init_syntax() {
12+
payload := {
13+
'test': 'test'
14+
}
15+
ret := encode(payload: payload, key: 'test')!
16+
println(ret)
17+
assert ret == 'test'
18+
}

0 commit comments

Comments
 (0)