cgen: optional multiple return values

pull/4774/head
Tanel Liiv 2020-05-07 16:36:04 +03:00 committed by GitHub
parent 12221fb999
commit d0afa748ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 102 additions and 47 deletions

View File

@ -130,6 +130,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
}
g.init()
//
mut tests_inited := false
mut autofree_used := false
for file in files {
g.file = file
@ -147,6 +148,12 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
g.autofree = true
autofree_used = true
}
// anon fn may include assert and thus this needs
// to be included before any test contents are written
if g.is_test && !tests_inited {
g.write_tests_main()
tests_inited = true
}
g.stmts(file.stmts)
}
if autofree_used {
@ -158,9 +165,6 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
// no init in builtin.o
g.write_init_function()
}
if g.is_test {
g.write_tests_main()
}
//
g.finish()
//
@ -856,7 +860,15 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.expr(assign_stmt.right[0])
g.is_assign_rhs = false
if is_optional {
g.or_block(mr_var_name, or_stmts, return_type)
val := assign_stmt.right[0]
match val {
ast.CallExpr {
or_stmts = it.or_block.stmts
return_type = it.return_type
g.or_block(mr_var_name, or_stmts, return_type)
}
else {}
}
}
g.writeln(';')
for i, ident in assign_stmt.left {
@ -1972,22 +1984,35 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
}
fn (mut g Gen) return_statement(node ast.Return) {
g.write('return')
g.write('return ')
if g.fn_decl.name == 'main' {
g.writeln(' 0;')
g.writeln('0;')
return
}
fn_return_is_optional := g.fn_decl.return_type.flag_is(.optional)
// 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(' ')
fn_return_is_optional := g.fn_decl.return_type.flag_is(.optional)
// handle none/error for optional
if fn_return_is_optional {
optional_none := node.exprs[0] is ast.None
mut optional_error := false
match node.exprs[0] {
ast.CallExpr { optional_error = it.name == 'error' }
else { false }
}
if optional_none || optional_error {
g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
g.write(';')
return
}
}
// regular cases
if fn_return_is_multi { // not_optional_none { //&& !fn_return_is_optional {
// typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)
// mr_info := typ_sym.info as table.MultiReturn
mut styp := ''
if fn_return_is_optional { // && !node.types[0].flag_is(.optional) && node.types[0] !=
if fn_return_is_optional {
styp = g.base_type(g.fn_decl.return_type)
g.write('opt_ok(&($styp/*X*/[]) { ')
} else {
@ -2007,37 +2032,24 @@ fn (mut g Gen) return_statement(node ast.Return) {
}
} else if node.exprs.len >= 1 {
// normal return
g.write(' ')
return_sym := g.table.get_type_symbol(node.types[0])
// `return opt_ok(expr)` for functions that expect an optional
if fn_return_is_optional && !node.types[0].flag_is(.optional) && return_sym.name !=
'Option' {
mut is_none := false
mut is_error := false
expr0 := node.exprs[0]
match expr0 {
ast.None {
is_none = true
}
ast.CallExpr {
if it.name == 'error' {
is_error = true // TODO check name 'error'
}
}
else {}
styp := g.base_type(g.fn_decl.return_type)
g.write('/*:)$return_sym.name*/opt_ok(&($styp[]) { ')
if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() {
// Automatic Dereference for optional
g.write('*')
}
if !is_none && !is_error {
styp := g.base_type(g.fn_decl.return_type)
g.write('/*:)$return_sym.name*/opt_ok(&($styp[]) { ')
if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() {
// Automatic Dereference for optional
g.write('*')
for i, expr in node.exprs {
g.expr(expr)
if i < node.exprs.len - 1 {
g.write(', ')
}
g.expr(node.exprs[0])
g.writeln(' }, sizeof($styp));')
return
}
// g.write('/*OPTIONAL*/')
g.writeln(' }, sizeof($styp));')
return
}
if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() {
// Automatic Dereference

View File

@ -133,15 +133,3 @@ fn test_assigning_fns() {
//
// End assigning functions (IdentFn)
//

View File

@ -17,3 +17,58 @@ fn fn_mr_get_user() (string, int, []string, UserData) {
data := UserData{test: 'Test Data'}
return 'joe', 34, groups, data
}
fn split_to_two(s string) ?(string, string) {
mut tokens := s.split_nth(' ', 2)
if s.len == 0 {
return none
}
if tokens.len != 2 {
return error('error')
}
return tokens[0], tokens[1]
}
fn returnable_fail() string {
_,_ := split_to_two('bad') or {
return 'ok'
}
return 'nok'
}
fn test_multiple_ret() {
// returnable test
assert returnable_fail() == 'ok'
// good case
res1_1, res1_2 := split_to_two("fish house") or {
assert false
return
}
assert res1_1 == 'fish'
assert res1_2 == 'house'
// none case
wrapper1 := fn()(string, string){
res2_1, res2_2 := split_to_two("") or {
assert err == ''
return 'replaced', 'val'
}
return res2_1, res2_2
}
res2_1, res2_2 := wrapper1()
assert res2_1 == 'replaced'
assert res2_2 == 'val'
// error case
wrapper2 := fn()(string, string){
res3_1, res3_2 := split_to_two('fishhouse') or {
assert err == 'error'
return 'replaced', 'val'
}
return res3_1, res3_2
}
res3_1, res3_2 := wrapper2()
assert res3_1 == 'replaced'
assert res3_2 == 'val'
}