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.
|
// 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue