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 (#16828)

This commit is contained in:
yuyi 2023-01-01 15:14:55 +08:00 committed by GitHub
parent aaf3e25c3f
commit 51bb630ea4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 56 additions and 31 deletions

View File

@ -256,37 +256,29 @@ 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 {
if node.is_short_syntax {
concrete_types := c.infer_struct_generic_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 {
if !gtyp.has_flag(.generic) {
continue
}
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
&& !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 {
if !gtyp.has_flag(.generic) {
continue
}
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
}
}
}
@ -615,6 +607,17 @@ fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
}
}
}
if struct_sym.info is ast.Struct {
if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0 {
if node.is_short_syntax {
concrete_types := c.infer_struct_generic_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
}

View File

@ -0,0 +1,22 @@
struct Params[T] {
a []T
b int
}
fn take_input[T](p Params[T]) []f32 {
mut res := []f32{}
for x in p.a {
res << f32(x)
}
res << f32(p.b)
return res
}
fn test_generic_fn_with_generic_struct_init_syntax() {
a_in := [int(1), 2, 4]
res := take_input(a: a_in, b: 3)
// res := take_input(Params[int]{a: a_in, b: 3}) // this works
println(res)
assert res == [f32(1.0), 2.0, 4.0, 3.0]
}