all: implement multiple generics (#8231)
parent
b10b76bb0d
commit
500ebf77e4
|
@ -885,19 +885,19 @@ jobs:
|
|||
./v -o v2 cmd/v
|
||||
./v2 -o v3 cmd/v
|
||||
|
||||
vls-compiles:
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build V
|
||||
run: make -j2 && ./v -cc gcc -o v cmd/v
|
||||
- name: Clone VLS
|
||||
run: git clone --depth 1 https://github.com/vlang/vls
|
||||
- name: Build VLS
|
||||
run: cd vls; ../v cmd/vls ; cd ..
|
||||
- name: Build VLS with -prod
|
||||
run: cd vls; ../v -prod cmd/vls ; cd ..
|
||||
#vls-compiles:
|
||||
# runs-on: ubuntu-20.04
|
||||
# timeout-minutes: 30
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Build V
|
||||
# run: make -j2 && ./v -cc gcc -o v cmd/v
|
||||
# - name: Clone VLS
|
||||
# run: git clone --depth 1 https://github.com/vlang/vls
|
||||
# - name: Build VLS
|
||||
# run: cd vls; ../v cmd/vls ; cd ..
|
||||
# - name: Build VLS with -prod
|
||||
# run: cd vls; ../v -prod cmd/vls ; cd ..
|
||||
|
||||
vab-compiles:
|
||||
runs-on: ubuntu-20.04
|
||||
|
|
|
@ -324,7 +324,7 @@ pub:
|
|||
pos token.Position // function declaration position
|
||||
body_pos token.Position // function bodys position
|
||||
file string
|
||||
is_generic bool
|
||||
generic_params []GenericParam
|
||||
is_direct_arr bool // direct array access
|
||||
attrs []table.Attr
|
||||
pub mut:
|
||||
|
@ -336,6 +336,11 @@ pub mut:
|
|||
scope &Scope
|
||||
}
|
||||
|
||||
pub struct GenericParam {
|
||||
pub:
|
||||
name string
|
||||
}
|
||||
|
||||
// break, continue
|
||||
pub struct BranchStmt {
|
||||
pub:
|
||||
|
@ -362,7 +367,7 @@ pub mut:
|
|||
receiver_type table.Type // User
|
||||
return_type table.Type
|
||||
should_be_skipped bool
|
||||
generic_type table.Type // TODO array, to support multiple types
|
||||
generic_types []table.Type
|
||||
generic_list_pos token.Position
|
||||
free_receiver bool // true if the receiver expression needs to be freed
|
||||
scope &Scope
|
||||
|
|
|
@ -59,8 +59,16 @@ pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string, m2a map[string]s
|
|||
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] {
|
||||
f.write(' ')
|
||||
}
|
||||
if node.is_generic {
|
||||
f.write('<T>')
|
||||
if node.generic_params.len > 0 {
|
||||
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('(')
|
||||
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) {
|
||||
gt_name := 'T'
|
||||
mut typ := table.void_type
|
||||
for i, param in f.params {
|
||||
if call_expr.args.len == 0 {
|
||||
break
|
||||
mut inferred_types := []table.Type{}
|
||||
for gi, gt_name in f.generic_names {
|
||||
// skip known types
|
||||
if gi < call_expr.generic_types.len {
|
||||
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] }
|
||||
if param.typ.has_flag(.generic) {
|
||||
typ = arg.typ
|
||||
break
|
||||
}
|
||||
arg_sym := c.table.get_type_symbol(arg.typ)
|
||||
param_type_sym := c.table.get_type_symbol(param.typ)
|
||||
if arg_sym.kind == .array && param_type_sym.kind == .array {
|
||||
mut arg_elem_info := arg_sym.info as table.Array
|
||||
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)
|
||||
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
|
||||
for {
|
||||
if arg_elem_sym.kind == .array &&
|
||||
param_elem_sym.kind == .array && param_elem_sym.name != 'T'
|
||||
{
|
||||
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
|
||||
}
|
||||
mut typ := table.void_type
|
||||
for i, param in f.params {
|
||||
if call_expr.args.len == 0 {
|
||||
break
|
||||
}
|
||||
arg := if i != 0 && call_expr.is_method {
|
||||
call_expr.args[i - 1]
|
||||
} else {
|
||||
call_expr.args[i]
|
||||
}
|
||||
if param.typ.has_flag(.generic) {
|
||||
typ = arg.typ
|
||||
break
|
||||
}
|
||||
arg_sym := c.table.get_type_symbol(arg.typ)
|
||||
param_type_sym := c.table.get_type_symbol(param.typ)
|
||||
if arg_sym.kind == .array && param_type_sym.kind == .array {
|
||||
mut arg_elem_info := arg_sym.info as table.Array
|
||||
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)
|
||||
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
|
||||
for {
|
||||
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 {
|
||||
c.error('could not infer generic type `$gt_name` in call to `$f.name`', call_expr.pos)
|
||||
} else {
|
||||
if typ == table.void_type {
|
||||
c.error('could not infer generic type `$gt_name` in call to `$f.name`', call_expr.pos)
|
||||
}
|
||||
if c.pref.is_verbose {
|
||||
s := c.table.type_to_str(typ)
|
||||
println('inferred `$f.name<$s>`')
|
||||
}
|
||||
c.table.register_fn_gen_type(f.name, typ)
|
||||
call_expr.generic_type = typ
|
||||
inferred_types << 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
|
||||
in_for_count int // if checker is currently in a for loop
|
||||
// checked_ident string // to avoid infinite checker loops
|
||||
returns bool
|
||||
scope_returns bool
|
||||
mod string // current module name
|
||||
is_builtin_mod bool // are we in `builtin`?
|
||||
inside_unsafe bool
|
||||
inside_const bool
|
||||
skip_flags bool // should `#flag` and `#include` be skipped
|
||||
cur_generic_type table.Type
|
||||
returns bool
|
||||
scope_returns bool
|
||||
mod string // current module name
|
||||
is_builtin_mod bool // are we in `builtin`?
|
||||
inside_unsafe bool
|
||||
inside_const bool
|
||||
skip_flags bool // should `#flag` and `#include` be skipped
|
||||
cur_generic_types []table.Type
|
||||
mut:
|
||||
expr_level int // to avoid infinite recursion segfaults due to compiler bugs
|
||||
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)
|
||||
}
|
||||
}
|
||||
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)
|
||||
return 0
|
||||
}
|
||||
match type_sym.kind {
|
||||
.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' {
|
||||
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 != '' {
|
||||
// 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, c.cur_generic_type)
|
||||
c.table.register_fn_gen_type(c.mod + '.' + call_expr.name, generic_types)
|
||||
} 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
|
||||
// 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())
|
||||
continue
|
||||
}
|
||||
if method.is_generic {
|
||||
if method.generic_names.len > 0 {
|
||||
continue
|
||||
}
|
||||
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.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
|
||||
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
|
||||
return_sym := c.table.get_type_symbol(method.return_type)
|
||||
if return_sym.name == 'T' {
|
||||
mut typ := call_expr.generic_type
|
||||
if return_sym.name in method.generic_names {
|
||||
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())
|
||||
if method.return_type.has_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 {
|
||||
elem_info := return_sym.info as table.Array
|
||||
elem_sym := c.table.get_type_symbol(elem_info.elem_type)
|
||||
if elem_sym.name == 'T' {
|
||||
idx := c.table.find_or_register_array(call_expr.generic_type)
|
||||
if elem_sym.name in method.generic_names {
|
||||
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)
|
||||
}
|
||||
} else if return_sym.kind == .chan {
|
||||
elem_info := return_sym.info as table.Chan
|
||||
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() >
|
||||
0)
|
||||
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)
|
||||
}
|
||||
if method.is_generic {
|
||||
if method.generic_names.len > 0 {
|
||||
return call_expr.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)
|
||||
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 != '' {
|
||||
// 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, c.cur_generic_type)
|
||||
c.table.register_fn_gen_type(c.mod + '.' + fn_name, generic_types)
|
||||
} 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' {
|
||||
} else if fn_name == 'json.decode' && call_expr.args.len > 0 {
|
||||
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 {
|
||||
os_name := 'os.$fn_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']
|
||||
}
|
||||
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
|
||||
c.warn('function `$f.name` must be called from an `unsafe` block', call_expr.pos)
|
||||
}
|
||||
if f.is_generic {
|
||||
sym := c.table.get_type_symbol(call_expr.generic_type)
|
||||
for generic_type in call_expr.generic_types {
|
||||
sym := c.table.get_type_symbol(generic_type)
|
||||
if sym.kind == .placeholder {
|
||||
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)
|
||||
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)
|
||||
if rts.info is table.Struct {
|
||||
if rts.info.generic_types.len > 0 {
|
||||
gts := c.table.get_type_symbol(call_expr.generic_types[0])
|
||||
nrt := '$rts.name<$gts.name>'
|
||||
idx := c.table.type_idxs[nrt]
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
if f.is_generic {
|
||||
if f.generic_names.len > 0 {
|
||||
continue
|
||||
}
|
||||
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
|
||||
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
|
||||
return_sym := c.table.get_type_symbol(f.return_type)
|
||||
if return_sym.name == 'T' {
|
||||
mut typ := call_expr.generic_type
|
||||
mut return_sym := c.table.get_type_symbol(f.return_type)
|
||||
if return_sym.name in f.generic_names {
|
||||
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())
|
||||
if f.return_type.has_flag(.optional) {
|
||||
typ = typ.set_flag(.optional)
|
||||
}
|
||||
call_expr.return_type = typ
|
||||
return typ
|
||||
} else if return_sym.kind == .array && return_sym.name.contains('T') {
|
||||
mut info := return_sym.info as table.Array
|
||||
mut sym := c.table.get_type_symbol(info.elem_type)
|
||||
} else if return_sym.kind == .array {
|
||||
return_info := return_sym.info as table.Array
|
||||
mut sym := c.table.get_type_symbol(return_info.elem_type)
|
||||
mut dims := 1
|
||||
for {
|
||||
if sym.kind == .array {
|
||||
info = sym.info as table.Array
|
||||
sym = c.table.get_type_symbol(info.elem_type)
|
||||
if mut sym.info is table.Array {
|
||||
sym = c.table.get_type_symbol(sym.info.elem_type)
|
||||
dims++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
idx := c.table.find_or_register_array_with_dims(call_expr.generic_type, dims)
|
||||
typ := table.new_type(idx)
|
||||
call_expr.return_type = typ
|
||||
return typ
|
||||
} else if return_sym.kind == .chan && return_sym.name.contains('T') {
|
||||
idx := c.table.find_or_register_chan(call_expr.generic_type, call_expr.generic_type.nr_muls() >
|
||||
0)
|
||||
if sym.name in f.generic_names {
|
||||
generic_index := f.generic_names.index(sym.name)
|
||||
generic_type := call_expr.generic_types[generic_index]
|
||||
idx := c.table.find_or_register_array_with_dims(generic_type, dims)
|
||||
typ := table.new_type(idx)
|
||||
call_expr.return_type = typ
|
||||
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)
|
||||
call_expr.return_type = 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)
|
||||
}
|
||||
if f.is_generic {
|
||||
if f.generic_names.len > 0 {
|
||||
return call_expr.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
|
||||
match mut selector_expr.expr {
|
||||
ast.Ident {
|
||||
if selector_expr.expr.name == 'T' {
|
||||
name_type = table.Type(c.table.find_type_idx('T')).set_flag(.generic)
|
||||
name := selector_expr.expr.name
|
||||
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
|
||||
|
@ -2184,9 +2243,11 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
|
|||
}
|
||||
exp_is_optional := expected_type.has_flag(.optional)
|
||||
mut expected_types := [expected_type]
|
||||
if expected_type_sym.kind == .multi_return {
|
||||
mr_info := expected_type_sym.info as table.MultiReturn
|
||||
expected_types = mr_info.types
|
||||
if expected_type_sym.info is table.MultiReturn {
|
||||
expected_types = expected_type_sym.info.types
|
||||
if c.cur_generic_types.len > 0 {
|
||||
expected_types = expected_types.map(c.unwrap_generic(it))
|
||||
}
|
||||
}
|
||||
mut got_types := []table.Type{}
|
||||
for expr in return_stmt.exprs {
|
||||
|
@ -3230,11 +3291,17 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
|||
c.expected_type = table.void_type
|
||||
}
|
||||
|
||||
[inline]
|
||||
pub fn (c &Checker) unwrap_generic(typ table.Type) table.Type {
|
||||
if typ.has_flag(.generic) {
|
||||
// return c.cur_generic_type
|
||||
return c.cur_generic_type.derive(typ).clear_flag(.generic)
|
||||
sym := c.table.get_type_symbol(typ)
|
||||
mut idx := 0
|
||||
for i, generic_param in c.cur_fn.generic_params {
|
||||
if generic_param.name == sym.name {
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return c.cur_generic_types[idx].derive(typ).clear_flag(.generic)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
@ -3695,10 +3762,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
|
|||
// second use
|
||||
if ident.kind in [.constant, .global, .variable] {
|
||||
info := ident.info as ast.IdentVar
|
||||
// if info.typ == table.t_type {
|
||||
// Got a var with type T, return current generic type
|
||||
// return c.cur_generic_type
|
||||
// }
|
||||
return info.typ
|
||||
} else if ident.kind == .function {
|
||||
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
|
||||
}
|
||||
} 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 {
|
||||
|
@ -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() {
|
||||
// Loop thru each generic function concrete type.
|
||||
// Check each specific fn instantiation.
|
||||
|
||||
for i in 0 .. c.file.generic_fns.len {
|
||||
if c.table.fn_gen_types.len == 0 {
|
||||
// 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]
|
||||
c.mod = node.mod
|
||||
for gen_type in c.table.fn_gen_types[node.name] {
|
||||
c.cur_generic_type = gen_type
|
||||
for gen_types in c.table.fn_gen_types[node.name] {
|
||||
c.cur_generic_types = gen_types
|
||||
c.fn_decl(mut node)
|
||||
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) {
|
||||
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.
|
||||
// It will be processed later in c.post_process_generic_fns,
|
||||
// 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) {
|
||||
if node.generic_type != 0 && node.generic_type != table.void_type {
|
||||
if node.generic_types.len > 0 {
|
||||
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('>')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
fn test<A, B, D, E>() {
|
||||
}
|
|
@ -102,15 +102,15 @@ mut:
|
|||
is_builtin_mod bool
|
||||
hotcode_fn_names []string
|
||||
embedded_files []ast.EmbeddedFile
|
||||
// cur_fn ast.FnDecl
|
||||
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
|
||||
sql_i int
|
||||
sql_stmt_name string
|
||||
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
||||
inside_vweb_tmpl bool
|
||||
inside_return bool
|
||||
inside_or_block bool
|
||||
strs_to_free0 []string // strings.Builder
|
||||
cur_fn ast.FnDecl
|
||||
cur_generic_types []table.Type // `int`, `string`, etc in `foo<T>()`
|
||||
sql_i int
|
||||
sql_stmt_name string
|
||||
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
||||
inside_vweb_tmpl bool
|
||||
inside_return bool
|
||||
inside_or_block bool
|
||||
strs_to_free0 []string // strings.Builder
|
||||
// strs_to_free []string // strings.Builder
|
||||
inside_call bool
|
||||
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.type_definitions.writeln('struct $sym.cname {')
|
||||
info := sym.info as table.MultiReturn
|
||||
info := sym.mr_info()
|
||||
for i, mr_typ in info.types {
|
||||
type_name := g.typ(mr_typ)
|
||||
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.
|
||||
if node.mod != 'main' &&
|
||||
node.mod != 'help' && !should_bundle_module && !g.file.path.ends_with('_test.v') &&
|
||||
!node.is_generic
|
||||
node.generic_params.len == 0
|
||||
{
|
||||
skip = true
|
||||
}
|
||||
|
@ -2881,7 +2881,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
fn (mut g Gen) type_name(type_ table.Type) {
|
||||
mut typ := type_
|
||||
if typ.has_flag(.generic) {
|
||||
typ = g.cur_generic_type
|
||||
typ = g.cur_generic_types[0]
|
||||
}
|
||||
s := g.table.type_to_str(typ)
|
||||
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
|
||||
mut name := expr.name // util.no_dots(expr.name)
|
||||
// TODO: fn call is duplicated. merge with fn_call().
|
||||
if expr.generic_type != table.void_type && expr.generic_type != 0 {
|
||||
// Using _T_ to differentiate between get<string> and get_string
|
||||
// `foo<int>()` => `foo_T_int()`
|
||||
name += '_T_' + g.typ(expr.generic_type)
|
||||
for i, generic_type in expr.generic_types {
|
||||
if generic_type != table.void_type && generic_type != 0 {
|
||||
// Using _T_ to differentiate between get<string> and get_string
|
||||
// `foo<int>()` => `foo_T_int()`
|
||||
if i == 0 {
|
||||
name += '_T'
|
||||
}
|
||||
name += '_' + g.typ(generic_type)
|
||||
}
|
||||
}
|
||||
if expr.is_method {
|
||||
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') {
|
||||
// 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
|
||||
for gen_type in g.table.fn_gen_types[it.name] {
|
||||
sym := g.table.get_type_symbol(gen_type)
|
||||
for gen_types in g.table.fn_gen_types[it.name] {
|
||||
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.cur_generic_type = 0
|
||||
g.cur_generic_types = []
|
||||
return
|
||||
}
|
||||
// g.cur_fn = it
|
||||
g.cur_fn = it
|
||||
fn_start_pos := g.out.len
|
||||
g.write_v_source_line_info(it.pos)
|
||||
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)
|
||||
}
|
||||
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
|
||||
gen_name := g.typ(g.cur_generic_type)
|
||||
// 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 {
|
||||
// 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 {
|
||||
if typ.has_flag(.generic) {
|
||||
// return g.cur_generic_type
|
||||
return g.cur_generic_type.derive(typ).clear_flag(.generic)
|
||||
sym := g.table.get_type_symbol(typ)
|
||||
mut idx := 0
|
||||
for i, generic_param in g.cur_fn.generic_params {
|
||||
if generic_param.name == sym.name {
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return g.cur_generic_types[0].derive(typ).clear_flag(.generic)
|
||||
}
|
||||
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 {
|
||||
// Using _T_ to differentiate between get<string> and get_string
|
||||
// `foo<int>()` => `foo_T_int()`
|
||||
name += '_T_' + g.typ(node.generic_type)
|
||||
for i, generic_type in node.generic_types {
|
||||
if generic_type != table.void_type && generic_type != 0 {
|
||||
// Using _T_ to differentiate between get<string> and get_string
|
||||
// `foo<int>()` => `foo_T_int()`
|
||||
if i == 0 {
|
||||
name += '_T'
|
||||
}
|
||||
name += '_' + g.typ(generic_type)
|
||||
}
|
||||
}
|
||||
// TODO2
|
||||
// g.generate_tmp_autofree_arg_vars(node, name)
|
||||
|
@ -586,10 +600,13 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
|||
} else {
|
||||
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
|
||||
// `foo<int>()` => `foo_T_int()`
|
||||
name += '_T_' + g.typ(node.generic_type)
|
||||
if i == 0 {
|
||||
name += '_T'
|
||||
}
|
||||
name += '_' + g.typ(generic_type)
|
||||
}
|
||||
// TODO2
|
||||
// cgen shouldn't modify ast nodes, this should be moved
|
||||
|
|
|
@ -9,7 +9,6 @@ import v.token
|
|||
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.Expr {
|
||||
first_pos := p.tok.position()
|
||||
mut fn_name := if language == .c {
|
||||
'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 = ''
|
||||
or_kind = .block
|
||||
}
|
||||
mut generic_type := table.void_type
|
||||
mut generic_types := []table.Type{}
|
||||
mut generic_list_pos := p.tok.position()
|
||||
if p.tok.kind == .lt {
|
||||
// `foo<int>(10)`
|
||||
p.next() // `<`
|
||||
p.expr_mod = ''
|
||||
generic_type = p.parse_type()
|
||||
p.check(.gt) // `>`
|
||||
generic_types = p.parse_generic_type_list()
|
||||
generic_list_pos = generic_list_pos.extend(p.prev_tok.position())
|
||||
// In case of `foo<T>()`
|
||||
// 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)
|
||||
}
|
||||
p.table.register_fn_gen_type(full_generic_fn_name, generic_type)
|
||||
full_generic_fn_name := if fn_name.contains('.') { fn_name } else { p.prepend_mod(fn_name) }
|
||||
has_generic_generic := generic_types.filter(it.has_flag(.generic)).len > 0
|
||||
if !has_generic_generic {
|
||||
// will be added in checker
|
||||
p.table.register_fn_gen_type(full_generic_fn_name, generic_types)
|
||||
}
|
||||
}
|
||||
p.check(.lpar)
|
||||
|
@ -100,7 +95,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
|||
mod: p.mod
|
||||
pos: pos
|
||||
language: language
|
||||
generic_type: generic_type
|
||||
generic_types: generic_types
|
||||
generic_list_pos: generic_list_pos
|
||||
or_block: ast.OrExpr{
|
||||
stmts: or_stmts
|
||||
|
@ -291,12 +286,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
p.next()
|
||||
}
|
||||
// <T>
|
||||
is_generic := p.tok.kind == .lt
|
||||
if is_generic {
|
||||
p.next()
|
||||
p.next()
|
||||
p.check(.gt)
|
||||
}
|
||||
generic_params := p.parse_generic_params()
|
||||
// Args
|
||||
args2, are_args_type_only, is_variadic := p.fn_args()
|
||||
params << args2
|
||||
|
@ -353,7 +343,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
params: params
|
||||
return_type: return_type
|
||||
is_variadic: is_variadic
|
||||
is_generic: is_generic
|
||||
generic_names: generic_params.map(it.name)
|
||||
is_pub: is_pub
|
||||
is_deprecated: is_deprecated
|
||||
is_unsafe: is_unsafe
|
||||
|
@ -377,7 +367,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
params: params
|
||||
return_type: return_type
|
||||
is_variadic: is_variadic
|
||||
is_generic: is_generic
|
||||
generic_names: generic_params.map(it.name)
|
||||
is_pub: is_pub
|
||||
is_deprecated: is_deprecated
|
||||
is_unsafe: is_unsafe
|
||||
|
@ -416,12 +406,12 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
is_deprecated: is_deprecated
|
||||
is_direct_arr: is_direct_arr
|
||||
is_pub: is_pub
|
||||
is_generic: is_generic
|
||||
is_variadic: is_variadic
|
||||
receiver: ast.Field{
|
||||
name: rec_name
|
||||
typ: rec_type
|
||||
}
|
||||
generic_params: generic_params
|
||||
receiver_pos: receiver_pos
|
||||
is_method: is_method
|
||||
method_type_pos: rec_type_pos
|
||||
|
@ -440,6 +430,62 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
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 {
|
||||
pos := p.tok.position()
|
||||
p.check(.key_fn)
|
||||
|
|
|
@ -1099,9 +1099,9 @@ fn (p &Parser) is_generic_call() bool {
|
|||
// use heuristics to detect `func<T>()` from `var < expr`
|
||||
return !lit0_is_capital && p.peek_tok.kind == .lt && (match p.peek_tok2.kind {
|
||||
.name {
|
||||
// maybe `f<int>`, `f<map[`
|
||||
// maybe `f<int>`, `f<map[`, f<string,
|
||||
(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)
|
||||
}
|
||||
.lsbr {
|
||||
|
@ -1309,7 +1309,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
return p.struct_init(false) // short_syntax: false
|
||||
} else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) {
|
||||
// T.name
|
||||
if p.tok.lit == 'T' {
|
||||
if p.is_generic_name() {
|
||||
pos := p.tok.position()
|
||||
name := p.check_name()
|
||||
p.check(.dot)
|
||||
|
@ -1494,18 +1494,18 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
|||
}
|
||||
// Method call
|
||||
// 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()
|
||||
if is_generic_call {
|
||||
// `g.foo<int>(10)`
|
||||
p.next() // `<`
|
||||
generic_type = p.parse_type()
|
||||
p.check(.gt) // `>`
|
||||
generic_types = p.parse_generic_type_list()
|
||||
generic_list_pos = generic_list_pos.extend(p.prev_tok.position())
|
||||
// In case of `foo<T>()`
|
||||
// T is unwrapped and registered in the checker.
|
||||
if !generic_type.has_flag(.generic) {
|
||||
p.table.register_fn_gen_type(field_name, generic_type)
|
||||
has_generic_generic := generic_types.filter(it.has_flag(.generic)).len > 0
|
||||
if !has_generic_generic {
|
||||
// will be added in checker
|
||||
p.table.register_fn_gen_type(field_name, generic_types)
|
||||
}
|
||||
}
|
||||
if p.tok.kind == .lpar {
|
||||
|
@ -1550,7 +1550,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
|||
args: args
|
||||
pos: pos
|
||||
is_method: true
|
||||
generic_type: generic_type
|
||||
generic_types: generic_types
|
||||
generic_list_pos: generic_list_pos
|
||||
or_block: ast.OrExpr{
|
||||
stmts: or_stmts
|
||||
|
@ -1592,6 +1592,24 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.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`
|
||||
// `pref.BuildMode.default_mode`
|
||||
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()
|
||||
p.inside_match = false
|
||||
} else {
|
||||
if p.inside_if && p.tok.lit == 'T' {
|
||||
if p.inside_if && p.is_generic_name() {
|
||||
// $if T is string {}
|
||||
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
|
||||
cflags []cflag.CFlag
|
||||
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.`
|
||||
is_fmt bool
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ pub:
|
|||
return_type Type
|
||||
is_variadic bool
|
||||
language Language
|
||||
is_generic bool
|
||||
generic_names []string
|
||||
is_pub bool
|
||||
is_deprecated bool
|
||||
is_unsafe bool
|
||||
|
@ -42,8 +42,8 @@ pub mut:
|
|||
|
||||
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 ==
|
||||
o.is_variadic && f.language == o.language && f.is_generic == o.is_generic && f.is_pub ==
|
||||
o.is_pub && f.mod == o.mod && f.name == o.name
|
||||
o.is_variadic && f.language == o.language && f.generic_names == o.generic_names &&
|
||||
f.is_pub == o.is_pub && f.mod == o.mod && f.name == o.name
|
||||
}
|
||||
|
||||
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]
|
||||
if typ in a {
|
||||
if types in a {
|
||||
return
|
||||
}
|
||||
a << typ
|
||||
// sym := table.get_type_symbol(typ)
|
||||
// println('registering fn ($fn_name) gen type $sym.name')
|
||||
a << types
|
||||
table.fn_gen_types[fn_name] = a
|
||||
}
|
||||
|
||||
|
|
|
@ -29,13 +29,13 @@ fn test_ct_expressions() {
|
|||
}
|
||||
}
|
||||
|
||||
fn generic_t_is<T>() T {
|
||||
$if T is string {
|
||||
fn generic_t_is<O>() O {
|
||||
$if O is string {
|
||||
return 'It\'s a string!'
|
||||
} $else {
|
||||
return T{}
|
||||
return O{}
|
||||
}
|
||||
return T{}
|
||||
return O{}
|
||||
}
|
||||
|
||||
struct GenericTIsTest {}
|
||||
|
|
|
@ -27,7 +27,6 @@ fn test_identity() {
|
|||
|
||||
fn test_plus() {
|
||||
a := plus<int>(2, 3)
|
||||
println(a)
|
||||
assert a == 5
|
||||
assert plus<int>(10, 1) == 11
|
||||
assert plus<string>('a', 'b') == 'ab'
|
||||
|
@ -47,11 +46,9 @@ fn test_foo() {
|
|||
}
|
||||
|
||||
fn create<T>() {
|
||||
a := T{}
|
||||
println(a.name)
|
||||
_ := T{}
|
||||
mut xx := T{}
|
||||
xx.name = 'foo'
|
||||
println(xx.name)
|
||||
assert xx.name == 'foo'
|
||||
xx.init()
|
||||
}
|
||||
|
@ -73,11 +70,11 @@ fn (c City) init() {
|
|||
}
|
||||
|
||||
fn mut_arg<T>(mut x T) {
|
||||
println(x.name) // = 'foo'
|
||||
// println(x.name) // = 'foo'
|
||||
}
|
||||
|
||||
fn mut_arg2<T>(mut x T) T {
|
||||
println(x.name) // = 'foo'
|
||||
// println(x.name) // = 'foo'
|
||||
return *x
|
||||
}
|
||||
|
||||
|
@ -128,7 +125,6 @@ fn test_ptr() {
|
|||
assert *ptr('aa') == 'aa'
|
||||
}
|
||||
|
||||
/*
|
||||
fn map_f<T,U>(l []T, f fn(T)U) []U {
|
||||
mut r := []U{}
|
||||
for e in l {
|
||||
|
@ -137,6 +133,7 @@ fn map_f<T,U>(l []T, f fn(T)U) []U {
|
|||
return r
|
||||
}
|
||||
|
||||
/*
|
||||
fn foldl<T>(l []T, nil T, f fn(T,T)T) T {
|
||||
mut r := nil
|
||||
for e in l {
|
||||
|
@ -144,7 +141,7 @@ fn foldl<T>(l []T, nil T, f fn(T,T)T) T {
|
|||
}
|
||||
return r
|
||||
}
|
||||
|
||||
*/
|
||||
fn square(x int) int {
|
||||
return x*x
|
||||
}
|
||||
|
@ -153,18 +150,17 @@ fn mul_int(x int, y int) int {
|
|||
return x*y
|
||||
}
|
||||
|
||||
fn assert_eq<T>(a, b T) {
|
||||
fn assert_eq<T>(a T, b T) {
|
||||
r := a == b
|
||||
println('$a == $b: ${r.str()}')
|
||||
assert r
|
||||
}
|
||||
|
||||
fn print_nice<T>(x T, indent int) {
|
||||
fn print_nice<T>(x T, indent int) string {
|
||||
mut space := ''
|
||||
for _ in 0..indent {
|
||||
space = space + ' '
|
||||
}
|
||||
println('$space$x')
|
||||
return '$space$x'
|
||||
}
|
||||
|
||||
fn test_generic_fn() {
|
||||
|
@ -174,9 +170,9 @@ fn test_generic_fn() {
|
|||
assert_eq(plus(i64(4), i64(6)), i64(10))
|
||||
a := [1,2,3,4]
|
||||
b := map_f(a, square)
|
||||
assert_eq(sum(b), 30) // 1+4+9+16 = 30
|
||||
assert_eq(foldl(b, 1, mul_int), 576) // 1*4*9*16 = 576
|
||||
print_nice('str', 8)
|
||||
assert_eq(sum(b), 30) // 1+4+9+16 = 30
|
||||
//assert_eq(foldl(b, 1, mul_int), 576) // 1*4*9*16 = 576
|
||||
assert print_nice('str', 8) == ' str'
|
||||
}
|
||||
|
||||
struct Point {
|
||||
|
@ -185,7 +181,7 @@ mut:
|
|||
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.y += y
|
||||
}
|
||||
|
@ -213,7 +209,7 @@ fn test_generic_fn_in_for_in_expression() {
|
|||
assert value == 'a'
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// test generic struct
|
||||
struct DB {
|
||||
driver string
|
||||
|
@ -247,9 +243,7 @@ fn test_generic_struct() {
|
|||
name: 'joe'
|
||||
}
|
||||
}
|
||||
// a.model.name = 'joe'
|
||||
assert a.model.name == 'joe'
|
||||
println('a.model.name: $a.model.name')
|
||||
mut b := Repo<Group, Permission>{
|
||||
permission: Permission{
|
||||
name: 'superuser'
|
||||
|
@ -258,15 +252,8 @@ fn test_generic_struct() {
|
|||
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).name == 'User'
|
||||
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> {
|
||||
|
@ -322,3 +309,71 @@ fn test_generic_fn_with_variadics(){
|
|||
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