diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index f377029f2e..3beb5d5725 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -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 diff --git a/vlib/v/tests/fn_high_test.v b/vlib/v/tests/fn_high_test.v index e60c230c6e..7ce8802866 100644 --- a/vlib/v/tests/fn_high_test.v +++ b/vlib/v/tests/fn_high_test.v @@ -133,15 +133,3 @@ fn test_assigning_fns() { // // End assigning functions (IdentFn) // - - - - - - - - - - - - diff --git a/vlib/v/tests/fn_multiple_returns_test.v b/vlib/v/tests/fn_multiple_returns_test.v index 3d9e45d8e9..19448975cc 100644 --- a/vlib/v/tests/fn_multiple_returns_test.v +++ b/vlib/v/tests/fn_multiple_returns_test.v @@ -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' +}