mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
implement generic structs
This commit is contained in:
parent
b3a402eb82
commit
fbd9fedbfb
@ -401,7 +401,7 @@ fn (v &V) type_definitions() string {
|
||||
}
|
||||
// everything except builtin will get sorted
|
||||
for t_name, t in v.table.typesmap {
|
||||
if t_name in builtins {
|
||||
if t_name in builtins || t.is_generic {
|
||||
continue
|
||||
}
|
||||
types << t
|
||||
|
@ -214,8 +214,8 @@ fn (p mut Parser) name_expr() string {
|
||||
p.error('cannot use `_` as value')
|
||||
}
|
||||
// generic type check
|
||||
if name in p.cur_fn.dispatch_of.inst.keys() {
|
||||
name = p.cur_fn.dispatch_of.inst[name]
|
||||
if name in p.generic_dispatch.inst.keys() {
|
||||
name = p.generic_dispatch.inst[name]
|
||||
}
|
||||
// Raw string (`s := r'hello \n ')
|
||||
if name == 'r' && p.peek() == .str && p.prev_tok != .str_dollar {
|
||||
@ -349,7 +349,7 @@ fn (p mut Parser) name_expr() string {
|
||||
return enum_type.name
|
||||
}
|
||||
// normal struct init (non-C)
|
||||
else if p.peek() == .lcbr {
|
||||
else if p.peek() == .lcbr || p.peek() == .lt {
|
||||
return p.get_struct_type(name, false, ptr)
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ mut:
|
||||
defer_text []string
|
||||
type_pars []string
|
||||
type_inst []TypeInst
|
||||
dispatch_of TypeInst // current type inst of this generic instance
|
||||
generic_fn_idx int
|
||||
parser_idx int
|
||||
fn_name_token_idx int // used by error reporting
|
||||
@ -349,8 +348,7 @@ fn (p mut Parser) fn_decl() {
|
||||
if p.tok == .lt {
|
||||
// instance (dispatch)
|
||||
if p.generic_dispatch.inst.size > 0 {
|
||||
f.dispatch_of = p.generic_dispatch
|
||||
rename_generic_fn_instance(mut f, f.dispatch_of)
|
||||
rename_generic_fn_instance(mut f, &p.generic_dispatch)
|
||||
}
|
||||
else {
|
||||
f.is_generic = true
|
||||
@ -475,8 +473,9 @@ fn (p mut Parser) fn_decl() {
|
||||
p.table.register_fn(f)
|
||||
}
|
||||
}
|
||||
p.set_current_fn(EmptyFn)
|
||||
p.skip_fn_body()
|
||||
p.set_current_fn( EmptyFn )
|
||||
//p.skip_fn_body()
|
||||
p.skip_block(true)
|
||||
return
|
||||
}
|
||||
else {
|
||||
@ -604,6 +603,22 @@ fn (p mut Parser) fn_decl() {
|
||||
p.returns = false
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn (p mut Parser) skip_block(inside_lcbr bool) {
|
||||
mut cbr_depth := if inside_lcbr { 1 } else { 0 }
|
||||
for {
|
||||
if p.tok == .lcbr {
|
||||
cbr_depth++
|
||||
}
|
||||
if p.tok == .rcbr {
|
||||
cbr_depth--
|
||||
if cbr_depth == 0 { break }
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
p.check(.rcbr)
|
||||
}
|
||||
|
||||
[inline]
|
||||
// Skips the entire function's body in the first pass.
|
||||
fn (p mut Parser) skip_fn_body() {
|
||||
@ -769,10 +784,16 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
||||
}
|
||||
f.is_used = true
|
||||
cgen_name := p.table.fn_gen_name(f)
|
||||
p.next() // fn name
|
||||
p.next() // fn name
|
||||
mut generic_param_types := []string
|
||||
if p.tok == .lt {
|
||||
mut i := p.token_idx
|
||||
p.check(.lt)
|
||||
mut i := 0
|
||||
for {
|
||||
param_type := p.check_name()
|
||||
generic_param_types << param_type
|
||||
if p.tok != .comma { break }
|
||||
p.check(.comma)
|
||||
if p.tokens[i].tok == .gt {
|
||||
p.error('explicit type arguments are not allowed; remove `<...>`')
|
||||
}
|
||||
@ -782,6 +803,17 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
||||
}
|
||||
i++
|
||||
}
|
||||
p.check(.gt)
|
||||
// mut i := p.token_idx
|
||||
// for {
|
||||
// if p.tokens[i].tok == .gt {
|
||||
// //p.error('explicit type arguments are not allowed; remove `<...>`')
|
||||
// } else if p.tokens[i].tok == .lpar {
|
||||
// // probably a typo, do not concern the user with the above error message
|
||||
// break
|
||||
// }
|
||||
// i++
|
||||
// }
|
||||
}
|
||||
// if p.pref.is_prof {
|
||||
// p.cur_fn.called_fns << cgen_name
|
||||
@ -838,7 +870,7 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
||||
// if f is generic, the name is changed to a suitable instance in dispatch_generic_fn_instance()
|
||||
// we then replace `cgen_name` with the instance's name
|
||||
generic := f.is_generic
|
||||
p.fn_call_args(mut f)
|
||||
p.fn_call_args(mut f, generic_param_types)
|
||||
if generic {
|
||||
line := if p.cgen.is_tmp { p.cgen.tmp_line } else { p.cgen.cur_line }
|
||||
p.cgen.resetln(line.replace('$cgen_name (', '$f.name ('))
|
||||
@ -975,7 +1007,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
||||
}
|
||||
|
||||
// foo *(1, 2, 3, mut bar)*
|
||||
fn (p mut Parser) fn_call_args(f mut Fn) {
|
||||
fn (p mut Parser) fn_call_args(f mut Fn, generic_param_types []string) {
|
||||
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
||||
// C func. # of args is not known
|
||||
p.check(.lpar)
|
||||
@ -1007,7 +1039,8 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
||||
file_path := cescaped_path(p.file_path)
|
||||
p.cgen.resetln(p.cgen.cur_line.replace('v_panic (', 'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '))
|
||||
}
|
||||
mut saved_args := []string
|
||||
//mut saved_args := []string
|
||||
mut saved_args := generic_param_types
|
||||
for i, arg in f.args {
|
||||
// Receiver is the first arg
|
||||
// Skip the receiver, because it was already generated in the expression
|
||||
@ -1382,6 +1415,10 @@ fn replace_generic_type_params(f mut Fn, ti &TypeInst) {
|
||||
}
|
||||
f.args = args
|
||||
f.typ = replace_generic_type(f.typ, ti)
|
||||
if f.typ.ends_with('_T') {
|
||||
par := ti.inst.keys()[0]
|
||||
f.typ = f.typ + '_' + ti.inst[par]
|
||||
}
|
||||
}
|
||||
|
||||
fn (p mut Parser) register_vargs_stuct(typ string, len int) string {
|
||||
@ -1483,9 +1520,6 @@ fn (p mut Parser) register_multi_return_stuct(types []string) string {
|
||||
}
|
||||
|
||||
fn rename_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
||||
if f.is_method && f.dispatch_of.inst.size == 0 {
|
||||
f.name = f.receiver_typ + '_' + f.name
|
||||
}
|
||||
f.name = f.name + '_T'
|
||||
for k in ti.inst.keys() {
|
||||
f.name = f.name + '_' + type_to_safe_str(ti.inst[k])
|
||||
@ -1512,9 +1546,9 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
||||
}
|
||||
f.type_inst << *ti
|
||||
p.table.register_fn(f)
|
||||
if f.is_method { f.name = f.receiver_typ + '_' + f.name }
|
||||
rename_generic_fn_instance(mut f, ti)
|
||||
replace_generic_type_params(mut f, ti)
|
||||
f.dispatch_of = *ti
|
||||
// TODO: Handle case where type not defined yet, see above
|
||||
// if f.typ in f.type_pars { f.typ = '_ANYTYPE_' }
|
||||
// if f.typ in ti.inst {
|
||||
@ -1529,12 +1563,13 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
||||
}
|
||||
mut gp := p.v.parsers[f.parser_idx]
|
||||
gp.is_vgen = true
|
||||
gp.generic_dispatch = *ti
|
||||
saved_state := p.save_state()
|
||||
p.clear_state(false, true)
|
||||
gp.token_idx = f.generic_fn_idx
|
||||
gp.generic_dispatch = *ti
|
||||
gp.next()
|
||||
gp.fn_decl()
|
||||
gp.generic_dispatch = TypeInst{}
|
||||
p.cgen.lines_extra << p.cgen.lines
|
||||
p.restore_state(saved_state, false, true)
|
||||
p.cgen.fns << '${p.fn_signature(f)};'
|
||||
|
@ -472,7 +472,7 @@ fn (p mut Parser) gen_array_set(typ string, is_ptr, is_map bool, fn_ph, assign_p
|
||||
}
|
||||
|
||||
// returns true in case of an early return
|
||||
fn (p mut Parser) gen_struct_init(typ string, t Type) bool {
|
||||
fn (p mut Parser) gen_struct_init(typ string, t &Type) bool {
|
||||
// TODO hack. If it's a C type, we may need to add "struct" before declaration:
|
||||
// a := &C.A{} ==> struct A* a = malloc(sizeof(struct A));
|
||||
if p.is_c_struct_init {
|
||||
|
@ -163,7 +163,7 @@ fn (p mut Parser) gen_array_set(typ string, is_ptr, is_map bool,fn_ph, assign_po
|
||||
}
|
||||
|
||||
// returns true in case of an early return
|
||||
fn (p mut Parser) gen_struct_init(typ string, t Type) bool {
|
||||
fn (p mut Parser) gen_struct_init(typ string, t &Type) bool {
|
||||
p.next()
|
||||
p.check(.lcbr)
|
||||
ptr := typ.contains('*')
|
||||
|
@ -125,7 +125,7 @@ fn (p mut Parser) get_type2() Type {
|
||||
p.check(.amp)
|
||||
}
|
||||
// generic type check
|
||||
ti := p.cur_fn.dispatch_of.inst
|
||||
ti := p.generic_dispatch.inst
|
||||
if p.lit in ti.keys() {
|
||||
typ += ti[p.lit]
|
||||
}
|
||||
|
@ -154,14 +154,17 @@ pub fn (v &V) finalize_compilation() {
|
||||
}
|
||||
|
||||
pub fn (v mut V) add_parser(parser Parser) int {
|
||||
pidx := v.parsers.len
|
||||
v.parsers << parser
|
||||
pidx := v.parsers.len - 1
|
||||
v.file_parser_idx[os.realpath(parser.file_path)] = pidx
|
||||
file_path := if filepath.is_abs(parser.file_path) {
|
||||
parser.file_path } else { os.realpath(parser.file_path) }
|
||||
v.file_parser_idx[file_path] = pidx
|
||||
return pidx
|
||||
}
|
||||
|
||||
pub fn (v &V) get_file_parser_index(file string) ?int {
|
||||
file_path := os.realpath(file)
|
||||
file_path := if filepath.is_abs(file) {
|
||||
file } else { os.realpath(file) }
|
||||
if file_path in v.file_parser_idx {
|
||||
return v.file_parser_idx[file_path]
|
||||
}
|
||||
|
@ -473,7 +473,7 @@ fn (p mut Parser) parse(pass Pass) {
|
||||
p.const_decl()
|
||||
}
|
||||
.key_struct, .key_union, .key_interface {
|
||||
p.struct_decl()
|
||||
p.struct_decl([])
|
||||
}
|
||||
.key_enum {
|
||||
p.enum_decl(false)
|
||||
@ -497,7 +497,7 @@ fn (p mut Parser) parse(pass Pass) {
|
||||
p.attribute()
|
||||
}
|
||||
.key_struct, .key_interface, .key_union, .lsbr {
|
||||
p.struct_decl()
|
||||
p.struct_decl([])
|
||||
}
|
||||
.key_const {
|
||||
p.const_decl()
|
||||
@ -1035,7 +1035,7 @@ fn (p mut Parser) get_type() string {
|
||||
p.check(.amp)
|
||||
}
|
||||
// generic type check
|
||||
ti := p.cur_fn.dispatch_of.inst
|
||||
ti := p.generic_dispatch.inst
|
||||
if p.lit in ti.keys() {
|
||||
typ += ti[p.lit]
|
||||
}
|
||||
@ -1090,6 +1090,25 @@ fn (p mut Parser) get_type() string {
|
||||
p.error('type `$t.name` is private')
|
||||
}
|
||||
}
|
||||
// generic struct
|
||||
if p.peek() == .lt {
|
||||
p.next()
|
||||
p.check(.lt)
|
||||
typ = '${typ}_T'
|
||||
mut type_params := []string
|
||||
for p.tok != .gt {
|
||||
type_param := p.check_name()
|
||||
type_params << type_param
|
||||
if p.generic_dispatch.inst.size > 0 {
|
||||
if type_param in p.generic_dispatch.inst {
|
||||
typ = '${typ}_' + p.generic_dispatch.inst[type_param]
|
||||
}
|
||||
}
|
||||
if p.tok == .comma { p.check(.comma) }
|
||||
}
|
||||
p.check(.gt)
|
||||
return typ
|
||||
}
|
||||
if typ == 'void' {
|
||||
p.error('unknown type `$typ`')
|
||||
}
|
||||
@ -1829,7 +1848,7 @@ fn (p mut Parser) var_expr(v Var) string {
|
||||
if p.base_type(typ).starts_with('fn ') && p.tok == .lpar {
|
||||
T := p.table.find_type(p.base_type(typ))
|
||||
p.gen('(')
|
||||
p.fn_call_args(mut T.func)
|
||||
p.fn_call_args(mut T.func, [])
|
||||
p.gen(')')
|
||||
typ = T.func.typ
|
||||
}
|
||||
@ -1839,7 +1858,7 @@ fn (p mut Parser) var_expr(v Var) string {
|
||||
if p.base_type(typ).starts_with('fn ') && p.tok == .lpar {
|
||||
T := p.table.find_type(p.base_type(typ))
|
||||
p.gen('(')
|
||||
p.fn_call_args(mut T.func)
|
||||
p.fn_call_args(mut T.func, [])
|
||||
p.gen(')')
|
||||
typ = T.func.typ
|
||||
}
|
||||
@ -2014,7 +2033,7 @@ pub:
|
||||
p.gen('$dot$field.name')
|
||||
p.gen('(')
|
||||
p.check(.name)
|
||||
p.fn_call_args(mut f)
|
||||
p.fn_call_args(mut f, [])
|
||||
p.gen(')')
|
||||
return f.typ
|
||||
}
|
||||
@ -2915,7 +2934,7 @@ fn (p mut Parser) attribute() {
|
||||
return
|
||||
}
|
||||
else if p.tok == .key_struct {
|
||||
p.struct_decl()
|
||||
p.struct_decl([])
|
||||
p.attr = ''
|
||||
return
|
||||
}
|
||||
|
@ -7,9 +7,8 @@ import (
|
||||
strings
|
||||
)
|
||||
// also unions and interfaces
|
||||
|
||||
|
||||
fn (p mut Parser) struct_decl() {
|
||||
fn (p mut Parser) struct_decl(generic_param_types []string) {
|
||||
decl_tok_idx := p.cur_tok_index()
|
||||
is_pub := p.tok == .key_pub
|
||||
if is_pub {
|
||||
p.next()
|
||||
@ -40,6 +39,31 @@ fn (p mut Parser) struct_decl() {
|
||||
if is_interface && !name.ends_with('er') {
|
||||
p.error('interface names temporarily have to end with `er` (e.g. `Speaker`, `Reader`)')
|
||||
}
|
||||
|
||||
mut generic_types := map[string]string
|
||||
mut is_generic := false
|
||||
if p.tok == .lt {
|
||||
p.check(.lt)
|
||||
mut i := 0
|
||||
for {
|
||||
if generic_param_types.len > 0 && i != generic_param_types.len-1 {
|
||||
p.error('mismatched generic type params')
|
||||
}
|
||||
type_param := p.check_name()
|
||||
if generic_param_types.len > 0 {
|
||||
generic_types[type_param] = generic_param_types[i]
|
||||
} else {
|
||||
generic_types[type_param] = ''
|
||||
}
|
||||
if p.tok != .comma { break }
|
||||
p.check(.comma)
|
||||
i++
|
||||
}
|
||||
p.check(.gt)
|
||||
is_generic = true
|
||||
}
|
||||
is_generic_instance := is_generic && generic_param_types.len > 0
|
||||
|
||||
is_c := name == 'C' && p.tok == .dot
|
||||
if is_c {
|
||||
/*
|
||||
@ -81,6 +105,11 @@ fn (p mut Parser) struct_decl() {
|
||||
kind := if is_union { 'union' } else { 'struct' }
|
||||
p.gen_typedef('typedef $kind $name $name;')
|
||||
}
|
||||
parser_idx := p.v.get_file_parser_index(p.file_path) or { 0 }
|
||||
//if !p.scanner.is_vh {
|
||||
// parser_idx = p.v.get_file_parser_index(p.file_path) or { panic('cant find parser idx for $p.file_path') }
|
||||
// println('HERE: $parser_idx')
|
||||
//}
|
||||
// Register the type
|
||||
mut is_ph := false
|
||||
if typ.is_placeholder {
|
||||
@ -93,6 +122,9 @@ fn (p mut Parser) struct_decl() {
|
||||
typ.cat = cat
|
||||
typ.parent = objc_parent
|
||||
typ.is_public = is_pub || p.is_vh
|
||||
typ.is_generic = is_generic && !is_generic_instance
|
||||
typ.decl_tok_idx = decl_tok_idx
|
||||
typ.parser_idx = parser_idx
|
||||
p.table.rewrite_type(typ)
|
||||
}
|
||||
else {
|
||||
@ -103,6 +135,9 @@ fn (p mut Parser) struct_decl() {
|
||||
cat: cat
|
||||
parent: objc_parent
|
||||
is_public: is_pub || p.is_vh
|
||||
is_generic: is_generic && !is_generic_instance
|
||||
decl_tok_idx: decl_tok_idx
|
||||
parser_idx: parser_idx
|
||||
}
|
||||
}
|
||||
// Struct `C.Foo` declaration, no body
|
||||
@ -110,6 +145,18 @@ fn (p mut Parser) struct_decl() {
|
||||
p.table.register_type(typ)
|
||||
return
|
||||
}
|
||||
// generic template
|
||||
if is_generic && !is_generic_instance {
|
||||
p.table.register_type(typ)
|
||||
p.table.generic_struct_params[typ.name] = generic_types.keys()
|
||||
//}
|
||||
// TODO: re are skipping genrcic struct in gen (cgen.v) we can go as normal and remove this
|
||||
p.skip_block(false)
|
||||
return
|
||||
}
|
||||
if is_generic_instance {
|
||||
typ.rename_generic_struct(generic_types)
|
||||
}
|
||||
p.fspace()
|
||||
p.check(.lcbr)
|
||||
// Struct fields
|
||||
@ -119,7 +166,7 @@ fn (p mut Parser) struct_decl() {
|
||||
mut names := []string // to avoid dup names TODO alloc perf
|
||||
mut fmt_max_len := p.table.max_field_len[name]
|
||||
// println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass')
|
||||
if !is_ph && p.first_pass() {
|
||||
if (!is_ph && p.first_pass()) || is_generic {
|
||||
p.table.register_type(typ)
|
||||
// println('registering 1 nrfields=$typ.fields.len')
|
||||
}
|
||||
@ -206,6 +253,12 @@ fn (p mut Parser) struct_decl() {
|
||||
// `pub` access mod
|
||||
// access_mod := if is_pub_field { AccessMod.public } else { AccessMod.private}
|
||||
p.fspace()
|
||||
defer { if is_generic_instance { p.generic_dispatch=TypeInst{} } }
|
||||
if is_generic_instance {
|
||||
p.generic_dispatch=TypeInst{
|
||||
inst: generic_types
|
||||
}
|
||||
}
|
||||
tt := p.get_type2()
|
||||
field_type := tt.name
|
||||
if field_type == name {
|
||||
@ -260,8 +313,9 @@ fn (p mut Parser) struct_decl() {
|
||||
}
|
||||
did_gen_something = true
|
||||
is_mut := access_mod in [.private_mut, .public_mut, .global]
|
||||
if p.first_pass() {
|
||||
p.table.add_field(typ.name, field_name, field_type, is_mut, attr, access_mod)
|
||||
if p.first_pass() || is_generic {
|
||||
p.table.add_field(typ.name, field_name, field_type, is_mut,
|
||||
attr, access_mod)
|
||||
}
|
||||
p.fgen_nl() // newline between struct fields
|
||||
}
|
||||
@ -278,13 +332,34 @@ fn (p mut Parser) struct_decl() {
|
||||
// p.fgenln('//kek')
|
||||
}
|
||||
// `User{ foo: bar }`
|
||||
fn (p mut Parser) struct_init(typ string) string {
|
||||
fn (p mut Parser) struct_init(typ_ string) string {
|
||||
p.is_struct_init = true
|
||||
t := p.table.find_type(typ)
|
||||
mut typ := typ_
|
||||
mut t := p.table.find_type(typ)
|
||||
if !t.is_public && t.mod != p.mod {
|
||||
p.warn('type `$t.name` is private')
|
||||
}
|
||||
if p.gen_struct_init(typ, t) {
|
||||
|
||||
// generic struct init
|
||||
if p.peek() == .lt {
|
||||
p.next()
|
||||
p.check(.lt)
|
||||
mut type_params := []string
|
||||
for {
|
||||
mut type_param := p.check_name()
|
||||
if type_param in p.generic_dispatch.inst {
|
||||
type_param = p.generic_dispatch.inst[type_param]
|
||||
}
|
||||
type_params << type_param
|
||||
if p.tok != .comma { break }
|
||||
p.check(.comma)
|
||||
}
|
||||
p.dispatch_generic_struct(mut t, type_params)
|
||||
t = p.table.find_type(t.name)
|
||||
typ = t.name
|
||||
}
|
||||
|
||||
if p.gen_struct_init(typ, &t) {
|
||||
return typ
|
||||
}
|
||||
ptr := typ.contains('*')
|
||||
@ -416,3 +491,41 @@ fn (p mut Parser) struct_init(typ string) string {
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (t mut Type) rename_generic_struct(generic_types map[string]string) {
|
||||
t.name = t.name + '_T'
|
||||
for _, v in generic_types {
|
||||
t.name = t.name + '_' + type_to_safe_str(v)
|
||||
}
|
||||
}
|
||||
|
||||
fn (p mut Parser) dispatch_generic_struct(t mut Type, type_params []string) {
|
||||
mut generic_types := map[string]string
|
||||
if t.name in p.table.generic_struct_params {
|
||||
mut i := 0
|
||||
for _,v in p.table.generic_struct_params[t.name] {
|
||||
generic_types[v] = type_params[i]
|
||||
i++
|
||||
}
|
||||
t.rename_generic_struct(generic_types)
|
||||
if p.table.known_type(t.name) {
|
||||
return
|
||||
}
|
||||
p.cgen.typedefs << 'typedef struct $t.name $t.name;\n'
|
||||
}
|
||||
|
||||
mut gp := p.v.parsers[t.parser_idx]
|
||||
gp.is_vgen = true
|
||||
saved_state := p.save_state()
|
||||
p.clear_state(false, true)
|
||||
gp.token_idx = t.decl_tok_idx
|
||||
// FIXME: TODO: why are tokens cleared?
|
||||
if gp.tokens.len == 0 {
|
||||
gp.scanner.pos = 0
|
||||
gp.scan_tokens()
|
||||
}
|
||||
gp.next()
|
||||
gp.struct_decl(type_params)
|
||||
p.cgen.lines_extra << p.cgen.lines
|
||||
p.restore_state(saved_state, false, true)
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ pub mut:
|
||||
// enum_vals map[string][]string
|
||||
// names []Name
|
||||
max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12
|
||||
generic_struct_params map[string][]string
|
||||
}
|
||||
|
||||
struct VargAccess {
|
||||
@ -114,6 +115,8 @@ pub mut:
|
||||
enum_vals []string
|
||||
gen_types []string
|
||||
default_vals []string // `struct Foo { bar int = 2 }`
|
||||
parser_idx int
|
||||
decl_tok_idx int
|
||||
// `is_placeholder` is used for types that are not defined yet but are known to exist.
|
||||
// It allows having things like `fn (f Foo) bar()` before `Foo` is defined.
|
||||
// This information is needed in the first pass.
|
||||
@ -121,6 +124,7 @@ pub mut:
|
||||
gen_str bool // needs `.str()` method generation
|
||||
is_flag bool // enum bitfield flag
|
||||
// max_field_len int
|
||||
is_generic bool
|
||||
}
|
||||
|
||||
struct TypeNode {
|
||||
|
@ -98,3 +98,34 @@ fn test_generic_fn_in_for_in_expression() {
|
||||
assert value == 'a'
|
||||
}
|
||||
}
|
||||
|
||||
// test generic struct
|
||||
struct DB {
|
||||
driver string
|
||||
}
|
||||
|
||||
struct User {
|
||||
db DB
|
||||
mut:
|
||||
name string
|
||||
}
|
||||
|
||||
struct Repo<T> {
|
||||
db DB
|
||||
mut:
|
||||
model T
|
||||
}
|
||||
|
||||
fn new_repo<U>(db DB) Repo<U> {
|
||||
return Repo<U>{db: db}
|
||||
}
|
||||
|
||||
fn test_generic_struct() {
|
||||
mut a := new_repo<User>(DB{})
|
||||
a.model.name = 'joe'
|
||||
mut b := Repo<User>{db: DB{}}
|
||||
b.model.name = 'joe'
|
||||
assert a.model.name == 'joe'
|
||||
assert b.model.name == 'joe'
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user