cgen: fix interface type generation for generic struct (fix #22186) (#22189)

This commit is contained in:
Felipe Pena 2024-09-14 17:14:59 -03:00 committed by GitHub
parent 4eb087a347
commit 5fcfc27e50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 129 additions and 6 deletions

View file

@ -626,6 +626,8 @@ fn (t Tree) struct_decl(node ast.StructDecl) &Node {
obj.add_terse('attrs', t.array_node_attr(node.attrs)) obj.add_terse('attrs', t.array_node_attr(node.attrs))
obj.add('end_comments', t.array_node_comment(node.end_comments)) obj.add('end_comments', t.array_node_comment(node.end_comments))
obj.add_terse('embeds', t.array_node_embed(node.embeds)) obj.add_terse('embeds', t.array_node_embed(node.embeds))
obj.add('is_implements', t.bool_node(node.is_implements))
obj.add_terse('implements_types', t.array_node_type_expr(node.implements_types))
return obj return obj
} }

View file

@ -418,7 +418,7 @@ pub:
embeds []Embed embeds []Embed
is_implements bool is_implements bool
implements_types []Type implements_types []TypeNode
pub mut: pub mut:
fields []StructField fields []StructField
} }

View file

@ -952,6 +952,11 @@ pub fn (t &Struct) is_empty_struct() bool {
return t.fields.len == 0 && t.embeds.len == 0 return t.fields.len == 0 && t.embeds.len == 0
} }
@[inline]
pub fn (t &Struct) is_unresolved_generic() bool {
return t.generic_types.len > 0 && t.concrete_types.len == 0
}
pub fn (t &TypeSymbol) is_array_fixed() bool { pub fn (t &TypeSymbol) is_array_fixed() bool {
if t.info is ArrayFixed { if t.info is ArrayFixed {
return true return true

View file

@ -334,7 +334,24 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
// cgen error if I use `println(sym)` without handling the option with `or{}` // cgen error if I use `println(sym)` without handling the option with `or{}`
struct_type := c.table.find_type_idx(node.name) // or { panic(err) } struct_type := c.table.find_type_idx(node.name) // or { panic(err) }
for t in node.implements_types { for t in node.implements_types {
c.type_implements(struct_type, t, node.pos) t_sym := c.table.sym(t.typ)
if t_sym.info is ast.Interface {
if t_sym.info.is_generic {
itype_name := c.table.type_to_str(t.typ)
if !itype_name.contains('[') {
c.error('missing generic type on ${t_sym.name}', t.pos)
}
if itype_name.contains('<') {
struct_generic_letters := node.generic_types.map(c.table.type_to_str(it))
unknown_letters := itype_name.all_after('<').all_before('>').split(',').filter(it !in struct_generic_letters)
if unknown_letters.len > 0 {
c.error('unknown generic type ${unknown_letters.first()}',
t.pos)
}
}
}
}
c.type_implements(struct_type, t.typ, node.pos)
} }
} }
} }

View file

