generics: fix generic fn type mismatch of returning a generic struct (#10553)

pull/10559/head
yuyi 2021-06-24 17:47:36 +08:00 committed by GitHub
parent 8324a766e8
commit 4bfe76123a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 50 deletions

View File

@ -1133,7 +1133,7 @@ fn (mut table Table) does_type_implement_interface(typ Type, inter_typ Type) boo
// resolve_generic_to_concrete resolves generics to real types T => int. // resolve_generic_to_concrete resolves generics to real types T => int.
// Even map[string]map[string]T can be resolved. // Even map[string]map[string]T can be resolved.
// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl. // This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl.
pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_names []string, concrete_types []Type, is_inst bool) ?Type { pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_names []string, concrete_types []Type) ?Type {
mut sym := t.get_type_symbol(generic_type) mut sym := t.get_type_symbol(generic_type)
if sym.name in generic_names { if sym.name in generic_names {
index := generic_names.index(sym.name) index := generic_names.index(sym.name)
@ -1151,24 +1151,18 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
elem_sym = t.get_type_symbol(elem_type) elem_sym = t.get_type_symbol(elem_type)
dims++ dims++
} }
if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types, if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types) {
is_inst)
{
idx := t.find_or_register_array_with_dims(typ, dims) idx := t.find_or_register_array_with_dims(typ, dims)
return new_type(idx).derive(generic_type).clear_flag(.generic) return new_type(idx).derive(generic_type).clear_flag(.generic)
} }
} else if sym.kind == .array_fixed { } else if sym.kind == .array_fixed {
info := sym.info as ArrayFixed info := sym.info as ArrayFixed
if typ := t.resolve_generic_to_concrete(info.elem_type, generic_names, concrete_types, if typ := t.resolve_generic_to_concrete(info.elem_type, generic_names, concrete_types) {
is_inst)
{
idx := t.find_or_register_array_fixed(typ, info.size, None{}) idx := t.find_or_register_array_fixed(typ, info.size, None{})
return new_type(idx).derive(generic_type).clear_flag(.generic) return new_type(idx).derive(generic_type).clear_flag(.generic)
} }
} else if mut sym.info is Chan { } else if mut sym.info is Chan {
if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names, concrete_types, if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names, concrete_types) {
is_inst)
{
idx := t.find_or_register_chan(typ, typ.nr_muls() > 0) idx := t.find_or_register_chan(typ, typ.nr_muls() > 0)
return new_type(idx).derive(generic_type).clear_flag(.generic) return new_type(idx).derive(generic_type).clear_flag(.generic)
} }
@ -1176,9 +1170,7 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
mut types := []Type{} mut types := []Type{}
mut type_changed := false mut type_changed := false
for ret_type in sym.info.types { for ret_type in sym.info.types {
if typ := t.resolve_generic_to_concrete(ret_type, generic_names, concrete_types, if typ := t.resolve_generic_to_concrete(ret_type, generic_names, concrete_types) {
is_inst)
{
types << typ types << typ
type_changed = true type_changed = true
} else { } else {
@ -1193,15 +1185,11 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
mut type_changed := false mut type_changed := false
mut unwrapped_key_type := sym.info.key_type mut unwrapped_key_type := sym.info.key_type
mut unwrapped_value_type := sym.info.value_type mut unwrapped_value_type := sym.info.value_type
if typ := t.resolve_generic_to_concrete(sym.info.key_type, generic_names, concrete_types, if typ := t.resolve_generic_to_concrete(sym.info.key_type, generic_names, concrete_types) {
is_inst)
{
unwrapped_key_type = typ unwrapped_key_type = typ
type_changed = true type_changed = true
} }
if typ := t.resolve_generic_to_concrete(sym.info.value_type, generic_names, concrete_types, if typ := t.resolve_generic_to_concrete(sym.info.value_type, generic_names, concrete_types) {
is_inst)
{
unwrapped_value_type = typ unwrapped_value_type = typ
type_changed = true type_changed = true
} }
@ -1210,11 +1198,11 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
return new_type(idx).derive(generic_type).clear_flag(.generic) return new_type(idx).derive(generic_type).clear_flag(.generic)
} }
} else if mut sym.info is Struct { } else if mut sym.info is Struct {
if sym.info.is_generic && is_inst { if sym.info.is_generic {
mut nrt := '$sym.name<' mut nrt := '$sym.name<'
for i in 0 .. sym.info.generic_types.len { for i in 0 .. sym.info.generic_types.len {
if ct := t.resolve_generic_to_concrete(sym.info.generic_types[i], generic_names, if ct := t.resolve_generic_to_concrete(sym.info.generic_types[i], generic_names,
concrete_types, false) concrete_types)
{ {
gts := t.get_type_symbol(ct) gts := t.get_type_symbol(ct)
nrt += gts.name nrt += gts.name
@ -1233,18 +1221,14 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
} else if mut sym.info is FnType { } else if mut sym.info is FnType {
mut func := sym.info.func mut func := sym.info.func
if func.return_type.has_flag(.generic) { if func.return_type.has_flag(.generic) {
if typ := t.resolve_generic_to_concrete(func.return_type, generic_names, concrete_types, if typ := t.resolve_generic_to_concrete(func.return_type, generic_names, concrete_types) {
is_inst)
{
func.return_type = typ func.return_type = typ
} }
} }
func.params = func.params.clone() func.params = func.params.clone()
for mut param in func.params { for mut param in func.params {
if param.typ.has_flag(.generic) { if param.typ.has_flag(.generic) {
if typ := t.resolve_generic_to_concrete(param.typ, generic_names, concrete_types, if typ := t.resolve_generic_to_concrete(param.typ, generic_names, concrete_types) {
is_inst)
{
param.typ = typ param.typ = typ
} }
} }
@ -1271,7 +1255,7 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() {
generic_names := parent_info.generic_types.map(t.get_type_symbol(it).name) generic_names := parent_info.generic_types.map(t.get_type_symbol(it).name)
for i in 0 .. fields.len { for i in 0 .. fields.len {
if t_typ := t.resolve_generic_to_concrete(fields[i].typ, generic_names, if t_typ := t.resolve_generic_to_concrete(fields[i].typ, generic_names,
info.concrete_types, true) info.concrete_types)
{ {
fields[i].typ = t_typ fields[i].typ = t_typ
} }

View File

@ -637,7 +637,7 @@ fn (mut c Checker) unwrap_generic_struct(struct_type ast.Type, generic_names []s
mut c_nrt := '${ts.cname}_T_' mut c_nrt := '${ts.cname}_T_'
for i in 0 .. ts.info.generic_types.len { for i in 0 .. ts.info.generic_types.len {
if ct := c.table.resolve_generic_to_concrete(ts.info.generic_types[i], if ct := c.table.resolve_generic_to_concrete(ts.info.generic_types[i],
generic_names, concrete_types, false) generic_names, concrete_types)
{ {
gts := c.table.get_type_symbol(ct) gts := c.table.get_type_symbol(ct)
nrt += gts.name nrt += gts.name
@ -657,7 +657,7 @@ fn (mut c Checker) unwrap_generic_struct(struct_type ast.Type, generic_names []s
mut fields := ts.info.fields.clone() mut fields := ts.info.fields.clone()
for i in 0 .. fields.len { for i in 0 .. fields.len {
if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, generic_names, if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, generic_names,
concrete_types, true) concrete_types)
{ {
fields[i].typ = t_typ fields[i].typ = t_typ
} }
@ -666,7 +666,7 @@ fn (mut c Checker) unwrap_generic_struct(struct_type ast.Type, generic_names []s
mut info_concrete_types := []ast.Type{} mut info_concrete_types := []ast.Type{}
for i in 0 .. ts.info.generic_types.len { for i in 0 .. ts.info.generic_types.len {
if t_typ := c.table.resolve_generic_to_concrete(ts.info.generic_types[i], if t_typ := c.table.resolve_generic_to_concrete(ts.info.generic_types[i],
generic_names, concrete_types, true) generic_names, concrete_types)
{ {
info_concrete_types << t_typ info_concrete_types << t_typ
} }
@ -2057,7 +2057,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
} }
if call_expr.concrete_types.len > 0 && method.return_type != 0 { if call_expr.concrete_types.len > 0 && method.return_type != 0 {
if typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names, if typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names,
concrete_types, false) concrete_types)
{ {
call_expr.return_type = typ call_expr.return_type = typ
return typ return typ
@ -2613,7 +2613,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
if param.typ.has_flag(.generic) if param.typ.has_flag(.generic)
&& func.generic_names.len == call_expr.concrete_types.len { && func.generic_names.len == call_expr.concrete_types.len {
if unwrap_typ := c.table.resolve_generic_to_concrete(param.typ, func.generic_names, if unwrap_typ := c.table.resolve_generic_to_concrete(param.typ, func.generic_names,
concrete_types, false) concrete_types)
{ {
c.check_expected_call_arg(c.unwrap_generic(typ), unwrap_typ, call_expr.language) or { c.check_expected_call_arg(c.unwrap_generic(typ), unwrap_typ, call_expr.language) or {
c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos) c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos)
@ -2631,7 +2631,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
} }
if call_expr.concrete_types.len > 0 && func.return_type != 0 { if call_expr.concrete_types.len > 0 && func.return_type != 0 {
if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names, if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
concrete_types, false) concrete_types)
{ {
call_expr.return_type = typ call_expr.return_type = typ
return typ return typ
@ -3033,13 +3033,6 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
pub fn (mut c Checker) return_stmt(mut node ast.Return) { pub fn (mut c Checker) return_stmt(mut node ast.Return) {
c.expected_type = c.table.cur_fn.return_type c.expected_type = c.table.cur_fn.return_type
mut expected_type := c.unwrap_generic(c.expected_type) mut expected_type := c.unwrap_generic(c.expected_type)
if expected_type.has_flag(.generic) && c.table.get_type_symbol(expected_type).kind == .struct_ {
if t_typ := c.table.resolve_generic_to_concrete(expected_type, c.table.cur_fn.generic_names,
c.table.cur_concrete_types, true)
{
expected_type = t_typ
}
}
expected_type_sym := c.table.get_type_symbol(expected_type) expected_type_sym := c.table.get_type_symbol(expected_type)
if node.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type { if node.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type {
c.error('unexpected argument, current function does not return anything', node.exprs[0].position()) c.error('unexpected argument, current function does not return anything', node.exprs[0].position())
@ -4669,7 +4662,7 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type { pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
if typ.has_flag(.generic) { if typ.has_flag(.generic) {
if t_typ := c.table.resolve_generic_to_concrete(typ, c.table.cur_fn.generic_names, if t_typ := c.table.resolve_generic_to_concrete(typ, c.table.cur_fn.generic_names,
c.table.cur_concrete_types, false) c.table.cur_concrete_types)
{ {
return t_typ return t_typ
} }

View File

@ -5,6 +5,20 @@ vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:26:1: error: g
| ~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~
27 | t := <-g.ch 27 | t := <-g.ch
28 | handle(t) 28 | handle(t)
vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:27:7: error: <- operator can only be used with `chan` types
25 |
26 | fn g_worker(g Generic<T>) {
27 | t := <-g.ch
| ~~
28 | handle(t)
29 | // println("${t.msg}")
vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:28:9: error: cannot use `void` as `T` in argument 1 to `handle`
26 | fn g_worker(g Generic<T>) {
27 | t := <-g.ch
28 | handle(t)
| ^
29 | // println("${t.msg}")
30 | }
vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:32:1: error: generic function declaration must specify generic type names, e.g. foo<T> vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:32:1: error: generic function declaration must specify generic type names, e.g. foo<T>
30 | } 30 | }
31 | 31 |
@ -12,10 +26,3 @@ vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:32:1: error: g
| ~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~
33 | println("hi") 33 | println("hi")
34 | } 34 | }
vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:21:14: error: cannot use `Generic<Concrete>` as `Generic<T>` in argument 1 to `g_worker`
19 | }
20 |
21 | go g_worker(g)
| ^
22 |
23 | return g

View File

@ -8,7 +8,7 @@ import v.ast
fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
if typ.has_flag(.generic) { if typ.has_flag(.generic) {
if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names, if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names,
g.table.cur_concrete_types, true) g.table.cur_concrete_types)
{ {
return t_typ return t_typ
} }

View File

@ -124,3 +124,22 @@ fn test_generics_return_generic_struct_from_fn() {
println(it.next() or { -1 }) println(it.next() or { -1 })
assert '$it.next()' == 'Option(1)' assert '$it.next()' == 'Option(1)'
} }
struct ListNode<T> {
pub mut:
val T
next &ListNode<T> = 0
}
fn (mut node ListNode<T>) test() &ListNode<T> {
return node.next
}
fn test_generics_return_generic_struct_field() {
mut node1 := &ListNode<int>{100, 0}
mut node2 := &ListNode<int>{200, 0}
node1.next = node2
ret := node1.test()
println(ret)
assert ret.val == 200
}