Skip to content

Commit bf6a2f8

Browse files
authored
checker: fix generics with generic struct receiver (#9658)
1 parent 5273214 commit bf6a2f8

File tree

5 files changed

+59
-23
lines changed

5 files changed

+59
-23
lines changed

vlib/v/ast/table.v

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,9 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() {
11081108
fields[i] = field
11091109
}
11101110
parent_info.generic_types = []
1111+
parent_info.concrete_types = info.generic_types.clone()
11111112
parent_info.fields = fields
1113+
parent_info.parent_type = new_type(info.parent_idx).set_flag(.generic)
11121114
typ.is_public = true
11131115
typ.kind = .struct_
11141116
typ.info = parent_info

vlib/v/ast/types.v

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -715,12 +715,14 @@ pub struct Struct {
715715
pub:
716716
attrs []Attr
717717
pub mut:
718-
embeds []Type
719-
fields []StructField
720-
is_typedef bool // C. [typedef]
721-
is_union bool
722-
is_heap bool
723-
generic_types []Type
718+
embeds []Type
719+
fields []StructField
720+
is_typedef bool // C. [typedef]
721+
is_union bool
722+
is_heap bool
723+
generic_types []Type
724+
concrete_types []Type
725+
parent_type Type
724726
}
725727

726728
// instantiation of a generic struct

vlib/v/checker/check_types.v

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,16 @@ pub fn (mut c Checker) infer_fn_types(f ast.Fn, mut call_expr ast.CallExpr) {
486486
mut typ := ast.void_type
487487
for i, param in f.params {
488488
mut to_set := ast.void_type
489+
// resolve generic struct receiver (TODO: multi generic struct)
490+
if i == 0 && call_expr.is_method && param.typ.has_flag(.generic) {
491+
sym := c.table.get_type_symbol(call_expr.receiver_type)
492+
if sym.kind == .struct_ {
493+
info := sym.info as ast.Struct
494+
if info.concrete_types.len > 0 {
495+
typ = info.concrete_types[0]
496+
}
497+
}
498+
}
489499
arg_i := if i != 0 && call_expr.is_method { i - 1 } else { i }
490500
if call_expr.args.len <= arg_i {
491501
break

vlib/v/checker/checker.v

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,7 +1464,7 @@ fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_e
14641464
pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
14651465
left_type := c.expr(call_expr.left)
14661466
c.expected_type = left_type
1467-
is_generic := left_type.has_flag(.generic)
1467+
mut is_generic := left_type.has_flag(.generic)
14681468
call_expr.left_type = left_type
14691469
// Set default values for .return_type & .receiver_type too,
14701470
// or there will be hard to diagnose 0 type panics in cgen.
@@ -1533,22 +1533,31 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
15331533
} else {
15341534
// can this logic be moved to ast.type_find_method() so it can be used from anywhere
15351535
if left_type_sym.info is ast.Struct {
1536-
mut found_methods := []ast.Fn{}
1537-
mut embed_of_found_methods := []ast.Type{}
1538-
for embed in left_type_sym.info.embeds {
1539-
embed_sym := c.table.get_type_symbol(embed)
1540-
if m := c.table.type_find_method(embed_sym, method_name) {
1541-
found_methods << m
1542-
embed_of_found_methods << embed
1543-
}
1544-
}
1545-
if found_methods.len == 1 {
1546-
method = found_methods[0]
1547-
has_method = true
1548-
is_method_from_embed = true
1549-
call_expr.from_embed_type = embed_of_found_methods[0]
1550-
} else if found_methods.len > 1 {
1551-
c.error('ambiguous method `$method_name`', call_expr.pos)
1536+
if left_type_sym.info.parent_type != 0 {
1537+
type_sym := c.table.get_type_symbol(left_type_sym.info.parent_type)
1538+
if m := c.table.type_find_method(type_sym, method_name) {
1539+
method = m
1540+
has_method = true
1541+
is_generic = true
1542+
}
1543+
} else {
1544+
mut found_methods := []ast.Fn{}
1545+
mut embed_of_found_methods := []ast.Type{}
1546+
for embed in left_type_sym.info.embeds {
1547+
embed_sym := c.table.get_type_symbol(embed)
1548+
if m := c.table.type_find_method(embed_sym, method_name) {
1549+
found_methods << m
1550+
embed_of_found_methods << embed
1551+
}
1552+
}
1553+
if found_methods.len == 1 {
1554+
method = found_methods[0]
1555+
has_method = true
1556+
is_method_from_embed = true
1557+
call_expr.from_embed_type = embed_of_found_methods[0]
1558+
} else if found_methods.len > 1 {
1559+
c.error('ambiguous method `$method_name`', call_expr.pos)
1560+
}
15521561
}
15531562
}
15541563
if left_type_sym.kind == .aggregate {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
struct Num<T> {
2+
num T
3+
}
4+
5+
fn (num Num<T>) is_autom<T>() bool {
6+
return true
7+
}
8+
9+
fn test_generics_with_generic_struct_receiver() {
10+
num := Num<int>{3}
11+
println(num.is_autom())
12+
assert num.is_autom()
13+
}

0 commit comments

Comments
 (0)