cgen: pass around functions

pull/4733/head
Tanel Liiv 2020-05-05 17:21:21 +03:00 committed by GitHub
parent 4de48e86d2
commit b16281d6e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 92 additions and 15 deletions

View File

@ -1614,8 +1614,10 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
return table.string_type
}
ast.AnonFn {
keep_ret_type := c.fn_return_type
c.fn_return_type = it.decl.return_type
c.stmts(it.decl.stmts)
c.fn_return_type = keep_ret_type
return it.typ
}
else {

View File

@ -358,7 +358,10 @@ typedef struct {
.function {
info := typ.info as table.FnType
func := info.func
if !info.has_decl && !info.is_anon {
sym := g.table.get_type_symbol(func.return_type)
is_multi := sym.kind == .multi_return
is_fn_sig := func.name == ''
if !info.has_decl && (!info.is_anon || is_fn_sig) && !is_multi {
fn_name := if func.is_c {
func.name.replace('.', '__')
} else if info.is_anon {
@ -556,6 +559,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
println('build module `$g.module_built` fn `$it.name`')
}
}
keep_fn_decl := g.fn_decl
g.fn_decl = it // &it
if it.name == 'main' {
// just remember `it`; main code will be generated in finish()
@ -563,7 +567,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
} else {
g.gen_fn_decl(it)
}
g.fn_decl = 0
g.fn_decl = keep_fn_decl
if skip {
g.out.go_back_to(pos)
}
@ -820,15 +824,22 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
if assign_stmt.is_static {
g.write('static ')
}
if assign_stmt.left.len > assign_stmt.right.len {
// multi return
mut or_stmts := []ast.Stmt{}
mut return_type := table.void_type
if assign_stmt.right[0] is ast.CallExpr {
it := assign_stmt.right[0] as ast.CallExpr
or_stmts = it.or_block.stmts
return_type = it.return_type
}
mut is_multi := false
// json_test failed w/o this check
if return_type != 0 {
sym := g.table.get_type_symbol(return_type)
// the left vs. right is ugly and should be removed
is_multi = sym.kind == .multi_return || assign_stmt.left.len > assign_stmt.right.len ||
assign_stmt.left.len > 1
}
if is_multi {
// multi return
mut or_stmts := []ast.Stmt{}
is_optional := return_type.flag_is(.optional)
mr_var_name := 'mr_$assign_stmt.pos.pos'
mr_styp := g.typ(return_type)
@ -865,7 +876,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
styp := g.typ(ident_var_info.typ)
mut is_call := false
mut or_stmts := []ast.Stmt{}
mut return_type := table.void_type
blank_assign := ident.kind == .blank_ident
match val {
ast.CallExpr {
is_call = true
@ -874,6 +885,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
}
ast.AnonFn {
// TODO: no buffer fiddling
if blank_assign {
g.write('{')
}
ret_styp := g.typ(it.decl.return_type)
g.write('$ret_styp (*$ident.name) (')
def_pos := g.definitions.len
@ -882,13 +896,16 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.write(') = ')
g.expr(*it)
g.writeln(';')
if blank_assign {
g.write('}')
}
continue
}
else {}
}
gen_or := is_call && return_type.flag_is(.optional)
g.is_assign_rhs = true
if ident.kind == .blank_ident {
if blank_assign {
if is_call {
g.expr(val)
} else {
@ -1909,8 +1926,13 @@ fn (mut g Gen) return_statement(node ast.Return) {
return
}
fn_return_is_optional := g.fn_decl.return_type.flag_is(.optional)
// multiple returns
if node.exprs.len > 1 {
// got to do a correct check for multireturn
sym := g.table.get_type_symbol(g.fn_decl.return_type)
fn_return_is_multi := sym.kind == .multi_return
// optional multi not supported
if fn_return_is_multi && !fn_return_is_optional {
g.write(' ')
// typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)
// mr_info := typ_sym.info as table.MultiReturn
@ -1933,7 +1955,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
if fn_return_is_optional {
g.write(' }, sizeof($styp))')
}
} else if node.exprs.len == 1 {
} else if node.exprs.len >= 1 {
// normal return
g.write(' ')
return_sym := g.table.get_type_symbol(node.types[0])

View File

@ -369,11 +369,12 @@ pub fn (mut t Table) find_or_register_multi_return(mr_typs []Type) int {
pub fn (mut t Table) find_or_register_fn_type(f Fn, is_anon, has_decl bool) int {
name := if f.name.len == 0 { 'anon_fn_$f.signature()' } else { f.name }
anon := f.name.len == 0 || is_anon
return t.register_type_symbol(TypeSymbol{
kind: .function
name: name
info: FnType{
is_anon: f.name.len == 0 || is_anon
is_anon: anon
has_decl: has_decl
func: f
}

View File

@ -120,6 +120,58 @@ fn high_fn_multi_return(a int, b fn (c []int, d []string) ([]int, []string)) {
}
fn high_fn_return_single_anon() (fn(int)f32) {
_ := 1
correct := fn(n int)f32 {
return n * n
}
return correct
}
fn high_fn_return_multi_anons() (fn(int)f32, fn(int)string) {
// parsing trap
_ := fn(n int)byte {
return 0x00
}
correct_second := fn(n int)string {
return '$n'
}
correct_first := fn(n int)f32 {
return n * n
}
// parsing trap
_ := fn(n int)[]int {
return [n]
}
return correct_first, correct_second
}
fn high_fn_return_named_fn() (fn(int)int) {
return sqr
}
fn test_high_fn_ret_anons() {
param := 13
func_sqr1 := high_fn_return_single_anon()
assert func_sqr1(param) == param * param
func_sqr2, func_repr := high_fn_return_multi_anons()
assert func_sqr2(param) == (param * param)
assert func_repr(param) == '$param'
top_lvl_sqr := high_fn_return_named_fn()
assert top_lvl_sqr(param) == param * param
}
fn high_fn_applier(arg int, func fn(a int)string) string {
return func(arg)
}
fn test_high_fn_applier() {
arg := 13
expect := '$arg $arg'
func := fn (arg int) string {
return '$arg $arg'
}
assert expect == high_fn_applier(arg, func)
}
fn sqr(x int) int {
return x * x
}