generics: fix generic fn type mismatch of returning a generic struct (#10553)
parent
8324a766e8
commit
4bfe76123a
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue