compiler: add variadic support for generic functions

pull/2514/head
joe-conigliaro 2019-10-24 01:48:07 +11:00 committed by Alexander Medvednikov
parent 374133086d
commit 0cf3e2e677
1 changed files with 59 additions and 39 deletions

View File

@ -27,20 +27,21 @@ mut:
// idx int
scope_level int
typ string // return type
is_c bool
receiver_typ string
is_c bool
is_public bool
is_method bool
returns_error bool
is_decl bool // type myfn fn(int, int)
is_unsafe bool
is_unsafe bool
is_deprecated bool
is_variadic bool
is_generic bool
returns_error bool
defer_text []string
is_generic bool
type_pars []string
type_inst []TypeInst
dispatch_of TypeInst // current type inst of this generic instance
body_idx int // idx of the first body statement
type_pars []string
type_inst []TypeInst
dispatch_of TypeInst // current type inst of this generic instance
body_idx int // idx of the first body statement
fn_name_token_idx int // used by error reporting
}
@ -782,11 +783,11 @@ fn (p mut Parser) fn_args(f mut Fn) {
if p.tok == .rpar {
p.error('you must provide a type for vargs: eg `...string`. multiple types `...` are not supported yet.')
}
f.is_variadic = true
t := p.get_type()
// register varg struct, incase function is never called
if p.first_pass() {
vargs_struct := p.fn_register_vargs_stuct(f, t, []string)
p.cgen.typedefs << 'typedef struct $vargs_struct $vargs_struct;\n'
if p.first_pass() && !f.is_generic {
p.fn_register_vargs_stuct(f, t, []string)
}
typ = '...$t'
} else {
@ -839,11 +840,6 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
// println('fn_call_args() name=$f.name args.len=$f.args.len')
// C func. # of args is not known
p.check(.lpar)
mut is_variadic := false
if f.args.len > 0 {
last_arg := f.args.last()
is_variadic = last_arg.typ.starts_with('...')
}
if f.is_c {
for p.tok != .rpar {
//C.func(var1, var2.method())
@ -926,7 +922,6 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(')
}
mut typ := p.bool_expression()
if typ.starts_with('...') { typ = typ.right(3) }
if clone {
p.gen(')')
}
@ -1052,20 +1047,20 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
// Check for commas
if i < f.args.len - 1 {
// Handle 0 args passed to varargs
if p.tok != .comma && !is_variadic {
if p.tok != .comma && !f.is_variadic {
p.error('wrong number of arguments for $i,$arg.name fn `$f.name`: expected $f.args.len, but got less')
}
if p.tok == .comma && (!is_variadic || (is_variadic && i < f.args.len-2 )) {
if p.tok == .comma && (!f.is_variadic || (f.is_variadic && i < f.args.len-2 )) {
p.check(.comma)
p.gen(',')
}
}
}
// varargs
if !p.first_pass() && is_variadic {
p.fn_gen_caller_vargs(mut f)
varg_type, varg_values := p.fn_call_vargs(f)
if f.is_variadic {
saved_args << '...$varg_type'
}
if p.tok == .comma {
p.error('wrong number of arguments for fn `$f.name`: expected $f.args.len, but got more')
}
@ -1074,6 +1069,9 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
type_map := p.extract_type_inst(f, saved_args)
p.dispatch_generic_fn_instance(mut f, type_map)
}
if f.is_variadic {
p.fn_gen_caller_vargs(f, varg_type, varg_values)
}
}
// From a given generic function and an argument list matching its signature,
@ -1160,6 +1158,9 @@ fn (p mut Parser) replace_type_params(f &Fn, ti TypeInst) []string {
fi = fi.right(6)
fr += 'array_'
}
if fi.starts_with('...') {
fi = fi.right(3)
}
if fi in ti.inst.keys() {
fr += ti.inst[fi]
// println("replaced $a => $fr")
@ -1178,31 +1179,46 @@ fn (p mut Parser) fn_register_vargs_stuct(f &Fn, typ string, values []string) st
name: vargs_struct,
mod: p.mod
}
if values.len > 0 {
p.table.rewrite_type(varg_type)
} else {
if !p.table.known_type(vargs_struct) {
p.table.register_type2(varg_type)
p.cgen.typedefs << 'typedef struct $vargs_struct $vargs_struct;\n'
} else {
p.table.rewrite_type(varg_type)
}
p.table.add_field(vargs_struct, 'len', 'int', false, '', .public)
p.table.add_field(vargs_struct, 'args[$values.len]', typ, false, '', .public)
return vargs_struct
}
fn (p mut Parser) fn_gen_caller_vargs(f mut Fn) {
fn (p mut Parser) fn_call_vargs(f Fn) (string, []string) {
if !f.is_variadic {
return '', []string
}
last_arg := f.args.last()
varg_def_type := last_arg.typ.right(3)
mut varg_def_type := last_arg.typ.right(3)
mut types := []string
mut values := []string
for p.tok != .rpar {
if p.tok == .comma {
p.check(.comma)
}
p.cgen.start_tmp()
varg_type := p.bool_expression()
mut varg_type := p.bool_expression()
varg_value := p.cgen.end_tmp()
p.check_types(last_arg.typ, varg_type)
if !f.is_generic {
p.check_types(last_arg.typ, varg_type)
} else {
if types.len > 0 {
for t in types {
p.check_types(varg_type, t)
}
}
varg_def_type = varg_type
}
ref_deref := if last_arg.typ.ends_with('*') && !varg_type.ends_with('*') { '&' }
else if !last_arg.typ.ends_with('*') && varg_type.ends_with('*') { '*' }
else { '' }
types << varg_type
values << '$ref_deref$varg_value'
}
for va in p.table.varg_access {
@ -1214,9 +1230,12 @@ fn (p mut Parser) fn_gen_caller_vargs(f mut Fn) {
if f.args.len > 1 {
p.cgen.gen(',')
}
vargs_struct := p.fn_register_vargs_stuct(f, varg_def_type, values)
p.cgen.gen('&($vargs_struct){.len=$values.len,.args={'+values.join(',')+'}}')
return varg_def_type, values
}
fn (p mut Parser) fn_gen_caller_vargs(f &Fn, varg_type string, values []string) {
vargs_struct := p.fn_register_vargs_stuct(f, varg_type, values)
p.cgen.gen('&($vargs_struct){.len=$values.len,.args={'+values.join(',')+'}}')
}
fn (p mut Parser) register_multi_return_stuct(types []string) string {
@ -1234,6 +1253,13 @@ fn (p mut Parser) register_multi_return_stuct(types []string) string {
return typ
}
fn (p mut Parser) rename_generic_fn_instance(f mut Fn, ti TypeInst) {
f.name = f.name + '_T'
for k in ti.inst.keys() {
f.name = f.name + '_' + type_to_safe_str(ti.inst[k].replace('...', ''))
}
}
fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti TypeInst) {
mut new_inst := true
for e in f.type_inst {
@ -1244,10 +1270,7 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti TypeInst) {
}
if !new_inst {
f.name = f.name + '_T'
for k in ti.inst.keys() {
f.name = f.name + '_' + type_to_safe_str(ti.inst[k])
}
p.rename_generic_fn_instance(mut f, ti)
_f := p.table.find_fn(f.name) or {
p.error('function instance `$f.name` not found')
return
@ -1276,10 +1299,7 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti TypeInst) {
saved_tmp_line := p.cgen.tmp_line
returns := p.returns // should be always false
f.name = f.name + '_T'
for k in ti.inst.keys() {
f.name = f.name + '_' + type_to_safe_str(ti.inst[k])
}
p.rename_generic_fn_instance(mut f, ti)
f.is_generic = false // the instance is a normal function
f.type_inst = []TypeInst
f.scope_level = 0