checker: fix generic structs init (#10134)

pull/10147/head
yuyi 2021-05-20 14:18:51 +08:00 committed by GitHub
parent 906b207e58
commit 492d264d08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 107 additions and 18 deletions

View File

@ -609,6 +609,56 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
} }
} }
fn (mut c Checker) unwrap_generics_struct_init(struct_type ast.Type) ast.Type {
ts := c.table.get_type_symbol(struct_type)
if ts.info is ast.Struct {
if ts.info.is_generic {
mut nrt := '$ts.name<'
mut c_nrt := '${ts.name}_T_'
for i in 0 .. ts.info.generic_types.len {
gts := c.table.get_type_symbol(c.unwrap_generic(ts.info.generic_types[i]))
nrt += gts.name
c_nrt += gts.name
if i != ts.info.generic_types.len - 1 {
nrt += ','
c_nrt += '_'
}
}
nrt += '>'
idx := c.table.type_idxs[nrt]
if idx != 0 && c.table.type_symbols[idx].kind != .placeholder {
return ast.new_type(idx).derive(struct_type).clear_flag(.generic)
} else {
mut fields := ts.info.fields.clone()
if ts.info.generic_types.len == c.cur_concrete_types.len {
generic_names := ts.info.generic_types.map(c.table.get_type_symbol(it).name)
for i in 0 .. fields.len {
if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ,
generic_names, c.cur_concrete_types, true)
{
fields[i].typ = t_typ
}
}
mut info := ts.info
info.is_generic = false
info.concrete_types = c.cur_concrete_types.clone()
info.parent_type = struct_type
info.fields = fields
stru_idx := c.table.register_type_symbol(ast.TypeSymbol{
kind: .struct_
name: nrt
cname: util.no_dots(c_nrt)
mod: c.mod
info: info
})
return ast.new_type(stru_idx).derive(struct_type).clear_flag(.generic)
}
}
}
}
return struct_type
}
pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
// typ := c.table.find_type(struct_init.typ.typ.name) or { // typ := c.table.find_type(struct_init.typ.typ.name) or {
// c.error('unknown struct: $struct_init.typ.typ.name', struct_init.pos) // c.error('unknown struct: $struct_init.typ.typ.name', struct_init.pos)
@ -627,7 +677,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
struct_init.typ = c.expected_type struct_init.typ = c.expected_type
} }
} }
utyp := c.unwrap_generic(struct_init.typ) utyp := c.unwrap_generics_struct_init(struct_init.typ)
c.ensure_type_exists(utyp, struct_init.pos) or {} c.ensure_type_exists(utyp, struct_init.pos) or {}
type_sym := c.table.get_type_symbol(utyp) type_sym := c.table.get_type_symbol(utyp)
if !c.inside_unsafe && type_sym.kind == .sum_type { if !c.inside_unsafe && type_sym.kind == .sum_type {
@ -757,7 +807,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
if is_embed { if is_embed {
expected_type = embed_type expected_type = embed_type
c.expected_type = expected_type c.expected_type = expected_type
expr_type = c.expr(field.expr) expr_type = c.unwrap_generic(c.expr(field.expr))
expr_type_sym := c.table.get_type_symbol(expr_type) expr_type_sym := c.table.get_type_symbol(expr_type)
if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
c.check_expected(expr_type, embed_type) or { c.check_expected(expr_type, embed_type) or {
@ -772,7 +822,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
field_type_sym := c.table.get_type_symbol(info_field.typ) field_type_sym := c.table.get_type_symbol(info_field.typ)
expected_type = info_field.typ expected_type = info_field.typ
c.expected_type = expected_type c.expected_type = expected_type
expr_type = c.expr(field.expr) expr_type = c.unwrap_generic(c.expr(field.expr))
if !info_field.typ.has_flag(.optional) { if !info_field.typ.has_flag(.optional) {
expr_type = c.check_expr_opt_call(field.expr, expr_type) expr_type = c.check_expr_opt_call(field.expr, expr_type)
} }
@ -882,7 +932,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
c.error('expression is not an lvalue', struct_init.update_expr.position()) c.error('expression is not an lvalue', struct_init.update_expr.position())
} }
} }
return struct_init.typ return utyp
} }
fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) { fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) {
@ -2930,7 +2980,14 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) ast.Typ
// TODO: non deferred // TODO: non deferred
pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
c.expected_type = c.table.cur_fn.return_type c.expected_type = c.table.cur_fn.return_type
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.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 return_stmt.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type { if return_stmt.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type {
c.error('unexpected argument, current function does not return anything', return_stmt.exprs[0].position()) c.error('unexpected argument, current function does not return anything', return_stmt.exprs[0].position())

View File

@ -1,6 +0,0 @@
vlib/v/checker/tests/check_generic_int_init.vv:2:9: error: type `int` is private
1 | fn test<T>() T {
2 | return T{}
| ~~~
3 | }
4 |

View File

@ -1,7 +0,0 @@
fn test<T>() T {
return T{}
}
fn main() {
_ := test<int>()
}

View File

@ -12,3 +12,10 @@ 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

@ -5,3 +5,9 @@ vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv:13:32: error: retu
| ~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~
14 | d := GenericChannelStruct{ 14 | d := GenericChannelStruct{
15 | ch: chan T{} 15 | ch: chan T{}
vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv:17:9: error: cannot use `GenericChannelStruct<Simple>` as type `GenericChannelStruct` in return argument
15 | ch: chan T{}
16 | }
17 | return d
| ^
18 | }

View File

@ -0,0 +1,32 @@
struct List<T> {
mut:
count u32
first &ListNode<T>
last &ListNode<T>
}
struct ListNode<T> {
mut:
val T
next &ListNode<T> = 0
}
fn create<T>(arr []T) &List<T> {
assert arr.len > 0
mut n := &ListNode{
val: arr[0]
next: 0
}
mut l := &List{
first: n
last: n
count: 1
}
return l
}
fn test_generics_with_generic_structs_init() {
n := create([1, 2, 3])
println(n)
assert n.count == 1
}