1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

checker: fix generic fn with short generic struct init syntax (#16504)

This commit is contained in:
yuyi 2022-11-22 19:27:49 +08:00 committed by GitHub
parent 00383edd3d
commit cc7e6006f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 169 additions and 36 deletions

View File

@ -643,6 +643,122 @@ pub fn (mut c Checker) symmetric_check(left ast.Type, right ast.Type) bool {
return c.check_basic(left, right)
}
fn (mut c Checker) infer_generic_struct_init_concrete_types(typ ast.Type, node ast.StructInit) []ast.Type {
mut concrete_types := []ast.Type{}
sym := c.table.sym(typ)
if sym.info is ast.Struct {
generic_names := sym.info.generic_types.map(c.table.sym(it).name)
gname: for gt_name in generic_names {
for ft in sym.info.fields {
field_sym := c.table.sym(ft.typ)
if field_sym.name == gt_name {
for t in node.fields {
if ft.name == t.name && t.typ != 0 {
concrete_types << t.typ
continue gname
}
}
}
if field_sym.kind == .array {
for t in node.fields {
if ft.name == t.name {
init_sym := c.table.sym(t.typ)
if init_sym.kind == .array {
mut init_elem_info := init_sym.info as ast.Array
mut field_elem_info := field_sym.info as ast.Array
mut init_elem_sym := c.table.sym(init_elem_info.elem_type)
mut field_elem_sym := c.table.sym(field_elem_info.elem_type)
for {
if init_elem_sym.kind == .array && field_elem_sym.kind == .array {
init_elem_info = init_elem_sym.info as ast.Array
init_elem_sym = c.table.sym(init_elem_info.elem_type)
field_elem_info = field_elem_sym.info as ast.Array
field_elem_sym = c.table.sym(field_elem_info.elem_type)
} else {
if field_elem_sym.name == gt_name {
mut elem_typ := init_elem_info.elem_type
if field_elem_info.elem_type.nr_muls() > 0
&& elem_typ.nr_muls() > 0 {
elem_typ = elem_typ.set_nr_muls(0)
}
concrete_types << elem_typ
continue gname
}
break
}
}
}
}
}
} else if field_sym.kind == .array_fixed {
for t in node.fields {
if ft.name == t.name {
init_sym := c.table.sym(t.typ)
if init_sym.kind == .array_fixed {
mut init_elem_info := init_sym.info as ast.ArrayFixed
mut field_elem_info := field_sym.info as ast.ArrayFixed
mut init_elem_sym := c.table.sym(init_elem_info.elem_type)
mut field_elem_sym := c.table.sym(field_elem_info.elem_type)
for {
if init_elem_sym.kind == .array_fixed
&& field_elem_sym.kind == .array_fixed {
init_elem_info = init_elem_sym.info as ast.ArrayFixed
init_elem_sym = c.table.sym(init_elem_info.elem_type)
field_elem_info = field_elem_sym.info as ast.ArrayFixed
field_elem_sym = c.table.sym(field_elem_info.elem_type)
} else {
if field_elem_sym.name == gt_name {
mut elem_typ := init_elem_info.elem_type
if field_elem_info.elem_type.nr_muls() > 0
&& elem_typ.nr_muls() > 0 {
elem_typ = elem_typ.set_nr_muls(0)
}
concrete_types << elem_typ
continue gname
}
break
}
}
}
}
}
} else if field_sym.kind == .map {
for t in node.fields {
if ft.name == t.name {
init_sym := c.table.sym(t.typ)
if init_sym.kind == .map {
init_map_info := init_sym.info as ast.Map
field_map_info := field_sym.info as ast.Map
if field_map_info.key_type.has_flag(.generic)
&& c.table.sym(field_map_info.key_type).name == gt_name {
mut key_typ := init_map_info.key_type
if field_map_info.key_type.nr_muls() > 0
&& key_typ.nr_muls() > 0 {
key_typ = key_typ.set_nr_muls(0)
}
concrete_types << key_typ
continue gname
}
if field_map_info.value_type.has_flag(.generic)
&& c.table.sym(field_map_info.value_type).name == gt_name {
mut val_typ := init_map_info.value_type
if field_map_info.value_type.nr_muls() > 0
&& val_typ.nr_muls() > 0 {
val_typ = val_typ.set_nr_muls(0)
}
concrete_types << val_typ
continue gname
}
}
}
}
}
}
}
}
return concrete_types
}
pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr) {
mut inferred_types := []ast.Type{}
for gi, gt_name in func.generic_names {

View File

@ -1002,7 +1002,10 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
continue
}
arg_typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr))
mut arg_typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr))
if call_arg.expr is ast.StructInit {
arg_typ = c.expr(call_arg.expr)
}
node.args[i].typ = arg_typ
if c.inside_comptime_for_field {
if mut call_arg.expr is ast.Ident {

View File

@ -254,26 +254,35 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
c.error('unknown type `${ct_sym.name}`', node.pos)
}
}
if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0
&& !node.is_short_syntax {
if c.table.cur_concrete_types.len == 0 {
c.error('generic struct init must specify type parameter, e.g. Foo<int>',
node.pos)
} else if node.generic_types.len == 0 {
c.error('generic struct init must specify type parameter, e.g. Foo<T>',
node.pos)
} else if node.generic_types.len > 0
&& node.generic_types.len != struct_sym.info.generic_types.len {
c.error('generic struct init expects ${struct_sym.info.generic_types.len} generic parameter, but got ${node.generic_types.len}',
node.pos)
} else if node.generic_types.len > 0 && c.table.cur_fn != unsafe { nil } {
for gtyp in node.generic_types {
gtyp_name := c.table.sym(gtyp).name
if gtyp_name !in c.table.cur_fn.generic_names {
cur_generic_names := '(' + c.table.cur_fn.generic_names.join(',') + ')'
c.error('generic struct init type parameter `${gtyp_name}` must be within the parameters `${cur_generic_names}` of the current generic function',
node.pos)
break
if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0 {
if node.is_short_syntax {
concrete_types := c.infer_generic_struct_init_concrete_types(node.typ,
node)
if concrete_types.len > 0 {
generic_names := struct_sym.info.generic_types.map(c.table.sym(it).name)
node.typ = c.table.unwrap_generic_type(node.typ, generic_names, concrete_types)
return node.typ
}
} else {
if c.table.cur_concrete_types.len == 0 {
c.error('generic struct init must specify type parameter, e.g. Foo<int>',
node.pos)
} else if node.generic_types.len == 0 {
c.error('generic struct init must specify type parameter, e.g. Foo<T>',
node.pos)
} else if node.generic_types.len > 0
&& node.generic_types.len != struct_sym.info.generic_types.len {
c.error('generic struct init expects ${struct_sym.info.generic_types.len} generic parameter, but got ${node.generic_types.len}',
node.pos)
} else if node.generic_types.len > 0 && c.table.cur_fn != unsafe { nil } {
for gtyp in node.generic_types {
gtyp_name := c.table.sym(gtyp).name
if gtyp_name !in c.table.cur_fn.generic_names {
cur_generic_names := '(' + c.table.cur_fn.generic_names.join(',') + ')'
c.error('generic struct init type parameter `${gtyp_name}` must be within the parameters `${cur_generic_names}` of the current generic function',
node.pos)
break
}
}
}
}
@ -447,7 +456,8 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
c.mark_as_referenced(mut &field.expr, true)
}
}
} else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
} else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder
&& !field_info.typ.has_flag(.generic) {
c.check_expected(c.unwrap_generic(expr_type), c.unwrap_generic(field_info.typ)) or {
c.error('cannot assign to field `${field_info.name}`: ${err.msg()}',
field.pos)

View File

@ -5,13 +5,6 @@ vlib/v/checker/tests/generics_struct_init_type_parameter_err.vv:6:9: error: gene
| ~~~~~~~~~~~~
7 | result: res
8 | }
vlib/v/checker/tests/generics_struct_init_type_parameter_err.vv:7:3: error: cannot assign to field `result`: expected `U`, not `int`
5 | fn send_1<A, B>(res A, b B) string {
6 | msg := Response<U>{
7 | result: res
| ~~~~~~~~~~~
8 | }
9 | println(b)
vlib/v/checker/tests/generics_struct_init_type_parameter_err.vv:14:9: error: generic struct init expects 1 generic parameter, but got 2
12 |
13 | fn send_2<A, B>(res A, b B) string {
@ -19,10 +12,3 @@ vlib/v/checker/tests/generics_struct_init_type_parameter_err.vv:14:9: error: gen
| ~~~~~~~~~~~~~~~
15 | result: res
16 | }
vlib/v/checker/tests/generics_struct_init_type_parameter_err.vv:15:3: error: cannot assign to field `result`: expected `U`, not `int`
13 | fn send_2<A, B>(res A, b B) string {
14 | msg := Response<A, B>{
15 | result: res
| ~~~~~~~~~~~
16 | }
17 | println(b)

View File

@ -0,0 +1,18 @@
pub struct EncodeOptions<T> {
payload T
key string
algorithm string = 'HS256'
}
pub fn encode<T>(options EncodeOptions<T>) !string {
return 'test'
}
fn test_generic_fn_with_generic_struct_init_syntax() {
payload := {
'test': 'test'
}
ret := encode(payload: payload, key: 'test')!
println(ret)
assert ret == 'test'
}