diff --git a/examples/websocket/client-server/client.v b/examples/websocket/client-server/client.v index 5b1b1c289e..14f2bace63 100644 --- a/examples/websocket/client-server/client.v +++ b/examples/websocket/client-server/client.v @@ -49,6 +49,6 @@ fn start_client() ?&websocket.Client { ws.connect() or { println(term.red('error on connect: $err')) } - go ws.listen() or { println(term.red('error on listen $err')) } + go ws.listen() // or { println(term.red('error on listen $err')) } return ws } diff --git a/examples/websocket/ping.v b/examples/websocket/ping.v index 7298f92ea1..ba7a554864 100644 --- a/examples/websocket/ping.v +++ b/examples/websocket/ping.v @@ -64,7 +64,7 @@ fn start_client() ? { // // println('type: $msg.opcode payload:\n$msg.payload ref: $r') // }, &r) ws.connect() or { println('error on connect: $err') } - go write_echo(mut ws) or { println('error on write_echo $err') } + go write_echo(mut ws) // or { println('error on write_echo $err') } ws.listen() or { println('error on listen $err') } unsafe { ws.free() diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 16a200db45..78bd3f8b37 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1333,9 +1333,13 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { elem_sym := c.table.get_type_symbol(elem_typ) if elem_sym.kind == .thread { if call_expr.args.len != 0 { - c.error('wait() does not have any arguments', call_expr.args[0].pos) + c.error('`.wait()` does not have any arguments', call_expr.args[0].pos) } thread_ret_type := elem_sym.thread_info().return_type + if thread_ret_type.has_flag(.optional) { + c.error('`.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.', + call_expr.pos) + } call_expr.return_type = c.table.find_or_register_array(thread_ret_type) } else { c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)', @@ -1428,6 +1432,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { if call_expr.args.len > 0 { c.error('wait() does not have any arguments', call_expr.args[0].pos) } + call_expr.return_type = info.return_type return info.return_type } mut method := table.Fn{} @@ -3155,6 +3160,10 @@ fn (mut c Checker) stmt(node ast.Stmt) { } ast.GoStmt { c.go_stmt(mut node) + if node.call_expr.or_block.kind != .absent { + c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`', + node.call_expr.or_block.pos) + } } ast.GotoLabel {} ast.GotoStmt { @@ -3634,7 +3643,11 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { return c.call_expr(mut node) } ast.GoExpr { - ret_type := c.call_expr(mut node.go_stmt.call_expr) + mut ret_type := c.call_expr(mut node.go_stmt.call_expr) + if node.go_stmt.call_expr.or_block.kind != .absent { + c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`', + node.go_stmt.call_expr.or_block.pos) + } return c.table.find_or_register_thread(ret_type) } ast.ChanInit { diff --git a/vlib/v/checker/tests/go_wait_or.out b/vlib/v/checker/tests/go_wait_or.out new file mode 100644 index 0000000000..146f4fbbf4 --- /dev/null +++ b/vlib/v/checker/tests/go_wait_or.out @@ -0,0 +1,69 @@ +vlib/v/checker/tests/go_wait_or.vv:11:17: error: unexpected `?`, the function `wait` does not return an optional + 9 | go d(1) + 10 | ] + 11 | r := tg.wait() ? + | ^ + 12 | println(r) + 13 | s := tg[0].wait() or { panic('problem') } +vlib/v/checker/tests/go_wait_or.vv:13:20: error: unexpected `or` block, the function `wait` does not return an optional + 11 | r := tg.wait() ? + 12 | println(r) + 13 | s := tg[0].wait() or { panic('problem') } + | ~~~~~~~~~~~~~~~~~~~~~~~ + 14 | println(s) + 15 | tg2 := [ +vlib/v/checker/tests/go_wait_or.vv:19:13: error: unexpected `or` block, the function `wait` does not return an optional + 17 | go e(1) + 18 | ] + 19 | tg2.wait() or { panic('problem') } + | ~~~~~~~~~~~~~~~~~~~~~~~ + 20 | tg2[0].wait() ? + 21 | tg3 := [ +vlib/v/checker/tests/go_wait_or.vv:20:16: error: unexpected `?`, the function `wait` does not return an optional + 18 | ] + 19 | tg2.wait() or { panic('problem') } + 20 | tg2[0].wait() ? + | ^ + 21 | tg3 := [ + 22 | go f(0) +vlib/v/checker/tests/go_wait_or.vv:25:6: error: `.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`. + 23 | go f(1) + 24 | ] + 25 | tg3.wait() or { panic('problem') } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 26 | for t in tg3 { + 27 | a := t.wait() +vlib/v/checker/tests/go_wait_or.vv:27:10: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end + 25 | tg3.wait() or { panic('problem') } + 26 | for t in tg3 { + 27 | a := t.wait() + | ~~~~~~ + 28 | println(a) + 29 | } +vlib/v/checker/tests/go_wait_or.vv:31:15: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end + 29 | } + 30 | for i, _ in tg3 { + 31 | a := tg3[i].wait() + | ~~~~~~ + 32 | println(a) + 33 | } +vlib/v/checker/tests/go_wait_or.vv:38:6: error: `.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`. + 36 | go g(1) + 37 | ] + 38 | tg4.wait() + | ~~~~~~ + 39 | tg4[0].wait() + 40 | go g(3) or { panic('problem') } +vlib/v/checker/tests/go_wait_or.vv:39:9: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end + 37 | ] + 38 | tg4.wait() + 39 | tg4[0].wait() + | ~~~~~~ + 40 | go g(3) or { panic('problem') } + 41 | } +vlib/v/checker/tests/go_wait_or.vv:40:10: error: optional handling cannot be done in `go` call. Do it when calling `.wait()` + 38 | tg4.wait() + 39 | tg4[0].wait() + 40 | go g(3) or { panic('problem') } + | ~~~~~~~~~~~~~~~~~~~~~~~ + 41 | } diff --git a/vlib/v/checker/tests/go_wait_or.vv b/vlib/v/checker/tests/go_wait_or.vv new file mode 100644 index 0000000000..3522f90851 --- /dev/null +++ b/vlib/v/checker/tests/go_wait_or.vv @@ -0,0 +1,41 @@ +fn d(n int) f64 { return f64(n) } +fn e(n int) { } +fn f(n int) ?f64 { return f64(n) } +fn g(n int) ? { } + +fn main() { + tg := [ + go d(0) + go d(1) + ] + r := tg.wait() ? + println(r) + s := tg[0].wait() or { panic('problem') } + println(s) + tg2 := [ + go e(0) + go e(1) + ] + tg2.wait() or { panic('problem') } + tg2[0].wait() ? + tg3 := [ + go f(0) + go f(1) + ] + tg3.wait() or { panic('problem') } + for t in tg3 { + a := t.wait() + println(a) + } + for i, _ in tg3 { + a := tg3[i].wait() + println(a) + } + tg4 := [ + go g(0) + go g(1) + ] + tg4.wait() + tg4[0].wait() + go g(3) or { panic('problem') } +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 4e3734e340..0faeea9d84 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5942,10 +5942,12 @@ fn (mut g Gen) go_stmt(node ast.GoStmt, joinable bool) string { if g.pref.os == .windows && node.call_expr.return_type != table.void_type { g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));') } + is_opt := node.call_expr.return_type.has_flag(.optional) gohandle_name := if node.call_expr.return_type == table.void_type { - '__v_thread' + if is_opt { '__v_thread_Option_void' } else { '__v_thread' } } else { - '__v_thread_' + g.table.get_type_symbol(g.unwrap_generic(node.call_expr.return_type)).cname + opt := if is_opt { 'Option_' } else { '' } + '__v_thread_$opt${g.table.get_type_symbol(g.unwrap_generic(node.call_expr.return_type)).cname}' } if g.pref.os == .windows { simple_handle := if joinable && node.call_expr.return_type != table.void_type { diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 08138cbdc5..51eb4f1cdd 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -120,14 +120,27 @@ pub fn (mut p Parser) parse_chan_type() table.Type { } pub fn (mut p Parser) parse_thread_type() table.Type { + is_opt := p.peek_tok.kind == .question + if is_opt { + p.next() + } if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp && p.peek_tok.kind != .lsbr { p.next() - return table.thread_type + if is_opt { + mut ret_type := table.void_type + ret_type = ret_type.set_flag(.optional) + idx := p.table.find_or_register_thread(ret_type) + return table.new_type(idx) + } else { + return table.thread_type + } } - p.next() - elem_type := p.parse_type() - idx := p.table.find_or_register_thread(elem_type) + if !is_opt { + p.next() + } + ret_type := p.parse_type() + idx := p.table.find_or_register_thread(ret_type) return table.new_type(idx) } diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index 0ab7bf72d1..bd629e0b77 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -487,22 +487,32 @@ pub fn (t &Table) chan_cname(elem_type Type, is_mut bool) string { [inline] pub fn (t &Table) thread_name(return_type Type) string { - if return_type == void_type { - return 'thread' + if return_type.idx() == void_type_idx { + if return_type.has_flag(.optional) { + return 'thread ?' + } else { + return 'thread' + } } return_type_sym := t.get_type_symbol(return_type) ptr := if return_type.is_ptr() { '&' } else { '' } - return 'thread $ptr$return_type_sym.name' + opt := if return_type.has_flag(.optional) { '?' } else { '' } + return 'thread $opt$ptr$return_type_sym.name' } [inline] pub fn (t &Table) thread_cname(return_type Type) string { if return_type == void_type { - return '__v_thread' + if return_type.has_flag(.optional) { + return '__v_thread_Option_void' + } else { + return '__v_thread' + } } return_type_sym := t.get_type_symbol(return_type) suffix := if return_type.is_ptr() { '_ptr' } else { '' } - return '__v_thread_$return_type_sym.cname$suffix' + prefix := if return_type.has_flag(.optional) { 'Option_' } else { '' } + return '__v_thread_$prefix$return_type_sym.cname$suffix' } // map_source_name generates the original name for the v source. diff --git a/vlib/v/tests/go_wait_option_test.v b/vlib/v/tests/go_wait_option_test.v new file mode 100644 index 0000000000..80cf7e2e96 --- /dev/null +++ b/vlib/v/tests/go_wait_option_test.v @@ -0,0 +1,79 @@ +fn f(n int) ?f64 { + if n < 0 { + return error('negative number') + } + return n + f64(n) / 2 +} + +fn g(n int) ? { + if n % 2 == 0 { + return error('even number') + } else { + return + } +} + +fn test_opt_val_wait() { + h1 := go f(-1) + h2 := go f(3) + r1 := h1.wait() or { 17.0 } + r2 := h2.wait() or { 23.0 } + assert r1 == 17.0 + assert r2 == 4.5 +} + +fn test_opt_void_wait() { + h1 := go g(2) + h2 := go g(3) + mut x := 0 + mut y := 0 + h1.wait() or { x = 1 } + h2.wait() or { y = 1 } + assert x == 1 + assert y == 0 +} + +fn propagate(n int, m int) ?f64 { + h1 := go f(n) + h2 := go g(m) + r := h1.wait() ? + h2.wait() ? + return r +} + +fn test_propagate() { + x := propagate(5, 3) or { 27.0 } + y := propagate(-3, 3) or { 29.0 } + z := propagate(5, 2) or { 31.0 } + assert x == 7.5 + assert y == 29.0 + assert z == 31.0 +} + +fn test_array_void_interate() { + mut r := []thread ?{} + for i in 0 .. 3 { + r << go g(i) + } + mut res := []int{len: 3, init: 17} + for i, t in r { + t.wait() or { res[i] = i } + } + assert res[0] == 0 + assert res[1] == 17 + assert res[2] == 2 +} + +fn test_array_val_interate() { + mut r := []thread ?f64{} + for i in -1 .. 2 { + r << go f(i) + } + mut res := []f64{len: 3} + for i, t in r { + res[i] = t.wait() or { 17.0 } + } + assert res[0] == 17.0 + assert res[1] == 0.0 + assert res[2] == 1.5 +}