checker: fix generics with generic struct receiver (#9658)

pull/9661/head
yuyi 2021-04-10 10:00:53 +08:00 committed by GitHub
parent 5273214ec2
commit bf6a2f80ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 22 deletions

View File

@ -1108,7 +1108,9 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() {
fields[i] = field
}
parent_info.generic_types = []
parent_info.concrete_types = info.generic_types.clone()
parent_info.fields = fields
parent_info.parent_type = new_type(info.parent_idx).set_flag(.generic)
typ.is_public = true
typ.kind = .struct_
typ.info = parent_info

View File

@ -715,12 +715,14 @@ pub struct Struct {
pub:
attrs []Attr
pub mut:
embeds []Type
fields []StructField
is_typedef bool // C. [typedef]
is_union bool
is_heap bool
generic_types []Type
embeds []Type
fields []StructField
is_typedef bool // C. [typedef]
is_union bool
is_heap bool
generic_types []Type
concrete_types []Type
parent_type Type
}
// instantiation of a generic struct

View File

@ -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
for i, param in f.params {
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 }
if call_expr.args.len <= arg_i {
break

View File

@ -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 {
left_type := c.expr(call_expr.left)
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
// Set default values for .return_type & .receiver_type too,
// 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 {
// 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 {
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 left_type_sym.info.parent_type != 0 {
type_sym := c.table.get_type_symbol(left_type_sym.info.parent_type)
if m := c.table.type_find_method(type_sym, method_name) {
method = m
has_method = true
is_generic = true
}
} 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 {

View File

@ -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()
}