@ -0,0 +1,21 @@
vlib/v/checker/tests/implements_generic_err.vv:12:31: error: missing generic type on IB
10 | }
11 |
12 | struct Foo1[T] implements IA, IB {
| ~~
13 | a int
14 | b T
vlib/v/checker/tests/implements_generic_err.vv:17:31: error: unknown generic type Y
15 | }
16 |
17 | struct Foo2[T] implements IA, IB[Y] {
| ~~
18 | a int
19 | b T
vlib/v/checker/tests/implements_generic_err.vv:22:31: error: unknown generic type Y
20 | }
21 |
22 | struct Foo3[T] implements IA, IB[T,Y] {
| ~~
23 | a int
24 | b T

View file

@ -0,0 +1,37 @@
interface IA {
a int
fa()
}
interface IB[T] {
a int
b T
fa()
}
struct Foo1[T] implements IA, IB {
a int
b T
}
struct Foo2[T] implements IA, IB[Y] {
a int
b T
}
struct Foo3[T] implements IA, IB[T,Y] {
a int
b T
}
fn (foo Foo1[T]) fa() {
println(foo.b)
}
fn (foo Foo2[T]) fa() {
println(foo.b)
}
fn (foo Foo3[T]) fa() {
println(foo.b)
}

View file

@ -34,11 +34,11 @@ pub fn (mut f Fmt) struct_decl(node ast.StructDecl, is_anon bool) {
if node.is_implements { if node.is_implements {
f.write(' implements ') f.write(' implements ')
for i, t in node.implements_types { for i, t in node.implements_types {
f.write(f.table.type_to_str_using_aliases(t, f.mod2alias)) f.write(f.table.type_to_str_using_aliases(t.typ, f.mod2alias))
if i < node.implements_types.len - 1 { if i < node.implements_types.len - 1 {
f.write(', ') f.write(', ')
} }
f.mark_types_import_as_used(t) f.mark_types_import_as_used(t.typ)
} }
} }
// Calculate the alignments first // Calculate the alignments first

View file

@ -1055,6 +1055,9 @@ pub fn (mut g Gen) write_typeof_functions() {
g.writeln('static char * v_typeof_interface_${sym.cname}(int sidx) { /* ${sym.name} */ ') g.writeln('static char * v_typeof_interface_${sym.cname}(int sidx) { /* ${sym.name} */ ')
for t in inter_info.types { for t in inter_info.types {
sub_sym := g.table.sym(ast.mktyp(t)) sub_sym := g.table.sym(ast.mktyp(t))
if sub_sym.info is ast.Struct && sub_sym.info.is_unresolved_generic() {
continue
}
g.writeln('\tif (sidx == _${sym.cname}_${sub_sym.cname}_index) return "${util.strip_main_name(sub_sym.name)}";') g.writeln('\tif (sidx == _${sym.cname}_${sub_sym.cname}_index) return "${util.strip_main_name(sub_sym.name)}";')
} }
g.writeln('\treturn "unknown ${util.strip_main_name(sym.name)}";') g.writeln('\treturn "unknown ${util.strip_main_name(sym.name)}";')
@ -1063,6 +1066,9 @@ pub fn (mut g Gen) write_typeof_functions() {
g.writeln('static int v_typeof_interface_idx_${sym.cname}(int sidx) { /* ${sym.name} */ ') g.writeln('static int v_typeof_interface_idx_${sym.cname}(int sidx) { /* ${sym.name} */ ')
for t in inter_info.types { for t in inter_info.types {
sub_sym := g.table.sym(ast.mktyp(t)) sub_sym := g.table.sym(ast.mktyp(t))
if sub_sym.info is ast.Struct && sub_sym.info.is_unresolved_generic() {
continue
}
g.writeln('\tif (sidx == _${sym.cname}_${sub_sym.cname}_index) return ${int(t.set_nr_muls(0))};') g.writeln('\tif (sidx == _${sym.cname}_${sub_sym.cname}_index) return ${int(t.set_nr_muls(0))};')
} }
g.writeln('\treturn ${int(ityp)};') g.writeln('\treturn ${int(ityp)};')
@ -7688,6 +7694,10 @@ fn (mut g Gen) interface_table() string {
iinidx_minimum_base := 1000 // Note: NOT 0, to avoid map entries set to 0 later, so `if already_generated_mwrappers[name] > 0 {` works. iinidx_minimum_base := 1000 // Note: NOT 0, to avoid map entries set to 0 later, so `if already_generated_mwrappers[name] > 0 {` works.
mut current_iinidx := iinidx_minimum_base mut current_iinidx := iinidx_minimum_base
for st in inter_info.types { for st in inter_info.types {
st_sym_info := g.table.sym(st)
if st_sym_info.info is ast.Struct && st_sym_info.info.is_unresolved_generic() {
continue
}
st_sym := g.table.sym(ast.mktyp(st)) st_sym := g.table.sym(ast.mktyp(st))
// cctype is the Cleaned Concrete Type name, *without ptr*, // cctype is the Cleaned Concrete Type name, *without ptr*,
// i.e. cctype is always just Cat, not Cat_ptr: // i.e. cctype is always just Cat, not Cat_ptr:

View file

@ -94,7 +94,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
mut is_field_pub := false mut is_field_pub := false
mut is_field_global := false mut is_field_global := false
mut is_implements := false mut is_implements := false
mut implements_types := []ast.Type{cap: 3} // ast.void_type mut implements_types := []ast.TypeNode{cap: 3} // ast.void_type
mut last_line := p.prev_tok.pos().line_nr + 1 mut last_line := p.prev_tok.pos().line_nr + 1
mut end_comments := []ast.Comment{} mut end_comments := []ast.Comment{}
if !no_body { if !no_body {
@ -102,7 +102,11 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
is_implements = true is_implements = true
for { for {
p.next() p.next()
implements_types << p.parse_type() type_pos := p.tok.pos()
implements_types << ast.TypeNode{
typ: p.parse_type()
pos: type_pos
}
if p.tok.kind != .comma { if p.tok.kind != .comma {
break break
} }

View file

@ -0,0 +1,27 @@
interface IA {
a int
fa()
}
interface IB[T] {
a int
b T
fa()
}
struct Foo[T] implements IA, IB[T] {
a int
b T
}
fn (foo Foo[T]) fa() {
println(foo.b)
}
fn test_main() {
foo := Foo[u8]{
b: 16
}
foo.fa()
assert true
}