all: generic functions
parent
b8c028c727
commit
ce1a181699
|
@ -226,6 +226,7 @@ pub:
|
|||
pos token.Position
|
||||
body_pos token.Position
|
||||
file string
|
||||
is_generic bool
|
||||
pub mut:
|
||||
return_type table.Type
|
||||
}
|
||||
|
@ -251,6 +252,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
|
||||
}
|
||||
|
||||
pub struct CallArg {
|
||||
|
|
|
@ -37,7 +37,11 @@ pub fn (node &FnDecl) str(t &table.Table) string {
|
|||
else if node.language == .js {
|
||||
name = 'JS.$name'
|
||||
}
|
||||
f.write('fn ${receiver}${name}(')
|
||||
f.write('fn ${receiver}${name}')
|
||||
if node.is_generic {
|
||||
f.write('<T>')
|
||||
}
|
||||
f.write('(')
|
||||
for i, arg in node.args {
|
||||
// skip receiver
|
||||
// if (node.is_method || node.is_interface) && i == 0 {
|
||||
|
|
|
@ -320,7 +320,6 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
|||
} else {
|
||||
info = type_sym.info as table.Struct
|
||||
}
|
||||
|
||||
if struct_init.is_short && struct_init.fields.len > info.fields.len {
|
||||
c.error('too many fields', struct_init.pos)
|
||||
}
|
||||
|
@ -889,6 +888,13 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
c.error('unknown function: $fn_name', call_expr.pos)
|
||||
return table.void_type
|
||||
}
|
||||
if call_expr.generic_type != table.void_type && f.return_type != 0 { // table.t_type {
|
||||
// Handle `foo<T>() T` => `foo<int>() int` => return int
|
||||
sym := c.table.get_type_symbol(f.return_type)
|
||||
if sym.name == 'T' {
|
||||
return call_expr.generic_type
|
||||
}
|
||||
}
|
||||
if !found_in_args && call_expr.mod in ['builtin', 'main'] {
|
||||
scope := c.file.scope.innermost(call_expr.pos.pos)
|
||||
if _ := scope.find_var(fn_name) {
|
||||
|
@ -896,8 +902,8 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
call_expr.pos)
|
||||
}
|
||||
}
|
||||
if !f.is_pub && f.language == .v && !c.is_builtin_mod && !c.pref.is_test && f.mod != c.mod && f.name !=
|
||||
'' && f.mod != '' {
|
||||
if !f.is_pub && f.language == .v && !c.is_builtin_mod && !c.pref.is_test && f.mod != c.mod &&
|
||||
f.name != '' && f.mod != '' {
|
||||
c.warn('function `$f.name` is private. curmod=$c.mod fmod=$f.mod', call_expr.pos)
|
||||
}
|
||||
call_expr.return_type = f.return_type
|
||||
|
@ -1536,7 +1542,8 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
|||
mut scope := c.file.scope.innermost(it.pos.pos)
|
||||
sym := c.table.get_type_symbol(typ)
|
||||
if sym.kind == .map && !(it.key_var.len > 0 && it.val_var.len > 0) {
|
||||
c.error('for in: cannot use one variable in map', it.pos)
|
||||
c.error('declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
|
||||
'use `_` if you do not need the variable', it.pos)
|
||||
}
|
||||
if it.key_var.len > 0 {
|
||||
key_type := match sym.kind {
|
||||
|
|
|
@ -95,6 +95,7 @@ mut:
|
|||
is_builtin_mod bool
|
||||
hotcode_fn_names []string
|
||||
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
|
||||
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -290,6 +291,10 @@ pub fn (mut g Gen) write_typeof_functions() {
|
|||
// V type to C type
|
||||
fn (mut g Gen) typ(t table.Type) string {
|
||||
mut styp := g.base_type(t)
|
||||
if styp.len == 1 && g.cur_generic_type != 0 {
|
||||
// T => int etc
|
||||
return g.typ(g.cur_generic_type)
|
||||
}
|
||||
base := styp
|
||||
if t.flag_is(.optional) {
|
||||
if t.is_ptr() {
|
||||
|
@ -365,12 +370,10 @@ typedef struct {
|
|||
.alias {
|
||||
parent := &g.table.types[typ.parent_idx]
|
||||
styp := typ.name.replace('.', '__')
|
||||
is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] == `.`
|
||||
parent_styp := if is_c_parent {
|
||||
'struct ' + parent.name[2..].replace('.', '__')
|
||||
} else {
|
||||
parent.name.replace('.', '__')
|
||||
}
|
||||
is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] ==
|
||||
`.`
|
||||
parent_styp := if is_c_parent { 'struct ' + parent.name[2..].replace('.', '__') } else { parent.name.replace('.',
|
||||
'__') }
|
||||
g.definitions.writeln('typedef $parent_styp $styp;')
|
||||
}
|
||||
.array {
|
||||
|
@ -1712,8 +1715,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||
g.expr(node.right)
|
||||
g.write(')')
|
||||
} else if node.op in [.plus, .minus, .mul, .div, .mod] && (left_sym.name[0].is_capital() ||
|
||||
left_sym.name.contains('.')) && left_sym.kind != .alias ||
|
||||
left_sym.kind == .alias && (left_sym.info as table.Alias).language == .c {
|
||||
left_sym.name.contains('.')) && left_sym.kind != .alias || left_sym.kind == .alias && (left_sym.info as table.Alias).language ==
|
||||
.c {
|
||||
// !left_sym.is_number() {
|
||||
g.write(g.typ(node.left_type))
|
||||
g.write('_')
|
||||
|
@ -2258,7 +2261,9 @@ fn (mut g Gen) go_back_out(n int) {
|
|||
}
|
||||
|
||||
fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
||||
skip_init := ['strconv__ftoa__Uf32', 'strconv__ftoa__Uf64', 'strconv__Float64u', 'struct stat', 'struct addrinfo']
|
||||
skip_init := ['strconv__ftoa__Uf32', 'strconv__ftoa__Uf64', 'strconv__Float64u', 'struct stat',
|
||||
'struct addrinfo'
|
||||
]
|
||||
styp := g.typ(struct_init.typ)
|
||||
if styp in skip_init {
|
||||
g.go_back_out(3)
|
||||
|
@ -2274,7 +2279,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
|||
g.writeln('($styp){')
|
||||
}
|
||||
// mut fields := []string{}
|
||||
mut inited_fields := map[string]int // TODO this is done in checker, move to ast node
|
||||
mut inited_fields := map[string]int{} // TODO this is done in checker, move to ast node
|
||||
/*
|
||||
if struct_init.fields.len == 0 && struct_init.exprs.len > 0 {
|
||||
// Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
|
||||
|
@ -2378,7 +2383,7 @@ fn (mut g Gen) assoc(node ast.Assoc) {
|
|||
}
|
||||
styp := g.typ(node.typ)
|
||||
g.writeln('($styp){')
|
||||
mut inited_fields := map[string]int
|
||||
mut inited_fields := map[string]int{}
|
||||
for i, field in node.fields {
|
||||
inited_fields[field] = i
|
||||
}
|
||||
|
@ -2860,10 +2865,12 @@ fn (mut g Gen) gen_map(node ast.CallExpr) {
|
|||
if it.kind == .function {
|
||||
g.writeln('${it.name}(it)')
|
||||
} else {
|
||||
g.expr(node.args[0].expr)
|
||||
g.expr(node.args[0].expr)
|
||||
}
|
||||
}
|
||||
else { g.expr(node.args[0].expr) }
|
||||
else {
|
||||
g.expr(node.args[0].expr)
|
||||
}
|
||||
}
|
||||
g.writeln(';')
|
||||
g.writeln('\tarray_push(&$tmp, &ti);')
|
||||
|
@ -2898,10 +2905,12 @@ fn (mut g Gen) gen_filter(node ast.CallExpr) {
|
|||
if it.kind == .function {
|
||||
g.writeln('${node.args[0]}(it)')
|
||||
} else {
|
||||
g.expr(node.args[0].expr)
|
||||
g.expr(node.args[0].expr)
|
||||
}
|
||||
}
|
||||
else { g.expr(node.args[0].expr) }
|
||||
else {
|
||||
g.expr(node.args[0].expr)
|
||||
}
|
||||
}
|
||||
g.writeln(') array_push(&$tmp, &it); \n }')
|
||||
g.write(s)
|
||||
|
@ -3815,7 +3824,7 @@ fn (g &Gen) interface_table() string {
|
|||
mut methods_struct_def := strings.new_builder(100)
|
||||
methods_struct_def.writeln('$methods_struct_name {')
|
||||
mut imethods := map[string]string{} // a map from speak -> _Speaker_speak_fn
|
||||
mut methodidx := map[string]int
|
||||
mut methodidx := map[string]int{}
|
||||
for k, method in ityp.methods {
|
||||
methodidx[method.name] = k
|
||||
typ_name := '_${interface_name}_${method.name}_fn'
|
||||
|
@ -3948,7 +3957,7 @@ fn (mut g Gen) array_init(it ast.ArrayInit) {
|
|||
g.write('0, ')
|
||||
}
|
||||
g.write('sizeof($elem_type_str), ')
|
||||
if it.has_default || (it.has_len && it.elem_type == table.string_type) {
|
||||
if it.has_default || (it.has_len && it.elem_type == table.string_type) {
|
||||
g.write('&_val_$it.pos.pos)')
|
||||
} else {
|
||||
g.write('0)')
|
||||
|
|
|
@ -13,6 +13,16 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
|||
return
|
||||
}
|
||||
is_main := it.name == 'main'
|
||||
if it.is_generic && g.cur_generic_type == 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] {
|
||||
g.cur_generic_type = gen_type
|
||||
g.gen_fn_decl(it)
|
||||
println(gen_type)
|
||||
}
|
||||
g.cur_generic_type = 0
|
||||
return
|
||||
}
|
||||
//
|
||||
if is_main && g.pref.is_liveshared {
|
||||
return
|
||||
|
@ -61,6 +71,10 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
|||
} else {
|
||||
name = c_name(name)
|
||||
}
|
||||
if g.cur_generic_type != 0 {
|
||||
// foo<T>() => foo_int(), foo_string() etc
|
||||
name += '_' + g.typ(g.cur_generic_type)
|
||||
}
|
||||
// if g.pref.show_cc && it.is_builtin {
|
||||
// println(name)
|
||||
// }
|
||||
|
@ -293,7 +307,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
verror('method receiver type is 0, this means there are some uchecked exprs')
|
||||
}
|
||||
// mut receiver_type_name := g.cc_type(node.receiver_type)
|
||||
//mut receiver_type_name := g.typ(node.receiver_type)
|
||||
// mut receiver_type_name := g.typ(node.receiver_type)
|
||||
typ_sym := g.table.get_type_symbol(node.receiver_type)
|
||||
mut receiver_type_name := typ_sym.name.replace('.', '__')
|
||||
if typ_sym.kind == .interface_ {
|
||||
|
@ -429,6 +443,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
|||
// `json__encode` => `json__encode_User`
|
||||
name += '_' + json_type_str.replace('.', '__')
|
||||
}
|
||||
if node.generic_type != table.void_type {
|
||||
// `foo<int>()` => `foo_int()`
|
||||
name += '_' + g.typ(node.generic_type)
|
||||
}
|
||||
// Generate tmp vars for values that have to be freed.
|
||||
/*
|
||||
mut tmps := []string{}
|
||||
|
|
|
@ -25,11 +25,13 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
|||
p.expr_mod = ''
|
||||
is_or_block_used = true
|
||||
}
|
||||
mut generic_type := table.void_type
|
||||
if p.tok.kind == .lt {
|
||||
// `foo<int>(10)`
|
||||
p.next() // `<`
|
||||
p.parse_type()
|
||||
generic_type = p.parse_type()
|
||||
p.check(.gt) // `>`
|
||||
p.table.register_fn_gen_type(fn_name, generic_type)
|
||||
}
|
||||
p.check(.lpar)
|
||||
args := p.call_args()
|
||||
|
@ -40,6 +42,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
|||
pos: first_pos.pos
|
||||
len: last_pos.pos - first_pos.pos + last_pos.len
|
||||
}
|
||||
// `foo() or {}``
|
||||
mut or_stmts := []ast.Stmt{}
|
||||
if p.tok.kind == .key_orelse {
|
||||
p.inside_or_expr = true
|
||||
|
@ -80,6 +83,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
|||
stmts: or_stmts
|
||||
is_used: is_or_block_used
|
||||
}
|
||||
generic_type: generic_type
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
@ -284,6 +288,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
args: args
|
||||
is_deprecated: is_deprecated
|
||||
is_pub: is_pub
|
||||
is_generic: is_generic
|
||||
is_variadic: is_variadic
|
||||
receiver: ast.Field{
|
||||
name: rec_name
|
||||
|
|
|
@ -804,7 +804,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
// p.warn('name expr $p.tok.lit $p.peek_tok.str()')
|
||||
// fn call or type cast
|
||||
if p.peek_tok.kind == .lpar || (p.peek_tok.kind == .lt && p.peek_tok2.kind == .name &&
|
||||
p.peek_tok.pos == p.peek_tok2.pos - 1) { // foo() or foo<int>()
|
||||
p.peek_tok.pos == p.peek_tok2.pos - 1) { // foo() or foo<int>() TODO remove whitespace sensitivity
|
||||
mut name := p.tok.lit
|
||||
if mod.len > 0 {
|
||||
name = '${mod}.$name'
|
||||
|
|
|
@ -191,6 +191,7 @@ pub const (
|
|||
array_type_idx = 20
|
||||
map_type_idx = 21
|
||||
any_type_idx = 22
|
||||
t_type_idx = 23
|
||||
)
|
||||
|
||||
pub const (
|
||||
|
@ -237,6 +238,7 @@ pub const (
|
|||
array_type = new_type(array_type_idx)
|
||||
map_type = new_type(map_type_idx)
|
||||
any_type = new_type(any_type_idx)
|
||||
t_type = new_type(t_type_idx)
|
||||
)
|
||||
|
||||
pub const (
|
||||
|
|
|
@ -15,7 +15,7 @@ pub mut:
|
|||
modules []string // List of all modules registered by the application
|
||||
cflags []cflag.CFlag
|
||||
redefined_fns []string
|
||||
fn_gen_types map[string][]string
|
||||
fn_gen_types map[string][]Type
|
||||
}
|
||||
|
||||
pub struct Fn {
|
||||
|
@ -569,3 +569,9 @@ pub fn (table &Table) qualify_module(mod, file_path string) string {
|
|||
}
|
||||
return mod
|
||||
}
|
||||
|
||||
pub fn (table &Table) register_fn_gen_type(fn_name string, typ Type) {
|
||||
mut a := table.fn_gen_types[fn_name]
|
||||
a << typ
|
||||
table.fn_gen_types[fn_name] = a
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
fn test_todo() {}
|
||||
|
||||
/*
|
||||
// QTODO
|
||||
fn simple<T>(p T) T {
|
||||
return p
|
||||
fn test_todo() {
|
||||
}
|
||||
|
||||
fn simple<T>(p T) T {
|
||||
return p
|
||||
}
|
||||
|
||||
fn test_generic_fn() {
|
||||
assert simple<int>(1) == 1
|
||||
}
|
||||
|
||||
/*
|
||||
fn sum<T>(l []T) T {
|
||||
mut r := T(0)
|
||||
for e in l {
|
||||
|
|
Loading…
Reference in New Issue