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_concrete_types = []
for t_typ in sym.generic_types {
tname := t.sym(t_typ).name
index := generic_names.index(tname)
if index >= 0 && index < concrete_types.len {
t_concrete_types << concrete_types[index]
if !t_typ.has_flag(.generic) {
t_concrete_types << t_typ
} else {
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.generic_types.len > 0 {
// 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 {
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_concrete_types = []
for t_typ in ts.generic_types {
tname := t.sym(t_typ).name
index := generic_names.index(tname)
if index >= 0 && index < concrete_types.len {
t_concrete_types << concrete_types[index]
if !t_typ.has_flag(.generic) {
t_concrete_types << t_typ
} else {
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)
} 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(',') + ')'

View File

@ -109,6 +109,10 @@ fn (mut g Gen) final_gen_str(typ StrType) {
}
styp := typ.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) {
g.gen_str_for_option(typ.typ, styp, str_fn_name)
return

View File

@ -157,6 +157,7 @@ mut:
defer_vars []string
str_types []StrType // types that need automatic str() generation
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()`
waiter_fns shared []string // functions that wait for `go xxx()` to finish
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) {
mut struct_names := []string{cap: 16}
for sym in symbols {
if sym.name.starts_with('C.') {
continue
@ -5377,7 +5379,10 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
mut name := sym.cname
match sym.info {
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 { TODO
@ -5401,9 +5406,10 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
}
}
ast.SumType {
if sym.info.is_generic {
if sym.info.is_generic || name in struct_names {
continue
}
struct_names << name
g.typedefs.writeln('typedef struct ${name} ${name};')
g.type_definitions.writeln('')
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_cname += '_T_'
mut concrete_types := []ast.Type{}
mut is_instance := false
mut is_instance := true
for p.tok.kind != .eof {
mut type_pos := p.tok.pos()
gt := p.parse_type()
type_pos = type_pos.extend(p.prev_tok.pos())
if !gt.has_flag(.generic) {
is_instance = true
if gt.has_flag(.generic) {
is_instance = false
}
gts := p.table.sym(gt)
if gts.kind == .multi_return {
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.',
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
}