all: implement multiple generics (#8231)
parent
b10b76bb0d
commit
500ebf77e4
|
@ -885,19 +885,19 @@ jobs:
|
||||||
./v -o v2 cmd/v
|
./v -o v2 cmd/v
|
||||||
./v2 -o v3 cmd/v
|
./v2 -o v3 cmd/v
|
||||||
|
|
||||||
vls-compiles:
|
#vls-compiles:
|
||||||
runs-on: ubuntu-20.04
|
# runs-on: ubuntu-20.04
|
||||||
timeout-minutes: 30
|
# timeout-minutes: 30
|
||||||
steps:
|
# steps:
|
||||||
- uses: actions/checkout@v2
|
# - uses: actions/checkout@v2
|
||||||
- name: Build V
|
# - name: Build V
|
||||||
run: make -j2 && ./v -cc gcc -o v cmd/v
|
# run: make -j2 && ./v -cc gcc -o v cmd/v
|
||||||
- name: Clone VLS
|
# - name: Clone VLS
|
||||||
run: git clone --depth 1 https://github.com/vlang/vls
|
# run: git clone --depth 1 https://github.com/vlang/vls
|
||||||
- name: Build VLS
|
# - name: Build VLS
|
||||||
run: cd vls; ../v cmd/vls ; cd ..
|
# run: cd vls; ../v cmd/vls ; cd ..
|
||||||
- name: Build VLS with -prod
|
# - name: Build VLS with -prod
|
||||||
run: cd vls; ../v -prod cmd/vls ; cd ..
|
# run: cd vls; ../v -prod cmd/vls ; cd ..
|
||||||
|
|
||||||
vab-compiles:
|
vab-compiles:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
|
@ -324,7 +324,7 @@ pub:
|
||||||
pos token.Position // function declaration position
|
pos token.Position // function declaration position
|
||||||
body_pos token.Position // function bodys position
|
body_pos token.Position // function bodys position
|
||||||
file string
|
file string
|
||||||
is_generic bool
|
generic_params []GenericParam
|
||||||
is_direct_arr bool // direct array access
|
is_direct_arr bool // direct array access
|
||||||
attrs []table.Attr
|
attrs []table.Attr
|
||||||
pub mut:
|
pub mut:
|
||||||
|
@ -336,6 +336,11 @@ pub mut:
|
||||||
scope &Scope
|
scope &Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GenericParam {
|
||||||
|
pub:
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
// break, continue
|
// break, continue
|
||||||
pub struct BranchStmt {
|
pub struct BranchStmt {
|
||||||
pub:
|
pub:
|
||||||
|
@ -362,7 +367,7 @@ pub mut:
|
||||||
receiver_type table.Type // User
|
receiver_type table.Type // User
|
||||||
return_type table.Type
|
return_type table.Type
|
||||||
should_be_skipped bool
|
should_be_skipped bool
|
||||||
generic_type table.Type // TODO array, to support multiple types
|
generic_types []table.Type
|
||||||
generic_list_pos token.Position
|
generic_list_pos token.Position
|
||||||
free_receiver bool // true if the receiver expression needs to be freed
|
free_receiver bool // true if the receiver expression needs to be freed
|
||||||
scope &Scope
|
scope &Scope
|
||||||
|
|
|
@ -59,8 +59,16 @@ pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string, m2a map[string]s
|
||||||
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] {
|
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] {
|
||||||
f.write(' ')
|
f.write(' ')
|
||||||
}
|
}
|
||||||
if node.is_generic {
|
if node.generic_params.len > 0 {
|
||||||
f.write('<T>')
|
f.write('<')
|
||||||
|
for i, param in node.generic_params {
|
||||||
|
is_last := i == node.generic_params.len - 1
|
||||||
|
f.write(param.name)
|
||||||
|
if !is_last {
|
||||||
|
f.write(', ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.write('>')
|
||||||
}
|
}
|
||||||
f.write('(')
|
f.write('(')
|
||||||
for i, arg in node.params {
|
for i, arg in node.params {
|
||||||
|
|
|
@ -427,48 +427,60 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) table.T
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) infer_fn_types(f table.Fn, mut call_expr ast.CallExpr) {
|
pub fn (mut c Checker) infer_fn_types(f table.Fn, mut call_expr ast.CallExpr) {
|
||||||
gt_name := 'T'
|
mut inferred_types := []table.Type{}
|
||||||
mut typ := table.void_type
|
for gi, gt_name in f.generic_names {
|
||||||
for i, param in f.params {
|
// skip known types
|
||||||
if call_expr.args.len == 0 {
|
if gi < call_expr.generic_types.len {
|
||||||
break
|
inferred_types << call_expr.generic_types[gi]
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
arg := if i != 0 && call_expr.is_method { call_expr.args[i - 1] } else { call_expr.args[i] }
|
mut typ := table.void_type
|
||||||
if param.typ.has_flag(.generic) {
|
for i, param in f.params {
|
||||||
typ = arg.typ
|
if call_expr.args.len == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
arg_sym := c.table.get_type_symbol(arg.typ)
|
arg := if i != 0 && call_expr.is_method {
|
||||||
param_type_sym := c.table.get_type_symbol(param.typ)
|
call_expr.args[i - 1]
|
||||||
if arg_sym.kind == .array && param_type_sym.kind == .array {
|
} else {
|
||||||
mut arg_elem_info := arg_sym.info as table.Array
|
call_expr.args[i]
|
||||||
mut param_elem_info := param_type_sym.info as table.Array
|
}
|
||||||
mut arg_elem_sym := c.table.get_type_symbol(arg_elem_info.elem_type)
|
if param.typ.has_flag(.generic) {
|
||||||
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
|
typ = arg.typ
|
||||||
for {
|
break
|
||||||
if arg_elem_sym.kind == .array &&
|
}
|
||||||
param_elem_sym.kind == .array && param_elem_sym.name != 'T'
|
arg_sym := c.table.get_type_symbol(arg.typ)
|
||||||
{
|
param_type_sym := c.table.get_type_symbol(param.typ)
|
||||||
arg_elem_info = arg_elem_sym.info as table.Array
|
if arg_sym.kind == .array && param_type_sym.kind == .array {
|
||||||
arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type)
|
mut arg_elem_info := arg_sym.info as table.Array
|
||||||
param_elem_info = param_elem_sym.info as table.Array
|
mut param_elem_info := param_type_sym.info as table.Array
|
||||||
param_elem_sym = c.table.get_type_symbol(param_elem_info.elem_type)
|
mut arg_elem_sym := c.table.get_type_symbol(arg_elem_info.elem_type)
|
||||||
} else {
|
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
|
||||||
typ = arg_elem_info.elem_type
|
for {
|
||||||
break
|
if arg_elem_sym.kind == .array &&
|
||||||
}
|
param_elem_sym.kind == .array && c.cur_fn.generic_params.filter(it.name ==
|
||||||
|
param_elem_sym.name).len == 0
|
||||||
|
{
|
||||||
|
arg_elem_info = arg_elem_sym.info as table.Array
|
||||||
|
arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type)
|
||||||
|
param_elem_info = param_elem_sym.info as table.Array
|
||||||
|
param_elem_sym = c.table.get_type_symbol(param_elem_info.elem_type)
|
||||||
|
} else {
|
||||||
|
typ = arg_elem_info.elem_type
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
if typ == table.void_type {
|
||||||
if typ == table.void_type {
|
c.error('could not infer generic type `$gt_name` in call to `$f.name`', call_expr.pos)
|
||||||
c.error('could not infer generic type `$gt_name` in call to `$f.name`', call_expr.pos)
|
}
|
||||||
} else {
|
|
||||||
if c.pref.is_verbose {
|
if c.pref.is_verbose {
|
||||||
s := c.table.type_to_str(typ)
|
s := c.table.type_to_str(typ)
|
||||||
println('inferred `$f.name<$s>`')
|
println('inferred `$f.name<$s>`')
|
||||||
}
|
}
|
||||||
c.table.register_fn_gen_type(f.name, typ)
|
inferred_types << typ
|
||||||
call_expr.generic_type = typ
|
call_expr.generic_types << typ
|
||||||
}
|
}
|
||||||
|
c.table.register_fn_gen_type(f.name, inferred_types)
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,14 +53,14 @@ pub mut:
|
||||||
rlocked_names []string // vars that are currently read-locked
|
rlocked_names []string // vars that are currently read-locked
|
||||||
in_for_count int // if checker is currently in a for loop
|
in_for_count int // if checker is currently in a for loop
|
||||||
// checked_ident string // to avoid infinite checker loops
|
// checked_ident string // to avoid infinite checker loops
|
||||||
returns bool
|
returns bool
|
||||||
scope_returns bool
|
scope_returns bool
|
||||||
mod string // current module name
|
mod string // current module name
|
||||||
is_builtin_mod bool // are we in `builtin`?
|
is_builtin_mod bool // are we in `builtin`?
|
||||||
inside_unsafe bool
|
inside_unsafe bool
|
||||||
inside_const bool
|
inside_const bool
|
||||||
skip_flags bool // should `#flag` and `#include` be skipped
|
skip_flags bool // should `#flag` and `#include` be skipped
|
||||||
cur_generic_type table.Type
|
cur_generic_types []table.Type
|
||||||
mut:
|
mut:
|
||||||
expr_level int // to avoid infinite recursion segfaults due to compiler bugs
|
expr_level int // to avoid infinite recursion segfaults due to compiler bugs
|
||||||
inside_sql bool // to handle sql table fields pseudo variables
|
inside_sql bool // to handle sql table fields pseudo variables
|
||||||
|
@ -521,8 +521,9 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
||||||
struct_init.pos)
|
struct_init.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if type_sym.name.len == 1 && !c.cur_fn.is_generic {
|
if type_sym.name.len == 1 && c.cur_fn.generic_params.len == 0 {
|
||||||
c.error('unknown struct `$type_sym.name`', struct_init.pos)
|
c.error('unknown struct `$type_sym.name`', struct_init.pos)
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
match type_sym.kind {
|
match type_sym.kind {
|
||||||
.placeholder {
|
.placeholder {
|
||||||
|
@ -1254,15 +1255,23 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
if left_type_sym.kind == .sum_type && method_name == 'type_name' {
|
if left_type_sym.kind == .sum_type && method_name == 'type_name' {
|
||||||
return table.string_type
|
return table.string_type
|
||||||
}
|
}
|
||||||
if call_expr.generic_type.has_flag(.generic) {
|
mut has_generic_generic := false // x.foo<T>() instead of x.foo<int>()
|
||||||
|
mut generic_types := []table.Type{}
|
||||||
|
for generic_type in call_expr.generic_types {
|
||||||
|
if generic_type.has_flag(.generic) {
|
||||||
|
has_generic_generic = true
|
||||||
|
generic_types << c.unwrap_generic(generic_type)
|
||||||
|
} else {
|
||||||
|
generic_types << generic_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has_generic_generic {
|
||||||
if c.mod != '' {
|
if c.mod != '' {
|
||||||
// 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']`
|
c.table.register_fn_gen_type(c.mod + '.' + call_expr.name, generic_types)
|
||||||
c.table.register_fn_gen_type(c.mod + '.' + call_expr.name, c.cur_generic_type)
|
|
||||||
} else {
|
} else {
|
||||||
c.table.register_fn_gen_type(call_expr.name, c.cur_generic_type)
|
c.table.register_fn_gen_type(call_expr.name, generic_types)
|
||||||
}
|
}
|
||||||
// call_expr.generic_type = c.unwrap_generic(call_expr.generic_type)
|
|
||||||
}
|
}
|
||||||
// TODO: remove this for actual methods, use only for compiler magic
|
// TODO: remove this for actual methods, use only for compiler magic
|
||||||
// FIXME: Argument count != 1 will break these
|
// FIXME: Argument count != 1 will break these
|
||||||
|
@ -1451,7 +1460,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position())
|
c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if method.is_generic {
|
if method.generic_names.len > 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.check_expected_call_arg(got_arg_typ, exp_arg_typ) or {
|
c.check_expected_call_arg(got_arg_typ, exp_arg_typ) or {
|
||||||
|
@ -1505,15 +1514,16 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
call_expr.receiver_type = method.params[0].typ
|
call_expr.receiver_type = method.params[0].typ
|
||||||
}
|
}
|
||||||
call_expr.return_type = method.return_type
|
call_expr.return_type = method.return_type
|
||||||
if method.is_generic && call_expr.generic_type == table.void_type {
|
if method.generic_names.len != call_expr.generic_types.len {
|
||||||
// no type arguments given in call, attempt implicit instantiation
|
// no type arguments given in call, attempt implicit instantiation
|
||||||
c.infer_fn_types(method, mut call_expr)
|
c.infer_fn_types(method, mut call_expr)
|
||||||
}
|
}
|
||||||
if call_expr.generic_type != table.void_type && method.return_type != 0 { // table.t_type {
|
if call_expr.generic_types.len > 0 && method.return_type != 0 {
|
||||||
// Handle `foo<T>() T` => `foo<int>() int` => return int
|
// Handle `foo<T>() T` => `foo<int>() int` => return int
|
||||||
return_sym := c.table.get_type_symbol(method.return_type)
|
return_sym := c.table.get_type_symbol(method.return_type)
|
||||||
if return_sym.name == 'T' {
|
if return_sym.name in method.generic_names {
|
||||||
mut typ := call_expr.generic_type
|
generic_index := method.generic_names.index(return_sym.name)
|
||||||
|
mut typ := call_expr.generic_types[generic_index]
|
||||||
typ = typ.set_nr_muls(method.return_type.nr_muls())
|
typ = typ.set_nr_muls(method.return_type.nr_muls())
|
||||||
if method.return_type.has_flag(.optional) {
|
if method.return_type.has_flag(.optional) {
|
||||||
typ = typ.set_flag(.optional)
|
typ = typ.set_flag(.optional)
|
||||||
|
@ -1523,24 +1533,26 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
} else if return_sym.kind == .array {
|
} else if return_sym.kind == .array {
|
||||||
elem_info := return_sym.info as table.Array
|
elem_info := return_sym.info as table.Array
|
||||||
elem_sym := c.table.get_type_symbol(elem_info.elem_type)
|
elem_sym := c.table.get_type_symbol(elem_info.elem_type)
|
||||||
if elem_sym.name == 'T' {
|
if elem_sym.name in method.generic_names {
|
||||||
idx := c.table.find_or_register_array(call_expr.generic_type)
|
generic_index := method.generic_names.index(elem_sym.name)
|
||||||
|
typ := call_expr.generic_types[generic_index]
|
||||||
|
idx := c.table.find_or_register_array(typ)
|
||||||
return table.new_type(idx)
|
return table.new_type(idx)
|
||||||
}
|
}
|
||||||
} else if return_sym.kind == .chan {
|
} else if return_sym.kind == .chan {
|
||||||
elem_info := return_sym.info as table.Chan
|
elem_info := return_sym.info as table.Chan
|
||||||
elem_sym := c.table.get_type_symbol(elem_info.elem_type)
|
elem_sym := c.table.get_type_symbol(elem_info.elem_type)
|
||||||
if elem_sym.name == 'T' {
|
if elem_sym.name in method.generic_names {
|
||||||
idx := c.table.find_or_register_chan(elem_info.elem_type, elem_info.elem_type.nr_muls() >
|
idx := c.table.find_or_register_chan(elem_info.elem_type, elem_info.elem_type.nr_muls() >
|
||||||
0)
|
0)
|
||||||
return table.new_type(idx)
|
return table.new_type(idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if call_expr.generic_type.is_full() && !method.is_generic {
|
if call_expr.generic_types.len > 0 && method.generic_names.len == 0 {
|
||||||
c.error('a non generic function called like a generic one', call_expr.generic_list_pos)
|
c.error('a non generic function called like a generic one', call_expr.generic_list_pos)
|
||||||
}
|
}
|
||||||
if method.is_generic {
|
if method.generic_names.len > 0 {
|
||||||
return call_expr.return_type
|
return call_expr.return_type
|
||||||
}
|
}
|
||||||
return method.return_type
|
return method.return_type
|
||||||
|
@ -1594,19 +1606,24 @@ 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.has_flag(.generic) {
|
mut has_generic_generic := false // foo<T>() instead of foo<int>()
|
||||||
|
mut generic_types := []table.Type{}
|
||||||
|
for generic_type in call_expr.generic_types {
|
||||||
|
if generic_type.has_flag(.generic) {
|
||||||
|
has_generic_generic = true
|
||||||
|
generic_types << c.unwrap_generic(generic_type)
|
||||||
|
} else {
|
||||||
|
generic_types << generic_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has_generic_generic {
|
||||||
if c.mod != '' {
|
if c.mod != '' {
|
||||||
// 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']`
|
c.table.register_fn_gen_type(c.mod + '.' + fn_name, generic_types)
|
||||||
c.table.register_fn_gen_type(c.mod + '.' + fn_name, c.cur_generic_type)
|
|
||||||
} else {
|
} else {
|
||||||
c.table.register_fn_gen_type(fn_name, c.cur_generic_type)
|
c.table.register_fn_gen_type(fn_name, generic_types)
|
||||||
}
|
}
|
||||||
// call_expr.generic_type = c.unwrap_generic(call_expr.generic_type)
|
|
||||||
}
|
}
|
||||||
// if c.fileis('json_test.v') {
|
|
||||||
// println(fn_name)
|
|
||||||
// }
|
|
||||||
if fn_name == 'json.encode' {
|
if fn_name == 'json.encode' {
|
||||||
} else if fn_name == 'json.decode' && call_expr.args.len > 0 {
|
} else if fn_name == 'json.decode' && call_expr.args.len > 0 {
|
||||||
expr := call_expr.args[0].expr
|
expr := call_expr.args[0].expr
|
||||||
|
@ -1671,7 +1688,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
if c.pref.is_script && !found {
|
if c.pref.is_script && !found {
|
||||||
os_name := 'os.$fn_name'
|
os_name := 'os.$fn_name'
|
||||||
if f1 := c.table.find_fn(os_name) {
|
if f1 := c.table.find_fn(os_name) {
|
||||||
if f1.is_generic && call_expr.generic_type != table.void_type {
|
if f1.generic_names.len == call_expr.generic_types.len {
|
||||||
c.table.fn_gen_types[os_name] = c.table.fn_gen_types['${call_expr.mod}.$call_expr.name']
|
c.table.fn_gen_types[os_name] = c.table.fn_gen_types['${call_expr.mod}.$call_expr.name']
|
||||||
}
|
}
|
||||||
call_expr.name = os_name
|
call_expr.name = os_name
|
||||||
|
@ -1716,22 +1733,17 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
// builtin C.m*, C.s* only - temp
|
// builtin C.m*, C.s* only - temp
|
||||||
c.warn('function `$f.name` must be called from an `unsafe` block', call_expr.pos)
|
c.warn('function `$f.name` must be called from an `unsafe` block', call_expr.pos)
|
||||||
}
|
}
|
||||||
if f.is_generic {
|
for generic_type in call_expr.generic_types {
|
||||||
sym := c.table.get_type_symbol(call_expr.generic_type)
|
sym := c.table.get_type_symbol(generic_type)
|
||||||
if sym.kind == .placeholder {
|
if sym.kind == .placeholder {
|
||||||
c.error('unknown type `$sym.name`', call_expr.generic_list_pos)
|
c.error('unknown type `$sym.name`', call_expr.generic_list_pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if f.is_generic && f.return_type.has_flag(.generic) {
|
if f.generic_names.len > 0 && f.return_type.has_flag(.generic) {
|
||||||
rts := c.table.get_type_symbol(f.return_type)
|
rts := c.table.get_type_symbol(f.return_type)
|
||||||
if rts.kind == .struct_ {
|
if rts.info is table.Struct {
|
||||||
rts_info := rts.info as table.Struct
|
if rts.info.generic_types.len > 0 {
|
||||||
if rts_info.generic_types.len > 0 {
|
gts := c.table.get_type_symbol(call_expr.generic_types[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>'
|
nrt := '$rts.name<$gts.name>'
|
||||||
idx := c.table.type_idxs[nrt]
|
idx := c.table.type_idxs[nrt]
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
|
@ -1836,56 +1848,101 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
if typ_sym.kind == .void && arg_typ_sym.kind == .string {
|
if typ_sym.kind == .void && arg_typ_sym.kind == .string {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if f.is_generic {
|
if f.generic_names.len > 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.error('$err in argument ${i + 1} to `$fn_name`', call_arg.pos)
|
c.error('$err in argument ${i + 1} to `$fn_name`', call_arg.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if f.is_generic && call_expr.generic_type == table.void_type {
|
if f.generic_names.len != call_expr.generic_types.len {
|
||||||
// no type arguments given in call, attempt implicit instantiation
|
// no type arguments given in call, attempt implicit instantiation
|
||||||
c.infer_fn_types(f, mut call_expr)
|
c.infer_fn_types(f, mut call_expr)
|
||||||
}
|
}
|
||||||
if call_expr.generic_type != table.void_type && f.return_type != 0 { // table.t_type {
|
if call_expr.generic_types.len > 0 && f.return_type != 0 {
|
||||||
|
// TODO: this logic needs to be cleaned up; maybe make it reusable?
|
||||||
// Handle `foo<T>() T` => `foo<int>() int` => return int
|
// Handle `foo<T>() T` => `foo<int>() int` => return int
|
||||||
return_sym := c.table.get_type_symbol(f.return_type)
|
mut return_sym := c.table.get_type_symbol(f.return_type)
|
||||||
if return_sym.name == 'T' {
|
if return_sym.name in f.generic_names {
|
||||||
mut typ := call_expr.generic_type
|
index := f.generic_names.index(return_sym.name)
|
||||||
|
mut typ := call_expr.generic_types[index]
|
||||||
typ = typ.set_nr_muls(f.return_type.nr_muls())
|
typ = typ.set_nr_muls(f.return_type.nr_muls())
|
||||||
if f.return_type.has_flag(.optional) {
|
if f.return_type.has_flag(.optional) {
|
||||||
typ = typ.set_flag(.optional)
|
typ = typ.set_flag(.optional)
|
||||||
}
|
}
|
||||||
call_expr.return_type = typ
|
call_expr.return_type = typ
|
||||||
return typ
|
return typ
|
||||||
} else if return_sym.kind == .array && return_sym.name.contains('T') {
|
} else if return_sym.kind == .array {
|
||||||
mut info := return_sym.info as table.Array
|
return_info := return_sym.info as table.Array
|
||||||
mut sym := c.table.get_type_symbol(info.elem_type)
|
mut sym := c.table.get_type_symbol(return_info.elem_type)
|
||||||
mut dims := 1
|
mut dims := 1
|
||||||
for {
|
for {
|
||||||
if sym.kind == .array {
|
if mut sym.info is table.Array {
|
||||||
info = sym.info as table.Array
|
sym = c.table.get_type_symbol(sym.info.elem_type)
|
||||||
sym = c.table.get_type_symbol(info.elem_type)
|
|
||||||
dims++
|
dims++
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
idx := c.table.find_or_register_array_with_dims(call_expr.generic_type, dims)
|
if sym.name in f.generic_names {
|
||||||
typ := table.new_type(idx)
|
generic_index := f.generic_names.index(sym.name)
|
||||||
call_expr.return_type = typ
|
generic_type := call_expr.generic_types[generic_index]
|
||||||
return typ
|
idx := c.table.find_or_register_array_with_dims(generic_type, dims)
|
||||||
} else if return_sym.kind == .chan && return_sym.name.contains('T') {
|
typ := table.new_type(idx)
|
||||||
idx := c.table.find_or_register_chan(call_expr.generic_type, call_expr.generic_type.nr_muls() >
|
call_expr.return_type = typ
|
||||||
0)
|
return typ
|
||||||
|
}
|
||||||
|
} else if return_sym.kind == .chan {
|
||||||
|
return_info := return_sym.info as table.Chan
|
||||||
|
elem_sym := c.table.get_type_symbol(return_info.elem_type)
|
||||||
|
if elem_sym.name in f.generic_names {
|
||||||
|
generic_index := f.generic_names.index(elem_sym.name)
|
||||||
|
generic_type := call_expr.generic_types[generic_index]
|
||||||
|
idx := c.table.find_or_register_chan(generic_type, generic_type.nr_muls() > 0)
|
||||||
|
typ := table.new_type(idx)
|
||||||
|
call_expr.return_type = typ
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
} else if mut return_sym.info is table.MultiReturn {
|
||||||
|
mut types := []table.Type{}
|
||||||
|
for return_type in return_sym.info.types {
|
||||||
|
multi_return_sym := c.table.get_type_symbol(return_type)
|
||||||
|
if multi_return_sym.name in f.generic_names {
|
||||||
|
generic_index := f.generic_names.index(multi_return_sym.name)
|
||||||
|
types << call_expr.generic_types[generic_index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idx := c.table.find_or_register_multi_return(types)
|
||||||
typ := table.new_type(idx)
|
typ := table.new_type(idx)
|
||||||
call_expr.return_type = typ
|
call_expr.return_type = typ
|
||||||
return typ
|
return typ
|
||||||
|
} else if mut return_sym.info is table.Map {
|
||||||
|
mut type_changed := false
|
||||||
|
mut unwrapped_key_type := return_sym.info.key_type
|
||||||
|
mut unwrapped_value_type := return_sym.info.value_type
|
||||||
|
if return_sym.info.key_type.has_flag(.generic) {
|
||||||
|
key_sym := c.table.get_type_symbol(return_sym.info.key_type)
|
||||||
|
index := f.generic_names.index(key_sym.name)
|
||||||
|
unwrapped_key_type = call_expr.generic_types[index]
|
||||||
|
type_changed = true
|
||||||
|
}
|
||||||
|
if return_sym.info.value_type.has_flag(.generic) {
|
||||||
|
value_sym := c.table.get_type_symbol(return_sym.info.value_type)
|
||||||
|
index := f.generic_names.index(value_sym.name)
|
||||||
|
unwrapped_value_type = call_expr.generic_types[index]
|
||||||
|
type_changed = true
|
||||||
|
}
|
||||||
|
if type_changed {
|
||||||
|
idx := c.table.find_or_register_map(unwrapped_key_type, unwrapped_value_type)
|
||||||
|
typ := table.new_type(idx)
|
||||||
|
call_expr.return_type = typ
|
||||||
|
return typ
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if call_expr.generic_type.is_full() && !f.is_generic {
|
if call_expr.generic_types.len > 0 && f.generic_names.len == 0 {
|
||||||
c.error('a non generic function called like a generic one', call_expr.generic_list_pos)
|
c.error('a non generic function called like a generic one', call_expr.generic_list_pos)
|
||||||
}
|
}
|
||||||
if f.is_generic {
|
if f.generic_names.len > 0 {
|
||||||
return call_expr.return_type
|
return call_expr.return_type
|
||||||
}
|
}
|
||||||
return f.return_type
|
return f.return_type
|
||||||
|
@ -2047,8 +2104,10 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T
|
||||||
mut name_type := 0
|
mut name_type := 0
|
||||||
match mut selector_expr.expr {
|
match mut selector_expr.expr {
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
if selector_expr.expr.name == 'T' {
|
name := selector_expr.expr.name
|
||||||
name_type = table.Type(c.table.find_type_idx('T')).set_flag(.generic)
|
valid_generic := c.cur_fn.generic_params.filter(it.name == name).len != 0
|
||||||
|
if valid_generic {
|
||||||
|
name_type = table.Type(c.table.find_type_idx(name)).set_flag(.generic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Note: in future typeof() should be a type known at compile-time
|
// Note: in future typeof() should be a type known at compile-time
|
||||||
|
@ -2184,9 +2243,11 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
|
||||||
}
|
}
|
||||||
exp_is_optional := expected_type.has_flag(.optional)
|
exp_is_optional := expected_type.has_flag(.optional)
|
||||||
mut expected_types := [expected_type]
|
mut expected_types := [expected_type]
|
||||||
if expected_type_sym.kind == .multi_return {
|
if expected_type_sym.info is table.MultiReturn {
|
||||||
mr_info := expected_type_sym.info as table.MultiReturn
|
expected_types = expected_type_sym.info.types
|
||||||
expected_types = mr_info.types
|
if c.cur_generic_types.len > 0 {
|
||||||
|
expected_types = expected_types.map(c.unwrap_generic(it))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mut got_types := []table.Type{}
|
mut got_types := []table.Type{}
|
||||||
for expr in return_stmt.exprs {
|
for expr in return_stmt.exprs {
|
||||||
|
@ -3230,11 +3291,17 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
||||||
c.expected_type = table.void_type
|
c.expected_type = table.void_type
|
||||||
}
|
}
|
||||||
|
|
||||||
[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.has_flag(.generic) {
|
if typ.has_flag(.generic) {
|
||||||
// return c.cur_generic_type
|
sym := c.table.get_type_symbol(typ)
|
||||||
return c.cur_generic_type.derive(typ).clear_flag(.generic)
|
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[idx].derive(typ).clear_flag(.generic)
|
||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
@ -3695,10 +3762,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
|
||||||
// second use
|
// second use
|
||||||
if ident.kind in [.constant, .global, .variable] {
|
if ident.kind in [.constant, .global, .variable] {
|
||||||
info := ident.info as ast.IdentVar
|
info := ident.info as ast.IdentVar
|
||||||
// if info.typ == table.t_type {
|
|
||||||
// Got a var with type T, return current generic type
|
// Got a var with type T, return current generic type
|
||||||
// return c.cur_generic_type
|
|
||||||
// }
|
|
||||||
return info.typ
|
return info.typ
|
||||||
} else if ident.kind == .function {
|
} else if ident.kind == .function {
|
||||||
info := ident.info as ast.IdentFn
|
info := ident.info as ast.IdentFn
|
||||||
|
@ -4630,7 +4694,8 @@ fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
|
||||||
!different
|
!different
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.error('invalid `\$if` condition: $cond.left.type_name()', cond.pos)
|
c.error('invalid `\$if` condition: ${cond.left.type_name()}1',
|
||||||
|
cond.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -5223,6 +5288,7 @@ fn (mut c Checker) fetch_and_verify_orm_fields(info table.Struct, pos token.Posi
|
||||||
fn (mut c Checker) post_process_generic_fns() {
|
fn (mut c Checker) post_process_generic_fns() {
|
||||||
// Loop thru each generic function concrete type.
|
// Loop thru each generic function concrete type.
|
||||||
// Check each specific fn instantiation.
|
// Check each specific fn instantiation.
|
||||||
|
|
||||||
for i in 0 .. c.file.generic_fns.len {
|
for i in 0 .. c.file.generic_fns.len {
|
||||||
if c.table.fn_gen_types.len == 0 {
|
if c.table.fn_gen_types.len == 0 {
|
||||||
// no concrete types, so just skip:
|
// no concrete types, so just skip:
|
||||||
|
@ -5230,20 +5296,20 @@ fn (mut c Checker) post_process_generic_fns() {
|
||||||
}
|
}
|
||||||
mut node := c.file.generic_fns[i]
|
mut node := c.file.generic_fns[i]
|
||||||
c.mod = node.mod
|
c.mod = node.mod
|
||||||
for gen_type in c.table.fn_gen_types[node.name] {
|
for gen_types in c.table.fn_gen_types[node.name] {
|
||||||
c.cur_generic_type = gen_type
|
c.cur_generic_types = gen_types
|
||||||
c.fn_decl(mut node)
|
c.fn_decl(mut node)
|
||||||
if node.name in ['vweb.run_app', 'vweb.run'] {
|
if node.name in ['vweb.run_app', 'vweb.run'] {
|
||||||
c.vweb_gen_types << gen_type
|
c.vweb_gen_types << gen_types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.cur_generic_type = 0
|
c.cur_generic_types = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||||
c.returns = false
|
c.returns = false
|
||||||
if node.is_generic && c.cur_generic_type == 0 {
|
if node.generic_params.len > 0 && c.cur_generic_types.len == 0 {
|
||||||
// Just remember the generic function for now.
|
// Just remember the generic function for now.
|
||||||
// It will be processed later in c.post_process_generic_fns,
|
// It will be processed later in c.post_process_generic_fns,
|
||||||
// after all other normal functions are processed.
|
// after all other normal functions are processed.
|
||||||
|
|
|
@ -1626,9 +1626,15 @@ pub fn (mut f Fmt) at_expr(node ast.AtExpr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut f Fmt) write_generic_if_require(node ast.CallExpr) {
|
fn (mut f Fmt) write_generic_if_require(node ast.CallExpr) {
|
||||||
if node.generic_type != 0 && node.generic_type != table.void_type {
|
if node.generic_types.len > 0 {
|
||||||
f.write('<')
|
f.write('<')
|
||||||
f.write(f.table.type_to_str(node.generic_type))
|
for i, generic_type in node.generic_types {
|
||||||
|
is_last := i == node.generic_types.len - 1
|
||||||
|
f.write(f.table.type_to_str(generic_type))
|
||||||
|
if !is_last {
|
||||||
|
f.write(', ')
|
||||||
|
}
|
||||||
|
}
|
||||||
f.write('>')
|
f.write('>')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
fn test<A, B, D, E>() {
|
||||||
|
}
|
|
@ -102,15 +102,15 @@ mut:
|
||||||
is_builtin_mod bool
|
is_builtin_mod bool
|
||||||
hotcode_fn_names []string
|
hotcode_fn_names []string
|
||||||
embedded_files []ast.EmbeddedFile
|
embedded_files []ast.EmbeddedFile
|
||||||
// cur_fn ast.FnDecl
|
cur_fn ast.FnDecl
|
||||||
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
|
cur_generic_types []table.Type // `int`, `string`, etc in `foo<T>()`
|
||||||
sql_i int
|
sql_i int
|
||||||
sql_stmt_name string
|
sql_stmt_name string
|
||||||
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
||||||
inside_vweb_tmpl bool
|
inside_vweb_tmpl bool
|
||||||
inside_return bool
|
inside_return bool
|
||||||
inside_or_block bool
|
inside_or_block bool
|
||||||
strs_to_free0 []string // strings.Builder
|
strs_to_free0 []string // strings.Builder
|
||||||
// strs_to_free []string // strings.Builder
|
// strs_to_free []string // strings.Builder
|
||||||
inside_call bool
|
inside_call bool
|
||||||
for_in_mul_val_name string
|
for_in_mul_val_name string
|
||||||
|
@ -747,7 +747,7 @@ pub fn (mut g Gen) write_multi_return_types() {
|
||||||
}
|
}
|
||||||
g.typedefs.writeln('typedef struct $sym.cname $sym.cname;')
|
g.typedefs.writeln('typedef struct $sym.cname $sym.cname;')
|
||||||
g.type_definitions.writeln('struct $sym.cname {')
|
g.type_definitions.writeln('struct $sym.cname {')
|
||||||
info := sym.info as table.MultiReturn
|
info := sym.mr_info()
|
||||||
for i, mr_typ in info.types {
|
for i, mr_typ in info.types {
|
||||||
type_name := g.typ(mr_typ)
|
type_name := g.typ(mr_typ)
|
||||||
g.type_definitions.writeln('\t$type_name arg$i;')
|
g.type_definitions.writeln('\t$type_name arg$i;')
|
||||||
|
@ -1029,7 +1029,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
// their functions in main.c.
|
// their functions in main.c.
|
||||||
if node.mod != 'main' &&
|
if node.mod != 'main' &&
|
||||||
node.mod != 'help' && !should_bundle_module && !g.file.path.ends_with('_test.v') &&
|
node.mod != 'help' && !should_bundle_module && !g.file.path.ends_with('_test.v') &&
|
||||||
!node.is_generic
|
node.generic_params.len == 0
|
||||||
{
|
{
|
||||||
skip = true
|
skip = true
|
||||||
}
|
}
|
||||||
|
@ -2881,7 +2881,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
fn (mut g Gen) type_name(type_ table.Type) {
|
fn (mut g Gen) type_name(type_ table.Type) {
|
||||||
mut typ := type_
|
mut typ := type_
|
||||||
if typ.has_flag(.generic) {
|
if typ.has_flag(.generic) {
|
||||||
typ = g.cur_generic_type
|
typ = g.cur_generic_types[0]
|
||||||
}
|
}
|
||||||
s := g.table.type_to_str(typ)
|
s := g.table.type_to_str(typ)
|
||||||
g.write('_SLIT("${util.strip_main_name(s)}")')
|
g.write('_SLIT("${util.strip_main_name(s)}")')
|
||||||
|
@ -5642,10 +5642,15 @@ fn (mut g Gen) go_stmt(node ast.GoStmt, joinable bool) string {
|
||||||
expr := node.call_expr
|
expr := node.call_expr
|
||||||
mut name := expr.name // util.no_dots(expr.name)
|
mut name := expr.name // util.no_dots(expr.name)
|
||||||
// TODO: fn call is duplicated. merge with fn_call().
|
// TODO: fn call is duplicated. merge with fn_call().
|
||||||
if expr.generic_type != table.void_type && expr.generic_type != 0 {
|
for i, generic_type in expr.generic_types {
|
||||||
// Using _T_ to differentiate between get<string> and get_string
|
if generic_type != table.void_type && generic_type != 0 {
|
||||||
// `foo<int>()` => `foo_T_int()`
|
// Using _T_ to differentiate between get<string> and get_string
|
||||||
name += '_T_' + g.typ(expr.generic_type)
|
// `foo<int>()` => `foo_T_int()`
|
||||||
|
if i == 0 {
|
||||||
|
name += '_T'
|
||||||
|
}
|
||||||
|
name += '_' + g.typ(generic_type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if expr.is_method {
|
if expr.is_method {
|
||||||
receiver_sym := g.table.get_type_symbol(expr.receiver_type)
|
receiver_sym := g.table.get_type_symbol(expr.receiver_type)
|
||||||
|
|
|
@ -28,20 +28,20 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) {
|
||||||
// if g.fileis('vweb.v') {
|
// if g.fileis('vweb.v') {
|
||||||
// println('\ngen_fn_decl() $it.name $it.is_generic $g.cur_generic_type')
|
// println('\ngen_fn_decl() $it.name $it.is_generic $g.cur_generic_type')
|
||||||
// }
|
// }
|
||||||
if it.is_generic && g.cur_generic_type == 0 { // need the cur_generic_type check to avoid inf. recursion
|
if it.generic_params.len > 0 && g.cur_generic_types.len == 0 { // need the cur_generic_type check to avoid inf. recursion
|
||||||
// loop thru each generic type and generate a function
|
// loop thru each generic type and generate a function
|
||||||
for gen_type in g.table.fn_gen_types[it.name] {
|
for gen_types in g.table.fn_gen_types[it.name] {
|
||||||
sym := g.table.get_type_symbol(gen_type)
|
|
||||||
if g.pref.is_verbose {
|
if g.pref.is_verbose {
|
||||||
println('gen fn `$it.name` for type `$sym.name`')
|
syms := gen_types.map(g.table.get_type_symbol(it))
|
||||||
|
println('gen fn `$it.name` for type `${syms.map(it.name).join(', ')}`')
|
||||||
}
|
}
|
||||||
g.cur_generic_type = gen_type
|
g.cur_generic_types = gen_types
|
||||||
g.gen_fn_decl(it, skip)
|
g.gen_fn_decl(it, skip)
|
||||||
}
|
}
|
||||||
g.cur_generic_type = 0
|
g.cur_generic_types = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// g.cur_fn = it
|
g.cur_fn = it
|
||||||
fn_start_pos := g.out.len
|
fn_start_pos := g.out.len
|
||||||
g.write_v_source_line_info(it.pos)
|
g.write_v_source_line_info(it.pos)
|
||||||
msvc_attrs := g.write_fn_attrs(it.attrs)
|
msvc_attrs := g.write_fn_attrs(it.attrs)
|
||||||
|
@ -69,11 +69,14 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) {
|
||||||
name = c_name(name)
|
name = c_name(name)
|
||||||
}
|
}
|
||||||
mut type_name := g.typ(it.return_type)
|
mut type_name := g.typ(it.return_type)
|
||||||
if g.cur_generic_type != 0 {
|
if g.cur_generic_types.len > 0 {
|
||||||
// foo<T>() => foo_T_int(), foo_T_string() etc
|
// foo<T>() => foo_T_int(), foo_T_string() etc
|
||||||
gen_name := g.typ(g.cur_generic_type)
|
|
||||||
// Using _T_ to differentiate between get<string> and get_string
|
// Using _T_ to differentiate between get<string> and get_string
|
||||||
name += '_T_' + gen_name
|
name += '_T'
|
||||||
|
for generic_type in g.cur_generic_types {
|
||||||
|
gen_name := g.typ(generic_type)
|
||||||
|
name += '_' + gen_name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if g.pref.show_cc && it.is_builtin {
|
// if g.pref.show_cc && it.is_builtin {
|
||||||
// println(name)
|
// println(name)
|
||||||
|
@ -314,11 +317,17 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[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.has_flag(.generic) {
|
if typ.has_flag(.generic) {
|
||||||
// return g.cur_generic_type
|
sym := g.table.get_type_symbol(typ)
|
||||||
return g.cur_generic_type.derive(typ).clear_flag(.generic)
|
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[0].derive(typ).clear_flag(.generic)
|
||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
@ -442,10 +451,15 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if node.generic_type != table.void_type && node.generic_type != 0 {
|
for i, generic_type in node.generic_types {
|
||||||
// Using _T_ to differentiate between get<string> and get_string
|
if generic_type != table.void_type && generic_type != 0 {
|
||||||
// `foo<int>()` => `foo_T_int()`
|
// Using _T_ to differentiate between get<string> and get_string
|
||||||
name += '_T_' + g.typ(node.generic_type)
|
// `foo<int>()` => `foo_T_int()`
|
||||||
|
if i == 0 {
|
||||||
|
name += '_T'
|
||||||
|
}
|
||||||
|
name += '_' + g.typ(generic_type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO2
|
// TODO2
|
||||||
// g.generate_tmp_autofree_arg_vars(node, name)
|
// g.generate_tmp_autofree_arg_vars(node, name)
|
||||||
|
@ -586,10 +600,13 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
} else {
|
} else {
|
||||||
name = c_name(name)
|
name = c_name(name)
|
||||||
}
|
}
|
||||||
if node.generic_type != table.void_type && node.generic_type != 0 {
|
for i, generic_type in node.generic_types {
|
||||||
// Using _T_ to differentiate between get<string> and get_string
|
// Using _T_ to differentiate between get<string> and get_string
|
||||||
// `foo<int>()` => `foo_T_int()`
|
// `foo<int>()` => `foo_T_int()`
|
||||||
name += '_T_' + g.typ(node.generic_type)
|
if i == 0 {
|
||||||
|
name += '_T'
|
||||||
|
}
|
||||||
|
name += '_' + g.typ(generic_type)
|
||||||
}
|
}
|
||||||
// TODO2
|
// TODO2
|
||||||
// cgen shouldn't modify ast nodes, this should be moved
|
// cgen shouldn't modify ast nodes, this should be moved
|
||||||
|
|
|
@ -9,7 +9,6 @@ import v.token
|
||||||
import v.util
|
import v.util
|
||||||
|
|
||||||
pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExpr {
|
pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExpr {
|
||||||
// pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.Expr {
|
|
||||||
first_pos := p.tok.position()
|
first_pos := p.tok.position()
|
||||||
mut fn_name := if language == .c {
|
mut fn_name := if language == .c {
|
||||||
'C.$p.check_name()'
|
'C.$p.check_name()'
|
||||||
|
@ -29,24 +28,20 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||||
p.expr_mod = ''
|
p.expr_mod = ''
|
||||||
or_kind = .block
|
or_kind = .block
|
||||||
}
|
}
|
||||||
mut generic_type := table.void_type
|
mut generic_types := []table.Type{}
|
||||||
mut generic_list_pos := p.tok.position()
|
mut generic_list_pos := p.tok.position()
|
||||||
if p.tok.kind == .lt {
|
if p.tok.kind == .lt {
|
||||||
// `foo<int>(10)`
|
// `foo<int>(10)`
|
||||||
p.next() // `<`
|
|
||||||
p.expr_mod = ''
|
p.expr_mod = ''
|
||||||
generic_type = p.parse_type()
|
generic_types = p.parse_generic_type_list()
|
||||||
p.check(.gt) // `>`
|
|
||||||
generic_list_pos = generic_list_pos.extend(p.prev_tok.position())
|
generic_list_pos = generic_list_pos.extend(p.prev_tok.position())
|
||||||
// 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.has_flag(.generic) {
|
full_generic_fn_name := if fn_name.contains('.') { fn_name } else { p.prepend_mod(fn_name) }
|
||||||
full_generic_fn_name := if fn_name.contains('.') {
|
has_generic_generic := generic_types.filter(it.has_flag(.generic)).len > 0
|
||||||
fn_name
|
if !has_generic_generic {
|
||||||
} else {
|
// will be added in checker
|
||||||
p.prepend_mod(fn_name)
|
p.table.register_fn_gen_type(full_generic_fn_name, generic_types)
|
||||||
}
|
|
||||||
p.table.register_fn_gen_type(full_generic_fn_name, generic_type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.check(.lpar)
|
p.check(.lpar)
|
||||||
|
@ -100,7 +95,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
pos: pos
|
pos: pos
|
||||||
language: language
|
language: language
|
||||||
generic_type: generic_type
|
generic_types: generic_types
|
||||||
generic_list_pos: generic_list_pos
|
generic_list_pos: generic_list_pos
|
||||||
or_block: ast.OrExpr{
|
or_block: ast.OrExpr{
|
||||||
stmts: or_stmts
|
stmts: or_stmts
|
||||||
|
@ -291,12 +286,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
// <T>
|
// <T>
|
||||||
is_generic := p.tok.kind == .lt
|
generic_params := p.parse_generic_params()
|
||||||
if is_generic {
|
|
||||||
p.next()
|
|
||||||
p.next()
|
|
||||||
p.check(.gt)
|
|
||||||
}
|
|
||||||
// Args
|
// Args
|
||||||
args2, are_args_type_only, is_variadic := p.fn_args()
|
args2, are_args_type_only, is_variadic := p.fn_args()
|
||||||
params << args2
|
params << args2
|
||||||
|
@ -353,7 +343,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
params: params
|
params: params
|
||||||
return_type: return_type
|
return_type: return_type
|
||||||
is_variadic: is_variadic
|
is_variadic: is_variadic
|
||||||
is_generic: is_generic
|
generic_names: generic_params.map(it.name)
|
||||||
is_pub: is_pub
|
is_pub: is_pub
|
||||||
is_deprecated: is_deprecated
|
is_deprecated: is_deprecated
|
||||||
is_unsafe: is_unsafe
|
is_unsafe: is_unsafe
|
||||||
|
@ -377,7 +367,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
params: params
|
params: params
|
||||||
return_type: return_type
|
return_type: return_type
|
||||||
is_variadic: is_variadic
|
is_variadic: is_variadic
|
||||||
is_generic: is_generic
|
generic_names: generic_params.map(it.name)
|
||||||
is_pub: is_pub
|
is_pub: is_pub
|
||||||
is_deprecated: is_deprecated
|
is_deprecated: is_deprecated
|
||||||
is_unsafe: is_unsafe
|
is_unsafe: is_unsafe
|
||||||
|
@ -416,12 +406,12 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
is_deprecated: is_deprecated
|
is_deprecated: is_deprecated
|
||||||
is_direct_arr: is_direct_arr
|
is_direct_arr: is_direct_arr
|
||||||
is_pub: is_pub
|
is_pub: is_pub
|
||||||
is_generic: is_generic
|
|
||||||
is_variadic: is_variadic
|
is_variadic: is_variadic
|
||||||
receiver: ast.Field{
|
receiver: ast.Field{
|
||||||
name: rec_name
|
name: rec_name
|
||||||
typ: rec_type
|
typ: rec_type
|
||||||
}
|
}
|
||||||
|
generic_params: generic_params
|
||||||
receiver_pos: receiver_pos
|
receiver_pos: receiver_pos
|
||||||
is_method: is_method
|
is_method: is_method
|
||||||
method_type_pos: rec_type_pos
|
method_type_pos: rec_type_pos
|
||||||
|
@ -440,6 +430,62 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
return fn_decl
|
return fn_decl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut p Parser) parse_generic_params() []ast.GenericParam {
|
||||||
|
mut param_names := []string{}
|
||||||
|
if p.tok.kind != .lt {
|
||||||
|
return []ast.GenericParam{}
|
||||||
|
}
|
||||||
|
p.check(.lt)
|
||||||
|
mut first_done := false
|
||||||
|
mut count := 0
|
||||||
|
for p.tok.kind !in [.gt, .eof] {
|
||||||
|
if first_done {
|
||||||
|
p.check(.comma)
|
||||||
|
}
|
||||||
|
name := p.tok.lit
|
||||||
|
if name.len > 0 && !name[0].is_capital() {
|
||||||
|
p.error('generic parameter needs to be uppercase')
|
||||||
|
}
|
||||||
|
if name.len > 1 {
|
||||||
|
p.error('generic parameter name needs to be exactly one char')
|
||||||
|
}
|
||||||
|
if is_generic_name_reserved(p.tok.lit) {
|
||||||
|
p.error('`$p.tok.lit` is a reserved name and cannot be used for generics')
|
||||||
|
}
|
||||||
|
if name in param_names {
|
||||||
|
p.error('duplicated generic parameter `$name`')
|
||||||
|
}
|
||||||
|
if count > 8 {
|
||||||
|
p.error('cannot have more than 9 generic parameters')
|
||||||
|
}
|
||||||
|
p.check(.name)
|
||||||
|
param_names << name
|
||||||
|
first_done = true
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
p.check(.gt)
|
||||||
|
return param_names.map(ast.GenericParam{it})
|
||||||
|
}
|
||||||
|
|
||||||
|
// is_valid_generic_character returns true if the character is reserved for someting else.
|
||||||
|
fn is_generic_name_reserved(name string) bool {
|
||||||
|
// C is used for cinterop
|
||||||
|
if name == 'C' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// is_generic_name returns true if the current token is a generic name.
|
||||||
|
fn is_generic_name(name string) bool {
|
||||||
|
return name.len == 1 && name.is_capital() && !is_generic_name_reserved(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// is_generic_name returns true if the current token is a generic name.
|
||||||
|
fn (p Parser) is_generic_name() bool {
|
||||||
|
return p.tok.kind == .name && is_generic_name(p.tok.lit)
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut p Parser) anon_fn() ast.AnonFn {
|
fn (mut p Parser) anon_fn() ast.AnonFn {
|
||||||
pos := p.tok.position()
|
pos := p.tok.position()
|
||||||
p.check(.key_fn)
|
p.check(.key_fn)
|
||||||
|
|
|
@ -1099,9 +1099,9 @@ fn (p &Parser) is_generic_call() bool {
|
||||||
// use heuristics to detect `func<T>()` from `var < expr`
|
// use heuristics to detect `func<T>()` from `var < expr`
|
||||||
return !lit0_is_capital && p.peek_tok.kind == .lt && (match p.peek_tok2.kind {
|
return !lit0_is_capital && p.peek_tok.kind == .lt && (match p.peek_tok2.kind {
|
||||||
.name {
|
.name {
|
||||||
// maybe `f<int>`, `f<map[`
|
// maybe `f<int>`, `f<map[`, f<string,
|
||||||
(p.peek_tok2.kind == .name &&
|
(p.peek_tok2.kind == .name &&
|
||||||
p.peek_tok3.kind == .gt) ||
|
p.peek_tok3.kind in [.gt, .comma]) ||
|
||||||
(p.peek_tok2.lit == 'map' && p.peek_tok3.kind == .lsbr)
|
(p.peek_tok2.lit == 'map' && p.peek_tok3.kind == .lsbr)
|
||||||
}
|
}
|
||||||
.lsbr {
|
.lsbr {
|
||||||
|
@ -1309,7 +1309,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||||
return p.struct_init(false) // short_syntax: false
|
return p.struct_init(false) // short_syntax: false
|
||||||
} else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) {
|
} else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) {
|
||||||
// T.name
|
// T.name
|
||||||
if p.tok.lit == 'T' {
|
if p.is_generic_name() {
|
||||||
pos := p.tok.position()
|
pos := p.tok.position()
|
||||||
name := p.check_name()
|
name := p.check_name()
|
||||||
p.check(.dot)
|
p.check(.dot)
|
||||||
|
@ -1494,18 +1494,18 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
||||||
}
|
}
|
||||||
// Method call
|
// Method call
|
||||||
// TODO move to fn.v call_expr()
|
// TODO move to fn.v call_expr()
|
||||||
mut generic_type := table.void_type
|
mut generic_types := []table.Type{}
|
||||||
mut generic_list_pos := p.tok.position()
|
mut generic_list_pos := p.tok.position()
|
||||||
if is_generic_call {
|
if is_generic_call {
|
||||||
// `g.foo<int>(10)`
|
// `g.foo<int>(10)`
|
||||||
p.next() // `<`
|
generic_types = p.parse_generic_type_list()
|
||||||
generic_type = p.parse_type()
|
|
||||||
p.check(.gt) // `>`
|
|
||||||
generic_list_pos = generic_list_pos.extend(p.prev_tok.position())
|
generic_list_pos = generic_list_pos.extend(p.prev_tok.position())
|
||||||
// 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.has_flag(.generic) {
|
has_generic_generic := generic_types.filter(it.has_flag(.generic)).len > 0
|
||||||
p.table.register_fn_gen_type(field_name, generic_type)
|
if !has_generic_generic {
|
||||||
|
// will be added in checker
|
||||||
|
p.table.register_fn_gen_type(field_name, generic_types)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.tok.kind == .lpar {
|
if p.tok.kind == .lpar {
|
||||||
|
@ -1550,7 +1550,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
||||||
args: args
|
args: args
|
||||||
pos: pos
|
pos: pos
|
||||||
is_method: true
|
is_method: true
|
||||||
generic_type: generic_type
|
generic_types: generic_types
|
||||||
generic_list_pos: generic_list_pos
|
generic_list_pos: generic_list_pos
|
||||||
or_block: ast.OrExpr{
|
or_block: ast.OrExpr{
|
||||||
stmts: or_stmts
|
stmts: or_stmts
|
||||||
|
@ -1592,6 +1592,24 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
||||||
return sel_expr
|
return sel_expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut p Parser) parse_generic_type_list() []table.Type {
|
||||||
|
mut types := []table.Type{}
|
||||||
|
if p.tok.kind != .lt {
|
||||||
|
return types
|
||||||
|
}
|
||||||
|
p.next() // `<`
|
||||||
|
mut first_done := false
|
||||||
|
for p.tok.kind !in [.eof, .gt] {
|
||||||
|
if first_done {
|
||||||
|
p.check(.comma)
|
||||||
|
}
|
||||||
|
types << p.parse_type()
|
||||||
|
first_done = true
|
||||||
|
}
|
||||||
|
p.check(.gt) // `>`
|
||||||
|
return types
|
||||||
|
}
|
||||||
|
|
||||||
// `.green`
|
// `.green`
|
||||||
// `pref.BuildMode.default_mode`
|
// `pref.BuildMode.default_mode`
|
||||||
fn (mut p Parser) enum_val() ast.EnumVal {
|
fn (mut p Parser) enum_val() ast.EnumVal {
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||||
node = p.sql_expr()
|
node = p.sql_expr()
|
||||||
p.inside_match = false
|
p.inside_match = false
|
||||||
} else {
|
} else {
|
||||||
if p.inside_if && p.tok.lit == 'T' {
|
if p.inside_if && p.is_generic_name() {
|
||||||
// $if T is string {}
|
// $if T is string {}
|
||||||
p.expecting_type = true
|
p.expecting_type = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
vlib/v/parser/tests/duplicated_generic_err.vv:1:12: error: duplicated generic parameter `A`
|
||||||
|
1 | fn test<A, A>() {}
|
||||||
|
| ^
|
|
@ -0,0 +1 @@
|
||||||
|
fn test<A, A>() {}
|
|
@ -0,0 +1,3 @@
|
||||||
|
vlib/v/parser/tests/generic_lowercase_err.vv:1:22: error: generic parameter needs to be uppercase
|
||||||
|
1 | fn lowercase_generic<a>() {}
|
||||||
|
| ^
|
|
@ -0,0 +1 @@
|
||||||
|
fn lowercase_generic<a>() {}
|
|
@ -0,0 +1,3 @@
|
||||||
|
vlib/v/parser/tests/long_generic_err.vv:1:17: error: generic parameter name needs to be exactly one char
|
||||||
|
1 | fn long_generic<Abc>() {}
|
||||||
|
| ~~~
|
|
@ -0,0 +1 @@
|
||||||
|
fn long_generic<Abc>() {}
|
|
@ -0,0 +1,3 @@
|
||||||
|
vlib/v/parser/tests/too_many_generics_err.vv:1:40: error: cannot have more than 9 generic parameters
|
||||||
|
1 | fn too_many<A, B, D, E, F, G, H, I, J, K>() {}
|
||||||
|
| ^
|
|
@ -0,0 +1 @@
|
||||||
|
fn too_many<A, B, D, E, F, G, H, I, J, K>() {}
|
|
@ -16,7 +16,7 @@ pub mut:
|
||||||
modules []string // Topologically sorted list of all modules registered by the application
|
modules []string // Topologically sorted list of all modules registered by the application
|
||||||
cflags []cflag.CFlag
|
cflags []cflag.CFlag
|
||||||
redefined_fns []string
|
redefined_fns []string
|
||||||
fn_gen_types map[string][]Type // for generic functions
|
fn_gen_types map[string][][]Type // for generic functions
|
||||||
cmod_prefix string // needed for table.type_to_str(Type) while vfmt; contains `os.`
|
cmod_prefix string // needed for table.type_to_str(Type) while vfmt; contains `os.`
|
||||||
is_fmt bool
|
is_fmt bool
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ pub:
|
||||||
return_type Type
|
return_type Type
|
||||||
is_variadic bool
|
is_variadic bool
|
||||||
language Language
|
language Language
|
||||||
is_generic bool
|
generic_names []string
|
||||||
is_pub bool
|
is_pub bool
|
||||||
is_deprecated bool
|
is_deprecated bool
|
||||||
is_unsafe bool
|
is_unsafe bool
|
||||||
|
@ -42,8 +42,8 @@ pub mut:
|
||||||
|
|
||||||
fn (f &Fn) method_equals(o &Fn) bool {
|
fn (f &Fn) method_equals(o &Fn) bool {
|
||||||
return f.params[1..].equals(o.params[1..]) && f.return_type == o.return_type && f.is_variadic ==
|
return f.params[1..].equals(o.params[1..]) && f.return_type == o.return_type && f.is_variadic ==
|
||||||
o.is_variadic && f.language == o.language && f.is_generic == o.is_generic && f.is_pub ==
|
o.is_variadic && f.language == o.language && f.generic_names == o.generic_names &&
|
||||||
o.is_pub && f.mod == o.mod && f.name == o.name
|
f.is_pub == o.is_pub && f.mod == o.mod && f.name == o.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Param {
|
pub struct Param {
|
||||||
|
@ -723,14 +723,12 @@ pub fn (t &Table) mktyp(typ Type) Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut table Table) register_fn_gen_type(fn_name string, typ Type) {
|
pub fn (mut table Table) register_fn_gen_type(fn_name string, types []Type) {
|
||||||
mut a := table.fn_gen_types[fn_name]
|
mut a := table.fn_gen_types[fn_name]
|
||||||
if typ in a {
|
if types in a {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a << typ
|
a << types
|
||||||
// sym := table.get_type_symbol(typ)
|
|
||||||
// println('registering fn ($fn_name) gen type $sym.name')
|
|
||||||
table.fn_gen_types[fn_name] = a
|
table.fn_gen_types[fn_name] = a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,13 +29,13 @@ fn test_ct_expressions() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_t_is<T>() T {
|
fn generic_t_is<O>() O {
|
||||||
$if T is string {
|
$if O is string {
|
||||||
return 'It\'s a string!'
|
return 'It\'s a string!'
|
||||||
} $else {
|
} $else {
|
||||||
return T{}
|
return O{}
|
||||||
}
|
}
|
||||||
return T{}
|
return O{}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GenericTIsTest {}
|
struct GenericTIsTest {}
|
||||||
|
|
|
@ -27,7 +27,6 @@ fn test_identity() {
|
||||||
|
|
||||||
fn test_plus() {
|
fn test_plus() {
|
||||||
a := plus<int>(2, 3)
|
a := plus<int>(2, 3)
|
||||||
println(a)
|
|
||||||
assert a == 5
|
assert a == 5
|
||||||
assert plus<int>(10, 1) == 11
|
assert plus<int>(10, 1) == 11
|
||||||
assert plus<string>('a', 'b') == 'ab'
|
assert plus<string>('a', 'b') == 'ab'
|
||||||
|
@ -47,11 +46,9 @@ fn test_foo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create<T>() {
|
fn create<T>() {
|
||||||
a := T{}
|
_ := T{}
|
||||||
println(a.name)
|
|
||||||
mut xx := T{}
|
mut xx := T{}
|
||||||
xx.name = 'foo'
|
xx.name = 'foo'
|
||||||
println(xx.name)
|
|
||||||
assert xx.name == 'foo'
|
assert xx.name == 'foo'
|
||||||
xx.init()
|
xx.init()
|
||||||
}
|
}
|
||||||
|
@ -73,11 +70,11 @@ fn (c City) init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mut_arg<T>(mut x T) {
|
fn mut_arg<T>(mut x T) {
|
||||||
println(x.name) // = 'foo'
|
// println(x.name) // = 'foo'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mut_arg2<T>(mut x T) T {
|
fn mut_arg2<T>(mut x T) T {
|
||||||
println(x.name) // = 'foo'
|
// println(x.name) // = 'foo'
|
||||||
return *x
|
return *x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +125,6 @@ fn test_ptr() {
|
||||||
assert *ptr('aa') == 'aa'
|
assert *ptr('aa') == 'aa'
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
fn map_f<T,U>(l []T, f fn(T)U) []U {
|
fn map_f<T,U>(l []T, f fn(T)U) []U {
|
||||||
mut r := []U{}
|
mut r := []U{}
|
||||||
for e in l {
|
for e in l {
|
||||||
|
@ -137,6 +133,7 @@ fn map_f<T,U>(l []T, f fn(T)U) []U {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
fn foldl<T>(l []T, nil T, f fn(T,T)T) T {
|
fn foldl<T>(l []T, nil T, f fn(T,T)T) T {
|
||||||
mut r := nil
|
mut r := nil
|
||||||
for e in l {
|
for e in l {
|
||||||
|
@ -144,7 +141,7 @@ fn foldl<T>(l []T, nil T, f fn(T,T)T) T {
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
fn square(x int) int {
|
fn square(x int) int {
|
||||||
return x*x
|
return x*x
|
||||||
}
|
}
|
||||||
|
@ -153,18 +150,17 @@ fn mul_int(x int, y int) int {
|
||||||
return x*y
|
return x*y
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_eq<T>(a, b T) {
|
fn assert_eq<T>(a T, b T) {
|
||||||
r := a == b
|
r := a == b
|
||||||
println('$a == $b: ${r.str()}')
|
|
||||||
assert r
|
assert r
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_nice<T>(x T, indent int) {
|
fn print_nice<T>(x T, indent int) string {
|
||||||
mut space := ''
|
mut space := ''
|
||||||
for _ in 0..indent {
|
for _ in 0..indent {
|
||||||
space = space + ' '
|
space = space + ' '
|
||||||
}
|
}
|
||||||
println('$space$x')
|
return '$space$x'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_generic_fn() {
|
fn test_generic_fn() {
|
||||||
|
@ -174,9 +170,9 @@ fn test_generic_fn() {
|
||||||
assert_eq(plus(i64(4), i64(6)), i64(10))
|
assert_eq(plus(i64(4), i64(6)), i64(10))
|
||||||
a := [1,2,3,4]
|
a := [1,2,3,4]
|
||||||
b := map_f(a, square)
|
b := map_f(a, square)
|
||||||
assert_eq(sum(b), 30) // 1+4+9+16 = 30
|
assert_eq(sum(b), 30) // 1+4+9+16 = 30
|
||||||
assert_eq(foldl(b, 1, mul_int), 576) // 1*4*9*16 = 576
|
//assert_eq(foldl(b, 1, mul_int), 576) // 1*4*9*16 = 576
|
||||||
print_nice('str', 8)
|
assert print_nice('str', 8) == ' str'
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Point {
|
struct Point {
|
||||||
|
@ -185,7 +181,7 @@ mut:
|
||||||
y f64
|
y f64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Point) translate<T>(x, y T) {
|
fn (mut p Point) translate<T>(x T, y T) {
|
||||||
p.x += x
|
p.x += x
|
||||||
p.y += y
|
p.y += y
|
||||||
}
|
}
|
||||||
|
@ -213,7 +209,7 @@ 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
|
||||||
|
@ -247,9 +243,7 @@ fn test_generic_struct() {
|
||||||
name: 'joe'
|
name: 'joe'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// a.model.name = 'joe'
|
|
||||||
assert a.model.name == 'joe'
|
assert a.model.name == 'joe'
|
||||||
println('a.model.name: $a.model.name')
|
|
||||||
mut b := Repo<Group, Permission>{
|
mut b := Repo<Group, Permission>{
|
||||||
permission: Permission{
|
permission: Permission{
|
||||||
name: 'superuser'
|
name: 'superuser'
|
||||||
|
@ -258,15 +252,8 @@ fn test_generic_struct() {
|
||||||
b.model.name = 'admins'
|
b.model.name = 'admins'
|
||||||
assert b.model.name == 'admins'
|
assert b.model.name == 'admins'
|
||||||
assert b.permission.name == 'superuser'
|
assert b.permission.name == 'superuser'
|
||||||
println('b.model.name: $b.model.name')
|
|
||||||
println('b.permission.name: $b.permission.name')
|
|
||||||
assert typeof(a.model).name == 'User'
|
assert typeof(a.model).name == 'User'
|
||||||
assert typeof(b.model).name == 'Group'
|
assert typeof(b.model).name == 'Group'
|
||||||
println('typeof(a.model): ' + typeof(a.model).name)
|
|
||||||
println('typeof(b.model): ' + typeof(b.model).name)
|
|
||||||
// mut x := new_repo<User>(DB{})
|
|
||||||
// x.model.name = 'joe2'
|
|
||||||
// println(x.model.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Foo<T> {
|
struct Foo<T> {
|
||||||
|
@ -322,3 +309,71 @@ fn test_generic_fn_with_variadics(){
|
||||||
p('Good', 'morning', 'world')
|
p('Good', 'morning', 'world')
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
struct Context {}
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
mut:
|
||||||
|
context Context
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test<T>(mut app T) {
|
||||||
|
nested_test<T>(mut app)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nested_test<T>(mut app T) {
|
||||||
|
app.context = Context {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_pass_generic_to_nested_function() {
|
||||||
|
mut app := App{}
|
||||||
|
test(mut app)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct NestedGeneric {}
|
||||||
|
|
||||||
|
fn (ng NestedGeneric) nested_test<T>(mut app T) {
|
||||||
|
app.context = Context {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method_test<T>(mut app T) {
|
||||||
|
ng := NestedGeneric{}
|
||||||
|
ng.nested_test<T>(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_pass_generic_to_nested_method() {
|
||||||
|
mut app := App{}
|
||||||
|
method_test(mut app)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
fn generic_return_map<M>() map[string]M {
|
||||||
|
return {'': M{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generic_return_map() {
|
||||||
|
assert typeof(generic_return_map<string>()).name == 'map[string]string'
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
fn generic_return_nested_map<M>() map[string]map[string]M {
|
||||||
|
return {'': {'': M{}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generic_return_nested_map() {
|
||||||
|
assert typeof(generic_return_nested_map<string>()).name == 'map[string]map[string]string'
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn multi_return<A, B>() (A, B) {
|
||||||
|
return A{}, B{}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo1{}
|
||||||
|
struct Foo2{}
|
||||||
|
struct Foo3{}
|
||||||
|
struct Foo4{}
|
||||||
|
|
||||||
|
fn test_multi_return() {
|
||||||
|
// TODO: multi_return<Foo1, Foo2>()
|
||||||
|
// TODO: temulti_returnst<Foo3, Foo4>()
|
||||||
|
}*/
|
||||||
|
|
Loading…
Reference in New Issue