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

cgen: fix generic struct init with inconsistent generic types (fix #16677) (#16702)

This commit is contained in:
yuyi 2022-12-19 16:43:27 +08:00 committed by GitHub
parent 04936b00c5
commit aad95ac818
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 201 additions and 15 deletions

View File

@ -1710,10 +1710,14 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
t_generic_names = sym.info.generic_types.map(t.sym(it).name) t_generic_names = sym.info.generic_types.map(t.sym(it).name)
t_concrete_types = [] t_concrete_types = []
for t_typ in sym.generic_types { for t_typ in sym.generic_types {
tname := t.sym(t_typ).name if !t_typ.has_flag(.generic) {
index := generic_names.index(tname) t_concrete_types << t_typ
if index >= 0 && index < concrete_types.len { } else {
t_concrete_types << concrete_types[index] tname := t.sym(t_typ).name
index := generic_names.index(tname)
if index >= 0 && index < concrete_types.len {
t_concrete_types << concrete_types[index]
}
} }
} }
} }
@ -1801,7 +1805,7 @@ pub fn (mut t Table) generic_type_names(generic_type Type) []string {
if sym.info.is_generic { if sym.info.is_generic {
if sym.generic_types.len > 0 { if sym.generic_types.len > 0 {
// Foo[U] (declaration: Foo[T]) // Foo[U] (declaration: Foo[T])
names << sym.generic_types.map(t.sym(it).name) names << sym.generic_types.filter(it.has_flag(.generic)).map(t.sym(it).name)
} else { } else {
names << sym.info.generic_types.map(t.sym(it).name) names << sym.info.generic_types.map(t.sym(it).name)
} }
@ -1862,10 +1866,14 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
t_generic_names = ts.info.generic_types.map(t.sym(it).name) t_generic_names = ts.info.generic_types.map(t.sym(it).name)
t_concrete_types = [] t_concrete_types = []
for t_typ in ts.generic_types { for t_typ in ts.generic_types {
tname := t.sym(t_typ).name if !t_typ.has_flag(.generic) {
index := generic_names.index(tname) t_concrete_types << t_typ
if index >= 0 && index < concrete_types.len { } else {
t_concrete_types << concrete_types[index] tname := t.sym(t_typ).name
index := generic_names.index(tname)
if index >= 0 && index < concrete_types.len {
t_concrete_types << concrete_types[index]
}
} }
} }
} }

View File

@ -277,6 +277,9 @@ fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
node.pos) node.pos)
} else if node.generic_types.len > 0 && c.table.cur_fn != unsafe { nil } { } else if node.generic_types.len > 0 && c.table.cur_fn != unsafe { nil } {
for gtyp in node.generic_types { for gtyp in node.generic_types {
if !gtyp.has_flag(.generic) {
continue
}
gtyp_name := c.table.sym(gtyp).name gtyp_name := c.table.sym(gtyp).name
if gtyp_name !in c.table.cur_fn.generic_names { if gtyp_name !in c.table.cur_fn.generic_names {
cur_generic_names := '(' + c.table.cur_fn.generic_names.join(',') + ')' cur_generic_names := '(' + c.table.cur_fn.generic_names.join(',') + ')'

View File

@ -109,6 +109,10 @@ fn (mut g Gen) final_gen_str(typ StrType) {
} }
styp := typ.styp styp := typ.styp
str_fn_name := styp_to_str_fn_name(styp) str_fn_name := styp_to_str_fn_name(styp)
if str_fn_name in g.str_fn_names {
return
}
g.str_fn_names << str_fn_name
if typ.typ.has_flag(.optional) { if typ.typ.has_flag(.optional) {
g.gen_str_for_option(typ.typ, styp, str_fn_name) g.gen_str_for_option(typ.typ, styp, str_fn_name)
return return

View File

@ -157,6 +157,7 @@ mut:
defer_vars []string defer_vars []string
str_types []StrType // types that need automatic str() generation str_types []StrType // types that need automatic str() generation
generated_str_fns []StrType // types that already have a str() function generated_str_fns []StrType // types that already have a str() function
str_fn_names []string // remove duplicate function names
threaded_fns shared []string // for generating unique wrapper types and fns for `go xxx()` threaded_fns shared []string // for generating unique wrapper types and fns for `go xxx()`
waiter_fns shared []string // functions that wait for `go xxx()` to finish waiter_fns shared []string // functions that wait for `go xxx()` to finish
needed_equality_fns []ast.Type needed_equality_fns []ast.Type
@ -5363,6 +5364,7 @@ fn (mut g Gen) write_sorted_types() {
} }
fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
mut struct_names := []string{cap: 16}
for sym in symbols { for sym in symbols {
if sym.name.starts_with('C.') { if sym.name.starts_with('C.') {
continue continue
@ -5377,7 +5379,10 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
mut name := sym.cname mut name := sym.cname
match sym.info { match sym.info {
ast.Struct { ast.Struct {
g.struct_decl(sym.info, name, false) if name !in struct_names {
g.struct_decl(sym.info, name, false)
struct_names << name
}
} }
ast.Alias { ast.Alias {
// ast.Alias { TODO // ast.Alias { TODO
@ -5401,9 +5406,10 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
} }
} }
ast.SumType { ast.SumType {
if sym.info.is_generic { if sym.info.is_generic || name in struct_names {
continue continue
} }
struct_names << name
g.typedefs.writeln('typedef struct ${name} ${name};') g.typedefs.writeln('typedef struct ${name} ${name};')
g.type_definitions.writeln('') g.type_definitions.writeln('')
g.type_definitions.writeln('// Union sum type ${name} = ') g.type_definitions.writeln('// Union sum type ${name} = ')

View File

@ -708,19 +708,19 @@ pub fn (mut p Parser) parse_generic_inst_type(name string) ast.Type {
bs_name += '[' bs_name += '['
bs_cname += '_T_' bs_cname += '_T_'
mut concrete_types := []ast.Type{} mut concrete_types := []ast.Type{}
mut is_instance := false mut is_instance := true
for p.tok.kind != .eof { for p.tok.kind != .eof {
mut type_pos := p.tok.pos() mut type_pos := p.tok.pos()
gt := p.parse_type() gt := p.parse_type()
type_pos = type_pos.extend(p.prev_tok.pos()) type_pos = type_pos.extend(p.prev_tok.pos())
if !gt.has_flag(.generic) { if gt.has_flag(.generic) {
is_instance = true is_instance = false
} }
gts := p.table.sym(gt) gts := p.table.sym(gt)
if gts.kind == .multi_return { if gts.kind == .multi_return {
p.error_with_pos('cannot use multi return as generic concrete type', type_pos) p.error_with_pos('cannot use multi return as generic concrete type', type_pos)
} }
if !is_instance && gts.name.len > 1 { if gt.has_flag(.generic) && gts.name.len > 1 {
p.error_with_pos('the parameter type name of a generic struct, must be a single capital letter placeholder name, like T or X, or a non-generic type name like int, string, etc.', p.error_with_pos('the parameter type name of a generic struct, must be a single capital letter placeholder name, like T or X, or a non-generic type name like int, string, etc.',
type_pos) type_pos)
} }

View File

@ -0,0 +1,165 @@
struct Tuple1[A] {
a A
}
struct Tuple2[A, B] {
a A
b B
}
struct Tuple3[A, B, X] { // note: "C" is reserved for C language...
a A
b B
c X
}
// map to array of key tuples
fn map_to_array1_k[K, V](m map[K]V) []Tuple1[K] {
mut r := []Tuple1[K]{cap: m.len}
for k, _ in m {
r << Tuple1[K]{k}
}
return r
}
// map to array of value tuples
fn map_to_array1_v[K, V](m map[K]V) []Tuple1[V] {
mut r := []Tuple1[V]{cap: m.len}
for _, v in m {
r << Tuple1[V]{v}
}
return r
}
// map to array of key-value tuples
fn map_to_array2_k_v[K, V](m map[K]V) []Tuple2[K, V] {
mut r := []Tuple2[K, V]{cap: m.len}
for k, v in m {
r << Tuple2[K, V]{k, v}
}
return r
}
// map to array of value-key tuples
fn map_to_array2_v_k[K, V](m map[K]V) []Tuple2[V, K] {
mut r := []Tuple2[V, K]{cap: m.len}
for k, v in m {
r << Tuple2[V, K]{v, k}
}
return r
}
// map to array of key-int tuples
fn map_to_array2_k_int[K, V](m map[K]V) []Tuple2[K, int] {
mut r := []Tuple2[K, int]{cap: m.len}
mut i := 0
for k, _ in m {
r << Tuple2[K, int]{k, i}
i += 1
}
return r
}
// map to array of value-int-key tuples
fn map_to_array3_v_int_k[K, V](m map[K]V) []Tuple3[V, int, K] {
mut r := []Tuple3[V, int, K]{cap: m.len}
mut i := 0
for k, v in m {
r << Tuple3[V, int, K]{v, i, k}
i += 1
}
return r
}
fn test_generics_struct_init_with_inconsistent_generic_types() {
x := {
'one': 1
'two': 2
}
y := {
3: 'three'
4: 'four'
}
println(x)
rx1 := map_to_array1_k(x)
println(rx1)
assert rx1[0].a == 'one'
assert rx1[1].a == 'two'
rx2 := map_to_array1_v(x)
println(rx2)
assert rx2[0].a == 1
assert rx2[1].a == 2
rx3 := map_to_array2_k_v(x)
println(rx3)
assert rx3[0].a == 'one'
assert rx3[0].b == 1
assert rx3[1].a == 'two'
assert rx3[1].b == 2
rx4 := map_to_array2_v_k(x)
println(rx4)
assert rx4[0].a == 1
assert rx4[0].b == 'one'
assert rx4[1].a == 2
assert rx4[1].b == 'two'
rx5 := map_to_array2_k_int(x)
println(rx5)
assert rx5[0].a == 'one'
assert rx5[0].b == 0
assert rx5[1].a == 'two'
assert rx5[1].b == 1
rx6 := map_to_array3_v_int_k(x)
println(rx6)
assert rx6[0].a == 1
assert rx6[0].b == 0
assert rx6[0].c == 'one'
assert rx6[1].a == 2
assert rx6[1].b == 1
assert rx6[1].c == 'two'
println(y)
ry1 := map_to_array1_k(y)
println(ry1)
assert ry1[0].a == 3
assert ry1[1].a == 4
ry2 := map_to_array1_v(y)
println(ry2)
assert ry2[0].a == 'three'
assert ry2[1].a == 'four'
ry3 := map_to_array2_k_v(y)
println(ry3)
assert ry3[0].a == 3
assert ry3[0].b == 'three'
assert ry3[1].a == 4
assert ry3[1].b == 'four'
ry4 := map_to_array2_v_k(y)
println(ry4)
assert ry4[0].a == 'three'
assert ry4[0].b == 3
assert ry4[1].a == 'four'
assert ry4[1].b == 4
ry5 := map_to_array2_k_int(y)
println(ry5)
assert ry5[0].a == 3
assert ry5[0].b == 0
assert ry5[1].a == 4
assert ry5[1].b == 1
ry6 := map_to_array3_v_int_k(y)
println(ry6)
assert ry6[0].a == 'three'
assert ry6[0].b == 0
assert ry6[0].c == 3
assert ry6[1].a == 'four'
assert ry6[1].b == 1
assert ry6[1].c == 4
}