checker: fix generics with generic struct receiver (#9658)
parent
5273214ec2
commit
bf6a2f80ef
|
@ -1108,7 +1108,9 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() {
|
||||||
fields[i] = field
|
fields[i] = field
|
||||||
}
|
}
|
||||||
parent_info.generic_types = []
|
parent_info.generic_types = []
|
||||||
|
parent_info.concrete_types = info.generic_types.clone()
|
||||||
parent_info.fields = fields
|
parent_info.fields = fields
|
||||||
|
parent_info.parent_type = new_type(info.parent_idx).set_flag(.generic)
|
||||||
typ.is_public = true
|
typ.is_public = true
|
||||||
typ.kind = .struct_
|
typ.kind = .struct_
|
||||||
typ.info = parent_info
|
typ.info = parent_info
|
||||||
|
|
|
@ -715,12 +715,14 @@ pub struct Struct {
|
||||||
pub:
|
pub:
|
||||||
attrs []Attr
|
attrs []Attr
|
||||||
pub mut:
|
pub mut:
|
||||||
embeds []Type
|
embeds []Type
|
||||||
fields []StructField
|
fields []StructField
|
||||||
is_typedef bool // C. [typedef]
|
is_typedef bool // C. [typedef]
|
||||||
is_union bool
|
is_union bool
|
||||||
is_heap bool
|
is_heap bool
|
||||||
generic_types []Type
|
generic_types []Type
|
||||||
|
concrete_types []Type
|
||||||
|
parent_type Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// instantiation of a generic struct
|
// instantiation of a generic struct
|
||||||
|
|
|
@ -486,6 +486,16 @@ pub fn (mut c Checker) infer_fn_types(f ast.Fn, mut call_expr ast.CallExpr) {
|
||||||
mut typ := ast.void_type
|
mut typ := ast.void_type
|
||||||
for i, param in f.params {
|
for i, param in f.params {
|
||||||
mut to_set := ast.void_type
|
mut to_set := ast.void_type
|
||||||
|
// resolve generic struct receiver (TODO: multi generic struct)
|
||||||
|
if i == 0 && call_expr.is_method && param.typ.has_flag(.generic) {
|
||||||
|
sym := c.table.get_type_symbol(call_expr.receiver_type)
|
||||||
|
if sym.kind == .struct_ {
|
||||||
|
info := sym.info as ast.Struct
|
||||||
|
if info.concrete_types.len > 0 {
|
||||||
|
typ = info.concrete_types[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
arg_i := if i != 0 && call_expr.is_method { i - 1 } else { i }
|
arg_i := if i != 0 && call_expr.is_method { i - 1 } else { i }
|
||||||
if call_expr.args.len <= arg_i {
|
if call_expr.args.len <= arg_i {
|
||||||
break
|
break
|
||||||
|
|
|
@ -1464,7 +1464,7 @@ fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_e
|
||||||
pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
|
pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
|
||||||
left_type := c.expr(call_expr.left)
|
left_type := c.expr(call_expr.left)
|
||||||
c.expected_type = left_type
|
c.expected_type = left_type
|
||||||
is_generic := left_type.has_flag(.generic)
|
mut is_generic := left_type.has_flag(.generic)
|
||||||
call_expr.left_type = left_type
|
call_expr.left_type = left_type
|
||||||
// Set default values for .return_type & .receiver_type too,
|
// Set default values for .return_type & .receiver_type too,
|
||||||
// or there will be hard to diagnose 0 type panics in cgen.
|
// 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 {
|
||||||
} else {
|
} else {
|
||||||
// can this logic be moved to ast.type_find_method() so it can be used from anywhere
|
// can this logic be moved to ast.type_find_method() so it can be used from anywhere
|
||||||
if left_type_sym.info is ast.Struct {
|
if left_type_sym.info is ast.Struct {
|
||||||
mut found_methods := []ast.Fn{}
|
if left_type_sym.info.parent_type != 0 {
|
||||||
mut embed_of_found_methods := []ast.Type{}
|
type_sym := c.table.get_type_symbol(left_type_sym.info.parent_type)
|
||||||
for embed in left_type_sym.info.embeds {
|
if m := c.table.type_find_method(type_sym, method_name) {
|
||||||
embed_sym := c.table.get_type_symbol(embed)
|
method = m
|
||||||
if m := c.table.type_find_method(embed_sym, method_name) {
|
has_method = true
|
||||||
found_methods << m
|
is_generic = true
|
||||||
embed_of_found_methods << embed
|
}
|
||||||
|
} else {
|
||||||
|
mut found_methods := []ast.Fn{}
|
||||||
|
mut embed_of_found_methods := []ast.Type{}
|
||||||
|
for embed in left_type_sym.info.embeds {
|
||||||
|
embed_sym := c.table.get_type_symbol(embed)
|
||||||
|
if m := c.table.type_find_method(embed_sym, method_name) {
|
||||||
|
found_methods << m
|
||||||
|
embed_of_found_methods << embed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found_methods.len == 1 {
|
||||||
|
method = found_methods[0]
|
||||||
|
has_method = true
|
||||||
|
is_method_from_embed = true
|
||||||
|
call_expr.from_embed_type = embed_of_found_methods[0]
|
||||||
|
} else if found_methods.len > 1 {
|
||||||
|
c.error('ambiguous method `$method_name`', call_expr.pos)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if found_methods.len == 1 {
|
|
||||||
method = found_methods[0]
|
|
||||||
has_method = true
|
|
||||||
is_method_from_embed = true
|
|
||||||
call_expr.from_embed_type = embed_of_found_methods[0]
|
|
||||||
} else if found_methods.len > 1 {
|
|
||||||
c.error('ambiguous method `$method_name`', call_expr.pos)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if left_type_sym.kind == .aggregate {
|
if left_type_sym.kind == .aggregate {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
struct Num<T> {
|
||||||
|
num T
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (num Num<T>) is_autom<T>() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generics_with_generic_struct_receiver() {
|
||||||
|
num := Num<int>{3}
|
||||||
|
println(num.is_autom())
|
||||||
|
assert num.is_autom()
|
||||||
|
}
|
Loading…
Reference in New Issue