mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
generic structs: initial implementation
This commit is contained in:
parent
76176eddab
commit
ab37dcaa9c
|
@ -14,6 +14,7 @@ pub fn (mut b Builder) gen_c(v_files []string) string {
|
||||||
parse_time := t1 - t0
|
parse_time := t1 - t0
|
||||||
b.info('PARSE: ${parse_time}ms')
|
b.info('PARSE: ${parse_time}ms')
|
||||||
//
|
//
|
||||||
|
b.instantiate_generic_structs()
|
||||||
b.checker.check_files(b.parsed_files)
|
b.checker.check_files(b.parsed_files)
|
||||||
t2 := time.ticks()
|
t2 := time.ticks()
|
||||||
check_time := t2 - t1
|
check_time := t2 - t1
|
||||||
|
|
35
vlib/v/builder/generics.v
Normal file
35
vlib/v/builder/generics.v
Normal file
|
@ -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) {
|
(exp_idx == table.char_type_idx && got_idx == table.charptr_type_idx) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if expected == table.t_type && got == table.t_type {
|
// TODO: this should no longer be needed
|
||||||
return true
|
// if expected == table.t_type && got == table.t_type {
|
||||||
}
|
// return true
|
||||||
|
// }
|
||||||
// # NOTE: use symbols from this point on for perf
|
// # NOTE: use symbols from this point on for perf
|
||||||
got_type_sym := t.get_type_symbol(got)
|
got_type_sym := t.get_type_symbol(got)
|
||||||
exp_type_sym := t.get_type_symbol(expected)
|
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 {
|
pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
left_type := c.expr(call_expr.left)
|
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
|
call_expr.left_type = left_type
|
||||||
left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type))
|
left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type))
|
||||||
method_name := call_expr.name
|
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 {
|
if is_generic {
|
||||||
// We need the receiver to be T in cgen.
|
// 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 {
|
} else {
|
||||||
call_expr.receiver_type = method.args[0].typ
|
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)
|
// TODO: impl typeof properly (probably not going to be a fn call)
|
||||||
return table.string_type
|
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' {
|
if c.mod != '' && c.mod != 'main' {
|
||||||
// Need to prepend the module when adding a generic type to a function
|
// Need to prepend the module when adding a generic type to a function
|
||||||
// `fn_gen_types['mymod.myfn'] == ['string', 'int']`
|
// `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 {
|
if f.is_deprecated {
|
||||||
c.warn('function `$f.name` has been deprecated', call_expr.pos)
|
c.warn('function `$f.name` has been deprecated', call_expr.pos)
|
||||||
}
|
}
|
||||||
|
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
|
call_expr.return_type = f.return_type
|
||||||
|
}
|
||||||
if f.return_type == table.void_type &&
|
if f.return_type == table.void_type &&
|
||||||
f.ctdefine.len > 0 && f.ctdefine !in c.pref.compile_defines {
|
f.ctdefine.len > 0 && f.ctdefine !in c.pref.compile_defines {
|
||||||
call_expr.should_be_skipped = true
|
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
|
return f.return_type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1899,9 +1924,9 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
pub fn (c &Checker) unwrap_generic(typ table.Type) table.Type {
|
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
|
||||||
return c.cur_generic_type.derive(typ)
|
return c.cur_generic_type.derive(typ).clear_flag(.generic)
|
||||||
}
|
}
|
||||||
return typ
|
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 |
|
2 |
|
||||||
3 | fn main() {
|
3 | fn main() {
|
||||||
4 | x := A('test')
|
4 | x := AA('test')
|
||||||
| ~~~~~~
|
| ~~~~~~
|
||||||
5 | _ = match x {
|
5 | _ = match x {
|
||||||
6 | int {
|
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)
|
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() {
|
3 | fn main() {
|
||||||
4 | x := A('test')
|
4 | x := AA('test')
|
||||||
5 | _ = match x {
|
5 | _ = match x {
|
||||||
| ~~~~~~~~~
|
| ~~~~~~~~~
|
||||||
6 | int {
|
6 | int {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
type A = int | string | f64
|
type AA = int | string | f64
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
x := A('test')
|
x := AA('test')
|
||||||
_ = match x {
|
_ = match x {
|
||||||
int {
|
int {
|
||||||
'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 {
|
fn (g &Gen) cc_type(t table.Type) string {
|
||||||
sym := g.table.get_type_symbol(g.unwrap_generic(t))
|
sym := g.table.get_type_symbol(g.unwrap_generic(t))
|
||||||
mut styp := sym.name.replace('.', '__')
|
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__') {
|
if styp.starts_with('C__') {
|
||||||
styp = styp[3..]
|
styp = styp[3..]
|
||||||
if sym.kind == .struct_ {
|
if sym.kind == .struct_ {
|
||||||
|
@ -2815,10 +2835,17 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// sym := g.table.get_type_symbol(typ)
|
// sym := g.table.get_type_symbol(typ)
|
||||||
name := typ.name.replace('.', '__')
|
mut name := typ.name.replace('.', '__')
|
||||||
match typ.info {
|
match typ.info as info {
|
||||||
table.Struct {
|
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
|
// TODO avoid buffer manip
|
||||||
start_pos := g.type_definitions.len
|
start_pos := g.type_definitions.len
|
||||||
if info.is_union {
|
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
|
// foo<T>() => foo_int(), foo_string() etc
|
||||||
gen_name := g.typ(g.cur_generic_type)
|
gen_name := g.typ(g.cur_generic_type)
|
||||||
name += '_' + gen_name
|
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 {
|
// if g.pref.show_cc && it.is_builtin {
|
||||||
// println(name)
|
// println(name)
|
||||||
|
@ -339,9 +339,9 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
pub fn (g &Gen) unwrap_generic(typ table.Type) table.Type {
|
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
|
||||||
return g.cur_generic_type.derive(typ)
|
return g.cur_generic_type.derive(typ).clear_flag(.generic)
|
||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,6 +238,7 @@ pub fn (mut g JsGen) typ(t table.Type) string {
|
||||||
.struct_ {
|
.struct_ {
|
||||||
styp = g.struct_typ(sym.name)
|
styp = g.struct_typ(sym.name)
|
||||||
}
|
}
|
||||||
|
.generic_struct_instance {}
|
||||||
// 'multi_return_int_int' => '[number, number]'
|
// 'multi_return_int_int' => '[number, number]'
|
||||||
.multi_return {
|
.multi_return {
|
||||||
info := sym.info as table.MultiReturn
|
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) // `>`
|
p.check(.gt) // `>`
|
||||||
// In case of `foo<T>()`
|
// In case of `foo<T>()`
|
||||||
// T is unwrapped and registered in the checker.
|
// 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)
|
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()
|
pos := p.tok.position()
|
||||||
mut arg_type := p.parse_type()
|
mut arg_type := p.parse_type()
|
||||||
if is_mut {
|
if is_mut {
|
||||||
if arg_type != table.t_type {
|
if !arg_type.has_flag(.generic) {
|
||||||
p.check_fn_mutable_arguments(arg_type, pos)
|
p.check_fn_mutable_arguments(arg_type, pos)
|
||||||
}
|
}
|
||||||
// if arg_type.is_ptr() {
|
// if arg_type.is_ptr() {
|
||||||
|
@ -444,7 +444,7 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) {
|
||||||
pos := p.tok.position()
|
pos := p.tok.position()
|
||||||
mut typ := p.parse_type()
|
mut typ := p.parse_type()
|
||||||
if is_mut {
|
if is_mut {
|
||||||
if typ != table.t_type {
|
if !typ.has_flag(.generic) {
|
||||||
p.check_fn_mutable_arguments(typ, pos)
|
p.check_fn_mutable_arguments(typ, pos)
|
||||||
}
|
}
|
||||||
typ = typ.set_nr_muls(1)
|
typ = typ.set_nr_muls(1)
|
||||||
|
|
|
@ -246,6 +246,20 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool) table
|
||||||
return table.bool_type
|
return table.bool_type
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if name.len == 1 && name[0].is_capital() {
|
||||||
|
return p.parse_generic_template_type(name)
|
||||||
|
}
|
||||||
|
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 / placeholder
|
||||||
// struct / enum
|
// struct / enum
|
||||||
mut idx := p.table.find_type_idx(name)
|
mut idx := p.table.find_type_idx(name)
|
||||||
|
@ -257,7 +271,57 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool) table
|
||||||
// println('NOT FOUND: $name - adding placeholder - $idx')
|
// println('NOT FOUND: $name - adding placeholder - $idx')
|
||||||
return table.new_type(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.check(.dot)
|
||||||
p.expr_mod = mod
|
p.expr_mod = mod
|
||||||
}
|
}
|
||||||
|
lit0_is_capital := p.tok.lit[0].is_capital()
|
||||||
// p.warn('name expr $p.tok.lit $p.peek_tok.str()')
|
// p.warn('name expr $p.tok.lit $p.peek_tok.str()')
|
||||||
// fn call or type cast
|
// fn call or type cast
|
||||||
if p.peek_tok.kind == .lpar ||
|
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>()
|
// foo() or foo<int>()
|
||||||
mut name := p.tok.lit
|
mut name := p.tok.lit
|
||||||
if mod.len > 0 {
|
if mod.len > 0 {
|
||||||
|
@ -922,10 +924,10 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||||
// println('calling $p.tok.lit')
|
// println('calling $p.tok.lit')
|
||||||
node = p.call_expr(language, mod)
|
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) {
|
!p.inside_for { // && (p.tok.lit[0].is_capital() || p.builtin_mod) {
|
||||||
return p.struct_init(false) // short_syntax: false
|
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`
|
// `Color.green`
|
||||||
mut enum_name := p.check_name()
|
mut enum_name := p.check_name()
|
||||||
if mod != '' {
|
if mod != '' {
|
||||||
|
@ -1523,6 +1525,9 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
||||||
end_pos := p.tok.position()
|
end_pos := p.tok.position()
|
||||||
decl_pos := start_pos.extend(end_pos)
|
decl_pos := start_pos.extend(end_pos)
|
||||||
name := p.check_name()
|
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{}
|
mut sum_variants := []table.Type{}
|
||||||
if p.tok.kind == .assign {
|
if p.tok.kind == .assign {
|
||||||
p.next() // TODO require `=`
|
p.next() // TODO require `=`
|
||||||
|
|
|
@ -32,13 +32,31 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
p.next() // C || JS
|
p.next() // C || JS
|
||||||
p.next() // .
|
p.next() // .
|
||||||
}
|
}
|
||||||
|
|
||||||
is_typedef := 'typedef' in p.attrs
|
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 {
|
if language == .v && no_body {
|
||||||
p.error('`$p.tok.lit` lacks 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() {
|
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)
|
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_typedef: is_typedef
|
||||||
is_union: is_union
|
is_union: is_union
|
||||||
is_ref_only: 'ref_only' in p.attrs
|
is_ref_only: 'ref_only' in p.attrs
|
||||||
|
generic_types: generic_types
|
||||||
}
|
}
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
is_public: is_pub
|
is_public: is_pub
|
||||||
|
|
|
@ -16,7 +16,7 @@ import strings
|
||||||
pub type Type int
|
pub type Type int
|
||||||
|
|
||||||
pub type TypeInfo = Alias | Array | ArrayFixed | Enum | FnType | Interface | Map | MultiReturn |
|
pub type TypeInfo = Alias | Array | ArrayFixed | Enum | FnType | Interface | Map | MultiReturn |
|
||||||
Struct | SumType
|
Struct | GenericStructInstance | SumType
|
||||||
|
|
||||||
pub enum Language {
|
pub enum Language {
|
||||||
v
|
v
|
||||||
|
@ -134,7 +134,7 @@ pub fn (t Type) derive(t_from Type) Type {
|
||||||
[inline]
|
[inline]
|
||||||
pub fn new_type(idx int) Type {
|
pub fn new_type(idx int) Type {
|
||||||
if idx < 1 || idx > 65535 {
|
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
|
return idx
|
||||||
}
|
}
|
||||||
|
@ -215,9 +215,9 @@ pub const (
|
||||||
array_type_idx = 20
|
array_type_idx = 20
|
||||||
map_type_idx = 21
|
map_type_idx = 21
|
||||||
any_type_idx = 22
|
any_type_idx = 22
|
||||||
t_type_idx = 23
|
// t_type_idx = 23
|
||||||
any_flt_type_idx = 24
|
any_flt_type_idx = 23
|
||||||
any_int_type_idx = 25
|
any_int_type_idx = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
pub const (
|
pub const (
|
||||||
|
@ -267,7 +267,7 @@ pub const (
|
||||||
array_type = new_type(array_type_idx)
|
array_type = new_type(array_type_idx)
|
||||||
map_type = new_type(map_type_idx)
|
map_type = new_type(map_type_idx)
|
||||||
any_type = new_type(any_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_flt_type = new_type(any_flt_type_idx)
|
||||||
any_int_type = new_type(any_int_type_idx)
|
any_int_type = new_type(any_int_type_idx)
|
||||||
)
|
)
|
||||||
|
@ -321,6 +321,7 @@ pub enum Kind {
|
||||||
map
|
map
|
||||||
any
|
any
|
||||||
struct_
|
struct_
|
||||||
|
generic_struct_instance
|
||||||
multi_return
|
multi_return
|
||||||
sum_type
|
sum_type
|
||||||
alias
|
alias
|
||||||
|
@ -505,12 +506,12 @@ pub fn (mut t Table) register_builtin_type_symbols() {
|
||||||
name: 'any'
|
name: 'any'
|
||||||
mod: 'builtin'
|
mod: 'builtin'
|
||||||
})
|
})
|
||||||
t.register_type_symbol({
|
// t.register_type_symbol({
|
||||||
kind: .any
|
// kind: .any
|
||||||
name: 'T'
|
// name: 'T'
|
||||||
mod: 'builtin'
|
// mod: 'builtin'
|
||||||
is_public: true
|
// is_public: true
|
||||||
})
|
// })
|
||||||
t.register_type_symbol({
|
t.register_type_symbol({
|
||||||
kind: .any_float
|
kind: .any_float
|
||||||
name: 'any_float'
|
name: 'any_float'
|
||||||
|
@ -619,6 +620,13 @@ pub mut:
|
||||||
is_typedef bool // C. [typedef]
|
is_typedef bool // C. [typedef]
|
||||||
is_union bool
|
is_union bool
|
||||||
is_ref_only bool
|
is_ref_only bool
|
||||||
|
generic_types []Type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GenericStructInstance {
|
||||||
|
pub mut:
|
||||||
|
parent_idx int
|
||||||
|
generic_types []Type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Interface {
|
pub struct Interface {
|
||||||
|
|
|
@ -40,22 +40,22 @@ fn test_foo() {
|
||||||
|
|
||||||
fn create<T>() {
|
fn create<T>() {
|
||||||
a := T{}
|
a := T{}
|
||||||
println(a.foo)
|
println(a.name)
|
||||||
mut xx := T{}
|
mut xx := T{}
|
||||||
xx.foo = 'foo'
|
xx.name = 'foo'
|
||||||
println(xx.foo)
|
println(xx.name)
|
||||||
assert xx.foo == 'foo'
|
assert xx.name == 'foo'
|
||||||
xx.init()
|
xx.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
struct User {
|
struct User {
|
||||||
mut:
|
mut:
|
||||||
foo string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
struct City {
|
struct City {
|
||||||
mut:
|
mut:
|
||||||
foo string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (u User) init() {
|
fn (u User) init() {
|
||||||
|
@ -65,12 +65,12 @@ fn (c City) init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mut_arg<T>(mut x T) {
|
fn mut_arg<T>(mut x T) {
|
||||||
println(x.foo) // = 'foo'
|
println(x.name) // = 'foo'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn mut_arg2<T>(mut x T) T {
|
fn mut_arg2<T>(mut x T) T {
|
||||||
println(x.foo) // = 'foo'
|
println(x.name) // = 'foo'
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,40 +182,65 @@ fn test_generic_fn_in_for_in_expression() {
|
||||||
assert value == 'a'
|
assert value == 'a'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// test generic struct
|
// test generic struct
|
||||||
struct DB {
|
struct DB {
|
||||||
driver string
|
driver string
|
||||||
}
|
}
|
||||||
|
|
||||||
struct User {
|
struct Group {
|
||||||
db DB
|
pub mut:
|
||||||
mut:
|
name string
|
||||||
|
group_name string
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Permission {
|
||||||
|
pub mut:
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Repo<T> {
|
struct Repo<T,U> {
|
||||||
db DB
|
db DB
|
||||||
mut:
|
pub mut:
|
||||||
model T
|
model T
|
||||||
|
permission U
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_repo<U>(db DB) Repo<U> {
|
// TODO: multiple type generic struct needs fixing in return for fn
|
||||||
return Repo<U>{db: db}
|
// fn new_repo<T>(db DB) Repo<T,U> {
|
||||||
}
|
// return Repo<T,Permission>{db: db}
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
fn test_generic_struct() {
|
fn test_generic_struct() {
|
||||||
mut a := new_repo<User>(DB{})
|
mut a := Repo<User,Permission>{
|
||||||
a.model.name = 'joe'
|
model: User{name: 'joe'}
|
||||||
mut b := Repo<User>{db: DB{}
|
|
||||||
}
|
}
|
||||||
b.model.name = 'joe'
|
// a.model.name = 'joe'
|
||||||
assert 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 }
|
struct Abc{ x int y int z int }
|
||||||
|
|
||||||
fn p<T>(args ...T) {
|
fn p<T>(args ...T) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user