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:
parent
00383edd3d
commit
cc7e6006f9
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
}
|
Loading…
Reference in New Issue
Block a user