generic structs: initial implementation
parent
76176eddab
commit
ab37dcaa9c
|
@ -14,6 +14,7 @@ pub fn (mut b Builder) gen_c(v_files []string) string {
|
|||
parse_time := t1 - t0
|
||||
b.info('PARSE: ${parse_time}ms')
|
||||
//
|
||||
b.instantiate_generic_structs()
|
||||
b.checker.check_files(b.parsed_files)
|
||||
t2 := time.ticks()
|
||||
check_time := t2 - t1
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
module builder
|
||||
|
||||
import v.table
|
||||
|
||||
pub fn (b &Builder) instantiate_generic_structs() {
|
||||
for idx, _ in b.table.types {
|
||||
mut typ := &b.table.types[idx]
|
||||
if typ.kind == .generic_struct_instance {
|
||||
info := typ.info as table.GenericStructInstance
|
||||
parent := b.table.types[info.parent_idx]
|
||||
mut parent_info := *(parent.info as table.Struct)
|
||||
mut fields := parent_info.fields.clone()
|
||||
for i, _ in parent_info.fields {
|
||||
mut field := fields[i]
|
||||
if field.typ.has_flag(.generic) {
|
||||
if parent_info.generic_types.len != info.generic_types.len {
|
||||
// TODO: proper error
|
||||
panic('generic template mismatch')
|
||||
}
|
||||
for j, gp in parent_info.generic_types {
|
||||
if gp == field.typ {
|
||||
field.typ = info.generic_types[j]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
fields[i] = field
|
||||
}
|
||||
parent_info.generic_types = []
|
||||
typ.is_public = true
|
||||
typ.kind = .struct_
|
||||
typ.info = {parent_info| fields: fields}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,9 +53,10 @@ pub fn (c &Checker) check_basic(got, expected table.Type) bool {
|
|||
(exp_idx == table.char_type_idx && got_idx == table.charptr_type_idx) {
|
||||
return true
|
||||
}
|
||||
if expected == table.t_type && got == table.t_type {
|
||||
return true
|
||||
}
|
||||
// TODO: this should no longer be needed
|
||||
// if expected == table.t_type && got == table.t_type {
|
||||
// return true
|
||||
// }
|
||||
// # NOTE: use symbols from this point on for perf
|
||||
got_type_sym := t.get_type_symbol(got)
|
||||
exp_type_sym := t.get_type_symbol(expected)
|
||||
|
|
|
@ -743,7 +743,7 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e
|
|||
|
||||
pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||
left_type := c.expr(call_expr.left)
|
||||
is_generic := left_type == table.t_type
|
||||
is_generic := left_type.has_flag(.generic)
|
||||
call_expr.left_type = left_type
|
||||
left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type))
|
||||
method_name := call_expr.name
|
||||
|
@ -886,7 +886,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
}
|
||||
if is_generic {
|
||||
// We need the receiver to be T in cgen.
|
||||
call_expr.receiver_type = table.t_type.derive(method.args[0].typ)
|
||||
// TODO: cant we just set all these to the concrete type in checker? then no need in gen
|
||||
call_expr.receiver_type = left_type.derive(method.args[0].typ).set_flag(.generic)
|
||||
} else {
|
||||
call_expr.receiver_type = method.args[0].typ
|
||||
}
|
||||
|
@ -932,7 +933,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
// TODO: impl typeof properly (probably not going to be a fn call)
|
||||
return table.string_type
|
||||
}
|
||||
if call_expr.generic_type == table.t_type {
|
||||
if call_expr.generic_type.has_flag(.generic) {
|
||||
if c.mod != '' && c.mod != 'main' {
|
||||
// Need to prepend the module when adding a generic type to a function
|
||||
// `fn_gen_types['mymod.myfn'] == ['string', 'int']`
|
||||
|
@ -1015,7 +1016,28 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
if f.is_deprecated {
|
||||
c.warn('function `$f.name` has been deprecated', call_expr.pos)
|
||||
}
|
||||
call_expr.return_type = f.return_type
|
||||
if f.is_generic {
|
||||
rts := c.table.get_type_symbol(f.return_type)
|
||||
if rts.kind == .struct_ {
|
||||
rts_info := rts.info as table.Struct
|
||||
if rts_info.generic_types.len > 0 {
|
||||
// TODO: multiple generic types
|
||||
// for gt in rts_info.generic_types {
|
||||
// gtss := c.table.get_type_symbol(gt)
|
||||
// }
|
||||
gts := c.table.get_type_symbol(call_expr.generic_type)
|
||||
nrt := '${rts.name}<$gts.name>'
|
||||
idx := c.table.type_idxs[nrt]
|
||||
if idx == 0 {
|
||||
c.error('unknown type: $nrt', call_expr.pos)
|
||||
}
|
||||
call_expr.return_type = table.new_type(idx).derive(f.return_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
call_expr.return_type = f.return_type
|
||||
}
|
||||
if f.return_type == table.void_type &&
|
||||
f.ctdefine.len > 0 && f.ctdefine !in c.pref.compile_defines {
|
||||
call_expr.should_be_skipped = true
|
||||
|
@ -1123,6 +1145,9 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
}
|
||||
}
|
||||
}
|
||||
if f.is_generic {
|
||||
return call_expr.return_type
|
||||
}
|
||||
return f.return_type
|
||||
}
|
||||
|
||||
|
@ -1899,9 +1924,9 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
|||
|
||||
[inline]
|
||||
pub fn (c &Checker) unwrap_generic(typ table.Type) table.Type {
|
||||
if typ.idx() == table.t_type_idx {
|
||||
if typ.has_flag(.generic) {
|
||||
// return c.cur_generic_type
|
||||
return c.cur_generic_type.derive(typ)
|
||||
return c.cur_generic_type.derive(typ).clear_flag(.generic)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
vlib/v/checker/tests/match_expr_else.v:4:9: error: cannot cast a string
|
||||
vlib/v/checker/tests/match_expr_else.v:4:10: error: cannot cast a string
|
||||
2 |
|
||||
3 | fn main() {
|
||||
4 | x := A('test')
|
||||
| ~~~~~~
|
||||
4 | x := AA('test')
|
||||
| ~~~~~~
|
||||
5 | _ = match x {
|
||||
6 | int {
|
||||
vlib/v/checker/tests/match_expr_else.v:5:6: error: match must be exhaustive (add match branches for: `f64` or `else {}` at the end)
|
||||
3 | fn main() {
|
||||
4 | x := A('test')
|
||||
4 | x := AA('test')
|
||||
5 | _ = match x {
|
||||
| ~~~~~~~~~
|
||||
6 | int {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
type A = int | string | f64
|
||||
type AA = int | string | f64
|
||||
|
||||
fn main() {
|
||||
x := A('test')
|
||||
x := AA('test')
|
||||
_ = match x {
|
||||
int {
|
||||
'int'
|
||||
|
|
|
@ -392,6 +392,26 @@ fn (mut g Gen) register_optional(t table.Type) string {
|
|||
fn (g &Gen) cc_type(t table.Type) string {
|
||||
sym := g.table.get_type_symbol(g.unwrap_generic(t))
|
||||
mut styp := sym.name.replace('.', '__')
|
||||
if sym.kind == .struct_ {
|
||||
// TODO: maybe keep c name in info ( this is yuck )
|
||||
info := sym.info as table.Struct
|
||||
if info.generic_types.len > 0 {
|
||||
mut sgts := '_T'
|
||||
for gt in info.generic_types {
|
||||
gts := g.table.get_type_symbol(if gt.has_flag(.generic) {
|
||||
g.unwrap_generic(gt)
|
||||
} else {
|
||||
gt
|
||||
})
|
||||
sgts += '_$gts.name'
|
||||
}
|
||||
styp += sgts
|
||||
}
|
||||
else {
|
||||
// TODO: maybe keep c name in info ( this is yuck )
|
||||
styp = styp.replace('<', '_T_').replace('>', '').replace(',', '_')
|
||||
}
|
||||
}
|
||||
if styp.starts_with('C__') {
|
||||
styp = styp[3..]
|
||||
if sym.kind == .struct_ {
|
||||
|
@ -2815,10 +2835,17 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
|
|||
continue
|
||||
}
|
||||
// sym := g.table.get_type_symbol(typ)
|
||||
name := typ.name.replace('.', '__')
|
||||
match typ.info {
|
||||
mut name := typ.name.replace('.', '__')
|
||||
match typ.info as info {
|
||||
table.Struct {
|
||||
info := typ.info as table.Struct
|
||||
if info.generic_types.len > 0 {
|
||||
continue
|
||||
}
|
||||
// TODO: maybe keep c name in info ( this is yuck )
|
||||
name = name.replace('<', '_T_').replace('>', '').replace(',', '_')
|
||||
if name.contains('_T_') {
|
||||
g.typedefs.writeln('typedef struct $name $name;')
|
||||
}
|
||||
// TODO avoid buffer manip
|
||||
start_pos := g.type_definitions.len
|
||||
if info.is_union {
|
||||
|
|
|
@ -82,7 +82,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
|||
// foo<T>() => foo_int(), foo_string() etc
|
||||
gen_name := g.typ(g.cur_generic_type)
|
||||
name += '_' + gen_name
|
||||
type_name = type_name.replace('T', gen_name)
|
||||
// type_name = type_name.replace('T', gen_name)
|
||||
}
|
||||
// if g.pref.show_cc && it.is_builtin {
|
||||
// println(name)
|
||||
|
@ -339,9 +339,9 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
|||
|
||||
[inline]
|
||||
pub fn (g &Gen) unwrap_generic(typ table.Type) table.Type {
|
||||
if typ.idx() == table.t_type_idx {
|
||||
if typ.has_flag(.generic) {
|
||||
// return g.cur_generic_type
|
||||
return g.cur_generic_type.derive(typ)
|
||||
return g.cur_generic_type.derive(typ).clear_flag(.generic)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
|
|
@ -238,6 +238,7 @@ pub fn (mut g JsGen) typ(t table.Type) string {
|
|||
.struct_ {
|
||||
styp = g.struct_typ(sym.name)
|
||||
}
|
||||
.generic_struct_instance {}
|
||||
// 'multi_return_int_int' => '[number, number]'
|
||||
.multi_return {
|
||||
info := sym.info as table.MultiReturn
|
||||
|
|
|
@ -34,7 +34,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
|||
p.check(.gt) // `>`
|
||||
// In case of `foo<T>()`
|
||||
// T is unwrapped and registered in the checker.
|
||||
if generic_type != table.t_type {
|
||||
if !generic_type.has_flag(.generic) {
|
||||
p.table.register_fn_gen_type(fn_name, generic_type)
|
||||
}
|
||||
}
|
||||
|
@ -391,7 +391,7 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) {
|
|||
pos := p.tok.position()
|
||||
mut arg_type := p.parse_type()
|
||||
if is_mut {
|
||||
if arg_type != table.t_type {
|
||||
if !arg_type.has_flag(.generic) {
|
||||
p.check_fn_mutable_arguments(arg_type, pos)
|
||||
}
|
||||
// if arg_type.is_ptr() {
|
||||
|
@ -444,7 +444,7 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) {
|
|||
pos := p.tok.position()
|
||||
mut typ := p.parse_type()
|
||||
if is_mut {
|
||||
if typ != table.t_type {
|
||||
if !typ.has_flag(.generic) {
|
||||
p.check_fn_mutable_arguments(typ, pos)
|
||||
}
|
||||
typ = typ.set_nr_muls(1)
|
||||
|
|
|
@ -246,18 +246,82 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool) table
|
|||
return table.bool_type
|
||||
}
|
||||
else {
|
||||
// struct / enum / placeholder
|
||||
// struct / enum
|
||||
mut idx := p.table.find_type_idx(name)
|
||||
if idx > 0 {
|
||||
return table.new_type(idx)
|
||||
if name.len == 1 && name[0].is_capital() {
|
||||
return p.parse_generic_template_type(name)
|
||||
}
|
||||
// not found - add placeholder
|
||||
idx = p.table.add_placeholder_type(name)
|
||||
// println('NOT FOUND: $name - adding placeholder - $idx')
|
||||
return table.new_type(idx)
|
||||
if p.peek_tok.kind == .lt {
|
||||
return p.parse_generic_struct_inst_type(name)
|
||||
}
|
||||
return p.parse_enum_or_struct_type(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) parse_enum_or_struct_type(name string) table.Type {
|
||||
// struct / enum / placeholder
|
||||
// struct / enum
|
||||
mut idx := p.table.find_type_idx(name)
|
||||
if idx > 0 {
|
||||
return table.new_type(idx)
|
||||
}
|
||||
// not found - add placeholder
|
||||
idx = p.table.add_placeholder_type(name)
|
||||
// println('NOT FOUND: $name - adding placeholder - $idx')
|
||||
return table.new_type(idx)
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) parse_generic_template_type(name string) table.Type {
|
||||
mut idx := p.table.find_type_idx(name)
|
||||
if idx > 0 {
|
||||
return table.new_type(idx).set_flag(.generic)
|
||||
}
|
||||
idx = p.table.register_type_symbol(table.TypeSymbol{
|
||||
name: name
|
||||
kind: .any
|
||||
is_public: true
|
||||
})
|
||||
return table.new_type(idx).set_flag(.generic)
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) parse_generic_struct_inst_type(name string) table.Type {
|
||||
mut bs_name := name
|
||||
p.next()
|
||||
bs_name += '<'
|
||||
mut generic_types := []table.Type{}
|
||||
mut is_instance := false
|
||||
for {
|
||||
gt := p.parse_type()
|
||||
if !gt.has_flag(.generic) {
|
||||
is_instance = true
|
||||
}
|
||||
gts := p.table.get_type_symbol(gt)
|
||||
bs_name += gts.name
|
||||
generic_types << gt
|
||||
if p.tok.kind != .comma {
|
||||
break
|
||||
}
|
||||
p.next()
|
||||
bs_name += ','
|
||||
}
|
||||
p.check(.gt)
|
||||
bs_name += '>'
|
||||
if is_instance && generic_types.len > 0 {
|
||||
mut gt_idx := p.table.find_type_idx(bs_name)
|
||||
if gt_idx > 0 {
|
||||
return table.new_type(gt_idx)
|
||||
}
|
||||
gt_idx = p.table.add_placeholder_type(bs_name)
|
||||
idx := p.table.register_type_symbol(table.TypeSymbol{
|
||||
kind: .generic_struct_instance
|
||||
name: bs_name
|
||||
info: table.GenericStructInstance{
|
||||
parent_idx: p.table.type_idxs[name]
|
||||
generic_types: generic_types
|
||||
}
|
||||
})
|
||||
return table.new_type(idx)
|
||||
}
|
||||
return p.parse_enum_or_struct_type(name)
|
||||
}
|
||||
|
|
|
@ -876,10 +876,12 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
p.check(.dot)
|
||||
p.expr_mod = mod
|
||||
}
|
||||
lit0_is_capital := p.tok.lit[0].is_capital()
|
||||
// p.warn('name expr $p.tok.lit $p.peek_tok.str()')
|
||||
// fn call or type cast
|
||||
if p.peek_tok.kind == .lpar ||
|
||||
(p.peek_tok.kind == .lt && p.peek_tok2.kind == .name && p.peek_tok3.kind == .gt) {
|
||||
(p.peek_tok.kind == .lt && !lit0_is_capital && p.peek_tok2.kind == .name &&
|
||||
p.peek_tok3.kind == .gt) {
|
||||
// foo() or foo<int>()
|
||||
mut name := p.tok.lit
|
||||
if mod.len > 0 {
|
||||
|
@ -922,10 +924,10 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
// println('calling $p.tok.lit')
|
||||
node = p.call_expr(language, mod)
|
||||
}
|
||||
} else if p.peek_tok.kind == .lcbr && !p.inside_match && !p.inside_match_case && !p.inside_if &&
|
||||
} else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital)) && !p.inside_match && !p.inside_match_case && !p.inside_if &&
|
||||
!p.inside_for { // && (p.tok.lit[0].is_capital() || p.builtin_mod) {
|
||||
return p.struct_init(false) // short_syntax: false
|
||||
} else if p.peek_tok.kind == .dot && (p.tok.lit[0].is_capital() && !known_var && language == .v) {
|
||||
} else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) {
|
||||
// `Color.green`
|
||||
mut enum_name := p.check_name()
|
||||
if mod != '' {
|
||||
|
@ -1523,6 +1525,9 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
|||
end_pos := p.tok.position()
|
||||
decl_pos := start_pos.extend(end_pos)
|
||||
name := p.check_name()
|
||||
if name.len == 1 && name[0].is_capital() {
|
||||
p.error_with_pos('single letter capital names are reserved for generic template types.', decl_pos)
|
||||
}
|
||||
mut sum_variants := []table.Type{}
|
||||
if p.tok.kind == .assign {
|
||||
p.next() // TODO require `=`
|
||||
|
|
|
@ -32,13 +32,31 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
p.next() // C || JS
|
||||
p.next() // .
|
||||
}
|
||||
|
||||
is_typedef := 'typedef' in p.attrs
|
||||
no_body := p.peek_tok.kind != .lcbr
|
||||
end_pos := p.tok.position()
|
||||
mut name := p.check_name()
|
||||
if name.len == 1 && name[0].is_capital() {
|
||||
p.error_with_pos('single letter capital names are reserved for generic template types.', end_pos)
|
||||
}
|
||||
mut generic_types := []table.Type{}
|
||||
if p.tok.kind == .lt {
|
||||
p.next()
|
||||
for {
|
||||
generic_types << p.parse_type()
|
||||
if p.tok.kind != .comma {
|
||||
break
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
p.check(.gt)
|
||||
}
|
||||
|
||||
no_body := p.tok.kind != .lcbr
|
||||
if language == .v && no_body {
|
||||
p.error('`$p.tok.lit` lacks body')
|
||||
}
|
||||
end_pos := p.tok.position()
|
||||
mut name := p.check_name()
|
||||
|
||||
if language == .v && p.mod != 'builtin' && name.len > 0 && !name[0].is_capital() {
|
||||
p.error_with_pos('struct name `$name` must begin with capital letter', end_pos)
|
||||
}
|
||||
|
@ -215,6 +233,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
is_typedef: is_typedef
|
||||
is_union: is_union
|
||||
is_ref_only: 'ref_only' in p.attrs
|
||||
generic_types: generic_types
|
||||
}
|
||||
mod: p.mod
|
||||
is_public: is_pub
|
||||
|
|
|
@ -16,7 +16,7 @@ import strings
|
|||
pub type Type int
|
||||
|
||||
pub type TypeInfo = Alias | Array | ArrayFixed | Enum | FnType | Interface | Map | MultiReturn |
|
||||
Struct | SumType
|
||||
Struct | GenericStructInstance | SumType
|
||||
|
||||
pub enum Language {
|
||||
v
|
||||
|
@ -134,7 +134,7 @@ pub fn (t Type) derive(t_from Type) Type {
|
|||
[inline]
|
||||
pub fn new_type(idx int) Type {
|
||||
if idx < 1 || idx > 65535 {
|
||||
panic('new_type_id: idx must be between 1 & 65535')
|
||||
panic('new_type: idx must be between 1 & 65535')
|
||||
}
|
||||
return idx
|
||||
}
|
||||
|
@ -215,9 +215,9 @@ pub const (
|
|||
array_type_idx = 20
|
||||
map_type_idx = 21
|
||||
any_type_idx = 22
|
||||
t_type_idx = 23
|
||||
any_flt_type_idx = 24
|
||||
any_int_type_idx = 25
|
||||
// t_type_idx = 23
|
||||
any_flt_type_idx = 23
|
||||
any_int_type_idx = 24
|
||||
)
|
||||
|
||||
pub const (
|
||||
|
@ -267,7 +267,7 @@ pub const (
|
|||
array_type = new_type(array_type_idx)
|
||||
map_type = new_type(map_type_idx)
|
||||
any_type = new_type(any_type_idx)
|
||||
t_type = new_type(t_type_idx)
|
||||
// t_type = new_type(t_type_idx)
|
||||
any_flt_type = new_type(any_flt_type_idx)
|
||||
any_int_type = new_type(any_int_type_idx)
|
||||
)
|
||||
|
@ -321,6 +321,7 @@ pub enum Kind {
|
|||
map
|
||||
any
|
||||
struct_
|
||||
generic_struct_instance
|
||||
multi_return
|
||||
sum_type
|
||||
alias
|
||||
|
@ -505,12 +506,12 @@ pub fn (mut t Table) register_builtin_type_symbols() {
|
|||
name: 'any'
|
||||
mod: 'builtin'
|
||||
})
|
||||
t.register_type_symbol({
|
||||
kind: .any
|
||||
name: 'T'
|
||||
mod: 'builtin'
|
||||
is_public: true
|
||||
})
|
||||
// t.register_type_symbol({
|
||||
// kind: .any
|
||||
// name: 'T'
|
||||
// mod: 'builtin'
|
||||
// is_public: true
|
||||
// })
|
||||
t.register_type_symbol({
|
||||
kind: .any_float
|
||||
name: 'any_float'
|
||||
|
@ -615,10 +616,17 @@ pub fn (kinds []Kind) str() string {
|
|||
|
||||
pub struct Struct {
|
||||
pub mut:
|
||||
fields []Field
|
||||
is_typedef bool // C. [typedef]
|
||||
is_union bool
|
||||
is_ref_only bool
|
||||
fields []Field
|
||||
is_typedef bool // C. [typedef]
|
||||
is_union bool
|
||||
is_ref_only bool
|
||||
generic_types []Type
|
||||
}
|
||||
|
||||
pub struct GenericStructInstance {
|
||||
pub mut:
|
||||
parent_idx int
|
||||
generic_types []Type
|
||||
}
|
||||
|
||||
pub struct Interface {
|
||||
|
|
|
@ -40,22 +40,22 @@ fn test_foo() {
|
|||
|
||||
fn create<T>() {
|
||||
a := T{}
|
||||
println(a.foo)
|
||||
println(a.name)
|
||||
mut xx := T{}
|
||||
xx.foo = 'foo'
|
||||
println(xx.foo)
|
||||
assert xx.foo == 'foo'
|
||||
xx.name = 'foo'
|
||||
println(xx.name)
|
||||
assert xx.name == 'foo'
|
||||
xx.init()
|
||||
}
|
||||
|
||||
struct User {
|
||||
mut:
|
||||
foo string
|
||||
name string
|
||||
}
|
||||
|
||||
struct City {
|
||||
mut:
|
||||
foo string
|
||||
name string
|
||||
}
|
||||
|
||||
fn (u User) init() {
|
||||
|
@ -65,12 +65,12 @@ fn (c City) init() {
|
|||
}
|
||||
|
||||
fn mut_arg<T>(mut x T) {
|
||||
println(x.foo) // = 'foo'
|
||||
println(x.name) // = 'foo'
|
||||
}
|
||||
|
||||
|
||||
fn mut_arg2<T>(mut x T) T {
|
||||
println(x.foo) // = 'foo'
|
||||
println(x.name) // = 'foo'
|
||||
return x
|
||||
}
|
||||
|
||||
|
@ -182,40 +182,65 @@ fn test_generic_fn_in_for_in_expression() {
|
|||
assert value == 'a'
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// test generic struct
|
||||
struct DB {
|
||||
driver string
|
||||
}
|
||||
|
||||
struct User {
|
||||
db DB
|
||||
mut:
|
||||
struct Group {
|
||||
pub mut:
|
||||
name string
|
||||
group_name string
|
||||
}
|
||||
|
||||
struct Permission {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
struct Repo<T> {
|
||||
struct Repo<T,U> {
|
||||
db DB
|
||||
mut:
|
||||
pub mut:
|
||||
model T
|
||||
permission U
|
||||
}
|
||||
|
||||
fn new_repo<U>(db DB) Repo<U> {
|
||||
return Repo<U>{db: db}
|
||||
}
|
||||
// TODO: multiple type generic struct needs fixing in return for fn
|
||||
// fn new_repo<T>(db DB) Repo<T,U> {
|
||||
// return Repo<T,Permission>{db: db}
|
||||
// }
|
||||
|
||||
|
||||
fn test_generic_struct() {
|
||||
mut a := new_repo<User>(DB{})
|
||||
a.model.name = 'joe'
|
||||
mut b := Repo<User>{db: DB{}
|
||||
mut a := Repo<User,Permission>{
|
||||
model: User{name: 'joe'}
|
||||
}
|
||||
b.model.name = 'joe'
|
||||
// a.model.name = 'joe'
|
||||
assert a.model.name == 'joe'
|
||||
assert b.model.name == 'joe'
|
||||
println('a.model.name: $a.model.name')
|
||||
|
||||
mut b := Repo<Group,Permission>{
|
||||
permission: Permission{name: 'superuser'}
|
||||
}
|
||||
b.model.name = 'admins'
|
||||
assert b.model.name == 'admins'
|
||||
assert b.permission.name == 'superuser'
|
||||
println('b.model.name: $b.model.name')
|
||||
println('b.permission.name: $b.permission.name')
|
||||
|
||||
assert typeof(a.model) == 'User'
|
||||
assert typeof(b.model) == 'Group'
|
||||
println('typeof(a.model): ' + typeof(a.model))
|
||||
println('typeof(b.model): ' + typeof(b.model))
|
||||
|
||||
// mut x := new_repo<User>(DB{})
|
||||
// x.model.name = 'joe2'
|
||||
// println(x.model.name)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/*
|
||||
struct Abc{ x int y int z int }
|
||||
|
||||
fn p<T>(args ...T) {
|
||||
|
|
Loading…
Reference in New Issue