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.
// 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.
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)
if sym.name in generic_names {
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)
dims++
}
if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types,
is_inst)
{
if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types) {
idx := t.find_or_register_array_with_dims(typ, dims)
return new_type(idx).derive(generic_type).clear_flag(.generic)
}
} else if sym.kind == .array_fixed {
info := sym.info as ArrayFixed
if typ := t.resolve_generic_to_concrete(info.elem_type, generic_names, concrete_types,
is_inst)
{
if typ := t.resolve_generic_to_concrete(info.elem_type, generic_names, concrete_types) {
idx := t.find_or_register_array_fixed(typ, info.size, None{})
return new_type(idx).derive(generic_type).clear_flag(.generic)
}
} else if mut sym.info is Chan {
if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names, concrete_types,
is_inst)
{
if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names, concrete_types) {
idx := t.find_or_register_chan(typ, typ.nr_muls() > 0)
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 type_changed := false
for ret_type in sym.info.types {
if typ := t.resolve_generic_to_concrete(ret_type, generic_names, concrete_types,
is_inst)
{
if typ := t.resolve_generic_to_concrete(ret_type, generic_names, concrete_types) {
types << typ
type_changed = true
} else {
@ -1193,15 +1185,11 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
mut type_changed := false
mut unwrapped_key_type := sym.info.key_type
mut unwrapped_value_type := sym.info.value_type
if typ := t.resolve_generic_to_concrete(sym.info.key_type, generic_names, concrete_types,
is_inst)
{
if typ := t.resolve_generic_to_concrete(sym.info.key_type, generic_names, concrete_types) {
unwrapped_key_type = typ
type_changed = true
}
if typ := t.resolve_generic_to_concrete(sym.info.value_type, generic_names, concrete_types,
is_inst)
{
if typ := t.resolve_generic_to_concrete(sym.info.value_type, generic_names, concrete_types) {
unwrapped_value_type = typ
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)
}
} else if mut sym.info is Struct {
if sym.info.is_generic && is_inst {
if sym.info.is_generic {
mut nrt := '$sym.name<'
for i in 0 .. sym.info.generic_types.len {
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)
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 {
mut func := sym.info.func
if func.return_type.has_flag(.generic) {
if typ := t.resolve_generic_to_concrete(func.return_type, generic_names, concrete_types,
is_inst)
{
if typ := t.resolve_generic_to_concrete(func.return_type, generic_names, concrete_types) {
func.return_type = typ
}
}
func.params = func.params.clone()
for mut param in func.params {
if param.typ.has_flag(.generic) {
if typ := t.resolve_generic_to_concrete(param.typ, generic_names, concrete_types,
is_inst)
{
if typ := t.resolve_generic_to_concrete(param.typ, generic_names, concrete_types) {
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)
for i in 0 .. fields.len {
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
}

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_'
for i in 0 .. ts.info.generic_types.len {
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)
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()
for i in 0 .. fields.len {
if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, generic_names,
concrete_types, true)
concrete_types)
{
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{}
for i in 0 .. ts.info.generic_types.len {
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
}
@ -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 typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names,
concrete_types, false)
concrete_types)
{
call_expr.return_type = 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)
&& func.generic_names.len == call_expr.concrete_types.len {
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.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 typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
concrete_types, false)
concrete_types)
{
call_expr.return_type = 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) {
c.expected_type = c.table.cur_fn.return_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)
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())
@ -4669,7 +4662,7 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
if typ.has_flag(.generic) {
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
}

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
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>
30 | }
31 |
@ -12,10 +26,3 @@ vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:32:1: error: g
| ~~~~~~~~~~~~~~
33 | println("hi")
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 {
if typ.has_flag(.generic) {
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
}

View File

@ -124,3 +124,22 @@ fn test_generics_return_generic_struct_from_fn() {
println(it.next() or { -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
}