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

ast, checker: fix generic struct init with inconsistent generic types (#16675)

This commit is contained in:
yuyi 2022-12-14 20:57:12 +08:00 committed by GitHub
parent 7f23ae595b
commit 8ab4c7742c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 52 deletions

View File

@ -1703,9 +1703,13 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
Struct, Interface, SumType {
if sym.info.is_generic {
mut nrt := '${sym.name}['
mut t_generic_names := generic_names.clone()
if generic_names.len == sym.info.generic_types.len {
t_generic_names = sym.info.generic_types.map(t.sym(it).name)
}
for i in 0 .. sym.info.generic_types.len {
if ct := t.resolve_generic_to_concrete(sym.info.generic_types[i],
generic_names, concrete_types)
t_generic_names, concrete_types)
{
gts := t.sym(ct)
nrt += gts.name
@ -1834,10 +1838,14 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
if !ts.info.is_generic {
return typ
}
mut t_generic_names := generic_names.clone()
if generic_names.len == ts.info.generic_types.len {
t_generic_names = ts.info.generic_types.map(t.sym(it).name)
}
nrt = '${ts.name}['
c_nrt = '${ts.cname}_T_'
for i in 0 .. ts.info.generic_types.len {
if ct := t.resolve_generic_to_concrete(ts.info.generic_types[i], generic_names,
if ct := t.resolve_generic_to_concrete(ts.info.generic_types[i], t_generic_names,
concrete_types)
{
gts := t.sym(ct)
@ -1860,10 +1868,10 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
if fields[i].typ.has_flag(.generic) {
sym := t.sym(fields[i].typ)
if sym.kind == .struct_ && fields[i].typ.idx() != typ.idx() {
fields[i].typ = t.unwrap_generic_type(fields[i].typ, generic_names,
fields[i].typ = t.unwrap_generic_type(fields[i].typ, t_generic_names,
concrete_types)
} else {
if t_typ := t.resolve_generic_to_concrete(fields[i].typ, generic_names,
if t_typ := t.resolve_generic_to_concrete(fields[i].typ, t_generic_names,
concrete_types)
{
fields[i].typ = t_typ
@ -1874,7 +1882,7 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
// update concrete types
for i in 0 .. ts.info.generic_types.len {
if t_typ := t.resolve_generic_to_concrete(ts.info.generic_types[i],
generic_names, concrete_types)
t_generic_names, concrete_types)
{
final_concrete_types << t_typ
}
@ -2005,48 +2013,6 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
return typ
}
// Foo[U]{ bar: U } to Foo[T]{ bar: T }
pub fn (mut t Table) replace_generic_type(typ Type, generic_types []Type) {
mut ts := t.sym(typ)
match mut ts.info {
Array {
mut elem_type := ts.info.elem_type
mut elem_sym := t.sym(elem_type)
mut dims := 1
for mut elem_sym.info is Array {
elem_type = elem_sym.info.elem_type
elem_sym = t.sym(elem_type)
dims++
}
t.replace_generic_type(elem_type, generic_types)
}
ArrayFixed {
t.replace_generic_type(ts.info.elem_type, generic_types)
}
Chan {
t.replace_generic_type(ts.info.elem_type, generic_types)
}
Map {
t.replace_generic_type(ts.info.key_type, generic_types)
t.replace_generic_type(ts.info.value_type, generic_types)
}
Struct, Interface, SumType {
generic_names := ts.info.generic_types.map(t.sym(it).name)
for i in 0 .. ts.info.fields.len {
if ts.info.fields[i].typ.has_flag(.generic) {
if t_typ := t.resolve_generic_to_concrete(ts.info.fields[i].typ, generic_names,
generic_types)
{
ts.info.fields[i].typ = t_typ
}
}
}
ts.info.generic_types = generic_types
}
else {}
}
}
// generic struct instantiations to concrete types
pub fn (mut t Table) generic_insts_to_concrete() {
for mut sym in t.type_symbols {

View File

@ -131,7 +131,10 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
c.error('fixed array cannot be returned by function', node.return_type_pos)
}
// Ensure each generic type of the parameter was declared in the function's definition
if node.return_type.has_flag(.generic) {
// TODO: fix inconsistent return_type type case
if node.return_type.has_flag(.generic) && (return_sym.kind == .any
|| (return_sym.kind == .array
&& c.table.sym((return_sym.info as ast.Array).elem_type).kind == .any)) {
generic_names := c.table.generic_type_names(node.return_type)
for name in generic_names {
if name !in node.generic_names {

View File

@ -286,10 +286,6 @@ fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
c.error('a non generic struct `${node.typ_str}` used like a generic struct',
node.name_pos)
}
if node.generic_types.len > 0 && struct_sym.info.generic_types.len == node.generic_types.len
&& struct_sym.info.generic_types != node.generic_types {
c.table.replace_generic_type(node.typ, node.generic_types)
}
} else if struct_sym.info is ast.Alias {
parent_sym := c.table.sym(struct_sym.info.parent_type)
// e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´

View File

@ -0,0 +1,28 @@
struct Tuple[A, B] {
a A
b B
}
// map to array of key-value tuples
fn map_to_array[K, V](m map[K]V) []Tuple[K, V] {
mut r := []Tuple[K, V]{cap: m.len}
for k, v in m {
r << Tuple[K, V]{k, v}
}
return r
}
fn test_generics_struct_init_with_inconsistent_generic_types() {
x := {
'one': 1
'two': 2
}
println(x)
arr := map_to_array(x)
println(arr)
assert arr.len == 2
assert arr[0].a == 'one'
assert arr[0].b == 1
assert arr[1].a == 'two'
assert arr[1].b == 2
}