diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index 0ca889e292..ae0f601f8a 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -105,6 +105,7 @@ const ( 'vlib/v/tests/interface_edge_cases/assign_to_interface_field_test.v', 'vlib/v/tests/interface_fields_test.v', 'vlib/v/tests/interface_variadic_test.v', + 'vlib/v/tests/option_2_test.v', 'vlib/v/tests/orm_sub_struct_test.v', 'vlib/v/tests/ref_struct_test.v', 'vlib/v/tests/semaphore_test.v', diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index de5aed918d..19cb2fec35 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1280,7 +1280,9 @@ pub fn (expr Expr) is_lvalue() bool { pub fn (expr Expr) is_expr() bool { match expr { IfExpr { return expr.is_expr } + LockExpr { return expr.is_expr } MatchExpr { return expr.is_expr } + SelectExpr { return expr.is_expr } else {} } return true diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 3ea8c0cc61..c5728bea8e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3670,7 +3670,11 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { return c.cast_expr(mut node) } ast.CallExpr { - return c.call_expr(mut node) + mut ret_type := c.call_expr(mut node) + if ret_type.has_flag(.optional) && node.or_block.kind != .absent { + ret_type = ret_type.clear_flag(.optional) + } + return ret_type } ast.GoExpr { mut ret_type := c.call_expr(mut node.go_stmt.call_expr) diff --git a/vlib/v/checker/tests/return_optional_err.out b/vlib/v/checker/tests/return_optional_err.out deleted file mode 100644 index e7066fcfea..0000000000 --- a/vlib/v/checker/tests/return_optional_err.out +++ /dev/null @@ -1,7 +0,0 @@ -vlib/v/checker/tests/return_optional_err.vv:4:13: error: cannot use `?string` as type `string` in return argument - 2 | - 3 | fn my_func() string { - 4 | return os.read_file('/some/file/that/exists.txt') or { panic(err.msg) } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 5 | } - 6 | diff --git a/vlib/v/checker/tests/return_optional_err.vv b/vlib/v/checker/tests/return_optional_err.vv deleted file mode 100644 index 03be0efe75..0000000000 --- a/vlib/v/checker/tests/return_optional_err.vv +++ /dev/null @@ -1,10 +0,0 @@ -import os - -fn my_func() string { - return os.read_file('/some/file/that/exists.txt') or { panic(err.msg) } -} - -fn main() { - aa := my_func() - println(aa) -} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 09c13c2939..f3a2a967f6 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -65,7 +65,8 @@ mut: tmp_count2 int // a separate tmp var counter for autofree fn calls is_c_call bool // e.g. `C.printf("v")` is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) - is_assign_rhs bool // inside right part of assign after `=` (val expr) + discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage + is_void_expr_stmt bool // ExprStmt whos result is discarded is_array_set bool is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc) @@ -1068,7 +1069,10 @@ fn (mut g Gen) stmt(node ast.Stmt) { // if af { // g.autofree_call_pregen(node.expr as ast.CallExpr) // } + old_is_void_expr_stmt := g.is_void_expr_stmt + g.is_void_expr_stmt = !node.is_expr g.expr(node.expr) + g.is_void_expr_stmt = old_is_void_expr_stmt // if af { // g.autofree_call_postgen() // } @@ -1758,9 +1762,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { mr_var_name := 'mr_$assign_stmt.pos.pos' mr_styp := g.typ(return_type) g.write('$mr_styp $mr_var_name = ') - g.is_assign_rhs = true g.expr(assign_stmt.right[0]) - g.is_assign_rhs = false g.writeln(';') for i, lx in assign_stmt.left { if lx is ast.Ident { @@ -1947,7 +1949,10 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } if blank_assign { if is_call { + old_is_void_expr_stmt := g.is_void_expr_stmt + g.is_void_expr_stmt = true g.expr(val) + g.is_void_expr_stmt = old_is_void_expr_stmt } else { g.write('{$styp _ = ') g.expr(val) @@ -2000,7 +2005,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.write(' = /*f*/string_add(') } g.is_assign_lhs = false - g.is_assign_rhs = true str_add = true } // Assignment Operator Overloading @@ -2053,7 +2057,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.expr(left) } g.is_assign_lhs = false - g.is_assign_rhs = true if is_fixed_array_copy { if is_decl { g.writeln(';') @@ -2080,7 +2083,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.write('*($styp*)') g.write(tmp_opt + '.data/*FFz*/') g.right_is_opt = false - g.is_assign_rhs = false if g.inside_ternary == 0 && !assign_stmt.is_simple { g.writeln(';') } @@ -2144,7 +2146,6 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.is_shared = false } g.right_is_opt = false - g.is_assign_rhs = false if g.inside_ternary == 0 && (assign_stmt.left.len > 1 || !assign_stmt.is_simple) { g.writeln(';') } @@ -2468,6 +2469,14 @@ fn (mut g Gen) map_fn_ptrs(key_typ table.TypeSymbol) (string, string, string, st fn (mut g Gen) expr(node ast.Expr) { // println('cgen expr() line_nr=$node.pos.line_nr') + old_discard_or_result := g.discard_or_result + old_is_void_expr_stmt := g.is_void_expr_stmt + if g.is_void_expr_stmt { + g.discard_or_result = true + g.is_void_expr_stmt = false + } else { + g.discard_or_result = false + } // NB: please keep the type names in the match here in alphabetical order: match mut node { ast.AnonFn { @@ -2733,7 +2742,7 @@ fn (mut g Gen) expr(node ast.Expr) { right_sym := g.table.get_type_symbol(node.right_type) mut right_inf := right_sym.info as table.Chan elem_type := right_inf.elem_type - is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs + is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result cur_line := if is_gen_or_and_assign_rhs { line := g.go_before_stmt(0) g.out.write_string(c.tabs[g.indent]) @@ -2831,6 +2840,8 @@ fn (mut g Gen) expr(node ast.Expr) { g.expr(node.expr) } } + g.discard_or_result = old_discard_or_result + g.is_void_expr_stmt = old_is_void_expr_stmt } // T.name, typeof(expr).name @@ -4320,7 +4331,7 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) { } needs_clone := info.elem_type == table.string_type_idx && g.is_autofree && !g.is_assign_lhs - is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs + is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result cur_line := if is_gen_or_and_assign_rhs { line := g.go_before_stmt(0) g.out.write_string(c.tabs[g.indent]) @@ -4493,7 +4504,7 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) { g.write('}, &($elem_type_str[]){ $zero }))') } else { zero := g.type_default(info.value_type) - is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs + is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result cur_line := if is_gen_or_and_assign_rhs { line := g.go_before_stmt(0) g.out.write_string(c.tabs[g.indent]) @@ -4724,11 +4735,15 @@ fn (mut g Gen) return_statement(node ast.Return) { } else if node.exprs.len >= 1 { // normal return return_sym := g.table.get_type_symbol(node.types[0]) - // `return opt_ok(expr)` for functions that expect an optional - mut expr_type_is_opt := node.types[0].has_flag(.optional) expr0 := node.exprs[0] - if expr0 is ast.CallExpr { - expr_type_is_opt = expr0.return_type.has_flag(.optional) + // `return opt_ok(expr)` for functions that expect an optional + expr_type_is_opt := match expr0 { + ast.CallExpr { + expr0.return_type.has_flag(.optional) && expr0.or_block.kind == .absent + } + else { + node.types[0].has_flag(.optional) + } } if fn_return_is_optional && !expr_type_is_opt && return_sym.name !in ['Option', 'Option2'] { styp := g.base_type(g.fn_decl.return_type) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 5dccd53909..4a622f2dc6 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -410,7 +410,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { g.inside_call = false } gen_or := node.or_block.kind != .absent // && !g.is_autofree - is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs + is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result cur_line := if is_gen_or_and_assign_rhs { // && !g.is_autofree { // `x := foo() or { ...}` // cut everything that has been generated to prepend optional variable creation @@ -421,14 +421,8 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { } else { '' } - // if gen_or && g.pref.autofree && g.inside_return { - if gen_or && g.inside_return { - // TODO optional return af hack (tmp_count gets increased in .return_statement()) - g.tmp_count-- - } tmp_opt := if gen_or { g.new_tmp_var() } else { '' } - if gen_or && !g.inside_return { - // if is_gen_or_and_assign_rhs { + if gen_or { styp := g.typ(node.return_type.set_flag(.optional)) g.write('$styp $tmp_opt = ') } @@ -455,10 +449,10 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { } else if g.table.get_type_symbol(node.return_type).kind == .multi_return { g.write('\n $cur_line $tmp_opt /*U*/') } else { - g.write('\n $cur_line *($unwrapped_styp*)${tmp_opt}.data') + if !g.inside_const { + g.write('\n $cur_line *($unwrapped_styp*)${tmp_opt}.data') + } } - // g.write('\n /*call_expr cur_line:*/ $cur_line /*C*/ $tmp_opt /*end*/') - // g.insert_before_stmt('\n /* VVV */ $tmp_opt') } } } diff --git a/vlib/v/tests/option_2_test.v b/vlib/v/tests/option_2_test.v new file mode 100644 index 0000000000..7404fd265c --- /dev/null +++ b/vlib/v/tests/option_2_test.v @@ -0,0 +1,59 @@ +fn f(n int) ?int { + if n < 0 { + return error('negative arg') + } + return n +} + +fn test_lhs_option() { + mut a := [0, 1, 2, 3, 4] + a[f(-1) or { 2 }] = 7 + assert a == [0, 1, 7, 3, 4] +} + +fn ret_no_opt(n int) int { + return f(n) or { panic(err.msg) } +} + +fn test_opt_return_no_opt() { + aa := ret_no_opt(3) + assert aa == 3 +} + +fn test_range_for_and_array_push() { + mut a := []int{} + for n in f(-3) or { -1 } .. f(3) or { 12 } { + a << f(n) or { -5 } + } + assert a == [-5, 0, 1, 2] +} + +fn test_channel_push() { + ch := chan f64{cap: 2} + ch <- 12.25 + ch.close() + mut res := []f64{cap: 3} + for _ in 0 .. 3 { + res << <-ch or { -6.75 } + } + assert res == [12.25, -6.75, -6.75] +} + +fn test_thread_wait() { + thrs := [ + go f(3) + go f(-7) + go f(12) + ] + mut res := []int{cap: 3} + for t in thrs { + res << t.wait() or { -13 } + } + assert res == [3, -13, 12] +} + +fn test_nested_opt() { + a := f(f(f(-3) or { -7 }) or { 4 }) or { 17 } + assert a == 4 +} +