checker/gen: fix generic struct init (#8322)
parent
58b37519e0
commit
d477e525bb
|
@ -90,7 +90,7 @@ mut:
|
|||
struct WebhookServer {
|
||||
vweb.Context
|
||||
mut:
|
||||
gen_vc &GenVC
|
||||
gen_vc &GenVC = 0 // initialized in init_once
|
||||
}
|
||||
|
||||
// storage for flag options
|
||||
|
|
|
@ -264,6 +264,7 @@ pub:
|
|||
pos token.Position
|
||||
is_short bool
|
||||
pub mut:
|
||||
unresolved bool
|
||||
pre_comments []Comment
|
||||
typ table.Type
|
||||
update_expr Expr
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
module ast
|
||||
|
||||
import v.table
|
||||
|
||||
pub fn resolve_init(node StructInit, typ table.Type, t &table.Table) Expr {
|
||||
type_sym := t.get_type_symbol(typ)
|
||||
if type_sym.kind == .array {
|
||||
array_info := type_sym.info as table.Array
|
||||
mut has_len := false
|
||||
mut has_cap := false
|
||||
mut has_default := false
|
||||
mut len_expr := Expr{}
|
||||
mut cap_expr := Expr{}
|
||||
mut default_expr := Expr{}
|
||||
mut exprs := []Expr{}
|
||||
for field in node.fields {
|
||||
match field.name {
|
||||
'len' {
|
||||
has_len = true
|
||||
len_expr = field.expr
|
||||
}
|
||||
'cap' {
|
||||
has_cap = true
|
||||
len_expr = field.expr
|
||||
}
|
||||
'default' {
|
||||
has_default = true
|
||||
len_expr = field.expr
|
||||
}
|
||||
else {
|
||||
exprs << field.expr
|
||||
}
|
||||
}
|
||||
}
|
||||
return ArrayInit{
|
||||
// TODO: mod is not being set for now, we could need this in future
|
||||
// mod: mod
|
||||
pos: node.pos
|
||||
typ: typ
|
||||
elem_type: array_info.elem_type
|
||||
has_len: has_len
|
||||
has_cap: has_cap
|
||||
has_default: has_default
|
||||
len_expr: len_expr
|
||||
cap_expr: cap_expr
|
||||
default_expr: default_expr
|
||||
exprs: exprs
|
||||
}
|
||||
} else if type_sym.kind == .map {
|
||||
map_info := type_sym.info as table.Map
|
||||
mut keys := []Expr{}
|
||||
mut vals := []Expr{}
|
||||
for field in node.fields {
|
||||
keys << StringLiteral{
|
||||
val: field.name
|
||||
}
|
||||
vals << field.expr
|
||||
}
|
||||
return MapInit{
|
||||
typ: typ
|
||||
key_type: map_info.key_type
|
||||
value_type: map_info.value_type
|
||||
keys: keys
|
||||
vals: vals
|
||||
}
|
||||
}
|
||||
// struct / other (sumtype?)
|
||||
return StructInit{
|
||||
...node
|
||||
unresolved: false
|
||||
}
|
||||
}
|
|
@ -460,6 +460,7 @@ pub fn (mut c Checker) infer_fn_types(f table.Fn, mut call_expr ast.CallExpr) {
|
|||
}
|
||||
if typ == table.void_type {
|
||||
c.error('could not infer generic type `$gt_name` in call to `$f.name`', call_expr.pos)
|
||||
return
|
||||
}
|
||||
if c.pref.is_verbose {
|
||||
s := c.table.type_to_str(typ)
|
||||
|
|
|
@ -508,7 +508,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
|||
if struct_init.typ == 0 {
|
||||
c.error('unknown type', struct_init.pos)
|
||||
}
|
||||
type_sym := c.table.get_type_symbol(struct_init.typ)
|
||||
utyp := c.unwrap_generic(struct_init.typ)
|
||||
type_sym := c.table.get_type_symbol(utyp)
|
||||
if type_sym.kind == .sum_type && struct_init.fields.len == 1 {
|
||||
sexpr := struct_init.fields[0].expr.str()
|
||||
c.error('cast to sum type using `${type_sym.name}($sexpr)` not `$type_sym.name{$sexpr}`',
|
||||
|
@ -517,15 +518,16 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
|||
if type_sym.kind == .interface_ {
|
||||
c.error('cannot instantiate interface `$type_sym.name`', struct_init.pos)
|
||||
}
|
||||
if type_sym.kind == .alias {
|
||||
info := type_sym.info as table.Alias
|
||||
if info.parent_type.is_number() {
|
||||
if type_sym.info is table.Alias {
|
||||
if type_sym.info.parent_type.is_number() {
|
||||
c.error('cannot instantiate number type alias `$type_sym.name`', struct_init.pos)
|
||||
return table.void_type
|
||||
}
|
||||
}
|
||||
if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.mod != c.mod
|
||||
&& type_sym.language != .c {
|
||||
// allow init structs from generic if they're private except the type is from builtin module
|
||||
if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.language != .c
|
||||
&& (type_sym.mod != c.mod && !(struct_init.typ.has_flag(.generic)
|
||||
&& type_sym.mod != 'builtin')) {
|
||||
c.error('type `$type_sym.name` is private', struct_init.pos)
|
||||
}
|
||||
if type_sym.kind == .struct_ {
|
||||
|
@ -3349,14 +3351,11 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
|||
pub fn (c &Checker) unwrap_generic(typ table.Type) table.Type {
|
||||
if typ.has_flag(.generic) {
|
||||
sym := c.table.get_type_symbol(typ)
|
||||
mut idx := 0
|
||||
for i, generic_param in c.cur_fn.generic_params {
|
||||
if generic_param.name == sym.name {
|
||||
idx = i
|
||||
break
|
||||
return c.cur_generic_types[i].derive(typ).clear_flag(.generic)
|
||||
}
|
||||
}
|
||||
return c.cur_generic_types[idx].derive(typ).clear_flag(.generic)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
@ -3583,6 +3582,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
|||
return c.string_inter_lit(mut node)
|
||||
}
|
||||
ast.StructInit {
|
||||
if node.unresolved {
|
||||
return c.expr(ast.resolve_init(node, c.unwrap_generic(node.typ), c.table))
|
||||
}
|
||||
return c.struct_init(mut node)
|
||||
}
|
||||
ast.Type {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
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 |
|
|
@ -0,0 +1,7 @@
|
|||
fn test<T>() T {
|
||||
return T{}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
_ := test<int>()
|
||||
}
|
|
@ -2721,55 +2721,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
g.match_expr(node)
|
||||
}
|
||||
ast.MapInit {
|
||||
key_typ_str := g.typ(node.key_type)
|
||||
value_typ_str := g.typ(node.value_type)
|
||||
value_typ := g.table.get_type_symbol(node.value_type)
|
||||
key_typ := g.table.get_type_symbol(node.key_type)
|
||||
hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_typ)
|
||||
size := node.vals.len
|
||||
mut shared_styp := '' // only needed for shared &[]{...}
|
||||
mut styp := ''
|
||||
is_amp := g.is_amp
|
||||
g.is_amp = false
|
||||
if is_amp {
|
||||
g.out.go_back(1) // delete the `&` already generated in `prefix_expr()
|
||||
}
|
||||
if g.is_shared {
|
||||
mut shared_typ := node.typ.set_flag(.shared_f)
|
||||
shared_styp = g.typ(shared_typ)
|
||||
g.writeln('($shared_styp*)__dup_shared_map(&($shared_styp){.val = ')
|
||||
} else if is_amp {
|
||||
styp = g.typ(node.typ)
|
||||
g.write('($styp*)memdup(ADDR($styp, ')
|
||||
}
|
||||
if size > 0 {
|
||||
if value_typ.kind == .function {
|
||||
g.write('new_map_init_2($hash_fn, $key_eq_fn, $clone_fn, $free_fn, $size, sizeof($key_typ_str), sizeof(voidptr), _MOV(($key_typ_str[$size]){')
|
||||
} else {
|
||||
g.write('new_map_init_2($hash_fn, $key_eq_fn, $clone_fn, $free_fn, $size, sizeof($key_typ_str), sizeof($value_typ_str), _MOV(($key_typ_str[$size]){')
|
||||
}
|
||||
for expr in node.keys {
|
||||
g.expr(expr)
|
||||
g.write(', ')
|
||||
}
|
||||
if value_typ.kind == .function {
|
||||
g.write('}), _MOV((voidptr[$size]){')
|
||||
} else {
|
||||
g.write('}), _MOV(($value_typ_str[$size]){')
|
||||
}
|
||||
for expr in node.vals {
|
||||
g.expr(expr)
|
||||
g.write(', ')
|
||||
}
|
||||
g.write('}))')
|
||||
} else {
|
||||
g.write('new_map_2(sizeof($key_typ_str), sizeof($value_typ_str), $hash_fn, $key_eq_fn, $clone_fn, $free_fn)')
|
||||
}
|
||||
if g.is_shared {
|
||||
g.write('}, sizeof($shared_styp))')
|
||||
} else if is_amp {
|
||||
g.write('), sizeof($styp))')
|
||||
}
|
||||
g.map_init(node)
|
||||
}
|
||||
ast.None {
|
||||
g.write('opt_none()')
|
||||
|
@ -2863,8 +2815,12 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
g.string_inter_literal(node)
|
||||
}
|
||||
ast.StructInit {
|
||||
// `user := User{name: 'Bob'}`
|
||||
g.struct_init(node)
|
||||
if node.unresolved {
|
||||
g.expr(ast.resolve_init(node, g.unwrap_generic(node.typ), g.table))
|
||||
} else {
|
||||
// `user := User{name: 'Bob'}`
|
||||
g.struct_init(node)
|
||||
}
|
||||
}
|
||||
ast.SelectorExpr {
|
||||
g.selector_expr(node)
|
||||
|
@ -3645,6 +3601,58 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) map_init(node ast.MapInit) {
|
||||
key_typ_str := g.typ(node.key_type)
|
||||
value_typ_str := g.typ(node.value_type)
|
||||
value_typ := g.table.get_type_symbol(node.value_type)
|
||||
key_typ := g.table.get_type_symbol(node.key_type)
|
||||
hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_typ)
|
||||
size := node.vals.len
|
||||
mut shared_styp := '' // only needed for shared &[]{...}
|
||||
mut styp := ''
|
||||
is_amp := g.is_amp
|
||||
g.is_amp = false
|
||||
if is_amp {
|
||||
g.out.go_back(1) // delete the `&` already generated in `prefix_expr()
|
||||
}
|
||||
if g.is_shared {
|
||||
mut shared_typ := node.typ.set_flag(.shared_f)
|
||||
shared_styp = g.typ(shared_typ)
|
||||
g.writeln('($shared_styp*)__dup_shared_map(&($shared_styp){.val = ')
|
||||
} else if is_amp {
|
||||
styp = g.typ(node.typ)
|
||||
g.write('($styp*)memdup(ADDR($styp, ')
|
||||
}
|
||||
if size > 0 {
|
||||
if value_typ.kind == .function {
|
||||
g.write('new_map_init_2($hash_fn, $key_eq_fn, $clone_fn, $free_fn, $size, sizeof($key_typ_str), sizeof(voidptr), _MOV(($key_typ_str[$size]){')
|
||||
} else {
|
||||
g.write('new_map_init_2($hash_fn, $key_eq_fn, $clone_fn, $free_fn, $size, sizeof($key_typ_str), sizeof($value_typ_str), _MOV(($key_typ_str[$size]){')
|
||||
}
|
||||
for expr in node.keys {
|
||||
g.expr(expr)
|
||||
g.write(', ')
|
||||
}
|
||||
if value_typ.kind == .function {
|
||||
g.write('}), _MOV((voidptr[$size]){')
|
||||
} else {
|
||||
g.write('}), _MOV(($value_typ_str[$size]){')
|
||||
}
|
||||
for expr in node.vals {
|
||||
g.expr(expr)
|
||||
g.write(', ')
|
||||
}
|
||||
g.write('}))')
|
||||
} else {
|
||||
g.write('new_map_2(sizeof($key_typ_str), sizeof($value_typ_str), $hash_fn, $key_eq_fn, $clone_fn, $free_fn)')
|
||||
}
|
||||
if g.is_shared {
|
||||
g.write('}, sizeof($shared_styp))')
|
||||
} else if is_amp {
|
||||
g.write('), sizeof($styp))')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) select_expr(node ast.SelectExpr) {
|
||||
is_expr := node.is_expr || g.inside_ternary > 0
|
||||
cur_line := if is_expr {
|
||||
|
|
|
@ -354,14 +354,11 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
|||
pub fn (g &Gen) unwrap_generic(typ table.Type) table.Type {
|
||||
if typ.has_flag(.generic) {
|
||||
sym := g.table.get_type_symbol(typ)
|
||||
mut idx := 0
|
||||
for i, generic_param in g.cur_fn.generic_params {
|
||||
if generic_param.name == sym.name {
|
||||
idx = i
|
||||
break
|
||||
return g.cur_generic_types[i].derive(typ).clear_flag(.generic)
|
||||
}
|
||||
}
|
||||
return g.cur_generic_types[idx].derive(typ).clear_flag(.generic)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
|
|
@ -558,6 +558,13 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
|||
g.write("string('$text')")
|
||||
}
|
||||
ast.StructInit {
|
||||
// TODO: once generic fns/unwrap_generic is implemented
|
||||
// if node.unresolved {
|
||||
// g.expr(ast.resolve_init(node, g.unwrap_generic(node.typ), g.table))
|
||||
// } else {
|
||||
// // `user := User{name: 'Bob'}`
|
||||
// g.gen_struct_init(node)
|
||||
// }
|
||||
// `user := User{name: 'Bob'}`
|
||||
g.gen_struct_init(node)
|
||||
}
|
||||
|
@ -1037,11 +1044,7 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) {
|
|||
g.inc_indent()
|
||||
g.write('return `$js_name {')
|
||||
for i, field in node.fields {
|
||||
g.write(if i == 0 {
|
||||
' '
|
||||
} else {
|
||||
', '
|
||||
})
|
||||
g.write(if i == 0 { ' ' } else { ', ' })
|
||||
match g.typ(field.typ).split('.').last() {
|
||||
'string' { g.write('$field.name: "\${this["$field.name"].toString()}"') }
|
||||
else { g.write('$field.name: \${this["$field.name"].toString()} ') }
|
||||
|
|
|
@ -409,7 +409,8 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
|
|||
p.check(.rcbr)
|
||||
}
|
||||
p.is_amp = saved_is_amp
|
||||
node := ast.StructInit{
|
||||
return ast.StructInit{
|
||||
unresolved: typ.has_flag(.generic)
|
||||
typ: typ
|
||||
fields: fields
|
||||
update_expr: update_expr
|
||||
|
@ -419,7 +420,6 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
|
|||
is_short: no_keys
|
||||
pre_comments: pre_comments
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
||||
|
|
|
@ -44,10 +44,27 @@ fn (v Foo) new<T>() T {
|
|||
|
||||
fn test_generic_method_with_map_type() {
|
||||
foo := Foo{}
|
||||
assert foo.new<map[string]string>() == map[string]string{}
|
||||
mut a := foo.new<map[string]string>()
|
||||
assert a == map[string]string{}
|
||||
assert a.len == 0
|
||||
a['a'] = 'a'
|
||||
assert a.len == 1
|
||||
assert a['a'] == 'a'
|
||||
}
|
||||
|
||||
fn test_generic_method_with_array_type() {
|
||||
foo := Foo{}
|
||||
assert foo.new<[]string>() == []string{}
|
||||
mut a := foo.new<[]string>()
|
||||
assert a == []string{}
|
||||
assert a.len == 0
|
||||
a << 'a'
|
||||
assert a.len == 1
|
||||
assert a[0] == 'a'
|
||||
}
|
||||
|
||||
fn test_generic_method_with_struct_type() {
|
||||
foo := Foo{}
|
||||
mut a := foo.new<Person>()
|
||||
a.name = 'a'
|
||||
assert a.name == 'a'
|
||||
}
|
||||
|
|
|
@ -386,3 +386,25 @@ fn test_multi_generic_args() {
|
|||
assert multi_generic_args("Super", 2021)
|
||||
}
|
||||
|
||||
fn new<T>() T {
|
||||
return T{}
|
||||
}
|
||||
|
||||
fn test_generic_init() {
|
||||
// array init
|
||||
mut a := new<[]string>()
|
||||
assert a.len == 0
|
||||
a << 'a'
|
||||
assert a.len == 1
|
||||
assert a[0] == 'a'
|
||||
// map init
|
||||
mut b := new<map[string]string>()
|
||||
assert b.len == 0
|
||||
b['b'] = 'b'
|
||||
assert b.len == 1
|
||||
assert b['b'] == 'b'
|
||||
// struct init
|
||||
mut c := new<User>()
|
||||
c.name = 'c'
|
||||
assert c.name == 'c'
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue